diff --git a/src/cpp/sensor_monitor/esp32/Makefile b/src/cpp/sensor_monitor/esp32/Makefile new file mode 100644 index 000000000..35c04008a --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/Makefile @@ -0,0 +1,4 @@ +PROJECT_NAME := hardware_lib + +export IDF_PATH := /usr/share/esp/esp-idf +include $(IDF_PATH)/make/project.mk diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/CMakeLists.txt b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/CMakeLists.txt new file mode 100644 index 000000000..8017b3ef7 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.7) +project(esp-mqtt) + +set(CMAKE_C_STANDARD 99) + +include_directories( + test/build/include + test/xtensa-esp32-elf/xtensa-esp32-elf/include + test/esp-idf/components/esp32/include + test/esp-idf/components/freertos/include + test/esp-idf/components/log/include + test/esp-idf/components/tcpip_adapter/include + test/esp-idf/components/lwip/include/lwip/port + test/esp-idf/components/lwip/include/lwip + lwmqtt/include + .) + +set(SOURCE_FILES + esp_lwmqtt.c + esp_lwmqtt.h + esp_mqtt.c + esp_mqtt.h + test/main/main.c) + +add_library(esp-mqtt ${SOURCE_FILES}) diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/Kconfig b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/Kconfig new file mode 100644 index 000000000..b7c855bef --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/Kconfig @@ -0,0 +1,26 @@ +menu "esp-mqtt" + +config ESP_MQTT_ENABLED + bool "Enable the MQTT component" + select LWIP_SO_RCVBUF + default y + help + This is currently only used to enforce the selection of LWIP_SO_RCVBUF. + Disabling the component does not actually disable something. + +config ESP_MQTT_TASK_STACK_SIZE + int "MQTT background process task stack size" + default 4096 + +config ESP_MQTT_TASK_STACK_PRIORITY + int "MQTT background process task priority" + default 5 + +config ESP_MQTT_EVENT_QUEUE_SIZE + int "MQTT event queue size" + default 64 + help + This value defines the amount of messages that are queued during various + calls and dispatched by the background process. + +endmenu diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/LICENSE.md b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/LICENSE.md new file mode 100644 index 000000000..5179277e5 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Joël Gähwiler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/Makefile b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/Makefile new file mode 100644 index 000000000..e1d81e5e9 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/Makefile @@ -0,0 +1,40 @@ +UNAME := $(shell uname) + +XTENSA_TOOLCHAIN := "xtensa-esp32-elf-linux64-1.22.0-73-ge28a011-5.2.0.tar.gz" + +ifeq ($(UNAME), Darwin) +XTENSA_TOOLCHAIN := "xtensa-esp32-elf-osx-1.22.0-73-ge28a011-5.2.0.tar.gz" +endif + +fmt: + clang-format -i ./*.c ./*.h -style="{BasedOnStyle: Google, ColumnLimit: 120}" + clang-format -i ./test/main/*.c -style="{BasedOnStyle: Google, ColumnLimit: 120}" + +test/xtensa-esp32-elf: + wget https://dl.espressif.com/dl/$(XTENSA_TOOLCHAIN) + cd test; tar -xzf ../$(XTENSA_TOOLCHAIN) + rm *.tar.gz + +test/esp-idf: + git clone --recursive --depth 1 https://github.com/espressif/esp-idf.git test/esp-idf + +defconfig: test/xtensa-esp32-elf test/esp-idf + export PATH=$(shell pwd)/test/xtensa-esp32-elf/bin:$$PATH; cd ./test; make defconfig + +erase: test/xtensa-esp32-elf test/esp-idf + export PATH=$(shell pwd)/test/xtensa-esp32-elf/bin:$$PATH; cd ./test; make erase_flash + +clean: test/xtensa-esp32-elf test/esp-idf + export PATH=$(shell pwd)/test/xtensa-esp32-elf/bin:$$PATH; cd ./test; make clean + +build: test/xtensa-esp32-elf test/esp-idf + export PATH=$(shell pwd)/test/xtensa-esp32-elf/bin:$$PATH; cd ./test; make + +flash: test/xtensa-esp32-elf test/esp-idf + export PATH=$(shell pwd)/test/xtensa-esp32-elf/bin:$$PATH; cd ./test; make flash + +monitor: test/xtensa-esp32-elf test/esp-idf + @clear + miniterm.py /dev/cu.SLAB_USBtoUART 115200 --rts 0 --dtr 0 --raw --exit-char 99 + +run: erase build flash monitor diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/README.md b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/README.md new file mode 100644 index 000000000..df645a58a --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/README.md @@ -0,0 +1,49 @@ +# esp-mqtt + +[![Build Status](https://travis-ci.org/256dpi/esp-mqtt.svg?branch=master)](https://travis-ci.org/256dpi/esp-mqtt) +[![Release](https://img.shields.io/github/release/256dpi/esp-mqtt.svg)](https://github.com/256dpi/esp-mqtt/releases) + +**MQTT component for esp-idf projects based on the [lwmqtt](https://github.com/256dpi/lwmqtt) library** + +This component bundles the lwmqtt client and adds a simple async API similar to other esp networking components. + +## Installation + +You can install the component by adding it as a git submodule: + +```bash +git submodule add https://github.com/256dpi/esp-mqtt.git components/esp-mqtt +git submodule update --init --recursive +``` + +The component will automatically enable the LWIP receive buffers. + +## API + +Initialize the component once by passing the necessary callbacks: + +```c++ +void esp_mqtt_init(esp_mqtt_status_callback_t scb, esp_mqtt_message_callback_t mcb, + size_t buffer_size, int command_timeout); +``` + +When the WiFi connection has been established, start the process: + +```c++ +void esp_mqtt_start(const char *host, int port, const char *client_id, + const char *username, const char *password); +``` + +When the client has connected, interact with the broker: + +```c++ +bool esp_mqtt_subscribe(const char *topic, int qos); +bool esp_mqtt_unsubscribe(const char *topic); +bool esp_mqtt_publish(const char *topic, uint8_t *payload, size_t len, int qos, bool retained); +``` + +If the WiFi connection has been lost, stop the process: + +```c++ +void esp_mqtt_stop(); +``` diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/component.mk b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/component.mk new file mode 100644 index 000000000..22c23c1c1 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/component.mk @@ -0,0 +1,2 @@ +COMPONENT_ADD_INCLUDEDIRS := . lwmqtt/include +COMPONENT_SRCDIRS := . lwmqtt/src diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_lwmqtt.c b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_lwmqtt.c new file mode 100644 index 000000000..3ef7c4f86 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_lwmqtt.c @@ -0,0 +1,150 @@ +#include + +// Some docs about netconn: http://www.ecoscentric.com/ecospro/doc/html/ref/lwip-api-sequential-reference.html. + +#include "esp_lwmqtt.h" + +void esp_lwmqtt_timer_set(lwmqtt_client_t *client, void *ref, uint32_t timeout) { + // cast timer reference + esp_lwmqtt_timer_t *t = (esp_lwmqtt_timer_t *)ref; + + // set deadline + t->deadline = (xTaskGetTickCount() / portTICK_PERIOD_MS) + timeout; +} + +uint32_t esp_lwmqtt_timer_get(lwmqtt_client_t *client, void *ref) { + // cast timer reference + esp_lwmqtt_timer_t *t = (esp_lwmqtt_timer_t *)ref; + + return t->deadline - (xTaskGetTickCount() / portTICK_PERIOD_MS); +} + +lwmqtt_err_t esp_lwmqtt_network_connect(esp_lwmqtt_network_t *network, char *host, int port) { + // disconnect if not already the case + esp_lwmqtt_network_disconnect(network); + + // resolve address + ip_addr_t addr; + err_t err = netconn_gethostbyname_addrtype(host, &addr, NETCONN_DNS_IPV4); + if (err != ERR_OK) { + return LWMQTT_NETWORK_FAILED_CONNECT; + } + + // create new connection + network->conn = netconn_new(NETCONN_TCP); + + // create new socket + err = netconn_connect(network->conn, &addr, (u16_t)port); + if (err != ERR_OK) { + return LWMQTT_NETWORK_FAILED_CONNECT; + } + + return LWMQTT_SUCCESS; +} + +void esp_lwmqtt_network_disconnect(esp_lwmqtt_network_t *network) { + // immediately return if conn is not set + if (network->conn == NULL) { + return; + } + + // delete connection + netconn_delete(network->conn); +} + +lwmqtt_err_t esp_lwmqtt_network_peek(lwmqtt_client_t *client, esp_lwmqtt_network_t *network, size_t *available) { + *available = (size_t)network->conn->recv_avail; + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t esp_lwmqtt_network_read(lwmqtt_client_t *client, void *ref, uint8_t *buffer, size_t len, size_t *read, + uint32_t timeout) { + // cast network reference + esp_lwmqtt_network_t *network = (esp_lwmqtt_network_t *)ref; + + // prepare counter + size_t copied_len = 0; + + // check if some data is left + if (network->rest_len > 0) { + // copy from rest buffer + netbuf_copy_partial(network->rest_buf, buffer, (u16_t)len, + (u16_t)(netbuf_len(network->rest_buf) - network->rest_len)); + + // check if there is still data left + if (network->rest_len > len) { + network->rest_len -= len; + *read += len; + return LWMQTT_SUCCESS; + } + + // delete rest buffer + copied_len = network->rest_len; + netbuf_delete(network->rest_buf); + network->rest_len = 0; + + // immediately return if we have enough + if (copied_len == len) { + *read += len; + return LWMQTT_SUCCESS; + } + } + + // copied_len has the already written amount of data + + // set timeout + netconn_set_recvtimeout(network->conn, timeout); + + // receive data + struct netbuf *buf; + err_t err = netconn_recv(network->conn, &buf); + if (err == ERR_TIMEOUT) { + // return zero if timeout has been reached + *read += copied_len; + return LWMQTT_SUCCESS; + } else if (err != ERR_OK) { + return LWMQTT_NETWORK_FAILED_READ; + } + + // get length + size_t bytes = netbuf_len(buf); + + // copy data + netbuf_copy(buf, buffer + copied_len, len - copied_len); + + // delete buffer and return bytes less or equal to the missing amount + if (copied_len + bytes <= len) { + netbuf_delete(buf); + *read += copied_len + bytes; + return LWMQTT_SUCCESS; + } + + // otherwise save the rest and current offset + network->rest_buf = buf; + network->rest_len = bytes - (len - copied_len); + + // adjust counter + *read += len; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t esp_lwmqtt_network_write(lwmqtt_client_t *client, void *ref, uint8_t *buffer, size_t len, size_t *sent, + uint32_t timeout) { + // cast network reference + esp_lwmqtt_network_t *network = (esp_lwmqtt_network_t *)ref; + + // set timeout + netconn_set_sendtimeout(network->conn, timeout); + + // send data + err_t err = netconn_write(network->conn, buffer, len, NETCONN_COPY); + if (err != ERR_OK) { + return LWMQTT_NETWORK_FAILED_WRITE; + } + + // adjust counter + *sent += len; + + return LWMQTT_SUCCESS; +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_lwmqtt.h b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_lwmqtt.h new file mode 100644 index 000000000..b8763564e --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_lwmqtt.h @@ -0,0 +1,65 @@ +#ifndef ESP_LWMQTT_H +#define ESP_LWMQTT_H + +#include +#include + +/** + * The lwmqtt timer object for the esp platform. + */ +typedef struct { + uint32_t deadline; +} esp_lwmqtt_timer_t; + +/** + * The lwmqtt timer set callback for the esp platform. + */ +void esp_lwmqtt_timer_set(lwmqtt_client_t *client, void *ref, uint32_t timeout); + +/** + * The lwmqtt timer get callback for the esp platform. + */ +uint32_t esp_lwmqtt_timer_get(lwmqtt_client_t *client, void *ref); + +/** + * The lwmqtt network object for the esp platform. + */ +typedef struct { + struct netconn *conn; + struct netbuf *rest_buf; + size_t rest_len; +} esp_lwmqtt_network_t; + +/** + * The initializer for the lwmqtt network object. + */ +#define esp_lwmqtt_default_network \ + { NULL, NULL, 0 } + +/** + * Initiate a connection to the specified remote hose. + */ +lwmqtt_err_t esp_lwmqtt_network_connect(esp_lwmqtt_network_t *network, char *host, int port); + +/** + * Terminate the connection. + */ +void esp_lwmqtt_network_disconnect(esp_lwmqtt_network_t *network); + +/** + * Will set available to the available amount of data in the underlying network buffer. + */ +lwmqtt_err_t esp_lwmqtt_network_peek(lwmqtt_client_t *client, esp_lwmqtt_network_t *network, size_t *available); + +/** + * The lwmqtt network read callback for the esp platform. + */ +lwmqtt_err_t esp_lwmqtt_network_read(lwmqtt_client_t *client, void *ref, uint8_t *buf, size_t len, size_t *read, + uint32_t timeout); +/** + * The lwmqtt network write callback for the esp platform. + */ +lwmqtt_err_t esp_lwmqtt_network_write(lwmqtt_client_t *client, void *ref, uint8_t *buf, size_t len, size_t *sent, + uint32_t timeout); + +#endif // ESP_LWMQTT_H diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_mqtt.c b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_mqtt.c new file mode 100644 index 000000000..9cbd5d508 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_mqtt.c @@ -0,0 +1,443 @@ +#include +#include +#include +#include +#include +#include + +#include "esp_lwmqtt.h" +#include "esp_mqtt.h" + +#define ESP_MQTT_LOG_TAG "esp_mqtt" + +static SemaphoreHandle_t esp_mqtt_mutex = NULL; + +#define ESP_MQTT_LOCK() \ + do { \ + } while (xSemaphoreTake(esp_mqtt_mutex, portMAX_DELAY) != pdPASS) + +#define ESP_MQTT_UNLOCK() xSemaphoreGive(esp_mqtt_mutex) + +static TaskHandle_t esp_mqtt_task = NULL; + +static size_t esp_mqtt_buffer_size; +static uint32_t esp_mqtt_command_timeout; + +static struct { + char *host; + int port; + char *client_id; + char *username; + char *password; +} esp_mqtt_config = {.host = NULL, .port = 1883, .client_id = NULL, .username = NULL, .password = NULL}; + +static bool esp_mqtt_running = false; +static bool esp_mqtt_connected = false; + +static esp_mqtt_status_callback_t esp_mqtt_status_callback = NULL; +static esp_mqtt_message_callback_t esp_mqtt_message_callback = NULL; + +static lwmqtt_client_t esp_mqtt_client; + +static esp_lwmqtt_network_t esp_mqtt_network = esp_lwmqtt_default_network; + +static esp_lwmqtt_timer_t esp_mqtt_timer1, esp_mqtt_timer2; + +static void *esp_mqtt_write_buffer; +static void *esp_mqtt_read_buffer; + +static QueueHandle_t esp_mqtt_event_queue = NULL; + +typedef struct { + lwmqtt_string_t topic; + lwmqtt_message_t message; +} esp_mqtt_event_t; + +void esp_mqtt_init(esp_mqtt_status_callback_t scb, esp_mqtt_message_callback_t mcb, size_t buffer_size, + int command_timeout) { + // set callbacks + esp_mqtt_status_callback = scb; + esp_mqtt_message_callback = mcb; + esp_mqtt_buffer_size = buffer_size; + esp_mqtt_command_timeout = (uint32_t)command_timeout; + + // allocate buffers + esp_mqtt_write_buffer = malloc((size_t)buffer_size); + esp_mqtt_read_buffer = malloc((size_t)buffer_size); + + // create mutex + esp_mqtt_mutex = xSemaphoreCreateMutex(); + + // create queue + esp_mqtt_event_queue = xQueueCreate(CONFIG_ESP_MQTT_EVENT_QUEUE_SIZE, sizeof(esp_mqtt_event_t *)); +} + +static void esp_mqtt_message_handler(lwmqtt_client_t *client, void *ref, lwmqtt_string_t topic, lwmqtt_message_t msg) { + // create message + esp_mqtt_event_t *evt = malloc(sizeof(esp_mqtt_event_t)); + + // copy topic with additional null termination + evt->topic.len = topic.len; + evt->topic.data = malloc((size_t)topic.len + 1); + memcpy(evt->topic.data, topic.data, (size_t)topic.len); + evt->topic.data[topic.len] = 0; + + // copy message with additional null termination + evt->message.retained = msg.retained; + evt->message.qos = msg.qos; + evt->message.payload_len = msg.payload_len; + evt->message.payload = malloc((size_t)msg.payload_len + 1); + memcpy(evt->message.payload, msg.payload, (size_t)msg.payload_len); + evt->message.payload[msg.payload_len] = 0; + + // queue event + if (xQueueSend(esp_mqtt_event_queue, &evt, 0) != pdTRUE) { + ESP_LOGI(ESP_MQTT_LOG_TAG, "xQueueSend: queue is full, dropping message"); + free(evt->topic.data); + free(evt->message.payload); + free(evt); + } +} + +static void esp_mqtt_dispatch_events() { + // prepare event + esp_mqtt_event_t *evt = NULL; + + // receive next event + while (xQueueReceive(esp_mqtt_event_queue, &evt, 0) == pdTRUE) { + // call callback if existing + if (esp_mqtt_message_callback) { + esp_mqtt_message_callback(evt->topic.data, evt->message.payload, evt->message.payload_len); + } + + // free data + free(evt->topic.data); + free(evt->message.payload); + free(evt); + } +} + +static bool esp_mqtt_process_connect() { + // initialize the client + lwmqtt_init(&esp_mqtt_client, esp_mqtt_write_buffer, esp_mqtt_buffer_size, esp_mqtt_read_buffer, + esp_mqtt_buffer_size); + lwmqtt_set_network(&esp_mqtt_client, &esp_mqtt_network, esp_lwmqtt_network_read, esp_lwmqtt_network_write); + lwmqtt_set_timers(&esp_mqtt_client, &esp_mqtt_timer1, &esp_mqtt_timer2, esp_lwmqtt_timer_set, esp_lwmqtt_timer_get); + lwmqtt_set_callback(&esp_mqtt_client, NULL, esp_mqtt_message_handler); + + // attempt network connection + lwmqtt_err_t err = esp_lwmqtt_network_connect(&esp_mqtt_network, esp_mqtt_config.host, esp_mqtt_config.port); + if (err != LWMQTT_SUCCESS) { + ESP_LOGE(ESP_MQTT_LOG_TAG, "esp_lwmqtt_network_connect: %d", err); + return false; + } + + // setup connect data + lwmqtt_options_t options = lwmqtt_default_options; + options.keep_alive = 10; + options.client_id = lwmqtt_string(esp_mqtt_config.client_id); + options.username = lwmqtt_string(esp_mqtt_config.username); + options.password = lwmqtt_string(esp_mqtt_config.password); + + // attempt connection + lwmqtt_return_code_t return_code; + err = lwmqtt_connect(&esp_mqtt_client, options, NULL, &return_code, esp_mqtt_command_timeout); + if (err != LWMQTT_SUCCESS) { + ESP_LOGE(ESP_MQTT_LOG_TAG, "lwmqtt_connect: %d", err); + return false; + } + + return true; +} + +static void esp_mqtt_process(void *p) { + // connection loop + for (;;) { + // log attempt + ESP_LOGI(ESP_MQTT_LOG_TAG, "esp_mqtt_process: begin connection attempt"); + + // acquire mutex + ESP_MQTT_LOCK(); + + // make connection attempt + if (esp_mqtt_process_connect()) { + // log success + ESP_LOGI(ESP_MQTT_LOG_TAG, "esp_mqtt_process: attempt successful"); + + // set local flag + esp_mqtt_connected = true; + + // release mutex + ESP_MQTT_UNLOCK(); + + // exit loop + break; + } + + // release mutex + ESP_MQTT_UNLOCK(); + + // log fail + ESP_LOGI(ESP_MQTT_LOG_TAG, "esp_mqtt_process: attempt failed"); + + // delay loop by 1s and yield to other processes + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + + // call callback if existing + if (esp_mqtt_status_callback) { + esp_mqtt_status_callback(ESP_MQTT_STATUS_CONNECTED); + } + + // yield loop + for (;;) { + // dispatch queued events + esp_mqtt_dispatch_events(); + + // acquire mutex + ESP_MQTT_LOCK(); + + // get the available bytes to be read + size_t available = 0; + lwmqtt_err_t err = esp_lwmqtt_network_peek(&esp_mqtt_client, &esp_mqtt_network, &available); + if (err != LWMQTT_SUCCESS) { + ESP_LOGE(ESP_MQTT_LOG_TAG, "esp_lwmqtt_network_peek: %d", err); + break; + } + + // yield to client if data is available + if (available > 0) { + err = lwmqtt_yield(&esp_mqtt_client, available, esp_mqtt_command_timeout); + if (err != LWMQTT_SUCCESS) { + ESP_LOGE(ESP_MQTT_LOG_TAG, "lwmqtt_yield: %d", err); + break; + } + } + + // do mqtt background work + err = lwmqtt_keep_alive(&esp_mqtt_client, esp_mqtt_command_timeout); + if (err != LWMQTT_SUCCESS) { + ESP_LOGE(ESP_MQTT_LOG_TAG, "lwmqtt_keep_alive: %d", err); + break; + } + + // release mutex + ESP_MQTT_UNLOCK(); + + // dispatch queued events + esp_mqtt_dispatch_events(); + + // yield to other processes + vTaskDelay(1); + } + + // mutex has already been acquired above + + // disconnect network + esp_lwmqtt_network_disconnect(&esp_mqtt_network); + + // set local flags + esp_mqtt_connected = false; + esp_mqtt_running = false; + + // release mutex + ESP_MQTT_UNLOCK(); + + ESP_LOGI(ESP_MQTT_LOG_TAG, "esp_mqtt_process: exit task"); + + // call callback if existing + if (esp_mqtt_status_callback) { + esp_mqtt_status_callback(ESP_MQTT_STATUS_DISCONNECTED); + } + + // delete task + vTaskDelete(NULL); +} + +void esp_mqtt_start(const char *host, int port, const char *client_id, const char *username, const char *password) { + // acquire mutex + ESP_MQTT_LOCK(); + + // check if already running + if (esp_mqtt_running) { + ESP_LOGE(ESP_MQTT_LOG_TAG, "esp_mqtt_start: already running"); + ESP_MQTT_UNLOCK(); + return; + } + + // free host if set + if (esp_mqtt_config.host != NULL) { + free(esp_mqtt_config.host); + esp_mqtt_config.host = NULL; + } + + // free client id if set + if (esp_mqtt_config.client_id != NULL) { + free(esp_mqtt_config.client_id); + esp_mqtt_config.client_id = NULL; + } + + // free username if set + if (esp_mqtt_config.username != NULL) { + free(esp_mqtt_config.username); + esp_mqtt_config.username = NULL; + } + + // free password if set + if (esp_mqtt_config.password != NULL) { + free(esp_mqtt_config.password); + esp_mqtt_config.password = NULL; + } + + // set host if provided + if (host != NULL) { + esp_mqtt_config.host = strdup(host); + } + + // set port + esp_mqtt_config.port = port; + + // set client id if provided + if (client_id != NULL) { + esp_mqtt_config.client_id = strdup(client_id); + } + + // set username if provided + if (username != NULL) { + esp_mqtt_config.username = strdup(username); + } + + // set password if provided + if (password != NULL) { + esp_mqtt_config.password = strdup(password); + } + + // create mqtt thread + ESP_LOGI(ESP_MQTT_LOG_TAG, "esp_mqtt_start: create task"); + xTaskCreatePinnedToCore(esp_mqtt_process, "esp_mqtt", CONFIG_ESP_MQTT_TASK_STACK_SIZE, NULL, + CONFIG_ESP_MQTT_TASK_STACK_PRIORITY, &esp_mqtt_task, 1); + + // set local flag + esp_mqtt_running = true; + + // release mutex + ESP_MQTT_UNLOCK(); +} + +bool esp_mqtt_subscribe(const char *topic, int qos) { + // acquire mutex + ESP_MQTT_LOCK(); + + // check if still connected + if (!esp_mqtt_connected) { + ESP_LOGI(ESP_MQTT_LOG_TAG, "esp_mqtt_subscribe: not connected"); + ESP_MQTT_UNLOCK(); + return false; + } + + // subscribe to topic + lwmqtt_err_t err = + lwmqtt_subscribe_one(&esp_mqtt_client, lwmqtt_string(topic), (lwmqtt_qos_t)qos, esp_mqtt_command_timeout); + if (err != LWMQTT_SUCCESS) { + ESP_LOGE(ESP_MQTT_LOG_TAG, "lwmqtt_subscribe_one: %d", err); + ESP_MQTT_UNLOCK(); + return false; + } + + // release mutex + ESP_MQTT_UNLOCK(); + + return true; +} + +bool esp_mqtt_unsubscribe(const char *topic) { + // acquire mutex + ESP_MQTT_LOCK(); + + // check if still connected + if (!esp_mqtt_connected) { + ESP_LOGI(ESP_MQTT_LOG_TAG, "esp_mqtt_unsubscribe: not connected"); + ESP_MQTT_UNLOCK(); + return false; + } + + // unsubscribe from topic + lwmqtt_err_t err = lwmqtt_unsubscribe_one(&esp_mqtt_client, lwmqtt_string(topic), esp_mqtt_command_timeout); + if (err != LWMQTT_SUCCESS) { + ESP_LOGE(ESP_MQTT_LOG_TAG, "lwmqtt_unsubscribe_one: %d", err); + ESP_MQTT_UNLOCK(); + return false; + } + + // release mutex + ESP_MQTT_UNLOCK(); + + return true; +} + +bool esp_mqtt_publish(const char *topic, uint8_t *payload, size_t len, int qos, bool retained) { + // acquire mutex + ESP_MQTT_LOCK(); + + // check if still connected + if (!esp_mqtt_connected) { + ESP_LOGI(ESP_MQTT_LOG_TAG, "esp_mqtt_publish: not connected"); + ESP_MQTT_UNLOCK(); + return false; + } + + // prepare message + lwmqtt_message_t message; + message.qos = (lwmqtt_qos_t)qos; + message.retained = retained; + message.payload = payload; + message.payload_len = len; + + // publish message + lwmqtt_err_t err = lwmqtt_publish(&esp_mqtt_client, lwmqtt_string(topic), message, esp_mqtt_command_timeout); + if (err != LWMQTT_SUCCESS) { + ESP_LOGE(ESP_MQTT_LOG_TAG, "lwmqtt_publish: %d", err); + ESP_MQTT_UNLOCK(); + return false; + } + + // release mutex + ESP_MQTT_UNLOCK(); + + return true; +} + +void esp_mqtt_stop() { + // acquire mutex + ESP_MQTT_LOCK(); + + // return immediately if not running anymore + if (!esp_mqtt_running) { + ESP_MQTT_UNLOCK(); + return; + } + + // attempt to properly disconnect a connected client + if (esp_mqtt_connected) { + lwmqtt_err_t err = lwmqtt_disconnect(&esp_mqtt_client, esp_mqtt_command_timeout); + if (err != LWMQTT_SUCCESS) { + ESP_LOGE(ESP_MQTT_LOG_TAG, "lwmqtt_disconnect: %d", err); + } + + // set flag + esp_mqtt_connected = false; + } + + // disconnect network + esp_lwmqtt_network_disconnect(&esp_mqtt_network); + + // kill mqtt task + ESP_LOGI(ESP_MQTT_LOG_TAG, "esp_mqtt_stop: deleting task"); + vTaskDelete(esp_mqtt_task); + + // set flag + esp_mqtt_running = false; + + // release mutex + ESP_MQTT_UNLOCK(); +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_mqtt.h b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_mqtt.h new file mode 100644 index 000000000..f567f46de --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/esp_mqtt.h @@ -0,0 +1,80 @@ +#ifndef ESP_MQTT_H +#define ESP_MQTT_H + +#include +#include + +/** + * The statuses emitted by the callback. + */ +typedef enum esp_mqtt_status_t { ESP_MQTT_STATUS_DISCONNECTED, ESP_MQTT_STATUS_CONNECTED } esp_mqtt_status_t; + +/** + * The status change callback. + */ +typedef void (*esp_mqtt_status_callback_t)(esp_mqtt_status_t); + +/** + * The message callback. + */ +typedef void (*esp_mqtt_message_callback_t)(const char *topic, uint8_t *payload, size_t len); + +/** + * Initialize the MQTT management system. + * + * Note: Should only be called once on boot. + * + * @param scb - The status callback. + * @param mcb - The message callback. + * @param buffer_size - The read and write buffer size. + * @param command_timeout - The command timeout. + */ +void esp_mqtt_init(esp_mqtt_status_callback_t scb, esp_mqtt_message_callback_t mcb, size_t buffer_size, + int command_timeout); + +/** + * Start the MQTT process. + * + * @param host - The broker host. + * @param port - The broker port. + * @param client_id - The client id. + * @param username - The client username. + * @param password - The client password. + */ +void esp_mqtt_start(const char *host, int port, const char *client_id, const char *username, const char *password); + +/** + * Subscribe to specified topic. + * + * @param topic - The topic. + * @param qos - The qos level. + * @return + */ +bool esp_mqtt_subscribe(const char *topic, int qos); + +/** + * Unsubscribe from specified topic. + * + * @param topic - The topic. + * @return + */ +bool esp_mqtt_unsubscribe(const char *topic); + +/** + * Publish bytes payload to specified topic. + * + * @param topic - The topic. + * @param payload - The payload. + * @param len - The payload length. + * @param qos - The qos level. + * @param retained - The retained flag. + * @return + */ +bool esp_mqtt_publish(const char *topic, uint8_t *payload, size_t len, int qos, bool retained); + +/** + * Stop the MQTT process. + */ +void esp_mqtt_stop(); + +#endif // ESP_MQTT_H diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/.editorconfig b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/.editorconfig new file mode 100644 index 000000000..cd8d16dc7 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/.editorconfig @@ -0,0 +1,3 @@ +[Makefile] +indent_style = tab +indent_size = 4 diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/.gitignore b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/.gitignore new file mode 100644 index 000000000..34de27079 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/.gitignore @@ -0,0 +1,2 @@ +cmake-build-debug/ +gtest/ diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/CMakeLists.txt b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/CMakeLists.txt new file mode 100644 index 000000000..21385977e --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 2.8.7) +project(lwmqtt) + +add_subdirectory(gtest/googletest) +include_directories(gtest/googletest/include) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -Werror -Wno-unused-parameter") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Werror -Wno-unused-parameter") + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c1x -D_POSIX_C_SOURCE=200112L -D_BSD_SOURCE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") +else() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif() + +include_directories(include) + +set(SOURCE_FILES + include/lwmqtt.h + include/lwmqtt/unix.h + src/client.c + src/helpers.c + src/helpers.h + src/packet.c + src/packet.h + src/string.c + src/os/unix.c) + +add_library(lwmqtt ${SOURCE_FILES}) + +set(EXAMPLE_FILES example/main.c) + +add_executable(example ${EXAMPLE_FILES}) + +target_link_libraries(example lwmqtt) + +set(TEST_FILES + tests/client.cpp + tests/helpers.cpp + tests/packet.cpp + tests/tests.cpp) + +add_executable(tests ${TEST_FILES}) + +target_link_libraries(tests lwmqtt gtest gtest_main) diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/LICENSE b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/LICENSE new file mode 100644 index 000000000..5179277e5 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Joël Gähwiler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/Makefile b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/Makefile new file mode 100644 index 000000000..f862b367b --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/Makefile @@ -0,0 +1,9 @@ +fmt: + clang-format -i include/*.h include/lwmqtt/*.h -style="{BasedOnStyle: Google, ColumnLimit: 120}" + clang-format -i src/*.c src/*.h -style="{BasedOnStyle: Google, ColumnLimit: 120}" + clang-format -i src/os/*.c -style="{BasedOnStyle: Google, ColumnLimit: 120}" + clang-format -i example/*.c -style="{BasedOnStyle: Google, ColumnLimit: 120}" + clang-format -i tests/*.cpp -style="{BasedOnStyle: Google, ColumnLimit: 120}" + +gtest: + git clone --depth 1 https://github.com/google/googletest.git gtest diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/README.md b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/README.md new file mode 100644 index 000000000..6376ffa85 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/README.md @@ -0,0 +1,6 @@ +# lwmqtt + +[![Build Status](https://travis-ci.org/256dpi/lwmqtt.svg?branch=master)](https://travis-ci.org/256dpi/lwmqtt) +[![Release](https://img.shields.io/github/release/256dpi/lwmqtt.svg)](https://github.com/256dpi/lwmqtt/releases) + +The "**L**ight **W**eight **MQTT**" library implements a MQTT client that is optimized to be used in embedded and constraint systems. It can be used with any operating system and network stack and only requires a handful of callbacks for a full integration. The project is derived from the [Paho MQTT Embeded C](https://github.com/eclipse/paho.mqtt.embedded-c), [gomqtt](https://github.com/gomqtt) and [mosquitto](https://github.com/eclipse/mosquitto) project. diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/example/main.c b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/example/main.c new file mode 100644 index 000000000..c9c3847a3 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/example/main.c @@ -0,0 +1,75 @@ +#include +#include + +#include +#include + +#define COMMAND_TIMEOUT 5000 + +static void message_arrived(lwmqtt_client_t *client, void *ref, lwmqtt_string_t topic, lwmqtt_message_t msg) { + printf("message_arrived: %.*s => %.*s (%d)\n", (int)topic.len, topic.data, (int)msg.payload_len, (char *)msg.payload, + (int)msg.payload_len); +} + +int main() { + lwmqtt_unix_network_t network; + lwmqtt_unix_timer_t timer1, timer2; + + lwmqtt_client_t client; + + lwmqtt_init(&client, malloc(512), 512, malloc(512), 512); + + lwmqtt_set_network(&client, &network, lwmqtt_unix_network_read, lwmqtt_unix_network_write); + lwmqtt_set_timers(&client, &timer1, &timer2, lwmqtt_unix_timer_set, lwmqtt_unix_timer_get); + lwmqtt_set_callback(&client, NULL, message_arrived); + + lwmqtt_err_t err = lwmqtt_unix_network_connect(&network, "broker.shiftr.io", 1883); + if (err != LWMQTT_SUCCESS) { + printf("failed lwmqtt_unix_network_connect: %d\n", err); + exit(1); + } + + lwmqtt_options_t options = lwmqtt_default_options; + options.client_id = lwmqtt_string("lwmqtt"); + options.username = lwmqtt_string("try"); + options.password = lwmqtt_string("try"); + options.keep_alive = 5; + + lwmqtt_return_code_t return_code; + err = lwmqtt_connect(&client, options, NULL, &return_code, COMMAND_TIMEOUT); + if (err != LWMQTT_SUCCESS) { + printf("failed lwmqtt_connect: %d (%d)\n", err, return_code); + exit(1); + } + + printf("connected!\n"); + + err = lwmqtt_subscribe_one(&client, lwmqtt_string("hello"), LWMQTT_QOS0, COMMAND_TIMEOUT); + if (err != LWMQTT_SUCCESS) { + printf("failed lwmqtt_subscribe: %d (%d)\n", err, return_code); + exit(1); + } + + while (true) { + size_t available = 0; + err = lwmqtt_unix_network_peek(&network, &available); + if (err != LWMQTT_SUCCESS) { + printf("failed lwmqtt_unix_network_peek: %d\n", err); + exit(1); + } + + if (available > 0) { + err = lwmqtt_yield(&client, available, COMMAND_TIMEOUT); + if (err != LWMQTT_SUCCESS) { + printf("failed lwmqtt_yield: %d\n", err); + exit(1); + } + } + + err = lwmqtt_keep_alive(&client, COMMAND_TIMEOUT); + if (err != LWMQTT_SUCCESS) { + printf("failed lwmqtt_keep_alive: %d\n", err); + exit(1); + } + } +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/include/lwmqtt.h b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/include/lwmqtt.h new file mode 100644 index 000000000..5b4302c7f --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/include/lwmqtt.h @@ -0,0 +1,354 @@ +#ifndef LWMQTT_H +#define LWMQTT_H + +#include +#include +#include + +/** + * The error type used by all exposed APIs. + */ +typedef enum { + LWMQTT_SUCCESS = 0, + LWMQTT_BUFFER_TOO_SHORT = -1, + LWMQTT_VARNUM_OVERFLOW = -2, + LWMQTT_NETWORK_FAILED_CONNECT = -3, + LWMQTT_NETWORK_TIMEOUT = -4, + LWMQTT_NETWORK_FAILED_READ = -5, + LWMQTT_NETWORK_FAILED_WRITE = -6, + LWMQTT_REMAINING_LENGTH_OVERFLOW = -7, + LWMQTT_REMAINING_LENGTH_MISMATCH = -8, + LWMQTT_MISSING_OR_WRONG_PACKET = -9, + LWMQTT_CONNECTION_DENIED = -10, + LWMQTT_FAILED_SUBSCRIPTION = -11, + LWMQTT_SUBACK_ARRAY_OVERFLOW = -12, + LWMQTT_UNANSWERED_PING = -13, +} lwmqtt_err_t; + +/** + * A common string object. + */ +typedef struct { + uint16_t len; + char *data; +} lwmqtt_string_t; + +/** + * The initializer for string objects. + */ +#define lwmqtt_default_string \ + { 0, NULL } + +/** + * Returns a string object for the passed C string. + * + * @param str - The C string. + * @return A string object. + */ +lwmqtt_string_t lwmqtt_string(const char *str); + +/** + * Compares a string object to a C string. + * + * @param a - The string object to compare. + * @param b - The C string to compare. + * @return Similarity e.g. strcmp(). + */ +int lwmqtt_strcmp(lwmqtt_string_t a, const char *b); + +/** + * The available QOS levels. + */ +typedef enum { LWMQTT_QOS0 = 0, LWMQTT_QOS1 = 1, LWMQTT_QOS2 = 2, LWMQTT_QOS_FAILURE = 128 } lwmqtt_qos_t; + +/** + * The message object used to publish and receive messages. + */ +typedef struct { + lwmqtt_qos_t qos; + bool retained; + uint8_t *payload; + size_t payload_len; +} lwmqtt_message_t; + +/** + * The initializer for message objects. + */ +#define lwmqtt_default_message \ + { LWMQTT_QOS0, false, NULL, 0 } + +/** + * Forward declaration of the client object. + */ +typedef struct lwmqtt_client_t lwmqtt_client_t; + +/** + * The callback used to read from a network object. + * + * The callbacks is expected to read up to the amount of bytes in to the passed buffer. It should block the specified + * timeout and wait for more incoming data. + */ +typedef lwmqtt_err_t (*lwmqtt_network_read_t)(lwmqtt_client_t *client, void *ref, uint8_t *buf, size_t len, + size_t *read, uint32_t timeout); + +/** + * The callback used to write to a network object. + * + * The callback is expected to write up to the amount of bytes from the passed buffer. It should wait up to the + * specified timeout to write the specified data to the network. + */ +typedef lwmqtt_err_t (*lwmqtt_network_write_t)(lwmqtt_client_t *client, void *ref, uint8_t *buf, size_t len, + size_t *sent, uint32_t timeout); + +/** + * The callback used to set a timer. + */ +typedef void (*lwmqtt_timer_set_t)(lwmqtt_client_t *client, void *ref, uint32_t timeout); + +/** + * The callback used to get a timers value. + */ +typedef uint32_t (*lwmqtt_timer_get_t)(lwmqtt_client_t *client, void *ref); + +/** + * The callback used to forward incoming messages. + * + * Note: The callback is mostly executed because of a call to lwmqtt_yield() that processes incoming messages. However, + * it is possible that the callback is also executed during a call to lwmqtt_subscribe(), lwmqtt_publish() or + * lwmqtt_unsubscribe() if incoming messages are received between the required acknowledgements. It is therefore not + * recommended to call any further lwmqtt methods in the callback as this might result in weird call stacks. The + * callback should place the received messages in a queue and dispatch them after the caller has returned. + */ +typedef void (*lwmqtt_callback_t)(lwmqtt_client_t *client, void *ref, lwmqtt_string_t str, lwmqtt_message_t msg); + +/** + * The client object. + */ +struct lwmqtt_client_t { + uint16_t last_packet_id; + uint32_t keep_alive_interval; + bool ping_outstanding; + + size_t write_buf_size, read_buf_size; + uint8_t *write_buf, *read_buf; + + void *callback_ref; + lwmqtt_callback_t callback; + + void *network; + lwmqtt_network_read_t network_read; + lwmqtt_network_write_t network_write; + + void *keep_alive_timer; + void *command_timer; + lwmqtt_timer_set_t timer_set; + lwmqtt_timer_get_t timer_get; +}; + +/** + * Will initialize the specified client object. + * + * @param client - The client object. + * @param write_buf - The write buffer. + * @param write_buf_size - The write buffer size. + * @param read_buf - The read buffer. + * @param read_buf_size - The read buffer size. + */ +void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf, + size_t read_buf_size); + +/** + * Will set the network reference and callbacks for this client object. + * + * @param client - The client object. + * @param ref - The reference to the network object. + * @param read - The read callback. + * @param write - The write callback. + */ +void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write); + +/** + * Will set the timer references and callbacks for this client object. + * + * @param client - The client object. + * @param keep_alive_timer - The reference to the keep alive timer. + * @param network_timer - The reference to the network timer. + * @param set - The set callback. + * @param get - The get callback. + */ +void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *network_timer, lwmqtt_timer_set_t set, + lwmqtt_timer_get_t get); + +/** + * Will set the callback used to receive incoming messages. + * + * @param client - The client object. + * @param ref - A custom reference that will passed to the callback. + * @param cb - The callback to be called. + */ +void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb); + +/** + * The object defining the last will of a client. + */ +typedef struct { + lwmqtt_string_t topic; + lwmqtt_qos_t qos; + bool retained; + lwmqtt_string_t payload; +} lwmqtt_will_t; + +/** + * The default initializer for the will object. + */ +#define lwmqtt_default_will \ + { lwmqtt_default_string, LWMQTT_QOS0, false, lwmqtt_default_string } + +/** + * The object containing the connection options for a client. + */ +typedef struct { + lwmqtt_string_t client_id; + uint16_t keep_alive; + bool clean_session; + lwmqtt_string_t username; + lwmqtt_string_t password; +} lwmqtt_options_t; + +/** + * The default initializer for the options object. + */ +#define lwmqtt_default_options \ + { lwmqtt_default_string, 60, true, lwmqtt_default_string, lwmqtt_default_string } + +/** + * The available return codes transported by the connack packet. + */ +typedef enum { + LWMQTT_CONNACK_CONNECTION_ACCEPTED = 0, + LWMQTT_CONNACK_UNACCEPTABLE_PROTOCOL = 1, + LWMQTT_CONNACK_IDENTIFIER_REJECTED = 2, + LWMQTT_CONNACK_SERVER_UNAVAILABLE = 3, + LWMQTT_CONNACK_BAD_USERNAME_OR_PASSWORD = 4, + LWMQTT_CONNACK_NOT_AUTHORIZED = 5 +} lwmqtt_return_code_t; + +/** + * Will send a connect packet and wait for a connack response and set the return code. + * + * The network object must already be connected to the server. An error is returned if the broker rejects the + * connection. + * + * @param client - The client object. + * @param options - The options object. + * @param will - The will object. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will, + lwmqtt_return_code_t *return_code, uint32_t timeout); + +/** + * Will send a publish packet and wait for all acks to complete. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param topic - The topic. + * @param message - The message. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t msg, uint32_t timeout); + +/** + * Will send a subscribe packet with multiple topic filters plus QOS levels and wait for the suback to complete. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param count - The number of topic filters and QOS levels. + * @param topic_filter - The topic filters. + * @param qos - The list of QOS levels. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos, + uint32_t timeout); + +/** + * Will send a subscribe packet with a single topic filter plus QOS level and wait for the suback to complete. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param topic_filter - The topic filter. + * @param qos - The QOS level. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos, + uint32_t timeout); + +/** + * Will send an unsubscribe packet with multiple topic filters and wait for the unsuback to complete. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param count - The number of topic filters. + * @param topic_filter - The topic filter. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout); + +/** + * Will send an unsubscribe packet with a single topic filter and wait for the unsuback to complete. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param topic_filter - The topic filter. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout); + +/** + * Will send a disconnect packet and finish the client. + * + * @param client - The client object. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout); + +/** + * Will yield control to the client and receive incoming packets from the network. + * + * Applications may peek on the network if there is data available to read before calling yield and potentially block + * until the timeout is reached. Furthermore, applications may specify the amount of bytes available to read in order + * to constrain the yield to only receive packets that are already inflight. + * + * If no availability data is given the yield will return after one packet has been successfully read. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param available - The available bytes to read. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout); + +/** + * Will yield control to the client to keep the connection alive. + * + * @param client - The client object. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout); + +#endif // LWMQTT_H diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/include/lwmqtt/unix.h b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/include/lwmqtt/unix.h new file mode 100644 index 000000000..099071a83 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/include/lwmqtt/unix.h @@ -0,0 +1,66 @@ +#ifndef LWMQTT_UNIX_H +#define LWMQTT_UNIX_H + +#include + +#include + +/** + * The UNIX timer object. + */ +typedef struct { struct timeval end; } lwmqtt_unix_timer_t; + +/** + * Callback to set the UNIX timer object. + */ +void lwmqtt_unix_timer_set(lwmqtt_client_t *client, void *ref, uint32_t timeout); + +/** + * Callback to read the UNIX timer object. + */ +uint32_t lwmqtt_unix_timer_get(lwmqtt_client_t *client, void *ref); + +/** + * The UNIX network object. + */ +typedef struct { int socket; } lwmqtt_unix_network_t; + +/** + * Function to establish a UNIX network connection. + * + * @param network - The network object. + * @param host - The host. + * @param port - The port. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_unix_network_connect(lwmqtt_unix_network_t *network, char *host, int port); + +/** + * Function to disconnect a UNIX network connection. + * + * @param network - The network object. + */ +void lwmqtt_unix_network_disconnect(lwmqtt_unix_network_t *network); + +/** + * Function to peek available bytes on a UNIX network connection. + * + * @param network - The network object. + * @param available - The available bytes. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_unix_network_peek(lwmqtt_unix_network_t *network, size_t *available); + +/** + * Callback to read from a UNIX network connection. + */ +lwmqtt_err_t lwmqtt_unix_network_read(lwmqtt_client_t *client, void *ref, uint8_t *buf, size_t len, size_t *read, + uint32_t timeout); + +/** + * Callback to write to a UNIX network connection. + */ +lwmqtt_err_t lwmqtt_unix_network_write(lwmqtt_client_t *client, void *ref, uint8_t *buf, size_t len, size_t *sent, + uint32_t timeout); + +#endif // LWMQTT_UNIX_H diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/client.c b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/client.c new file mode 100644 index 000000000..0bf3022e2 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/client.c @@ -0,0 +1,616 @@ +#include "packet.h" + +void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf, + size_t read_buf_size) { + client->last_packet_id = 1; + client->keep_alive_interval = 0; + client->ping_outstanding = false; + + client->write_buf = write_buf; + client->write_buf_size = write_buf_size; + client->read_buf = read_buf; + client->read_buf_size = read_buf_size; + client->callback = NULL; + + client->network = NULL; + client->network_read = NULL; + client->network_write = NULL; + + client->keep_alive_timer = NULL; + client->command_timer = NULL; + client->timer_set = NULL; + client->timer_get = NULL; +} + +void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write) { + client->network = ref; + client->network_read = read; + client->network_write = write; +} + +void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *network_timer, lwmqtt_timer_set_t set, + lwmqtt_timer_get_t get) { + client->keep_alive_timer = keep_alive_timer; + client->command_timer = network_timer; + client->timer_set = set; + client->timer_get = get; + + client->timer_set(client, client->keep_alive_timer, 0); + client->timer_set(client, client->command_timer, 0); +} + +void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb) { + client->callback_ref = ref; + client->callback = cb; +} + +static uint16_t lwmqtt_get_next_packet_id(lwmqtt_client_t *client) { + // check overflow + if (client->last_packet_id == 65535) { + client->last_packet_id = 1; + return 1; + } + + // increment packet id + client->last_packet_id++; + + return client->last_packet_id; +} + +static lwmqtt_err_t lwmqtt_read_from_network(lwmqtt_client_t *client, size_t offset, size_t len) { + // check read buffer capacity + if (client->read_buf_size < offset + len) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // prepare counter + size_t read = 0; + + // read while data is missing + while (read < len) { + // get remaining time + uint32_t remaining_time = client->timer_get(client, client->command_timer); + + // check timeout + if (remaining_time <= 0) { + return LWMQTT_NETWORK_TIMEOUT; + } + + // read + size_t partial_read = 0; + lwmqtt_err_t err = client->network_read(client, client->network, client->read_buf + offset + read, len - read, + &partial_read, remaining_time); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // increment counter + read += partial_read; + } + + return LWMQTT_SUCCESS; +} + +static lwmqtt_err_t lwmqtt_write_to_network(lwmqtt_client_t *client, size_t offset, size_t len) { + // prepare counter + size_t written = 0; + + // write while data is left + while (written < len) { + // get remaining time + uint32_t remaining_time = client->timer_get(client, client->command_timer); + + // check timeout + if (remaining_time <= 0) { + return LWMQTT_NETWORK_TIMEOUT; + } + + // read + size_t partial_write = 0; + lwmqtt_err_t err = client->network_write(client, client->network, client->write_buf + offset + written, + len - written, &partial_write, remaining_time); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // increment counter + written += partial_write; + } + + return LWMQTT_SUCCESS; +} + +static lwmqtt_err_t lwmqtt_read_packet_in_buffer(lwmqtt_client_t *client, size_t *read, + lwmqtt_packet_type_t *packet_type) { + // preset packet type + *packet_type = LWMQTT_NO_PACKET; + + // read or wait for header byte + lwmqtt_err_t err = lwmqtt_read_from_network(client, 0, 1); + if (err == LWMQTT_NETWORK_TIMEOUT) { + // this is ok as no data has been read at all + return LWMQTT_SUCCESS; + } else if (err != LWMQTT_SUCCESS) { + return err; + } + + // detect packet type + err = lwmqtt_detect_packet_type(client->read_buf, 1, packet_type); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // prepare variables + size_t len = 0; + uint32_t rem_len = 0; + + do { + // adjust len + len++; + + // read next byte + err = lwmqtt_read_from_network(client, len, 1); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // attempt to detect remaining length + err = lwmqtt_detect_remaining_length(client->read_buf + 1, len, &rem_len); + } while (err == LWMQTT_BUFFER_TOO_SHORT); + + // check final error + if (err != LWMQTT_SUCCESS) { + return err; + } + + // read the rest of the buffer if needed + if (rem_len > 0) { + err = lwmqtt_read_from_network(client, 1 + len, rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // adjust counter + *read += 1 + len + rem_len; + + return LWMQTT_SUCCESS; +} + +static lwmqtt_err_t lwmqtt_send_packet_in_buffer(lwmqtt_client_t *client, size_t length) { + // write to network + lwmqtt_err_t err = lwmqtt_write_to_network(client, 0, length); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // reset keep alive timer + client->timer_set(client, client->keep_alive_timer, client->keep_alive_interval * 1000); + + return LWMQTT_SUCCESS; +} + +static lwmqtt_err_t lwmqtt_cycle(lwmqtt_client_t *client, size_t *read, lwmqtt_packet_type_t *packet_type) { + // read next packet from the network + lwmqtt_err_t err = lwmqtt_read_packet_in_buffer(client, read, packet_type); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (*packet_type == LWMQTT_NO_PACKET) { + return LWMQTT_SUCCESS; + } + + switch (*packet_type) { + // handle publish packets + case LWMQTT_PUBLISH_PACKET: { + // decode publish packet + bool dup; + uint16_t packet_id; + lwmqtt_string_t topic; + lwmqtt_message_t msg; + err = lwmqtt_decode_publish(client->read_buf, client->read_buf_size, &dup, &packet_id, &topic, &msg); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // call callback if set + if (client->callback != NULL) { + client->callback(client, client->callback_ref, topic, msg); + } + + // break early on qos zero + if (msg.qos == LWMQTT_QOS0) { + break; + } + + // define ack packet + lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET; + if (msg.qos == LWMQTT_QOS1) { + ack_type = LWMQTT_PUBREC_PACKET; + } else if (msg.qos == LWMQTT_QOS2) { + ack_type = LWMQTT_PUBREL_PACKET; + } + + // encode ack packet + size_t len; + err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, ack_type, false, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send ack packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + break; + } + + // handle pubrec packets + case LWMQTT_PUBREC_PACKET: { + // decode pubrec packet + bool dup; + uint16_t packet_id; + err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, packet_type, &dup, &packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // encode pubrel packet + size_t len; + err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBREL_PACKET, 0, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send pubrel packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + break; + } + + // handle pubrel packets + case LWMQTT_PUBREL_PACKET: { + // decode pubrec packet + bool dup; + uint16_t packet_id; + err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, packet_type, &dup, &packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // encode pubcomp packet + size_t len; + err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBCOMP_PACKET, 0, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send pubcomp packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + break; + } + + // handle pingresp packets + case LWMQTT_PINGRESP_PACKET: { + // set flag + client->ping_outstanding = false; + + break; + } + + // handle all other packets + default: { break; } + } + + return LWMQTT_SUCCESS; +} + +static lwmqtt_err_t lwmqtt_cycle_until(lwmqtt_client_t *client, lwmqtt_packet_type_t *packet_type, size_t available, + lwmqtt_packet_type_t needle) { + // prepare counter + size_t read = 0; + + // loop until timeout has been reached + do { + // do one cycle + lwmqtt_err_t err = lwmqtt_cycle(client, &read, packet_type); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // return when one packet has been successfully read when no availability has been given + if (needle == LWMQTT_NO_PACKET && available == 0) { + return LWMQTT_SUCCESS; + } + + // otherwise check if needle has been found + if (*packet_type == needle) { + return LWMQTT_SUCCESS; + } + } while (client->timer_get(client, client->command_timer) > 0 && (available == 0 || read < available)); + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout) { + // set timeout + client->timer_set(client, client->command_timer, timeout); + + // cycle until timeout has been reached + lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; + lwmqtt_err_t err = lwmqtt_cycle_until(client, &packet_type, available, LWMQTT_NO_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will, + lwmqtt_return_code_t *return_code, uint32_t timeout) { + // set timer to command timeout + client->timer_set(client, client->command_timer, timeout); + + // save keep alive interval + client->keep_alive_interval = options.keep_alive; + + // set keep alive timer + if (client->keep_alive_interval > 0) { + client->timer_set(client, client->keep_alive_timer, client->keep_alive_interval * 1000); + } + + // encode connect packet + size_t len; + lwmqtt_err_t err = lwmqtt_encode_connect(client->write_buf, client->write_buf_size, &len, options, will); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // wait for connack packet + lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; + err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_CONNACK_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (packet_type != LWMQTT_CONNACK_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // decode connack packet + bool session_present; + err = lwmqtt_decode_connack(client->read_buf, client->read_buf_size, &session_present, return_code); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // return error if connection was not accepted + if (*return_code != LWMQTT_CONNACK_CONNECTION_ACCEPTED) { + return LWMQTT_CONNECTION_DENIED; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos, + uint32_t timeout) { + // set timeout + client->timer_set(client, client->command_timer, timeout); + + // encode subscribe packet + size_t len; + lwmqtt_err_t err = lwmqtt_encode_subscribe(client->write_buf, client->write_buf_size, &len, + lwmqtt_get_next_packet_id(client), count, topic_filter, qos); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // wait for suback packet + lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; + err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_SUBACK_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (packet_type != LWMQTT_SUBACK_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // decode packet + int suback_count = 0; + lwmqtt_qos_t granted_qos[count]; + uint16_t packet_id; + err = lwmqtt_decode_suback(client->read_buf, client->read_buf_size, &packet_id, count, &suback_count, granted_qos); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check suback codes + for (int i = 0; i < suback_count; i++) { + if (granted_qos[i] == LWMQTT_QOS_FAILURE) { + return LWMQTT_FAILED_SUBSCRIPTION; + } + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos, + uint32_t timeout) { + return lwmqtt_subscribe(client, 1, &topic_filter, &qos, timeout); +} + +lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout) { + // set timer + client->timer_set(client, client->command_timer, timeout); + + // encode unsubscribe packet + size_t len; + lwmqtt_err_t err = lwmqtt_encode_unsubscribe(client->write_buf, client->write_buf_size, &len, + lwmqtt_get_next_packet_id(client), count, topic_filter); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send unsubscribe packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // wait for unsuback packet + lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; + err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_UNSUBACK_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (packet_type != LWMQTT_UNSUBACK_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // decode unsuback packet + bool dup; + uint16_t packet_id; + err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, &packet_type, &dup, &packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout) { + return lwmqtt_unsubscribe(client, 1, &topic_filter, timeout); +} + +lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t message, + uint32_t timeout) { + // set timer + client->timer_set(client, client->command_timer, timeout); + + // add packet id if at least qos 1 + uint16_t packet_id = 0; + if (message.qos == LWMQTT_QOS1 || message.qos == LWMQTT_QOS2) { + packet_id = lwmqtt_get_next_packet_id(client); + } + + // encode publish packet + size_t len = 0; + lwmqtt_err_t err = + lwmqtt_encode_publish(client->write_buf, client->write_buf_size, &len, 0, packet_id, topic, message); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // immediately return on qos zero + if (message.qos == LWMQTT_QOS0) { + return LWMQTT_SUCCESS; + } + + // define ack packet + lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET; + if (message.qos == LWMQTT_QOS1) { + ack_type = LWMQTT_PUBACK_PACKET; + } else if (message.qos == LWMQTT_QOS2) { + ack_type = LWMQTT_PUBCOMP_PACKET; + } + + // wait for ack packet + lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; + err = lwmqtt_cycle_until(client, &packet_type, 0, ack_type); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (packet_type != ack_type) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // decode ack packet + bool dup; + err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, &packet_type, &dup, &packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout) { + // set timer + client->timer_set(client, client->command_timer, timeout); + + // encode disconnect packet + size_t len; + lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_DISCONNECT_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send disconnected packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout) { + // set timer + client->timer_set(client, client->command_timer, timeout); + + // return immediately if keep alive interval is zero + if (client->keep_alive_interval == 0) { + return LWMQTT_SUCCESS; + } + + // return immediately if no ping is due + if (client->timer_get(client, client->keep_alive_timer) > 0) { + return LWMQTT_SUCCESS; + } + + // a ping is due + + // fail immediately if a ping is still outstanding + if (client->ping_outstanding) { + return LWMQTT_UNANSWERED_PING; + } + + // encode pingreq packet + size_t len; + lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_PINGREQ_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set flag + client->ping_outstanding = true; + + return LWMQTT_SUCCESS; +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/helpers.c b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/helpers.c new file mode 100644 index 000000000..28a6d7365 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/helpers.c @@ -0,0 +1,243 @@ +#include + +#include "helpers.h" + +lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len) { + // check zero length + if (len == 0) { + *data = NULL; + return LWMQTT_SUCCESS; + } + + // check buffer size + if ((size_t)(buf_end - (*buf)) < len) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // read data + *data = *buf; + + // advance pointer + *buf += len; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len) { + // check zero length + if (len == 0) { + return LWMQTT_SUCCESS; + } + + // check buffer size + if ((size_t)(buf_end - (*buf)) < len) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // write data + memcpy(*buf, data, len); + + // advance pointer + *buf += len; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num) { + // check buffer size + if ((size_t)(buf_end - (*buf)) < 2) { + *num = 0; + return LWMQTT_BUFFER_TOO_SHORT; + } + + // read two byte integer + *num = (uint16_t)256 * (*buf)[0] + (*buf)[1]; + + // adjust pointer + *buf += 2; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num) { + // check buffer size + if ((size_t)(buf_end - (*buf)) < 2) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // write bytes + (*buf)[0] = (uint8_t)(num / 256); + (*buf)[1] = (uint8_t)(num % 256); + + // adjust pointer + *buf += 2; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str) { + // read length + uint16_t len; + lwmqtt_err_t err = lwmqtt_read_num(buf, buf_end, &len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // read data + err = lwmqtt_read_data(buf, buf_end, (uint8_t **)&str->data, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set length + str->len = len; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str) { + // write string length + lwmqtt_err_t err = lwmqtt_write_num(buf, buf_end, str.len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write data + err = lwmqtt_write_data(buf, buf_end, (uint8_t *)str.data, str.len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte) { + // check buffer size + if ((size_t)(buf_end - (*buf)) < 1) { + *byte = 0; + return LWMQTT_BUFFER_TOO_SHORT; + } + + // read byte + *byte = (*buf)[0]; + + // adjust pointer + *buf += 1; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte) { + // check buffer size + if ((size_t)(buf_end - (*buf)) < 1) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // write byte + (*buf)[0] = byte; + + // adjust pointer + *buf += 1; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len) { + if (varnum < 128) { + *len = 1; + return LWMQTT_SUCCESS; + } else if (varnum < 16384) { + *len = 2; + return LWMQTT_SUCCESS; + } else if (varnum < 2097151) { + *len = 3; + return LWMQTT_SUCCESS; + } else if (varnum < 268435455) { + *len = 4; + return LWMQTT_SUCCESS; + } else { + *len = 0; + return LWMQTT_VARNUM_OVERFLOW; + } +} + +lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum) { + // prepare last byte + uint8_t byte; + + // prepare multiplier + uint32_t multiplier = 1; + + // prepare length + size_t len = 0; + + // initialize number + *varnum = 0; + + // decode variadic number + do { + // increment length + len++; + + // return error if buffer is to small + if ((size_t)(buf_end - (*buf)) < len) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // return error if the length has overflowed + if (len > 4) { + return LWMQTT_VARNUM_OVERFLOW; + } + + // read byte + byte = (*buf)[len - 1]; + + // add byte to number + *varnum += (byte & 127) * multiplier; + + // increase multiplier + multiplier *= 128; + } while ((byte & 128) != 0); + + // adjust pointer + *buf += len; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum) { + // init len counter + size_t len = 0; + + // encode variadic number + do { + // check overflow + if (len == 4) { + return LWMQTT_VARNUM_OVERFLOW; + } + + // return error if buffer is to small + if ((size_t)(buf_end - (*buf)) < len + 1) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // calculate current byte + uint8_t byte = (uint8_t)(varnum % 128); + + // change remaining length + varnum /= 128; + + // set the top bit of this byte if there are more to encode + if (varnum > 0) { + byte |= 0x80; + } + + // write byte + (*buf)[len++] = byte; + } while (varnum > 0); + + // adjust pointer + *buf += len; + + return LWMQTT_SUCCESS; +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/helpers.h b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/helpers.h new file mode 100644 index 000000000..b3ab05d11 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/helpers.h @@ -0,0 +1,117 @@ +#ifndef LWMQTT_HELPERS_H +#define LWMQTT_HELPERS_H + +#include + +/** + * Reads arbitrary data from the specified buffer. The pointer is incremented by bytes read. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param data - Pointer to beginning of data. + * @param len - The amount of data to read. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len); + +/** + * Writes arbitrary data to the specified buffer. The pointer is incremented by the bytes written. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param data - Pointer to the to be written data. + * @param len - The amount of data to write. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len); + +/** + * Reads two byte number from the specified buffer. The pointer is incremented by two. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param num - The read number. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num); + +/** + * Writes a two byte number to the specified buffer. The pointer is incremented by two. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param num - The number to write. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num); + +/** + * Reads a string from the specified buffer into the passed object. The pointer is incremented by the bytes read. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param str - The object into which the data is to be read. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str); + +/** + * Writes a string to the specified buffer. The pointer is incremented by the bytes written. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param str - The string to write. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str); + +/** + * Reads one byte from the buffer. The pointer is incremented by one. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param byte - The read byte. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte); + +/** + * Writes one byte to the specified buffer. The pointer is incremented by one. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param byte - The byte to write. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte); + +/** + * Returns the amount of bytes required by the variable number. + * + * @param varnum - The number to check. + * @param len - The required length; + * @return LWMQTT_SUCCESS or LWMQTT_VARNUM_OVERFLOW. + */ +lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len); + +/** + * Reads a variable number from the specified buffer. The pointer is incremented by the bytes read. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param varnum - The read varnum. + * @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW. + */ +lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum); + +/** + * Writes a variable number to the specified buffer. The pointer is incremented by the bytes written. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param varnum - The number to write. + * @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW. + */ +lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum); + +#endif diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/os/unix.c b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/os/unix.c new file mode 100644 index 000000000..1f4949dda --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/os/unix.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +void lwmqtt_unix_timer_set(lwmqtt_client_t *client, void *ref, uint32_t timeout) { + // cast timer reference + lwmqtt_unix_timer_t *t = (lwmqtt_unix_timer_t *)ref; + + // clear end time + timerclear(&t->end); + + // get current time + struct timeval now; + gettimeofday(&now, NULL); + + // set future end time + struct timeval interval = {timeout / 1000, (timeout % 1000) * 1000}; + timeradd(&now, &interval, &t->end); +} + +uint32_t lwmqtt_unix_timer_get(lwmqtt_client_t *client, void *ref) { + // cast timer reference + lwmqtt_unix_timer_t *t = (lwmqtt_unix_timer_t *)ref; + + // get current time + struct timeval now; + gettimeofday(&now, NULL); + + // get difference to end time + struct timeval res; + timersub(&t->end, &now, &res); + + // convert to ms + return res.tv_sec < 0 ? 0 : (uint32_t)(res.tv_sec * 1000 + res.tv_usec / 1000); +} + +lwmqtt_err_t lwmqtt_unix_network_connect(lwmqtt_unix_network_t *network, char *host, int port) { + // close any open socket + lwmqtt_unix_network_disconnect(network); + + // prepare resolver hints + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; + + // resolve address + struct addrinfo *result = NULL; + int rc = getaddrinfo(host, NULL, &hints, &result); + if (rc < 0) { + return LWMQTT_NETWORK_FAILED_CONNECT; + } + + // prepare selected result + struct addrinfo *current = result; + struct addrinfo *selected = NULL; + + // traverse list and select first found ipv4 address + while (current) { + // check if ipv4 address + if (current->ai_family == AF_INET) { + selected = current; + break; + } + + // move one to next + current = current->ai_next; + } + + // return error if none found + if (selected == NULL) { + return LWMQTT_NETWORK_FAILED_CONNECT; + } + + // populate address struct + struct sockaddr_in address; + address.sin_port = htons(port); + address.sin_family = AF_INET; + address.sin_addr = ((struct sockaddr_in *)(selected->ai_addr))->sin_addr; + + // free result + freeaddrinfo(result); + + // create new socket + network->socket = socket(AF_INET, SOCK_STREAM, 0); + if (network->socket < 0) { + return LWMQTT_NETWORK_FAILED_CONNECT; + } + + // connect socket + rc = connect(network->socket, (struct sockaddr *)&address, sizeof(address)); + if (rc < 0) { + return LWMQTT_NETWORK_FAILED_CONNECT; + } + + return LWMQTT_SUCCESS; +} + +void lwmqtt_unix_network_disconnect(lwmqtt_unix_network_t *network) { + // close socket if present + if (network->socket) { + close(network->socket); + network->socket = 0; + } +} + +lwmqtt_err_t lwmqtt_unix_network_peek(lwmqtt_unix_network_t *network, size_t *available) { + // get the available bytes on the socket + int rc = ioctl(network->socket, FIONREAD, available); + if (rc < 0) { + return LWMQTT_NETWORK_FAILED_READ; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_unix_network_read(lwmqtt_client_t *client, void *ref, uint8_t *buffer, size_t len, size_t *read, + uint32_t timeout) { + // cast network reference + lwmqtt_unix_network_t *n = (lwmqtt_unix_network_t *)ref; + + // set timeout + struct timeval t = {.tv_sec = timeout / 1000, .tv_usec = (timeout % 1000) * 1000}; + int rc = setsockopt(n->socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&t, sizeof(t)); + if (rc < 0) { + return LWMQTT_NETWORK_FAILED_READ; + } + + // read from socket + int bytes = (int)recv(n->socket, buffer, len, 0); + if (bytes < 0 && errno != EAGAIN) { + return LWMQTT_NETWORK_FAILED_READ; + } + + // increment counter + *read += bytes; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_unix_network_write(lwmqtt_client_t *client, void *ref, uint8_t *buffer, size_t len, size_t *sent, + uint32_t timeout) { + // cast network reference + lwmqtt_unix_network_t *n = (lwmqtt_unix_network_t *)ref; + + // set timeout + struct timeval t = {.tv_sec = timeout / 1000, .tv_usec = (timeout % 1000) * 1000}; + int rc = setsockopt(n->socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&t, sizeof(t)); + if (rc < 0) { + return LWMQTT_NETWORK_FAILED_WRITE; + } + + // write to socket + int bytes = (int)send(n->socket, buffer, len, 0); + if (bytes < 0 && errno != EAGAIN) { + return LWMQTT_NETWORK_FAILED_WRITE; + } + + // increment counter + *sent += bytes; + + return LWMQTT_SUCCESS; +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/packet.c b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/packet.c new file mode 100644 index 000000000..0d1ac48bd --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/packet.c @@ -0,0 +1,683 @@ +#include "packet.h" + +typedef union { + uint8_t byte; + struct { + unsigned int retain : 1; + unsigned int qos : 2; + unsigned int dup : 1; + unsigned int type : 4; + } bits; +} lwmqtt_header_t; + +typedef union { + uint8_t byte; + struct { + unsigned int _ : 1; + unsigned int clean_session : 1; + unsigned int will : 1; + unsigned int will_qos : 2; + unsigned int will_retain : 1; + unsigned int password : 1; + unsigned int username : 1; + } bits; +} lwmqtt_connect_flags_t; + +typedef union { + uint8_t byte; + struct { + unsigned int _ : 7; + unsigned int session_present : 1; + } bits; +} lwmqtt_connack_flags_t; + +lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type) { + // set default packet type + *packet_type = LWMQTT_NO_PACKET; + + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // prepare header + lwmqtt_header_t header; + + // reader header + lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set packet type + *packet_type = (lwmqtt_packet_type_t)header.bits.type; + + // check if packet type is correct and can be received + switch (*packet_type) { + case LWMQTT_CONNACK_PACKET: + case LWMQTT_PUBLISH_PACKET: + case LWMQTT_PUBACK_PACKET: + case LWMQTT_PUBREC_PACKET: + case LWMQTT_PUBREL_PACKET: + case LWMQTT_PUBCOMP_PACKET: + case LWMQTT_SUBACK_PACKET: + case LWMQTT_UNSUBACK_PACKET: + case LWMQTT_PINGRESP_PACKET: + return LWMQTT_SUCCESS; + default: + *packet_type = LWMQTT_NO_PACKET; + return LWMQTT_MISSING_OR_WRONG_PACKET; + } +} + +lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len) { + // prepare pointer + uint8_t *ptr = buf; + + // attempt to decode remaining length + lwmqtt_err_t err = lwmqtt_read_varnum(&ptr, buf + buf_len, rem_len); + if (err == LWMQTT_VARNUM_OVERFLOW) { + *rem_len = 0; + return LWMQTT_REMAINING_LENGTH_OVERFLOW; + } else if (err != LWMQTT_SUCCESS) { + *rem_len = 0; + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options, + lwmqtt_will_t *will) { + // prepare pointers + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // fixed header is 10 + uint32_t rem_len = 10; + + // add client id to remaining length + rem_len += options.client_id.len + 2; + + // add will if present to remaining length + if (will != NULL) { + rem_len += will->topic.len + 2 + will->payload.len + 2; + } + + // add username if present to remaining length + if (options.username.len > 0) { + rem_len += options.username.len + 2; + + // add password if present to remaining length + if (options.password.len > 0) { + rem_len += options.password.len + 2; + } + } + + // check remaining length length + int rem_len_len; + lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); + if (err == LWMQTT_VARNUM_OVERFLOW) { + return LWMQTT_REMAINING_LENGTH_OVERFLOW; + } + + // prepare header + lwmqtt_header_t header = {0}; + header.bits.type = LWMQTT_CONNECT_PACKET; + + // write header + err = lwmqtt_write_byte(&buf_ptr, buf_end, header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write version string + err = lwmqtt_write_string(&buf_ptr, buf_end, lwmqtt_string("MQTT")); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write version number + err = lwmqtt_write_byte(&buf_ptr, buf_end, 4); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // prepare flags + lwmqtt_connect_flags_t flags = {0}; + flags.bits.clean_session = options.clean_session ? 1 : 0; + + // set will flags if present + if (will != NULL) { + flags.bits.will = 1; + flags.bits.will_qos = (unsigned int)will->qos; + flags.bits.will_retain = will->retained ? 1 : 0; + } + + // set username flag if present + if (options.username.len > 0) { + flags.bits.username = 1; + + // set password flag if present + if (options.password.len > 0) { + flags.bits.password = 1; + } + } + + // write flags + err = lwmqtt_write_byte(&buf_ptr, buf_end, flags.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write keep alive + err = lwmqtt_write_num(&buf_ptr, buf_end, options.keep_alive); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write client id + err = lwmqtt_write_string(&buf_ptr, buf_end, options.client_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write will if present + if (will != NULL) { + // write topic + err = lwmqtt_write_string(&buf_ptr, buf_end, will->topic); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write payload length + err = lwmqtt_write_num(&buf_ptr, buf_end, (uint16_t)will->payload.len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write payload + err = lwmqtt_write_data(&buf_ptr, buf_end, (uint8_t *)will->payload.data, will->payload.len); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // write username if present + if (flags.bits.username) { + err = lwmqtt_write_string(&buf_ptr, buf_end, options.username); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // write password if present + if (flags.bits.username && flags.bits.password) { + err = lwmqtt_write_string(&buf_ptr, buf_end, options.password); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // set written length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present, + lwmqtt_return_code_t *return_code) { + // prepare pointers + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // read header + lwmqtt_header_t header; + lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (header.bits.type != LWMQTT_CONNACK_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // read remaining length + uint32_t rem_len; + err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check remaining length + if (rem_len != 2) { + return LWMQTT_REMAINING_LENGTH_MISMATCH; + } + + // read flags + lwmqtt_connack_flags_t flags; + err = lwmqtt_read_byte(&buf_ptr, buf_end, &flags.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // read return code + uint8_t raw_return_code; + err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_return_code); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set variables + *session_present = flags.bits.session_present == 1; + *return_code = (lwmqtt_return_code_t)raw_return_code; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // write header + lwmqtt_header_t header = {0}; + header.bits.type = packet_type; + lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, 0); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type, bool *dup, + uint16_t *packet_id) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // read header + lwmqtt_header_t header = {0}; + lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set variables + *dup = header.bits.dup == 1; + *packet_type = (lwmqtt_packet_type_t)header.bits.type; + + // read remaining length + uint32_t rem_len; + err = lwmqtt_read_varnum(&buf_ptr, buf + buf_len, &rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check remaining length + if (rem_len != 2) { + return LWMQTT_REMAINING_LENGTH_MISMATCH; + } + + // read packet id + err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup, + uint16_t packet_id) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // prepare header + lwmqtt_header_t header = {0}; + header.bits.type = packet_type; + header.bits.dup = dup ? 1 : 0; + header.bits.qos = (packet_type == LWMQTT_PUBREL_PACKET) ? 1 : 0; + + // write header + lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, 2); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write packet id + err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set written length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic, + lwmqtt_message_t *msg) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // read header + lwmqtt_header_t header; + lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (header.bits.type != LWMQTT_PUBLISH_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // set variables + *dup = header.bits.dup == 1; + msg->qos = (lwmqtt_qos_t)header.bits.qos; + msg->retained = header.bits.retain == 1; + + // read remaining length + uint32_t rem_len; + err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check remaining length (topic length) + if (rem_len < 2) { + return LWMQTT_REMAINING_LENGTH_MISMATCH; + } + + // check buffer capacity + if (buf_end - buf_ptr < rem_len) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // reset buf end + buf_end = buf_ptr + rem_len; + + // read topic + err = lwmqtt_read_string(&buf_ptr, buf_end, topic); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // read packet id if qos is at least 1 + if (msg->qos > 0) { + err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + } else { + *packet_id = 0; + } + + // set payload length + msg->payload_len = buf_end - buf_ptr; + + // read payload + err = lwmqtt_read_data(&buf_ptr, buf_end, &msg->payload, buf_end - buf_ptr); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id, + lwmqtt_string_t topic, lwmqtt_message_t msg) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // calculate remaining length + uint32_t rem_len = 2 + topic.len + (uint32_t)msg.payload_len; + if (msg.qos > 0) { + rem_len += 2; + } + + // check remaining length length + int rem_len_len; + lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); + if (err == LWMQTT_VARNUM_OVERFLOW) { + return LWMQTT_REMAINING_LENGTH_OVERFLOW; + } + + // prepare header + lwmqtt_header_t header = {0}; + header.bits.type = LWMQTT_PUBLISH_PACKET; + header.bits.dup = dup ? 1 : 0; + header.bits.qos = (unsigned int)msg.qos; + header.bits.retain = msg.retained ? 1 : 0; + + // write header + err = lwmqtt_write_byte(&buf_ptr, buf_end, header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write topic + err = lwmqtt_write_string(&buf_ptr, buf_end, topic); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write packet id if qos is at least 1 + if (msg.qos > 0) { + err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // write payload + err = lwmqtt_write_data(&buf_ptr, buf_end, msg.payload, msg.payload_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, + lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // calculate remaining length + uint32_t rem_len = 2; + for (int i = 0; i < count; i++) { + rem_len += 2 + topic_filters[i].len + 1; + } + + // check remaining length length + int rem_len_len; + lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); + if (err == LWMQTT_VARNUM_OVERFLOW) { + return LWMQTT_REMAINING_LENGTH_OVERFLOW; + } + + // prepare header + lwmqtt_header_t header = {0}; + header.bits.type = LWMQTT_SUBSCRIBE_PACKET; + header.bits.qos = 1; + + // write header + err = lwmqtt_write_byte(&buf_ptr, buf_end, header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write packet id + err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write all subscriptions + for (int i = 0; i < count; i++) { + // write topic + err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write qos level + err = lwmqtt_write_byte(&buf_ptr, buf_end, (uint8_t)qos_levels[i]); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // set length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count, + lwmqtt_qos_t *granted_qos_levels) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // read header + lwmqtt_header_t header; + lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (header.bits.type != LWMQTT_SUBACK_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // read remaining length + uint32_t rem_len; + err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check remaining length (packet id + min. one suback code) + if (rem_len < 3) { + return LWMQTT_REMAINING_LENGTH_MISMATCH; + } + + // read packet id + err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // read all suback codes + for (*count = 0; *count < (int)rem_len - 2; (*count)++) { + // check max count + if (*count > max_count) { + return LWMQTT_SUBACK_ARRAY_OVERFLOW; + } + + // read qos level + uint8_t raw_qos_level; + err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_qos_level); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set qos level + granted_qos_levels[*count] = (lwmqtt_qos_t)raw_qos_level; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, + lwmqtt_string_t *topic_filters) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // calculate remaining length + uint32_t rem_len = 2; + for (int i = 0; i < count; i++) { + rem_len += 2 + topic_filters[i].len; + } + + // check remaining length length + int rem_len_len; + lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); + if (err == LWMQTT_VARNUM_OVERFLOW) { + return LWMQTT_REMAINING_LENGTH_OVERFLOW; + } + + // prepare header + lwmqtt_header_t header = {0}; + header.bits.type = LWMQTT_UNSUBSCRIBE_PACKET; + header.bits.qos = 1; + + // write header + err = lwmqtt_write_byte(&buf_ptr, buf_end, header.byte); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write packet id + err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write topics + for (int i = 0; i < count; i++) { + err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // set length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/packet.h b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/packet.h new file mode 100644 index 000000000..99eea03f1 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/packet.h @@ -0,0 +1,185 @@ +#ifndef LWMQTT_PACKET_H +#define LWMQTT_PACKET_H + +#include "helpers.h" + +/** + * The available packet types. + */ +typedef enum { + LWMQTT_NO_PACKET = 0, + LWMQTT_CONNECT_PACKET = 1, + LWMQTT_CONNACK_PACKET, + LWMQTT_PUBLISH_PACKET, + LWMQTT_PUBACK_PACKET, + LWMQTT_PUBREC_PACKET, + LWMQTT_PUBREL_PACKET, + LWMQTT_PUBCOMP_PACKET, + LWMQTT_SUBSCRIBE_PACKET, + LWMQTT_SUBACK_PACKET, + LWMQTT_UNSUBSCRIBE_PACKET, + LWMQTT_UNSUBACK_PACKET, + LWMQTT_PINGREQ_PACKET, + LWMQTT_PINGRESP_PACKET, + LWMQTT_DISCONNECT_PACKET +} lwmqtt_packet_type_t; + +/** + * Will detect the packet type from the at least one byte long buffer. + * + * @param buf - The buffer from which the packet type will be detected. + * @param buf_len - The length of the specified buffer. + * @param packet_type - The packet type. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type); + +/** + * Will detect the remaining length form the at least on byte long buffer. + * + * It will return LWMQTT_BUFFER_TOO_SHORT if the buffer is to short and an additional byte should be read from the + * network. In case the remaining length is overflowed it will return LWMQTT_REMAINING_LENGTH_OVERFLOW. + * + * @param buf - The buffer from which the remaining length will be detected. + * @param buf_len - The length of the specified buffer. + * @param rem_len - The detected remaining length. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len); + +/** + * Encodes a connect packet into the supplied buffer. + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param options - The options to be used to build the connect packet. + * @param will - The last will and testament. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options, + lwmqtt_will_t *will); + +/** + * Decodes a connack packet from the supplied buffer. + * + * @param buf - The raw buffer data. + * @param buf_len - The length of the specified buffer. + * @param session_present - The session present flag. + * @param return_code - The return code. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present, + lwmqtt_return_code_t *return_code); + +/** + * Encodes a zero (disconnect, pingreq) packet into the supplied buffer. + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param packet_type - The packets type. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type); + +/** + * Decodes an ack (puback, pubrec, pubrel, pubcomp, unsuback) packet from the supplied buffer. + * + * @param buf - The raw buffer data. + * @param buf_len - The length of the specified buffer. + * @param packet_type - The packet type. + * @param dup - The dup flag. + * @param packet_id - The packet id. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type, bool *dup, + uint16_t *packet_id); + +/** + * Encodes an ack (puback, pubrec, pubrel, pubcomp) packet into the supplied buffer. + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param packet_type - The packets type. + * @param dup - The dup flag. + * @param packet_id - The packet id. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup, + uint16_t packet_id); + +/** + * Decodes a publish packet from the supplied buffer. + * + * @param buf - The raw buffer data. + * @param buf_len - The length of the specified buffer. + * @param dup - The dup flag. + * @param packet_id - The packet id. + * @param topic - The topic. + * @parma msg - The message. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic, + lwmqtt_message_t *msg); + +/** + * Encodes a publish packet into the supplied buffer. + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param dup - The dup flag. + * @param packet_id - The packet id. + * @param topic - The topic. + * @param msg - The message. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id, + lwmqtt_string_t topic, lwmqtt_message_t msg); + +/** + * Encodes a subscribe packet into the supplied buffer. + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param packet_id - The packet id. + * @param count - The number of members in the topic_filters and qos_levels array. + * @param topic_filters - The array of topic filter. + * @param qos_levels - The array of requested QoS levels. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, + lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels); + +/** + * Decodes a suback packet from the supplied buffer. + * + * @param buf - The raw buffer data. + * @param buf_len - The length of the specified buffer. + * @param packet_id - The packet id. + * @param max_count - The maximum number of members allowed in the granted_qos_levels array. + * @param count - The number of members in the granted_qos_levels array. + * @param granted_qos_levels - The granted QoS levels. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count, + lwmqtt_qos_t *granted_qos_levels); + +/** + * Encodes the supplied unsubscribe data into the supplied buffer, ready for sending + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param packet_id - The packet id. + * @param count - The number of members in the topic_filters array. + * @param topic_filters - The array of topic filters. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, + lwmqtt_string_t *topic_filters); + +#endif // LWMQTT_PACKET_H diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/string.c b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/string.c new file mode 100644 index 000000000..fe32babb7 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/src/string.c @@ -0,0 +1,23 @@ +#include + +#include + +lwmqtt_string_t lwmqtt_string(const char *str) { + // get length + uint16_t len = (uint16_t)strlen(str); + + return (lwmqtt_string_t){len, (char *)str}; +} + +int lwmqtt_strcmp(lwmqtt_string_t a, const char *b) { + // get length of b + size_t len = strlen(b); + + // otherwise check if length matches + if (len != a.len) { + return -1; + } + + // compare memory + return strncmp(a.data, b, len); +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/client.cpp b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/client.cpp new file mode 100644 index 000000000..157ddf7f2 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/client.cpp @@ -0,0 +1,397 @@ +#include + +extern "C" { +#include +#include +} + +#define COMMAND_TIMEOUT 5000 + +#define PAYLOAD_LEN 256 +uint8_t payload[PAYLOAD_LEN + 1]; + +#define BIG_PAYLOAD_LEN 9800 +uint8_t big_payload[BIG_PAYLOAD_LEN + 1]; + +volatile int counter; + +const char *custom_ref = "cool"; + +static void message_arrived(lwmqtt_client_t *c, void *ref, lwmqtt_string_t t, lwmqtt_message_t m) { + ASSERT_EQ(ref, custom_ref); + + int res = lwmqtt_strcmp(t, "lwmqtt"); + ASSERT_EQ(res, 0); + + ASSERT_EQ(m.payload_len, (size_t)PAYLOAD_LEN); + res = memcmp(payload, (char *)m.payload, (size_t)m.payload_len); + ASSERT_EQ(res, 0); + + counter++; +} + +static void big_message_arrived(lwmqtt_client_t *c, void *ref, lwmqtt_string_t t, lwmqtt_message_t m) { + ASSERT_EQ(ref, custom_ref); + + int res = lwmqtt_strcmp(t, "lwmqtt"); + ASSERT_EQ(res, 0); + + ASSERT_EQ(m.payload_len, (size_t)BIG_PAYLOAD_LEN); + res = memcmp(big_payload, (char *)m.payload, (size_t)m.payload_len); + ASSERT_EQ(res, 0); + + counter++; +} + +TEST(Client, PublishSubscribeQOS0) { + lwmqtt_unix_network_t network; + lwmqtt_unix_timer_t timer1, timer2; + + lwmqtt_client_t client; + + lwmqtt_init(&client, (uint8_t *)malloc(512), 512, (uint8_t *)malloc(512), 512); + + lwmqtt_set_network(&client, &network, lwmqtt_unix_network_read, lwmqtt_unix_network_write); + lwmqtt_set_timers(&client, &timer1, &timer2, lwmqtt_unix_timer_set, lwmqtt_unix_timer_get); + lwmqtt_set_callback(&client, (void *)custom_ref, message_arrived); + + lwmqtt_err_t err = lwmqtt_unix_network_connect(&network, (char *)"broker.shiftr.io", 1883); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_options_t data = lwmqtt_default_options; + data.client_id = lwmqtt_string("lwmqtt"); + data.username = lwmqtt_string("try"); + data.password = lwmqtt_string("try"); + + lwmqtt_return_code_t return_code; + err = lwmqtt_connect(&client, data, NULL, &return_code, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + err = lwmqtt_subscribe_one(&client, lwmqtt_string("lwmqtt"), LWMQTT_QOS0, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + counter = 0; + + for (int i = 0; i < 5; i++) { + lwmqtt_message_t msg = lwmqtt_default_message; + msg.qos = LWMQTT_QOS0; + msg.payload = payload; + msg.payload_len = PAYLOAD_LEN; + + err = lwmqtt_publish(&client, lwmqtt_string("lwmqtt"), msg, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + } + + while (counter < 5) { + size_t available = 0; + err = lwmqtt_unix_network_peek(&network, &available); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + if (available > 0) { + err = lwmqtt_yield(&client, available, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + } + } + + err = lwmqtt_unsubscribe_one(&client, lwmqtt_string("lwmqtt"), COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + err = lwmqtt_disconnect(&client, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_unix_network_disconnect(&network); +} + +TEST(Client, PublishSubscribeQOS1) { + lwmqtt_unix_network_t network; + lwmqtt_unix_timer_t timer1, timer2; + + lwmqtt_client_t client; + + lwmqtt_init(&client, (uint8_t *)malloc(512), 512, (uint8_t *)malloc(512), 512); + + lwmqtt_set_network(&client, &network, lwmqtt_unix_network_read, lwmqtt_unix_network_write); + lwmqtt_set_timers(&client, &timer1, &timer2, lwmqtt_unix_timer_set, lwmqtt_unix_timer_get); + lwmqtt_set_callback(&client, (void *)custom_ref, message_arrived); + + lwmqtt_err_t err = lwmqtt_unix_network_connect(&network, (char *)"broker.shiftr.io", 1883); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_options_t options = lwmqtt_default_options; + options.client_id = lwmqtt_string("lwmqtt"); + options.username = lwmqtt_string("try"); + options.password = lwmqtt_string("try"); + + lwmqtt_return_code_t return_code; + err = lwmqtt_connect(&client, options, NULL, &return_code, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + err = lwmqtt_subscribe_one(&client, lwmqtt_string("lwmqtt"), LWMQTT_QOS1, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + counter = 0; + + for (int i = 0; i < 5; i++) { + lwmqtt_message_t msg = lwmqtt_default_message; + msg.qos = LWMQTT_QOS1; + msg.payload = payload; + msg.payload_len = PAYLOAD_LEN; + + err = lwmqtt_publish(&client, lwmqtt_string("lwmqtt"), msg, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + } + + while (counter < 5) { + size_t available = 0; + err = lwmqtt_unix_network_peek(&network, &available); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + if (available > 0) { + err = lwmqtt_yield(&client, available, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + } + } + + err = lwmqtt_unsubscribe_one(&client, lwmqtt_string("lwmqtt"), COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + err = lwmqtt_disconnect(&client, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_unix_network_disconnect(&network); +} + +TEST(Client, PublishSubscribeQOS2) { + lwmqtt_unix_network_t network; + lwmqtt_unix_timer_t timer1, timer2; + + lwmqtt_client_t client; + + lwmqtt_init(&client, (uint8_t *)malloc(512), 512, (uint8_t *)malloc(512), 512); + + lwmqtt_set_network(&client, &network, lwmqtt_unix_network_read, lwmqtt_unix_network_write); + lwmqtt_set_timers(&client, &timer1, &timer2, lwmqtt_unix_timer_set, lwmqtt_unix_timer_get); + lwmqtt_set_callback(&client, (void *)custom_ref, message_arrived); + + lwmqtt_err_t err = lwmqtt_unix_network_connect(&network, (char *)"broker.shiftr.io", 1883); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_options_t options = lwmqtt_default_options; + options.client_id = lwmqtt_string("lwmqtt"); + options.username = lwmqtt_string("try"); + options.password = lwmqtt_string("try"); + + lwmqtt_return_code_t return_code; + err = lwmqtt_connect(&client, options, NULL, &return_code, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + err = lwmqtt_subscribe_one(&client, lwmqtt_string("lwmqtt"), LWMQTT_QOS2, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + counter = 0; + + for (int i = 0; i < 5; i++) { + lwmqtt_message_t msg = lwmqtt_default_message; + msg.qos = LWMQTT_QOS2; + msg.payload = payload; + msg.payload_len = PAYLOAD_LEN; + + err = lwmqtt_publish(&client, lwmqtt_string("lwmqtt"), msg, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + } + + while (counter < 5) { + size_t available = 0; + err = lwmqtt_unix_network_peek(&network, &available); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + if (available > 0) { + err = lwmqtt_yield(&client, available, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + } + } + + err = lwmqtt_unsubscribe_one(&client, lwmqtt_string("lwmqtt"), COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + err = lwmqtt_disconnect(&client, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_unix_network_disconnect(&network); +} + +TEST(Client, BufferOverflowProtection) { + lwmqtt_unix_network_t network; + lwmqtt_unix_timer_t timer1, timer2; + + lwmqtt_client_t client; + + lwmqtt_init(&client, (uint8_t *)malloc(512), 512, (uint8_t *)malloc(512), 256); + + lwmqtt_set_network(&client, &network, lwmqtt_unix_network_read, lwmqtt_unix_network_write); + lwmqtt_set_timers(&client, &timer1, &timer2, lwmqtt_unix_timer_set, lwmqtt_unix_timer_get); + lwmqtt_set_callback(&client, (void *)custom_ref, message_arrived); + + lwmqtt_err_t err = lwmqtt_unix_network_connect(&network, (char *)"broker.shiftr.io", 1883); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_options_t options = lwmqtt_default_options; + options.client_id = lwmqtt_string("lwmqtt"); + options.username = lwmqtt_string("try"); + options.password = lwmqtt_string("try"); + + lwmqtt_return_code_t return_code; + err = lwmqtt_connect(&client, options, NULL, &return_code, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + err = lwmqtt_subscribe_one(&client, lwmqtt_string("lwmqtt"), LWMQTT_QOS0, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + counter = 0; + + lwmqtt_message_t msg = lwmqtt_default_message; + msg.qos = LWMQTT_QOS0; + msg.payload = payload; + msg.payload_len = PAYLOAD_LEN; + + err = lwmqtt_publish(&client, lwmqtt_string("lwmqtt"), msg, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + while (counter < 1) { + size_t available = 0; + err = lwmqtt_unix_network_peek(&network, &available); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + if (available > 0) { + err = lwmqtt_yield(&client, available, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_BUFFER_TOO_SHORT); + break; + } + } + + err = lwmqtt_disconnect(&client, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_unix_network_disconnect(&network); +} + +TEST(Client, BigBuffersAndPayload) { + lwmqtt_unix_network_t network; + lwmqtt_unix_timer_t timer1, timer2; + + lwmqtt_client_t client; + + lwmqtt_init(&client, (uint8_t *)malloc(10000), 10000, (uint8_t *)malloc(10000), 10000); + + lwmqtt_set_network(&client, &network, lwmqtt_unix_network_read, lwmqtt_unix_network_write); + lwmqtt_set_timers(&client, &timer1, &timer2, lwmqtt_unix_timer_set, lwmqtt_unix_timer_get); + lwmqtt_set_callback(&client, (void *)custom_ref, big_message_arrived); + + lwmqtt_err_t err = lwmqtt_unix_network_connect(&network, (char *)"broker.shiftr.io", 1883); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_options_t options = lwmqtt_default_options; + options.client_id = lwmqtt_string("lwmqtt"); + options.username = lwmqtt_string("try"); + options.password = lwmqtt_string("try"); + + lwmqtt_return_code_t return_code; + err = lwmqtt_connect(&client, options, NULL, &return_code, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + err = lwmqtt_subscribe_one(&client, lwmqtt_string("lwmqtt"), LWMQTT_QOS0, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + counter = 0; + + for (int i = 0; i < 5; i++) { + lwmqtt_message_t msg = lwmqtt_default_message; + msg.qos = LWMQTT_QOS0; + msg.payload = big_payload; + msg.payload_len = BIG_PAYLOAD_LEN; + + err = lwmqtt_publish(&client, lwmqtt_string("lwmqtt"), msg, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + } + + while (counter < 5) { + size_t available = 0; + err = lwmqtt_unix_network_peek(&network, &available); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + if (available > 0) { + err = lwmqtt_yield(&client, available, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + } + } + + err = lwmqtt_unsubscribe_one(&client, lwmqtt_string("lwmqtt"), COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + err = lwmqtt_disconnect(&client, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_unix_network_disconnect(&network); +} + +TEST(Client, MultipleSubscriptions) { + lwmqtt_unix_network_t network; + lwmqtt_unix_timer_t timer1, timer2; + + lwmqtt_client_t client; + + lwmqtt_init(&client, (uint8_t *)malloc(512), 512, (uint8_t *)malloc(512), 512); + + lwmqtt_set_network(&client, &network, lwmqtt_unix_network_read, lwmqtt_unix_network_write); + lwmqtt_set_timers(&client, &timer1, &timer2, lwmqtt_unix_timer_set, lwmqtt_unix_timer_get); + lwmqtt_set_callback(&client, (void *)custom_ref, message_arrived); + + lwmqtt_err_t err = lwmqtt_unix_network_connect(&network, (char *)"broker.shiftr.io", 1883); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_options_t options = lwmqtt_default_options; + options.client_id = lwmqtt_string("lwmqtt"); + options.username = lwmqtt_string("try"); + options.password = lwmqtt_string("try"); + + lwmqtt_return_code_t return_code; + err = lwmqtt_connect(&client, options, NULL, &return_code, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_string_t topic_filters[2] = {lwmqtt_string("foo"), lwmqtt_string("lwmqtt")}; + lwmqtt_qos_t qos_levels[2] = {LWMQTT_QOS0, LWMQTT_QOS0}; + + err = lwmqtt_subscribe(&client, 2, topic_filters, qos_levels, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + counter = 0; + + for (int i = 0; i < 5; i++) { + lwmqtt_message_t msg = lwmqtt_default_message; + msg.qos = LWMQTT_QOS0; + msg.payload = payload; + msg.payload_len = PAYLOAD_LEN; + + err = lwmqtt_publish(&client, lwmqtt_string("lwmqtt"), msg, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + } + + while (counter < 5) { + size_t available = 0; + err = lwmqtt_unix_network_peek(&network, &available); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + if (available > 0) { + err = lwmqtt_yield(&client, available, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + } + } + + err = lwmqtt_unsubscribe(&client, 2, topic_filters, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + err = lwmqtt_disconnect(&client, COMMAND_TIMEOUT); + ASSERT_EQ(err, LWMQTT_SUCCESS); + + lwmqtt_unix_network_disconnect(&network); +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/helpers.cpp b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/helpers.cpp new file mode 100644 index 000000000..8900838db --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/helpers.cpp @@ -0,0 +1,120 @@ +#include + +extern "C" { +#include "../src/helpers.h" +} + +TEST(VarNum1, Valid) { + uint8_t buf[2]; + memset(buf, 0, 2); + + for (uint32_t i = 1; i < 128; i++) { + int len; + lwmqtt_err_t err = lwmqtt_varnum_length(i, &len); + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_EQ(len, 1); + + uint8_t *ptr1 = buf; + err = lwmqtt_write_varnum(&ptr1, buf + 1, i); + EXPECT_EQ(err, LWMQTT_SUCCESS); + + uint8_t *ptr2 = buf; + uint32_t num; + err = lwmqtt_read_varnum(&ptr2, buf + 1, &num); + EXPECT_EQ(err, LWMQTT_SUCCESS); + + EXPECT_EQ(num, i); + EXPECT_EQ(buf[1], 0); + EXPECT_EQ(ptr1 - buf, 1); + EXPECT_EQ(ptr2 - buf, 1); + } +} + +TEST(VarNum2, Valid) { + uint8_t buf[3]; + memset(buf, 0, 3); + + for (uint32_t i = 1; i < 128; i++) { + int len; + lwmqtt_err_t err = lwmqtt_varnum_length(128 * i, &len); + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_EQ(len, 2); + + uint8_t *ptr1 = buf; + err = lwmqtt_write_varnum(&ptr1, buf + 2, 128 * i); + EXPECT_EQ(err, LWMQTT_SUCCESS); + + uint8_t *ptr2 = buf; + uint32_t num; + err = lwmqtt_read_varnum(&ptr2, buf + 2, &num); + EXPECT_EQ(err, LWMQTT_SUCCESS); + + EXPECT_EQ(num, 128 * i); + EXPECT_EQ(buf[2], 0); + EXPECT_EQ(ptr1 - buf, 2); + EXPECT_EQ(ptr2 - buf, 2); + } +} + +TEST(VarNum3, Valid) { + uint8_t buf[4]; + memset(buf, 0, 4); + + for (uint32_t i = 1; i < 128; i++) { + int len; + lwmqtt_err_t err = lwmqtt_varnum_length(128 * 128 * i, &len); + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_EQ(len, 3); + + uint8_t *ptr1 = buf; + err = lwmqtt_write_varnum(&ptr1, buf + 3, 128 * 128 * i); + EXPECT_EQ(err, LWMQTT_SUCCESS); + + uint8_t *ptr2 = buf; + uint32_t num; + err = lwmqtt_read_varnum(&ptr2, buf + 3, &num); + EXPECT_EQ(err, LWMQTT_SUCCESS); + + EXPECT_EQ(num, 128 * 128 * i); + EXPECT_EQ(buf[3], 0); + EXPECT_EQ(ptr1 - buf, 3); + EXPECT_EQ(ptr2 - buf, 3); + } +} + +TEST(VarNum4, Valid) { + uint8_t buf[5]; + memset(buf, 0, 5); + + for (uint32_t i = 1; i < 128; i++) { + int len; + lwmqtt_err_t err = lwmqtt_varnum_length(128 * 128 * 128 * i, &len); + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_EQ(len, 4); + + uint8_t *ptr1 = buf; + err = lwmqtt_write_varnum(&ptr1, buf + 4, 128 * 128 * 128 * i); + EXPECT_EQ(err, LWMQTT_SUCCESS); + + uint8_t *ptr2 = buf; + uint32_t num; + err = lwmqtt_read_varnum(&ptr2, buf + 4, &num); + EXPECT_EQ(err, LWMQTT_SUCCESS); + + EXPECT_EQ(num, 128 * 128 * 128 * i); + EXPECT_EQ(buf[4], 0); + EXPECT_EQ(ptr1 - buf, 4); + EXPECT_EQ(ptr2 - buf, 4); + } +} + +TEST(VarNumMax, Valid) { + uint8_t buf[4] = {0xFF, 0xFF, 0xFF, 0x7F}; + + uint8_t *ptr = buf; + uint32_t num; + lwmqtt_err_t err = lwmqtt_read_varnum(&ptr, buf + 4, &num); + EXPECT_EQ(err, LWMQTT_SUCCESS); + + EXPECT_EQ(num, (uint32_t)268435455); +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/packet.cpp b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/packet.cpp new file mode 100644 index 000000000..27b2de880 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/packet.cpp @@ -0,0 +1,697 @@ +#include + +extern "C" { +#include +#include "../src/packet.h" +} + +#define EXPECT_ARRAY_EQ(reference, actual, element_count) \ + { \ + for (size_t cmp_i = 0; cmp_i < element_count; cmp_i++) { \ + EXPECT_EQ(reference[cmp_i], actual[cmp_i]) << "At byte: " << cmp_i; \ + } \ + } + +TEST(DetectPacketType, Valid) { + uint8_t h = LWMQTT_CONNACK_PACKET << 4; + lwmqtt_packet_type_t p; + lwmqtt_err_t err = lwmqtt_detect_packet_type(&h, 1, &p); + EXPECT_EQ(p, LWMQTT_CONNACK_PACKET); + EXPECT_EQ(err, LWMQTT_SUCCESS); +} + +TEST(DetectPacketType, Invalid) { + uint8_t h = 255; + lwmqtt_packet_type_t p; + lwmqtt_err_t err = lwmqtt_detect_packet_type(&h, 1, &p); + EXPECT_EQ(p, LWMQTT_NO_PACKET); + EXPECT_EQ(err, LWMQTT_MISSING_OR_WRONG_PACKET); +} + +TEST(DetectRemainingLength, Valid1) { + uint8_t h = 60; + uint32_t rem_len = 0; + lwmqtt_err_t err = lwmqtt_detect_remaining_length(&h, 1, &rem_len); + EXPECT_EQ(rem_len, (uint32_t)60); + EXPECT_EQ(err, LWMQTT_SUCCESS); +} + +TEST(DetectRemainingLength, Valid2) { + uint8_t h[2] = {255, 60}; + uint32_t rem_len = 0; + lwmqtt_err_t err = lwmqtt_detect_remaining_length(h, 2, &rem_len); + EXPECT_EQ(rem_len, (uint32_t)7807); + EXPECT_EQ(err, LWMQTT_SUCCESS); +} + +TEST(DetectRemainingLength, ToShort) { + uint8_t h = 255; + uint32_t rem_len = 0; + lwmqtt_err_t err = lwmqtt_detect_remaining_length(&h, 1, &rem_len); + EXPECT_EQ(err, LWMQTT_BUFFER_TOO_SHORT); +} + +TEST(DetectRemainingLength, Overflow) { + uint8_t h[5] = {255, 255, 255, 255, 255}; + uint32_t rem_len = 0; + lwmqtt_err_t err = lwmqtt_detect_remaining_length(h, 5, &rem_len); + EXPECT_EQ(err, LWMQTT_REMAINING_LENGTH_OVERFLOW); +} + +TEST(ConnectTest, Encode1) { + uint8_t pkt[62] = { + LWMQTT_CONNECT_PACKET << 4, + 60, + 0, // Protocol String MSB + 4, // Protocol String LSB + 'M', + 'Q', + 'T', + 'T', + 4, // Protocol level 4 + 204, // Connect Flags + 0, // Keep Alive MSB + 10, // Keep Alive LSB + 0, // Client ID MSB + 7, // Client ID LSB + 's', + 'u', + 'r', + 'g', + 'e', + 'm', + 'q', + 0, // Will Topic MSB + 4, // Will Topic LSB + 'w', + 'i', + 'l', + 'l', + 0, // Will Message MSB + 12, // Will Message LSB + 's', + 'e', + 'n', + 'd', + ' ', + 'm', + 'e', + ' ', + 'h', + 'o', + 'm', + 'e', + 0, // Username ID MSB + 7, // Username ID LSB + 's', + 'u', + 'r', + 'g', + 'e', + 'm', + 'q', + 0, // Password ID MSB + 10, // Password ID LSB + 'v', + 'e', + 'r', + 'y', + 's', + 'e', + 'c', + 'r', + 'e', + 't', + }; + + uint8_t buf[62]; + + lwmqtt_will_t will = lwmqtt_default_will; + will.topic = lwmqtt_string("will"); + will.payload = lwmqtt_string("send me home"); + will.qos = LWMQTT_QOS1; + + lwmqtt_options_t opts = lwmqtt_default_options; + opts.clean_session = false; + opts.keep_alive = 10; + opts.client_id = lwmqtt_string("surgemq"); + opts.username = lwmqtt_string("surgemq"); + opts.password = lwmqtt_string("verysecret"); + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_connect(buf, 62, &len, opts, &will); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_ARRAY_EQ(pkt, buf, len); +} + +TEST(ConnectTest, Encode2) { + uint8_t pkt[14] = { + LWMQTT_CONNECT_PACKET << 4, + 12, + 0, // Protocol String MSB + 4, // Protocol String LSB + 'M', + 'Q', + 'T', + 'T', + 4, // Protocol level 4 + 2, // Connect Flags + 0, // Keep Alive MSB + 60, // Keep Alive LSB + 0, // Client ID MSB + 0, // Client ID LSB + }; + + uint8_t buf[14]; + + lwmqtt_options_t opts = lwmqtt_default_options; + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_connect(buf, 14, &len, opts, NULL); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_ARRAY_EQ(pkt, buf, len); +} + +TEST(ConnectTest, EncodeError1) { + uint8_t buf[4]; // <- too small buffer + + lwmqtt_options_t opts = lwmqtt_default_options; + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_connect(buf, 4, &len, opts, NULL); + + EXPECT_EQ(err, LWMQTT_BUFFER_TOO_SHORT); +} + +TEST(ConnackTest, Decode1) { + uint8_t pkt[4] = { + LWMQTT_CONNACK_PACKET << 4, 2, + 0, // session not present + 0, // connection accepted + }; + + bool session_present; + lwmqtt_return_code_t return_code; + lwmqtt_err_t err = lwmqtt_decode_connack(pkt, 4, &session_present, &return_code); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_EQ(session_present, 0); + EXPECT_EQ(return_code, 0); +} + +TEST(ConnackTest, DecodeError1) { + uint8_t pkt[4] = { + LWMQTT_CONNACK_PACKET << 4, + 3, // <-- wrong size + 0, // session not present + 0, // connection accepted + }; + + bool session_present; + lwmqtt_return_code_t return_code; + lwmqtt_err_t err = lwmqtt_decode_connack(pkt, 4, &session_present, &return_code); + + EXPECT_EQ(err, LWMQTT_REMAINING_LENGTH_MISMATCH); +} + +TEST(ConnackTest, DecodeError2) { + uint8_t pkt[3] = { + LWMQTT_CONNACK_PACKET << 4, 3, + 0, // session not present + // <- missing packet size + }; + + bool session_present; + lwmqtt_return_code_t return_code; + lwmqtt_err_t err = lwmqtt_decode_connack(pkt, 3, &session_present, &return_code); + + EXPECT_EQ(err, LWMQTT_REMAINING_LENGTH_MISMATCH); +} + +TEST(ZeroTest, Encode1) { + uint8_t pkt[2] = {LWMQTT_PINGREQ_PACKET << 4, 0}; + + uint8_t buf[2]; + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_zero(buf, 2, &len, LWMQTT_PINGREQ_PACKET); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_ARRAY_EQ(pkt, buf, len); +} + +TEST(AckTest, Decode1) { + uint8_t pkt[4] = { + LWMQTT_PUBACK_PACKET << 4, 2, + 0, // packet ID MSB + 7, // packet ID LSB + }; + + lwmqtt_packet_type_t packet_type; + bool dup; + uint16_t packet_id; + lwmqtt_err_t err = lwmqtt_decode_ack(pkt, 4, &packet_type, &dup, &packet_id); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_EQ(packet_type, LWMQTT_PUBACK_PACKET); + EXPECT_EQ(dup, false); + EXPECT_EQ(packet_id, 7); +} + +TEST(AckTest, DecodeError1) { + uint8_t pkt[4] = { + LWMQTT_PUBACK_PACKET << 4, + 1, // <-- wrong remaining length + 0, // packet ID MSB + 7, // packet ID LSB + }; + + lwmqtt_packet_type_t packet_type; + bool dup; + uint16_t packet_id; + lwmqtt_err_t err = lwmqtt_decode_ack(pkt, 4, &packet_type, &dup, &packet_id); + + EXPECT_EQ(err, LWMQTT_REMAINING_LENGTH_MISMATCH); +} + +TEST(AckTest, DecodeError2) { + uint8_t pkt[3] = { + LWMQTT_PUBACK_PACKET << 4, + 1, // <-- wrong remaining length + 0, // packet ID MSB + // <- insufficient bytes + }; + + lwmqtt_packet_type_t packet_type; + bool dup; + uint16_t packet_id; + lwmqtt_err_t err = lwmqtt_decode_ack(pkt, 4, &packet_type, &dup, &packet_id); + + EXPECT_EQ(err, LWMQTT_REMAINING_LENGTH_MISMATCH); +} + +TEST(AckTest, Encode1) { + uint8_t pkt[4] = { + LWMQTT_PUBACK_PACKET << 4, 2, + 0, // packet ID MSB + 7, // packet ID LSB + }; + + uint8_t buf[4]; + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_ack(buf, 4, &len, LWMQTT_PUBACK_PACKET, 0, 7); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_ARRAY_EQ(pkt, buf, len); +} + +TEST(AckTest, EncodeError1) { + uint8_t buf[2]; // <- too small buffer + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_ack(buf, 2, &len, LWMQTT_PUBACK_PACKET, 0, 7); + + EXPECT_EQ(err, LWMQTT_BUFFER_TOO_SHORT); +} + +TEST(PublishTest, Decode1) { + uint8_t pkt[25] = { + LWMQTT_PUBLISH_PACKET << 4 | 11, + 23, + 0, // topic name MSB + 7, // topic name LSB + 's', + 'u', + 'r', + 'g', + 'e', + 'm', + 'q', + 0, // packet ID MSB + 7, // packet ID LSB + 's', + 'e', + 'n', + 'd', + ' ', + 'm', + 'e', + ' ', + 'h', + 'o', + 'm', + 'e', + }; + + bool dup; + uint16_t packet_id; + lwmqtt_string_t topic; + lwmqtt_message_t msg; + lwmqtt_err_t err = lwmqtt_decode_publish(pkt, 25, &dup, &packet_id, &topic, &msg); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_EQ(dup, true); + EXPECT_EQ(msg.qos, 1); + EXPECT_EQ(msg.retained, true); + EXPECT_EQ(packet_id, 7); + EXPECT_ARRAY_EQ("surgemq", topic.data, 7); + EXPECT_EQ(msg.payload_len, (size_t)12); + EXPECT_ARRAY_EQ("send me home", msg.payload, 12); +} + +TEST(PublishTest, Decode2) { + uint8_t pkt[23] = { + LWMQTT_PUBLISH_PACKET << 4, + 21, + 0, // topic name MSB + 7, // topic name LSB + 's', + 'u', + 'r', + 'g', + 'e', + 'm', + 'q', + 's', + 'e', + 'n', + 'd', + ' ', + 'm', + 'e', + ' ', + 'h', + 'o', + 'm', + 'e', + }; + + bool dup; + uint16_t packet_id; + lwmqtt_string_t topic = lwmqtt_default_string; + lwmqtt_message_t msg; + lwmqtt_err_t err = lwmqtt_decode_publish(pkt, 23, &dup, &packet_id, &topic, &msg); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_EQ(dup, false); + EXPECT_EQ(msg.qos, 0); + EXPECT_EQ(msg.retained, false); + EXPECT_EQ(packet_id, 0); + EXPECT_ARRAY_EQ("surgemq", topic.data, 7); + EXPECT_EQ(msg.payload_len, (size_t)12); + EXPECT_ARRAY_EQ("send me home", msg.payload, 12); +} + +TEST(PublishTest, DecodeError1) { + uint8_t pkt[2] = { + LWMQTT_PUBLISH_PACKET << 4, + 2, // <-- too much + }; + + bool dup; + uint16_t packet_id; + lwmqtt_string_t topic = lwmqtt_default_string; + lwmqtt_message_t msg; + lwmqtt_err_t err = lwmqtt_decode_publish(pkt, 2, &dup, &packet_id, &topic, &msg); + + EXPECT_EQ(err, LWMQTT_BUFFER_TOO_SHORT); +} + +TEST(PublishTest, Encode1) { + uint8_t pkt[25] = { + LWMQTT_PUBLISH_PACKET << 4 | 11, + 23, + 0, // topic name MSB + 7, // topic name LSB + 's', + 'u', + 'r', + 'g', + 'e', + 'm', + 'q', + 0, // packet ID MSB + 7, // packet ID LSB + 's', + 'e', + 'n', + 'd', + ' ', + 'm', + 'e', + ' ', + 'h', + 'o', + 'm', + 'e', + }; + + uint8_t buf[25]; + + lwmqtt_string_t topic = lwmqtt_string("surgemq"); + lwmqtt_message_t msg = lwmqtt_default_message; + msg.qos = LWMQTT_QOS1; + msg.payload = (uint8_t*)"send me home"; + msg.payload_len = 12; + msg.retained = true; + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_publish(buf, 25, &len, true, 7, topic, msg); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_ARRAY_EQ(pkt, buf, len); +} + +TEST(PublishTest, Encode2) { + uint8_t pkt[23] = { + LWMQTT_PUBLISH_PACKET << 4, + 21, + 0, // topic name MSB + 7, // topic name LSB + 's', + 'u', + 'r', + 'g', + 'e', + 'm', + 'q', + 's', + 'e', + 'n', + 'd', + ' ', + 'm', + 'e', + ' ', + 'h', + 'o', + 'm', + 'e', + }; + + uint8_t buf[23]; + + lwmqtt_string_t topic = lwmqtt_string("surgemq"); + lwmqtt_message_t msg = lwmqtt_default_message; + msg.payload = (uint8_t*)"send me home"; + msg.payload_len = 12; + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_publish(buf, 23, &len, false, 0, topic, msg); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_ARRAY_EQ(pkt, buf, len); +} + +TEST(PublishTest, EncodeError1) { + uint8_t buf[2]; // <- too small buffer + + lwmqtt_string_t topic = lwmqtt_string("surgemq"); + lwmqtt_message_t msg = lwmqtt_default_message; + msg.payload = (uint8_t*)"send me home"; + msg.payload_len = 12; + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_publish(buf, 2, &len, false, 0, topic, msg); + + EXPECT_EQ(err, LWMQTT_BUFFER_TOO_SHORT); +} + +TEST(SubackTest, Decode1) { + uint8_t pkt[8] = { + LWMQTT_SUBACK_PACKET << 4, + 4, + 0, // packet ID MSB + 7, // packet ID LSB + 0, // return code 1 + 1, // return code 2 + }; + + uint16_t packet_id; + int count; + lwmqtt_qos_t granted_qos_levels[2]; + lwmqtt_err_t err = lwmqtt_decode_suback(pkt, 8, &packet_id, 2, &count, granted_qos_levels); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_EQ(packet_id, 7); + EXPECT_EQ(count, 2); + EXPECT_EQ(granted_qos_levels[0], 0); + EXPECT_EQ(granted_qos_levels[1], 1); +} + +TEST(SubackTest, DecodeError1) { + uint8_t pkt[5] = { + LWMQTT_SUBACK_PACKET << 4, + 1, // <- wrong remaining length + 0, // packet ID MSB + 7, // packet ID LSB + 0, // return code 1 + }; + + uint16_t packet_id; + int count; + lwmqtt_qos_t granted_qos_levels[2]; + lwmqtt_err_t err = lwmqtt_decode_suback(pkt, 5, &packet_id, 2, &count, granted_qos_levels); + + EXPECT_EQ(err, LWMQTT_REMAINING_LENGTH_MISMATCH); +} + +TEST(SubscribeTest, Encode1) { + uint8_t pkt[38] = { + LWMQTT_SUBSCRIBE_PACKET << 4 | 2, + 36, + 0, // packet ID MSB + 7, // packet ID LSB + 0, // topic name MSB + 7, // topic name LSB + 's', + 'u', + 'r', + 'g', + 'e', + 'm', + 'q', + 0, // QOS + 0, // topic name MSB + 8, // topic name LSB + '/', + 'a', + '/', + 'b', + '/', + '#', + '/', + 'c', + 1, // QOS + 0, // topic name MSB + 10, // topic name LSB + '/', + 'a', + '/', + 'b', + '/', + '#', + '/', + 'c', + 'd', + 'd', + 2, // QOS + }; + + uint8_t buf[38]; + + lwmqtt_string_t topic_filters[3]; + topic_filters[0] = lwmqtt_string("surgemq"); + topic_filters[1] = lwmqtt_string("/a/b/#/c"); + topic_filters[2] = lwmqtt_string("/a/b/#/cdd"); + + lwmqtt_qos_t qos_levels[3] = {LWMQTT_QOS0, LWMQTT_QOS1, LWMQTT_QOS2}; + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_subscribe(buf, 38, &len, 7, 3, topic_filters, qos_levels); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_ARRAY_EQ(pkt, buf, len); +} + +TEST(SubscribeTest, EncodeError1) { + uint8_t buf[2]; // <- too small buffer + + lwmqtt_string_t topic_filters[1]; + topic_filters[0] = lwmqtt_string("surgemq"); + + lwmqtt_qos_t qos_levels[1] = {LWMQTT_QOS0}; + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_subscribe(buf, 2, &len, 7, 1, topic_filters, qos_levels); + + EXPECT_EQ(err, LWMQTT_BUFFER_TOO_SHORT); +} + +TEST(UnsubscribeTest, Encode1) { + uint8_t pkt[35] = { + LWMQTT_UNSUBSCRIBE_PACKET << 4 | 2, + 33, + 0, // packet ID MSB + 7, // packet ID LSB + 0, // topic name MSB + 7, // topic name LSB + 's', + 'u', + 'r', + 'g', + 'e', + 'm', + 'q', + 0, // topic name MSB + 8, // topic name LSB + '/', + 'a', + '/', + 'b', + '/', + '#', + '/', + 'c', + 0, // topic name MSB + 10, // topic name LSB + '/', + 'a', + '/', + 'b', + '/', + '#', + '/', + 'c', + 'd', + 'd', + }; + + uint8_t buf[38]; + + lwmqtt_string_t topic_filters[3]; + topic_filters[0] = lwmqtt_string("surgemq"); + topic_filters[1] = lwmqtt_string("/a/b/#/c"); + topic_filters[2] = lwmqtt_string("/a/b/#/cdd"); + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_unsubscribe(buf, 38, &len, 7, 3, topic_filters); + + EXPECT_EQ(err, LWMQTT_SUCCESS); + EXPECT_ARRAY_EQ(pkt, buf, len); +} + +TEST(UnsubscribeTest, EncodeError1) { + uint8_t buf[2]; // <- too small buffer + + lwmqtt_string_t topic_filters[1]; + topic_filters[0] = lwmqtt_string("surgemq"); + + size_t len; + lwmqtt_err_t err = lwmqtt_encode_unsubscribe(buf, 2, &len, 7, 1, topic_filters); + + EXPECT_EQ(err, LWMQTT_BUFFER_TOO_SHORT); +} diff --git a/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/tests.cpp b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/tests.cpp new file mode 100644 index 000000000..4d820af77 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/components/esp-mqtt/lwmqtt/tests/tests.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/cpp/sensor_monitor/esp32/main/component.mk b/src/cpp/sensor_monitor/esp32/main/component.mk new file mode 100644 index 000000000..e69de29bb diff --git a/src/cpp/sensor_monitor/esp32/main/main.c b/src/cpp/sensor_monitor/esp32/main/main.c new file mode 100644 index 000000000..e1be06b8f --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/main/main.c @@ -0,0 +1,6 @@ +#include "main.h" + +void app_main() { + start_wifi(); + start_sensor(); +} \ No newline at end of file diff --git a/src/cpp/sensor_monitor/esp32/main/main.h b/src/cpp/sensor_monitor/esp32/main/main.h new file mode 100644 index 000000000..330042158 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/main/main.h @@ -0,0 +1,4 @@ +#include +#include +#include "network.h" +#include "sensor.h" \ No newline at end of file diff --git a/src/cpp/sensor_monitor/esp32/main/network.c b/src/cpp/sensor_monitor/esp32/main/network.c new file mode 100644 index 000000000..8610395c0 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/main/network.c @@ -0,0 +1,85 @@ +#include "network.h" + +static TaskHandle_t task = NULL; +static void networking(void *p) { + char *payload = "ping \n"; + for (;;) { + + mqtt_write(payload, "ping"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} + +void mqtt_write(char* payload, char* channel) +{ + esp_mqtt_publish(channel, (void *)payload, (int)strlen(payload), 0, false); + printf("%s", payload); +} + + +void start_wifi(){ + tcpip_adapter_init(); + ESP_ERROR_CHECK(esp_event_loop_init(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 = WIFI_SSID, + .password = WIFI_PASS, + }, + }; + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + start_mqtt(); +} + +void start_mqtt(){ + esp_mqtt_init(status_callback, message_callback, 256, 2000); +} + +static void status_callback(esp_mqtt_status_t status) { + switch (status) { + case ESP_MQTT_STATUS_CONNECTED: + esp_mqtt_subscribe("test", 0); + esp_mqtt_subscribe("ping", 0); + xTaskCreatePinnedToCore(networking, "networking", 4096, NULL, 10, &task, 1); + break; + case ESP_MQTT_STATUS_DISCONNECTED: + vTaskDelete(task); + break; + } + } + +static void message_callback(const char *topic, uint8_t *payload, size_t len) { + printf("incoming: %s => %s (%d)\n", topic, payload, (int)len); + } + +static esp_err_t 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: + esp_mqtt_start(MQTT_HOST, MQTT_PORT, CLIENT_NAME, MQTT_USER, MQTT_PASS); + break; + + case SYSTEM_EVENT_STA_DISCONNECTED: + esp_mqtt_stop(); + esp_wifi_connect(); + break; + + default: + break; + } + + return ESP_OK; + } + \ No newline at end of file diff --git a/src/cpp/sensor_monitor/esp32/main/network.h b/src/cpp/sensor_monitor/esp32/main/network.h new file mode 100644 index 000000000..0f72df6b8 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/main/network.h @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include + +#define WIFI_SSID "SSID" +#define WIFI_PASS "PASS" + +#define MQTT_HOST "HOST" +#define MQTT_PORT 1883 +#define MQTT_USER "" +#define MQTT_PASS "" +#define CLIENT_NAME "ESP32-test" + +static void start(void*); +void start_wifi(); +void start_mqtt(); +static void status_callback(esp_mqtt_status_t); +static void message_callback(const char*, uint8_t*, size_t); +static esp_err_t event_handler(void*, system_event_t*); +void mqtt_write(char*, char*); \ No newline at end of file diff --git a/src/cpp/sensor_monitor/esp32/main/sensor.c b/src/cpp/sensor_monitor/esp32/main/sensor.c new file mode 100644 index 000000000..45f09e337 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/main/sensor.c @@ -0,0 +1,116 @@ +#include "sensor.h" +#include "network.h" + +static TaskHandle_t task = NULL; +static xQueueHandle gpio_evt_queue = NULL; + +static void IRAM_ATTR gpio_isr_handler(void* arg) +{ + uint32_t gpio_num = (uint32_t) arg; + xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); +} + +uint64_t * carriages; // 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 + // |=========1. sensor times=========| |=========0. sensor times=========| + // |t__out| |t__out| |tstart| |tstart| |t__out| |t__out| |tstart| |tstart| + +void calculate(uint64_t *carriage){ + uint16_t t_out[2]; + uint16_t t_start[2]; + for(int i = 0; i<2; i++){ + t_out[i] = (*carriage>>((2*i + 1) * 16)); + t_start[i] = (*carriage>>((2*i) * 16)); + + } + *carriage = 0; + + if(fabs(t_out[1] - t_out[0]) > EPS_MEASURE) return; + uint8_t dt = fabs(t_start[1] - t_start[0]); + uint8_t t_block = (t_out[1] + t_out[0])/2; + char* payload = (char*)malloc(sizeof(char)*50); + double speed = ((double)SENSOR_DISTANCE) * 100 /dt; + double length = speed * t_block / 100; + sprintf(payload, "speed: %010.3lfcm/s length: %010.3lfcm\n", speed, length); + mqtt_write(payload, "test"); +} + + +static void sensor() { + init_sensor(); + + uint8_t level[2], last_level[2] = {1, 1}; + uint64_t time[2] = {0, 0}; + uint32_t io_num; + uint8_t id; + + carriages = (uint64_t*)malloc(sizeof(uint64_t)*5); + for(int i = 0; i<5; i++)carriages[i] = 0; + uint8_t i = 0; + + while (1){ + if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) { + id = getId(io_num); + level[id] = gpio_get_level(io_num); + if(clock() - time[id] > EPS){ + + switch(level[id] - last_level[id]){ + case -1: //LOW vezérlés esetén jön a vonat + i = 0; + while(carriages[i] & ((uint64_t)((1<<16) - 1)<<(2*id*16))) i++; + carriages[i] |= ((uint64_t)(clock() * 100 / CLOCKS_PER_SEC)) << (2*id*16); //shifting the current time (on 16 bits) to its correct place (in 'centiseconds') -> max is reached after cca 10 minutes + break; + case 1: + i = 0; + while(carriages[i] & ((uint64_t)((1<<16) - 1)<<((2*id+1)*16))) i++; + carriages[i] |= ((uint64_t)(clock() * 100 / CLOCKS_PER_SEC - (uint64_t)((carriages[i] & ((uint64_t)((1<<16) - 1)<<(2*id*16)))>>(2*id*16)))) << ((2*id+1)*16); //shifting the time difference (on 16 bits) to its correct place + if(is_full(carriages[i])) calculate(&(carriages[i])); + break; + default: + break; + } + time[id] = clock(); + last_level[id] = level[id]; + } + + } + } +} + +uint8_t is_full(uint64_t carriage) +{ + for(int i = 0; i<4; i++){ + if(!(uint16_t)(carriage>>i*16)) return 0; + } + return 1; +} + +uint8_t getId(uint32_t pin){ + switch(pin){ + case GPIO_INPUT_IO_0: + return 0; + break; + case GPIO_INPUT_IO_1: + return 1; + break; + } + return -1; +} + + +void start_sensor(TaskHandle_t* task){ + xTaskCreatePinnedToCore(sensor, "sensor", 8192, NULL, 10, task, 0); +} + +void init_sensor(){ + gpio_config_t io_conf; + io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = 1; + gpio_config(&io_conf); + gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE); + gpio_set_intr_type(GPIO_INPUT_IO_1, GPIO_INTR_ANYEDGE); + gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t)); + gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); + gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0); + gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1); +} \ No newline at end of file diff --git a/src/cpp/sensor_monitor/esp32/main/sensor.h b/src/cpp/sensor_monitor/esp32/main/sensor.h new file mode 100644 index 000000000..52dc02726 --- /dev/null +++ b/src/cpp/sensor_monitor/esp32/main/sensor.h @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/gpio.h" +#include "time.h" + + +#define GPIO_INPUT_IO_0 13 +#define GPIO_INPUT_IO_1 27 +#define GPIO_INPUT_PIN_SEL ((1<(arg); + while(1){ + moni->update(); + } +} + + +void printHex(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + //Serial.print(buffer[i] < 0x10 ? " 0" : " "); + //Serial.print(buffer[i], HEX); + } +} + + + +void SensorMonitor::update(){ + + //local variables + uint16_t red=-1,green=-1,blue=-1; + int rawX=-1,rawY=-1,rawZ=-1; + double accX=-1,accY=-1,accZ=-1; + + if(running){ + //Serial.println("updating"); + isRunning=true; + //local variables + uint16_t red=-1,green=-1,blue=-1; + int rawX=-1,rawY=-1,rawZ=-1; + double accX=-1,accY=-1,accZ=-1; + + //Serial.println("read sensor data..."); + + //save timestamp + timeStack.push_back(millis()); + + + //read color sensor + //Serial.println("read color sensor"); + color.readRedLight(red); + color.readBlueLight(blue); + color.readGreenLight(green); + + /*old gyro sensor + sensors_event_t gyroData; + gyro.getEvent(&gyroData); + */ + + //read gyro sensor + //Serial.println("read gyro sensor"); + BMI160.readGyro(rawX, rawY, rawZ); + + accX=convertRawGyro(rawX); + accY=convertRawGyro(rawY); + accZ=convertRawGyro(rawZ); + + //read RFID sensor + //Serial.println("read rfid sensor"); + + if ( rfid.PICC_IsNewCardPresent()) + if (rfid.PICC_ReadCardSerial()){ + + //Serial.println(F("The NUID tag is:")); + //Serial.print(F("In hex: ")); + prevNUID = String(rfid.uid.uidByte[0]) +" >>> "+ String(rfid.uid.uidByte[1]) +" >>> "+ String(rfid.uid.uidByte[2]) +" >>> "+ String(rfid.uid.uidByte[3]); + //Serial.println(prevNUID); + //rfidStack.push_back(prevNUID); + } + + + + + //Serial.println("saving data..."); + + gyroXStack.push_back(accX); + gyroYStack.push_back(accY); + gyroZStack.push_back(accZ); + + + colorRStack.push_back(red); + colorBStack.push_back(blue); + colorGStack.push_back(green); + + //Serial.println("data has saved successfully"); + + }else{ + isRunning=false; + } + +} + + +SensorMonitor::SensorMonitor(){ + //Serial.println("initialising the sensors..."); + + xTaskCreatePinnedToCore(updateCallback,"update",50000,this,3,NULL,0); + + //initialise the color sensor + color.init(); + color.enableLightSensor(false); + + /*old gyro sensor + * gyro.begin(); + */ + + //initialise the gyro sensor + BMI160.begin(BMI160GenClass::SPI_MODE, ssPinGyro); + BMI160.setGyroRange(250); + + //initialise RFID sensor + SPI.begin(); + rfid.PCD_Init(); + + //Serial.println("sensors successfully initialised"); +} + + +void SensorMonitor::start(){ + //Serial.println("start monitoring"); + prevNUID="NONE"; + clear(); + running=true; +} + +void SensorMonitor::stop(){ + //Serial.println("stop monitoring..."); + running=false; + while(isRunning){ + //Serial.println("waiting for core 0"); + delay(5); + } + //Serial.println("monitoring has stopped"); +} + +void SensorMonitor::clear(){ + timeStack.clear(); + colorRStack.clear(); + colorGStack.clear(); + colorBStack.clear(); + gyroXStack.clear(); + gyroYStack.clear(); + gyroZStack.clear(); +} + +std::list SensorMonitor::getR()const{ + if(!running && !isRunning){ + return colorRStack; + } + //baj van a programnak le kell, hogy fagyjon + while(1); +} + +std::list SensorMonitor::getG()const{ + if(!running && !isRunning){ + return colorGStack; + } + //baj van a programnak le kell, hogy fagyjon + while(1); +} + +std::list SensorMonitor::getB()const{ + if(!running && !isRunning){ + return colorBStack; + } + //baj van a programnak le kell, hogy fagyjon + while(1); +} + +std::list SensorMonitor::getX()const{ + if(!running && !isRunning){ + return colorRStack; + } + //baj van a programnak le kell, hogy fagyjon + while(1); +} + +std::list SensorMonitor::getY()const{ + if(!running && !isRunning){ + return colorRStack; + } + //baj van a programnak le kell, hogy fagyjon + while(1); +} + +std::list SensorMonitor::getZ()const{ + if(!running && !isRunning){ + return colorRStack; + } + //baj van a programnak le kell, hogy fagyjon + while(1); +} + +std::list SensorMonitor::getT()const{ + if(!running && !isRunning){ + return timeStack; + } + //baj van a programnak le kell, hogy fagyjon + //Serial.println("the reading of data has not been stopped"); + //Serial.println("restarting the core"); + while(1); +} + +String SensorMonitor::getRFID()const{ + if(!running && !isRunning){ + return prevNUID; + } + //baj van a programnak le kell, hogy fagyjon + //Serial.println("the reading of data has not been stopped"); + //Serial.println("restarting the core"); + while(1); +} + + + diff --git a/src/cpp/sensor_monitor/sensor_monitor.h b/src/cpp/sensor_monitor/sensor_monitor.h new file mode 100644 index 000000000..a3b9bfaaa --- /dev/null +++ b/src/cpp/sensor_monitor/sensor_monitor.h @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#ifndef SENSOR_MONITOR +#define SENSOR_MONITOR + + + +class SensorMonitor{ + private: + Adafruit_LSM303_Accel_Unified gyro=Adafruit_LSM303_Accel_Unified(54321); + SparkFun_APDS9960 color=SparkFun_APDS9960(); + MFRC522 rfid; + MFRC522::MIFARE_Key key; + const int coreID=1; + const int ssPinGyro=16; + const int ssPinRFID=5; + const int rstPinRFID=17; + bool running; + bool isRunning=false; + std::list colorRStack; + std::list colorGStack; + std::list colorBStack; + std::list gyroXStack; + std::list gyroYStack; + std::list gyroZStack; + std::list timeStack; + std::list rfidStack; + public: + String prevNUID; + SensorMonitor(); + void start(); + void stop(); + void clear(); + std::list getR() const; + std::list getG() const; + std::list getB() const; + std::list getX() const; + std::list getY() const; + std::list getZ() const; + std::list getT() const; + String getRFID() const; + void update(); +}; + + +#endif diff --git a/src/cpp/sensor_monitor/sensor_monitor.ino b/src/cpp/sensor_monitor/sensor_monitor.ino new file mode 100644 index 000000000..1a7885f98 --- /dev/null +++ b/src/cpp/sensor_monitor/sensor_monitor.ino @@ -0,0 +1,50 @@ +#include "sensor_monitor.h" + + + +void setup() { + //Serial.begin(115200); + //Serial.println("start testing..."); + SensorMonitor sm=SensorMonitor(); + while(1){ + + sm.start(); + + delay(1000); + + sm.stop(); + + std::list timestamp=sm.getT(); + std::list r=sm.getR(); + std::list x=sm.getX(); + //Serial.println("send to serial the data..."); + //Serial.print("size of the list: "); + //Serial.println(timestamp.size()); + for(int i=timestamp.size();i>0;i--){ + //Serial.print(timestamp.front()); + //Serial.print(" : "); + //Serial.print(r.front()); + //Serial.print(" : "); + //Serial.print(x.front()); + ////Serial.println(ref.front()); + + timestamp.pop_front(); + x.pop_front(); + r.pop_front(); + + //ref.pop_front(); + } + + //Serial.println(sm.prevNUID); + + + sm.clear(); + + } + // put your setup code here, to run once: + +} + +void loop() { + // put your main code here, to run repeatedly: +} diff --git a/src/hardware/IoT watchman/test b/src/hardware/IoT watchman/test new file mode 100644 index 000000000..1a193b104 --- /dev/null +++ b/src/hardware/IoT watchman/test @@ -0,0 +1 @@ +This folder contains the source files of the hardware of the watchman.