diff --git a/CHANGELOG.md b/CHANGELOG.md index 6751de2c..e2ef83e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ### Builds after release 0.12.0 +#### Build 2112080 + +- Version bump to 0.13.0-b6 "Toki" +- Added "ESP02" (ESP8266 with 2M of flash) to PIO/release binaries + #### Build 2112070 - Added new effect "Fairy", replacing "Police All" diff --git a/platformio.ini b/platformio.ini index d45f52be..72196928 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,7 @@ ; default_envs = travis_esp8266, travis_esp32 # Release binaries -default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth +default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # Build everything ; default_envs = esp32dev, esp8285_4CH_MagicHome, esp8285_4CH_H801, codm-controller-0.6-rev2, codm-controller-0.6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, travis_esp8266, travis_esp32, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_5CH_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips @@ -20,6 +20,7 @@ default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth # Single binaries (uncomment your board) ; default_envs = elekstube_ips ; default_envs = nodemcuv2 +; default_envs = esp8266_2m ; default_envs = esp01_1m_full ; default_envs = esp07 ; default_envs = d1_mini @@ -53,14 +54,14 @@ extra_configs = arduino_core_2_6_3 = espressif8266@2.3.3 arduino_core_2_7_4 = espressif8266@2.6.2 arduino_core_3_0_0 = espressif8266@3.0.0 -;arduino_core_3_2_0 = espressif8266@3.2.0 +arduino_core_3_2_0 = espressif8266@3.2.0 # Development platforms arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage # Platform to use for ESP8266 -platform_wled_default = ${common.arduino_core_2_7_4} +platform_wled_default = ${common.arduino_core_3_0_2} # We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 platformio/toolchain-xtensa @ ~2.40802.200502 @@ -105,6 +106,7 @@ build_flags = -DBEARSSL_SSL_BASIC -D CORE_DEBUG_LEVEL=0 -D NDEBUG + -Dregister= #build_flags for the IRremoteESP8266 library (enabled decoders have to appear here) -D _IR_ENABLE_DEFAULT_=false -D DECODE_HASH=true @@ -242,6 +244,13 @@ build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP8266 lib_deps = ${esp8266.lib_deps} +[env:esp8266_2m] +board = esp_wroom_02 +platform = ${common.platform_wled_default} +board_build.ldscript = ${common.ldscript_2m512k} +build_flags = ${common.build_flags_esp8266} -D WLED_RELEASE_NAME=ESP02 +lib_deps = ${esp8266.lib_deps} + [env:esp01_1m_full] board = esp01_1m platform = ${common.platform_wled_default} diff --git a/usermods/SN_Photoresistor/usermod_sn_photoresistor.h b/usermods/SN_Photoresistor/usermod_sn_photoresistor.h index 3b785fad..6a7e9f5a 100644 --- a/usermods/SN_Photoresistor/usermod_sn_photoresistor.h +++ b/usermods/SN_Photoresistor/usermod_sn_photoresistor.h @@ -123,6 +123,11 @@ public: } } + uint16_t getLastLDRValue() + { + return lastLDRValue; + } + void addToJsonInfo(JsonObject &root) { JsonObject user = root[F("u")]; diff --git a/usermods/quinled-an-penta/quinled-an-penta.h b/usermods/quinled-an-penta/quinled-an-penta.h index 10777e36..5153ee58 100644 --- a/usermods/quinled-an-penta/quinled-an-penta.h +++ b/usermods/quinled-an-penta/quinled-an-penta.h @@ -61,10 +61,10 @@ class QuinLEDAnPentaUsermod : public Usermod float shtLastKnownHumidity = 0; // Pin/IO vars - const int8_t anPentaPins[5] = {14, 13, 12, 4, 2}; + const int8_t anPentaLEDPins[5] = {14, 13, 12, 4, 2}; int8_t oledSpiClk = 15; int8_t oledSpiData = 16; - int8_t oledSpiCs = 0; + int8_t oledSpiCs = 27; int8_t oledSpiDc = 32; int8_t oledSpiRst = 33; int8_t shtSda = 1; @@ -75,7 +75,7 @@ class QuinLEDAnPentaUsermod : public Usermod { for(int8_t i = 0; i <= 4; i++) { - if(anPentaPins[i] == pin) + if(anPentaLEDPins[i] == pin) return true; } return false; @@ -313,7 +313,7 @@ class QuinLEDAnPentaUsermod : public Usermod byte drawnLines = 0; for (int8_t app = 0; app <= 4; app++) { for (int8_t clp = 0; clp <= 4; clp++) { - if (anPentaPins[app] == currentLedPins[clp]) { + if (anPentaLEDPins[app] == currentLedPins[clp]) { char charCurrentLedcReads[17]; sprintf(charCurrentLedcReads, "LED %d:", app+1); if (oledUseProgressBars) { diff --git a/usermods/quinled-an-penta/readme.md b/usermods/quinled-an-penta/readme.md index c292736c..4a4c0290 100644 --- a/usermods/quinled-an-penta/readme.md +++ b/usermods/quinled-an-penta/readme.md @@ -1,12 +1,12 @@ # QuinLED-An-Penta -The (un)official usermod to get the best out of the QuinLED-An-Penta, like using the OLED and the SHT30 temperature/humidity sensor. +The (un)official usermod to get the best out of the QuinLED-An-Penta (https://quinled.info/quinled-an-penta/), like using the OLED and the SHT30 temperature/humidity sensor. ## Requirements * "u8gs" by olikraus, v2.28 or higher: https://github.com/olikraus/u8g2 * "SHT85" by Rob Tillaart, v0.2 or higher: https://github.com/RobTillaart/SHT85 ## Usermod installation -Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one and add the buildflag `-D QUINLED_AN_PENTA`. +Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one, add the buildflag `-D QUINLED_AN_PENTA` and the below library dependencies. ESP32 (**without** ethernet): ``` @@ -33,14 +33,19 @@ This mod has been optimized for an SSD1306 driven 128x64 OLED. Using a smaller O I highly recommend using these "two color monochromatic OLEDs", which have the first 16 pixels in a different color than the other 48, e.g. a yellow/blue OLED. Also note, you need to have an **SPI** driven OLED, **not i2c**! +### Limitations combined with Ethernet +The initial development of this mod had been done with a beta version of the QuinLED-An-Penta, which had a different IO layout for the OLED: The CS pin used to be IO_0, but has been changed to IO27 with the first v1 public release. Unfortunately, IO27 is used by the Ethernet boards, so WLED will not let you enable the OLED screen, if you're using it with Ethernet. This unfortunately makes the development I've done to support/show Ethernet information void, as it cannot be used. +However (and I've not tried this, as I don't own a v1 board): You can try to modify this mod and try to use IO27 for the OLED and share it with the Ethernet board. It is "just" the chip select pin, so there is a chance that both can coexist and use the same IO. You need to skip WLEDs PinManager for the CS pin, so WLED will not block using it. If you don't know how this works: Leave it. If you know what I'm talking about: Try it and please let me know on the Intermit.Tech (QuinLED) Discord server: https://discord.gg/WdbAauG + ### My OLED flickers after some time, what should I do? -That's a tricky one: During development I saw that the OLED sometimes starts to "bug out" / flicker and won't work anymore. This seems to be caused by the high PWM interference the board produces. It seems to loose it's settings and then doesn't know how to draw anymore. Turns out the only way to fix this is to call the libraries `begin()` method again which will re-initialize the display. +That's a tricky one: During development I saw that the OLED sometimes starts to "bug out" / flicker and won't work anymore. This seems to be caused by the high PWM interference the board produces. It seems to loose its settings and then doesn't know how to draw anymore. Turns out the only way to fix this is to call the libraries `begin()` method again which will re-initialize the display. If you're facing this issue, you can enable a setting I've added which will call the `begin()` roughly every 60 seconds between a page change. This will make the page change take ~500ms, but will fix the display. + ## Configuration Navigate to the "Config" and then to the "Usermods" section. If you compiled WLED with `-D QUINLED_AN_PENTA`, you will see the config for it there: * Enable-OLED: - * What it does: Enabled the optional SPI driven OLED that can be mounted to the 7-pin female header + * What it does: Enables the optional SPI driven OLED that can be mounted to the 7-pin female header. Won't work with Ethernet, read above. * Possible values: Enabled/Disabled * Default: Disabled * OLED-Use-Progress-Bars: @@ -60,10 +65,15 @@ Navigate to the "Config" and then to the "Usermods" section. If you compiled WLE * Possible values: Enabled/Disabled * Default: Disabled * Enable-SHT30-Temp-Humidity-Sensor: - * What it does: Enabled the onboard SHT30 temperature and humidity sensor + * What it does: Enables the onboard SHT30 temperature and humidity sensor * Possible values: Enabled/Disabled * Default: Disabled ## Change log +2021-12 +* Adjusted IO layout to match An-Penta v1r1 2021-10 -* First implementation. \ No newline at end of file +* First implementation. + +## Credits +ezcGman | Andy: Find me on the Intermit.Tech (QuinLED) Discord server: https://discord.gg/WdbAauG \ No newline at end of file diff --git a/usermods/seven_segment_display_reloaded/readme.md b/usermods/seven_segment_display_reloaded/readme.md new file mode 100644 index 00000000..09479754 --- /dev/null +++ b/usermods/seven_segment_display_reloaded/readme.md @@ -0,0 +1,129 @@ +# Seven Segment Display Reloaded + +Usermod that uses the overlay feature to create a configurable seven segment display. +Optimized for maximum configurability and use with seven segment clocks by parallyze (https://www.instructables.com/member/parallyze/instructables/) +Very loosely based on the existing usermod "seven segment display". + + +## Installation + +Add the compile-time option `-D USERMOD_SSDR` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_SSDR` in `my_config.h`. + +For the auto brightness option, the usermod SN_Photoresistor has to be installed as well. See SN_Photoresistor/readme.md for instructions. + +## Settings +All settings can be controlled the usermod setting page. +Part of the settings can be controlled through MQTT with a raw payload or through a json request to /json/state. + +### enabled +Enables/disables this overlay usermod + +### inverted +Enables the inverted mode in which the background should be enabled and the digits should be black (leds off) + +### Colon-blinking +Enables the blinking colon(s) if they are defined + +### enable-auto-brightness +Enables the auto brightness feature. Can be only used with the usermod SN_Photoresistor installed. + +### auto-brightness-min / auto-brightness-max +The lux value calculated from usermod SN_Photoresistor will be mapped to the values defined here. +The mapping is 0 - 1000 lux will be mapped to auto-brightness-min - auto-brightness-max + +The mA current protection of WLED will override the calculated value if it is too high. + +### Display-Mask +Defines the type of the time/date display. +For example "H:m" (default) +- H - 00-23 hours +- h - 01-12 hours +- k - 01-24 hours +- m - 00-59 minutes +- s - 00-59 seconds +- d - 01-31 day of month +- M - 01-12 month +- y - 21 last two positions of year +- Y - 2021 year +- : for a colon + +### LED-Numbers +- LED-Numbers-Hours +- LED-Numbers-Minutes +- LED-Numbers-Seconds +- LED-Numbers-Colons +- LED-Numbers-Day +- LED-Numbers-Month +- LED-Numbers-Year + +See following example for usage. + + +## Example + +Example for Leds definition +``` + < A > +/\ /\ +F B +\/ \/ + < G > +/\ /\ +E C +\/ \/ + < D > +``` + +Leds or Range of Leds are seperated by a comma "," + +Segments are seperated by a semicolon ";" and are read as A;B;C;D;E;F;G + +Digits are seperated by colon ":" -> A;B;C;D;E;F;G:A;B;C;D;E;F;G + +Ranges are defined as lower to higher (lower first) + +For example, an clock definition for the following clock (https://www.instructables.com/Lazy-7-Quick-Build-Edition/) is + +- hour "59,46;47-48;50-51;52-53;54-55;57-58;49,56:0,13;1-2;4-5;6-7;8-9;11-12;3,10" + +- minute "37-38;39-40;42-43;44,31;32-33;35-36;34,41:21-22;23-24;26-27;28,15;16-17;19-20;18,25" + +or + +- hour "6,7;8,9;11,12;13,0;1,2;4,5;3,10:52,53;54,55;57,58;59,46;47,48;50,51;49,56" + +- minute "15,28;16,17;19,20;21,22;23,24;26,27;18,25:31,44;32,33;35,36;37,38;39,40;42,43;34,41" + +depending on the orientation. + +# The example detailed: +hour "59,46;47-48;50-51;52-53;54-55;57-58;49,56:0,13;1-2;4-5;6-7;8-9;11-12;3,10" + +there are two digits seperated by ":" + +- 59,46;47-48;50-51;52-53;54-55;57-58;49,56 +- 0,13;1-2;4-5;6-7;8-9;11-12;3,10 + +In the first digit, +the **segment A** consists of the leds number **59 and 46**., **segment B** consists of the leds number **47, 48** and so on + +The second digit starts again with **segment A** and leds **0 and 13**, **segment B** consists of the leds number **1 and 2** and so on + +### first digit of the hour +- Segment A: 59, 46 +- Segment B: 47, 48 +- Segment C: 50, 51 +- Segment D: 52, 53 +- Segment E: 54, 55 +- Segment F: 57, 58 +- Segment G: 49, 56 + +### second digit of the hour + +- Segment A: 0, 13 +- Segment B: 1, 2 +- Segment C: 4, 5 +- Segment D: 6, 7 +- Segment E: 8, 9 +- Segment F: 11, 12 +- Segment G: 3, 10 \ No newline at end of file diff --git a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h new file mode 100644 index 00000000..c4a64886 --- /dev/null +++ b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h @@ -0,0 +1,544 @@ +#pragma once + +#include "wled.h" + +class UsermodSSDR : public Usermod { + +//#define REFRESHTIME 497 + +private: + //Runtime variables. + unsigned long umSSDRLastRefresh = 0; + unsigned long umSSDRResfreshTime = 3000; + bool umSSDRDisplayTime = false; + bool umSSDRInverted = false; + bool umSSDRColonblink = true; + bool umSSDREnableLDR = false; + String umSSDRHours = ""; + String umSSDRMinutes = ""; + String umSSDRSeconds = ""; + String umSSDRColons = ""; + String umSSDRDays = ""; + String umSSDRMonths = ""; + String umSSDRYears = ""; + uint16_t umSSDRLength = 0; + uint16_t umSSDRBrightnessMin = 0; + uint16_t umSSDRBrightnessMax = 255; + + bool* umSSDRMask = 0; + + /*// H - 00-23 hours + // h - 01-12 hours + // k - 01-24 hours + // m - 00-59 minutes + // s - 00-59 seconds + // d - 01-31 day of month + // M - 01-12 month + // y - 21 last two positions of year + // Y - 2021 year + // : for a colon + */ + String umSSDRDisplayMask = "H:m"; //This should reflect physical equipment. + + /* Segment order, seen from the front: + + < A > + /\ /\ + F B + \/ \/ + < G > + /\ /\ + E C + \/ \/ + < D > + + */ + + uint8_t umSSDRNumbers[11][7] = { + // A B C D E F G + { 1, 1, 1, 1, 1, 1, 0 }, // 0 + { 0, 1, 1, 0, 0, 0, 0 }, // 1 + { 1, 1, 0, 1, 1, 0, 1 }, // 2 + { 1, 1, 1, 1, 0, 0, 1 }, // 3 + { 0, 1, 1, 0, 0, 1, 1 }, // 4 + { 1, 0, 1, 1, 0, 1, 1 }, // 5 + { 1, 0, 1, 1, 1, 1, 1 }, // 6 + { 1, 1, 1, 0, 0, 0, 0 }, // 7 + { 1, 1, 1, 1, 1, 1, 1 }, // 8 + { 1, 1, 1, 1, 0, 1, 1 } // 9 + }; + + //String to reduce flash memory usage + static const char _str_name[]; + static const char _str_ldrEnabled[]; + static const char _str_timeEnabled[]; + static const char _str_inverted[]; + static const char _str_colonblink[]; + static const char _str_displayMask[]; + static const char _str_hours[]; + static const char _str_minutes[]; + static const char _str_seconds[]; + static const char _str_colons[]; + static const char _str_days[]; + static const char _str_months[]; + static const char _str_years[]; + static const char _str_minBrightness[]; + static const char _str_maxBrightness[]; + +#ifdef USERMOD_ID_SN_PHOTORESISTOR + Usermod_SN_Photoresistor *ptr; +#else + void* ptr = nullptr; +#endif + + void _overlaySevenSegmentDraw() { + int displayMaskLen = static_cast(umSSDRDisplayMask.length()); + bool colonsDone = false; + _setAllFalse(); + for (int index = 0; index < displayMaskLen; index++) { + int timeVar = 0; + switch (umSSDRDisplayMask[index]) { + case 'h': + timeVar = hourFormat12(localTime); + _showElements(&umSSDRHours, timeVar, 0); + break; + case 'H': + timeVar = hour(localTime); + _showElements(&umSSDRHours, timeVar, 0); + break; + case 'k': + timeVar = hour(localTime) + 1; + _showElements(&umSSDRHours, timeVar, 0); + break; + case 'm': + timeVar = minute(localTime); + _showElements(&umSSDRMinutes, timeVar, 0); + break; + case 's': + timeVar = second(localTime); + _showElements(&umSSDRSeconds, timeVar, 0); + break; + case 'd': + timeVar = day(localTime); + _showElements(&umSSDRDays, timeVar, 0); + break; + case 'M': + timeVar = month(localTime); + _showElements(&umSSDRMonths, timeVar, 0); + break; + case 'y': + timeVar = second(localTime); + _showElements(&umSSDRYears, timeVar, 0); + break; + case 'Y': + timeVar = year(localTime); + _showElements(&umSSDRYears, timeVar, 0); + break; + case ':': + if (!colonsDone) { // only call _setColons once as all colons are printed when the first colon is found + _setColons(); + colonsDone = true; + } + break; + } + } + _setMaskToLeds(); + } + + void _setColons() { + if ( umSSDRColonblink ) { + if ( second(localTime) % 2 == 0 ) { + _showElements(&umSSDRColons, 0, 1); + } + } else { + _showElements(&umSSDRColons, 0, 1); + } + } + + void _showElements(String *map, int timevar, bool isColon) { + if (!(*map).equals("") && !(*map) == NULL) { + int length = String(timevar).length(); + bool addZero = false; + if (length == 1) { + length = 2; + addZero = true; + } + int timeArr[length]; + if(addZero) { + timeArr[1] = 0; + timeArr[0] = timevar; + } else { + int count = 0; + while (timevar) { + timeArr[count] = timevar%10; + timevar /= 10; + count++; + }; + } + + + int colonsLen = static_cast((*map).length()); + int count = 0; + int countSegments = 0; + int countDigit = 0; + bool range = false; + int lastSeenLedNr = 0; + + for (int index = 0; index < colonsLen; index++) { + switch ((*map)[index]) { + case '-': + lastSeenLedNr = _checkForNumber(count, index, map); + count = 0; + range = true; + break; + case ':': + _setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon); + count = 0; + range = false; + countDigit++; + countSegments = 0; + break; + case ';': + _setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon); + count = 0; + range = false; + countSegments++; + break; + case ',': + _setLeds(_checkForNumber(count, index, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon); + count = 0; + range = false; + break; + default: + count++; + break; + } + } + _setLeds(_checkForNumber(count, colonsLen, map), lastSeenLedNr, range, countSegments, timeArr[countDigit], isColon); + } + } + + void _setLeds(int lednr, int lastSeenLedNr, bool range, int countSegments, int number, bool colon) { + + if ((colon && umSSDRColonblink) || umSSDRNumbers[number][countSegments]) { + + if (range) { + for(int i = lastSeenLedNr; i <= lednr; i++) { + umSSDRMask[i] = true; + } + } else { + umSSDRMask[lednr] = true; + } + } + } + + void _setMaskToLeds() { + for(int i = 0; i <= umSSDRLength; i++) { + if ((!umSSDRInverted && !umSSDRMask[i]) || (umSSDRInverted && umSSDRMask[i])) { + strip.setPixelColor(i, 0x000000); + } + } + } + + void _setAllFalse() { + for(int i = 0; i <= umSSDRLength; i++) { + umSSDRMask[i] = false; + } + } + + int _checkForNumber(int count, int index, String *map) { + String number = (*map).substring(index - count, index); + return number.toInt(); + } + + void _publishMQTTint_P(const char *subTopic, int value) + { + if(mqtt == NULL) return; + + char buffer[64]; + char valBuffer[12]; + sprintf_P(buffer, PSTR("%s/%S/%S"), mqttDeviceTopic, _str_name, subTopic); + sprintf_P(valBuffer, PSTR("%d"), value); + mqtt->publish(buffer, 2, true, valBuffer); + } + + void _publishMQTTstr_P(const char *subTopic, String Value) + { + if(mqtt == NULL) return; + char buffer[64]; + sprintf_P(buffer, PSTR("%s/%S/%S"), mqttDeviceTopic, _str_name, subTopic); + mqtt->publish(buffer, 2, true, Value.c_str(), Value.length()); + } + + bool _cmpIntSetting_P(char *topic, char *payload, const char *setting, void *value) + { + if (strcmp_P(topic, setting) == 0) + { + *((int *)value) = strtol(payload, NULL, 10); + _publishMQTTint_P(setting, *((int *)value)); + return true; + } + return false; + } + + bool _handleSetting(char *topic, char *payload) { + if (_cmpIntSetting_P(topic, payload, _str_timeEnabled, &umSSDRDisplayTime)) { + return true; + } + if (_cmpIntSetting_P(topic, payload, _str_ldrEnabled, &umSSDREnableLDR)) { + return true; + } + if (_cmpIntSetting_P(topic, payload, _str_inverted, &umSSDRInverted)) { + return true; + } + if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) { + return true; + } + if (strcmp_P(topic, _str_displayMask) == 0) { + umSSDRDisplayMask = String(payload); + _publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask); + return true; + } + return false; + } + + void _updateMQTT() + { + _publishMQTTint_P(_str_timeEnabled, umSSDRDisplayTime); + _publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR); + _publishMQTTint_P(_str_inverted, umSSDRInverted); + _publishMQTTint_P(_str_colonblink, umSSDRColonblink); + + _publishMQTTstr_P(_str_hours, umSSDRHours); + _publishMQTTstr_P(_str_minutes, umSSDRMinutes); + _publishMQTTstr_P(_str_seconds, umSSDRSeconds); + _publishMQTTstr_P(_str_colons, umSSDRColons); + _publishMQTTstr_P(_str_days, umSSDRDays); + _publishMQTTstr_P(_str_months, umSSDRMonths); + _publishMQTTstr_P(_str_years, umSSDRYears); + _publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask); + + _publishMQTTint_P(_str_minBrightness, umSSDRBrightnessMin); + _publishMQTTint_P(_str_maxBrightness, umSSDRBrightnessMax); + } + + void _addJSONObject(JsonObject& root) { + JsonObject ssdrObj = root[FPSTR(_str_name)]; + if (ssdrObj.isNull()) { + ssdrObj = root.createNestedObject(FPSTR(_str_name)); + } + + ssdrObj[FPSTR(_str_timeEnabled)] = umSSDRDisplayTime; + ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR; + ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted; + ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink; + ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask; + ssdrObj[FPSTR(_str_hours)] = umSSDRHours; + ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes; + ssdrObj[FPSTR(_str_seconds)] = umSSDRSeconds; + ssdrObj[FPSTR(_str_colons)] = umSSDRColons; + ssdrObj[FPSTR(_str_days)] = umSSDRDays; + ssdrObj[FPSTR(_str_months)] = umSSDRMonths; + ssdrObj[FPSTR(_str_years)] = umSSDRYears; + ssdrObj[FPSTR(_str_minBrightness)] = umSSDRBrightnessMin; + ssdrObj[FPSTR(_str_maxBrightness)] = umSSDRBrightnessMax; + } + +public: + //Functions called by WLED + + /* + * setup() is called once at boot. WiFi is not yet connected at this point. + * You can use it to initialize variables, sensors or similar. + */ + void setup() { + umSSDRLength = strip.getLengthTotal(); + if (umSSDRMask != 0) { + umSSDRMask = (bool*) realloc(umSSDRMask, umSSDRLength * sizeof(bool)); + } else { + umSSDRMask = (bool*) malloc(umSSDRLength * sizeof(bool)); + } + _setAllFalse(); + + #ifdef USERMOD_ID_SN_PHOTORESISTOR + ptr = (Usermod_SN_Photoresistor*) usermods.lookup(USERMOD_ID_SN_PHOTORESISTOR); + #endif + DEBUG_PRINTLN(F("Setup done")); + } + + /* + * loop() is called continuously. Here you can check for events, read sensors, etc. + */ + void loop() { + if (!umSSDRDisplayTime || strip.isUpdating()) { + return; + } + #ifdef USERMOD_ID_SN_PHOTORESISTOR + if(bri != 0 && umSSDREnableLDR && (millis() - umSSDRLastRefresh > umSSDRResfreshTime)) { + if (ptr != nullptr) { + uint16_t lux = ptr->getLastLDRValue(); + uint16_t brightness = map(lux, 0, 1000, umSSDRBrightnessMin, umSSDRBrightnessMax); + if (bri != brightness) { + bri = brightness; + colorUpdated(1); + } + } + umSSDRLastRefresh = millis(); + } + #endif + } + + void handleOverlayDraw() { + if (umSSDRDisplayTime) { + _overlaySevenSegmentDraw(); + } + } + +/* + * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. + * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. + * Below it is shown how this could be used for e.g. a light sensor + */ + void addToJsonInfo(JsonObject& root) { + JsonObject user = root[F("u")]; + if (user.isNull()) { + user = root.createNestedObject(F("u")); + } + JsonArray enabled = user.createNestedArray("Time enabled"); + enabled.add(umSSDRDisplayTime); + JsonArray invert = user.createNestedArray("Time inverted"); + invert.add(umSSDRInverted); + JsonArray blink = user.createNestedArray("Blinking colon"); + blink.add(umSSDRColonblink); + JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled"); + ldrEnable.add(umSSDREnableLDR); + + } + + /* + * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). + * Values in the state object may be modified by connected clients + */ + void addToJsonState(JsonObject& root) { + JsonObject user = root[F("u")]; + if (user.isNull()) { + user = root.createNestedObject(F("u")); + } + _addJSONObject(user); + } + + /* + * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). + * Values in the state object may be modified by connected clients + */ + void readFromJsonState(JsonObject& root) { + JsonObject user = root[F("u")]; + if (!user.isNull()) { + JsonObject ssdrObj = user[FPSTR(_str_name)]; + umSSDRDisplayTime = ssdrObj[FPSTR(_str_timeEnabled)] | umSSDRDisplayTime; + umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR; + umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted; + umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink; + umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask; + } + } + + void onMqttConnect(bool sessionPresent) { + char subBuffer[48]; + if (mqttDeviceTopic[0] != 0) + { + _updateMQTT(); + //subscribe for sevenseg messages on the device topic + sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttDeviceTopic, _str_name); + mqtt->subscribe(subBuffer, 2); + } + + if (mqttGroupTopic[0] != 0) + { + //subcribe for sevenseg messages on the group topic + sprintf_P(subBuffer, PSTR("%s/%S/+/set"), mqttGroupTopic, _str_name); + mqtt->subscribe(subBuffer, 2); + } + } + + bool onMqttMessage(char *topic, char *payload) { + //If topic beings iwth sevenSeg cut it off, otherwise not our message. + size_t topicPrefixLen = strlen_P(PSTR("/wledSS/")); + if (strncmp_P(topic, PSTR("/wledSS/"), topicPrefixLen) == 0) { + topic += topicPrefixLen; + } else { + return false; + } + //We only care if the topic ends with /set + size_t topicLen = strlen(topic); + if (topicLen > 4 && + topic[topicLen - 4] == '/' && + topic[topicLen - 3] == 's' && + topic[topicLen - 2] == 'e' && + topic[topicLen - 1] == 't') + { + //Trim /set and handle it + topic[topicLen - 4] = '\0'; + _handleSetting(topic, payload); + } + return true; + } + + void addToConfig(JsonObject &root) { + _addJSONObject(root); + } + + bool readFromConfig(JsonObject &root) { + JsonObject top = root[FPSTR(_str_name)]; + + if (top.isNull()) { + DEBUG_PRINT(FPSTR(_str_name)); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; + } + + umSSDRDisplayTime = (top[FPSTR(_str_timeEnabled)] | umSSDRDisplayTime); + umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR); + umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted); + umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink); + + umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask; + umSSDRHours = top[FPSTR(_str_hours)] | umSSDRHours; + umSSDRMinutes = top[FPSTR(_str_minutes)] | umSSDRMinutes; + umSSDRSeconds = top[FPSTR(_str_seconds)] | umSSDRSeconds; + umSSDRColons = top[FPSTR(_str_colons)] | umSSDRColons; + umSSDRDays = top[FPSTR(_str_days)] | umSSDRDays; + umSSDRMonths = top[FPSTR(_str_months)] | umSSDRMonths; + umSSDRYears = top[FPSTR(_str_years)] | umSSDRYears; + umSSDRBrightnessMin = top[FPSTR(_str_minBrightness)] | umSSDRBrightnessMin; + umSSDRBrightnessMax = top[FPSTR(_str_maxBrightness)] | umSSDRBrightnessMax; + + DEBUG_PRINT(FPSTR(_str_name)); + DEBUG_PRINTLN(F(" config (re)loaded.")); + + return true; + } + /* + * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). + * This could be used in the future for the system to determine whether your usermod is installed. + */ + uint16_t getId() { + return USERMOD_ID_SSDR; + } +}; + +const char UsermodSSDR::_str_name[] PROGMEM = "UsermodSSDR"; +const char UsermodSSDR::_str_timeEnabled[] PROGMEM = "enabled"; +const char UsermodSSDR::_str_inverted[] PROGMEM = "inverted"; +const char UsermodSSDR::_str_colonblink[] PROGMEM = "Colon-blinking"; +const char UsermodSSDR::_str_displayMask[] PROGMEM = "Display-Mask"; +const char UsermodSSDR::_str_hours[] PROGMEM = "LED-Numbers-Hours"; +const char UsermodSSDR::_str_minutes[] PROGMEM = "LED-Numbers-Minutes"; +const char UsermodSSDR::_str_seconds[] PROGMEM = "LED-Numbers-Seconds"; +const char UsermodSSDR::_str_colons[] PROGMEM = "LED-Numbers-Colons"; +const char UsermodSSDR::_str_days[] PROGMEM = "LED-Numbers-Day"; +const char UsermodSSDR::_str_months[] PROGMEM = "LED-Numbers-Month"; +const char UsermodSSDR::_str_years[] PROGMEM = "LED-Numbers-Year"; +const char UsermodSSDR::_str_ldrEnabled[] PROGMEM = "enable-auto-brightness"; +const char UsermodSSDR::_str_minBrightness[] PROGMEM = "auto-brightness-min"; +const char UsermodSSDR::_str_maxBrightness[] PROGMEM = "auto-brightness-max"; diff --git a/wled00/FX.cpp b/wled00/FX.cpp index de7ecc5d..bad85e5a 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2091,7 +2091,7 @@ uint16_t WS2812FX::mode_colortwinkle() } } } - return FRAMETIME; + return FRAMETIME_FIXED; } @@ -2876,7 +2876,7 @@ uint16_t WS2812FX::candle(bool multi) } } - return FRAMETIME; + return FRAMETIME_FIXED; } uint16_t WS2812FX::mode_candle() @@ -3904,18 +3904,24 @@ uint16_t WS2812FX::mode_washing_machine(void) { Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e */ uint16_t WS2812FX::mode_blends(void) { - uint16_t dataSize = sizeof(uint32_t) * SEGLEN; // max segment length of 56 pixels on 16 segment ESP8266 + uint16_t pixelLen = SEGLEN > UINT8_MAX ? UINT8_MAX : SEGLEN; + uint16_t dataSize = sizeof(uint32_t) * (pixelLen + 1); // max segment length of 56 pixels on 16 segment ESP8266 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed uint32_t* pixels = reinterpret_cast(SEGENV.data); uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128); - uint8_t shift = (now * ((SEGMENT.speed >> 3) +1)) >> 8; + uint8_t shift = (now * ((SEGMENT.speed >> 3) +1)) >> 8; - for (int i = 0; i < SEGLEN; i++) { + for (int i = 0; i < pixelLen; i++) { pixels[i] = color_blend(pixels[i], color_from_palette(shift + quadwave8((i + 1) * 16), false, PALETTE_SOLID_WRAP, 255), blendSpeed); - setPixelColor(i, pixels[i]); shift += 3; } + uint16_t offset = 0; + for (int i = 0; i < SEGLEN; i++) { + setPixelColor(i, pixels[offset++]); + if (offset > pixelLen) offset = 0; + } + return FRAMETIME; } diff --git a/wled00/FX.h b/wled00/FX.h index 03429309..9088c514 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -48,7 +48,8 @@ /* Not used in all effects yet */ #define WLED_FPS 42 -#define FRAMETIME (1000/WLED_FPS) +#define FRAMETIME_FIXED (1000/WLED_FPS) +#define FRAMETIME _frametime /* each segment uses 52 bytes of SRAM memory, so if you're application fails because of insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ @@ -71,7 +72,7 @@ #define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS) #define LED_SKIP_AMOUNT 1 -#define MIN_SHOW_DELAY 15 +#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) #define NUM_COLORS 3 /* number of colors per segment */ #define SEGMENT _segments[_segment_index] @@ -655,6 +656,7 @@ class WS2812FX { 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), + setTargetFps(uint8_t fps), setPixelSegment(uint8_t n), deserializeMap(uint8_t n=0); @@ -685,6 +687,7 @@ class WS2812FX { getActiveSegmentsNum(void), //getFirstSelectedSegment(void), getMainSegmentId(void), + getTargetFps(void), gamma8(uint8_t), gamma8_cal(uint8_t, float), sin_gap(uint16_t), @@ -856,6 +859,8 @@ class WS2812FX { uint16_t _usedSegmentData = 0; uint16_t _transitionDur = 750; + uint8_t _targetFps = 42; + uint16_t _frametime = (1000/42); uint16_t _cumulativeFps = 2; bool diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 9ab75663..07299e7b 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -165,12 +165,12 @@ void WS2812FX::service() { _triggered = false; } -void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { +void IRAM_ATTR WS2812FX::setPixelColor(uint16_t n, uint32_t c) { setPixelColor(n, R(c), G(c), B(c), W(c)); } //used to map from segment index to physical pixel, taking into account grouping, offsets, reverse and mirroring -uint16_t WS2812FX::realPixelIndex(uint16_t i) { +uint16_t IRAM_ATTR WS2812FX::realPixelIndex(uint16_t i) { int16_t iGroup = i * SEGMENT.groupLength(); /* reverse just an individual segment */ @@ -187,7 +187,7 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) { return realIndex; } -void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) +void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) { if (SEGLEN) {//from segment uint16_t realIndex = realPixelIndex(i); @@ -350,6 +350,16 @@ uint16_t WS2812FX::getFps() { return _cumulativeFps +1; } +uint8_t WS2812FX::getTargetFps() { + return _targetFps; +} + +void WS2812FX::setTargetFps(uint8_t fps) { + if (fps > 0 && fps <= 120) _targetFps = fps; + //_targetFps = min(max((int)fps,1),120); + _frametime = 1000 / _targetFps; +} + /** * Forces the next frame to be computed on all active segments. */ @@ -775,7 +785,7 @@ void WS2812FX::setTransitionMode(bool t) /* * color blend function */ -uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) { +uint32_t IRAM_ATTR WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) { if(blend == 0) return color1; uint16_t blendmax = b16 ? 0xFFFF : 0xFF; if(blend == blendmax) return color2; @@ -878,13 +888,13 @@ void WS2812FX::blur(uint8_t blur_amount) } } -uint16_t WS2812FX::triwave16(uint16_t in) +uint16_t IRAM_ATTR WS2812FX::triwave16(uint16_t in) { if (in < 0x8000) return in *2; return 0xFFFF - (in - 0x8000)*2; } -uint8_t WS2812FX::sin_gap(uint16_t in) { +uint8_t IRAM_ATTR WS2812FX::sin_gap(uint16_t in) { if (in & 0x100) return 0; //if (in > 255) return 0; return sin8(in + 192); //correct phase shift of sine so that it starts and stops at 0 @@ -951,13 +961,13 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) { } -uint32_t WS2812FX::crgb_to_col(CRGB fastled) +uint32_t IRAM_ATTR WS2812FX::crgb_to_col(CRGB fastled) { return RGBW32(fastled.red, fastled.green, fastled.blue, 0); } -CRGB WS2812FX::col_to_crgb(uint32_t color) +CRGB IRAM_ATTR WS2812FX::col_to_crgb(uint32_t color) { CRGB fastled_col; fastled_col.red = R(color); @@ -1080,7 +1090,7 @@ void WS2812FX::handle_palette(void) * @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling) * @returns Single color from palette */ -uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) +uint32_t IRAM_ATTR WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) { if (SEGMENT.palette == 0 && mcol < 3) { uint32_t color = SEGCOLOR(mcol); diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 00f4993f..4cf56c79 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -288,7 +288,7 @@ class BusPwm : public Bus { if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) { deallocatePins(); return; } - _pins[i] = currentPin; // store only after allocatePin() succeeds + _pins[i] = currentPin; //store only after allocatePin() succeeds #ifdef ESP8266 pinMode(_pins[i], OUTPUT); #else @@ -396,7 +396,7 @@ class BusPwm : public Bus { private: uint8_t _pins[5] = {255, 255, 255, 255, 255}; - uint8_t _data[5] = {255, 255, 255, 255, 255}; + uint8_t _data[5] = {0}; #ifdef ARDUINO_ARCH_ESP32 uint8_t _ledcStart = 255; #endif diff --git a/wled00/button.cpp b/wled00/button.cpp index aef18c77..96cdfed5 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -20,7 +20,7 @@ void shortPressAction(uint8_t b) default: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break; } } else { - applyPreset(macroButton[b], CALL_MODE_BUTTON); + applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET); } // publish MQTT message @@ -39,7 +39,7 @@ void longPressAction(uint8_t b) default: bri += 8; colorUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action } } else { - applyPreset(macroLongPress[b], CALL_MODE_BUTTON); + applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET); } // publish MQTT message @@ -58,7 +58,7 @@ void doublePressAction(uint8_t b) default: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break; } } else { - applyPreset(macroDoublePress[b], CALL_MODE_BUTTON); + applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET); } // publish MQTT message @@ -105,12 +105,12 @@ void handleSwitch(uint8_t b) if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce) if (!buttonPressedBefore[b]) { // on -> off - if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON); + if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET); else { //turn on if (!bri) {toggleOnOff(); colorUpdated(CALL_MODE_BUTTON);} } } else { // off -> on - if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON); + if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET); else { //turn off if (bri) {toggleOnOff(); colorUpdated(CALL_MODE_BUTTON);} } diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index ea5a583c..88b40a12 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -88,6 +88,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(cctFromRgb, hw_led[F("cr")]); CJSON(strip.cctBlending, hw_led[F("cb")]); Bus::setCCTBlend(strip.cctBlending); + strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS + //strip.setTargetFps(hw_led["fps"].isNull() ? WLED_FPS : hw_led["fps"].as()); JsonArray ins = hw_led["ins"]; @@ -546,8 +548,9 @@ void serializeConfig() { hw_led[F("ledma")] = strip.milliampsPerLed; hw_led["cct"] = correctWB; hw_led[F("cr")] = cctFromRgb; - hw_led[F("cb")] = strip.cctBlending; - hw_led[F("rgbwm")] = Bus::getAutoWhiteMode(); + hw_led[F("cb")] = strip.cctBlending; + hw_led["fps"] = strip.getTargetFps(); + hw_led[F("rgbwm")] = Bus::getAutoWhiteMode(); JsonArray hw_led_ins = hw_led.createNestedArray("ins"); diff --git a/wled00/const.h b/wled00/const.h index 902521ae..22d57a2f 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -64,6 +64,7 @@ #define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 //Usermod "usermod_v2_seven_segment_display.h" #define USERMOD_RGB_ROTARY_ENCODER 22 //Usermod "rgb-rotary-encoder.h" #define USERMOD_ID_QUINLED_AN_PENTA 23 //Usermod "quinled-an-penta.h" +#define USERMOD_ID_SSDR 24 //Usermod "usermod_v2_seven_segment_display_reloaded.h" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot @@ -74,7 +75,7 @@ //Notifier callMode #define CALL_MODE_INIT 0 //no updates on init, can be used to disable updates #define CALL_MODE_DIRECT_CHANGE 1 -#define CALL_MODE_BUTTON 2 +#define CALL_MODE_BUTTON 2 //default button actions applied to selected segments #define CALL_MODE_NOTIFICATION 3 #define CALL_MODE_NIGHTLIGHT 4 #define CALL_MODE_NO_NOTIFY 5 @@ -84,6 +85,7 @@ #define CALL_MODE_BLYNK 9 #define CALL_MODE_ALEXA 10 #define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only +#define CALL_MODE_BUTTON_PRESET 12 //button/IR JSON preset/macro //RGB to RGBW conversion mode #define RGBW_MODE_MANUAL_ONLY 0 //No automatic white channel calculation. Manual white channel slider diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 53f26fab..8446ed34 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -563,6 +563,7 @@ ${i+1}:
+ Target refresh rate: FPS
Config template:

diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 40dfa507..53b3fb81 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -162,7 +162,7 @@ const uint8_t PAGE_settings_wifi[] PROGMEM = { // Autogenerated from wled00/data/settings_leds.htm, do not edit!! -const uint16_t PAGE_settings_leds_length = 6524; +const uint16_t PAGE_settings_leds_length = 6549; const uint8_t PAGE_settings_leds[] PROGMEM = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xa5, 0x3c, 0x6b, 0x57, 0xdb, 0xc8, 0x92, 0xdf, 0xfd, 0x2b, 0x9a, 0x9e, 0x19, 0x22, 0x5d, 0x84, 0x91, 0xfc, 0x60, 0x88, 0x6d, 0x99, @@ -566,12 +566,14 @@ const uint8_t PAGE_settings_leds[] PROGMEM = { 0x4c, 0x59, 0xaf, 0xc4, 0xc7, 0x00, 0xc4, 0x78, 0x8a, 0xd9, 0x0c, 0x6b, 0xd9, 0x69, 0xf4, 0x08, 0x83, 0xcd, 0x1d, 0xe2, 0xaf, 0xe8, 0x21, 0x2c, 0x78, 0x62, 0x8b, 0x84, 0xe0, 0x48, 0x73, 0xc7, 0x5a, 0x68, 0xf0, 0x10, 0x4f, 0xdf, 0xac, 0x41, 0x97, 0x28, 0x34, 0x2e, 0x7e, 0x89, 0x47, 0x2e, - 0xef, 0x19, 0x6c, 0x8d, 0xd7, 0xd9, 0xf9, 0xc6, 0xd1, 0x18, 0x4f, 0x31, 0x86, 0x23, 0x7f, 0x4c, - 0x52, 0x3e, 0x9d, 0xe1, 0xa2, 0x96, 0x66, 0x64, 0x95, 0x3c, 0x25, 0xab, 0xbd, 0x31, 0x27, 0x2b, - 0xa6, 0x64, 0x59, 0xfe, 0x48, 0xf5, 0xf6, 0x61, 0xb6, 0xcb, 0x58, 0x83, 0x0c, 0x5f, 0x04, 0xdf, - 0xb2, 0x7c, 0x0c, 0x3b, 0xc0, 0xe5, 0x09, 0xdf, 0x1b, 0x7a, 0xcb, 0xbb, 0x5a, 0xcb, 0x47, 0xd8, - 0x2e, 0x2f, 0x54, 0x50, 0xf2, 0x8f, 0xe2, 0x68, 0xb2, 0x47, 0xd8, 0x68, 0xc7, 0xae, 0x3b, 0xfe, - 0x0d, 0xa6, 0xff, 0x03, 0xc4, 0xb5, 0x9b, 0x7d, 0x93, 0x49, 0x00, 0x00 + 0x5f, 0x28, 0x65, 0xf0, 0xb1, 0x3c, 0xe8, 0x82, 0x1d, 0x1e, 0xfe, 0x36, 0x41, 0x6b, 0x87, 0xe2, + 0xd4, 0xb2, 0x34, 0xfe, 0x23, 0xa4, 0xf1, 0x95, 0x5c, 0xd0, 0x1f, 0x6f, 0xfa, 0x3b, 0xeb, 0x12, + 0x71, 0x7a, 0x72, 0x34, 0xc6, 0x33, 0x92, 0xe1, 0xc8, 0x1f, 0x93, 0x94, 0x4f, 0x67, 0x41, 0x91, + 0x7e, 0xa5, 0x3c, 0xe1, 0xab, 0xbd, 0x31, 0xe3, 0x2b, 0x26, 0x7c, 0x59, 0x76, 0x4a, 0xf5, 0xe6, + 0x64, 0xb6, 0x87, 0x59, 0x83, 0xfa, 0x41, 0x84, 0xf6, 0xb2, 0x6c, 0x0f, 0xfb, 0xcb, 0xe5, 0xe9, + 0xe4, 0x1b, 0x3a, 0xd7, 0xbb, 0x1a, 0xd7, 0x47, 0xd8, 0x8c, 0x2f, 0xd4, 0x67, 0xf2, 0x4f, 0xee, + 0x68, 0xb2, 0x47, 0xd8, 0xc6, 0xc7, 0x9e, 0x3e, 0xfe, 0x85, 0xa7, 0xff, 0x03, 0xeb, 0x8d, 0x55, + 0xa8, 0xf1, 0x49, 0x00, 0x00 }; diff --git a/wled00/improv.cpp b/wled00/improv.cpp index 56ee9e0a..5a30cc55 100644 --- a/wled00/improv.cpp +++ b/wled00/improv.cpp @@ -189,7 +189,7 @@ void sendImprovInfoResponse() { out[11] = 4; //Firmware len ("WLED") out[12] = 'W'; out[13] = 'L'; out[14] = 'E'; out[15] = 'D'; uint8_t lengthSum = 17; - uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.0-b5/%i"),VERSION); + uint8_t vlen = sprintf_P(out+lengthSum,PSTR("0.13.0-b6/%i"),VERSION); out[16] = vlen; lengthSum += vlen; uint8_t hlen = 7; #ifdef ESP8266 diff --git a/wled00/ir.cpp b/wled00/ir.cpp index a73a0db5..544b0d30 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -72,7 +72,7 @@ void decBrightness() void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) { byte prevError = errorFlag; - if (!applyPreset(presetID, CALL_MODE_BUTTON)) { + if (!applyPreset(presetID, CALL_MODE_BUTTON_PRESET)) { effectCurrent = effectID; effectPalette = paletteID; errorFlag = prevError; //clear error 12 from non-existent preset @@ -90,7 +90,7 @@ bool decodeIRCustom(uint32_t code) { //just examples, feel free to modify or remove case IRCUSTOM_ONOFF : toggleOnOff(); break; - case IRCUSTOM_MACRO1 : applyPreset(1, CALL_MODE_BUTTON); break; + case IRCUSTOM_MACRO1 : applyPreset(1, CALL_MODE_BUTTON_PRESET); break; default: return false; } @@ -638,7 +638,7 @@ void decodeIRJson(uint32_t code) colorUpdated(CALL_MODE_BUTTON); } else if (!jsonCmdObj.isNull()) { // command is JSON object - deserializeState(jsonCmdObj, CALL_MODE_BUTTON); + deserializeState(jsonCmdObj, CALL_MODE_BUTTON_PRESET); } releaseJSONBufferLock(); } diff --git a/wled00/json.cpp b/wled00/json.cpp index 746d6a22..81ebabb2 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -526,7 +526,7 @@ void serializeInfo(JsonObject root) } leds[F("pwr")] = strip.currentMilliamps; - leds[F("fps")] = strip.getFps(); + leds["fps"] = strip.getFps(); leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0; leds[F("maxseg")] = strip.getMaxSegments(); //leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config diff --git a/wled00/led.cpp b/wled00/led.cpp index 9fbe2b72..311ec75e 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -76,10 +76,11 @@ bool colorChanged() 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 + // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa 11: ws send only 12: button preset if (callMode != CALL_MODE_INIT && callMode != CALL_MODE_DIRECT_CHANGE && - callMode != CALL_MODE_NO_NOTIFY) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments + callMode != CALL_MODE_NO_NOTIFY && + callMode != CALL_MODE_BUTTON_PRESET) strip.applyToAllSelected = true; //if not from JSON api, which directly sets segments bool someSel = false; diff --git a/wled00/playlist.cpp b/wled00/playlist.cpp index 1e9d472d..0105f782 100644 --- a/wled00/playlist.cpp +++ b/wled00/playlist.cpp @@ -119,6 +119,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) { void handlePlaylist() { static unsigned long presetCycledTime = 0; + // if fileDoc is not null JSON buffer is in use so just quit if (currentPlaylist < 0 || playlistEntries == nullptr || fileDoc != nullptr) return; if (millis() - presetCycledTime > (100*playlistEntryDur)) { diff --git a/wled00/set.cpp b/wled00/set.cpp index b5f99b8c..f73c1ea4 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -77,6 +77,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) strip.cctBlending = request->arg(F("CB")).toInt(); Bus::setCCTBlend(strip.cctBlending); Bus::setAutoWhiteMode(request->arg(F("AW")).toInt()); + strip.setTargetFps(request->arg(F("FR")).toInt()); for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) { char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin diff --git a/wled00/udp.cpp b/wled00/udp.cpp index 0d04b7ed..85b36601 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -17,6 +17,7 @@ void notify(byte callMode, bool followUp) case CALL_MODE_INIT: return; case CALL_MODE_DIRECT_CHANGE: if (!notifyDirect) return; break; case CALL_MODE_BUTTON: if (!notifyButton) return; break; + case CALL_MODE_BUTTON_PRESET: if (!notifyButton) return; break; case CALL_MODE_NIGHTLIGHT: if (!notifyDirect) return; break; case CALL_MODE_HUE: if (!notifyHue) return; break; case CALL_MODE_PRESET_CYCLE: if (!notifyDirect) return; break; diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 0a16381f..caceae45 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -104,6 +104,10 @@ #include "../usermods/seven_segment_display/usermod_v2_seven_segment_display.h" #endif +#ifdef USERMOD_SSDR +#include "../usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h" +#endif + #ifdef QUINLED_AN_PENTA #include "../usermods/quinled-an-penta/quinled-an-penta.h" #endif @@ -200,6 +204,10 @@ void registerUsermods() usermods.add(new SevenSegmentDisplay()); #endif + #ifdef USERMOD_SSDR + usermods.add(new UsermodSSDR()); + #endif + #ifdef QUINLED_AN_PENTA usermods.add(new QuinLEDAnPentaUsermod()); #endif diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 9ea0bedd..a8638127 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -336,6 +336,7 @@ void getSettingsJS(byte subPage, char* dest) sappend('c',SET_F("CCT"),correctWB); sappend('c',SET_F("CR"),cctFromRgb); sappend('v',SET_F("CB"),strip.cctBlending); + sappend('v',SET_F("FR"),strip.getTargetFps()); sappend('v',SET_F("AW"),Bus::getAutoWhiteMode()); for (uint8_t s=0; s < busses.getNumBusses(); s++) {