fix(common): Improve linux port

This commit is contained in:
David Cermak
2023-04-14 15:57:12 +02:00
parent 588465d9db
commit 7d4755f119
10 changed files with 91 additions and 64 deletions

View File

@ -9,6 +9,11 @@
#include <stdint.h> #include <stdint.h>
#include "bsd/string.h" #include "bsd/string.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct esp_timer *esp_timer_handle_t; typedef struct esp_timer *esp_timer_handle_t;
typedef void (*esp_timer_cb_t)(void *arg); 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); esp_err_t esp_timer_delete(esp_timer_handle_t timer);
int64_t esp_timer_get_time(void); int64_t esp_timer_get_time(void);
#ifdef __cplusplus
}
#endif

View File

@ -117,7 +117,7 @@ void vQueueDelete( QueueHandle_t xQueue )
{ {
struct generic_queue_handle *h = xQueue; struct generic_queue_handle *h = xQueue;
if (h->q) { if (h->q) {
if (h->type == MUTEX) { if (h->type == MUTEX || h->type == MUTEX_REC) {
osal_mutex_delete(h->q); osal_mutex_delete(h->q);
} else { } else {
osal_queue_delete(h->q); osal_queue_delete(h->q);

View File

@ -32,6 +32,5 @@ typedef int BaseType_t;
#define pdMS_TO_TICKS(tick) (tick) #define pdMS_TO_TICKS(tick) (tick)
uint32_t esp_get_free_heap_size(void);
uint32_t esp_random(void); uint32_t esp_random(void);
void vTaskSuspendAll(void); void vTaskSuspendAll(void);

View File

@ -7,6 +7,9 @@
#include <condition_variable> #include <condition_variable>
#include "osal_api.h" #include "osal_api.h"
class SignalGroup {
struct SignalGroupInternal { struct SignalGroupInternal {
std::condition_variable notify; std::condition_variable notify;
std::mutex m; std::mutex m;
@ -15,11 +18,8 @@ struct SignalGroupInternal {
using SignalT = std::unique_ptr<SignalGroupInternal>; using SignalT = std::unique_ptr<SignalGroupInternal>;
class SignalGroup {
public: public:
explicit SignalGroup(): event_group(std::make_unique<SignalGroupInternal>()) {}
void set(uint32_t bits) void set(uint32_t bits)
{ {
std::unique_lock<std::mutex> lock(event_group->m); std::unique_lock<std::mutex> lock(event_group->m);
@ -61,10 +61,8 @@ public:
return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] { return flags & event_group->flags; }); return event_group->notify.wait_for(lock, std::chrono::milliseconds(time_ms), [&] { return flags & event_group->flags; });
} }
~SignalGroup() = default;
private: private:
SignalT event_group; SignalT event_group{std::make_unique<SignalGroupInternal>()};
}; };

View File

@ -16,9 +16,6 @@
template <class T> template <class T>
class Queue { class Queue {
public: public:
Queue(): q(), m(), c() {}
~Queue() {}
void send(std::unique_ptr<T> t) void send(std::unique_ptr<T> t)
{ {
std::lock_guard<std::mutex> lock(m); std::lock_guard<std::mutex> lock(m);
@ -26,11 +23,11 @@ public:
c.notify_one(); c.notify_one();
} }
std::unique_ptr<T> receive(uint32_t ms) std::unique_ptr<T> receive(std::chrono::milliseconds ms)
{ {
std::unique_lock<std::mutex> lock(m); std::unique_lock<std::mutex> lock(m);
while (q.empty()) { 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; return nullptr;
} }
} }
@ -40,28 +37,30 @@ public:
} }
private: private:
std::queue<std::unique_ptr<T>> q; std::queue<std::unique_ptr<T>> q{};
mutable std::mutex m; mutable std::mutex m{};
std::condition_variable c; std::condition_variable c{};
}; };
using item_t = std::vector<uint8_t>;
void *osal_queue_create(void) void *osal_queue_create(void)
{ {
auto *q = new Queue<std::vector<uint8_t>>(); auto *q = new Queue<item_t>();
return q; return q;
} }
void osal_queue_delete(void *q) void osal_queue_delete(void *q)
{ {
auto *queue = static_cast<Queue<std::vector<uint8_t>> *>(q); auto *queue = static_cast<Queue<item_t> *>(q);
delete (queue); delete (queue);
} }
bool osal_queue_send(void *q, uint8_t *data, size_t len) bool osal_queue_send(void *q, uint8_t *data, size_t len)
{ {
auto v = std::make_unique<std::vector<uint8_t>>(len); auto v = std::make_unique<item_t>(len);
v->assign(data, data + len); v->assign(data, data + len);
auto queue = static_cast<Queue<std::vector<uint8_t>> *>(q); auto queue = static_cast<Queue<item_t> *>(q);
queue->send(std::move(v)); queue->send(std::move(v));
return true; 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) bool osal_queue_recv(void *q, uint8_t *data, size_t len, uint32_t ms)
{ {
auto queue = static_cast<Queue<std::vector<uint8_t>> *>(q); auto queue = static_cast<Queue<item_t> *>(q);
auto v = queue->receive(ms); auto v = queue->receive(std::chrono::milliseconds(ms));
if (v == nullptr) { if (v != nullptr) {
return false;
}
memcpy(data, (void *)v->data(), len); memcpy(data, (void *)v->data(), len);
return true; return true;
} }
return false;
}

View File

@ -1,19 +1,21 @@
# The following four lines of boilerplate have to be in your project's CMakeLists # This project serves as a demo to enable using esp-mqtt on ESP platform targets as well as on linux
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
# (Not part of the boilerplate) # For ESP32 platform target
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
if(${IDF_TARGET} STREQUAL "linux") 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) if(WITH_LWIP STREQUAL 1)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_tapif_io set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_tapif_io
"../../common_components/linux_compat/esp_timer") "../../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) set(COMPONENTS main esp_netif lwip protocol_examples_tapif_io startup esp_hw_support esp_system nvs_flash mqtt esp_timer)
else() else()
list(APPEND EXTRA_COMPONENT_DIRS list(APPEND EXTRA_COMPONENT_DIRS
"../../common_components/linux_compat/freertos"
"../../common_components/linux_compat/esp_timer" "../../common_components/linux_compat/esp_timer"
"$ENV{IDF_PATH}/examples/protocols/linux_stubs/esp_stubs") "$ENV{IDF_PATH}/examples/protocols/linux_stubs/esp_stubs")
set(COMPONENTS main nvs_flash esp-tls esp_stubs mqtt protocol_examples_common esp_timer) 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() endif()
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mqtt_tcp) project(esp_mqtt_demo)

41
examples/mqtt/README.md Normal file
View File

@ -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)

View File

@ -2,9 +2,3 @@ idf_component_register(SRCS "app_main.cpp"
INCLUDE_DIRS ".") INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") 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()

View File

@ -6,8 +6,4 @@ menu "Example Configuration"
help help
URL of the broker to connect to URL of the broker to connect to
config BROKER_URL_FROM_STDIN
bool
default y if BROKER_URL = "FROM_STDIN"
endmenu endmenu

View File

@ -12,15 +12,13 @@
#include "esp_event.h" #include "esp_event.h"
#include "esp_netif.h" #include "esp_netif.h"
#include "protocol_examples_common.h" #include "protocol_examples_common.h"
#include "esp_netif.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h" #include "esp_system.h"
#include "esp_log.h" #include "esp_log.h"
#include "mqtt_client.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) 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); esp_mqtt_client_start(client);
} }
#include "esp_netif.h"
#include "netdb.h"
extern "C" void app_main(void) 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("*", ESP_LOG_INFO);
esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE);
esp_log_level_set("MQTT_EXAMPLE", 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("transport_base", ESP_LOG_VERBOSE);
esp_log_level_set("esp-tls", 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_log_level_set("outbox", ESP_LOG_VERBOSE);
ESP_ERROR_CHECK(nvs_flash_init()); 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. * examples/protocols/README.md for more information about this function.
*/ */
ESP_ERROR_CHECK(example_connect()); 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(); mqtt_app_start();
vTaskDelay(pdMS_TO_TICKS(1000));
} }