feat(wifi_remote): Add support for simple eppp based RPC

This commit is contained in:
David Cermak
2024-04-10 18:51:26 +02:00
parent fbdb2483f5
commit fd168d86fc
36 changed files with 1190 additions and 124 deletions

View File

@ -1,15 +1,20 @@
if(NOT CONFIG_ESP_WIFI_ENABLED) if(NOT CONFIG_ESP_WIFI_ENABLED)
set(src_wifi_is_remote esp_wifi_remote.c esp_wifi_with_remote.c) set(src_wifi_is_remote esp_wifi_remote.c esp_wifi_with_remote.c esp_wifi_remote_net.c)
endif()
if(CONFIG_ESP_WIFI_REMOTE_LIBRARY_EPPP)
set(src_wifi_remote_eppp eppp/wifi_remote_rpc_client.cpp eppp/wifi_remote_rpc_server.cpp eppp/eppp_init.c)
else()
set(src_wifi_remote_weak esp_wifi_remote_weak.c)
endif() endif()
idf_component_register(INCLUDE_DIRS include idf_component_register(INCLUDE_DIRS include
SRCS ${src_wifi_is_remote} SRCS ${src_wifi_remote_weak}
esp_wifi_remote_net.c ${src_wifi_remote_eppp}
esp_wifi_remote_weak.c ${src_wifi_is_remote}
PRIV_INCLUDE_DIRS eppp
REQUIRES esp_event esp_netif REQUIRES esp_event esp_netif
PRIV_REQUIRES esp_wifi) PRIV_REQUIRES esp_wifi esp-tls)
idf_component_optional_requires(PRIVATE esp_hosted)
idf_component_get_property(wifi esp_wifi COMPONENT_LIB) idf_component_get_property(wifi esp_wifi COMPONENT_LIB)
target_link_libraries(${wifi} PUBLIC ${COMPONENT_LIB}) target_link_libraries(${wifi} PUBLIC ${COMPONENT_LIB})

View File

@ -5,6 +5,7 @@ menu "Wi-Fi Remote"
default y default y
orsource "./Kconfig.soc_wifi_caps.in" orsource "./Kconfig.soc_wifi_caps.in"
orsource "./Kconfig.rpc.in"
config ESP_WIFI_STATIC_RX_BUFFER_NUM config ESP_WIFI_STATIC_RX_BUFFER_NUM
int "Max number of WiFi static RX buffers" int "Max number of WiFi static RX buffers"
@ -191,7 +192,7 @@ menu "Wi-Fi Remote"
Set the size of WiFi Block Ack RX window. Generally a bigger value means higher throughput and better Set the size of WiFi Block Ack RX window. Generally a bigger value means higher throughput and better
compatibility but more memory. Most of time we should NOT change the default value unless special compatibility but more memory. Most of time we should NOT change the default value unless special
reason, e.g. test the maximum UDP RX throughput with iperf etc. For iperf test in shieldbox, the reason, e.g. test the maximum UDP RX throughput with iperf etc. For iperf test in shieldbox, the
recommended value is 9~12. If PSRAM is used and WiFi memory is prefered to allocat in PSRAM first, recommended value is 9~12. If PSRAM is used and WiFi memory is preferred to allocate in PSRAM first,
the default and minimum value should be 16 to achieve better throughput and compatibility with both the default and minimum value should be 16 to achieve better throughput and compatibility with both
stations and APs. stations and APs.
@ -228,14 +229,14 @@ menu "Wi-Fi Remote"
help help
ESP-MESH utilizes beacon frames to detect and resolve root node conflicts (see documentation). However ESP-MESH utilizes beacon frames to detect and resolve root node conflicts (see documentation). However
the default length of a beacon frame can simultaneously hold only five root node identifier structures, the default length of a beacon frame can simultaneously hold only five root node identifier structures,
meaning that a root node conflict of up to five nodes can be detected at one time. In the occurence of meaning that a root node conflict of up to five nodes can be detected at one time. In the occurrence of
more root nodes conflict involving more than five root nodes, the conflict resolution process will more root nodes conflict involving more than five root nodes, the conflict resolution process will
detect five of the root nodes, resolve the conflict, and re-detect more root nodes. This process will detect five of the root nodes, resolve the conflict, and re-detect more root nodes. This process will
repeat until all root node conflicts are resolved. However this process can generally take a very long repeat until all root node conflicts are resolved. However this process can generally take a very long
time. time.
To counter this situation, the beacon frame length can be increased such that more root nodes can be To counter this situation, the beacon frame length can be increased such that more root nodes can be
detected simultaneously. Each additional root node will require 36 bytes and should be added ontop of detected simultaneously. Each additional root node will require 36 bytes and should be added on top of
the default beacon frame length of the default beacon frame length of
752 bytes. For example, if you want to detect 10 root nodes simultaneously, you need to set the beacon 752 bytes. For example, if you want to detect 10 root nodes simultaneously, you need to set the beacon
frame length as frame length as
@ -487,9 +488,9 @@ menu "Wi-Fi Remote"
help help
Select this option to use MbedTLS TLS client for WPA2 enterprise connection. Select this option to use MbedTLS TLS client for WPA2 enterprise connection.
Please note that from MbedTLS-3.0 onwards, MbedTLS does not support SSL-3.0 Please note that from MbedTLS-3.0 onwards, MbedTLS does not support SSL-3.0
TLS-v1.0, TLS-v1.1 versions. Incase your server is using one of these version, TLS-v1.0, TLS-v1.1 versions. In case your server is using one of these version,
it is advisable to update your server. it is advisable to update your server.
Please disable this option for compatibilty with older TLS versions. Please disable this option for compatibility with older TLS versions.
config ESP_WIFI_EAP_TLS1_3 config ESP_WIFI_EAP_TLS1_3
bool "Enable EAP-TLS v1.3 Support for WiFi Enterprise connection" bool "Enable EAP-TLS v1.3 Support for WiFi Enterprise connection"
@ -556,6 +557,26 @@ menu "Wi-Fi Remote"
help help
Select this option to enable WiFi Multiband operation certification support. Select this option to enable WiFi Multiband operation certification support.
config ESP_WIFI_ENABLE_ROAMING_APP
bool "Advanced support for Wi-Fi Roaming (Experimental)"
depends on IDF_EXPERIMENTAL_FEATURES
default n
select ESP_WIFI_SCAN_CACHE
help
Enable Espressif's roaming app to allow for efficient Wi-Fi roaming.
This includes configurable periodic environment scans, maintaining a cache of the
best APs, handling low rssi events etc.
Risk Warning
Please note that this feature is still experimental and enabling this potentially can
lead to unpredictable scanning, connection and roaming attempts.
We are still working on tuning and optimising this feature to ensure reliable and stable use.
menu "Configure roaming App"
depends on ESP_WIFI_ENABLE_ROAMING_APP
rsource "wifi_apps/roaming_app/src/Kconfig.roaming"
endmenu
config ESP_WIFI_DPP_SUPPORT config ESP_WIFI_DPP_SUPPORT
bool "Enable DPP support" bool "Enable DPP support"
default n default n
@ -597,7 +618,7 @@ menu "Wi-Fi Remote"
default n default n
help help
Select this option to enable validate each WPS attribute Select this option to enable validate each WPS attribute
rigorously. Disabling this add the workaorunds with various APs. rigorously. Disabling this add the workarounds with various APs.
Enabling this may cause inter operability issues with some APs. Enabling this may cause inter operability issues with some APs.
config ESP_WIFI_WPS_PASSPHRASE config ESP_WIFI_WPS_PASSPHRASE
@ -605,9 +626,9 @@ menu "Wi-Fi Remote"
default n default n
help help
Select this option to get passphrase during WPS configuration. Select this option to get passphrase during WPS configuration.
This option fakes the virtual display capabilites to get the This option fakes the virtual display capabilities to get the
configuration in passphrase mode. configuration in passphrase mode.
Not recommanded to be used since WPS credentials should not Not recommended to be used since WPS credentials should not
be shared to other devices, making it in readable format increases be shared to other devices, making it in readable format increases
that risk, also passphrase requires pbkdf2 to convert in psk. that risk, also passphrase requires pbkdf2 to convert in psk.

