From 7d4755f119e37f4e14b0ad131d9fa410ee907793 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 14 Apr 2023 15:57:12 +0200 Subject: [PATCH] fix(common): Improve linux port --- .../esp_timer/include/esp_timer.h | 9 ++++ .../linux_compat/freertos/freertos_linux.c | 2 +- .../freertos/include/freertos/FreeRTOS.h | 1 - .../freertos/osal/event_group.cpp | 22 +++++----- .../linux_compat/freertos/osal/queue.cpp | 35 ++++++++-------- examples/mqtt/CMakeLists.txt | 14 ++++--- examples/mqtt/README.md | 41 +++++++++++++++++++ examples/mqtt/main/CMakeLists.txt | 6 --- examples/mqtt/main/Kconfig.projbuild | 4 -- examples/mqtt/main/app_main.cpp | 21 +++------- 10 files changed, 91 insertions(+), 64 deletions(-) create mode 100644 examples/mqtt/README.md diff --git a/common_components/linux_compat/esp_timer/include/esp_timer.h b/common_components/linux_compat/esp_timer/include/esp_timer.h index aa27c9d93..8267e403d 100644 --- a/common_components/linux_compat/esp_timer/include/esp_timer.h +++ b/common_components/linux_compat/esp_timer/include/esp_timer.h @@ -9,6 +9,11 @@ #include #include "bsd/string.h" +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct esp_timer *esp_timer_handle_t; typedef void (*esp_timer_cb_t)(void *arg); @@ -34,3 +39,7 @@ esp_err_t esp_timer_stop(esp_timer_handle_t timer); esp_err_t esp_timer_delete(esp_timer_handle_t timer); int64_t esp_timer_get_time(void); + +#ifdef __cplusplus +} +#endif diff --git a/common_components/linux_compat/freertos/freertos_linux.c b/common_components/linux_compat/freertos/freertos_linux.c index 40a3a2241..277805760 100644 --- a/common_components/linux_compat/freertos/freertos_linux.c +++ b/common_components/linux_compat/freertos/freertos_linux.c @@ -117,7 +117,7 @@ void vQueueDelete( QueueHandle_t xQueue ) { struct generic_queue_handle *h = xQueue; if (h->q) { - if (h->type == MUTEX) { + if (h->type == MUTEX || h->type == MUTEX_REC) { osal_mutex_delete(h->q); } else { osal_queue_delete(h->q); diff --git a/common_components/linux_compat/freertos/include/freertos/FreeRTOS.h b/common_components/linux_compat/freertos/include/freertos/FreeRTOS.h index 321863227..b8d553b74 100644 --- a/common_components/linux_compat/freertos/include/freertos/FreeRTOS.h +++ b/common_components/linux_compat/freertos/include/freertos/FreeRTOS.h @@ -32,6 +32,5 @@ typedef int BaseType_t; #define pdMS_TO_TICKS(tick) (tick) -uint32_t esp_get_free_heap_size(void); uint32_t esp_random(void); void vTaskSuspendAll(void); diff --git a/common_components/linux_compat/freertos/osal/event_group.cpp b/common_components/linux_compat/freertos/osal/event_group.cpp index 24438416f..8548f957a 100644 --- a/common_components/linux_compat/freertos/osal/event_group.cpp +++ b/common_components/linux_compat/freertos/osal/event_group.cpp @@ -7,18 +7,18 @@ #include #include "osal_api.h" -struct SignalGroupInternal { - std::condition_variable notify; - std::mutex m; - uint32_t flags{ 0 }; -}; - -using SignalT = std::unique_ptr; class SignalGroup { -public: - explicit SignalGroup(): event_group(std::make_unique()) {} + struct SignalGroupInternal { + std::condition_variable notify; + std::mutex m; + uint32_t flags{ 0 }; + }; + + using SignalT = std::unique_ptr; + +public: void set(uint32_t bits) { @@ -61,10 +61,8 @@ public: return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] { return flags & event_group->flags; }); } - ~SignalGroup() = default; - private: - SignalT event_group; + SignalT event_group{std::make_unique()}; }; diff --git a/common_components/linux_compat/freertos/osal/queue.cpp b/common_components/linux_compat/freertos/osal/queue.cpp index 2be3a9826..54d74f003 100644 --- a/common_components/linux_compat/freertos/osal/queue.cpp +++ b/common_components/linux_compat/freertos/osal/queue.cpp @@ -16,9 +16,6 @@ template class Queue { public: - Queue(): q(), m(), c() {} - ~Queue() {} - void send(std::unique_ptr t) { std::lock_guard lock(m); @@ -26,11 +23,11 @@ public: c.notify_one(); } - std::unique_ptr receive(uint32_t ms) + std::unique_ptr receive(std::chrono::milliseconds ms) { std::unique_lock lock(m); while (q.empty()) { - if (c.wait_for(lock, std::chrono::milliseconds(ms)) == std::cv_status::timeout) { + if (c.wait_for(lock, ms) == std::cv_status::timeout) { return nullptr; } } @@ -40,28 +37,30 @@ public: } private: - std::queue> q; - mutable std::mutex m; - std::condition_variable c; + std::queue> q{}; + mutable std::mutex m{}; + std::condition_variable c{}; }; +using item_t = std::vector; + void *osal_queue_create(void) { - auto *q = new Queue>(); + auto *q = new Queue(); return q; } void osal_queue_delete(void *q) { - auto *queue = static_cast> *>(q); + auto *queue = static_cast *>(q); delete (queue); } bool osal_queue_send(void *q, uint8_t *data, size_t len) { - auto v = std::make_unique>(len); + auto v = std::make_unique(len); v->assign(data, data + len); - auto queue = static_cast> *>(q); + auto queue = static_cast *>(q); queue->send(std::move(v)); return true; } @@ -69,11 +68,11 @@ bool osal_queue_send(void *q, uint8_t *data, size_t len) bool osal_queue_recv(void *q, uint8_t *data, size_t len, uint32_t ms) { - auto queue = static_cast> *>(q); - auto v = queue->receive(ms); - if (v == nullptr) { - return false; + auto queue = static_cast *>(q); + auto v = queue->receive(std::chrono::milliseconds(ms)); + if (v != nullptr) { + memcpy(data, (void *)v->data(), len); + return true; } - memcpy(data, (void *)v->data(), len); - return true; + return false; } diff --git a/examples/mqtt/CMakeLists.txt b/examples/mqtt/CMakeLists.txt index 522cb199e..25cc297c6 100644 --- a/examples/mqtt/CMakeLists.txt +++ b/examples/mqtt/CMakeLists.txt @@ -1,19 +1,21 @@ -# The following four lines of boilerplate have to be in your project's CMakeLists -# in this exact order for cmake to work correctly +# This project serves as a demo to enable using esp-mqtt on ESP platform targets as well as on linux cmake_minimum_required(VERSION 3.16) -# (Not part of the boilerplate) -# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. +# For ESP32 platform target set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) if(${IDF_TARGET} STREQUAL "linux") +# For linux-target we have two options: +# - With lwIP (must be defined on command line, e.g. idf.py -DWITH_LWIP=1) +# access networking from linux `tap` interface (TAP networking mode) +# - Without lwIP (must be defined on command line, e.g. idf.py -DWITH_LWIP=0) +# no designated interface, accesses user network via linux/socket sys calls if(WITH_LWIP STREQUAL 1) set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_tapif_io "../../common_components/linux_compat/esp_timer") set(COMPONENTS main esp_netif lwip protocol_examples_tapif_io startup esp_hw_support esp_system nvs_flash mqtt esp_timer) else() list(APPEND EXTRA_COMPONENT_DIRS - "../../common_components/linux_compat/freertos" "../../common_components/linux_compat/esp_timer" "$ENV{IDF_PATH}/examples/protocols/linux_stubs/esp_stubs") set(COMPONENTS main nvs_flash esp-tls esp_stubs mqtt protocol_examples_common esp_timer) @@ -21,4 +23,4 @@ if(${IDF_TARGET} STREQUAL "linux") endif() include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(mqtt_tcp) +project(esp_mqtt_demo) diff --git a/examples/mqtt/README.md b/examples/mqtt/README.md new file mode 100644 index 000000000..acf680b75 --- /dev/null +++ b/examples/mqtt/README.md @@ -0,0 +1,41 @@ +# MQTT demo application that runs on linux + +## Overview + +This is a simple example demonstrating connecting to an MQTT broker, subscribing and publishing some data. +This example uses IDF build system and could be configured to be build and executed: +* for any ESP32 family chip +* for linux target + +## How to use example + +### Hardware Required + +To run this example, you need any ESP32 development board or just PC/virtual machine/container running linux operating system. + +### Host build modes + +Linux build is supported in these two modes: +* `WITH_LWIP=0`: Without lwIP component. The project uses linux BSD socket interface to interact with TCP/IP stack. There's no connection phase, we use the host network as users. This mode is often referred to as user-side networking. +* `WITH_LWIP=1`: Including lwIP component, which is added to the list of required components and compiled on host. In this mode, we have to map the host network (linux TCP/IP stack) to the target network (lwip). We use IDF's [`tapif_io`](https://github.com/espressif/esp-idf/tree/master/examples/common_components/protocol_examples_tapif_io) component to create a network interface, which will be used to pass packets to and from the simulated target. Please refer to the [README](https://github.com/espressif/esp-idf/tree/master/examples/common_components/protocol_examples_tapif_io#readme) for more details about the host side networking. + +### Building on linux + +1) Configure linux target +```bash +idf.py --preview set-target linux +``` + +2) Build the project with preferred components (with or without lwip) +```bash +idf.py -DWITH_LWIP=0 build # Building without lwip (user networking) +idf.py -DWITH_LWIP=1 build # Building with lwip (TAP networking) +``` + +3) Run the project + +It is possible to run the project elf file directly, or using `idf.py` monitor target (no need to flash): +```bash +idf.py monitor +``` +idf.py -DWITH_LWIP=0 build # Building without lwip (user networking) diff --git a/examples/mqtt/main/CMakeLists.txt b/examples/mqtt/main/CMakeLists.txt index eafc10f15..df13a8f41 100644 --- a/examples/mqtt/main/CMakeLists.txt +++ b/examples/mqtt/main/CMakeLists.txt @@ -2,9 +2,3 @@ idf_component_register(SRCS "app_main.cpp" INCLUDE_DIRS ".") target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") - -if(${IDF_TARGET} STREQUAL "linux") - if(WITH_LWIP STREQUAL "1") - target_compile_definitions(${COMPONENT_LIB} PUBLIC WITH_LWIP) - endif() -endif() diff --git a/examples/mqtt/main/Kconfig.projbuild b/examples/mqtt/main/Kconfig.projbuild index c11539fb8..e9f684b85 100644 --- a/examples/mqtt/main/Kconfig.projbuild +++ b/examples/mqtt/main/Kconfig.projbuild @@ -6,8 +6,4 @@ menu "Example Configuration" help URL of the broker to connect to - config BROKER_URL_FROM_STDIN - bool - default y if BROKER_URL = "FROM_STDIN" - endmenu diff --git a/examples/mqtt/main/app_main.cpp b/examples/mqtt/main/app_main.cpp index fe6a6b37e..f0bd5fa38 100644 --- a/examples/mqtt/main/app_main.cpp +++ b/examples/mqtt/main/app_main.cpp @@ -12,15 +12,13 @@ #include "esp_event.h" #include "esp_netif.h" #include "protocol_examples_common.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" +#include "esp_netif.h" #include "esp_system.h" #include "esp_log.h" #include "mqtt_client.h" -static const char *TAG = "MQTT_EXAMPLE"; +static const char *TAG = "esp_mqtt_demo"; static void log_error_if_nonzero(const char *message, int error_code) @@ -109,8 +107,6 @@ static void mqtt_app_start(void) esp_mqtt_client_start(client); } -#include "esp_netif.h" -#include "netdb.h" extern "C" void app_main(void) { @@ -120,10 +116,10 @@ extern "C" void app_main(void) esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); - esp_log_level_set("MQTT_EXAMPLE", ESP_LOG_VERBOSE); - esp_log_level_set("TRANSPORT_BASE", ESP_LOG_VERBOSE); + esp_log_level_set("esp_mqtt_demo", ESP_LOG_VERBOSE); + esp_log_level_set("transport_base", ESP_LOG_VERBOSE); esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); - esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE); + esp_log_level_set("transport", ESP_LOG_VERBOSE); esp_log_level_set("outbox", ESP_LOG_VERBOSE); ESP_ERROR_CHECK(nvs_flash_init()); @@ -135,13 +131,6 @@ extern "C" void app_main(void) * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); -#if CONFIG_IDF_TARGET_LINUX && WITH_LWIP - esp_netif_dns_info_t dns; - dns.ip.u_addr.ip4.addr = ipaddr_addr("8.8.8.8"); - dns.ip.type = IPADDR_TYPE_V4; - ESP_ERROR_CHECK(esp_netif_set_dns_info(esp_netif_get_handle_from_ifkey("TAP"), ESP_NETIF_DNS_MAIN, &dns)); -#endif mqtt_app_start(); - vTaskDelay(pdMS_TO_TICKS(1000)); }