mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-06-25 09:21:32 +02:00
fix(wifi_remote): Make services restartable, code cleanup
This commit is contained in:
2
.github/workflows/wifi_remote__build.yml
vendored
2
.github/workflows/wifi_remote__build.yml
vendored
@ -48,7 +48,7 @@ jobs:
|
||||
run: |
|
||||
${IDF_PATH}/install.sh --enable-pytest
|
||||
. ${IDF_PATH}/export.sh
|
||||
echo "python ./ci/build_apps.py ./components/esp_wifi_remote/${{matrix.test.path}} -vv --preserve-all"
|
||||
python ./ci/build_apps.py ./components/esp_wifi_remote/${{matrix.test.path}} -vv --preserve-all
|
||||
|
||||
build_wifi_remote_example:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'wifi_remote') || github.event_name == 'push'
|
||||
|
@ -15,35 +15,20 @@ choice ESP_WIFI_REMOTE_LIBRARY
|
||||
endchoice
|
||||
|
||||
if ESP_WIFI_REMOTE_LIBRARY_EPPP
|
||||
menu "WiFi remote by EPPP"
|
||||
|
||||
choice ESP_WIFI_REMOTE_EPPP_TRANSPORT
|
||||
prompt "Choose EPPP transport"
|
||||
default ESP_WIFI_REMOTE_EPPP_TRANSPORT_UART
|
||||
config ESP_WIFI_REMOTE_EPPP_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
default 10
|
||||
range 0 31
|
||||
help
|
||||
Select type of EPPP transport
|
||||
Pin number of UART TX.
|
||||
|
||||
config ESP_WIFI_REMOTE_EPPP_TRANSPORT_UART
|
||||
bool "UART"
|
||||
config ESP_WIFI_REMOTE_EPPP_TRANSPORT_SPI
|
||||
bool "SPI"
|
||||
endchoice
|
||||
|
||||
if ESP_WIFI_REMOTE_EPPP_TRANSPORT_UART
|
||||
config ESP_WIFI_REMOTE_EPPP_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
default 10
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART TX.
|
||||
|
||||
config ESP_WIFI_REMOTE_EPPP_UART_RX_PIN
|
||||
int "RXD Pin Number"
|
||||
default 11
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RX.
|
||||
endif
|
||||
config ESP_WIFI_REMOTE_EPPP_UART_RX_PIN
|
||||
int "RXD Pin Number"
|
||||
default 11
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RX.
|
||||
|
||||
config ESP_WIFI_REMOTE_EPPP_SERVER_CA
|
||||
string "Servers CA certificate"
|
||||
@ -68,6 +53,5 @@ endchoice
|
||||
config ESP_WIFI_REMOTE_EPPP_SERVER_KEY
|
||||
string "Server key"
|
||||
default "--- Please copy content of the Client key ---"
|
||||
endmenu
|
||||
|
||||
endif
|
||||
|
@ -7,17 +7,14 @@
|
||||
#include "esp_wifi.h"
|
||||
#include "eppp_link.h"
|
||||
|
||||
esp_netif_t *wifi_remote_eppp_init(eppp_type_t role)
|
||||
__attribute__((weak)) esp_netif_t *wifi_remote_eppp_init(eppp_type_t role)
|
||||
{
|
||||
uint32_t our_ip = role == EPPP_SERVER ? EPPP_DEFAULT_SERVER_IP() : EPPP_DEFAULT_CLIENT_IP();
|
||||
uint32_t their_ip = role == EPPP_SERVER ? EPPP_DEFAULT_CLIENT_IP() : EPPP_DEFAULT_SERVER_IP();
|
||||
eppp_config_t config = EPPP_DEFAULT_CONFIG(our_ip, their_ip);
|
||||
#if CONFIG_ESP_WIFI_REMOTE_EPPP_TRANSPORT_UART
|
||||
// We currently support only UART transport
|
||||
config.transport = EPPP_TRANSPORT_UART;
|
||||
config.uart.tx_io = CONFIG_ESP_WIFI_REMOTE_EPPP_UART_TX_PIN;
|
||||
config.uart.rx_io = CONFIG_ESP_WIFI_REMOTE_EPPP_UART_RX_PIN;
|
||||
#else
|
||||
#error ESP_WIFI_REMOTE supports only UART transport
|
||||
#endif
|
||||
return eppp_open(role, &config, portMAX_DELAY);
|
||||
}
|
||||
|
@ -26,12 +26,15 @@ const char *TAG = "rpc_client";
|
||||
const unsigned char ca_crt[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_CA "\n-----END CERTIFICATE-----";
|
||||
const unsigned char crt[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_CRT "\n-----END CERTIFICATE-----";
|
||||
const unsigned char key[] = "-----BEGIN RSA PRIVATE KEY-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_KEY "\n-----END RSA PRIVATE KEY-----";
|
||||
// TODO: Add option to supply keys and certs via a global symbol (file)
|
||||
|
||||
}
|
||||
|
||||
using namespace client;
|
||||
|
||||
struct Sync {
|
||||
class Sync {
|
||||
friend class RpcInstance;
|
||||
public:
|
||||
void lock()
|
||||
{
|
||||
xSemaphoreTake(mutex, portMAX_DELAY);
|
||||
@ -48,7 +51,7 @@ struct Sync {
|
||||
}
|
||||
esp_err_t wait_for(EventBits_t bits, uint32_t timeout = portMAX_DELAY)
|
||||
{
|
||||
return xEventGroupWaitBits(events, bits, pdTRUE, pdTRUE, timeout) == bits ? ESP_OK : ESP_FAIL;
|
||||
return (xEventGroupWaitBits(events, bits, pdTRUE, pdTRUE, timeout) & bits) == bits ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
esp_err_t notify(EventBits_t bits)
|
||||
{
|
||||
@ -64,30 +67,36 @@ struct Sync {
|
||||
vEventGroupDelete(events);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
SemaphoreHandle_t mutex{nullptr};
|
||||
EventGroupHandle_t events{nullptr};
|
||||
|
||||
const int request = 1;
|
||||
const int resp_header = 2;
|
||||
const int resp_payload = 4;
|
||||
|
||||
const int restart = 8;
|
||||
};
|
||||
|
||||
class RpcInstance {
|
||||
friend class Sync;
|
||||
public:
|
||||
|
||||
template<typename T>
|
||||
esp_err_t send(api_id id, T *t)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(sync.notify(sync.request), TAG, "failed to notify req");
|
||||
pending_resp = id;
|
||||
ESP_RETURN_ON_ERROR(sync.notify(sync.request), TAG, "failed to notify req");
|
||||
ESP_RETURN_ON_ERROR(rpc.send<T>(id, t), TAG, "Failed to send request");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// specialization for (void)
|
||||
// overload of the templated method (used for functions with no arguments)
|
||||
esp_err_t send(api_id id)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(sync.notify(sync.request), TAG, "failed to notify req");
|
||||
pending_resp = id;
|
||||
ESP_RETURN_ON_ERROR(sync.notify(sync.request), TAG, "failed to notify req");
|
||||
ESP_RETURN_ON_ERROR(rpc.send(id), TAG, "Failed to send request");
|
||||
return ESP_OK;
|
||||
}
|
||||
@ -103,6 +112,7 @@ public:
|
||||
esp_err_t init()
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(netif = wifi_remote_eppp_init(EPPP_CLIENT), ESP_FAIL, TAG, "Failed to connect to EPPP server");
|
||||
ESP_RETURN_ON_ERROR(esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, got_ip, this), TAG, "Failed to register event");
|
||||
ESP_RETURN_ON_ERROR(sync.init(), TAG, "Failed to init sync primitives");
|
||||
ESP_RETURN_ON_ERROR(rpc.init(), TAG, "Failed to init RPC engine");
|
||||
return xTaskCreate(task, "client", 8192, this, 5, nullptr) == pdTRUE ? ESP_OK : ESP_FAIL;
|
||||
@ -168,15 +178,28 @@ private:
|
||||
static void task(void *ctx)
|
||||
{
|
||||
auto instance = static_cast<RpcInstance *>(ctx);
|
||||
while (instance->perform() == ESP_OK) {}
|
||||
do {
|
||||
while (instance->perform() == ESP_OK) {}
|
||||
} while (instance->restart() == ESP_OK);
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
esp_err_t restart()
|
||||
{
|
||||
rpc.deinit();
|
||||
ESP_RETURN_ON_ERROR(sync.wait_for(sync.restart, pdMS_TO_TICKS(10000)), TAG, "Didn't receive EPPP address in time");
|
||||
return rpc.init();
|
||||
}
|
||||
static void got_ip(void *ctx, esp_event_base_t base, int32_t id, void *data)
|
||||
{
|
||||
auto instance = static_cast<RpcInstance *>(ctx);
|
||||
instance->sync.notify(instance->sync.restart);
|
||||
}
|
||||
esp_netif_t *netif{nullptr};
|
||||
};
|
||||
|
||||
|
||||
namespace client {
|
||||
RpcInstance instance;
|
||||
constinit RpcInstance instance;
|
||||
} // namespace client
|
||||
|
||||
RpcInstance *RpcEngine::init_client()
|
||||
@ -196,20 +219,17 @@ RpcInstance *RpcEngine::init_client()
|
||||
cfg.clientkey_bytes = sizeof(client::key);
|
||||
cfg.common_name = "espressif.local";
|
||||
|
||||
tls_ = esp_tls_init();
|
||||
if (!tls_) {
|
||||
ESP_LOGE(TAG, "Failed to allocate esp_tls handle!");
|
||||
goto exit;
|
||||
}
|
||||
if (esp_tls_conn_new_sync(host, strlen(host), rpc_port, &cfg, tls_) <= 0) {
|
||||
ESP_LOGE(TAG, "Failed to open a new connection %s", host);
|
||||
goto exit;
|
||||
ESP_RETURN_ON_FALSE(tls_ = esp_tls_init(), nullptr, TAG, "Failed to create ESP-TLS instance");
|
||||
int retries = 0;
|
||||
while (esp_tls_conn_new_sync(host, strlen(host), rpc_port, &cfg, tls_) <= 0) {
|
||||
esp_tls_conn_destroy(tls_);
|
||||
tls_ = nullptr;
|
||||
ESP_RETURN_ON_FALSE(retries++ < 3, nullptr, TAG, "Failed to open connection to %s", host);
|
||||
ESP_LOGW(TAG, "Connection to RPC server failed! Will retry in %d second(s)", retries);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000 * retries));
|
||||
ESP_RETURN_ON_FALSE(tls_ = esp_tls_init(), nullptr, TAG, "Failed to create ESP-TLS instance");
|
||||
}
|
||||
return &client::instance;
|
||||
exit:
|
||||
esp_tls_conn_destroy(tls_);
|
||||
tls_ = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace eppp_rpc
|
||||
|
||||
|
@ -9,8 +9,11 @@
|
||||
|
||||
namespace eppp_rpc {
|
||||
|
||||
const int rpc_port = 3333;
|
||||
static constexpr int rpc_port = 3333;
|
||||
|
||||
/**
|
||||
* @brief Currently supported RPC commands/events
|
||||
*/
|
||||
enum class api_id : uint32_t {
|
||||
ERROR,
|
||||
UNDEF,
|
||||
@ -35,6 +38,9 @@ struct RpcHeader {
|
||||
uint32_t size;
|
||||
} __attribute((__packed__));
|
||||
|
||||
/**
|
||||
* @brief Structure holding the outgoing or incoming parameter
|
||||
*/
|
||||
template<typename T>
|
||||
struct RpcData {
|
||||
RpcHeader head;
|
||||
@ -54,11 +60,17 @@ struct RpcData {
|
||||
}
|
||||
} __attribute((__packed__));
|
||||
|
||||
/**
|
||||
* @brief Singleton holding the static data for either the client or server side
|
||||
*/
|
||||
class RpcInstance;
|
||||
|
||||
/**
|
||||
* @brief Engine that implements a simple RPC mechanism
|
||||
*/
|
||||
class RpcEngine {
|
||||
public:
|
||||
explicit RpcEngine(role r) : tls_(nullptr), role_(r) {}
|
||||
constexpr explicit RpcEngine(role r) : tls_(nullptr), role_(r) {}
|
||||
|
||||
esp_err_t init()
|
||||
{
|
||||
@ -74,6 +86,19 @@ public:
|
||||
return instance == nullptr ? ESP_FAIL : ESP_OK;
|
||||
}
|
||||
|
||||
void deinit()
|
||||
{
|
||||
if (tls_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (role_ == role::CLIENT) {
|
||||
esp_tls_conn_destroy(tls_);
|
||||
} else if (role_ == role::SERVER) {
|
||||
esp_tls_server_session_delete(tls_);
|
||||
}
|
||||
tls_ = nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
esp_err_t send(api_id id, T *t)
|
||||
{
|
||||
@ -90,7 +115,7 @@ public:
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t send(api_id id) // specialization for (void)
|
||||
esp_err_t send(api_id id) // overload for (void)
|
||||
{
|
||||
RpcHeader head = {.id = id, .size = 0};
|
||||
int len = esp_tls_conn_write(tls_, &head, sizeof(head));
|
||||
|
@ -26,6 +26,7 @@ const char *TAG = "rpc_server";
|
||||
const unsigned char ca_crt[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_CA "\n-----END CERTIFICATE-----";
|
||||
const unsigned char crt[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_CRT "\n-----END CERTIFICATE-----";
|
||||
const unsigned char key[] = "-----BEGIN RSA PRIVATE KEY-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_KEY "\n-----END RSA PRIVATE KEY-----";
|
||||
// TODO: Add option to supply keys and certs via a global symbol (file)
|
||||
|
||||
}
|
||||
|
||||
@ -53,7 +54,7 @@ private:
|
||||
{
|
||||
auto instance = static_cast<RpcInstance *>(ctx);
|
||||
while (instance->perform() == ESP_OK) {}
|
||||
vTaskDelete(nullptr);
|
||||
esp_restart();
|
||||
}
|
||||
esp_err_t start_server()
|
||||
{
|
||||
@ -181,7 +182,7 @@ private:
|
||||
|
||||
|
||||
namespace server {
|
||||
RpcInstance instance;
|
||||
constinit RpcInstance instance;
|
||||
}
|
||||
|
||||
RpcInstance *RpcEngine::init_server()
|
||||
|
@ -1,23 +1,5 @@
|
||||
# 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)
|
||||
|
||||
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/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)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp_mqtt_demo)
|
||||
|
@ -4,40 +4,28 @@ This is a simple mqtt demo, that connects to WiFi AP first. This application has
|
||||
|
||||
## 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
|
||||
When running this example on a target that doesn't natively support WiFi, please make sure that the remote target (slave application) is connected to your chipset via the configured transport interface.
|
||||
|
||||
## How to use example
|
||||
Connection to the slave device also depends on RPC library used. It is recommended to use [`esp_hosted`](https://github.com/espressif/esp-hosted). Alternatively you can use [`eppp_link`](https://components.espressif.com/components/espressif/eppp_link).
|
||||
|
||||
### Hardware Required
|
||||
Please note, that `esp_hosted` as a component is currently WIP, so the `wifi_remote` defaults to `eppp`, for now.
|
||||
|
||||
To run this example, you need any ESP32 development board or just PC/virtual machine/container running linux operating system.
|
||||
## HW connection
|
||||
|
||||
### Host build modes
|
||||
We currently support only `UART` transport, so the connection is very simple. You only need to connect Rx, Tx and GND with the remote target.
|
||||
You need to configure these fields according to your connection:
|
||||
* CONFIG_ESP_WIFI_REMOTE_EPPP_UART_TX_PIN
|
||||
* CONFIG_ESP_WIFI_REMOTE_EPPP_UART_RX_PIN
|
||||
|
||||
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.
|
||||
## SW configuration
|
||||
|
||||
### Building on linux
|
||||
The RPC mechanism between the host and the slave micro uses TLS with mutual authentication, so you would have to configure certificates and keys for both parties. This application -- host target -- is considered RPC client, so it needs client's certificate and key, as well as the CA certificate to validate the server (slave application).
|
||||
If self-signed certificates are acceptable, you can use [generate_test_certs](../test_certs/generate_test_certs.sh) script to generate both the CA and the keys itself and convert them to the PEM format that's accepted by the EPPP RPC engine.
|
||||
You will have to configure these options:
|
||||
* CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_CA
|
||||
* CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_CRT
|
||||
* CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_KEY
|
||||
|
||||
1) Configure linux target
|
||||
```bash
|
||||
idf.py --preview set-target linux
|
||||
```
|
||||
## Setting up slave device
|
||||
|
||||
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)
|
||||
You need to set up the connection and configuration in a similar way on the slave part (connection pins + certificates and keys). Please refer to the [slave_application](../server/README.md) README for more information.
|
||||
|
@ -1,7 +1,21 @@
|
||||
# WiFi remote EPPP RPC server
|
||||
|
||||
# Wi-Fi station to PPPoS server
|
||||
This is a standalone application serving as the slave device for `esp_wifi_remote` users (with `eppp` RPC).
|
||||
|
||||
This example demonstrate using NAPT to bring connectivity from WiFi station to PPPoS server.
|
||||
## Overview
|
||||
|
||||
This example expect a PPPoS client to connect to the server and use the connectivity.
|
||||
The client could be a Linux computer with `pppd` service or another microcontroller with PPP client (or another ESP32 with not WiFi interface)
|
||||
You need to configure and connect a slave device to the `esp_wifi_remote` host and run this application. Please fallow carefully these guidelines on HW connection and configuration of the slave device, based on the host device.
|
||||
|
||||
## HW connection
|
||||
|
||||
We currently support only `UART` transport you just need to connect Rx, Tx and GND and configure these fields accordingly:
|
||||
* CONFIG_ESP_WIFI_REMOTE_EPPP_UART_TX_PIN
|
||||
* CONFIG_ESP_WIFI_REMOTE_EPPP_UART_RX_PIN
|
||||
|
||||
## SW configuration
|
||||
|
||||
You will have to install server side certificates and keys, as well as the CA which should verify the client side.
|
||||
Please configure these options:
|
||||
* CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_CA
|
||||
* CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_CRT
|
||||
* CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_KEY
|
||||
|
@ -5,6 +5,6 @@ dependencies:
|
||||
espressif/eppp_link:
|
||||
version: '0.0.1'
|
||||
idf:
|
||||
version: '5.3'
|
||||
version: '>=5.3'
|
||||
# espressif/esp_hosted:
|
||||
# version: '*'
|
||||
|
@ -2,7 +2,7 @@
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: "5.3"
|
||||
version: '>=5.3'
|
||||
espressif/esp_wifi_remote:
|
||||
version: "*"
|
||||
override_path: ../../..
|
||||
|
Reference in New Issue
Block a user