Skip to content

Commit 6c82ce2

Browse files
committed
fix(wifi_remote): Make services restartable, code cleanup
1 parent d2b7c55 commit 6c82ce2

File tree

11 files changed

+122
-111
lines changed

11 files changed

+122
-111
lines changed

.github/workflows/wifi_remote__build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
run: |
4949
${IDF_PATH}/install.sh --enable-pytest
5050
. ${IDF_PATH}/export.sh
51-
echo "python ./ci/build_apps.py ./components/esp_wifi_remote/${{matrix.test.path}} -vv --preserve-all"
51+
python ./ci/build_apps.py ./components/esp_wifi_remote/${{matrix.test.path}} -vv --preserve-all
5252
5353
build_wifi_remote_example:
5454
if: contains(github.event.pull_request.labels.*.name, 'wifi_remote') || github.event_name == 'push'

components/esp_wifi_remote/Kconfig.rpc.in

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,20 @@ choice ESP_WIFI_REMOTE_LIBRARY
1515
endchoice
1616

1717
if ESP_WIFI_REMOTE_LIBRARY_EPPP
18-
menu "WiFi remote by EPPP"
1918

20-
choice ESP_WIFI_REMOTE_EPPP_TRANSPORT
21-
prompt "Choose EPPP transport"
22-
default ESP_WIFI_REMOTE_EPPP_TRANSPORT_UART
19+
config ESP_WIFI_REMOTE_EPPP_UART_TX_PIN
20+
int "TXD Pin Number"
21+
default 10
22+
range 0 31
2323
help
24-
Select type of EPPP transport
24+
Pin number of UART TX.
2525

26-
config ESP_WIFI_REMOTE_EPPP_TRANSPORT_UART
27-
bool "UART"
28-
config ESP_WIFI_REMOTE_EPPP_TRANSPORT_SPI
29-
bool "SPI"
30-
endchoice
31-
32-
if ESP_WIFI_REMOTE_EPPP_TRANSPORT_UART
33-
config ESP_WIFI_REMOTE_EPPP_UART_TX_PIN
34-
int "TXD Pin Number"
35-
default 10
36-
range 0 31
37-
help
38-
Pin number of UART TX.
39-
40-
config ESP_WIFI_REMOTE_EPPP_UART_RX_PIN
41-
int "RXD Pin Number"
42-
default 11
43-
range 0 31
44-
help
45-
Pin number of UART RX.
46-
endif
26+
config ESP_WIFI_REMOTE_EPPP_UART_RX_PIN
27+
int "RXD Pin Number"
28+
default 11
29+
range 0 31
30+
help
31+
Pin number of UART RX.
4732

4833
config ESP_WIFI_REMOTE_EPPP_SERVER_CA
4934
string "Servers CA certificate"
@@ -68,6 +53,5 @@ endchoice
6853
config ESP_WIFI_REMOTE_EPPP_SERVER_KEY
6954
string "Server key"
7055
default "--- Please copy content of the Client key ---"
71-
endmenu
7256

7357
endif

components/esp_wifi_remote/eppp/eppp_init.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,14 @@
77
#include "esp_wifi.h"
88
#include "eppp_link.h"
99

10-
esp_netif_t *wifi_remote_eppp_init(eppp_type_t role)
10+
__attribute__((weak)) esp_netif_t *wifi_remote_eppp_init(eppp_type_t role)
1111
{
1212
uint32_t our_ip = role == EPPP_SERVER ? EPPP_DEFAULT_SERVER_IP() : EPPP_DEFAULT_CLIENT_IP();
1313
uint32_t their_ip = role == EPPP_SERVER ? EPPP_DEFAULT_CLIENT_IP() : EPPP_DEFAULT_SERVER_IP();
1414
eppp_config_t config = EPPP_DEFAULT_CONFIG(our_ip, their_ip);
15-
#if CONFIG_ESP_WIFI_REMOTE_EPPP_TRANSPORT_UART
15+
// We currently support only UART transport
1616
config.transport = EPPP_TRANSPORT_UART;
1717
config.uart.tx_io = CONFIG_ESP_WIFI_REMOTE_EPPP_UART_TX_PIN;
1818
config.uart.rx_io = CONFIG_ESP_WIFI_REMOTE_EPPP_UART_RX_PIN;
19-
#else
20-
#error ESP_WIFI_REMOTE supports only UART transport
21-
#endif
2219
return eppp_open(role, &config, portMAX_DELAY);
2320
}

