diff --git a/.gitmodules b/.gitmodules index 0b2a99894..164d2ce98 100644 --- a/.gitmodules +++ b/.gitmodules @@ -53,3 +53,7 @@ [submodule "components/lwip/lwip"] path = components/lwip/lwip url = https://github.com/espressif/esp-lwip.git + +[submodule "components/mqtt/esp-mqtt"] + path = components/mqtt/esp-mqtt + url = https://github.com/espressif/esp-mqtt.git diff --git a/components/esp_http_client/CMakeLists.txt b/components/esp_http_client/CMakeLists.txt index 7e7d29996..2bea7ce02 100644 --- a/components/esp_http_client/CMakeLists.txt +++ b/components/esp_http_client/CMakeLists.txt @@ -1,14 +1,11 @@ set(COMPONENT_SRCS "esp_http_client.c" "lib/http_auth.c" "lib/http_header.c" - "lib/http_utils.c" - "lib/transport.c" - "lib/transport_ssl.c" - "lib/transport_tcp.c") + "lib/http_utils.c") set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_PRIV_INCLUDEDIRS "lib/include") set(COMPONENT_REQUIRES "nghttp") -set(COMPONENT_PRIV_REQUIRES "mbedtls" "lwip" "esp-tls") +set(COMPONENT_PRIV_REQUIRES "mbedtls" "lwip" "esp-tls" "tcp_transport") register_component() diff --git a/components/esp_http_client/lib/http_utils.c b/components/esp_http_client/lib/http_utils.c index 2e65888ab..267e39e6d 100644 --- a/components/esp_http_client/lib/http_utils.c +++ b/components/esp_http_client/lib/http_utils.c @@ -123,9 +123,3 @@ int http_utils_str_starts_with(const char *str, const char *start) } return 0; } - -void http_utils_ms_to_timeval(int timeout_ms, struct timeval *tv) -{ - tv->tv_sec = timeout_ms / 1000; - tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000; -} diff --git a/components/esp_http_client/lib/include/http_utils.h b/components/esp_http_client/lib/include/http_utils.h index 9212cac05..3d1728505 100644 --- a/components/esp_http_client/lib/include/http_utils.h +++ b/components/esp_http_client/lib/include/http_utils.h @@ -16,6 +16,7 @@ #ifndef _HTTP_UTILS_H_ #define _HTTP_UTILS_H_ #include +#include "transport_utils.h" /** * @brief Assign new_str to *str pointer, and realloc *str if it not NULL * @@ -78,18 +79,8 @@ char *http_utils_join_string(const char *first_str, int len_first, const char *s */ int http_utils_str_starts_with(const char *str, const char *start); -/** - * @brief Convert milliseconds to timeval struct - * - * @param[in] timeout_ms The timeout milliseconds - * @param[out] tv Timeval struct - */ -void http_utils_ms_to_timeval(int timeout_ms, struct timeval *tv); -#define HTTP_MEM_CHECK(TAG, a, action) if (!(a)) { \ - ESP_LOGE(TAG,"%s:%d (%s): %s", __FILE__, __LINE__, __FUNCTION__, "Memory exhausted"); \ - action; \ - } +#define HTTP_MEM_CHECK(TAG, a, action) TRANSPORT_MEM_CHECK(TAG, a, action) #endif diff --git a/components/mqtt/CMakeLists.txt b/components/mqtt/CMakeLists.txt new file mode 100644 index 000000000..ba5375c41 --- /dev/null +++ b/components/mqtt/CMakeLists.txt @@ -0,0 +1,7 @@ +set(COMPONENT_ADD_INCLUDEDIRS esp-mqtt/include) +set(COMPONENT_PRIV_INCLUDEDIRS "esp-mqtt/lib/include") +set(COMPONENT_SRCDIRS esp-mqtt esp-mqtt/lib) + +set(COMPONENT_REQUIRES lwip nghttp mbedtls tcp_transport) + +register_component() diff --git a/components/mqtt/Kconfig b/components/mqtt/Kconfig new file mode 100644 index 000000000..273a2da2f --- /dev/null +++ b/components/mqtt/Kconfig @@ -0,0 +1 @@ +source "$IDF_PATH/components/mqtt/esp-mqtt/Kconfig.included" diff --git a/components/mqtt/component.mk b/components/mqtt/component.mk new file mode 100644 index 000000000..19e498025 --- /dev/null +++ b/components/mqtt/component.mk @@ -0,0 +1,4 @@ +COMPONENT_SUBMODULES += esp-mqtt +COMPONENT_ADD_INCLUDEDIRS := esp-mqtt/include +COMPONENT_SRCDIRS := esp-mqtt esp-mqtt/lib +COMPONENT_PRIV_INCLUDEDIRS := esp-mqtt/lib/include diff --git a/components/mqtt/esp-mqtt b/components/mqtt/esp-mqtt new file mode 160000 index 000000000..abaab2abc --- /dev/null +++ b/components/mqtt/esp-mqtt @@ -0,0 +1 @@ +Subproject commit abaab2abccc019aa57f5b9afaf57f0d49f7b1b6f diff --git a/components/tcp_transport/CMakeLists.txt b/components/tcp_transport/CMakeLists.txt new file mode 100644 index 000000000..938f7f2e3 --- /dev/null +++ b/components/tcp_transport/CMakeLists.txt @@ -0,0 +1,6 @@ +set(COMPONENT_SRCDIRS ".") +set(COMPONENT_ADD_INCLUDEDIRS "include") + +set(COMPONENT_REQUIRES lwip esp-tls) + +register_component() \ No newline at end of file diff --git a/components/tcp_transport/component.mk b/components/tcp_transport/component.mk new file mode 100644 index 000000000..308f64f0e --- /dev/null +++ b/components/tcp_transport/component.mk @@ -0,0 +1,4 @@ +# +# Component Makefile +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/components/esp_http_client/lib/include/transport.h b/components/tcp_transport/include/transport.h similarity index 91% rename from components/esp_http_client/lib/include/transport.h rename to components/tcp_transport/include/transport.h index 0509d7733..a54cb83c5 100644 --- a/components/esp_http_client/lib/include/transport.h +++ b/components/tcp_transport/include/transport.h @@ -30,6 +30,7 @@ typedef int (*io_func)(transport_handle_t t, const char *buffer, int len, int ti typedef int (*io_read_func)(transport_handle_t t, char *buffer, int len, int timeout_ms); typedef int (*trans_func)(transport_handle_t t); typedef int (*poll_func)(transport_handle_t t, int timeout_ms); +typedef transport_handle_t (*payload_transfer_func)(transport_handle_t); /** * @brief Create transport list @@ -211,6 +212,17 @@ int transport_close(transport_handle_t t); */ void *transport_get_context_data(transport_handle_t t); +/** + * @brief Get transport handle of underlying protocol + * which can access this protocol payload directly + * (used for receiving longer msg multiple times) + * + * @param[in] t The transport handle + * + * @return Payload transport handle + */ +transport_handle_t transport_get_payload_transport_handle(transport_handle_t t); + /** * @brief Set the user context data for this transport * @@ -233,6 +245,7 @@ esp_err_t transport_set_context_data(transport_handle_t t, void *data); * @param[in] _poll_read The poll read function pointer * @param[in] _poll_write The poll write function pointer * @param[in] _destroy The destroy function pointer + * @param[in] _parrent_transport The parrent transfer getter pointer * * @return * - ESP_OK @@ -244,7 +257,8 @@ esp_err_t transport_set_func(transport_handle_t t, trans_func _close, poll_func _poll_read, poll_func _poll_write, - trans_func _destroy); + trans_func _destroy, + payload_transfer_func _parrent_transport); #ifdef __cplusplus } #endif diff --git a/components/esp_http_client/lib/include/transport_ssl.h b/components/tcp_transport/include/transport_ssl.h similarity index 100% rename from components/esp_http_client/lib/include/transport_ssl.h rename to components/tcp_transport/include/transport_ssl.h diff --git a/components/esp_http_client/lib/include/transport_tcp.h b/components/tcp_transport/include/transport_tcp.h similarity index 100% rename from components/esp_http_client/lib/include/transport_tcp.h rename to components/tcp_transport/include/transport_tcp.h diff --git a/components/tcp_transport/include/transport_utils.h b/components/tcp_transport/include/transport_utils.h new file mode 100644 index 000000000..0e72812ea --- /dev/null +++ b/components/tcp_transport/include/transport_utils.h @@ -0,0 +1,40 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _TRANSPORT_UTILS_H_ +#define _TRANSPORT_UTILS_H_ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Convert milliseconds to timeval struct + * + * @param[in] timeout_ms The timeout milliseconds + * @param[out] tv Timeval struct + */ +void transport_utils_ms_to_timeval(int timeout_ms, struct timeval *tv); + + +#define TRANSPORT_MEM_CHECK(TAG, a, action) if (!(a)) { \ + ESP_LOGE(TAG,"%s:%d (%s): %s", __FILE__, __LINE__, __FUNCTION__, "Memory exhausted"); \ + action; \ + } + +#ifdef __cplusplus +} +#endif +#endif /* _TRANSPORT_UTILS_H_ */ \ No newline at end of file diff --git a/components/esp_http_client/lib/transport.c b/components/tcp_transport/transport.c similarity index 88% rename from components/esp_http_client/lib/transport.c rename to components/tcp_transport/transport.c index 9351d8378..abff9670f 100644 --- a/components/esp_http_client/lib/transport.c +++ b/components/tcp_transport/transport.c @@ -20,7 +20,7 @@ #include "esp_log.h" #include "transport.h" -#include "http_utils.h" +#include "transport_utils.h" static const char *TAG = "TRANSPORT"; @@ -40,6 +40,8 @@ struct transport_item_t { poll_func _poll_read; /*!< Poll and read */ poll_func _poll_write; /*!< Poll and write */ trans_func _destroy; /*!< Destroy and free transport */ + payload_transfer_func _parrent_transfer; /*!< Function returning underlying transport layer */ + STAILQ_ENTRY(transport_item_t) next; }; @@ -53,7 +55,7 @@ STAILQ_HEAD(transport_list_t, transport_item_t); transport_list_handle_t transport_list_init() { transport_list_handle_t list = calloc(1, sizeof(struct transport_list_t)); - HTTP_MEM_CHECK(TAG, list, return NULL); + TRANSPORT_MEM_CHECK(TAG, list, return NULL); STAILQ_INIT(list); return list; } @@ -64,7 +66,7 @@ esp_err_t transport_list_add(transport_list_handle_t list, transport_handle_t t, return ESP_ERR_INVALID_ARG; } t->scheme = calloc(1, strlen(scheme) + 1); - HTTP_MEM_CHECK(TAG, t->scheme, return ESP_ERR_NO_MEM); + TRANSPORT_MEM_CHECK(TAG, t->scheme, return ESP_ERR_NO_MEM); strcpy(t->scheme, scheme); STAILQ_INSERT_TAIL(list, t, next); return ESP_OK; @@ -113,10 +115,18 @@ esp_err_t transport_list_clean(transport_list_handle_t list) transport_handle_t transport_init() { transport_handle_t t = calloc(1, sizeof(struct transport_item_t)); - HTTP_MEM_CHECK(TAG, t, return NULL); + TRANSPORT_MEM_CHECK(TAG, t, return NULL); return t; } +transport_handle_t transport_get_payload_transport_handle(transport_handle_t t) +{ + if (t && t->_read) { + return t->_parrent_transfer(t); + } + return NULL; +} + esp_err_t transport_destroy(transport_handle_t t) { if (t->scheme) { @@ -199,7 +209,8 @@ esp_err_t transport_set_func(transport_handle_t t, trans_func _close, poll_func _poll_read, poll_func _poll_write, - trans_func _destroy) + trans_func _destroy, + payload_transfer_func _parrent_transport) { if (t == NULL) { return ESP_FAIL; @@ -211,6 +222,7 @@ esp_err_t transport_set_func(transport_handle_t t, t->_poll_read = _poll_read; t->_poll_write = _poll_write; t->_destroy = _destroy; + t->_parrent_transfer = _parrent_transport; return ESP_OK; } @@ -230,3 +242,8 @@ esp_err_t transport_set_default_port(transport_handle_t t, int port) t->port = port; return ESP_OK; } + +transport_handle_t transport_get_handle(transport_handle_t t) +{ + return t; +} \ No newline at end of file diff --git a/components/esp_http_client/lib/transport_ssl.c b/components/tcp_transport/transport_ssl.c similarity index 93% rename from components/esp_http_client/lib/transport_ssl.c rename to components/tcp_transport/transport_ssl.c index bd2260aaa..9ccaf4028 100644 --- a/components/esp_http_client/lib/transport_ssl.c +++ b/components/tcp_transport/transport_ssl.c @@ -23,7 +23,7 @@ #include "transport.h" #include "transport_ssl.h" -#include "http_utils.h" +#include "transport_utils.h" static const char *TAG = "TRANS_SSL"; /** @@ -37,6 +37,8 @@ typedef struct { bool verify_server; } transport_ssl_t; +transport_handle_t transport_get_handle(transport_handle_t t); + static int ssl_close(transport_handle_t t); static int ssl_connect(transport_handle_t t, const char *host, int port, int timeout_ms) @@ -65,7 +67,7 @@ static int ssl_poll_read(transport_handle_t t, int timeout_ms) FD_ZERO(&readset); FD_SET(ssl->tls->sockfd, &readset); struct timeval timeout; - http_utils_ms_to_timeval(timeout_ms, &timeout); + transport_utils_ms_to_timeval(timeout_ms, &timeout); return select(ssl->tls->sockfd + 1, &readset, NULL, NULL, &timeout); } @@ -77,7 +79,7 @@ static int ssl_poll_write(transport_handle_t t, int timeout_ms) FD_ZERO(&writeset); FD_SET(ssl->tls->sockfd, &writeset); struct timeval timeout; - http_utils_ms_to_timeval(timeout_ms, &timeout); + transport_utils_ms_to_timeval(timeout_ms, &timeout); return select(ssl->tls->sockfd + 1, NULL, &writeset, NULL, &timeout); } @@ -147,9 +149,9 @@ transport_handle_t transport_ssl_init() { transport_handle_t t = transport_init(); transport_ssl_t *ssl = calloc(1, sizeof(transport_ssl_t)); - HTTP_MEM_CHECK(TAG, ssl, return NULL); + TRANSPORT_MEM_CHECK(TAG, ssl, return NULL); transport_set_context_data(t, ssl); - transport_set_func(t, ssl_connect, ssl_read, ssl_write, ssl_close, ssl_poll_read, ssl_poll_write, ssl_destroy); + transport_set_func(t, ssl_connect, ssl_read, ssl_write, ssl_close, ssl_poll_read, ssl_poll_write, ssl_destroy, transport_get_handle); return t; } diff --git a/components/esp_http_client/lib/transport_tcp.c b/components/tcp_transport/transport_tcp.c similarity index 92% rename from components/esp_http_client/lib/transport_tcp.c rename to components/tcp_transport/transport_tcp.c index 058fc9fca..2de0dab87 100644 --- a/components/esp_http_client/lib/transport_tcp.c +++ b/components/tcp_transport/transport_tcp.c @@ -23,7 +23,7 @@ #include "esp_system.h" #include "esp_err.h" -#include "http_utils.h" +#include "transport_utils.h" #include "transport.h" static const char *TAG = "TRANS_TCP"; @@ -32,6 +32,8 @@ typedef struct { int sock; } transport_tcp_t; +transport_handle_t transport_get_handle(transport_handle_t t); + static int resolve_dns(const char *host, struct sockaddr_in *ip) { struct hostent *he; @@ -74,7 +76,7 @@ static int tcp_connect(transport_handle_t t, const char *host, int port, int tim remote_ip.sin_family = AF_INET; remote_ip.sin_port = htons(port); - http_utils_ms_to_timeval(timeout_ms, &tv); + transport_utils_ms_to_timeval(timeout_ms, &tv); setsockopt(tcp->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); @@ -119,7 +121,7 @@ static int tcp_poll_read(transport_handle_t t, int timeout_ms) FD_ZERO(&readset); FD_SET(tcp->sock, &readset); struct timeval timeout; - http_utils_ms_to_timeval(timeout_ms, &timeout); + transport_utils_ms_to_timeval(timeout_ms, &timeout); return select(tcp->sock + 1, &readset, NULL, NULL, &timeout); } @@ -130,7 +132,7 @@ static int tcp_poll_write(transport_handle_t t, int timeout_ms) FD_ZERO(&writeset); FD_SET(tcp->sock, &writeset); struct timeval timeout; - http_utils_ms_to_timeval(timeout_ms, &timeout); + transport_utils_ms_to_timeval(timeout_ms, &timeout); return select(tcp->sock + 1, NULL, &writeset, NULL, &timeout); } @@ -157,9 +159,9 @@ transport_handle_t transport_tcp_init() { transport_handle_t t = transport_init(); transport_tcp_t *tcp = calloc(1, sizeof(transport_tcp_t)); - HTTP_MEM_CHECK(TAG, tcp, return NULL); + TRANSPORT_MEM_CHECK(TAG, tcp, return NULL); tcp->sock = -1; - transport_set_func(t, tcp_connect, tcp_read, tcp_write, tcp_close, tcp_poll_read, tcp_poll_write, tcp_destroy); + transport_set_func(t, tcp_connect, tcp_read, tcp_write, tcp_close, tcp_poll_read, tcp_poll_write, tcp_destroy, transport_get_handle); transport_set_context_data(t, tcp); return t; diff --git a/components/tcp_transport/transport_utils.c b/components/tcp_transport/transport_utils.c new file mode 100644 index 000000000..95174cf59 --- /dev/null +++ b/components/tcp_transport/transport_utils.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include + +#include "transport_utils.h" + +void transport_utils_ms_to_timeval(int timeout_ms, struct timeval *tv) +{ + tv->tv_sec = timeout_ms / 1000; + tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000; +} \ No newline at end of file diff --git a/docs/Doxyfile b/docs/Doxyfile index 876762be1..1db49e60c 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -92,6 +92,8 @@ INPUT = \ ## ## ESP-TLS ../../components/esp-tls/esp_tls.h \ + ## MQTT + ../../components/mqtt/esp-mqtt/include/mqtt_client.h \ ## mDNS ../../components/mdns/include/mdns.h \ ../../components/esp_http_client/include/esp_http_client.h \ diff --git a/docs/en/COPYRIGHT.rst b/docs/en/COPYRIGHT.rst index 2a6504abb..e9aa5a4ed 100644 --- a/docs/en/COPYRIGHT.rst +++ b/docs/en/COPYRIGHT.rst @@ -55,6 +55,8 @@ These third party libraries can be included into the application (firmware) prod * :component:`Asio `, Copyright (c) 2003-2018 Christopher M. Kohlhoff is licensed under the Boost Software License. +* :component:`ESP-MQTT ` MQTT Package (contiki-mqtt) - Copyright (c) 2014, Stephen Robinson, MQTT-ESP - Tuan PM is licensed under Apache License 2.0. + Build Tools ----------- @@ -155,3 +157,4 @@ Copyright (C) 2011, ChaN, all right reserved. .. _Mbed TLS: https://github.com/ARMmbed/mbedtls .. _spiffs: https://github.com/pellepl/spiffs .. _asio: https://github.com/chriskohlhoff/asio +.. _mqtt: https://github.com/espressif/esp-mqtt diff --git a/docs/en/api-reference/protocols/index.rst b/docs/en/api-reference/protocols/index.rst index 55dcf4325..96a5825f4 100644 --- a/docs/en/api-reference/protocols/index.rst +++ b/docs/en/api-reference/protocols/index.rst @@ -9,5 +9,6 @@ Protocols API HTTP Client HTTP Server ASIO + ESP-MQTT Example code for this API section is provided in :example:`protocols` directory of ESP-IDF examples. diff --git a/docs/en/api-reference/protocols/mqtt.rst b/docs/en/api-reference/protocols/mqtt.rst new file mode 100644 index 000000000..83dece2cc --- /dev/null +++ b/docs/en/api-reference/protocols/mqtt.rst @@ -0,0 +1,112 @@ +ESP-MQTT +======== + +Overview +-------- + +ESP-MQTT is an implementation of MQTT protocol client (MQTT is a lightweight publish/subscribe messaging protocol). + + +Features +-------- + * supports MQTT over TCP, SSL with mbedtls, MQTT over Websocket, MQTT over Websocket Secure. + * Easy to setup with URI + * Multiple instances (Multiple clients in one application) + * Support subscribing, publishing, authentication, will messages, keep alive pings and all 3 QoS levels (it should be a fully functional client). + + +Application Example +------------------- + + * :example:`protocols/mqtt/tcp`: MQTT over tcp, defalut port 1883 + * :example:`protocols/mqtt/ssl`: MQTT over tcp, defalut port 8883 + * :example:`protocols/mqtt/ws`: MQTT over Websocket, defalut port 80 + * :example:`protocols/mqtt/wss`: MQTT over Websocket Secure, defalut port 443 + + +Configuration +------------- +URI +^^^ + +- Curently support ``mqtt``, ``mqtts``, ``ws``, ``wss`` schemes +- MQTT over TCP samples: + + - ``mqtt://iot.eclipse.org``: MQTT over TCP, default port 1883: + - ``mqtt://iot.eclipse.org:1884`` MQTT over TCP, port 1884: + - ``mqtt://username:password@iot.eclipse.org:1884`` MQTT over TCP, + port 1884, with username and password + +- MQTT over SSL samples: + + - ``mqtts://iot.eclipse.org``: MQTT over SSL, port 8883 + - ``mqtts://iot.eclipse.org:8884``: MQTT over SSL, port 8884 + +- MQTT over Websocket samples: + + - ``ws://iot.eclipse.org:80/ws`` + +- MQTT over Websocket Secure samples: + + - ``wss://iot.eclipse.org:443/ws`` + +- Minimal configurations: + +.. code:: c + + const esp_mqtt_client_config_t mqtt_cfg = { + .uri = "mqtt://iot.eclipse.org", + .event_handle = mqtt_event_handler, + // .user_context = (void *)your_context + }; + +- If there are any options related to the URI in + ``esp_mqtt_client_config_t``, the option defined by the URI will be + overridden. Sample: + +.. code:: c + + const esp_mqtt_client_config_t mqtt_cfg = { + .uri = "mqtt://iot.eclipse.org:1234", + .event_handle = mqtt_event_handler, + .port = 4567, + }; + //MQTT client will connect to iot.eclipse.org using port 4567 + +SSL +^^^ + +- Get certificate from server, example: ``iot.eclipse.org`` + ``openssl s_client -showcerts -connect iot.eclipse.org:8883 /dev/null|openssl x509 -outform PEM >iot_eclipse_org.pem`` +- Check the sample application: ``examples/mqtt_ssl`` +- Configuration: + +.. code:: cpp + + const esp_mqtt_client_config_t mqtt_cfg = { + .uri = "mqtts://iot.eclipse.org:8883", + .event_handle = mqtt_event_handler, + .cert_pem = (const char *)iot_eclipse_org_pem_start, + }; + +For more options on ``esp_mqtt_client_config_t``, please refer to API reference below + +Change settings in ``menuconfig`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + make menuconfig + -> Component config -> ESP-MQTT Configuration + + +- :envvar:`CONFIG_MQTT_PROTOCOL_311`: Enables 3.1.1 version of MQTT protocol + +- :envvar:`MQTT_TRANSPORT_%TRANSPORT%`: Enables specific MQTT transport layer, such as SSL, WEBSOCKET, WEBSOCKET_SECURE + +- :envvar:`MQTT_CUSTOM_OUTBOX`: Disables default implementation of mqtt_outbox, so a specific implementaion can be supplied + + +API Reference +------------- + +.. include:: /_build/inc/mqtt_client.inc \ No newline at end of file diff --git a/docs/zh_CN/api-reference/protocols/mqtt.rst b/docs/zh_CN/api-reference/protocols/mqtt.rst new file mode 100644 index 000000000..4f5ad7d0c --- /dev/null +++ b/docs/zh_CN/api-reference/protocols/mqtt.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/protocols/mqtt.rst \ No newline at end of file diff --git a/examples/protocols/mqtt/ssl/CMakeLists.txt b/examples/protocols/mqtt/ssl/CMakeLists.txt new file mode 100644 index 000000000..13bdd8c21 --- /dev/null +++ b/examples/protocols/mqtt/ssl/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(mqtt_ssl) + +target_add_binary_data(mqtt_ssl.elf "main/iot_eclipse_org.pem" TEXT) diff --git a/examples/protocols/mqtt/ssl/Makefile b/examples/protocols/mqtt/ssl/Makefile new file mode 100644 index 000000000..bae0d73ba --- /dev/null +++ b/examples/protocols/mqtt/ssl/Makefile @@ -0,0 +1,7 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# +PROJECT_NAME := mqtt_ssl + +include $(IDF_PATH)/make/project.mk diff --git a/examples/protocols/mqtt/ssl/README.md b/examples/protocols/mqtt/ssl/README.md new file mode 100644 index 000000000..3d369bdf9 --- /dev/null +++ b/examples/protocols/mqtt/ssl/README.md @@ -0,0 +1,67 @@ +# ESP-MQTT SSL Sample application + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example connects to the broker iot.eclipse.org using ssl transport and as a demonstration subscribes/unsubscribes and send a message on certain topic. + +It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker. + +## How to use example + +### Hardware Required + +This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet. + +### Configure the project + +``` +make menuconfig +``` + +* Set serial port under Serial Flasher Options. + +* Set ssid and password for the board to connect to AP. + +Note how to create a PEM certificate for iot.eclipse.org: +``` +openssl s_client -showcerts -connect iot.eclipse.org:8883 /dev/null|openssl x509 -outform PEM >iot_eclipse_org.pem +``` + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +make -j4 flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +I (3714) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2 +I (3714) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (3964) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000 +I (4164) MQTTS_EXAMPLE: MQTT_EVENT_CONNECTED +I (4174) MQTTS_EXAMPLE: sent publish successful, msg_id=41464 +I (4174) MQTTS_EXAMPLE: sent subscribe successful, msg_id=17886 +I (4174) MQTTS_EXAMPLE: sent subscribe successful, msg_id=42970 +I (4184) MQTTS_EXAMPLE: sent unsubscribe successful, msg_id=50241 +I (4314) MQTTS_EXAMPLE: MQTT_EVENT_PUBLISHED, msg_id=41464 +I (4484) MQTTS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=17886 +I (4484) MQTTS_EXAMPLE: sent publish successful, msg_id=0 +I (4684) MQTTS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=42970 +I (4684) MQTTS_EXAMPLE: sent publish successful, msg_id=0 +I (4884) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19 +I (4884) MQTTS_EXAMPLE: MQTT_EVENT_DATA +TOPIC=/topic/qos0 +DATA=data +I (5194) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19 +I (5194) MQTTS_EXAMPLE: MQTT_EVENT_DATA +TOPIC=/topic/qos0 +DATA=data +``` + diff --git a/examples/protocols/mqtt/ssl/main/CMakeLists.txt b/examples/protocols/mqtt/ssl/main/CMakeLists.txt new file mode 100644 index 000000000..6b0350063 --- /dev/null +++ b/examples/protocols/mqtt/ssl/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "app_main.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/protocols/mqtt/ssl/main/Kconfig.projbuild b/examples/protocols/mqtt/ssl/main/Kconfig.projbuild new file mode 100644 index 000000000..176d8fb33 --- /dev/null +++ b/examples/protocols/mqtt/ssl/main/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu diff --git a/examples/protocols/mqtt/ssl/main/app_main.c b/examples/protocols/mqtt/ssl/main/app_main.c new file mode 100644 index 000000000..83f0b8a30 --- /dev/null +++ b/examples/protocols/mqtt/ssl/main/app_main.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include "esp_wifi.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_event_loop.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" + +#include "lwip/sockets.h" +#include "lwip/dns.h" +#include "lwip/netdb.h" + +#include "esp_log.h" +#include "mqtt_client.h" + +static const char *TAG = "MQTTS_EXAMPLE"; + +static EventGroupHandle_t wifi_event_group; +const static int CONNECTED_BIT = BIT0; + + + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_WIFI_SSID, + .password = CONFIG_WIFI_PASSWORD, + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_LOGI(TAG, "start the WIFI SSID:[%s]", CONFIG_WIFI_SSID); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_LOGI(TAG, "Waiting for wifi"); + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); +} + +extern const uint8_t iot_eclipse_org_pem_start[] asm("_binary_iot_eclipse_org_pem_start"); +extern const uint8_t iot_eclipse_org_pem_end[] asm("_binary_iot_eclipse_org_pem_end"); + +static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event) +{ + esp_mqtt_client_handle_t client = event->client; + int msg_id; + // your_context_t *context = event->context; + switch (event->event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); + ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + break; + } + return ESP_OK; +} + +static void mqtt_app_start(void) +{ + const esp_mqtt_client_config_t mqtt_cfg = { + .uri = "mqtts://iot.eclipse.org:8883", + .event_handle = mqtt_event_handler, + .cert_pem = (const char *)iot_eclipse_org_pem_start, + }; + + ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size()); + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + esp_mqtt_client_start(client); +} + +void app_main() +{ + ESP_LOGI(TAG, "[APP] Startup.."); + ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size()); + ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); + + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE); + esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE); + + nvs_flash_init(); + wifi_init(); + mqtt_app_start(); + +} diff --git a/examples/protocols/mqtt/ssl/main/component.mk b/examples/protocols/mqtt/ssl/main/component.mk new file mode 100644 index 000000000..797c4a1f6 --- /dev/null +++ b/examples/protocols/mqtt/ssl/main/component.mk @@ -0,0 +1 @@ +COMPONENT_EMBED_TXTFILES := iot_eclipse_org.pem diff --git a/examples/protocols/mqtt/ssl/main/iot_eclipse_org.pem b/examples/protocols/mqtt/ssl/main/iot_eclipse_org.pem new file mode 100644 index 000000000..edb593bcf --- /dev/null +++ b/examples/protocols/mqtt/ssl/main/iot_eclipse_org.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/examples/protocols/mqtt/ssl/mqtt_ssl_example_test.py b/examples/protocols/mqtt/ssl/mqtt_ssl_example_test.py new file mode 100644 index 000000000..c5ae31af2 --- /dev/null +++ b/examples/protocols/mqtt/ssl/mqtt_ssl_example_test.py @@ -0,0 +1,132 @@ +import re +import os +import sys +import time +import socket +import imp +import ssl + +use_mqtt_client_sketch = False + +try: + imp.find_module('paho') + import paho.mqtt.client as mqtt + # Make things with supposed existing module +except ImportError: + use_mqtt_client_sketch = True + pass + +global g_recv_topic +global g_recv_data + +g_recv_data="" + +# This is only a workaround for running mqtt client with 'hardcoded' data using plain socket interface +def mqtt_client_sketch(): + global g_recv_topic + global g_recv_data + connect_msg = bytearray([0x10, 0x0c, 00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x04, 0x02, 00, 0x3c, 00, 00]) + send_qos0_msg = bytearray([ 0x30, 0x1a, 0x00, 0x0b, 0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2f, 0x71, 0x6f, 0x73, 0x30, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x73, 0x70, 0x33, 0x32]) + subscribe_qos0 = bytearray([ 0x82, 0x10, 0x00, 0x01, 0x00, 0x0b, 0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2f, 0x71, 0x6f, 0x73, 0x30, 0x00] ) + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.settimeout(30) + cli = ssl.wrap_socket(client) + cli.connect(("iot.eclipse.org", 8883)) + cli.send(connect_msg) + data = cli.recv(1024) + print("Connect ack received {}".format(data)) + cli.send(subscribe_qos0) + data = cli.recv(1024) + print("Subscibe ack received {}".format(data)) + start = time.time() + while (time.time() - start) <= 20: + data = cli.recv(1024) + print("Data received {}".format(data[-17:])) + if data[-15:] == "/topic/qos0data": + g_recv_topic = data[-15:][:11] + g_recv_data = data[-4:] + cli.send(send_qos0_msg) + data = cli.recv(1024) + print("data ack received {}".format(data)) + break + cli.close() + +# The callback for when the client receives a CONNACK response from the server. +def on_connect(client, userdata, flags, rc): + print("Connected with result code "+str(rc)) + client.subscribe("/topic/qos0") + +# The callback for when a PUBLISH message is received from the server. +def on_message(client, userdata, msg): + global g_recv_topic + global g_recv_data + if g_recv_data == "" and msg.payload == "data": + client.publish("/topic/qos0", "data_to_esp32") + g_recv_topic = msg.topic + g_recv_data = msg.payload + print(msg.topic+" "+str(msg.payload)) + +# this is a test case write with tiny-test-fw. +# to run test cases outside tiny-test-fw, +# we need to set environment variable `TEST_FW_PATH`, +# then get and insert `TEST_FW_PATH` to sys path before import FW module +test_fw_path = os.getenv("TEST_FW_PATH") +if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) + +import TinyFW +import IDF + + + + +@IDF.idf_example_test(env_tag="Example_WIFI") +def test_examples_protocol_mqtt_ssl(env, extra_data): + global g_recv_topic + global g_recv_data + """ + steps: | + 1. join AP and connects to ssl broker + 2. Test connects a client to the same broker + 3. Test evaluates python client received correct qos0 message + 4. Test ESP32 client received correct qos0 message + """ + dut1 = env.get_dut("mqtt_ssl", "examples/protocols/mqtt/ssl") + # check and log bin size + binary_file = os.path.join(dut1.app.binary_path, "mqtt_ssl.bin") + bin_size = os.path.getsize(binary_file) + IDF.log_performance("mqtt_ssl_bin_size", "{}KB".format(bin_size//1024)) + IDF.check_performance("mqtt_ssl_size", bin_size//1024) + # 1. start test + dut1.start_app() + # 2. Test connects to a broker + if use_mqtt_client_sketch: + mqtt_client_sketch() + else: + client = mqtt.Client() + client.on_connect = on_connect + client.on_message = on_message + client.tls_set(None, + None, + None, cert_reqs=ssl.CERT_NONE, tls_version=ssl.PROTOCOL_TLSv1, ciphers=None) + client.tls_insecure_set(True) + + print "Connecting..." + client.connect("iot.eclipse.org", 8883, 60) + print "...done" + print "Start Looping..." + start = time.time() + while (time.time() - start) <= 20: + client.loop() + print "...done" + # 3. check the message received back from the server + if g_recv_topic == "/topic/qos0" and g_recv_data == "data" : + print("PASS: Received correct message") + else: + print("Failure!") + raise ValueError('Wrong data received topic: {}, data:{}'.format(g_recv_topic, g_recv_data)) + # 4. check that the esp32 client received data sent by this python client + dut1.expect(re.compile(r"DATA=data_to_esp32"), timeout=30) + +if __name__ == '__main__': + test_examples_protocol_mqtt_ssl() diff --git a/examples/protocols/mqtt/tcp/CMakeLists.txt b/examples/protocols/mqtt/tcp/CMakeLists.txt new file mode 100644 index 000000000..678d787af --- /dev/null +++ b/examples/protocols/mqtt/tcp/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(mqtt_tcp) \ No newline at end of file diff --git a/examples/protocols/mqtt/tcp/Makefile b/examples/protocols/mqtt/tcp/Makefile new file mode 100644 index 000000000..cd53fdbf5 --- /dev/null +++ b/examples/protocols/mqtt/tcp/Makefile @@ -0,0 +1,7 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# +PROJECT_NAME := mqtt_tcp + +include $(IDF_PATH)/make/project.mk diff --git a/examples/protocols/mqtt/tcp/README.md b/examples/protocols/mqtt/tcp/README.md new file mode 100644 index 000000000..2fda24070 --- /dev/null +++ b/examples/protocols/mqtt/tcp/README.md @@ -0,0 +1,61 @@ +# ESP-MQTT sample application +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example connects to the broker URI selected using `make menuconfig` (using mqtt tcp transport) and as a demonstration subscribes/unsubscribes and send a message on certain topic. +Note: If the URI equals `FROM_STDIN` then the broker address is read from stdin upon application startup (used for testing) + +It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker. + +## How to use example + +### Hardware Required + +This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet. + +### Configure the project + +``` +make menuconfig +``` + +* Set serial port under Serial Flasher Options. + +* Set ssid and password for the board to connect to AP. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +make -j4 flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +I (3714) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2 +I (3714) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (3964) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000 +I (4164) MQTT_EXAMPLE: MQTT_EVENT_CONNECTED +I (4174) MQTT_EXAMPLE: sent publish successful, msg_id=41464 +I (4174) MQTT_EXAMPLE: sent subscribe successful, msg_id=17886 +I (4174) MQTT_EXAMPLE: sent subscribe successful, msg_id=42970 +I (4184) MQTT_EXAMPLE: sent unsubscribe successful, msg_id=50241 +I (4314) MQTT_EXAMPLE: MQTT_EVENT_PUBLISHED, msg_id=41464 +I (4484) MQTT_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=17886 +I (4484) MQTT_EXAMPLE: sent publish successful, msg_id=0 +I (4684) MQTT_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=42970 +I (4684) MQTT_EXAMPLE: sent publish successful, msg_id=0 +I (4884) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19 +I (4884) MQTT_EXAMPLE: MQTT_EVENT_DATA +TOPIC=/topic/qos0 +DATA=data +I (5194) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19 +I (5194) MQTT_EXAMPLE: MQTT_EVENT_DATA +TOPIC=/topic/qos0 +DATA=data +``` diff --git a/examples/protocols/mqtt/tcp/main/CMakeLists.txt b/examples/protocols/mqtt/tcp/main/CMakeLists.txt new file mode 100644 index 000000000..6b0350063 --- /dev/null +++ b/examples/protocols/mqtt/tcp/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "app_main.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/protocols/mqtt/tcp/main/Kconfig.projbuild b/examples/protocols/mqtt/tcp/main/Kconfig.projbuild new file mode 100644 index 000000000..bb4194dcb --- /dev/null +++ b/examples/protocols/mqtt/tcp/main/Kconfig.projbuild @@ -0,0 +1,25 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +config BROKER_URL + string "Broker URL" + default "mqtt://iot.eclipse.org" + help + URL of the broker to connect to + +config BROKER_URL_FROM_STDIN + bool + default y if BROKER_URL = "FROM_STDIN" + +endmenu diff --git a/examples/protocols/mqtt/tcp/main/app_main.c b/examples/protocols/mqtt/tcp/main/app_main.c new file mode 100644 index 000000000..0d294bd46 --- /dev/null +++ b/examples/protocols/mqtt/tcp/main/app_main.c @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include "esp_wifi.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_event_loop.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" + +#include "lwip/sockets.h" +#include "lwip/dns.h" +#include "lwip/netdb.h" + +#include "esp_log.h" +#include "mqtt_client.h" + +static const char *TAG = "MQTT_EXAMPLE"; + +static EventGroupHandle_t wifi_event_group; +const static int CONNECTED_BIT = BIT0; + + +static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event) +{ + esp_mqtt_client_handle_t client = event->client; + int msg_id; + // your_context_t *context = event->context; + switch (event->event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); + ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + break; + } + return ESP_OK; +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_WIFI_SSID, + .password = CONFIG_WIFI_PASSWORD, + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_LOGI(TAG, "start the WIFI SSID:[%s]", CONFIG_WIFI_SSID); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_LOGI(TAG, "Waiting for wifi"); + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); +} + +static void mqtt_app_start(void) +{ + esp_mqtt_client_config_t mqtt_cfg = { + .uri = CONFIG_BROKER_URL, + .event_handle = mqtt_event_handler, + // .user_context = (void *)your_context + }; + +#if CONFIG_BROKER_URL_FROM_STDIN + char line[128]; + + if (strcmp(mqtt_cfg.uri, "FROM_STDIN") == 0) { + int count = 0; + printf("Please enter url of mqtt broker\n"); + while (count < 128) { + int c = fgetc(stdin); + if (c == '\n') { + line[count] = '\0'; + break; + } else if (c > 0 && c < 127) { + line[count] = c; + ++count; + } + vTaskDelay(10 / portTICK_PERIOD_MS); + } + mqtt_cfg.uri = line; + printf("Broker url: %s\n", line); + } else { + ESP_LOGE(TAG, "Configuration mismatch: wrong broker url"); + abort(); + } +#endif /* CONFIG_BROKER_URL_FROM_STDIN */ + + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + esp_mqtt_client_start(client); +} + +void app_main() +{ + ESP_LOGI(TAG, "[APP] Startup.."); + ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size()); + ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); + + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE); + esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE); + + nvs_flash_init(); + wifi_init(); + mqtt_app_start(); +} diff --git a/examples/protocols/mqtt/tcp/main/component.mk b/examples/protocols/mqtt/tcp/main/component.mk new file mode 100644 index 000000000..e69de29bb diff --git a/examples/protocols/mqtt/tcp/mqtt_tcp_example_test.py b/examples/protocols/mqtt/tcp/mqtt_tcp_example_test.py new file mode 100644 index 000000000..c1bf42d9f --- /dev/null +++ b/examples/protocols/mqtt/tcp/mqtt_tcp_example_test.py @@ -0,0 +1,101 @@ +import re +import os +import sys +from socket import * +from threading import Thread +import time + +global msgid + +def get_my_ip(): + s1 = socket(AF_INET, SOCK_DGRAM) + s1.connect(("8.8.8.8", 80)) + my_ip = s1.getsockname()[0] + s1.close() + return my_ip + +def mqqt_server_sketch(my_ip, port): + global msgid + print("Starting the server on {}".format(my_ip)) + s=socket(AF_INET, SOCK_STREAM) + s.settimeout(60) + s.bind((my_ip, port)) + s.listen(1) + q,addr=s.accept() + q.settimeout(30) + print("connection accepted") + # q.send(g_msg_to_client) + data = q.recv(1024) + # check if received initial empty message + print("received from client {}".format(data)) + data = bytearray([0x20, 0x02, 0x00, 0x00]) + q.send(data) + # try to receive qos1 + data = q.recv(1024) + msgid = ord(data[15])*256+ord(data[16]) + print("received from client {}, msgid: {}".format(data, msgid)) + data = bytearray([0x40, 0x02, data[15], data[16]]) + q.send(data) + time.sleep(5) + s.close() + print("server closed") + +# this is a test case write with tiny-test-fw. +# to run test cases outside tiny-test-fw, +# we need to set environment variable `TEST_FW_PATH`, +# then get and insert `TEST_FW_PATH` to sys path before import FW module +test_fw_path = os.getenv("TEST_FW_PATH") +if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) + +import TinyFW +import IDF + + + + +@IDF.idf_example_test(env_tag="Example_WIFI") +def test_examples_protocol_mqtt_qos1(env, extra_data): + global msgid + """ + steps: (QoS1: Happy flow) + 1. start the broker broker (with correctly sending ACK) + 2. DUT client connects to a broker and publishes qos1 message + 3. Test evaluates that qos1 message is queued and removed from queued after ACK received + 4. Test the broker received the same message id evaluated in step 3 + """ + dut1 = env.get_dut("mqtt_tcp", "examples/protocols/mqtt/tcp") + # check and log bin size + binary_file = os.path.join(dut1.app.binary_path, "mqtt_tcp.bin") + bin_size = os.path.getsize(binary_file) + IDF.log_performance("mqtt_tcp_bin_size", "{}KB".format(bin_size//1024)) + IDF.check_performance("mqtt_tcp_size", bin_size//1024) + # 1. start mqtt broker sketch + host_ip = get_my_ip() + thread1 = Thread(target = mqqt_server_sketch, args = (host_ip,1883)) + thread1.start() + # 2. start the dut test and wait till client gets IP address + dut1.start_app() + # waiting for getting the IP address + data = dut1.expect(re.compile(r" sta ip: ([^,]+),"), timeout=30) + # time.sleep(15) + print ("writing to device: {}".format("mqtt://" + host_ip + "\n")) + dut1.write("mqtt://" + host_ip + "\n") + thread1.join() + print ("Message id received from server: {}".format(msgid)) + # 3. check the message id was enqueued and then deleted + msgid_enqueued = dut1.expect(re.compile(r"OUTBOX: ENQUEUE msgid=([0-9]+)"), timeout=30) + # expect_txt="OUTBOX: ENQUEUE msgid=" + str(msgid) + # dut1.expect(re.compile(expect_txt), timeout=30) + msgid_deleted = dut1.expect(re.compile(r"OUTBOX: DELETED msgid=([0-9]+)"), timeout=30) + # expect_txt="OUTBOX: DELETED msgid=" + str(msgid) + # dut1.expect(re.compile(expect_txt), timeout=30) + # 4. check the msgid of received data are the same as that of enqueued and deleted from outbox + if (msgid_enqueued[0] == str(msgid) and msgid_deleted[0] == str(msgid)): + print("PASS: Received correct msg id") + else: + print("Failure!") + raise ValueError('Mismatch of msgid: received: {}, enqueued {}, deleted {}'.format(msgid, msgid_enqueued, msgid_deleted)) + +if __name__ == '__main__': + test_examples_protocol_mqtt_qos1() diff --git a/examples/protocols/mqtt/tcp/sdkconfig b/examples/protocols/mqtt/tcp/sdkconfig new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/protocols/mqtt/tcp/sdkconfig @@ -0,0 +1 @@ + diff --git a/examples/protocols/mqtt/tcp/sdkconfig.defaults b/examples/protocols/mqtt/tcp/sdkconfig.defaults new file mode 100644 index 000000000..51c813913 --- /dev/null +++ b/examples/protocols/mqtt/tcp/sdkconfig.defaults @@ -0,0 +1,7 @@ +CONFIG_BROKER_URL="FROM_STDIN" +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR= +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO= +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= diff --git a/examples/protocols/mqtt/ws/CMakeLists.txt b/examples/protocols/mqtt/ws/CMakeLists.txt new file mode 100644 index 000000000..58a2135da --- /dev/null +++ b/examples/protocols/mqtt/ws/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(mqtt_websocket) \ No newline at end of file diff --git a/examples/protocols/mqtt/ws/Makefile b/examples/protocols/mqtt/ws/Makefile new file mode 100644 index 000000000..668719bf1 --- /dev/null +++ b/examples/protocols/mqtt/ws/Makefile @@ -0,0 +1,7 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# +PROJECT_NAME := mqtt_websocket + +include $(IDF_PATH)/make/project.mk diff --git a/examples/protocols/mqtt/ws/README.md b/examples/protocols/mqtt/ws/README.md new file mode 100644 index 000000000..619519d92 --- /dev/null +++ b/examples/protocols/mqtt/ws/README.md @@ -0,0 +1,62 @@ +# ESP-MQTT MQTT over Websocket + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example connects to the broker iot.eclipse.org over web sockets as a demonstration subscribes/unsubscribes and send a message on certain topic. + +It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker. + +## How to use example + +### Hardware Required + +This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet. + +### Configure the project + +``` +make menuconfig +``` + +* Set serial port under Serial Flasher Options. + +* Set ssid and password for the board to connect to AP. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +make -j4 flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +I (3714) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2 +I (3714) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (3964) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000 +I (4164) MQTTWS_EXAMPLE: MQTT_EVENT_CONNECTED +I (4174) MQTTWS_EXAMPLE: sent publish successful, msg_id=41464 +I (4174) MQTTWS_EXAMPLE: sent subscribe successful, msg_id=17886 +I (4174) MQTTWS_EXAMPLE: sent subscribe successful, msg_id=42970 +I (4184) MQTTWS_EXAMPLE: sent unsubscribe successful, msg_id=50241 +I (4314) MQTTWS_EXAMPLE: MQTT_EVENT_PUBLISHED, msg_id=41464 +I (4484) MQTTWS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=17886 +I (4484) MQTTWS_EXAMPLE: sent publish successful, msg_id=0 +I (4684) MQTTWS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=42970 +I (4684) MQTTWS_EXAMPLE: sent publish successful, msg_id=0 +I (4884) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19 +I (4884) MQTTWS_EXAMPLE: MQTT_EVENT_DATA +TOPIC=/topic/qos0 +DATA=data +I (5194) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19 +I (5194) MQTTWS_EXAMPLE: MQTT_EVENT_DATA +TOPIC=/topic/qos0 +DATA=data +``` + diff --git a/examples/protocols/mqtt/ws/main/CMakeLists.txt b/examples/protocols/mqtt/ws/main/CMakeLists.txt new file mode 100644 index 000000000..6b0350063 --- /dev/null +++ b/examples/protocols/mqtt/ws/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "app_main.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/protocols/mqtt/ws/main/Kconfig.projbuild b/examples/protocols/mqtt/ws/main/Kconfig.projbuild new file mode 100644 index 000000000..176d8fb33 --- /dev/null +++ b/examples/protocols/mqtt/ws/main/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu diff --git a/examples/protocols/mqtt/ws/main/app_main.c b/examples/protocols/mqtt/ws/main/app_main.c new file mode 100644 index 000000000..cdb0f0ab5 --- /dev/null +++ b/examples/protocols/mqtt/ws/main/app_main.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include "esp_wifi.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_event_loop.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" + +#include "lwip/sockets.h" +#include "lwip/dns.h" +#include "lwip/netdb.h" + +#include "esp_log.h" +#include "mqtt_client.h" + +static const char *TAG = "MQTTWS_EXAMPLE"; + +static EventGroupHandle_t wifi_event_group; +const static int CONNECTED_BIT = BIT0; + + +static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event) +{ + esp_mqtt_client_handle_t client = event->client; + int msg_id; + // your_context_t *context = event->context; + switch (event->event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); + ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + break; + } + return ESP_OK; +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_WIFI_SSID, + .password = CONFIG_WIFI_PASSWORD, + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_LOGI(TAG, "start the WIFI SSID:[%s]", CONFIG_WIFI_SSID); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_LOGI(TAG, "Waiting for wifi"); + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); +} + +static void mqtt_app_start(void) +{ + const esp_mqtt_client_config_t mqtt_cfg = { + .uri = "ws://iot.eclipse.org:80/ws", + .event_handle = mqtt_event_handler, + // .user_context = (void *)your_context + }; + + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + esp_mqtt_client_start(client); +} + +void app_main() +{ + ESP_LOGI(TAG, "[APP] Startup.."); + ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size()); + ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); + + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_WS", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE); + esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE); + + nvs_flash_init(); + wifi_init(); + mqtt_app_start(); +} diff --git a/examples/protocols/mqtt/ws/main/component.mk b/examples/protocols/mqtt/ws/main/component.mk new file mode 100644 index 000000000..e69de29bb diff --git a/examples/protocols/mqtt/ws/mqtt_ws_example_test.py b/examples/protocols/mqtt/ws/mqtt_ws_example_test.py new file mode 100644 index 000000000..f00e4dd0b --- /dev/null +++ b/examples/protocols/mqtt/ws/mqtt_ws_example_test.py @@ -0,0 +1,128 @@ +import re +import os +import sys +import time +import socket +import imp + +use_mqtt_client_sketch = False + +try: + imp.find_module('paho') + import paho.mqtt.client as mqtt + # Make things with supposed existing module +except ImportError: + use_mqtt_client_sketch = True + pass + +global g_recv_topic +global g_recv_data + +g_recv_data="" + +# This is only a workaround for running mqtt client with 'hardcoded' data using plain socket interface +def mqtt_client_sketch(): + global g_recv_topic + global g_recv_data + http_connect = bytearray([ 0x47, 0x45, 0x54, 0x20, 0x2f, 0x77, 0x73, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x69, 0x6f, 0x74, 0x2e, 0x65, 0x63, 0x6c, 0x69, 0x70, 0x73, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x3a, 0x38, 0x30, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3a, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x33, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x3a, 0x20, 0x6d, 0x71, 0x74, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x4b, 0x65, 0x79, 0x3a, 0x20, 0x6c, 0x35, 0x61, 0x50, 0x41, 0x64, 0x6d, 0x4a, 0x52, 0x65, 0x32, 0x79, 0x55, 0x42, 0x79, 0x68, 0x37, 0x35, 0x72, 0x58, 0x68, 0x51, 0x3d, 0x3d, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x69, 0x6f, 0x74, 0x2e, 0x65, 0x63, 0x6c, 0x69, 0x70, 0x73, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x3a, 0x38, 0x30, 0x0d, 0x0a, 0x0d, 0x0a]) + connect_msg = bytearray([0x82, 0x8e, 0x82, 0x1a, 0xe6, 0x22, 0x92, 0x16, 0xe6, 0x26, 0xcf, 0x4b, 0xb2, 0x76, 0x86, 0x18, 0xe6, 0x1e, 0x82, 0x1a]) + send_qos0_msg = bytearray([ 0x82, 0x9c, 0x44, 0x78, 0xdf, 0x8e, 0x74, 0x62, 0xdf, 0x85, 0x6b, 0x0c, 0xb0, 0xfe, 0x2d, 0x1b, 0xf0, 0xff, 0x2b, 0x0b, 0xef, 0xea, 0x25, 0x0c, 0xbe, 0xd1, 0x30, 0x17, 0x80, 0xeb, 0x37, 0x08, 0xec, 0xbc ]) + subscribe_qos0 = bytearray([ 0x82, 0x92, 0x8e, 0x31, 0x8c, 0x4a, 0x0c, 0x21, 0x8c, 0x4b, 0x8e, 0x3a, 0xa3, 0x3e, 0xe1, 0x41, 0xe5, 0x29, 0xa1, 0x40, 0xe3, 0x39, 0xbe, 0x31] ) + cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + cli.settimeout(30) + cli.connect(("iot.eclipse.org", 80)) + cli.send(http_connect) + cli.send(connect_msg) + data = cli.recv(1024) + print("Connect ack received {}".format(data)) + cli.send(subscribe_qos0) + data = cli.recv(1024) + print("Subscibe ack received {}".format(data)) + start = time.time() + while (time.time() - start) <= 20: + data = cli.recv(1024) + print("Data received {}".format(data[-17:])) + if data[-15:] == "/topic/qos0data": + g_recv_topic = data[-15:][:11] + g_recv_data = data[-4:] + cli.send(send_qos0_msg) + data = cli.recv(1024) + print("data ack received {}".format(data)) + break + cli.close() + +# The callback for when the client receives a CONNACK response from the server. +def on_connect(client, userdata, flags, rc): + print("Connected with result code "+str(rc)) + client.subscribe("/topic/qos0") + +# The callback for when a PUBLISH message is received from the server. +def on_message(client, userdata, msg): + global g_recv_topic + global g_recv_data + if g_recv_data == "" and msg.payload == "data": + client.publish("/topic/qos0", "data_to_esp32") + g_recv_topic = msg.topic + g_recv_data = msg.payload + print(msg.topic+" "+str(msg.payload)) + +# this is a test case write with tiny-test-fw. +# to run test cases outside tiny-test-fw, +# we need to set environment variable `TEST_FW_PATH`, +# then get and insert `TEST_FW_PATH` to sys path before import FW module +test_fw_path = os.getenv("TEST_FW_PATH") +if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) + +import TinyFW +import IDF + + + + +@IDF.idf_example_test(env_tag="Example_WIFI") +def test_examples_protocol_mqtt_ws(env, extra_data): + global g_recv_topic + global g_recv_data + """ + steps: | + 1. join AP and connects to ws broker + 2. Test connects a client to the same broker + 3. Test evaluates it received correct qos0 message + 4. Test ESP32 client received correct qos0 message + """ + dut1 = env.get_dut("mqtt_websocket", "examples/protocols/mqtt/ws") + # check and log bin size + binary_file = os.path.join(dut1.app.binary_path, "mqtt_websocket.bin") + bin_size = os.path.getsize(binary_file) + IDF.log_performance("mqtt_websocket_bin_size", "{}KB".format(bin_size//1024)) + IDF.check_performance("mqtt_websocket_size", bin_size//1024) + # 1. start test + dut1.start_app() + # 2. Test connects to a broker + if use_mqtt_client_sketch: + mqtt_client_sketch() + else: + client = mqtt.Client(transport="websockets") + client.on_connect = on_connect + client.on_message = on_message + client.ws_set_options(path="/ws", headers=None) + print "Connecting..." + client.connect("iot.eclipse.org", 80, 60) + print "...done" + print "Start Looping..." + start = time.time() + while (time.time() - start) <= 20: + client.loop() + print "...done" + # 3. check the message received back from the server + if g_recv_topic == "/topic/qos0" and g_recv_data == "data" : + print("PASS: Received correct message") + else: + print("Failure!") + raise ValueError('Wrong data received topic: {}, data:{}'.format(g_recv_topic, g_recv_data)) + # 4. check that the esp32 client received data sent by this python client + dut1.expect(re.compile(r"DATA=data_to_esp32"), timeout=30) + +if __name__ == '__main__': + test_examples_protocol_mqtt_ws() diff --git a/examples/protocols/mqtt/wss/CMakeLists.txt b/examples/protocols/mqtt/wss/CMakeLists.txt new file mode 100644 index 000000000..7ba5e6295 --- /dev/null +++ b/examples/protocols/mqtt/wss/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following four lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(mqtt_websocket_secure) + +target_add_binary_data(mqtt_websocket_secure.elf "main/iot_eclipse_org.pem" TEXT) diff --git a/examples/protocols/mqtt/wss/Makefile b/examples/protocols/mqtt/wss/Makefile new file mode 100644 index 000000000..27047d049 --- /dev/null +++ b/examples/protocols/mqtt/wss/Makefile @@ -0,0 +1,7 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# +PROJECT_NAME := mqtt_websocket_secure + +include $(IDF_PATH)/make/project.mk diff --git a/examples/protocols/mqtt/wss/README.md b/examples/protocols/mqtt/wss/README.md new file mode 100644 index 000000000..43d829ccb --- /dev/null +++ b/examples/protocols/mqtt/wss/README.md @@ -0,0 +1,69 @@ +# ESP-MQTT MQTT over WSS Sample application +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example connects to the broker iot.eclipse.org over secure websockets and as a demonstration subscribes/unsubscribes and send a message on certain topic. + +It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker. + +## How to use example + +### Hardware Required + +This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet. + +### Configure the project + +``` +make menuconfig +``` + +* Set serial port under Serial Flasher Options. + +* Set ssid and password for the board to connect to AP. + +Note how to create a PEM certificate for iot.eclipse.org: + +``` +openssl s_client -showcerts -connect iot.eclipse.org:8883 /dev/null|openssl x509 -outform PEM >iot_eclipse_org.pem +``` + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +make -j4 flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +I (3714) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2 +I (3714) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (3964) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000 +I (4164) MQTTWSS_EXAMPLE: MQTT_EVENT_CONNECTED +I (4174) MQTTWSS_EXAMPLE: sent publish successful, msg_id=41464 +I (4174) MQTTWSS_EXAMPLE: sent subscribe successful, msg_id=17886 +I (4174) MQTTWSS_EXAMPLE: sent subscribe successful, msg_id=42970 +I (4184) MQTTWSS_EXAMPLE: sent unsubscribe successful, msg_id=50241 +I (4314) MQTTWSS_EXAMPLE: MQTT_EVENT_PUBLISHED, msg_id=41464 +I (4484) MQTTWSS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=17886 +I (4484) MQTTWSS_EXAMPLE: sent publish successful, msg_id=0 +I (4684) MQTTWSS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=42970 +I (4684) MQTTWSS_EXAMPLE: sent publish successful, msg_id=0 +I (4884) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19 +I (4884) MQTTWSS_EXAMPLE: MQTT_EVENT_DATA +TOPIC=/topic/qos0 +DATA=data +I (5194) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19 +I (5194) MQTTWSS_EXAMPLE: MQTT_EVENT_DATA +TOPIC=/topic/qos0 +DATA=data +``` + + + diff --git a/examples/protocols/mqtt/wss/main/CMakeLists.txt b/examples/protocols/mqtt/wss/main/CMakeLists.txt new file mode 100644 index 000000000..6b0350063 --- /dev/null +++ b/examples/protocols/mqtt/wss/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "app_main.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() diff --git a/examples/protocols/mqtt/wss/main/Kconfig.projbuild b/examples/protocols/mqtt/wss/main/Kconfig.projbuild new file mode 100644 index 000000000..176d8fb33 --- /dev/null +++ b/examples/protocols/mqtt/wss/main/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu diff --git a/examples/protocols/mqtt/wss/main/app_main.c b/examples/protocols/mqtt/wss/main/app_main.c new file mode 100644 index 000000000..bc1d045b5 --- /dev/null +++ b/examples/protocols/mqtt/wss/main/app_main.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include "esp_wifi.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_event_loop.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" + +#include "lwip/sockets.h" +#include "lwip/dns.h" +#include "lwip/netdb.h" + +#include "esp_log.h" +#include "mqtt_client.h" + +static const char *TAG = "MQTTWSS_EXAMPLE"; + +static EventGroupHandle_t wifi_event_group; +const static int CONNECTED_BIT = BIT0; + + + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_WIFI_SSID, + .password = CONFIG_WIFI_PASSWORD, + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_LOGI(TAG, "start the WIFI SSID:[%s]", CONFIG_WIFI_SSID); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_LOGI(TAG, "Waiting for wifi"); + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); +} + +extern const uint8_t iot_eclipse_org_pem_start[] asm("_binary_iot_eclipse_org_pem_start"); +extern const uint8_t iot_eclipse_org_pem_end[] asm("_binary_iot_eclipse_org_pem_end"); + +static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event) +{ + esp_mqtt_client_handle_t client = event->client; + int msg_id; + // your_context_t *context = event->context; + switch (event->event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); + ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + break; + } + return ESP_OK; +} + +static void mqtt_app_start(void) +{ + const esp_mqtt_client_config_t mqtt_cfg = { + .uri = "wss://iot.eclipse.org:443/ws", + .event_handle = mqtt_event_handler, + .cert_pem = (const char *)iot_eclipse_org_pem_start, + }; + + ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size()); + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + esp_mqtt_client_start(client); +} + +void app_main() +{ + ESP_LOGI(TAG, "[APP] Startup.."); + ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size()); + ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); + + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE); + esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE); + esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE); + + nvs_flash_init(); + wifi_init(); + mqtt_app_start(); +} diff --git a/examples/protocols/mqtt/wss/main/component.mk b/examples/protocols/mqtt/wss/main/component.mk new file mode 100644 index 000000000..797c4a1f6 --- /dev/null +++ b/examples/protocols/mqtt/wss/main/component.mk @@ -0,0 +1 @@ +COMPONENT_EMBED_TXTFILES := iot_eclipse_org.pem diff --git a/examples/protocols/mqtt/wss/main/iot_eclipse_org.pem b/examples/protocols/mqtt/wss/main/iot_eclipse_org.pem new file mode 100644 index 000000000..edb593bcf --- /dev/null +++ b/examples/protocols/mqtt/wss/main/iot_eclipse_org.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/examples/protocols/mqtt/wss/mqtt_wss_example_test.py b/examples/protocols/mqtt/wss/mqtt_wss_example_test.py new file mode 100644 index 000000000..9c153cf4b --- /dev/null +++ b/examples/protocols/mqtt/wss/mqtt_wss_example_test.py @@ -0,0 +1,132 @@ +import re +import os +import sys +import time +import socket +import imp +import ssl + +use_mqtt_client_sketch = False + +try: + imp.find_module('paho') + import paho.mqtt.client as mqtt + # Make things with supposed existing module +except ImportError: + use_mqtt_client_sketch = True + pass + +global g_recv_topic +global g_recv_data + +g_recv_data="" + +# This is only a workaround for running mqtt client with 'hardcoded' data using plain socket interface +def mqtt_client_sketch(): + global g_recv_topic + global g_recv_data + http_connect = bytearray([ 0x47, 0x45, 0x54, 0x20, 0x2f, 0x77, 0x73, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x3a, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x69, 0x6f, 0x74, 0x2e, 0x65, 0x63, 0x6c, 0x69, 0x70, 0x73, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x3a, 0x38, 0x30, 0x0d, 0x0a, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x3a, 0x20, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x31, 0x33, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x3a, 0x20, 0x6d, 0x71, 0x74, 0x74, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x0d, 0x0a, 0x53, 0x65, 0x63, 0x2d, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2d, 0x4b, 0x65, 0x79, 0x3a, 0x20, 0x6c, 0x35, 0x61, 0x50, 0x41, 0x64, 0x6d, 0x4a, 0x52, 0x65, 0x32, 0x79, 0x55, 0x42, 0x79, 0x68, 0x37, 0x35, 0x72, 0x58, 0x68, 0x51, 0x3d, 0x3d, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x69, 0x6f, 0x74, 0x2e, 0x65, 0x63, 0x6c, 0x69, 0x70, 0x73, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x3a, 0x38, 0x30, 0x0d, 0x0a, 0x0d, 0x0a]) + connect_msg = bytearray([0x82, 0x8e, 0x82, 0x1a, 0xe6, 0x22, 0x92, 0x16, 0xe6, 0x26, 0xcf, 0x4b, 0xb2, 0x76, 0x86, 0x18, 0xe6, 0x1e, 0x82, 0x1a]) + send_qos0_msg = bytearray([ 0x82, 0x9c, 0x44, 0x78, 0xdf, 0x8e, 0x74, 0x62, 0xdf, 0x85, 0x6b, 0x0c, 0xb0, 0xfe, 0x2d, 0x1b, 0xf0, 0xff, 0x2b, 0x0b, 0xef, 0xea, 0x25, 0x0c, 0xbe, 0xd1, 0x30, 0x17, 0x80, 0xeb, 0x37, 0x08, 0xec, 0xbc ]) + subscribe_qos0 = bytearray([ 0x82, 0x92, 0x8e, 0x31, 0x8c, 0x4a, 0x0c, 0x21, 0x8c, 0x4b, 0x8e, 0x3a, 0xa3, 0x3e, 0xe1, 0x41, 0xe5, 0x29, 0xa1, 0x40, 0xe3, 0x39, 0xbe, 0x31] ) + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.settimeout(30) + cli = ssl.wrap_socket(client) + cli.connect(("iot.eclipse.org", 443)) + cli.send(http_connect) + cli.send(connect_msg) + data = cli.recv(1024) + print("Connect ack received {}".format(data)) + cli.send(subscribe_qos0) + data = cli.recv(1024) + print("Subscibe ack received {}".format(data)) + start = time.time() + while (time.time() - start) <= 20: + data = cli.recv(1024) + print("Data received {}".format(data[-17:])) + if data[-15:] == "/topic/qos0data": + g_recv_topic = data[-15:][:11] + g_recv_data = data[-4:] + cli.send(send_qos0_msg) + data = cli.recv(1024) + print("data ack received {}".format(data)) + break + cli.close() + +# The callback for when the client receives a CONNACK response from the server. +def on_connect(client, userdata, flags, rc): + print("Connected with result code "+str(rc)) + client.subscribe("/topic/qos0") + +# The callback for when a PUBLISH message is received from the server. +def on_message(client, userdata, msg): + global g_recv_topic + global g_recv_data + if g_recv_data == "" and msg.payload == "data": + client.publish("/topic/qos0", "data_to_esp32") + g_recv_topic = msg.topic + g_recv_data = msg.payload + print(msg.topic+" "+str(msg.payload)) + +# this is a test case write with tiny-test-fw. +# to run test cases outside tiny-test-fw, +# we need to set environment variable `TEST_FW_PATH`, +# then get and insert `TEST_FW_PATH` to sys path before import FW module +test_fw_path = os.getenv("TEST_FW_PATH") +if test_fw_path and test_fw_path not in sys.path: + sys.path.insert(0, test_fw_path) + +import TinyFW +import IDF + + + + +@IDF.idf_example_test(env_tag="Example_WIFI") +def test_examples_protocol_mqtt_wss(env, extra_data): + global g_recv_topic + global g_recv_data + """ + steps: | + 1. join AP and connects to wss broker + 2. Test connects a client to the same broker + 3. Test evaluates it received correct qos0 message + 4. Test ESP32 client received correct qos0 message + """ + dut1 = env.get_dut("mqtt_websocket_secure", "examples/protocols/mqtt/wss") + # check and log bin size + binary_file = os.path.join(dut1.app.binary_path, "mqtt_websocket_secure.bin") + bin_size = os.path.getsize(binary_file) + IDF.log_performance("mqtt_websocket_secure_bin_size", "{}KB".format(bin_size//1024)) + IDF.check_performance("mqtt_websocket_secure_size", bin_size//1024) + # 1. start test + dut1.start_app() + # 2. Test connects to a broker + if use_mqtt_client_sketch: + mqtt_client_sketch() + else: + client = mqtt.Client(transport="websockets") + client.on_connect = on_connect + client.on_message = on_message + client.tls_set(None, + None, + None, cert_reqs=ssl.CERT_NONE, tls_version=ssl.PROTOCOL_TLSv1, ciphers=None) + print "Connecting..." + client.connect("iot.eclipse.org", 443, 60) + print "...done" + print "Start Looping..." + start = time.time() + while (time.time() - start) <= 20: + client.loop() + print "...done" + # 3. check the message received back from the server + if g_recv_topic == "/topic/qos0" and g_recv_data == "data" : + print("PASS: Received correct message") + else: + print("Failure!") + raise ValueError('Wrong data received topic: {}, data:{}'.format(g_recv_topic, g_recv_data)) + # 4. check that the esp32 client received data sent by this python client + dut1.expect(re.compile(r"DATA=data_to_esp32"), timeout=30) + +if __name__ == '__main__': + test_examples_protocol_mqtt_wss() diff --git a/tools/ci/mirror-list.txt b/tools/ci/mirror-list.txt index f45d0f76e..621a03b4c 100644 --- a/tools/ci/mirror-list.txt +++ b/tools/ci/mirror-list.txt @@ -14,3 +14,4 @@ components/asio/asio @GENERAL_MIRROR_SERVER@/idf/ components/lwip/lwip @GENERAL_MIRROR_SERVER@/idf/esp-lwip.git third-party/mruby @GENERAL_MIRROR_SERVER@/idf/mruby.git ALLOW_TO_SYNC_FROM_PUBLIC third-party/neverbleed @GENERAL_MIRROR_SERVER@/idf/neverbleed.git ALLOW_TO_SYNC_FROM_PUBLIC +components/mqtt/esp-mqtt @GENERAL_MIRROR_SERVER@/idf/esp-mqtt.git ALLOW_TO_SYNC_FROM_PUBLIC