View File

@ -0,0 +1,73 @@
choice ESP_WIFI_REMOTE_LIBRARY
prompt "Choose WiFi-remote implementation"
default ESP_WIFI_REMOTE_LIBRARY_EPPP
help
Select type of WiFi Remote implementation
ESP-HOSTED is the default and most versatile option.
It's also possible to use EPPP, which uses PPPoS link between micros and NAPT, so it's slower
and less universal.
config ESP_WIFI_REMOTE_LIBRARY_HOSTED
bool "ESP-HOSTED"
config ESP_WIFI_REMOTE_LIBRARY_EPPP
bool "EPPP"
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
help
Select type of EPPP transport
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_SERVER_CA
string "Servers CA certificate"
default "--- Please copy content of the CA certificate ---"
config ESP_WIFI_REMOTE_EPPP_CLIENT_CRT
string "Client certificate"
default "--- Please copy content of the Client certificate ---"
config ESP_WIFI_REMOTE_EPPP_CLIENT_KEY
string "Client key"
default "--- Please copy content of the Client key ---"
config ESP_WIFI_REMOTE_EPPP_CLIENT_CA
string "Clients CA certificate"
default "--- Please copy content of the CA certificate ---"
config ESP_WIFI_REMOTE_EPPP_SERVER_CRT
string "Server certificate"
default "--- Please copy content of the Client certificate ---"
config ESP_WIFI_REMOTE_EPPP_SERVER_KEY
string "Server key"
default "--- Please copy content of the Client key ---"
endmenu
endif

View File

@ -227,3 +227,11 @@ endif # ESP32C6
if SLAVE_IDF_TARGET_ESP32H2 if SLAVE_IDF_TARGET_ESP32H2
endif # ESP32H2 endif # ESP32H2
if SLAVE_IDF_TARGET_ESP32P4
config SLAVE_SOC_WIFI_LIGHT_SLEEP_CLK_WIDTH
int
default 12
endif # ESP32P4

View File

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_log.h"
#include "esp_wifi.h"
#include "eppp_link.h"
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
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);
}

View File

