Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESP-NOW sync (including audioreactive) with slave search for master's channel #4066

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 126 additions & 79 deletions usermods/audioreactive/audio_reactive.h
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this commit have changes that do not relate to ESP-NOW?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As nobody is really taking care of upstream audioreactive usermod ATM I took the liberty to fix inconsistencies in float usage while I was at implementing ESP-NOW sync for it. The changes do not change behaviour in any way.

There is a breaking change in v2 sync header to allow differentiation of packets when future usermods will implement their own ESP-NOW handling. This change is necessary IMO.

Copy link
Collaborator

@softhack007 softhack007 Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As nobody is really taking care of upstream audioreactive usermod ATM I took the liberty to fix inconsistencies in float usage while I was at implementing ESP-NOW sync for it. The changes do not change behaviour in any way.

@blazoncek Sorry this is not true at all. For sure you may add subtle bugs into audioreactive as you wish. But it's not a good idea to "sneak in" such changes under to cover of "esp-now enhancements". I an still here, and in case you want to modify audioreactive core functions, why not do it in a separate PR that gets my full attention?

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ void FourLineDisplayUsermod::redraw(bool forceRedraw) {
while (drawing && millis()-now < 25) delay(1); // wait if someone else is drawing
if (drawing || lockRedraw) return;

if (apActive && WLED_WIFI_CONFIGURED && now<15000) {
if (apActive && isWiFiConfigured() && now < 15000) {
knownSsid = apSSID;
networkOverlay(PSTR("NETWORK INFO"),30000);
return;
Expand Down
6 changes: 6 additions & 0 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject nw = doc["nw"];
#ifndef WLED_DISABLE_ESPNOW
CJSON(enableESPNow, nw[F("espnow")]);
char linked_remote[13];
getStringFromJson(linked_remote, nw[F("linked_remote")], 13);
linked_remote[12] = '\0';
#define hex2int(a) (((a)>='0' && (a)<='9') ? (a)-'0' : ((a)>='A' && (a)<='F') ? (a)-'A'+10 : ((a)>='a' && (a)<='f') ? (a)-'a'+10 : 0)
for (int i=0; i<6; i++) masterESPNow[i] = (hex2int(linked_remote[i*2])<<4) | hex2int(linked_remote[i*2+1]);
DEBUG_PRINTF_P(PSTR("ESP-NOW linked remote: " MACSTR "\n"), MAC2STR(masterESPNow));
#endif

size_t n = 0;
Expand Down Expand Up @@ -744,6 +748,8 @@ void serializeConfig() {
JsonObject nw = root.createNestedObject("nw");
#ifndef WLED_DISABLE_ESPNOW
nw[F("espnow")] = enableESPNow;
char linked_remote[13];
sprintf(linked_remote, "%02x%02x%02x%02x%02x%02x", MAC2STR(masterESPNow));
nw[F("linked_remote")] = linked_remote;
#endif

Expand Down
17 changes: 17 additions & 0 deletions wled00/fcn_declare.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,29 @@ void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w);
void refreshNodeList();
void sendSysInfoUDP();
#ifndef WLED_DISABLE_ESPNOW
typedef struct {
char magic[4]; // enough to store "WLED"
uint8_t packet:4; // packet sequence
uint8_t noOfPackets:4;// total number of packets
uint8_t data[245]; // payload
} __attribute__((packed, aligned(1))) EspNowPartialPacket;

typedef struct {
char magic[4]; // enough to store "WLED"
uint8_t version:4; // message packet version (changes when packet size changes); not intended to change beyond 15 (0 means unspecified/irrelevant)
uint8_t channel:4; // master's WiFi channel used
uint32_t time; // may be used for time synchronisation (NOTE: time_t varies in size on ESP32 and ESP8266)
uint8_t reserved[7]; // 7 bytes reserved for future use
} __attribute__((packed, aligned(1))) EspNowBeacon;

void espNowSentCB(uint8_t* address, uint8_t status);
void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rssi, bool broadcast);
#endif

//network.cpp
int getSignalQuality(int rssi);
int8_t findWiFi(bool doScan = false);
bool isWiFiConfigured();
void WiFiEvent(WiFiEvent_t event);

//um_manager.cpp
Expand Down
2 changes: 1 addition & 1 deletion wled00/improv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ void handleImprovPacket() {
case ImprovRPCType::Command_Wifi: parseWiFiCommand(rpcData); break;
case ImprovRPCType::Request_State: {
unsigned improvState = 0x02; //authorized
if (WLED_WIFI_CONFIGURED) improvState = 0x03; //provisioning
if (isWiFiConfigured()) improvState = 0x03; //provisioning
if (Network.isConnected()) improvState = 0x04; //provisioned
sendImprovStateResponse(improvState, false);
if (improvState == 0x04) sendImprovIPRPCResult(ImprovRPCType::Request_State);
Expand Down
4 changes: 1 addition & 3 deletions wled00/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -494,9 +494,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
bool apMode = getBoolVal(wifi[F("ap")], apActive);
if (!apActive && apMode) WLED::instance().initAP(); // start AP mode immediately
else if (apActive && !apMode) { // stop AP mode immediately
dnsServer.stop();
WiFi.softAPdisconnect(true);
apActive = false;
WLED::instance().stopAP();
}
//bool restart = wifi[F("restart")] | false;
//if (restart) forceReconnect = true;
Expand Down
133 changes: 126 additions & 7 deletions wled00/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include "wled_ethernet.h"


#ifdef WLED_USE_ETHERNET
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
// The following six pins are neither configurable nor
// can they be re-assigned through IOMUX / GPIO matrix.
// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface
Expand Down Expand Up @@ -170,19 +170,136 @@ int getSignalQuality(int rssi)
}


// performs asynchronous scan for available networks (which may take couple of seconds to finish)
// returns configured WiFi ID with the strongest signal (or default if no configured networks available)
int8_t findWiFi(bool doScan) {
if (multiWiFi.size() <= 1) {
DEBUG_PRINTLN(F("Defaulf WiFi used."));
return 0;
}

if (doScan) WiFi.scanDelete(); // restart scan

int status = WiFi.scanComplete(); // complete scan may take as much as several seconds (usually <6s with not very crowded air)

if (status == WIFI_SCAN_FAILED) {
DEBUG_PRINTF_P(PSTR("WiFi: Scan started. @ %lu ms\r\n"), millis());
WiFi.scanNetworks(true); // start scanning in asynchronous mode
} else if (status >= 0) { // status contains number of found networks (including duplicate SSIDs with different BSSID)
DEBUG_PRINTF_P(PSTR("WiFi: Scan completed: %d @ %lu ms\r\n"), status, millis());
int rssi = -9999;
unsigned selected = selectedWiFi;
for (int o = 0; o < status; o++) {
DEBUG_PRINT(F(" WiFi available: ")); DEBUG_PRINT(WiFi.SSID(o));
DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(WiFi.RSSI(o)); DEBUG_PRINTLN(F("dB"));
for (unsigned n = 0; n < multiWiFi.size(); n++)
if (!strcmp(WiFi.SSID(o).c_str(), multiWiFi[n].clientSSID)) {
// find the WiFi with the strongest signal (but keep priority of entry if signal difference is not big)
if ((n < selected && WiFi.RSSI(o) > rssi-10) || WiFi.RSSI(o) > rssi) {
rssi = WiFi.RSSI(o);
selected = n;
}
break;
}
}
DEBUG_PRINT(F("Selected SSID: ")); DEBUG_PRINT(multiWiFi[selected].clientSSID);
DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(rssi); DEBUG_PRINTLN(F("dB"));
return selected;
}
//DEBUG_PRINT(F("WiFi scan running."));
return status; // scan is still running or there was an error
}


bool isWiFiConfigured() {
return multiWiFi.size() > 1 || (strlen(multiWiFi[0].clientSSID) >= 1 && strcmp_P(multiWiFi[0].clientSSID, PSTR(DEFAULT_CLIENT_SSID)) != 0);
}

#if defined(ESP8266)
#define ARDUINO_EVENT_WIFI_AP_STADISCONNECTED WIFI_EVENT_SOFTAPMODE_STADISCONNECTED
#define ARDUINO_EVENT_WIFI_AP_STACONNECTED WIFI_EVENT_SOFTAPMODE_STACONNECTED
#define ARDUINO_EVENT_WIFI_STA_GOT_IP WIFI_EVENT_STAMODE_GOT_IP
#define ARDUINO_EVENT_WIFI_STA_CONNECTED WIFI_EVENT_STAMODE_CONNECTED
#define ARDUINO_EVENT_WIFI_STA_DISCONNECTED WIFI_EVENT_STAMODE_DISCONNECTED
#elif defined(ARDUINO_ARCH_ESP32) && !defined(ESP_ARDUINO_VERSION_MAJOR) //ESP_IDF_VERSION_MAJOR==3
// not strictly IDF v3 but Arduino core related
#define ARDUINO_EVENT_WIFI_AP_STADISCONNECTED SYSTEM_EVENT_AP_STADISCONNECTED
#define ARDUINO_EVENT_WIFI_AP_STACONNECTED SYSTEM_EVENT_AP_STACONNECTED
#define ARDUINO_EVENT_WIFI_STA_GOT_IP SYSTEM_EVENT_STA_GOT_IP
#define ARDUINO_EVENT_WIFI_STA_CONNECTED SYSTEM_EVENT_STA_CONNECTED
#define ARDUINO_EVENT_WIFI_STA_DISCONNECTED SYSTEM_EVENT_STA_DISCONNECTED
#define ARDUINO_EVENT_WIFI_AP_START SYSTEM_EVENT_AP_START
#define ARDUINO_EVENT_WIFI_AP_STOP SYSTEM_EVENT_AP_STOP
#define ARDUINO_EVENT_ETH_START SYSTEM_EVENT_ETH_START
#define ARDUINO_EVENT_ETH_CONNECTED SYSTEM_EVENT_ETH_CONNECTED
#define ARDUINO_EVENT_ETH_DISCONNECTED SYSTEM_EVENT_ETH_DISCONNECTED
#endif

//handle Ethernet connection event
void WiFiEvent(WiFiEvent_t event)
{
switch (event) {
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
case SYSTEM_EVENT_ETH_START:
case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
// AP client disconnected
DEBUG_PRINTLN(F("WiFi: AP Client Disconnected"));
if (--apClients == 0 && isWiFiConfigured()) forceReconnect = true; // no clients reconnect WiFi if awailable
DEBUG_PRINTLN(apClients);
break;
case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
// AP client connected
// it looks like this doesn't work as expected
DEBUG_PRINTLN(F("WiFi: AP Client Connected"));
apClients++;
DEBUG_PRINTLN(apClients);
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
DEBUG_PRINTLN();
DEBUG_PRINT(F("IP address: ")); DEBUG_PRINTLN(Network.localIP());
break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
DEBUG_PRINTF_P(PSTR("WiFi: Connected! @ %lu ms\r\n"), millis());
wasConnected = true;
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
if (wasConnected && interfacesInited) {
DEBUG_PRINTLN(F("WiFi: Disconnected"));
if (interfacesInited && WiFi.scanComplete() >= 0) {
findWiFi(true); // reinit WiFi scan
forceReconnect = true;
}
interfacesInited = false;
}
break;
#ifdef ARDUINO_ARCH_ESP32
case ARDUINO_EVENT_WIFI_AP_START:
DEBUG_PRINTLN(F("WiFi: AP Started"));
break;
case ARDUINO_EVENT_WIFI_AP_STOP:
DEBUG_PRINTLN(F("WiFi: AP Stopped"));
break;
#if defined(WLED_USE_ETHERNET)
case ARDUINO_EVENT_ETH_START:
DEBUG_PRINTLN(F("ETH Started"));
break;
case SYSTEM_EVENT_ETH_CONNECTED:
case ARDUINO_EVENT_ETH_CONNECTED:
{
DEBUG_PRINTLN(F("ETH Connected"));
if (!apActive) {
WiFi.disconnect(true);
#ifndef WLED_DISABLE_ESPNOW
if (useESPNowSync && statusESPNow == ESP_NOW_STATE_ON) {
DEBUG_PRINTLN(F("ESP-NOW restarting on ETH connected."));
quickEspNow.stop();
WiFi.disconnect(true); // disconnect from WiFi and disengage STA mode
WiFi.mode(WIFI_MODE_AP);
quickEspNow.onDataSent(espNowSentCB); // see udp.cpp
quickEspNow.onDataRcvd(espNowReceiveCB); // see udp.cpp
quickEspNow.setWiFiBandwidth(WIFI_IF_AP, WIFI_BW_HT20); // Only needed for ESP32 in case you need coexistence with ESP8266 in the same network
bool espNowOK = quickEspNow.begin(channelESPNow, WIFI_IF_AP); // Same channel must be used for both AP and ESP-NOW
statusESPNow = espNowOK ? ESP_NOW_STATE_ON : ESP_NOW_STATE_ERROR;
} else WiFi.disconnect(true); // otherwise disable WiFi entirely
#else
WiFi.disconnect(true); // disable WiFi entirely
#endif
}
if (multiWiFi[0].staticIP != (uint32_t)0x00000000 && multiWiFi[0].staticGW != (uint32_t)0x00000000) {
ETH.config(multiWiFi[0].staticIP, multiWiFi[0].staticGW, multiWiFi[0].staticSN, dnsAddress);
Expand All @@ -196,16 +313,18 @@ void WiFiEvent(WiFiEvent_t event)
showWelcomePage = false;
break;
}
case SYSTEM_EVENT_ETH_DISCONNECTED:
case ARDUINO_EVENT_ETH_DISCONNECTED:
DEBUG_PRINTLN(F("ETH Disconnected"));
// This doesn't really affect ethernet per se,
// as it's only configured once. Rather, it
// may be necessary to reconnect the WiFi when
// ethernet disconnects, as a way to provide
// alternative access to the device.
if (interfacesInited && WiFi.scanComplete() >= 0) findWiFi(true); // reinit WiFi scan
forceReconnect = true;
break;
#endif
#endif
#endif
default:
break;
}
Expand Down
13 changes: 1 addition & 12 deletions wled00/remote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,6 @@ static bool remoteJson(int button)
void handleRemote(uint8_t *incomingData, size_t len) {
message_structure_t *incoming = reinterpret_cast<message_structure_t *>(incomingData);

if (strcmp(last_signal_src, linked_remote) != 0) {
DEBUG_PRINT(F("ESP Now Message Received from Unlinked Sender: "));
DEBUG_PRINTLN(last_signal_src);
return;
}

if (len != sizeof(message_structure_t)) {
DEBUG_PRINTF_P(PSTR("Unknown incoming ESP Now message received of length %u\n"), len);
return;
Expand All @@ -195,12 +189,7 @@ void handleRemote(uint8_t *incomingData, size_t len) {
return;
}

DEBUG_PRINT(F("Incoming ESP Now Packet ["));
DEBUG_PRINT(cur_seq);
DEBUG_PRINT(F("] from sender ["));
DEBUG_PRINT(last_signal_src);
DEBUG_PRINT(F("] button: "));
DEBUG_PRINTLN(incoming->button);
DEBUG_PRINTF_P(PSTR("Incoming ESP Now Packet [%d] button: %d\n"), cur_seq, incoming->button);

if (!remoteJson(incoming->button))
switch (incoming->button) {
Expand Down
5 changes: 4 additions & 1 deletion wled00/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
bool oldESPNow = enableESPNow;
enableESPNow = request->hasArg(F("RE"));
if (oldESPNow != enableESPNow) forceReconnect = true;
char linked_remote[13];
strlcpy(linked_remote, request->arg(F("RMAC")).c_str(), 13);
strlwr(linked_remote); //Normalize MAC format to lowercase
#define hex2int(a) (((a)>='0' && (a)<='9') ? (a)-'0' : ((a)>='A' && (a)<='F') ? (a)-'A'+10 : ((a)>='a' && (a)<='f') ? (a)-'a'+10 : 0)
for (int i=0; i<6; i++) masterESPNow[i] = (hex2int(linked_remote[i*2])<<4) | hex2int(linked_remote[i*2+1]);
DEBUG_PRINTF_P(PSTR("ESP-NOW linked remote: " MACSTR "\n"), MAC2STR(masterESPNow));
#endif

#ifdef WLED_USE_ETHERNET
Expand Down
Loading
Loading