WLED/wled00/wled.cpp

942 lines
29 KiB
C++
Raw Normal View History

2020-04-10 12:30:08 +02:00
#define WLED_DEFINE_GLOBAL_VARS //only in one source file, wled.cpp!
#include "wled.h"
#include "wled_ethernet.h"
2020-04-10 12:30:08 +02:00
#include <Arduino.h>
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DISABLE_BROWNOUT_DET)
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#endif
/*
* Main WLED class implementation. Mostly initialization and connection logic
*/
WLED::WLED()
{
}
2020-04-10 12:30:08 +02:00
// turns all LEDs off and restarts ESP
void WLED::reset()
{
briT = 0;
#ifdef WLED_ENABLE_WEBSOCKETS
ws.closeAll(1012);
#endif
unsigned long dly = millis();
while (millis() - dly < 450) {
2020-04-10 12:30:08 +02:00
yield(); // enough time to send response to client
}
applyBri();
DEBUG_PRINTLN(F("WLED RESET"));
2020-04-10 12:30:08 +02:00
ESP.restart();
}
void WLED::loop()
{
#ifdef WLED_DEBUG
static unsigned long lastRun = 0;
unsigned long loopMillis = millis();
size_t loopDelay = loopMillis - lastRun;
if (lastRun == 0) loopDelay=0; // startup - don't have valid data from last run.
if (loopDelay > 2) DEBUG_PRINTF("Loop delayed more than %ums.\n", loopDelay);
static unsigned long maxLoopMillis = 0;
static size_t avgLoopMillis = 0;
static unsigned long maxUsermodMillis = 0;
2023-07-05 17:16:54 +02:00
static size_t avgUsermodMillis = 0;
static unsigned long maxStripMillis = 0;
2023-07-05 17:16:54 +02:00
static size_t avgStripMillis = 0;
unsigned long stripMillis;
#endif
2021-05-27 00:09:52 +02:00
handleTime();
2023-01-12 20:35:34 +01:00
#ifndef WLED_DISABLE_INFRARED
2020-04-10 12:30:08 +02:00
handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too
2023-01-12 20:35:34 +01:00
#endif
2020-04-10 12:30:08 +02:00
handleConnection();
#ifndef WLED_DISABLE_ESPNOW
handleRemote();
#endif
2020-04-10 12:30:08 +02:00
handleSerial();
handleImprovWifiScan();
2020-04-10 12:30:08 +02:00
handleNotifications();
handleTransitions();
#ifdef WLED_ENABLE_DMX
handleDMX();
#endif
userLoop();
#ifdef WLED_DEBUG
unsigned long usermodMillis = millis();
#endif
2020-05-28 02:20:02 +02:00
usermods.loop();
#ifdef WLED_DEBUG
usermodMillis = millis() - usermodMillis;
avgUsermodMillis += usermodMillis;
if (usermodMillis > maxUsermodMillis) maxUsermodMillis = usermodMillis;
#endif
2020-04-10 12:30:08 +02:00
yield();
handleIO();
2023-01-12 20:35:34 +01:00
#ifndef WLED_DISABLE_INFRARED
2020-04-10 12:30:08 +02:00
handleIR();
2023-01-12 20:35:34 +01:00
#endif
#ifndef WLED_DISABLE_ALEXA
2020-04-10 12:30:08 +02:00
handleAlexa();
#endif
2020-04-10 12:30:08 +02:00
2020-10-13 01:39:34 +02:00
if (doCloseFile) {
closeFile();
yield();
}
2020-04-10 12:30:08 +02:00
#ifdef WLED_DEBUG
stripMillis = millis();
#endif
if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) // block stuff if WARLS/Adalight is enabled
2020-04-10 12:30:08 +02:00
{
if (apActive) dnsServer.processNextRequest();
#ifndef WLED_DISABLE_OTA
2022-05-01 22:09:40 +02:00
if (WLED_CONNECTED && aOtaEnabled && !otaLock && correctPIN) ArduinoOTA.handle();
#endif
2020-04-10 12:30:08 +02:00
handleNightlight();
2020-11-11 23:48:14 +01:00
handlePlaylist();
2020-04-10 12:30:08 +02:00
yield();
#ifndef WLED_DISABLE_HUESYNC
2020-04-10 12:30:08 +02:00
handleHue();
yield();
#endif
handlePresets();
2020-04-10 12:30:08 +02:00
yield();
2022-02-04 13:28:00 +01:00
if (!offMode || strip.isOffRefreshRequired())
2020-04-10 12:30:08 +02:00
strip.service();
#ifdef ESP8266
else if (!noWifiSleep)
delay(1); //required to make sure ESP enters modem sleep (see #1184)
#endif
2020-04-10 12:30:08 +02:00
}
#ifdef WLED_DEBUG
stripMillis = millis() - stripMillis;
avgStripMillis += stripMillis;
if (stripMillis > maxStripMillis) maxStripMillis = stripMillis;
#endif
2020-04-10 12:30:08 +02:00
yield();
#ifdef ESP8266
MDNS.update();
#endif
2021-05-30 13:22:42 +02:00
//millis() rolls over every 50 days
if (lastMqttReconnectAttempt > millis()) {
rolloverMillis++;
lastMqttReconnectAttempt = 0;
ntpLastSyncTime = NTP_NEVER; // force new NTP query
2021-12-20 11:29:03 +01:00
strip.restartRuntime();
2021-05-30 13:22:42 +02:00
}
if (millis() - lastMqttReconnectAttempt > 30000 || lastMqttReconnectAttempt == 0) { // lastMqttReconnectAttempt==0 forces immediate broadcast
lastMqttReconnectAttempt = millis();
2023-01-12 20:35:34 +01:00
#ifndef WLED_DISABLE_MQTT
2020-04-10 12:30:08 +02:00
initMqtt();
2023-01-12 20:35:34 +01:00
#endif
2021-05-30 13:22:42 +02:00
yield();
// refresh WLED nodes list
2021-01-22 16:17:18 +01:00
refreshNodeList();
if (nodeBroadcastEnabled) sendSysInfoUDP();
2021-03-20 18:43:05 +01:00
yield();
}
2021-03-20 18:43:05 +01:00
2022-03-01 23:37:28 +01:00
// 15min PIN time-out
if (strlen(settingsPIN)>0 && correctPIN && millis() - lastEditTime > PIN_TIMEOUT) {
2022-03-01 23:37:28 +01:00
correctPIN = false;
createEditHandler(false);
2022-03-01 23:37:28 +01:00
}
2021-03-20 18:43:05 +01:00
//LED settings have been saved, re-init busses
2023-01-06 09:24:29 +01:00
//This code block causes severe FPS drop on ESP32 with the original "if (busConfigs[0] != nullptr)" conditional. Investigate!
if (doInitBusses) {
2021-03-23 03:10:24 +01:00
doInitBusses = false;
2021-05-30 13:22:42 +02:00
DEBUG_PRINTLN(F("Re-init busses."));
bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
2021-03-20 18:43:05 +01:00
busses.removeAll();
2023-06-27 00:38:30 +02:00
uint32_t mem = 0, globalBufMem = 0;
uint16_t maxlen = 0;
2022-12-31 17:06:18 +01:00
for (uint8_t i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
2021-03-20 18:43:05 +01:00
if (busConfigs[i] == nullptr) break;
2021-06-15 23:36:12 +02:00
mem += BusManager::memUsage(*busConfigs[i]);
2023-06-30 21:12:59 +02:00
if (useGlobalLedBuffer && busConfigs[i]->start + busConfigs[i]->count > maxlen) {
2023-06-27 00:38:30 +02:00
maxlen = busConfigs[i]->start + busConfigs[i]->count;
globalBufMem = maxlen * 4;
}
if (mem + globalBufMem <= MAX_LED_MEMORY) {
busses.add(*busConfigs[i]);
}
2021-03-20 18:43:05 +01:00
delete busConfigs[i]; busConfigs[i] = nullptr;
}
strip.finalizeInit(); // also loads default ledmap if present
if (aligned) strip.makeAutoSegments();
else strip.fixInvalidSegments();
doSerializeConfig = true;
2021-03-20 18:43:05 +01:00
}
2021-11-03 14:52:22 +01:00
if (loadLedmap >= 0) {
if (!strip.deserializeMap(loadLedmap) && strip.isMatrix && loadLedmap == 0) strip.setUpMatrix();
2021-11-03 14:52:22 +01:00
loadLedmap = -1;
}
yield();
if (doSerializeConfig) serializeConfig();
2021-06-15 23:36:12 +02:00
yield();
handleWs();
2021-10-27 17:49:35 +02:00
handleStatusLED();
2020-05-28 02:20:02 +02:00
toki.resetTick();
#if WLED_WATCHDOG_TIMEOUT > 0
// we finished our mainloop, reset the watchdog timer
static unsigned long lastWDTFeed = 0;
if (!strip.isUpdating() || millis() - lastWDTFeed > (WLED_WATCHDOG_TIMEOUT*500)) {
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#else
ESP.wdtFeed();
#endif
lastWDTFeed = millis();
}
#endif
if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration
reset();
// DEBUG serial logging (every 30s)
2020-04-10 12:30:08 +02:00
#ifdef WLED_DEBUG
loopMillis = millis() - loopMillis;
if (loopMillis > 30) {
DEBUG_PRINTF("Loop took %lums.\n", loopMillis);
DEBUG_PRINTF("Usermods took %lums.\n", usermodMillis);
DEBUG_PRINTF("Strip took %lums.\n", stripMillis);
}
avgLoopMillis += loopMillis;
if (loopMillis > maxLoopMillis) maxLoopMillis = loopMillis;
if (millis() - debugTime > 29999) {
2021-05-30 13:22:42 +02:00
DEBUG_PRINTLN(F("---DEBUG INFO---"));
DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis());
DEBUG_PRINT(F("Unix time: ")); toki.printTime(toki.getTime());
DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap());
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
2021-05-30 13:22:42 +02:00
if (psramFound()) {
DEBUG_PRINT(F("Total PSRAM: ")); DEBUG_PRINT(ESP.getPsramSize()/1024); DEBUG_PRINTLN("kB");
DEBUG_PRINT(F("Free PSRAM: ")); DEBUG_PRINT(ESP.getFreePsram()/1024); DEBUG_PRINTLN("kB");
}
2021-05-30 13:22:42 +02:00
#endif
DEBUG_PRINT(F("Wifi state: ")); DEBUG_PRINTLN(WiFi.status());
2020-05-28 02:20:02 +02:00
2020-04-10 12:30:08 +02:00
if (WiFi.status() != lastWifiState) {
wifiStateChangedTime = millis();
}
lastWifiState = WiFi.status();
DEBUG_PRINT(F("State time: ")); DEBUG_PRINTLN(wifiStateChangedTime);
DEBUG_PRINT(F("NTP last sync: ")); DEBUG_PRINTLN(ntpLastSyncTime);
DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP());
if (loops > 0) { // avoid division by zero
DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 30);
DEBUG_PRINT(F("Loop time[ms]: ")); DEBUG_PRINT(avgLoopMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxLoopMillis);
DEBUG_PRINT(F("UM time[ms]: ")); DEBUG_PRINT(avgUsermodMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxUsermodMillis);
DEBUG_PRINT(F("Strip time[ms]: ")); DEBUG_PRINT(avgStripMillis/loops); DEBUG_PRINT("/"); DEBUG_PRINTLN(maxStripMillis);
}
2022-08-03 21:36:47 +02:00
strip.printSize();
2020-04-10 12:30:08 +02:00
loops = 0;
maxLoopMillis = 0;
maxUsermodMillis = 0;
maxStripMillis = 0;
avgUsermodMillis = 0;
avgStripMillis = 0;
2020-04-10 12:30:08 +02:00
debugTime = millis();
}
loops++;
lastRun = millis();
2020-05-28 02:20:02 +02:00
#endif // WLED_DEBUG
2020-04-10 12:30:08 +02:00
}
void WLED::enableWatchdog() {
#if WLED_WATCHDOG_TIMEOUT > 0
#ifdef ARDUINO_ARCH_ESP32
esp_err_t watchdog = esp_task_wdt_init(WLED_WATCHDOG_TIMEOUT, true);
DEBUG_PRINT(F("Watchdog enabled: "));
if (watchdog == ESP_OK) {
DEBUG_PRINTLN(F("OK"));
} else {
DEBUG_PRINTLN(watchdog);
return;
}
esp_task_wdt_add(NULL);
#else
ESP.wdtEnable(WLED_WATCHDOG_TIMEOUT * 1000);
#endif
#endif
}
void WLED::disableWatchdog() {
#if WLED_WATCHDOG_TIMEOUT > 0
DEBUG_PRINTLN(F("Watchdog: disabled"));
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_delete(NULL);
#else
ESP.wdtDisable();
#endif
#endif
}
2020-04-10 12:30:08 +02:00
void WLED::setup()
{
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DISABLE_BROWNOUT_DET)
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detection
#endif
2023-03-16 13:08:34 +01:00
#ifdef ARDUINO_ARCH_ESP32
pinMode(hardwareRX, INPUT_PULLDOWN); delay(1); // suppress noise in case RX pin is floating (at low noise energy) - see issue #3128
#endif
2020-04-10 12:30:08 +02:00
Serial.begin(115200);
#if !ARDUINO_USB_CDC_ON_BOOT
Serial.setTimeout(50); // this causes troubles on new MCUs that have a "virtual" USB Serial (HWCDC)
#else
#endif
#if defined(WLED_DEBUG) && defined(ARDUINO_ARCH_ESP32) && (defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || ARDUINO_USB_CDC_ON_BOOT)
delay(2500); // allow CDC USB serial to initialise
#endif
#if !defined(WLED_DEBUG) && defined(ARDUINO_ARCH_ESP32) && !defined(WLED_DEBUG_HOST) && ARDUINO_USB_CDC_ON_BOOT
Serial.setDebugOutput(false); // switch off kernel messages when using USBCDC
#endif
2020-04-10 12:30:08 +02:00
DEBUG_PRINTLN();
2021-05-30 13:22:42 +02:00
DEBUG_PRINT(F("---WLED "));
2020-04-10 12:30:08 +02:00
DEBUG_PRINT(versionString);
DEBUG_PRINT(" ");
DEBUG_PRINT(VERSION);
2021-05-30 13:22:42 +02:00
DEBUG_PRINTLN(F(" INIT---"));
2020-04-10 12:30:08 +02:00
#ifdef ARDUINO_ARCH_ESP32
2021-05-30 13:22:42 +02:00
DEBUG_PRINT(F("esp32 "));
2020-04-10 12:30:08 +02:00
DEBUG_PRINTLN(ESP.getSdkVersion());
#if defined(ESP_ARDUINO_VERSION)
//DEBUG_PRINTF(F("arduino-esp32 0x%06x\n"), ESP_ARDUINO_VERSION);
DEBUG_PRINTF("arduino-esp32 v%d.%d.%d\n", int(ESP_ARDUINO_VERSION_MAJOR), int(ESP_ARDUINO_VERSION_MINOR), int(ESP_ARDUINO_VERSION_PATCH)); // availeable since v2.0.0
#else
DEBUG_PRINTLN(F("arduino-esp32 v1.0.x\n")); // we can't say in more detail.
#endif
DEBUG_PRINT(F("CPU: ")); DEBUG_PRINT(ESP.getChipModel());
DEBUG_PRINT(F(" rev.")); DEBUG_PRINT(ESP.getChipRevision());
DEBUG_PRINT(F(", ")); DEBUG_PRINT(ESP.getChipCores()); DEBUG_PRINT(F(" core(s)"));
DEBUG_PRINT(F(", ")); DEBUG_PRINT(ESP.getCpuFreqMHz()); DEBUG_PRINTLN(F("MHz."));
DEBUG_PRINT(F("FLASH: ")); DEBUG_PRINT((ESP.getFlashChipSize()/1024)/1024);
DEBUG_PRINT(F("MB, Mode ")); DEBUG_PRINT(ESP.getFlashChipMode());
#ifdef WLED_DEBUG
switch (ESP.getFlashChipMode()) {
// missing: Octal modes
case FM_QIO: DEBUG_PRINT(F(" (QIO)")); break;
case FM_QOUT: DEBUG_PRINT(F(" (QOUT)"));break;
case FM_DIO: DEBUG_PRINT(F(" (DIO)")); break;
case FM_DOUT: DEBUG_PRINT(F(" (DOUT)"));break;
default: break;
}
#endif
DEBUG_PRINT(F(", speed ")); DEBUG_PRINT(ESP.getFlashChipSpeed()/1000000);DEBUG_PRINTLN(F("MHz."));
2020-04-10 12:30:08 +02:00
#else
2021-05-30 13:22:42 +02:00
DEBUG_PRINT(F("esp8266 "));
2020-04-10 12:30:08 +02:00
DEBUG_PRINTLN(ESP.getCoreVersion());
#endif
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
2020-04-10 12:30:08 +02:00
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
#if defined(CONFIG_IDF_TARGET_ESP32S3)
// S3: reserve GPIO 33-37 for "octal" PSRAM
managed_pin_type pins[] = { {33, true}, {34, true}, {35, true}, {36, true}, {37, true} };
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
2023-05-30 16:09:51 +02:00
// S2: reserve GPIO 26-32 for PSRAM (may fail due to isPinOk() but that will also prevent other allocation)
managed_pin_type pins[] = { {26, true}, {27, true}, {28, true}, {29, true}, {30, true}, {31, true}, {32, true} };
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
2023-05-30 16:09:51 +02:00
// C3: reserve GPIO 12-17 for PSRAM (may fail due to isPinOk() but that will also prevent other allocation)
managed_pin_type pins[] = { {12, true}, {13, true}, {14, true}, {15, true}, {16, true}, {17, true} };
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
#else
// GPIO16/GPIO17 reserved for SPI RAM
managed_pin_type pins[] = { {16, true}, {17, true} };
pinManager.allocateMultiplePins(pins, sizeof(pins)/sizeof(managed_pin_type), PinOwner::SPI_RAM);
#endif
#if defined(WLED_USE_PSRAM)
if (psramFound()) {
DEBUG_PRINT(F("Total PSRAM: ")); DEBUG_PRINT(ESP.getPsramSize()/1024); DEBUG_PRINTLN("kB");
DEBUG_PRINT(F("Free PSRAM : ")); DEBUG_PRINT(ESP.getFreePsram()/1024); DEBUG_PRINTLN("kB");
}
#else
DEBUG_PRINTLN(F("PSRAM not used."));
2021-05-30 13:22:42 +02:00
#endif
#endif
2021-05-30 13:22:42 +02:00
2020-11-05 22:54:13 +01:00
//DEBUG_PRINT(F("LEDs inited. heap usage ~"));
//DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
2020-04-10 12:30:08 +02:00
2021-05-30 13:22:42 +02:00
#ifdef WLED_DEBUG
pinManager.allocatePin(hardwareTX, true, PinOwner::DebugOut); // TX (GPIO1 on ESP32) reserved for debug output
2021-05-30 13:22:42 +02:00
#endif
#ifdef WLED_ENABLE_DMX //reserve GPIO2 as hardcoded DMX pin
pinManager.allocatePin(2, true, PinOwner::DMX);
#endif
DEBUG_PRINTLN(F("Registering usermods ..."));
registerUsermods();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
for (uint8_t i=1; i<WLED_MAX_BUTTONS; i++) btnPin[i] = -1;
2020-11-05 22:54:13 +01:00
bool fsinit = false;
DEBUGFS_PRINTLN(F("Mount FS"));
#ifdef ARDUINO_ARCH_ESP32
fsinit = WLED_FS.begin(true);
#else
fsinit = WLED_FS.begin();
2020-04-10 12:30:08 +02:00
#endif
2020-11-05 22:54:13 +01:00
if (!fsinit) {
DEBUGFS_PRINTLN(F("FS failed!"));
errorFlag = ERR_FS_BEGIN;
2023-01-06 09:24:29 +01:00
}
#ifdef WLED_ADD_EEPROM_SUPPORT
else deEEP();
#else
initPresetsFile();
#endif
2020-11-05 22:54:13 +01:00
updateFSInfo();
2021-05-30 13:22:42 +02:00
// generate module IDs must be done before AP setup
escapedMac = WiFi.macAddress();
escapedMac.replace(":", "");
escapedMac.toLowerCase();
WLED_SET_AP_SSID(); // otherwise it is empty on first boot until config is saved
2021-05-30 13:22:42 +02:00
DEBUG_PRINTLN(F("Reading config"));
deserializeConfigFromFS();
2020-04-10 12:30:08 +02:00
2021-11-30 16:28:26 +01:00
#if defined(STATUSLED) && STATUSLED>=0
2021-10-27 17:49:35 +02:00
if (!pinManager.isPinAllocated(STATUSLED)) {
// NOTE: Special case: The status LED should *NOT* be allocated.
// See comments in handleStatusLed().
pinMode(STATUSLED, OUTPUT);
}
#endif
2021-05-30 13:22:42 +02:00
DEBUG_PRINTLN(F("Initializing strip"));
2020-04-10 12:30:08 +02:00
beginStrip();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
2021-05-30 13:22:42 +02:00
DEBUG_PRINTLN(F("Usermods setup"));
2020-04-10 12:30:08 +02:00
userSetup();
2020-05-28 02:20:02 +02:00
usermods.setup();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
2020-04-10 12:30:08 +02:00
if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0)
showWelcomePage = true;
WiFi.persistent(false);
#ifdef WLED_USE_ETHERNET
WiFi.onEvent(WiFiEvent);
#endif
2020-04-10 12:30:08 +02:00
2021-05-30 13:22:42 +02:00
#ifdef WLED_ENABLE_ADALIGHT
//Serial RX (Adalight, Improv, Serial JSON) only possible if GPIO3 unused
//Serial TX (Debug, Improv, Serial JSON) only possible if GPIO1 unused
if (!pinManager.isPinAllocated(hardwareRX) && !pinManager.isPinAllocated(hardwareTX)) {
2021-05-30 13:22:42 +02:00
Serial.println(F("Ada"));
}
#endif
2020-04-10 12:30:08 +02:00
// fill in unique mdns default
if (strcmp(cmDNS, "x") == 0) sprintf_P(cmDNS, PSTR("wled-%*s"), 6, escapedMac.c_str() + 6);
2023-01-12 20:35:34 +01:00
#ifndef WLED_DISABLE_MQTT
if (mqttDeviceTopic[0] == 0) sprintf_P(mqttDeviceTopic, PSTR("wled/%*s"), 6, escapedMac.c_str() + 6);
if (mqttClientID[0] == 0) sprintf_P(mqttClientID, PSTR("WLED-%*s"), 6, escapedMac.c_str() + 6);
2023-01-12 20:35:34 +01:00
#endif
2020-04-10 12:30:08 +02:00
#ifdef WLED_ENABLE_ADALIGHT
if (Serial.available() > 0 && Serial.peek() == 'I') handleImprovPacket();
#endif
2020-04-10 12:30:08 +02:00
#ifndef WLED_DISABLE_OTA
if (aOtaEnabled) {
ArduinoOTA.onStart([]() {
#ifdef ESP8266
wifi_set_sleep_type(NONE_SLEEP_T);
#endif
WLED::instance().disableWatchdog();
2020-09-20 01:18:31 +02:00
DEBUG_PRINTLN(F("Start ArduinoOTA"));
2020-04-10 12:30:08 +02:00
});
ArduinoOTA.onError([](ota_error_t error) {
// reenable watchdog on failed update
WLED::instance().enableWatchdog();
});
2020-04-10 12:30:08 +02:00
if (strlen(cmDNS) > 0)
ArduinoOTA.setHostname(cmDNS);
}
#endif
#ifdef WLED_ENABLE_DMX
initDMX();
#endif
#ifdef WLED_ENABLE_ADALIGHT
if (Serial.available() > 0 && Serial.peek() == 'I') handleImprovPacket();
#endif
2020-04-10 12:30:08 +02:00
// HTTP server page init
DEBUG_PRINTLN(F("initServer"));
2020-04-10 12:30:08 +02:00
initServer();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
2021-05-30 13:22:42 +02:00
enableWatchdog();
2021-05-30 13:22:42 +02:00
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DISABLE_BROWNOUT_DET)
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); //enable brownout detector
#endif
2020-04-10 12:30:08 +02:00
}
void WLED::beginStrip()
{
// Initialize NeoPixel Strip and button
2021-06-15 23:36:12 +02:00
strip.finalizeInit(); // busses created during deserializeConfig()
strip.makeAutoSegments();
2020-11-05 22:54:13 +01:00
strip.setBrightness(0);
2020-04-10 12:30:08 +02:00
strip.setShowCallback(handleOverlayDraw);
2021-06-15 23:36:12 +02:00
if (turnOnAtBoot) {
if (briS > 0) bri = briS;
else if (bri == 0) bri = 128;
} else {
2023-05-30 16:09:51 +02:00
// fix for #3196
briLast = briS; bri = 0;
strip.fill(BLACK);
strip.show();
}
2021-06-15 23:36:12 +02:00
if (bootPreset > 0) {
2021-07-10 17:01:20 +02:00
applyPreset(bootPreset, CALL_MODE_INIT);
2021-06-15 23:36:12 +02:00
}
colorUpdated(CALL_MODE_INIT);
2020-04-10 12:30:08 +02:00
2021-01-17 00:20:31 +01:00
// init relay pin
if (rlyPin>=0)
digitalWrite(rlyPin, (rlyMde ? bri : !bri));
2020-04-10 12:30:08 +02:00
}
void WLED::initAP(bool resetAP)
{
if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP)
return;
if (resetAP) {
WLED_SET_AP_SSID();
strcpy_P(apPass, PSTR(WLED_AP_PASS));
}
2020-09-20 01:18:31 +02:00
DEBUG_PRINT(F("Opening access point "));
2020-04-10 12:30:08 +02:00
DEBUG_PRINTLN(apSSID);
WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0));
WiFi.softAP(apSSID, apPass, apChannel, apHide);
2023-07-20 22:09:48 +02:00
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
WiFi.setTxPower(WIFI_POWER_8_5dBm);
#endif
2020-04-10 12:30:08 +02:00
if (!apActive) // start captive portal if AP active
2020-04-10 12:30:08 +02:00
{
2020-09-20 01:18:31 +02:00
DEBUG_PRINTLN(F("Init AP interfaces"));
2020-04-10 12:30:08 +02:00
server.begin();
if (udpPort > 0 && udpPort != ntpLocalPort) {
udpConnected = notifierUdp.begin(udpPort);
}
if (udpRgbPort > 0 && udpRgbPort != ntpLocalPort && udpRgbPort != udpPort) {
udpRgbConnected = rgbUdp.begin(udpRgbPort);
}
if (udpPort2 > 0 && udpPort2 != ntpLocalPort && udpPort2 != udpPort && udpPort2 != udpRgbPort) {
udp2Connected = notifier2Udp.begin(udpPort2);
}
e131.begin(false, e131Port, e131Universe, E131_MAX_UNIVERSE_COUNT);
2021-10-01 21:56:54 +02:00
ddp.begin(false, DDP_DEFAULT_PORT);
2021-01-17 00:20:31 +01:00
2020-04-10 12:30:08 +02:00
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(53, "*", WiFi.softAPIP());
}
apActive = true;
}
bool WLED::initEthernet()
2020-04-10 12:30:08 +02:00
{
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
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_PRINT(F("initE: Ignoring attempt for invalid ethernetType ")); DEBUG_PRINTLN(ethernetType);
return false;
}
DEBUG_PRINT(F("initE: Attempting ETH config: ")); DEBUG_PRINTLN(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_PRINT(F("initE: Failing due to invalid eth_clk_mode ("));
DEBUG_PRINT(es.eth_clk_mode);
2022-12-26 10:20:45 +01:00
DEBUG_PRINTLN(")");
return false;
}
if (!pinManager.allocateMultiplePins(pinsToAllocate, 10, PinOwner::Ethernet)) {
DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins"));
return false;
}
/*
2023-04-28 17:15:31 +02:00
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(
2023-01-06 09:24:29 +01:00
(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;
#else
return false; // Ethernet not enabled for build
#endif
}
void WLED::initConnection()
{
#ifdef WLED_ENABLE_WEBSOCKETS
ws.onEvent(wsEvent);
#endif
WiFi.disconnect(true); // close old connections
2020-04-10 12:30:08 +02:00
#ifdef ESP8266
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
#endif
if (staticIP[0] != 0 && staticGateway[0] != 0) {
WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(1, 1, 1, 1));
2020-04-10 12:30:08 +02:00
} else {
WiFi.config(IPAddress((uint32_t)0), IPAddress((uint32_t)0), IPAddress((uint32_t)0));
2020-04-10 12:30:08 +02:00
}
lastReconnectAttempt = millis();
if (!WLED_WIFI_CONFIGURED) {
DEBUG_PRINTLN(F("No connection configured."));
if (!apActive) initAP(); // instantly go to ap mode
2020-04-10 12:30:08 +02:00
return;
} else if (!apActive) {
if (apBehavior == AP_BEHAVIOR_ALWAYS) {
DEBUG_PRINTLN(F("Access point ALWAYS enabled."));
2020-04-10 12:30:08 +02:00
initAP();
} else {
DEBUG_PRINTLN(F("Access point disabled (init)."));
2020-04-10 12:30:08 +02:00
WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_STA);
2020-04-10 12:30:08 +02:00
}
}
showWelcomePage = false;
2020-09-20 01:18:31 +02:00
DEBUG_PRINT(F("Connecting to "));
2020-04-10 12:30:08 +02:00
DEBUG_PRINT(clientSSID);
DEBUG_PRINTLN("...");
// convert the "serverDescription" into a valid DNS hostname (alphanumeric)
char hostname[25];
prepareHostname(hostname);
2021-01-17 00:20:31 +01:00
2020-04-10 12:30:08 +02:00
#ifdef ESP8266
WiFi.hostname(hostname);
2020-04-10 12:30:08 +02:00
#endif
WiFi.begin(clientSSID, clientPass);
#ifdef ARDUINO_ARCH_ESP32
2023-07-20 22:09:48 +02:00
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
WiFi.setTxPower(WIFI_POWER_8_5dBm);
#endif
2020-04-10 12:30:08 +02:00
WiFi.setSleep(!noWifiSleep);
WiFi.setHostname(hostname);
2020-04-10 12:30:08 +02:00
#else
wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T);
#endif
}
void WLED::initInterfaces()
{
2020-09-20 01:18:31 +02:00
DEBUG_PRINTLN(F("Init STA interfaces"));
2020-04-10 12:30:08 +02:00
2021-05-30 13:22:42 +02:00
#ifndef WLED_DISABLE_HUESYNC
IPAddress ipAddress = Network.localIP();
2020-04-10 12:30:08 +02:00
if (hueIP[0] == 0) {
hueIP[0] = ipAddress[0];
hueIP[1] = ipAddress[1];
hueIP[2] = ipAddress[2];
2020-04-10 12:30:08 +02:00
}
2021-05-30 13:22:42 +02:00
#endif
2020-04-10 12:30:08 +02:00
2023-01-12 20:35:34 +01:00
#ifndef WLED_DISABLE_ALEXA
2020-04-10 12:30:08 +02:00
// init Alexa hue emulation
if (alexaEnabled)
alexaInit();
2023-01-12 20:35:34 +01:00
#endif
2020-04-10 12:30:08 +02:00
#ifndef WLED_DISABLE_OTA
if (aOtaEnabled)
ArduinoOTA.begin();
#endif
// Set up mDNS responder:
if (strlen(cmDNS) > 0) {
// "end" must be called before "begin" is called a 2nd time
// see https://github.com/esp8266/Arduino/issues/7213
MDNS.end();
MDNS.begin(cmDNS);
2020-04-10 12:30:08 +02:00
2020-09-20 01:18:31 +02:00
DEBUG_PRINTLN(F("mDNS started"));
2020-04-10 12:30:08 +02:00
MDNS.addService("http", "tcp", 80);
MDNS.addService("wled", "tcp", 80);
MDNS.addServiceTxt("wled", "tcp", "mac", escapedMac.c_str());
}
server.begin();
if (udpPort > 0 && udpPort != ntpLocalPort) {
udpConnected = notifierUdp.begin(udpPort);
if (udpConnected && udpRgbPort != udpPort)
udpRgbConnected = rgbUdp.begin(udpRgbPort);
if (udpConnected && udpPort2 != udpPort && udpPort2 != udpRgbPort)
udp2Connected = notifier2Udp.begin(udpPort2);
2020-04-10 12:30:08 +02:00
}
if (ntpEnabled)
ntpConnected = ntpUdp.begin(ntpLocalPort);
2020-04-13 00:42:27 +02:00
e131.begin(e131Multicast, e131Port, e131Universe, E131_MAX_UNIVERSE_COUNT);
2021-10-01 21:56:54 +02:00
ddp.begin(false, DDP_DEFAULT_PORT);
2020-04-10 12:30:08 +02:00
reconnectHue();
2023-01-12 20:35:34 +01:00
#ifndef WLED_DISABLE_MQTT
2020-04-10 12:30:08 +02:00
initMqtt();
2023-01-12 20:35:34 +01:00
#endif
2020-04-10 12:30:08 +02:00
interfacesInited = true;
wasConnected = true;
}
void WLED::handleConnection()
{
static byte stacO = 0;
static uint32_t lastHeap = UINT32_MAX;
static unsigned long heapTime = 0;
unsigned long now = millis();
if (now < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS))
2020-04-10 12:30:08 +02:00
return;
if (lastReconnectAttempt == 0) {
DEBUG_PRINTLN(F("lastReconnectAttempt == 0"));
2020-04-10 12:30:08 +02:00
initConnection();
return;
}
2020-04-10 12:30:08 +02:00
// reconnect WiFi to clear stale allocations if heap gets too low
if (now - heapTime > 5000) {
2020-04-10 12:30:08 +02:00
uint32_t heap = ESP.getFreeHeap();
2021-12-10 12:51:44 +01:00
if (heap < MIN_HEAP_SIZE && lastHeap < MIN_HEAP_SIZE) {
2020-09-20 01:18:31 +02:00
DEBUG_PRINT(F("Heap too low! "));
2020-04-10 12:30:08 +02:00
DEBUG_PRINTLN(heap);
forceReconnect = true;
2022-08-03 21:36:47 +02:00
strip.purgeSegments(true); // remove all but one segments from memory
} else if (heap < MIN_HEAP_SIZE) {
strip.purgeSegments();
2020-04-10 12:30:08 +02:00
}
lastHeap = heap;
heapTime = now;
2020-04-10 12:30:08 +02:00
}
byte stac = 0;
if (apActive) {
#ifdef ESP8266
stac = wifi_softap_get_station_num();
#else
wifi_sta_list_t stationList;
esp_wifi_ap_get_sta_list(&stationList);
stac = stationList.num;
#endif
if (stac != stacO) {
stacO = stac;
2020-09-20 01:18:31 +02:00
DEBUG_PRINT(F("Connected AP clients: "));
2020-04-10 12:30:08 +02:00
DEBUG_PRINTLN(stac);
if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) { // trying to connect, but not connected
if (stac)
WiFi.disconnect(); // disable search so that AP can work
else
initConnection(); // restart search
2020-04-10 12:30:08 +02:00
}
}
}
if (forceReconnect) {
2020-09-20 01:18:31 +02:00
DEBUG_PRINTLN(F("Forcing reconnect."));
2020-04-10 12:30:08 +02:00
initConnection();
interfacesInited = false;
forceReconnect = false;
wasConnected = false;
return;
}
if (!Network.isConnected()) {
2020-04-10 12:30:08 +02:00
if (interfacesInited) {
2020-09-20 01:18:31 +02:00
DEBUG_PRINTLN(F("Disconnected!"));
2020-04-10 12:30:08 +02:00
interfacesInited = false;
initConnection();
}
//send improv failed 6 seconds after second init attempt (24 sec. after provisioning)
if (improvActive > 2 && now - lastReconnectAttempt > 6000) {
sendImprovStateResponse(0x03, true);
improvActive = 2;
}
if (now - lastReconnectAttempt > ((stac) ? 300000 : 18000) && WLED_WIFI_CONFIGURED) {
if (improvActive == 2) improvActive = 3;
DEBUG_PRINTLN(F("Last reconnect too old."));
2020-04-10 12:30:08 +02:00
initConnection();
}
if (!apActive && now - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) {
DEBUG_PRINTLN(F("Not connected AP."));
2020-04-10 12:30:08 +02:00
initAP();
}
} else if (!interfacesInited) { //newly connected
2020-04-10 12:30:08 +02:00
DEBUG_PRINTLN("");
2020-09-20 01:18:31 +02:00
DEBUG_PRINT(F("Connected! IP address: "));
DEBUG_PRINTLN(Network.localIP());
if (improvActive) {
if (improvError == 3) sendImprovStateResponse(0x00, true);
sendImprovStateResponse(0x04);
if (improvActive > 1) sendImprovIPRPCResult(ImprovRPCType::Command_Wifi);
}
2020-04-10 12:30:08 +02:00
initInterfaces();
userConnected();
2020-05-28 02:20:02 +02:00
usermods.connected();
lastMqttReconnectAttempt = 0; // force immediate update
2020-04-10 12:30:08 +02:00
// shut down AP
if (apBehavior != AP_BEHAVIOR_ALWAYS && apActive) {
dnsServer.stop();
WiFi.softAPdisconnect(true);
apActive = false;
DEBUG_PRINTLN(F("Access point disabled (handle)."));
2020-04-10 12:30:08 +02:00
}
}
}
// If status LED pin is allocated for other uses, does nothing
// else blink at 1Hz when WLED_CONNECTED is false (no WiFi, ?? no Ethernet ??)
// else blink at 2Hz when MQTT is enabled but not connected
// else turn the status LED off
void WLED::handleStatusLED()
{
2021-11-30 16:28:26 +01:00
#if defined(STATUSLED)
uint32_t c = 0;
2021-11-30 16:28:26 +01:00
#if STATUSLED>=0
if (pinManager.isPinAllocated(STATUSLED)) {
return; //lower priority if something else uses the same pin
}
2021-11-30 16:28:26 +01:00
#endif
2021-01-17 00:20:31 +01:00
2021-11-30 16:28:26 +01:00
if (WLED_CONNECTED) {
c = RGBW32(0,255,0,0);
ledStatusType = 2;
} else if (WLED_MQTT_CONNECTED) {
c = RGBW32(0,128,0,0);
ledStatusType = 4;
} else if (apActive) {
c = RGBW32(0,0,255,0);
ledStatusType = 1;
}
if (ledStatusType) {
if (millis() - ledStatusLastMillis >= (1000/ledStatusType)) {
ledStatusLastMillis = millis();
2021-11-30 16:28:26 +01:00
ledStatusState = !ledStatusState;
#if STATUSLED>=0
digitalWrite(STATUSLED, ledStatusState);
2021-11-30 16:28:26 +01:00
#else
busses.setStatusPixel(ledStatusState ? c : 0);
#endif
}
} else {
2021-11-30 16:28:26 +01:00
#if STATUSLED>=0
#ifdef STATUSLEDINVERTED
digitalWrite(STATUSLED, HIGH);
2021-11-30 16:28:26 +01:00
#else
digitalWrite(STATUSLED, LOW);
2021-11-30 16:28:26 +01:00
#endif
#else
busses.setStatusPixel(0);
#endif
}
#endif
2021-10-11 10:56:25 +02:00
}