Skip to content

Commit

Permalink
Add ability to enter desired BSSID
Browse files Browse the repository at this point in the history
- add event handling (debug)
- fixes Aircoookie#2151
  • Loading branch information
blazoncek committed Dec 25, 2024
1 parent 8873832 commit 272129f
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 168 deletions.
16 changes: 11 additions & 5 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {

//long vid = doc[F("vid")]; // 2010020

#ifdef WLED_USE_ETHERNET
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
JsonObject ethernet = doc[F("eth")];
CJSON(ethernetType, ethernet["type"]);
// NOTE: Ethernet configuration takes priority over other use of pins
WLED::instance().initEthernet();
initEthernet();
#endif

JsonObject id = doc["id"];
Expand Down Expand Up @@ -53,16 +53,19 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonArray sn = wifi["sn"];
char ssid[33] = "";
char pass[65] = "";
char bssid[13] = "";
IPAddress nIP = (uint32_t)0U, nGW = (uint32_t)0U, nSN = (uint32_t)0x00FFFFFF; // little endian
getStringFromJson(ssid, wifi[F("ssid")], 33);
getStringFromJson(pass, wifi["psk"], 65); // password is not normally present but if it is, use it
getStringFromJson(bssid, wifi[F("bssid")], 13);
for (size_t i = 0; i < 4; i++) {
CJSON(nIP[i], ip[i]);
CJSON(nGW[i], gw[i]);
CJSON(nSN[i], sn[i]);
}
if (strlen(ssid) > 0) strlcpy(multiWiFi[n].clientSSID, ssid, 33); // this will keep old SSID intact if not present in JSON
if (strlen(pass) > 0) strlcpy(multiWiFi[n].clientPass, pass, 65); // this will keep old password intact if not present in JSON
if (strlen(bssid) > 0) fillStr2MAC(multiWiFi[n].bssid, bssid);
multiWiFi[n].staticIP = nIP;
multiWiFi[n].staticGW = nGW;
multiWiFi[n].staticSN = nSN;
Expand Down Expand Up @@ -702,8 +705,8 @@ void deserializeConfigFromFS() {
UsermodManager::readFromConfig(empty);
serializeConfig();
// init Ethernet (in case default type is set at compile time)
#ifdef WLED_USE_ETHERNET
WLED::instance().initEthernet();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
initEthernet();
#endif
return;
}
Expand Down Expand Up @@ -751,6 +754,9 @@ void serializeConfig() {
JsonObject wifi = nw_ins.createNestedObject();
wifi[F("ssid")] = multiWiFi[n].clientSSID;
wifi[F("pskl")] = strlen(multiWiFi[n].clientPass);
char bssid[13];
fillMAC2Str(bssid, multiWiFi[n].bssid);
wifi[F("bssid")] = bssid;
JsonArray wifi_ip = wifi.createNestedArray("ip");
JsonArray wifi_gw = wifi.createNestedArray("gw");
JsonArray wifi_sn = wifi.createNestedArray("sn");
Expand Down Expand Up @@ -786,7 +792,7 @@ void serializeConfig() {
wifi[F("txpwr")] = txPower;
#endif

#ifdef WLED_USE_ETHERNET
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
JsonObject ethernet = root.createNestedObject("eth");
ethernet["type"] = ethernetType;
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
Expand Down
3 changes: 2 additions & 1 deletion wled00/data/settings_wifi.htm
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,13 @@
gId("wifi_add").style.display = (i<maxNetworks) ? "inline":"none";
gId("wifi_rem").style.display = (i>1) ? "inline":"none";
}
function addWiFi(ssid="",pass="",ip=0,gw=0,sn=0x00ffffff) { // little endian
function addWiFi(ssid="",pass="",bssid="",ip=0,gw=0,sn=0x00ffffff) { // little endian
var i = gId("wifi_entries").childNodes.length;
if (i >= maxNetworks) return;
var b = `<div id="net${i}"><hr class="sml">
Network name (SSID${i==0?", empty to not connect":""}):<br><input type="text" id="CS${i}" name="CS${i}" maxlength="32" value="${ssid}" ${i>0?"required":""}><br>
Network password:<br><input type="password" name="PW${i}" maxlength="64" value="${pass}"><br>
BSSID (optional):<br><input type="text" id="BS${i}" name="BS${i}" maxlength="12" value="${bssid}"><br>
Static IP (leave at 0.0.0.0 for DHCP)${i==0?"<br>Also used by Ethernet":""}:<br>
<input name="IP${i}0" type="number" class="s" min="0" max="255" value="${ip&0xFF}" required>.<input name="IP${i}1" type="number" class="s" min="0" max="255" value="${(ip>>8)&0xFF}" required>.<input name="IP${i}2" type="number" class="s" min="0" max="255" value="${(ip>>16)&0xFF}" required>.<input name="IP${i}3" type="number" class="s" min="0" max="255" value="${(ip>>24)&0xFF}" required><br>
Static gateway:<br>
Expand Down
10 changes: 9 additions & 1 deletion wled00/fcn_declare.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
typedef struct WiFiConfig {
char clientSSID[33];
char clientPass[65];
uint8_t bssid[6];
IPAddress staticIP;
IPAddress staticGW;
IPAddress staticSN;
Expand All @@ -62,6 +63,7 @@ typedef struct WiFiConfig {
{
strncpy(clientSSID, ssid, 32); clientSSID[32] = 0;
strncpy(clientPass, pass, 64); clientPass[64] = 0;
memset(bssid, 0, sizeof(bssid));
}
} wifi_config;

Expand Down Expand Up @@ -340,7 +342,12 @@ void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rs
#endif

//network.cpp
int getSignalQuality(int rssi);
bool initEthernet(); // result is informational
int getSignalQuality(int rssi);
void fillMAC2Str(char *str, const uint8_t *mac);
void fillStr2MAC(uint8_t *mac, const char *str);
int findWiFi(bool doScan = false);
bool isWiFiConfigured();
void WiFiEvent(WiFiEvent_t event);

//um_manager.cpp
Expand Down Expand Up @@ -464,6 +471,7 @@ void userLoop();
#include "soc/wdev_reg.h"
#define HW_RND_REGISTER REG_READ(WDEV_RND_REG)
#endif
#define hex2int(a) (((a)>='0' && (a)<='9') ? (a)-'0' : ((a)>='A' && (a)<='F') ? (a)-'A'+10 : ((a)>='a' && (a)<='f') ? (a)-'a'+10 : 0)
int getNumVal(const String* req, uint16_t pos);
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
bool getVal(JsonVariant elem, byte* val, byte minv=0, byte maxv=255); // getVal supports inc/decrementing and random ("X~Y(r|~[w][-][Z])" form)
Expand Down
236 changes: 225 additions & 11 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 @@ -146,6 +146,101 @@ const ethernet_settings ethernetBoards[] = {
ETH_CLOCK_GPIO0_OUT // eth_clk_mode
}
};

bool initEthernet()
{
static bool successfullyConfiguredEthernet = false;

if (successfullyConfiguredEthernet) {
// DEBUG_PRINTLN(F("initE: ETH already successfully configured, ignoring"));
return false;
}
if (ethernetType == WLED_ETH_NONE) {
return false;
}
if (ethernetType >= WLED_NUM_ETH_TYPES) {
DEBUG_PRINTF_P(PSTR("initE: Ignoring attempt for invalid ethernetType (%d)\n"), ethernetType);
return false;
}

DEBUG_PRINTF_P(PSTR("initE: Attempting ETH config: %d\n"), ethernetType);

// Ethernet initialization should only succeed once -- else reboot required
ethernet_settings es = ethernetBoards[ethernetType];
managed_pin_type pinsToAllocate[10] = {
// first six pins are non-configurable
esp32_nonconfigurable_ethernet_pins[0],
esp32_nonconfigurable_ethernet_pins[1],
esp32_nonconfigurable_ethernet_pins[2],
esp32_nonconfigurable_ethernet_pins[3],
esp32_nonconfigurable_ethernet_pins[4],
esp32_nonconfigurable_ethernet_pins[5],
{ (int8_t)es.eth_mdc, true }, // [6] = MDC is output and mandatory
{ (int8_t)es.eth_mdio, true }, // [7] = MDIO is bidirectional and mandatory
{ (int8_t)es.eth_power, true }, // [8] = optional pin, not all boards use
{ ((int8_t)0xFE), false }, // [9] = replaced with eth_clk_mode, mandatory
};
// update the clock pin....
if (es.eth_clk_mode == ETH_CLOCK_GPIO0_IN) {
pinsToAllocate[9].pin = 0;
pinsToAllocate[9].isOutput = false;
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO0_OUT) {
pinsToAllocate[9].pin = 0;
pinsToAllocate[9].isOutput = true;
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO16_OUT) {
pinsToAllocate[9].pin = 16;
pinsToAllocate[9].isOutput = true;
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO17_OUT) {
pinsToAllocate[9].pin = 17;
pinsToAllocate[9].isOutput = true;
} else {
DEBUG_PRINTF_P(PSTR("initE: Failing due to invalid eth_clk_mode (%d)\n"), es.eth_clk_mode);
return false;
}

if (!PinManager::allocateMultiplePins(pinsToAllocate, 10, PinOwner::Ethernet)) {
DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins"));
return false;
}

/*
For LAN8720 the most correct way is to perform clean reset each time before init
applying LOW to power or nRST pin for at least 100 us (please refer to datasheet, page 59)
ESP_IDF > V4 implements it (150 us, lan87xx_reset_hw(esp_eth_phy_t *phy) function in
/components/esp_eth/src/esp_eth_phy_lan87xx.c, line 280)
but ESP_IDF < V4 does not. Lets do it:
[not always needed, might be relevant in some EMI situations at startup and for hot resets]
*/
#if ESP_IDF_VERSION_MAJOR==3
if(es.eth_power>0 && es.eth_type==ETH_PHY_LAN8720) {
pinMode(es.eth_power, OUTPUT);
digitalWrite(es.eth_power, 0);
delayMicroseconds(150);
digitalWrite(es.eth_power, 1);
delayMicroseconds(10);
}
#endif

if (!ETH.begin(
(uint8_t) es.eth_address,
(int) es.eth_power,
(int) es.eth_mdc,
(int) es.eth_mdio,
(eth_phy_type_t) es.eth_type,
(eth_clock_mode_t) es.eth_clk_mode
)) {
DEBUG_PRINTLN(F("initC: ETH.begin() failed"));
// de-allocate the allocated pins
for (managed_pin_type mpt : pinsToAllocate) {
PinManager::deallocatePin(mpt.pin, PinOwner::Ethernet);
}
return false;
}

successfullyConfiguredEthernet = true;
DEBUG_PRINTLN(F("initC: *** Ethernet successfully configured! ***"));
return true;
}
#endif


Expand All @@ -170,19 +265,136 @@ int getSignalQuality(int rssi)
}


void fillMAC2Str(char *str, const uint8_t *mac) {
sprintf_P(str, PSTR("%02x%02x%02x%02x%02x%02x"), MAC2STR(mac));
byte nul = 0;
for (int i = 0; i < 6; i++) nul |= *mac++; // do we have 0
if (!nul) str[0] = '\0'; // empty string
}

void fillStr2MAC(uint8_t *mac, const char *str) {
for (int i = 0; i < 6; i++) *mac++ = 0; // clear
if (!str) return; // null string
uint64_t MAC = strtoull(str, nullptr, 16);
for (int i = 0; i < 6; i++) { *--mac = MAC & 0xFF; MAC >>= 8; }
}


// 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)
int findWiFi(bool doScan) {
if (multiWiFi.size() <= 1) {
DEBUG_PRINTF_P(PSTR("WiFi: Defaulf SSID (%s) used.\n"), multiWiFi[0].clientSSID);
return 0;
}

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

if (doScan || status == WIFI_SCAN_FAILED) {
DEBUG_PRINTF_P(PSTR("WiFi: Scan started. @ %lus\n"), millis()/1000);
WiFi.scanNetworks(true); // start scanning in asynchronous mode (will delete old scan)
} else if (status >= 0) { // status contains number of found networks (including duplicate SSIDs with different BSSID)
DEBUG_PRINTF_P(PSTR("WiFi: Found %d SSIDs. @ %lus\n"), status, millis()/1000);
int rssi = -9999;
int selected = selectedWiFi;
for (int o = 0; o < status; o++) {
DEBUG_PRINTF_P(PSTR(" SSID: %s (BSSID: %s) RSSI: %ddB\n"), WiFi.SSID(o).c_str(), WiFi.BSSIDstr(o).c_str(), WiFi.RSSI(o));
for (unsigned n = 0; n < multiWiFi.size(); n++)
if (!strcmp(WiFi.SSID(o).c_str(), multiWiFi[n].clientSSID)) {
bool foundBSSID = memcmp(multiWiFi[n].bssid, WiFi.BSSID(o), 6) == 0;
// find the WiFi with the strongest signal (but keep priority of entry if signal difference is not big)
if (foundBSSID || (n < selected && WiFi.RSSI(o) > rssi-10) || WiFi.RSSI(o) > rssi) {
rssi = foundBSSID ? 0 : WiFi.RSSI(o); // RSSI is only ever negative
selected = n;
}
break;
}
}
DEBUG_PRINTF_P(PSTR("WiFi: Selected SSID: %s RSSI: %ddB\n"), multiWiFi[selected].clientSSID, rssi);
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_WIFI_SCAN_DONE SYSTEM_EVENT_SCAN_DONE
#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:
DEBUG_PRINTLN(F("ETH Started"));
case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
// AP client disconnected
if (--apClients == 0 && isWiFiConfigured()) forceReconnect = true; // no clients reconnect WiFi if awailable
DEBUG_PRINTF_P(PSTR("WiFi-E: AP Client Disconnected (%d) @ %lus.\n"), (int)apClients, millis()/1000);
break;
case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
// AP client connected
apClients++;
DEBUG_PRINTF_P(PSTR("WiFi-E: AP Client Connected (%d) @ %lus.\n"), (int)apClients, millis()/1000);
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
DEBUG_PRINT(F("WiFi-E: IP address: ")); DEBUG_PRINTLN(Network.localIP());
break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
// followed by IDLE and SCAN_DONE
DEBUG_PRINTF_P(PSTR("WiFi-E: Connected! @ %lus\n"), millis()/1000);
wasConnected = true;
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
if (wasConnected && interfacesInited) {
DEBUG_PRINTF_P(PSTR("WiFi-E: Disconnected! @ %lus\n"), millis()/1000);
if (interfacesInited && multiWiFi.size() > 1 && WiFi.scanComplete() >= 0) {
findWiFi(true); // reinit WiFi scan
forceReconnect = true;
}
interfacesInited = false;
}
break;
case SYSTEM_EVENT_ETH_CONNECTED:
#ifdef ARDUINO_ARCH_ESP32
case ARDUINO_EVENT_WIFI_SCAN_DONE:
// also triggered when connected to selected SSID
DEBUG_PRINTLN(F("WiFi-E: SSID scan completed."));
break;
case ARDUINO_EVENT_WIFI_AP_START:
DEBUG_PRINTLN(F("WiFi-E: AP Started"));
break;
case ARDUINO_EVENT_WIFI_AP_STOP:
DEBUG_PRINTLN(F("WiFi-E: AP Stopped"));
break;
#if defined(WLED_USE_ETHERNET)
case ARDUINO_EVENT_ETH_START:
DEBUG_PRINTLN(F("ETH-E: Started"));
break;
case ARDUINO_EVENT_ETH_CONNECTED:
{
DEBUG_PRINTLN(F("ETH Connected"));
DEBUG_PRINTLN(F("ETH-E: Connected"));
if (!apActive) {
WiFi.disconnect(true);
WiFi.disconnect(true); // disable WiFi entirely
}
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,18 +408,20 @@ void WiFiEvent(WiFiEvent_t event)
showWelcomePage = false;
break;
}
case SYSTEM_EVENT_ETH_DISCONNECTED:
DEBUG_PRINTLN(F("ETH Disconnected"));
case ARDUINO_EVENT_ETH_DISCONNECTED:
DEBUG_PRINTLN(F("ETH-E: 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:
DEBUG_PRINTF_P(PSTR("Network event: %d\n"), (int)event);
DEBUG_PRINTF_P(PSTR("WiFi-E: Event %d\n"), (int)event);
break;
}
}
Expand Down
Loading

0 comments on commit 272129f

Please sign in to comment.