@ -0,0 +1,277 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <netdb.h>
#include <memory>
#include <cinttypes>
#include "esp_log.h"
#include "esp_tls.h"
#include "esp_wifi.h"
#include "esp_check.h"
#include "wifi_remote_rpc_impl.hpp"
#include "eppp_link.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "wifi_remote_rpc_params.h"
extern "C" esp_netif_t *wifi_remote_eppp_init(eppp_type_t role);
namespace eppp_rpc {
namespace client {
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-----";
}
using namespace client;
struct Sync {
void lock()
{
xSemaphoreTake(mutex, portMAX_DELAY);
}
void unlock()
{
xSemaphoreGive(mutex);
}
esp_err_t init()
{
mutex = xSemaphoreCreateMutex();
events = xEventGroupCreate();
return mutex == nullptr || events == nullptr ? ESP_ERR_NO_MEM : ESP_OK;
}
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;
}
esp_err_t notify(EventBits_t bits)
{
xEventGroupSetBits(events, bits);
return ESP_OK;
}
~Sync()
{
if (mutex) {
vSemaphoreDelete(mutex);
}
if (events) {
vEventGroupDelete(events);
}
}
SemaphoreHandle_t mutex{nullptr};
EventGroupHandle_t events{nullptr};
const int request = 1;
const int resp_header = 2;
const int resp_payload = 4;
};
class RpcInstance {
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(rpc.send<T>(id, t), TAG, "Failed to send request");
return ESP_OK;
}
// specialization for (void)
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(rpc.send(id), TAG, "Failed to send request");
return ESP_OK;
}
template<typename T>
T get_resp(api_id id)
{
sync.wait_for(sync.resp_header);
auto ret = rpc.template get_payload<T>(id, pending_header);
sync.notify(sync.resp_payload);
return ret;
}
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(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;
}
RpcEngine rpc{eppp_rpc::role::CLIENT};
Sync sync;
private:
api_id pending_resp{api_id::UNDEF};
RpcHeader pending_header{};
esp_err_t process_ip_event(RpcHeader &header)
{
auto event = rpc.get_payload<esp_wifi_remote_eppp_ip_event>(api_id::IP_EVENT, header);
// Now bypass network layers with EPPP interface
ESP_RETURN_ON_ERROR(esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &event.dns), TAG, "Failed to set DNS info");
ESP_RETURN_ON_ERROR(esp_netif_set_default_netif(netif), TAG, "Failed to set default netif to EPPP");
ip_event_got_ip_t evt = {
.esp_netif = netif,
.ip_info = {},
.ip_changed = true,
};
esp_netif_get_ip_info(netif, &evt.ip_info);
ESP_RETURN_ON_ERROR(esp_event_post(IP_EVENT, IP_EVENT_STA_GOT_IP, &evt, sizeof(evt), 0), TAG, "Failed to post IP event");
ESP_LOGI(TAG, "Main DNS:" IPSTR, IP2STR(&event.dns.ip.u_addr.ip4));
ESP_LOGI(TAG, "EPPP IP:" IPSTR, IP2STR(&event.ppp_ip.ip));
ESP_LOGI(TAG, "WIFI IP:" IPSTR, IP2STR(&event.wifi_ip.ip));
ESP_LOGI(TAG, "WIFI GW:" IPSTR, IP2STR(&event.wifi_ip.gw));
ESP_LOGI(TAG, "WIFI mask:" IPSTR, IP2STR(&event.wifi_ip.netmask));
return ESP_OK;
}
esp_err_t process_wifi_event(RpcHeader &header)
{
auto event_id = rpc.get_payload<int32_t>(api_id::WIFI_EVENT, header);
ESP_RETURN_ON_ERROR(esp_event_post(WIFI_EVENT, event_id, nullptr, 0, 0), TAG, "Failed to post WiFi event");
return ESP_OK;
}
esp_err_t perform()
{
auto header = rpc.get_header();
if (api_id(header.id) == api_id::ERROR) { // network error
return ESP_FAIL;
}
if (api_id(header.id) == api_id::UNDEF) { // network timeout
return ESP_OK;
}
if (api_id(header.id) == api_id::IP_EVENT) {
return process_ip_event(header);
}
if (api_id(header.id) == api_id::WIFI_EVENT) {
return process_wifi_event(header);
}
if (sync.wait_for(sync.request, 0) == ESP_OK && api_id(header.id) == pending_resp) {
pending_header = header;
pending_resp = api_id::UNDEF;
sync.notify(sync.resp_header);
sync.wait_for(sync.resp_payload);
return ESP_OK;
}
ESP_LOGE(TAG, "Unexpected header %" PRIi32, static_cast<uint32_t>(header.id));
return ESP_FAIL;
}
static void task(void *ctx)
{
auto instance = static_cast<RpcInstance *>(ctx);
while (instance->perform() == ESP_OK) {}
vTaskDelete(nullptr);
}
esp_netif_t *netif{nullptr};
};
namespace client {
RpcInstance instance;
} // namespace client
RpcInstance *RpcEngine::init_client()
{
char host[4 * 4 + 1] = {}; // IPv4: 4 x (3 numbers + '.') + \0
esp_ip4_addr_t ip = { .addr = EPPP_DEFAULT_SERVER_IP() };
if (esp_ip4addr_ntoa(&ip, host, sizeof(host)) == nullptr) {
return nullptr;
}
esp_tls_cfg_t cfg = {};
cfg.cacert_buf = client::ca_crt;
cfg.cacert_bytes = sizeof(client::ca_crt);
cfg.clientcert_buf = client::crt;
cfg.clientcert_bytes = sizeof(client::crt);
cfg.clientkey_buf = client::key;
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;
}
return &client::instance;
exit:
esp_tls_conn_destroy(tls_);
tls_ = nullptr;
return nullptr;
}
} // namespace eppp_rpc
//
// esp_wifi_remote API implementation
//
using namespace eppp_rpc;
using namespace client;
extern "C" esp_err_t esp_wifi_remote_init(const wifi_init_config_t *config)
{
// Here we initialize this client's RPC
ESP_RETURN_ON_ERROR(instance.init(), TAG, "Failed to initialize eppp-rpc");
std::lock_guard<Sync> lock(instance.sync);
ESP_RETURN_ON_ERROR(instance.send(api_id::INIT, config), TAG, "Failed to send request");
return instance.get_resp<esp_err_t>(api_id::INIT);
}
extern "C" esp_err_t esp_wifi_remote_set_config(wifi_interface_t interface, wifi_config_t *conf)
{
esp_wifi_remote_config params = { .interface = interface, .conf = {} };
memcpy(&params.conf, conf, sizeof(wifi_config_t));
std::lock_guard<Sync> lock(instance.sync);
ESP_RETURN_ON_ERROR(instance.send(api_id::SET_CONFIG, &params), TAG, "Failed to send request");
return instance.get_resp<esp_err_t>(api_id::SET_CONFIG);
}
extern "C" esp_err_t esp_wifi_remote_start(void)
{
std::lock_guard<Sync> lock(instance.sync);
ESP_RETURN_ON_ERROR(instance.send(api_id::START), TAG, "Failed to send request");
return instance.get_resp<esp_err_t>(api_id::START);
}
extern "C" esp_err_t esp_wifi_remote_stop(void)
{
std::lock_guard<Sync> lock(instance.sync);
ESP_RETURN_ON_ERROR(instance.send(api_id::STOP), TAG, "Failed to send request");
return instance.get_resp<esp_err_t>(api_id::STOP);
}
extern "C" esp_err_t esp_wifi_remote_connect(void)
{
std::lock_guard<Sync> lock(instance.sync);
ESP_RETURN_ON_ERROR(instance.send(api_id::CONNECT), TAG, "Failed to send request");
return instance.get_resp<esp_err_t>(api_id::CONNECT);
}
extern "C" esp_err_t esp_wifi_remote_get_mac(wifi_interface_t ifx, uint8_t mac[6])
{
std::lock_guard<Sync> lock(instance.sync);
ESP_RETURN_ON_ERROR(instance.send(api_id::GET_MAC, &ifx), TAG, "Failed to send request");
auto ret = instance.get_resp<esp_wifi_remote_mac_t>(api_id::GET_MAC);
ESP_LOG_BUFFER_HEXDUMP("MAC", ret.mac, 6, ESP_LOG_DEBUG);
memcpy(mac, ret.mac, 6);
return ret.err;
}
extern "C" esp_err_t esp_wifi_remote_set_mode(wifi_mode_t mode)
{
std::lock_guard<Sync> lock(instance.sync);
ESP_RETURN_ON_ERROR(instance.send(api_id::SET_MODE, &mode), TAG, "Failed to send request");
return instance.get_resp<esp_err_t>(api_id::SET_MODE);
}

View File