components/esp_wifi_remote/eppp/wifi_remote_rpc_client.cpp

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@ const char *TAG = "rpc_client";
2626
const unsigned char ca_crt[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_CA "\n-----END CERTIFICATE-----";
2727
const unsigned char crt[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_CRT "\n-----END CERTIFICATE-----";
2828
const unsigned char key[] = "-----BEGIN RSA PRIVATE KEY-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_KEY "\n-----END RSA PRIVATE KEY-----";
29+
// TODO: Add option to supply keys and certs via a global symbol (file)
2930

3031
}
3132

3233
using namespace client;
3334

34-
struct Sync {
35+
class Sync {
36+
friend class RpcInstance;
37+
public:
3538
void lock()
3639
{
3740
xSemaphoreTake(mutex, portMAX_DELAY);
@@ -48,7 +51,7 @@ struct Sync {
4851
}
4952
esp_err_t wait_for(EventBits_t bits, uint32_t timeout = portMAX_DELAY)
5053
{
51-
return xEventGroupWaitBits(events, bits, pdTRUE, pdTRUE, timeout) == bits ? ESP_OK : ESP_FAIL;
54+
return (xEventGroupWaitBits(events, bits, pdTRUE, pdTRUE, timeout) & bits) == bits ? ESP_OK : ESP_FAIL;
5255
}
5356
esp_err_t notify(EventBits_t bits)
5457
{
@@ -64,30 +67,36 @@ struct Sync {
6467
vEventGroupDelete(events);
6568
}
6669
}
70+
71+
72+
private:
6773
SemaphoreHandle_t mutex{nullptr};
6874
EventGroupHandle_t events{nullptr};
75+
6976
const int request = 1;
7077
const int resp_header = 2;
7178
const int resp_payload = 4;
72-
79+
const int restart = 8;
7380
};
7481

7582
class RpcInstance {
83+
friend class Sync;
7684
public:
85+
7786
template<typename T>
7887
esp_err_t send(api_id id, T *t)
7988
{
80-
ESP_RETURN_ON_ERROR(sync.notify(sync.request), TAG, "failed to notify req");
8189
pending_resp = id;
90+
ESP_RETURN_ON_ERROR(sync.notify(sync.request), TAG, "failed to notify req");
8291
ESP_RETURN_ON_ERROR(rpc.send<T>(id, t), TAG, "Failed to send request");
8392
return ESP_OK;
8493
}
8594

86-
// specialization for (void)
95+
// overload of the templated method (used for functions with no arguments)
8796
esp_err_t send(api_id id)
8897
{
89-
ESP_RETURN_ON_ERROR(sync.notify(sync.request), TAG, "failed to notify req");
9098
pending_resp = id;
99+
ESP_RETURN_ON_ERROR(sync.notify(sync.request), TAG, "failed to notify req");
91100
ESP_RETURN_ON_ERROR(rpc.send(id), TAG, "Failed to send request");
92101
return ESP_OK;
93102
}
@@ -103,6 +112,7 @@ class RpcInstance {
103112
esp_err_t init()
104113
{
105114
ESP_RETURN_ON_FALSE(netif = wifi_remote_eppp_init(EPPP_CLIENT), ESP_FAIL, TAG, "Failed to connect to EPPP server");
115+
ESP_RETURN_ON_ERROR(esp_event_handler_register(IP_EVENT, IP_EVENT_PPP_GOT_IP, got_ip, this), TAG, "Failed to register event");
106116
ESP_RETURN_ON_ERROR(sync.init(), TAG, "Failed to init sync primitives");
107117
ESP_RETURN_ON_ERROR(rpc.init(), TAG, "Failed to init RPC engine");
108118
return xTaskCreate(task, "client", 8192, this, 5, nullptr) == pdTRUE ? ESP_OK : ESP_FAIL;
@@ -168,15 +178,28 @@ class RpcInstance {
168178
static void task(void *ctx)
169179
{
170180
auto instance = static_cast<RpcInstance *>(ctx);
171-
while (instance->perform() == ESP_OK) {}
181+
do {
182+
while (instance->perform() == ESP_OK) {}
183+
} while (instance->restart() == ESP_OK);
172184
vTaskDelete(nullptr);
173185
}
186+
esp_err_t restart()
187+
{
188+
rpc.deinit();
189+
ESP_RETURN_ON_ERROR(sync.wait_for(sync.restart, pdMS_TO_TICKS(10000)), TAG, "Didn't receive EPPP address in time");
190+
return rpc.init();
191+
}
192+
static void got_ip(void *ctx, esp_event_base_t base, int32_t id, void *data)
193+
{
194+
auto instance = static_cast<RpcInstance *>(ctx);
195+
instance->sync.notify(instance->sync.restart);
196+
}
174197
esp_netif_t *netif{nullptr};
175198
};
176199

177200

178201
namespace client {
179-
RpcInstance instance;
202+
constinit RpcInstance instance;
180203
} // namespace client
181204

182205
RpcInstance *RpcEngine::init_client()
@@ -196,20 +219,17 @@ RpcInstance *RpcEngine::init_client()
196219
cfg.clientkey_bytes = sizeof(client::key);
197220
cfg.common_name = "espressif.local";
198221

199-
tls_ = esp_tls_init();
200-
if (!tls_) {
201-
ESP_LOGE(TAG, "Failed to allocate esp_tls handle!");
202-
goto exit;
203-
}
204-
if (esp_tls_conn_new_sync(host, strlen(host), rpc_port, &cfg, tls_) <= 0) {
205-
ESP_LOGE(TAG, "Failed to open a new connection %s", host);
206-
goto exit;
222+
ESP_RETURN_ON_FALSE(tls_ = esp_tls_init(), nullptr, TAG, "Failed to create ESP-TLS instance");
223+
int retries = 0;
224+
while (esp_tls_conn_new_sync(host, strlen(host), rpc_port, &cfg, tls_) <= 0) {
225+
esp_tls_conn_destroy(tls_);
226+
tls_ = nullptr;
227+
ESP_RETURN_ON_FALSE(retries++ < 3, nullptr, TAG, "Failed to open connection to %s", host);
228+
ESP_LOGW(TAG, "Connection to RPC server failed! Will retry in %d second(s)", retries);
229+
vTaskDelay(pdMS_TO_TICKS(1000 * retries));
230+
ESP_RETURN_ON_FALSE(tls_ = esp_tls_init(), nullptr, TAG, "Failed to create ESP-TLS instance");
207231
}
208232
return &client::instance;
209-
exit:
210-
esp_tls_conn_destroy(tls_);
211-
tls_ = nullptr;
212-
return nullptr;
213233
}
214234
} // namespace eppp_rpc
215235

components/esp_wifi_remote/eppp/wifi_remote_rpc_impl.hpp

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99

1010
namespace eppp_rpc {
1111

12-
const int rpc_port = 3333;
12+
static constexpr int rpc_port = 3333;
1313

14+
/**
15+
* @brief Currently supported RPC commands/events
16+
*/
1417
enum class api_id : uint32_t {
1518
ERROR,
1619
UNDEF,
@@ -35,6 +38,9 @@ struct RpcHeader {
3538
uint32_t size;
3639
} __attribute((__packed__));
3740

41+
/**
42+
* @brief Structure holding the outgoing or incoming parameter
43+
*/
3844
template<typename T>
3945
struct RpcData {
4046
RpcHeader head;
@@ -54,11 +60,17 @@ struct RpcData {
5460
}
5561
} __attribute((__packed__));
5662

63+
/**
64+
* @brief Singleton holding the static data for either the client or server side
65+
*/
5766
class RpcInstance;
5867

68+
/**
69+
* @brief Engine that implements a simple RPC mechanism
70+
*/
5971
class RpcEngine {
6072
public:
61-
explicit RpcEngine(role r) : tls_(nullptr), role_(r) {}
73+
constexpr explicit RpcEngine(role r) : tls_(nullptr), role_(r) {}
6274

6375
esp_err_t init()
6476
{
@@ -74,6 +86,19 @@ class RpcEngine {
7486
return instance == nullptr ? ESP_FAIL : ESP_OK;
7587
}
7688

89+
void deinit()
90+
{
91+
if (tls_ == nullptr) {
92+
return;
93+
}
94+
if (role_ == role::CLIENT) {
95+
esp_tls_conn_destroy(tls_);
96+
} else if (role_ == role::SERVER) {
97+
esp_tls_server_session_delete(tls_);
98+
}
99+
tls_ = nullptr;
100+
}
101+
77102
template<typename T>
78103
esp_err_t send(api_id id, T *t)
79104
{
@@ -90,7 +115,7 @@ class RpcEngine {
90115
return ESP_OK;
91116
}
92117

93-
esp_err_t send(api_id id) // specialization for (void)
118+
esp_err_t send(api_id id) // overload for (void)
94119
{
95120
RpcHeader head = {.id = id, .size = 0};
96121
int len = esp_tls_conn_write(tls_, &head, sizeof(head));

components/esp_wifi_remote/eppp/wifi_remote_rpc_server.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const char *TAG = "rpc_server";
2626
const unsigned char ca_crt[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_CA "\n-----END CERTIFICATE-----";
2727
const unsigned char crt[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_CRT "\n-----END CERTIFICATE-----";
2828
const unsigned char key[] = "-----BEGIN RSA PRIVATE KEY-----\n" CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_KEY "\n-----END RSA PRIVATE KEY-----";
29+
// TODO: Add option to supply keys and certs via a global symbol (file)
2930

3031
}
3132

@@ -53,7 +54,7 @@ class RpcInstance {
5354
{
5455
auto instance = static_cast<RpcInstance *>(ctx);
5556
while (instance->perform() == ESP_OK) {}
56-
vTaskDelete(nullptr);
57+
esp_restart();
5758
}
5859
esp_err_t start_server()
5960
{
@@ -181,7 +182,7 @@ class RpcInstance {
181182

182183

183184
namespace server {
184-
RpcInstance instance;
185+
constinit RpcInstance instance;
185186
}
186187

187188
RpcInstance *RpcEngine::init_server()
Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,5 @@
11
# This project serves as a demo to enable using esp-mqtt on ESP platform targets as well as on linux
22
cmake_minimum_required(VERSION 3.16)
33

4-
if("${IDF_TARGET}" STREQUAL "linux")
5-
# For linux-target we have two options:
6-
# - With lwIP (must be defined on command line, e.g. idf.py -DWITH_LWIP=1)
7-
# access networking from linux `tap` interface (TAP networking mode)
8-
# - Without lwIP (must be defined on command line, e.g. idf.py -DWITH_LWIP=0)
9-
# no designated interface, accesses user network via linux/socket sys calls
10-
if(WITH_LWIP STREQUAL 1)
11-
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_tapif_io
12-
"../../common_components/linux_compat/esp_timer")
13-
set(COMPONENTS main esp_netif lwip protocol_examples_tapif_io startup esp_hw_support esp_system nvs_flash mqtt esp_timer)
14-
else()
15-
list(APPEND EXTRA_COMPONENT_DIRS
16-
"../../common_components/linux_compat/esp_timer"
17-
"$ENV{IDF_PATH}/examples/protocols/linux_stubs/esp_stubs")
18-
set(COMPONENTS main nvs_flash esp-tls esp_stubs mqtt protocol_examples_common esp_timer)
19-
endif()
20-
endif()
21-
224
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
235
project(esp_mqtt_demo)

components/esp_wifi_remote/examples/mqtt/README.md

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,28 @@ This is a simple mqtt demo, that connects to WiFi AP first. This application has
44

55
## Overview
66

7-
This is a simple example demonstrating connecting to an MQTT broker, subscribing and publishing some data.
8-
This example uses IDF build system and could be configured to be build and executed:
9-
* for any ESP32 family chip
10-
* for linux target
7+
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.
118

12-
## How to use example
9+
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).
1310

14-
### Hardware Required
11+
Please note, that `esp_hosted` as a component is currently WIP, so the `wifi_remote` defaults to `eppp`, for now.
1512

16-
To run this example, you need any ESP32 development board or just PC/virtual machine/container running linux operating system.
13+
## HW connection
1714

18-
### Host build modes
15+
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.
16+
You need to configure these fields according to your connection:
17+
* CONFIG_ESP_WIFI_REMOTE_EPPP_UART_TX_PIN
18+
* CONFIG_ESP_WIFI_REMOTE_EPPP_UART_RX_PIN
1919

20-
Linux build is supported in these two modes:
21-
* `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.
22-
* `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.
20+
## SW configuration
2321

24-
### Building on linux
22+
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).
23+
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.
24+
You will have to configure these options:
25+
* CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_CA
26+
* CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_CRT
27+
* CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_KEY
2528

26-
1) Configure linux target
27-
```bash
28-
idf.py --preview set-target linux
29-
```
29+
## Setting up slave device
3030

31-
2) Build the project with preferred components (with or without lwip)
32-
```bash
33-
idf.py -DWITH_LWIP=0 build # Building without lwip (user networking)
34-
idf.py -DWITH_LWIP=1 build # Building with lwip (TAP networking)
35-
```
36-
37-
3) Run the project
38-
39-
It is possible to run the project elf file directly, or using `idf.py` monitor target (no need to flash):
40-
```bash
41-
idf.py monitor
42-
```
43-
idf.py -DWITH_LWIP=0 build # Building without lwip (user networking)
31+
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.

0 commit comments

Comments
 (0)