diff --git a/wled00/FX.h b/wled00/FX.h index 9f1fda411..eec42990e 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -415,7 +415,8 @@ class WS2812FX { resetSegments(), setPixelColor(uint16_t n, uint32_t c), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), - show(void); + show(void), + setRgbwPwm(void); bool reverseMode = false, @@ -627,6 +628,12 @@ class WS2812FX { uint32_t _lastPaletteChange = 0; uint32_t _lastShow = 0; + #ifdef WLED_USE_ANALOG_LEDS + uint32_t _analogLastShow = 0; + uint32_t _analogLastColor = 0; + uint8_t _analogLastBri = 0; + #endif + uint8_t _segment_index = 0; uint8_t _segment_index_palette_last = 99; segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 73ddbf794..0707392d2 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -829,6 +829,40 @@ bool WS2812FX::segmentsAreIdentical(Segment* a, Segment* b) return true; } +#ifdef WLED_USE_ANALOG_LEDS +void WS2812FX::setRgbwPwm(void) { + uint32_t nowUp = millis(); // Be aware, millis() rolls over every 49 days + if (nowUp - _analogLastShow < MIN_SHOW_DELAY) return; + + _analogLastShow = nowUp; + + RgbwColor color = bus->GetPixelColorRgbw(0); + byte b = getBrightness(); + if (color == _analogLastColor && b == _analogLastBri) return; + + // check color values for Warm / Cold white mix (for RGBW) // EsplanexaDevice.cpp + #ifdef WLED_USE_5CH_LEDS + if (color.R == 255 && color.G == 255 && color.B == 255 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, 0, color.W * b / 255); + } else if (color.R == 127 && color.G == 127 && color.B == 127 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 512, color.W * b / 255); + } else if (color.R == 0 && color.G == 0 && color.B == 0 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); + } else if (color.R == 130 && color.G == 90 && color.B == 0 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 255, color.W * b / 512); + } else if (color.R == 255 && color.G == 153 && color.B == 0 && color.W == 255) { + bus->SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); + } else { // not only white colors + bus->SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); + } + #else + bus->SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); + #endif +} +#else +void WS2812FX::setRgbwPwm() {} +#endif + //gamma 2.4 lookup table used for color correction const byte gammaT[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index 54d21ac18..a9fbd7c2e 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -3,7 +3,9 @@ #define NpbWrapper_h //PIN CONFIGURATION +#ifndef LEDPIN #define LEDPIN 2 //strip pin. Any for ESP32, gpio2 or 3 is recommended for ESP8266 (gpio2/3 are labeled D4/RX on NodeMCU and Wemos) +#endif //#define USE_APA102 // Uncomment for using APA102 LEDs. //#define USE_WS2801 // Uncomment for using WS2801 LEDs (make sure you have NeoPixelBus v2.5.6 or newer) //#define USE_LPD8806 // Uncomment for using LPD8806 @@ -164,7 +166,7 @@ public: #endif } #else // ESP8266 - //init PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller + //init PWM pins pinMode(RPIN, OUTPUT); pinMode(GPIN, OUTPUT); pinMode(BPIN, OUTPUT); @@ -185,9 +187,9 @@ public: void SetRgbwPwm(uint8_t r, uint8_t g, uint8_t b, uint8_t w, uint8_t w2=0) { #ifdef ARDUINO_ARCH_ESP32 - ledcWrite(0, r); //RPIN - ledcWrite(1, g); //GPIN - ledcWrite(2, b); //BPIN + ledcWrite(0, r); + ledcWrite(1, g); + ledcWrite(2, b); switch (_type) { case NeoPixelType_Grb: break; #ifdef WLED_USE_5CH_LEDS @@ -196,7 +198,7 @@ public: case NeoPixelType_Grbw: ledcWrite(3, w); break; #endif } - #else + #else // ESP8266 analogWrite(RPIN, r); analogWrite(GPIN, g); analogWrite(BPIN, b); @@ -227,11 +229,6 @@ public: switch (_type) { case NeoPixelType_Grb: { _pGrb->SetPixelColor(indexPixel, RgbColor(color.R,color.G,color.B)); - #ifdef WLED_USE_ANALOG_LEDS - if (indexPixel != 0) return; //set analog LEDs from first pixel - byte b = _pGrb->GetBrightness(); - SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, 0); - #endif } break; case NeoPixelType_Grbw: { @@ -240,28 +237,6 @@ public: #else _pGrbw->SetPixelColor(indexPixel, color); #endif - #ifdef WLED_USE_ANALOG_LEDS - if (indexPixel != 0) return; //set analog LEDs from first pixel - byte b = _pGrbw->GetBrightness(); - // check color values for Warm / Cold white mix (for RGBW) // EsplanexaDevice.cpp - #ifdef WLED_USE_5CH_LEDS - if (color.R == 255 & color.G == 255 && color.B == 255 && color.W == 255) { - SetRgbwPwm(0, 0, 0, 0, color.W * b / 255); - } else if (color.R == 127 & color.G == 127 && color.B == 127 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 512, color.W * b / 255); - } else if (color.R == 0 & color.G == 0 && color.B == 0 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); - } else if (color.R == 130 & color.G == 90 && color.B == 0 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 255, color.W * b / 512); - } else if (color.R == 255 & color.G == 153 && color.B == 0 && color.W == 255) { - SetRgbwPwm(0, 0, 0, color.W * b / 255, 0); - } else { // not only white colors - SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); - } - #else - SetRgbwPwm(color.R * b / 255, color.G * b / 255, color.B * b / 255, color.W * b / 255); - #endif - #endif } break; } diff --git a/wled00/src/dependencies/arduino/core_esp8266_waveform.cpp b/wled00/src/dependencies/arduino/core_esp8266_waveform.cpp new file mode 100644 index 000000000..6a52b1b3a --- /dev/null +++ b/wled00/src/dependencies/arduino/core_esp8266_waveform.cpp @@ -0,0 +1,312 @@ +/* + esp8266_waveform - General purpose waveform generation and control, + supporting outputs on all pins in parallel. + + Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. + + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds). TIMER1 is set to 1-shot + mode and is always loaded with the time until the next edge of any live + waveforms. + + Up to one waveform generator per pin supported. + + Each waveform generator is synchronized to the ESP cycle counter, not the + timer. This allows for removing interrupt jitter and delay as the counter + always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. + + This replaces older tone(), analogWrite(), and the Servo classes. + + Everywhere in the code where "cycles" is used, it means ESP.getCycleTime() + cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz). + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifdef ESP8266 +#include +#include "ets_sys.h" +#include "core_esp8266_waveform.h" + +extern "C" { + +// Maximum delay between IRQs +#define MAXIRQUS (10000) + +// Set/clear GPIO 0-15 by bitmask +#define SetGPIO(a) do { GPOS = a; } while (0) +#define ClearGPIO(a) do { GPOC = a; } while (0) + +// Waveform generator can create tones, PWM, and servos +typedef struct { + uint32_t nextServiceCycle; // ESP cycle timer when a transition required + uint32_t expiryCycle; // For time-limited waveform, the cycle when this waveform must stop + uint32_t nextTimeHighCycles; // Copy over low->high to keep smooth waveform + uint32_t nextTimeLowCycles; // Copy over high->low to keep smooth waveform +} Waveform; + +static Waveform waveform[17]; // State of all possible pins +static volatile uint32_t waveformState = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code +static volatile uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code + +// Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine +static volatile uint32_t waveformToEnable = 0; // Message to the NMI handler to start a waveform on a inactive pin +static volatile uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation + +static uint32_t (*timer1CB)() = NULL; + + +// Non-speed critical bits +#pragma GCC optimize ("Os") + +static inline ICACHE_RAM_ATTR uint32_t GetCycleCount() { + uint32_t ccount; + __asm__ __volatile__("esync; rsr %0,ccount":"=a"(ccount)); + return ccount; +} + +// Interrupt on/off control +static ICACHE_RAM_ATTR void timer1Interrupt(); +static bool timerRunning = false; + +static void initTimer() { + timer1_disable(); + ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); + ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt); + timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); + timerRunning = true; +} + +static void ICACHE_RAM_ATTR deinitTimer() { + ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); + timer1_disable(); + timer1_isr_init(); + timerRunning = false; +} + +// Set a callback. Pass in NULL to stop it +void setTimer1Callback(uint32_t (*fn)()) { + timer1CB = fn; + if (!timerRunning && fn) { + initTimer(); + timer1_write(microsecondsToClockCycles(1)); // Cause an interrupt post-haste + } else if (timerRunning && !fn && !waveformEnabled) { + deinitTimer(); + } +} + +// Start up a waveform on a pin, or change the current one. Will change to the new +// waveform smoothly on next low->high transition. For immediate change, stopWaveform() +// first, then it will immediately begin. +int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) { + if ((pin > 16) || isFlashInterfacePin(pin)) { + return false; + } + Waveform *wave = &waveform[pin]; + // Adjust to shave off some of the IRQ time, approximately + wave->nextTimeHighCycles = microsecondsToClockCycles(timeHighUS); + wave->nextTimeLowCycles = microsecondsToClockCycles(timeLowUS); + wave->expiryCycle = runTimeUS ? GetCycleCount() + microsecondsToClockCycles(runTimeUS) : 0; + if (runTimeUS && !wave->expiryCycle) { + wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it + } + + uint32_t mask = 1<nextServiceCycle = GetCycleCount() + microsecondsToClockCycles(1); + waveformToEnable |= mask; + if (!timerRunning) { + initTimer(); + timer1_write(microsecondsToClockCycles(10)); + } else { + // Ensure timely service.... + if (T1L > microsecondsToClockCycles(10)) { + timer1_write(microsecondsToClockCycles(10)); + } + } + while (waveformToEnable) { + delay(0); // Wait for waveform to update + } + } + + return true; +} + +// Speed critical bits +#pragma GCC optimize ("O2") +// Normally would not want two copies like this, but due to different +// optimization levels the inline attribute gets lost if we try the +// other version. + +static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ() { + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a"(ccount)); + return ccount; +} + +static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b) { + if (a < b) { + return a; + } + return b; +} + +// Stops a waveform on a pin +int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) { + // Can't possibly need to stop anything if there is no timer active + if (!timerRunning) { + return false; + } + // If user sends in a pin >16 but <32, this will always point to a 0 bit + // If they send >=32, then the shift will result in 0 and it will also return false + uint32_t mask = 1< microsecondsToClockCycles(10)) { + timer1_write(microsecondsToClockCycles(10)); + } + while (waveformToDisable) { + /* no-op */ // Can't delay() since stopWaveform may be called from an IRQ + } + if (!waveformEnabled && !timer1CB) { + deinitTimer(); + } + return true; +} + +// The SDK and hardware take some time to actually get to our NMI code, so +// decrement the next IRQ's timer value by a bit so we can actually catch the +// real CPU cycle counter we want for the waveforms. +#if F_CPU == 80000000 + #define DELTAIRQ (microsecondsToClockCycles(3)) +#else + #define DELTAIRQ (microsecondsToClockCycles(2)) +#endif + + +static ICACHE_RAM_ATTR void timer1Interrupt() { + // Optimize the NMI inner loop by keeping track of the min and max GPIO that we + // are generating. In the common case (1 PWM) these may be the same pin and + // we can avoid looking at the other pins. + static int startPin = 0; + static int endPin = 0; + + uint32_t nextEventCycles = microsecondsToClockCycles(MAXIRQUS); + uint32_t timeoutCycle = GetCycleCountIRQ() + microsecondsToClockCycles(14); + + if (waveformToEnable || waveformToDisable) { + // Handle enable/disable requests from main app. + waveformEnabled = (waveformEnabled & ~waveformToDisable) | waveformToEnable; // Set the requested waveforms on/off + waveformState &= ~waveformToEnable; // And clear the state of any just started + waveformToEnable = 0; + waveformToDisable = 0; + // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t) + startPin = __builtin_ffs(waveformEnabled) - 1; + // Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one) + endPin = 32 - __builtin_clz(waveformEnabled); + } + + if (waveformEnabled) { + bool done = false; + do { + nextEventCycles = microsecondsToClockCycles(MAXIRQUS); + for (int i = startPin; i <= endPin; i++) { + uint32_t mask = 1<expiryCycle) { + int32_t expiryToGo = wave->expiryCycle - now; + if (expiryToGo < 0) { + // Done, remove! + waveformEnabled &= ~mask; + if (i == 16) { + GP16O &= ~1; + } else { + ClearGPIO(mask); + } + continue; + } + } + + // Check for toggles + int32_t cyclesToGo = wave->nextServiceCycle - now; + if (cyclesToGo < 0) { + cyclesToGo = -((-cyclesToGo) % (wave->nextTimeHighCycles + wave->nextTimeLowCycles)); + waveformState ^= mask; + if (waveformState & mask) { + if (i == 16) { + GP16O |= 1; // GPIO16 write slow as it's RMW + } else { + SetGPIO(mask); + } + wave->nextServiceCycle = now + wave->nextTimeHighCycles + cyclesToGo; + nextEventCycles = min_u32(nextEventCycles, min_u32(wave->nextTimeHighCycles + cyclesToGo, 1)); + } else { + if (i == 16) { + GP16O &= ~1; // GPIO16 write slow as it's RMW + } else { + ClearGPIO(mask); + } + wave->nextServiceCycle = now + wave->nextTimeLowCycles + cyclesToGo; + nextEventCycles = min_u32(nextEventCycles, min_u32(wave->nextTimeLowCycles + cyclesToGo, 1)); + } + } else { + uint32_t deltaCycles = wave->nextServiceCycle - now; + nextEventCycles = min_u32(nextEventCycles, deltaCycles); + } + } + + // Exit the loop if we've hit the fixed runtime limit or the next event is known to be after that timeout would occur + uint32_t now = GetCycleCountIRQ(); + int32_t cycleDeltaNextEvent = timeoutCycle - (now + nextEventCycles); + int32_t cyclesLeftTimeout = timeoutCycle - now; + done = (cycleDeltaNextEvent < 0) || (cyclesLeftTimeout < 0); + } while (!done); + } // if (waveformEnabled) + + if (timer1CB) { + nextEventCycles = min_u32(nextEventCycles, timer1CB()); + } + + if (nextEventCycles < microsecondsToClockCycles(10)) { + nextEventCycles = microsecondsToClockCycles(10); + } + nextEventCycles -= DELTAIRQ; + + // Do it here instead of global function to save time and because we know it's edge-IRQ +#if F_CPU == 160000000 + T1L = nextEventCycles >> 1; // Already know we're in range by MAXIRQUS +#else + T1L = nextEventCycles; // Already know we're in range by MAXIRQUS +#endif + TEIE |= TEIE1; // Edge int enable +} + +}; +#endif \ No newline at end of file diff --git a/wled00/src/dependencies/arduino/core_esp8266_waveform.h b/wled00/src/dependencies/arduino/core_esp8266_waveform.h new file mode 100644 index 000000000..24ce91fb3 --- /dev/null +++ b/wled00/src/dependencies/arduino/core_esp8266_waveform.h @@ -0,0 +1,71 @@ +/* + esp8266_waveform - General purpose waveform generation and control, + supporting outputs on all pins in parallel. + + Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. + + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds). TIMER1 is set to 1-shot + mode and is always loaded with the time until the next edge of any live + waveforms. + + Up to one waveform generator per pin supported. + + Each waveform generator is synchronized to the ESP cycle counter, not the + timer. This allows for removing interrupt jitter and delay as the counter + always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. + + This replaces older tone(), analogWrite(), and the Servo classes. + + Everywhere in the code where "cycles" is used, it means ESP.getCycleTime() + cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz). + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#ifndef __ESP8266_WAVEFORM_H +#define __ESP8266_WAVEFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Start or change a waveform of the specified high and low times on specific pin. +// If runtimeUS > 0 then automatically stop it after that many usecs. +// Returns true or false on success or failure. +int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS); +// Stop a waveform, if any, on the specified pin. +// Returns true or false on success or failure. +int stopWaveform(uint8_t pin); + +// Add a callback function to be called on *EVERY* timer1 trigger. The +// callback returns the number of microseconds until the next desired call. +// However, since it is called every timer1 interrupt, it may be called +// again before this period. It should therefore use the ESP Cycle Counter +// to determine whether or not to perform an operation. +// Pass in NULL to disable the callback and, if no other waveforms being +// generated, stop the timer as well. +// Make sure the CB function has the ICACHE_RAM_ATTR decorator. +void setTimer1Callback(uint32_t (*fn)()); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wled00/wled00.ino b/wled00/wled00.ino index c04258f06..ee97c85f5 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -93,6 +93,18 @@ #include #endif +// remove flicker because PWM signal of RGB channels can become out of phase +#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266) + #include "src/dependencies/arduino/core_esp8266_waveform.h" +#endif + +// enable additional debug output +#ifdef WLED_DEBUG + #ifndef ESP8266 + #include + #endif +#endif + //version code in format yymmddb (b = daily build) #define VERSION 2002192 @@ -526,6 +538,10 @@ void loop() { handleOverlays(); yield(); + #ifdef WLED_USE_ANALOG_LEDS + strip.setRgbwPwm(); + #endif + if (doReboot) reset(); if (!realtimeMode) //block stuff if WARLS/Adalight is enabled diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino index 40b01ae50..84e865292 100644 --- a/wled00/wled01_eeprom.ino +++ b/wled00/wled01_eeprom.ino @@ -3,7 +3,7 @@ * EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map */ -#define EEPSIZE 2560 +#define EEPSIZE 2560 //Maximum is 4096 //eeprom Version code, enables default settings instead of 0 init on update #define EEPVER 16 diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 820670c82..c4f0b45bd 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -194,10 +194,10 @@ void initConnection() WiFi.begin(clientSSID, clientPass); #ifdef ARDUINO_ARCH_ESP32 - if (noWifiSleep) WiFi.setSleep(false); + WiFi.setSleep(!noWifiSleep); WiFi.setHostname(serverDescription); #else - if (noWifiSleep) wifi_set_sleep_type(NONE_SLEEP_T); + wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T); #endif } diff --git a/wled00/wled17_mqtt.ino b/wled00/wled17_mqtt.ino index db34a742b..741c807f8 100644 --- a/wled00/wled17_mqtt.ino +++ b/wled00/wled17_mqtt.ino @@ -3,6 +3,7 @@ */ #ifdef WLED_ENABLE_MQTT +#define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds void parseMQTTBriPayload(char* payload) { @@ -129,7 +130,7 @@ bool initMqtt() strcpy(mqttStatusTopic, mqttDeviceTopic); strcat(mqttStatusTopic, "/status"); mqtt->setWill(mqttStatusTopic, 0, true, "offline"); - mqtt->setKeepAlive(60); + mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME); mqtt->connect(); return true; } diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino index 852dfdacf..6bf9aafda 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -267,14 +267,25 @@ void serializeInfo(JsonObject root) wifi_info["channel"] = WiFi.channel(); #ifdef ARDUINO_ARCH_ESP32 + #ifdef WLED_DEBUG + wifi_info["txPower"] = (int) WiFi.getTxPower(); + wifi_info["sleep"] = (bool) WiFi.getSleep(); + #endif root["arch"] = "esp32"; root["core"] = ESP.getSdkVersion(); //root["maxalloc"] = ESP.getMaxAllocHeap(); + #ifdef WLED_DEBUG + root["resetReason0"] = (int)rtc_get_reset_reason(0); + root["resetReason1"] = (int)rtc_get_reset_reason(1); + #endif root["lwip"] = 0; #else root["arch"] = "esp8266"; root["core"] = ESP.getCoreVersion(); //root["maxalloc"] = ESP.getMaxFreeBlockSize(); + #ifdef WLED_DEBUG + root["resetReason"] = (int)ESP.getResetInfoPtr()->reason; + #endif root["lwip"] = LWIP_VERSION_MAJOR; #endif