@ -0,0 +1,142 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <cstring>
#include <cerrno>
namespace eppp_rpc {
const int rpc_port = 3333;
enum class api_id : uint32_t {
ERROR,
UNDEF,
INIT,
SET_MODE,
SET_CONFIG,
START,
STOP,
CONNECT,
GET_MAC,
WIFI_EVENT,
IP_EVENT,
};
enum class role {
SERVER,
CLIENT,
};
struct RpcHeader {
api_id id;
uint32_t size;
} __attribute((__packed__));
template<typename T>
struct RpcData {
RpcHeader head;
T value_{};
explicit RpcData(api_id id) : head{id, sizeof(T)} {}
uint8_t *value()
{
return (uint8_t *) &value_;
}
uint8_t *marshall(T *t, size_t &size)
{
size = head.size + sizeof(RpcHeader);
memcpy(value(), t, sizeof(T));
return (uint8_t *) this;
}
} __attribute((__packed__));
class RpcInstance;
class RpcEngine {
public:
explicit RpcEngine(role r) : tls_(nullptr), role_(r) {}
esp_err_t init()
{
if (tls_ != nullptr) {
return ESP_OK;
}
if (role_ == role::CLIENT) {
instance = init_client();
}
if (role_ == role::SERVER) {
instance = init_server();
}
return instance == nullptr ? ESP_FAIL : ESP_OK;
}
template<typename T>
esp_err_t send(api_id id, T *t)
{
RpcData<T> req(id);
size_t size;
auto buf = req.marshall(t, size);
ESP_LOGD("rpc", "Sending API id:%d", (int) id);
ESP_LOG_BUFFER_HEXDUMP("rpc", buf, size, ESP_LOG_VERBOSE);
int len = esp_tls_conn_write(tls_, buf, size);
if (len <= 0) {
ESP_LOGE("rpc", "Failed to write data to the connection");
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t send(api_id id) // specialization for (void)
{
RpcHeader head = {.id = id, .size = 0};
int len = esp_tls_conn_write(tls_, &head, sizeof(head));
if (len <= 0) {
ESP_LOGE("rpc", "Failed to write data to the connection");
return ESP_FAIL;
}
return ESP_OK;
}
RpcHeader get_header()
{
RpcHeader header{};
int len = esp_tls_conn_read(tls_, (char *) &header, sizeof(header));
if (len <= 0) {
if (len < 0 && errno != EAGAIN) {
ESP_LOGE("rpc", "Failed to read header data from the connection %d %s", errno, strerror(errno));
return {.id = api_id::ERROR, .size = 0};
}
return {.id = api_id::UNDEF, .size = 0};
}
return header;
}
template<typename T>
T get_payload(api_id id, RpcHeader &head)
{
RpcData<T> resp(id);
if (head.id != id || head.size != resp.head.size) {
ESP_LOGE("rpc", "unexpected header %d %d or sizes %" PRIu32 " %" PRIu32, (int)head.id, (int)id, head.size, resp.head.size);
return {};
}
int len = esp_tls_conn_read(tls_, (char *) resp.value(), resp.head.size);
if (len <= 0) {
ESP_LOGE("rpc", "Failed to read data from the connection");
return {};
}
return resp.value_;
}
private:
RpcInstance *init_server();
RpcInstance *init_client();
esp_tls_t *tls_;
role role_;
RpcInstance *instance{nullptr};
};
};

View File

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
struct esp_wifi_remote_config {
wifi_interface_t interface;
wifi_config_t conf;
};
struct esp_wifi_remote_mac_t {
esp_err_t err;
uint8_t mac[6];
};
struct esp_wifi_remote_eppp_ip_event {
uint32_t id;
esp_netif_ip_info_t wifi_ip;
esp_netif_ip_info_t ppp_ip;
esp_netif_dns_info_t dns;
};

View File

@ -0,0 +1,209 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <netdb.h>
#include <memory>
#include <cerrno>
#include <sys/socket.h>
#include "esp_log.h"
#include "esp_check.h"
#include "esp_tls.h"
#include "esp_wifi.h"
#include "wifi_remote_rpc_impl.hpp"
#include "eppp_link.h"
#include "wifi_remote_rpc_params.h"
#include "lwip/apps/snmp.h"
extern "C" esp_netif_t *wifi_remote_eppp_init(eppp_type_t role);
namespace eppp_rpc {
namespace server {
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-----";
}
using namespace server;
class RpcInstance {
public:
RpcEngine rpc{role::SERVER};
int sock{-1};
esp_err_t init()
{
ESP_RETURN_ON_FALSE(netif = wifi_remote_eppp_init(EPPP_SERVER), ESP_FAIL, TAG, "Failed to init EPPP connection");
ESP_RETURN_ON_ERROR(start_server(), TAG, "Failed to start RPC server");
ESP_RETURN_ON_ERROR(rpc.init(), TAG, "Failed to init RPC engine");
ESP_RETURN_ON_ERROR(esp_netif_napt_enable(netif), TAG, "Failed to enable NAPT");
ESP_RETURN_ON_ERROR(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, handler, this), TAG, "Failed to register event");
ESP_RETURN_ON_ERROR(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, handler, this), TAG, "Failed to register event");
return xTaskCreate(task, "server", 8192, this, 5, nullptr) == pdTRUE ? ESP_OK : ESP_FAIL;
}
private:
esp_netif_t *netif{nullptr};
static void task(void *ctx)
{
auto instance = static_cast<RpcInstance *>(ctx);
while (instance->perform() == ESP_OK) {}
vTaskDelete(nullptr);
}
esp_err_t start_server()
{
struct sockaddr_in dest_addr = {};
int ret;
int opt = 1;
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(rpc_port);
int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
ESP_RETURN_ON_FALSE(listen_sock >= 0, ESP_FAIL, TAG, "Failed to create listening socket");
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
ret = bind(listen_sock, (struct sockaddr *) &dest_addr, sizeof(dest_addr));
ESP_RETURN_ON_FALSE(ret == 0, ESP_FAIL, TAG, "Failed to bind the listening socket");
ret = listen(listen_sock, 1);
ESP_RETURN_ON_FALSE(ret == 0, ESP_FAIL, TAG, "Failed to start listening");
struct sockaddr_storage source_addr {};
socklen_t addr_len = sizeof(source_addr);
sock = accept(listen_sock, (struct sockaddr *) &source_addr, &addr_len);
ESP_RETURN_ON_FALSE(sock >= 0, ESP_FAIL, TAG, "Failed to accept connections: errno %d", errno);
ESP_LOGI(TAG, "Socket accepted on: %s", inet_ntoa(((struct sockaddr_in *) &source_addr)->sin_addr));
return ESP_OK;
}
esp_err_t wifi_event(int32_t id)
{
ESP_LOGI(TAG, "Received WIFI event %" PRIi32, id);
ESP_RETURN_ON_ERROR(rpc.send(api_id::WIFI_EVENT, &id), TAG, "Failed to marshall WiFi event");
return ESP_OK;
}
esp_err_t ip_event(int32_t id, ip_event_got_ip_t *ip_data)
{
ESP_LOGI(TAG, "Received IP event %" PRIi32, id);
esp_wifi_remote_eppp_ip_event ip_event{};
ip_event.id = id;
if (ip_data->esp_netif) {
// marshall additional data, only if netif available
ESP_RETURN_ON_ERROR(esp_netif_get_dns_info(ip_data->esp_netif, ESP_NETIF_DNS_MAIN, &ip_event.dns), TAG, "Failed to get DNS info");
ESP_LOGI(TAG, "Main DNS:" IPSTR, IP2STR(&ip_event.dns.ip.u_addr.ip4));
memcpy(&ip_event.wifi_ip, &ip_data->ip_info, sizeof(ip_event.wifi_ip));
ESP_RETURN_ON_ERROR(esp_netif_get_ip_info(netif, &ip_event.ppp_ip), TAG, "Failed to get IP info");
ESP_LOGI(TAG, "IP address:" IPSTR, IP2STR(&ip_data->ip_info.ip));
}
ESP_RETURN_ON_ERROR(rpc.send(api_id::IP_EVENT, &ip_event), TAG, "Failed to marshal IP event");
return ESP_OK;
}
static void handler(void *ctx, esp_event_base_t base, int32_t id, void *data)
{
auto instance = static_cast<RpcInstance *>(ctx);
if (base == WIFI_EVENT) {
instance->wifi_event(id);
} else if (base == IP_EVENT) {
auto *ip_data = (ip_event_got_ip_t *)data;
instance->ip_event(id, ip_data);
}
}
esp_err_t perform()
{
auto header = rpc.get_header();
ESP_LOGI(TAG, "Received header id %d", (int) header.id);
switch (header.id) {
case api_id::SET_MODE: {
auto req = rpc.get_payload<wifi_mode_t>(api_id::SET_MODE, header);
auto ret = esp_wifi_set_mode(req);
if (rpc.send(api_id::SET_MODE, &ret) != ESP_OK) {
return ESP_FAIL;
}
break;
}
case api_id::INIT: {
auto req = rpc.get_payload<wifi_init_config_t>(api_id::INIT, header);
req.osi_funcs = &g_wifi_osi_funcs;
req.wpa_crypto_funcs = g_wifi_default_wpa_crypto_funcs;
auto ret = esp_wifi_init(&req);
if (rpc.send(api_id::INIT, &ret) != ESP_OK) {
return ESP_FAIL;
}
break;
}
case api_id::SET_CONFIG: {
auto req = rpc.get_payload<esp_wifi_remote_config>(api_id::SET_CONFIG, header);
auto ret = esp_wifi_set_config(req.interface, &req.conf);
if (rpc.send(api_id::SET_CONFIG, &ret) != ESP_OK) {
return ESP_FAIL;
}
break;
}
case api_id::START: {
if (header.size != 0) {
return ESP_FAIL;
}
auto ret = esp_wifi_start();
if (rpc.send(api_id::START, &ret) != ESP_OK) {
return ESP_FAIL;
}
break;
}
case api_id::CONNECT: {
if (header.size != 0) {
return ESP_FAIL;
}
auto ret = esp_wifi_connect();
if (rpc.send(api_id::CONNECT, &ret) != ESP_OK) {
return ESP_FAIL;
}
break;
}
case api_id::GET_MAC: {
auto req = rpc.get_payload<wifi_interface_t>(api_id::GET_MAC, header);
esp_wifi_remote_mac_t resp = {};
resp.err = esp_wifi_get_mac(req, resp.mac);
if (rpc.send(api_id::GET_MAC, &resp) != ESP_OK) {
return ESP_FAIL;
}
break;
}
default:
return ESP_FAIL;
}
return ESP_OK;
}
};
namespace server {
RpcInstance instance;
}
RpcInstance *RpcEngine::init_server()
{
esp_tls_cfg_server_t cfg = {};
cfg.cacert_buf = server::ca_crt;
cfg.cacert_bytes = sizeof(server::ca_crt);
cfg.servercert_buf = server::crt;
cfg.servercert_bytes = sizeof(server::crt);
cfg.serverkey_buf = server::key;
cfg.serverkey_bytes = sizeof(server::key);
ESP_RETURN_ON_FALSE(tls_ = esp_tls_init(), nullptr, TAG, "Failed to create ESP-TLS instance");
ESP_RETURN_ON_FALSE(esp_tls_server_session_create(&cfg, server::instance.sock, tls_) == ESP_OK, nullptr, TAG, "Failed to create TLS session");
return &server::instance;
}
} // namespace eppp_rpc
using namespace eppp_rpc;
extern "C" esp_err_t server_init(void)
{
return server::instance.init();
}

