mirror of
https://github.com/wled/WLED.git
synced 2025-04-19 20:37:23 +00:00
429 lines
15 KiB
C++
429 lines
15 KiB
C++
#include "wled.h"
|
|
#include "fcn_declare.h"
|
|
#include "wled_ethernet.h"
|
|
|
|
|
|
#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
|
|
const managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT] = {
|
|
{ 21, true }, // RMII EMAC TX EN == When high, clocks the data on TXD0 and TXD1 to transmitter
|
|
{ 19, true }, // RMII EMAC TXD0 == First bit of transmitted data
|
|
{ 22, true }, // RMII EMAC TXD1 == Second bit of transmitted data
|
|
{ 25, false }, // RMII EMAC RXD0 == First bit of received data
|
|
{ 26, false }, // RMII EMAC RXD1 == Second bit of received data
|
|
{ 27, true }, // RMII EMAC CRS_DV == Carrier Sense and RX Data Valid
|
|
};
|
|
|
|
const ethernet_settings ethernetBoards[] = {
|
|
// None
|
|
{
|
|
},
|
|
|
|
// WT32-EHT01
|
|
// Please note, from my testing only these pins work for LED outputs:
|
|
// IO2, IO4, IO12, IO14, IO15
|
|
// These pins do not appear to work from my testing:
|
|
// IO35, IO36, IO39
|
|
{
|
|
1, // eth_address,
|
|
16, // eth_power,
|
|
23, // eth_mdc,
|
|
18, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
ETH_CLOCK_GPIO0_IN // eth_clk_mode
|
|
},
|
|
|
|
// ESP32-POE
|
|
{
|
|
0, // eth_address,
|
|
12, // eth_power,
|
|
23, // eth_mdc,
|
|
18, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
|
|
},
|
|
|
|
// WESP32
|
|
{
|
|
0, // eth_address,
|
|
-1, // eth_power,
|
|
16, // eth_mdc,
|
|
17, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
ETH_CLOCK_GPIO0_IN // eth_clk_mode
|
|
},
|
|
|
|
// QuinLed-ESP32-Ethernet
|
|
{
|
|
0, // eth_address,
|
|
5, // eth_power,
|
|
23, // eth_mdc,
|
|
18, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
|
|
},
|
|
|
|
// TwilightLord-ESP32 Ethernet Shield
|
|
{
|
|
0, // eth_address,
|
|
5, // eth_power,
|
|
23, // eth_mdc,
|
|
18, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
|
|
},
|
|
|
|
// ESP3DEUXQuattro
|
|
{
|
|
1, // eth_address,
|
|
-1, // eth_power,
|
|
23, // eth_mdc,
|
|
18, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
|
|
},
|
|
|
|
// ESP32-ETHERNET-KIT-VE
|
|
{
|
|
0, // eth_address,
|
|
5, // eth_power,
|
|
23, // eth_mdc,
|
|
18, // eth_mdio,
|
|
ETH_PHY_IP101, // eth_type,
|
|
ETH_CLOCK_GPIO0_IN // eth_clk_mode
|
|
},
|
|
|
|
// QuinLed-Dig-Octa Brainboard-32-8L and LilyGO-T-ETH-POE
|
|
{
|
|
0, // eth_address,
|
|
-1, // eth_power,
|
|
23, // eth_mdc,
|
|
18, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
|
|
},
|
|
|
|
// ABC! WLED Controller V43 + Ethernet Shield & compatible
|
|
{
|
|
1, // eth_address,
|
|
5, // eth_power,
|
|
23, // eth_mdc,
|
|
33, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
|
|
},
|
|
|
|
// Serg74-ESP32 Ethernet Shield
|
|
{
|
|
1, // eth_address,
|
|
5, // eth_power,
|
|
23, // eth_mdc,
|
|
18, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
|
|
},
|
|
|
|
// ESP32-POE-WROVER
|
|
{
|
|
0, // eth_address,
|
|
12, // eth_power,
|
|
23, // eth_mdc,
|
|
18, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
ETH_CLOCK_GPIO0_OUT // eth_clk_mode
|
|
},
|
|
|
|
// LILYGO T-POE Pro
|
|
// https://github.com/Xinyuan-LilyGO/LilyGO-T-ETH-Series/blob/master/schematic/T-POE-PRO.pdf
|
|
{
|
|
0, // eth_address,
|
|
5, // eth_power,
|
|
23, // eth_mdc,
|
|
18, // eth_mdio,
|
|
ETH_PHY_LAN8720, // eth_type,
|
|
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
|
|
|
|
|
|
//by https://github.com/tzapu/WiFiManager/blob/master/WiFiManager.cpp
|
|
int getSignalQuality(int rssi)
|
|
{
|
|
int quality = 0;
|
|
|
|
if (rssi <= -100)
|
|
{
|
|
quality = 0;
|
|
}
|
|
else if (rssi >= -50)
|
|
{
|
|
quality = 100;
|
|
}
|
|
else
|
|
{
|
|
quality = 2 * (rssi + 100);
|
|
}
|
|
return quality;
|
|
}
|
|
|
|
|
|
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) {
|
|
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;
|
|
#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-E: Connected"));
|
|
if (!apActive) {
|
|
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);
|
|
} else {
|
|
ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
|
|
}
|
|
// convert the "serverDescription" into a valid DNS hostname (alphanumeric)
|
|
char hostname[64];
|
|
prepareHostname(hostname);
|
|
ETH.setHostname(hostname);
|
|
showWelcomePage = false;
|
|
break;
|
|
}
|
|
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
|
|
default:
|
|
DEBUG_PRINTF_P(PSTR("WiFi-E: Event %d\n"), (int)event);
|
|
break;
|
|
}
|
|
}
|
|
|