diff --git a/platformio.ini b/platformio.ini index 154406606..dd5f7a73c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -77,12 +77,14 @@ arduino_core_2_5_0 = espressif8266@2.0.4 arduino_core_2_5_2 = espressif8266@2.2.3 arduino_core_2_6_1 = espressif8266@2.3.0 arduino_core_2_6_2 = espressif8266@2.3.1 +arduino_core_2_6_3 = espressif8266@2.3.3 arduino_core_stage = https://github.com/platformio/platform-espressif8266.git#feature/stage -platform = ${common:esp8266.arduino_core_2_6_2} +platform = ${common:esp8266.arduino_core_2_6_3} build_flags = -D ESP8266 -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -Wl,-Teagle.flash.4m1m.ld ;;;; Required for core > v2.5.0 or staging version 4MB Flash 3MB SPIFFs +lib_ignore = AsyncTCP [common:esp8266_1M] platform = espressif8266@1.8.0 @@ -96,6 +98,7 @@ build_flags = -D WLED_DISABLE_CRONIXIE ; -D WLED_DISABLE_HUESYNC ; -D WLED_DISABLE_INFRARED +lib_ignore = ${common:esp8266.lib_ignore} [common:esp8266_512k] platform = espressif8266@1.8.0 @@ -109,12 +112,16 @@ build_flags = -D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC ; -D WLED_DISABLE_INFRARED +lib_ignore = ${common:esp8266.lib_ignore} [common:esp32] -platform = espressif32@1.11.1 +platform = espressif32@1.11.2 build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -D ARDUINO_ARCH_ESP32 +lib_ignore = + ESPAsyncTCP + ESPAsyncUDP # see: http://docs.platformio.org/en/latest/platforms/espressif8266.html [env:nodemcuv2] @@ -129,7 +136,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:d1_mini] board = d1_mini @@ -143,7 +150,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:esp01_1m] board = esp01_1m @@ -159,7 +166,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:esp01] board = esp01 @@ -174,7 +181,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:esp07] board = esp07 @@ -188,7 +195,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} # see: http://docs.platformio.org/en/latest/platforms/espressif32.html [env:esp32dev] @@ -202,8 +209,7 @@ build_flags = ${common:esp32.build_flags} lib_deps = ${common.lib_deps_external} -lib_ignore = - ESPAsyncUDP +lib_ignore = ${common:esp32.lib_ignore} lib_compat_mode = strict [env:esp8285_4CH_MagicHome] @@ -220,7 +226,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:esp8285_4CH_H801] board = esp8285 @@ -237,7 +243,7 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP +lib_ignore = ${common:esp8266.lib_ignore} [env:esp8285_5CH_H801] board = esp8285 @@ -255,5 +261,4 @@ build_flags = lib_deps = ${common.lib_deps_external} lib_compat_mode = strict -lib_ignore = AsyncTCP - +lib_ignore = ${common:esp8266.lib_ignore} diff --git a/usermods/rotary_encoder_change_effect/wled06_usermod.ino b/usermods/rotary_encoder_change_effect/wled06_usermod.ino index dbde9560a..f004ba2f4 100644 --- a/usermods/rotary_encoder_change_effect/wled06_usermod.ino +++ b/usermods/rotary_encoder_change_effect/wled06_usermod.ino @@ -39,7 +39,7 @@ void userLoop() { //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa - colorUpdated(6); + colorUpdated(NOTIFIER_CALL_MODE_FX_CHANGED); lastTime = millis(); } } diff --git a/usermods/stairway_wipe_basic/wled06_usermod.ino b/usermods/stairway_wipe_basic/wled06_usermod.ino index ab61c5afe..3d493cc1d 100644 --- a/usermods/stairway_wipe_basic/wled06_usermod.ino +++ b/usermods/stairway_wipe_basic/wled06_usermod.ino @@ -47,7 +47,7 @@ void userLoop() if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete effectCurrent = FX_MODE_STATIC; timeStaticStart = millis(); - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); wipeState = 2; } } else if (wipeState == 2) { //static @@ -59,7 +59,7 @@ void userLoop() #ifdef STAIRCASE_WIPE_OFF effectCurrent = FX_MODE_COLOR_WIPE; strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); wipeState = 4; #else turnOff(); @@ -93,7 +93,7 @@ void startWipe() bool doReverse = (userVar0 == 2); seg.setOption(1, doReverse); - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); } void turnOff() @@ -104,7 +104,7 @@ void turnOff() transitionDelayTemp = 4000; //fade out slowly #endif bri = 0; - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); wipeState = 0; userVar0 = 0; previousUserVar0 = 0; 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/const.h b/wled00/const.h index 1308bf5fe..4d2875723 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -7,6 +7,19 @@ #define AP_BEHAVIOR_ALWAYS 2 //Always open #define AP_BEHAVIOR_BUTTON_ONLY 3 //Only when button pressed for 6 sec +//Notifier callMode +#define NOTIFIER_CALL_MODE_INIT 0 // no updates on init, can be used to disable updates +#define NOTIFIER_CALL_MODE_DIRECT_CHANGE 1 +#define NOTIFIER_CALL_MODE_BUTTON 2 +#define NOTIFIER_CALL_MODE_NOTIFICATION 3 +#define NOTIFIER_CALL_MODE_NIGHTLIGHT 4 +#define NOTIFIER_CALL_MODE_NO_NOTIFY 5 +#define NOTIFIER_CALL_MODE_FX_CHANGED 6 +#define NOTIFIER_CALL_MODE_HUE 7 +#define NOTIFIER_CALL_MODE_PRESET_CYCLE 8 +#define NOTIFIER_CALL_MODE_BLYNK 9 +#define NOTIFIER_CALL_MODE_ALEXA 10 + //RGB to RGBW conversion mode #define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider #define RGBW_MODE_AUTO_BRIGHTER 1 //New algorithm. Adds as much white as the darkest RGBW channel 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 746f98d0b..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 @@ -301,7 +313,7 @@ unsigned long buttonWaitTime = 0; bool notifyDirectDefault = notifyDirect; bool receiveNotifications = true; unsigned long notificationSentTime = 0; -byte notificationSentCallMode = 0; +byte notificationSentCallMode = NOTIFIER_CALL_MODE_INIT; bool notificationTwoRequired = false; //effects @@ -369,7 +381,7 @@ unsigned long realtimeTimeout = 0; //mqtt long lastMqttReconnectAttempt = 0; long lastInterfaceUpdate = 0; -byte interfaceUpdateCallMode = 0; +byte interfaceUpdateCallMode = NOTIFIER_CALL_MODE_INIT; char mqttStatusTopic[40] = ""; //this must be global because of async handlers #if AUXPIN >= 0 @@ -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/wled03_set.ino b/wled00/wled03_set.ino index 6655dafd5..f7485917d 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -651,7 +651,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) if (pos < 1) XML_response(request); pos = req.indexOf("&NN"); //do not send UDP notifications this time - colorUpdated((pos > 0) ? 5:1); + colorUpdated((pos > 0) ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE); return true; } diff --git a/wled00/wled05_init.ino b/wled00/wled05_init.ino index 8d41756b4..c4f0b45bd 100644 --- a/wled00/wled05_init.ino +++ b/wled00/wled05_init.ino @@ -99,7 +99,7 @@ void beginStrip() #endif if (bootPreset>0) applyPreset(bootPreset, turnOnAtBoot, true, true); - colorUpdated(0); + colorUpdated(NOTIFIER_CALL_MODE_INIT); //init relay pin #if RLYPIN >= 0 @@ -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/wled07_notify.ino b/wled00/wled07_notify.ino index ab55df28d..fd83f1f81 100644 --- a/wled00/wled07_notify.ino +++ b/wled00/wled07_notify.ino @@ -11,15 +11,15 @@ void notify(byte callMode, bool followUp=false) if (!udpConnected) return; switch (callMode) { - case 0: return; - case 1: if (!notifyDirect) return; break; - case 2: if (!notifyButton) return; break; - case 4: if (!notifyDirect) return; break; - case 6: if (!notifyDirect) return; break; //fx change - case 7: if (!notifyHue) return; break; - case 8: if (!notifyDirect) return; break; - case 9: if (!notifyDirect) return; break; - case 10: if (!notifyAlexa) return; break; + case NOTIFIER_CALL_MODE_INIT: return; + case NOTIFIER_CALL_MODE_DIRECT_CHANGE: if (!notifyDirect) return; break; + case NOTIFIER_CALL_MODE_BUTTON: if (!notifyButton) return; break; + case NOTIFIER_CALL_MODE_NIGHTLIGHT: if (!notifyDirect) return; break; + case NOTIFIER_CALL_MODE_FX_CHANGED: if (!notifyDirect) return; break; //fx change + case NOTIFIER_CALL_MODE_HUE: if (!notifyHue) return; break; + case NOTIFIER_CALL_MODE_PRESET_CYCLE: if (!notifyDirect) return; break; + case NOTIFIER_CALL_MODE_BLYNK: if (!notifyDirect) return; break; + case NOTIFIER_CALL_MODE_ALEXA: if (!notifyAlexa) return; break; default: return; } byte udpOut[WLEDPACKETSIZE]; @@ -91,13 +91,12 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ //E1.31 protocol support // skip out-of-sequence packets - if (p->sequence_number < e131LastSequenceNumber && p->sequence_number - e131LastSequenceNumber > -20){ + if (p->sequence_number < e131LastSequenceNumber && p->sequence_number > 20 && e131LastSequenceNumber < 250){ DEBUG_PRINT("skipping E1.31 frame (last seq="); DEBUG_PRINT(e131LastSequenceNumber); DEBUG_PRINT(", current seq="); DEBUG_PRINT(p->sequence_number); DEBUG_PRINTLN(")"); - e131LastSequenceNumber = p->sequence_number; return; } e131LastSequenceNumber = p->sequence_number; @@ -157,9 +156,9 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP){ col[3] = p->property_values[DMXAddress+11]; //white colSec[3] = p->property_values[DMXAddress+12]; } - transitionDelayTemp = 0; // act fast - colorUpdated(3); // don't send UDP - return; // don't activate realtime live mode + transitionDelayTemp = 0; // act fast + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); // don't send UDP + return; // don't activate realtime live mode break; case DMX_MODE_MULTIPLE_RGB: @@ -329,7 +328,7 @@ void handleNotifications() if (nightlightActive) nightlightDelayMins = udpIn[7]; if (receiveNotificationBrightness || !someSel) bri = udpIn[2]; - colorUpdated(3); + colorUpdated(NOTIFIER_CALL_MODE_NOTIFICATION); } else if (udpIn[0] > 0 && udpIn[0] < 5 && receiveDirect) //1 warls //2 drgb //3 drgbw { diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino index fb0e8a7e8..124e43bd6 100644 --- a/wled00/wled08_led.ino +++ b/wled00/wled08_led.ino @@ -79,6 +79,7 @@ bool colorChanged() { if (col[i] != colIT[i]) return true; if (colSec[i] != colSecIT[i]) return true; + if (col[i] != colNlT[i]) return true; } if (bri != briIT) return true; return false; @@ -89,18 +90,24 @@ void colorUpdated(int callMode) { //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa - if (callMode != 0 && callMode != 1 && callMode != 5) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments + if (callMode != NOTIFIER_CALL_MODE_INIT && + callMode != NOTIFIER_CALL_MODE_DIRECT_CHANGE && + callMode != NOTIFIER_CALL_MODE_NO_NOTIFY) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments bool fxChanged = strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette); if (!colorChanged()) { - if (nightlightActive && !nightlightActiveOld && callMode != 3 && callMode != 5) + if (nightlightActive && !nightlightActiveOld && + callMode != NOTIFIER_CALL_MODE_NOTIFICATION && + callMode != NOTIFIER_CALL_MODE_NO_NOTIFY) { - notify(4); interfaceUpdateCallMode = 4; return; + notify(NOTIFIER_CALL_MODE_NIGHTLIGHT); + interfaceUpdateCallMode = NOTIFIER_CALL_MODE_NIGHTLIGHT; + return; } else if (fxChanged) { - notify(6); - if (callMode != 8) interfaceUpdateCallMode = 6; + notify(NOTIFIER_CALL_MODE_FX_CHANGED); + if (callMode != NOTIFIER_CALL_MODE_PRESET_CYCLE) interfaceUpdateCallMode = NOTIFIER_CALL_MODE_FX_CHANGED; if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; if (isPreset) {isPreset = false;} else {currentPreset = -1;} @@ -110,7 +117,7 @@ void colorUpdated(int callMode) if (realtimeTimeout == UINT32_MAX) realtimeTimeout = 0; if (isPreset) {isPreset = false;} else {currentPreset = -1;} - if (callMode != 5 && nightlightActive && nightlightFade) + if (callMode != NOTIFIER_CALL_MODE_NO_NOTIFY && nightlightActive && nightlightFade) { briNlT = bri; nightlightDelayMs -= (millis() - nightlightStartTime); @@ -123,8 +130,8 @@ void colorUpdated(int callMode) } if (briT == 0) { - setLedsStandard(true); //do not color transition if starting from off - if (callMode != 3) resetTimebase(); //effect start from beginning + setLedsStandard(true); //do not color transition if starting from off + if (callMode != NOTIFIER_CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning } briIT = bri; @@ -135,7 +142,7 @@ void colorUpdated(int callMode) if (fadeTransition) { //set correct delay if not using notification delay - if (callMode != 3 && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; + if (callMode != NOTIFIER_CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; jsonTransitionOnce = false; if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;} @@ -158,7 +165,7 @@ void colorUpdated(int callMode) strip.trigger(); } - if (callMode == 8) return; + if (callMode == NOTIFIER_CALL_MODE_PRESET_CYCLE) return; //set flag to update blynk and mqtt interfaceUpdateCallMode = callMode; } @@ -167,12 +174,13 @@ void colorUpdated(int callMode) void updateInterfaces(uint8_t callMode) { #ifndef WLED_DISABLE_ALEXA - if (espalexaDevice != nullptr && callMode != 10) { + if (espalexaDevice != nullptr && callMode != NOTIFIER_CALL_MODE_ALEXA) { espalexaDevice->setValue(bri); espalexaDevice->setColor(col[0], col[1], col[2]); } #endif - if (callMode != 9 && callMode != 5) updateBlynk(); + if (callMode != NOTIFIER_CALL_MODE_BLYNK && + callMode != NOTIFIER_CALL_MODE_NO_NOTIFY) updateBlynk(); doPublishMqtt = true; lastInterfaceUpdate = millis(); } @@ -233,7 +241,7 @@ void handleNightlight() { for (byte i=0; i<4; i++) col[i] = colNlT[i]+ ((colSec[i] - colNlT[i])*nper); // fading from actual color to secondary color } - colorUpdated(5); + colorUpdated(NOTIFIER_CALL_MODE_NO_NOTIFY); } if (nper >= 1) { @@ -241,7 +249,7 @@ void handleNightlight() if (!nightlightFade) { bri = nightlightTargetBri; - colorUpdated(5); + colorUpdated(NOTIFIER_CALL_MODE_NO_NOTIFY); } updateBlynk(); if (bri == 0) briLast = briNlT; @@ -257,7 +265,7 @@ void handleNightlight() applyPreset(presetCycCurr,presetApplyBri,presetApplyCol,presetApplyFx); presetCycCurr++; if (presetCycCurr > presetCycleMax) presetCycCurr = presetCycleMin; if (presetCycCurr > 25) presetCycCurr = 1; - colorUpdated(8); + colorUpdated(NOTIFIER_CALL_MODE_PRESET_CYCLE); presetCycledTime = millis(); } } diff --git a/wled00/wled09_button.ino b/wled00/wled09_button.ino index 86227dc3f..1cd868bec 100644 --- a/wled00/wled09_button.ino +++ b/wled00/wled09_button.ino @@ -7,7 +7,7 @@ void shortPressAction() if (!macroButton) { toggleOnOff(); - colorUpdated(2); + colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } else { applyMacro(macroButton); } diff --git a/wled00/wled12_alexa.ino b/wled00/wled12_alexa.ino index c3bc01ab0..9c27041b7 100644 --- a/wled00/wled12_alexa.ino +++ b/wled00/wled12_alexa.ino @@ -41,7 +41,7 @@ void onAlexaChange(EspalexaDevice* dev) if (bri == 0) { bri = briLast; - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } } else applyMacro(macroAlexaOn); } else if (m == EspalexaDeviceProperty::off) @@ -52,13 +52,13 @@ void onAlexaChange(EspalexaDevice* dev) { briLast = bri; bri = 0; - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } } else applyMacro(macroAlexaOff); } else if (m == EspalexaDeviceProperty::bri) { bri = espalexaDevice->getValue(); - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } else //color { uint32_t color = espalexaDevice->getRGB(); @@ -67,7 +67,7 @@ void onAlexaChange(EspalexaDevice* dev) col[1] = ((color >> 8) & 0xFF); col[2] = (color & 0xFF); if (useRGBW && col[3] == 0) colorRGBtoRGBW(col); // do not touch white value if EspalexaDevice.cpp did set the white channel - colorUpdated(10); + colorUpdated(NOTIFIER_CALL_MODE_ALEXA); } } diff --git a/wled00/wled15_hue.ino b/wled00/wled15_hue.ino index da400f5e8..77e6622c6 100644 --- a/wled00/wled15_hue.ino +++ b/wled00/wled15_hue.ino @@ -6,7 +6,7 @@ void handleHue() { if (hueReceived) { - colorUpdated(7); hueReceived = false; + colorUpdated(NOTIFIER_CALL_MODE_HUE); hueReceived = false; if (hueStoreAllowed && hueNewKey) { saveSettingsToEEPROM(); //save api key diff --git a/wled00/wled16_blynk.ino b/wled00/wled16_blynk.ino index c09d0c185..ea4d68771 100644 --- a/wled00/wled16_blynk.ino +++ b/wled00/wled16_blynk.ino @@ -41,45 +41,45 @@ void updateBlynk() BLYNK_WRITE(V0) { bri = param.asInt();//bri - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V1) { blHue = param.asInt();//hue colorHStoRGB(blHue*10,blSat,(false)? colSec:col); - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V2) { blSat = param.asInt();//sat colorHStoRGB(blHue*10,blSat,(false)? colSec:col); - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V3) { bool on = (param.asInt()>0); - if (!on != !bri) {toggleOnOff(); colorUpdated(9);} + if (!on != !bri) {toggleOnOff(); colorUpdated(NOTIFIER_CALL_MODE_BLYNK);} } BLYNK_WRITE(V4) { effectCurrent = param.asInt()-1;//fx - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V5) { effectSpeed = param.asInt();//sx - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V6) { effectIntensity = param.asInt();//ix - colorUpdated(9); + colorUpdated(NOTIFIER_CALL_MODE_BLYNK); } BLYNK_WRITE(V7) diff --git a/wled00/wled17_mqtt.ino b/wled00/wled17_mqtt.ino index aa72b9e1c..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) { @@ -12,7 +13,7 @@ void parseMQTTBriPayload(char* payload) uint8_t in = strtoul(payload, NULL, 10); if (in == 0 && bri > 0) briLast = bri; bri = in; - colorUpdated(1); + colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE); } } @@ -60,7 +61,7 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties if (strstr(topic, "/col")) { colorFromDecOrHexString(col, (char*)payload); - colorUpdated(1); + colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE); } else if (strstr(topic, "/api")) { String apireq = "win&"; @@ -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 219ef8bc1..6bf9aafda 100644 --- a/wled00/wled19_json.ino +++ b/wled00/wled19_json.ino @@ -154,7 +154,7 @@ bool deserializeState(JsonObject root) } } - colorUpdated(noNotification ? 5:1); + colorUpdated(noNotification ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE); ps = root["psave"] | -1; if (ps >= 0) savePreset(ps); @@ -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 diff --git a/wled00/wled20_ir.ino b/wled00/wled20_ir.ino index 113b19b53..617518c31 100644 --- a/wled00/wled20_ir.ino +++ b/wled00/wled20_ir.ino @@ -29,7 +29,7 @@ bool decodeIRCustom(uint32_t code) default: return false; } - if (code != IRCUSTOM_MACRO1) colorUpdated(2); //don't update color again if we apply macro, it already does it + if (code != IRCUSTOM_MACRO1) colorUpdated(NOTIFIER_CALL_MODE_BUTTON); //don't update color again if we apply macro, it already does it return true; } @@ -51,25 +51,25 @@ void decodeIR(uint32_t code) irTimesRepeated++; if (lastValidCode == IR24_BRIGHTER || lastValidCode == IR40_BPLUS ) { - relativeChange(&bri, 10); colorUpdated(2); + relativeChange(&bri, 10); colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } else if (lastValidCode == IR24_DARKER || lastValidCode == IR40_BMINUS ) { - relativeChange(&bri, -10, 5); colorUpdated(2); + relativeChange(&bri, -10, 5); colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } if (lastValidCode == IR40_WPLUS) { - relativeChangeWhite(10); colorUpdated(2); + relativeChangeWhite(10); colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } else if (lastValidCode == IR40_WMINUS) { - relativeChangeWhite(-10, 5); colorUpdated(2); + relativeChangeWhite(-10, 5); colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 ) { nightlightActive = true; nightlightStartTime = millis(); - colorUpdated(2); + colorUpdated(NOTIFIER_CALL_MODE_BUTTON); } return; } @@ -85,11 +85,12 @@ void decodeIR(uint32_t code) case 3: decodeIR40(code); break; // blue 40-key remote with 25%, 50%, 75% and 100% keys case 4: decodeIR44(code); break; // white 44-key remote with color-up/down keys and DIY1 to 6 keys case 5: decodeIR21(code); break; // white 21-key remote - case 6: decodeIR6(code); break; // black 6-key learning remote defaults: "CH" controls brightness, + case 6: decodeIR6(code); break; // black 6-key learning remote defaults: "CH" controls brightness, // "VOL +" controls effect, "VOL -" controls colour/palette, "MUTE" // sets bright plain white default: return; } + colorUpdated(NOTIFIER_CALL_MODE_BUTTON); //for notifier, IR is considered a button input } //code <= 0xF70000 also invalid } @@ -125,7 +126,6 @@ void decodeIR24(uint32_t code) default: return; } lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input } void decodeIR24OLD(uint32_t code) @@ -158,7 +158,6 @@ void decodeIR24OLD(uint32_t code) default: return; } lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input } @@ -194,7 +193,6 @@ void decodeIR24CT(uint32_t code) default: return; } lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input } @@ -253,7 +251,6 @@ void decodeIR40(uint32_t code) case IR40_FLASH : if (!applyPreset(4)) { effectCurrent = FX_MODE_RAINBOW; effectPalette = 0; } break; } lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input } void decodeIR44(uint32_t code) @@ -317,12 +314,11 @@ void decodeIR44(uint32_t code) case IR44_FADE7 : bri = 255; break; } lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input } void decodeIR21(uint32_t code) { - switch (code) { + switch (code) { case IR21_BRIGHTER: relativeChange(&bri, 10); break; case IR21_DARKER: relativeChange(&bri, -10, 5); break; case IR21_OFF: briLast = bri; bri = 0; break; @@ -345,51 +341,42 @@ void decodeIR21(uint32_t code) case IR21_FADE: if (!applyPreset(3)) { effectCurrent = FX_MODE_BREATH; effectPalette = 0; } break; case IR21_SMOOTH: if (!applyPreset(4)) { effectCurrent = FX_MODE_RAINBOW; effectPalette = 0; } break; default: return; - } - lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input + } + lastValidCode = code; } void decodeIR6(uint32_t code) { - - switch (code) { - case IR6_POWER: toggleOnOff(); break; - case IR6_CHANNEL_UP: relativeChange(&bri, 10); break; - case IR6_CHANNEL_DOWN: relativeChange(&bri, -10, 5); break; - case IR6_VOLUME_UP: /* next effect */ relativeChange(&effectCurrent, 1); break; - case IR6_VOLUME_DOWN: - /* next palette */ - - relativeChange(&effectPalette, 1); - - switch(lastIR6ColourIdx) - { - case 0: colorFromUint32(COLOR_RED); break; - case 1: colorFromUint32(COLOR_REDDISH); break; - case 2:colorFromUint32(COLOR_ORANGE); break; - case 3:colorFromUint32(COLOR_YELLOWISH); break; - case 4:colorFromUint32(COLOR_GREEN); break; - case 5:colorFromUint32(COLOR_GREENISH); break; - case 6:colorFromUint32(COLOR_TURQUOISE); break; - case 7: colorFromUint32(COLOR_CYAN); break; - case 8:colorFromUint32(COLOR_BLUE); break; - case 9:colorFromUint32(COLOR_DEEPBLUE); break; - case 10:colorFromUint32(COLOR_PURPLE); break; - case 11:colorFromUint32(COLOR_PINK); break; - case 12:colorFromUint32(COLOR_WHITE); break; - default:break; - - } - - lastIR6ColourIdx++; - if(lastIR6ColourIdx > 12) lastIR6ColourIdx = 0; - + switch (code) { + case IR6_POWER: toggleOnOff(); break; + case IR6_CHANNEL_UP: relativeChange(&bri, 10); break; + case IR6_CHANNEL_DOWN: relativeChange(&bri, -10, 5); break; + case IR6_VOLUME_UP: /* next effect */ relativeChange(&effectCurrent, 1); break; + case IR6_VOLUME_DOWN: + /* next palette */ + relativeChange(&effectPalette, 1); + switch(lastIR6ColourIdx) { + case 0: colorFromUint32(COLOR_RED); break; + case 1: colorFromUint32(COLOR_REDDISH); break; + case 2:colorFromUint32(COLOR_ORANGE); break; + case 3:colorFromUint32(COLOR_YELLOWISH); break; + case 4:colorFromUint32(COLOR_GREEN); break; + case 5:colorFromUint32(COLOR_GREENISH); break; + case 6:colorFromUint32(COLOR_TURQUOISE); break; + case 7: colorFromUint32(COLOR_CYAN); break; + case 8:colorFromUint32(COLOR_BLUE); break; + case 9:colorFromUint32(COLOR_DEEPBLUE); break; + case 10:colorFromUint32(COLOR_PURPLE); break; + case 11:colorFromUint32(COLOR_PINK); break; + case 12:colorFromUint32(COLOR_WHITE); break; + default:break; + } + lastIR6ColourIdx++; + if(lastIR6ColourIdx > 12) lastIR6ColourIdx = 0; break; - case IR6_MUTE: effectCurrent = 0; effectPalette = 0; colorFromUint32(COLOR_WHITE); bri=255; break; - } - lastValidCode = code; - colorUpdated(2); //for notifier, IR is considered a button input + case IR6_MUTE: effectCurrent = 0; effectPalette = 0; colorFromUint32(COLOR_WHITE); bri=255; break; + } + lastValidCode = code; }