View File

@ -271,26 +271,6 @@ WEAK esp_err_t esp_wifi_remote_set_csi(_Bool en)
LOG_UNSUPPORTED_AND_RETURN(ESP_ERR_NOT_SUPPORTED); LOG_UNSUPPORTED_AND_RETURN(ESP_ERR_NOT_SUPPORTED);
} }
WEAK esp_err_t esp_wifi_remote_set_ant_gpio(const wifi_ant_gpio_config_t *config)
{
LOG_UNSUPPORTED_AND_RETURN(ESP_ERR_NOT_SUPPORTED);
}
WEAK esp_err_t esp_wifi_remote_get_ant_gpio(wifi_ant_gpio_config_t *config)
{
LOG_UNSUPPORTED_AND_RETURN(ESP_ERR_NOT_SUPPORTED);
}
WEAK esp_err_t esp_wifi_remote_set_ant(const wifi_ant_config_t *config)
{
LOG_UNSUPPORTED_AND_RETURN(ESP_ERR_NOT_SUPPORTED);
}
WEAK esp_err_t esp_wifi_remote_get_ant(wifi_ant_config_t *config)
{
LOG_UNSUPPORTED_AND_RETURN(ESP_ERR_NOT_SUPPORTED);
}
WEAK int64_t esp_wifi_remote_get_tsf_time(wifi_interface_t interface) WEAK int64_t esp_wifi_remote_get_tsf_time(wifi_interface_t interface)
{ {
LOG_UNSUPPORTED_AND_RETURN(-1); LOG_UNSUPPORTED_AND_RETURN(-1);

View File

@ -267,26 +267,6 @@ esp_err_t esp_wifi_set_csi(_Bool en)
return esp_wifi_remote_set_csi(en); return esp_wifi_remote_set_csi(en);
} }
esp_err_t esp_wifi_set_ant_gpio(const wifi_ant_gpio_config_t *config)
{
return esp_wifi_remote_set_ant_gpio(config);
}
esp_err_t esp_wifi_get_ant_gpio(wifi_ant_gpio_config_t *config)
{
return esp_wifi_remote_get_ant_gpio(config);
}
esp_err_t esp_wifi_set_ant(const wifi_ant_config_t *config)
{
return esp_wifi_remote_set_ant(config);
}
esp_err_t esp_wifi_get_ant(wifi_ant_config_t *config)
{
return esp_wifi_remote_get_ant(config);
}
int64_t esp_wifi_get_tsf_time(wifi_interface_t interface) int64_t esp_wifi_get_tsf_time(wifi_interface_t interface)
{ {
return esp_wifi_remote_get_tsf_time(interface); return esp_wifi_remote_get_tsf_time(interface);

View File

@ -0,0 +1,23 @@
# 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)

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

@ -0,0 +1,4 @@
idf_component_register(SRCS "app_main.c"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -0,0 +1,21 @@
menu "Example Configuration"
config BROKER_URL
string "Broker URL"
default "mqtt://mqtt.eclipseprojects.io"
help
URL of the broker to connect to
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
endmenu

View File

@ -0,0 +1,204 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_netif.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "mqtt_client.h"
static const char *TAG = "esp_mqtt_demo";
static EventGroupHandle_t s_wifi_event_group;
static int s_retry_num = 0;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_MAXIMUM_RETRY 5
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}
static void mqtt_app_start(void)
{
esp_mqtt_client_config_t mqtt_cfg = {};
mqtt_cfg.broker.address.uri = CONFIG_BROKER_URL;
mqtt_cfg.credentials.client_id = "idf_on_linux_client";
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
esp_mqtt_client_register_event(client, (esp_mqtt_event_id_t)ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(client);
}
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "EVENT type %s id %d", event_base, (int)event_id);
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG, "connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
static void wifi_init_sta()
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
void app_main(void)
{
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("mqtt_client", 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("outbox", ESP_LOG_VERBOSE);
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta();
mqtt_app_start();
}

View File

@ -0,0 +1,5 @@
dependencies:
espressif/eppp_link: "^0.0.1"
esp_wifi_remote:
version: "*"
override_path: ../../..

View File

@ -0,0 +1,9 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) 5.3.0 Project Minimal Configuration
#
CONFIG_IDF_TARGET="esp32p4"
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
CONFIG_ESP_WIFI_REMOTE_EPPP_UART_TX_PIN=17
CONFIG_ESP_WIFI_REMOTE_EPPP_UART_RX_PIN=16

View File

@ -0,0 +1,3 @@
CONFIG_IDF_TARGET="esp32h2"
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_CONNECT_ETHERNET=y

View File

@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="linux"
# CONFIG_ESP_EVENT_POST_FROM_ISR is not set

View File

@ -0,0 +1,6 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(pppos_slave)

View File

@ -0,0 +1,7 @@
# Wi-Fi station to PPPoS server
This example demonstrate using NAPT to bring connectivity from WiFi station to PPPoS server.
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)

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "station_example_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,5 @@
dependencies:
espressif/eppp_link: "^0.0.1"
esp_wifi_remote:
version: "*"
override_path: ../../..

View File

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <string.h>
#include <esp_private/wifi.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "eppp_link.h"
#include "esp_wifi_remote.h"
esp_err_t server_init(void);
void app_main(void)
{
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
server_init();
}

View File

@ -0,0 +1,15 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) 5.3.0 Project Minimal Configuration
#
CONFIG_IDF_TARGET="esp32c6"
CONFIG_LWIP_IP_FORWARD=y
CONFIG_LWIP_IPV4_NAPT=y
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
CONFIG_ESP_WIFI_REMOTE_EPPP_UART_TX_PIN=22
CONFIG_ESP_WIFI_REMOTE_EPPP_UART_RX_PIN=23
CONFIG_ESP_WIFI_REMOTE_EPPP_CLIENT_CA="MIIDIzCCAgugAwIBAgIUTWOEAhITHAm2ixn5i2XlSeL01mowDQYJKoZIhvcNAQELBQAwITELMAkGA1UEBhMCQ1oxEjAQBgNVBAMMCUVzcHJlc3NpZjAeFw0yNDA0MTAxNTEzNTdaFw0yNTA0MTAxNTEzNTdaMCExCzAJBgNVBAYTAkNaMRIwEAYDVQQDDAlFc3ByZXNzaWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCSewupiSiyZNfLdVW0/smQB0yJ0Ua0KNU423ZZ7HMSrBPhfHtnQQ6SJqTdfqGCl1lvSsJZN9aT4iaEtyAm6N9trmSbSWVlkn0D8MQuBHwHCT7jRsLnyRYURRPUs11TkQdqvxtsIFqFVFB/8nJqy4IuU6JFTobCbUappQMdHKCyidXJUVHZ5y+KK2kEYFiv26rHlry+D0O/VO5/xl97uFIzP0JVdnGNu5sy9uoRYp+ua0moD3tx12tYe83XIuHKbKHMpIayjPIoaZzhCwomZMh0NETEQ5t7RXYneRGZvXXyIb/O8jPCmbfSqJ6umhPhf757xBXHaC0iG/xlND0dnRIvAgMBAAGjUzBRMB0GA1UdDgQWBBTgqejeFi/5UAgNhNv4aH7UniqmQjAfBgNVHSMEGDAWgBTgqejeFi/5UAgNhNv4aH7UniqmQjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCQnqIue6KLXkjOXGtO5Bl4TkZpYAPkQuGiM6RgaBhdt3P5J1mF4T6aav8qGXSHShy1E3XkMR5OC3hkhG+FKBVKSaQLsRipuo+CeHp5RfOCNEzNI0RZwKJI92RcdWlhOA+pOTruXSoYuZvj0xnaePEghTrr7PLdgirpzIffLjvgh8BcQAz5QzP0U1XHkAVzbQjUBChiEiXVAlKChk7kKB/wEzwX3cvYKlTc89RB6I3+a+KhYJt3LIAOIDeyVp+Bhmb1JSo3H7zMpJAksG2RMnZCwlHeR6cMbb/OtJYeUKpNUxj0SaeNyHo3y8Q21G8TXcc9suU6sYJi780ArulC3cbQ"
CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_CRT="MIICvzCCAacCFCanehvaDq0bhjZA/3W/h4b0p1VHMA0GCSqGSIb3DQEBCwUAMCExCzAJBgNVBAYTAkNaMRIwEAYDVQQDDAlFc3ByZXNzaWYwHhcNMjQwNDEwMTUxMzU3WhcNMjUwNDEwMTUxMzU3WjAXMRUwEwYDVQQDDAwxOTIuMTY4LjExLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsyR1FqBhBT3mr9AH/6iYoo1VCHbzB+V/StfCokv6LnMm6t7yiu1GgzqVk1aiHrUJGor5tBfpXywLnfVjFByZSBhhEkI26xQVdK5pZUsU1hMCJ6CWd105CD+0e5tTbGzF0PNH2KzFdg2YUqOSWBsfmgSNtnp3az8XmZN5i4958Sxe1kMN3f6EQwvkxZHGVgXCrUsdsHAEyV5NVfYq7P2nBxz3HJSGkTScFd+PRp3nfVFbBbCQDmqwoPZ7E/gUXjoLIFf7zjIMzCXTsZd/dKgXWWEFHq8SPWmLtAEvPCProT5QUaZ3gJSHup9Wmh+ok9W8wrwMj1sHlfiZWo3tatFmvAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAeWiWyTTyNbZRHq8X9qbr7oG6SYWOqDR3lPfs8wB8B+1Hez/JCrI/8quNqVAjhoTtebtp/2mcEseQDj5BOx8NBCDAmUOwB+ZKPzUoGiALy5XFGqUXzTv9tBMotC6dAMTIbT2Ql1mPJgtg2+Qv7vg6hsk4rlPMoICqN3lW6zXo2GOuJ56Tj5NkvVxv6MOVN2e3p67c92rRBysAxP6MaV8S9s2+VvnENuxpU5cq8sfzaxFkTn4UD9PoQSYGPNL9kv7Y/h7H5wlKiFY24KowPTHjulaH/DC9Fk4F1gNWjnkFcOgepzhiw/ibLrivMptUFtplFFbxGuwY5PaA26yhliBv8="
CONFIG_ESP_WIFI_REMOTE_EPPP_SERVER_KEY="MIIEowIBAAKCAQEArMkdRagYQU95q/QB/+omKKNVQh28wflf0rXwqJL+i5zJure8ortRoM6lZNWoh61CRqK+bQX6V8sC531YxQcmUgYYRJCNusUFXSuaWVLFNYTAieglnddOQg/tHubU2xsxdDzR9isxXYNmFKjklgbH5oEjbZ6d2s/F5mTeYuPefEsXtZDDd3+hEML5MWRxlYFwq1LHbBwBMleTVX2Kuz9pwcc9xyUhpE0nBXfj0ad531RWwWwkA5qsKD2exP4FF46CyBX+84yDMwl07GXf3SoF1lhBR6vEj1pi7QBLzwj66E+UFGmd4CUh7qfVpofqJPVvMK8DI9bB5X4mVqN7WrRZrwIDAQABAoIBAHDh833OlAoXIiZ1A9lt4AAstUa8ptUTVscSlmeLoUEU7bZO0659MpccanG2JKR/TQ1YxY/0l9lDiGI0Qgp24VI1KSWiSvX6Qcbc9bnlmXGdiSyPvgOg5j/Cp+fIZse+vFB0a7eoAFhXwBk/PhfF1lOBiuPS/M43b9NVkvSIapJIaS4pvmkBvKSzHEvSArDHcr+1vFuFssZyHTnXmVgB4WiYPX4ACE8S18cnjXIQDfx8zpBbF/itnqeHDC5echnto63UDB7qHZa+DVvakhEtv50rzAuhq3/uspBClucuQUhlAAimd4OeKuwB1UC0K9AamDZRCnsf0O/Bo8/W4SWYAgECgYEA5LpRmcQ9ghW8V6bzwYvK8XGWyeNy94qOpZysFeOjxqe2sUTHVY2Ty1s44RbDd/bm0n3xcxMtLof/6Oz4TX+JseskQWBQlRiwuqc46CcHHjUQ8qokfWtASwWYgb6AzLa4B/D+H91wP/AzRfYNdRB9xhSCr7AOk9Vo5KmEPRLN/VMCgYEAwWM3oDaCkMicoMgngz/9dZ2/1yohfYdrupC0pGPhtBFNKghP+9S+e6cwWKzwQJbbRjGgt1OA3e4UEuTHJjp1tw+BRkNQ/1FI0psJGwmOtveAE7yiHf7Tw7mNDk+j32vpAPWnL7I3222Kv4G8xi2vSbn3IaI2sl7M0RHLJc/JCrUCgYBh4dI16aMg3khhglLiSv6oYKHU9/8lLChreyaxn19hDjjCl9puJE5RQlKPEPzJg+G3xqnjQxucxBqiBXclQyUb/LLhP2R8ybonxpQ11S3YoYEFOAaxnYpAEL75Fxtrf+41h85YuJzm39YxZGDR7iLE99YNdVxnq3ZeFKVAtaFtfQKBgEalPRvc7eOANZ+SnsSWqru9regnLubRgqw70pG/HyONsnepY7amaBN55vJt8rJVqbLBzGlMKuZn45NnWc0ATCJcmqgVTVCH3Cd7lV74Jdd3IKWVIk/82FVGwl25AC9NF2hPVQzaeQdCxA3jkhd/dupi8gGqZXrRoNa7PlAI0POFAoGBAJDlvddpEYcKhT3pncXIorVfYR67TlXVcKZHvL9XljwgDuf1j52ZhAs8UuoWW+NSgOdWVxocAshrLbu5fjSIN92oDAq6YVeXvxis6e8l79xd6A5iFH9Po+dEuQSaOR8XgW56n39oVR2hfaFwqijawIEzDNlYpiXAD3qNyW5e0MKA"

