ESP-NOW bridge for P4 (#573)
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
#include <sdkconfig.h>
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED)
|
||||
|
||||
#include <Tactility/service/espnow/EspNowBackend.h>
|
||||
#include <Tactility/service/wifi/Wifi.h>
|
||||
|
||||
#include <esp_now.h>
|
||||
#include <esp_wifi.h>
|
||||
|
||||
#include <tactility/log.h>
|
||||
|
||||
namespace tt::service::espnow::backend {
|
||||
|
||||
constexpr auto* TAG = "EspNowBackendNative";
|
||||
static bool wifiStartedByEspNow = false;
|
||||
static bool longRangeProtocolApplied = false;
|
||||
static wifi_interface_t longRangeInterface = WIFI_IF_STA;
|
||||
static uint8_t savedProtocolBitmap = 0;
|
||||
|
||||
static bool deinitWifi();
|
||||
|
||||
static bool initWifi(const EspNowConfig& config) {
|
||||
auto wifi_state = wifi::getRadioState();
|
||||
bool wifi_already_running = (wifi_state != wifi::RadioState::Off && wifi_state != wifi::RadioState::OffPending);
|
||||
|
||||
wifi_mode_t mode;
|
||||
if (config.mode == Mode::Station) {
|
||||
mode = wifi_mode_t::WIFI_MODE_STA;
|
||||
} else {
|
||||
mode = wifi_mode_t::WIFI_MODE_AP;
|
||||
}
|
||||
|
||||
// Only initialize WiFi if it's not already running; ESP-NOW coexists with STA mode
|
||||
wifiStartedByEspNow = !wifi_already_running;
|
||||
if (wifiStartedByEspNow) {
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
if (esp_wifi_init(&cfg) != ESP_OK) {
|
||||
LOG_E(TAG,"esp_wifi_init() failed");
|
||||
wifiStartedByEspNow = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_wifi_set_storage(WIFI_STORAGE_RAM) != ESP_OK) {
|
||||
LOG_E(TAG,"esp_wifi_set_storage() failed");
|
||||
esp_wifi_deinit();
|
||||
wifiStartedByEspNow = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_wifi_set_mode(mode) != ESP_OK) {
|
||||
LOG_E(TAG,"esp_wifi_set_mode() failed");
|
||||
esp_wifi_deinit();
|
||||
wifiStartedByEspNow = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_wifi_start() != ESP_OK) {
|
||||
LOG_E(TAG,"esp_wifi_start() failed");
|
||||
esp_wifi_deinit();
|
||||
wifiStartedByEspNow = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.channel != 0 && esp_wifi_set_channel(config.channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) {
|
||||
LOG_E(TAG,"esp_wifi_set_channel() failed");
|
||||
deinitWifi();
|
||||
return false;
|
||||
}
|
||||
} else if (config.channel != 0 &&
|
||||
wifi_state != wifi::RadioState::ConnectionActive && wifi_state != wifi::RadioState::ConnectionPending) {
|
||||
// WifiService already owns the radio but isn't associated to an AP: an unassociated STA's
|
||||
// operating channel is otherwise left undefined/wherever it last scanned to, which silently
|
||||
// breaks ESP-NOW (esp_now_send() reports success but nothing reaches a peer sitting on a
|
||||
// different channel) - only skip this when actually connected/connecting, where the STA is
|
||||
// already locked to the AP's channel and forcing a different one would disrupt that link.
|
||||
if (esp_wifi_set_channel(config.channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) {
|
||||
LOG_W(TAG, "esp_wifi_set_channel() failed on externally managed radio - ESP-NOW may not reach peers");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.longRange) {
|
||||
wifi_interface_t wifi_interface = (config.mode == Mode::Station) ? WIFI_IF_STA : WIFI_IF_AP;
|
||||
|
||||
// Preserve whatever protocol mask the interface already had (e.g. set by WifiService if
|
||||
// it owns this interface) so it can be restored in deinitWifi() - esp_wifi_set_protocol()
|
||||
// below otherwise permanently stomps it, even when ESP-NOW doesn't own the radio. Skip
|
||||
// applying long-range at all if the snapshot fails: without a saved mask to restore,
|
||||
// changing the protocol would permanently alter an externally-managed interface with no
|
||||
// way to undo it later.
|
||||
uint8_t previousBitmap = 0;
|
||||
bool hadPreviousBitmap = esp_wifi_get_protocol(wifi_interface, &previousBitmap) == ESP_OK;
|
||||
|
||||
if (!hadPreviousBitmap) {
|
||||
LOG_W(TAG, "esp_wifi_get_protocol() failed - skipping long-range (can't safely restore protocol mask later)");
|
||||
} else if (esp_wifi_set_protocol(wifi_interface, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR) != ESP_OK) {
|
||||
LOG_W(TAG,"esp_wifi_set_protocol() for long range failed");
|
||||
} else {
|
||||
longRangeProtocolApplied = true;
|
||||
longRangeInterface = wifi_interface;
|
||||
savedProtocolBitmap = previousBitmap;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_I(TAG, "WiFi initialized for ESP-NOW (wifi already running: %s)", wifi_already_running ? "yes" : "no");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool deinitWifi() {
|
||||
// Restore the interface's protocol mask before either stopping WiFi or handing the radio
|
||||
// back to WifiService - covers both paths, since an externally managed radio (wifiStartedByEspNow
|
||||
// == false) is the case that actually needs its prior protocol bits put back.
|
||||
if (longRangeProtocolApplied) {
|
||||
if (esp_wifi_set_protocol(longRangeInterface, savedProtocolBitmap) != ESP_OK) {
|
||||
LOG_W(TAG, "Failed to restore prior WiFi protocol bitmap");
|
||||
}
|
||||
longRangeProtocolApplied = false;
|
||||
}
|
||||
|
||||
if (wifiStartedByEspNow) {
|
||||
esp_wifi_stop();
|
||||
esp_wifi_deinit();
|
||||
wifiStartedByEspNow = false;
|
||||
LOG_I(TAG, "WiFi stopped (was started by ESP-NOW)");
|
||||
} else {
|
||||
LOG_I(TAG, "WiFi left running (managed by WiFi service)");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t espnowVersion = 0;
|
||||
|
||||
bool init(const EspNowConfig& config, esp_now_recv_cb_t recvCallback) {
|
||||
if (!initWifi(config)) {
|
||||
LOG_E(TAG, "initWifi() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_now_init() != ESP_OK) {
|
||||
LOG_E(TAG, "esp_now_init() failed");
|
||||
deinitWifi();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_now_register_recv_cb(recvCallback) != ESP_OK) {
|
||||
LOG_E(TAG, "esp_now_register_recv_cb() failed");
|
||||
esp_now_deinit();
|
||||
deinitWifi();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_now_set_pmk(config.masterKey) != ESP_OK) {
|
||||
LOG_E(TAG, "esp_now_set_pmk() failed");
|
||||
esp_now_deinit();
|
||||
deinitWifi();
|
||||
return false;
|
||||
}
|
||||
|
||||
espnowVersion = 0;
|
||||
if (esp_now_get_version(&espnowVersion) == ESP_OK) {
|
||||
LOG_I(TAG, "ESP-NOW version: %u.0", (unsigned)espnowVersion);
|
||||
} else {
|
||||
LOG_W(TAG, "Failed to get ESP-NOW version");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deinit() {
|
||||
bool success = true;
|
||||
|
||||
if (esp_now_deinit() != ESP_OK) {
|
||||
LOG_E(TAG, "esp_now_deinit() failed");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!deinitWifi()) {
|
||||
LOG_E(TAG, "deinitWifi() failed");
|
||||
success = false;
|
||||
}
|
||||
|
||||
espnowVersion = 0;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool addPeer(const esp_now_peer_info_t& peer) {
|
||||
if (esp_now_add_peer(&peer) != ESP_OK) {
|
||||
LOG_E(TAG, "Failed to add peer");
|
||||
return false;
|
||||
} else {
|
||||
LOG_I(TAG, "Peer added");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool send(const uint8_t* address, const uint8_t* buffer, size_t bufferLength) {
|
||||
return esp_now_send(address, buffer, bufferLength) == ESP_OK;
|
||||
}
|
||||
|
||||
uint32_t getVersion() {
|
||||
return espnowVersion;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // CONFIG_SOC_WIFI_SUPPORTED && !CONFIG_SLAVE_SOC_WIFI_SUPPORTED
|
||||
Reference in New Issue
Block a user