View File

@ -2,6 +2,8 @@ version: 0.1.12
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_wifi_remote url: https://github.com/espressif/esp-protocols/tree/master/components/esp_wifi_remote
description: Utility wrapper for esp_wifi functionality on remote targets description: Utility wrapper for esp_wifi functionality on remote targets
dependencies: dependencies:
espressif/eppp_link:
version: '0.0.1'
idf: idf:
version: '5.3' version: '5.3'
# espressif/esp_hosted: # espressif/esp_hosted:

View File

@ -57,10 +57,6 @@ esp_err_t esp_wifi_remote_80211_tx(wifi_interface_t ifx, const void *buffer, int
esp_err_t esp_wifi_remote_set_csi_rx_cb(wifi_csi_cb_t cb, void *ctx); esp_err_t esp_wifi_remote_set_csi_rx_cb(wifi_csi_cb_t cb, void *ctx);
esp_err_t esp_wifi_remote_set_csi_config(const wifi_csi_config_t *config); esp_err_t esp_wifi_remote_set_csi_config(const wifi_csi_config_t *config);
esp_err_t esp_wifi_remote_set_csi(_Bool en); esp_err_t esp_wifi_remote_set_csi(_Bool en);
esp_err_t esp_wifi_remote_set_ant_gpio(const wifi_ant_gpio_config_t *config);
esp_err_t esp_wifi_remote_get_ant_gpio(wifi_ant_gpio_config_t *config);
esp_err_t esp_wifi_remote_set_ant(const wifi_ant_config_t *config);
esp_err_t esp_wifi_remote_get_ant(wifi_ant_config_t *config);
int64_t esp_wifi_remote_get_tsf_time(wifi_interface_t interface); int64_t esp_wifi_remote_get_tsf_time(wifi_interface_t interface);
esp_err_t esp_wifi_remote_set_inactive_time(wifi_interface_t ifx, uint16_t sec); esp_err_t esp_wifi_remote_set_inactive_time(wifi_interface_t ifx, uint16_t sec);
esp_err_t esp_wifi_remote_get_inactive_time(wifi_interface_t ifx, uint16_t *sec); esp_err_t esp_wifi_remote_get_inactive_time(wifi_interface_t ifx, uint16_t *sec);

View File

@ -15,6 +15,7 @@ Param = namedtuple('Param', ['ptr', 'array', 'qual', 'type', 'name'])
AUTO_GENERATED = 'This file is auto-generated' AUTO_GENERATED = 'This file is auto-generated'
COPYRIGHT_HEADER = open('copyright_header.h', 'r').read() COPYRIGHT_HEADER = open('copyright_header.h', 'r').read()
NAMESPACE = re.compile(r'^esp_wifi') NAMESPACE = re.compile(r'^esp_wifi')
DEPRECATED_API = ['esp_wifi_set_ant_gpio', 'esp_wifi_get_ant', 'esp_wifi_get_ant_gpio', 'esp_wifi_set_ant']
class FunctionVisitor(c_ast.NodeVisitor): class FunctionVisitor(c_ast.NodeVisitor):
@ -51,6 +52,8 @@ class FunctionVisitor(c_ast.NodeVisitor):
if isinstance(node.type, c_ast.TypeDecl): if isinstance(node.type, c_ast.TypeDecl):
func_name = node.type.declname func_name = node.type.declname
if func_name.startswith('esp_wifi') and func_name in self.content: if func_name.startswith('esp_wifi') and func_name in self.content:
if func_name in DEPRECATED_API:
return
ret = node.type.type.names[0] ret = node.type.type.names[0]
args = [] args = []
for param in node.args.params: for param in node.args.params:
@ -309,6 +312,7 @@ def generate_kconfig(idf_path, component_path):
f.write(' bool\n') f.write(' bool\n')
f.write(' default y\n\n') f.write(' default y\n\n')
f.write(' orsource "./Kconfig.soc_wifi_caps.in"\n') f.write(' orsource "./Kconfig.soc_wifi_caps.in"\n')
f.write(' orsource "./Kconfig.rpc.in"\n')
for line1 in lines: for line1 in lines:
line = line1.strip() line = line1.strip()
if re.match(r'^if\s+[A-Z_0-9]+\s*$', line): if re.match(r'^if\s+[A-Z_0-9]+\s*$', line):
@ -322,7 +326,7 @@ def generate_kconfig(idf_path, component_path):
line1 = re.compile(config).sub('SLAVE_' + config, line1) line1 = re.compile(config).sub('SLAVE_' + config, line1)
f.write(line1) f.write(line1)
if line.startswith('if ESP_WIFI_ENABLED'): if re.match(r'^if\s+\(?ESP_WIFI_ENABLED', line):
copy = nested_if copy = nested_if
f.write('endmenu # Wi-Fi Remote\n') f.write('endmenu # Wi-Fi Remote\n')
return [remote_kconfig] return [remote_kconfig]

View File

@ -17,5 +17,7 @@ menu "ESP Hosted Mock"
bool "esp32c6" bool "esp32c6"
config SLAVE_IDF_TARGET_ESP32H2 config SLAVE_IDF_TARGET_ESP32H2
bool "esp32h2" bool "esp32h2"
config SLAVE_IDF_TARGET_ESP32P4
bool "esp32p4"
endchoice endchoice
endmenu endmenu

View File

@ -267,26 +267,6 @@ esp_err_t esp_wifi_remote_set_csi(_Bool en)
return ESP_OK; return ESP_OK;
} }
esp_err_t esp_wifi_remote_set_ant_gpio(const wifi_ant_gpio_config_t *config)
{
return ESP_OK;
}
esp_err_t esp_wifi_remote_get_ant_gpio(wifi_ant_gpio_config_t *config)
{
return ESP_OK;
}
esp_err_t esp_wifi_remote_set_ant(const wifi_ant_config_t *config)
{
return ESP_OK;
}
esp_err_t esp_wifi_remote_get_ant(wifi_ant_config_t *config)
{
return ESP_OK;
}
int64_t esp_wifi_remote_get_tsf_time(wifi_interface_t interface) int64_t esp_wifi_remote_get_tsf_time(wifi_interface_t interface)
{ {
return 0; return 0;

View File

@ -57,10 +57,6 @@ esp_err_t esp_wifi_remote_80211_tx(wifi_interface_t ifx, const void *buffer, int
esp_err_t esp_wifi_remote_set_csi_rx_cb(wifi_csi_cb_t cb, void *ctx); esp_err_t esp_wifi_remote_set_csi_rx_cb(wifi_csi_cb_t cb, void *ctx);
esp_err_t esp_wifi_remote_set_csi_config(const wifi_csi_config_t *config); esp_err_t esp_wifi_remote_set_csi_config(const wifi_csi_config_t *config);
esp_err_t esp_wifi_remote_set_csi(_Bool en); esp_err_t esp_wifi_remote_set_csi(_Bool en);
esp_err_t esp_wifi_remote_set_ant_gpio(const wifi_ant_gpio_config_t *config);
esp_err_t esp_wifi_remote_get_ant_gpio(wifi_ant_gpio_config_t *config);
esp_err_t esp_wifi_remote_set_ant(const wifi_ant_config_t *config);
esp_err_t esp_wifi_remote_get_ant(wifi_ant_config_t *config);
int64_t esp_wifi_remote_get_tsf_time(wifi_interface_t interface); int64_t esp_wifi_remote_get_tsf_time(wifi_interface_t interface);
esp_err_t esp_wifi_remote_set_inactive_time(wifi_interface_t ifx, uint16_t sec); esp_err_t esp_wifi_remote_set_inactive_time(wifi_interface_t ifx, uint16_t sec);
esp_err_t esp_wifi_remote_get_inactive_time(wifi_interface_t ifx, uint16_t *sec); esp_err_t esp_wifi_remote_get_inactive_time(wifi_interface_t ifx, uint16_t *sec);

View File

@ -280,26 +280,6 @@ void run_all_wifi_apis(void)
esp_wifi_set_csi(en); esp_wifi_set_csi(en);
} }
{
const wifi_ant_gpio_config_t *config = NULL;
esp_wifi_set_ant_gpio(config);
}
{
wifi_ant_gpio_config_t *config = NULL;
esp_wifi_get_ant_gpio(config);
}
{
const wifi_ant_config_t *config = NULL;
esp_wifi_set_ant(config);
}
{
wifi_ant_config_t *config = NULL;
esp_wifi_get_ant(config);
}
{ {
wifi_interface_t interface = 0; wifi_interface_t interface = 0;
esp_wifi_get_tsf_time(interface); esp_wifi_get_tsf_time(interface);

View File

@ -280,26 +280,6 @@ void run_all_wifi_remote_apis(void)
esp_wifi_remote_set_csi(en); esp_wifi_remote_set_csi(en);
} }
{
const wifi_ant_gpio_config_t *config = NULL;
esp_wifi_remote_set_ant_gpio(config);
}
{
wifi_ant_gpio_config_t *config = NULL;
esp_wifi_remote_get_ant_gpio(config);
}
{
const wifi_ant_config_t *config = NULL;
esp_wifi_remote_set_ant(config);
}
{
wifi_ant_config_t *config = NULL;
esp_wifi_remote_get_ant(config);
}
{ {
wifi_interface_t interface = 0; wifi_interface_t interface = 0;
esp_wifi_remote_get_tsf_time(interface); esp_wifi_remote_get_tsf_time(interface);

View File

@ -0,0 +1,2 @@
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_SERVER_SUPPORT=y