diff --git a/.gitignore b/.gitignore index a95dd5ac2..7c0dd1e5b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,10 @@ /wled00/Release /wled00/extLibs /platformio_override.ini -/wled00/my_config.h +/wled00/my_config.h /build_output .DS_Store .gitignore .clang-format node_modules +.idea diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aabf97fd..f140367c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,52 @@ ### Development versions after 0.11.1 release +#### Build 2103130 + +- Added options for Auto Node discovery +- Optimized strings (no string both F() and raw) + +#### Build 2103090 + +- Added Auto Node discovery (PR #1683) +- Added tooltips to quick color selectors for accessibility + +#### Build 2103060 + +- Auto start field population in bus config + +#### Build 2103050 + +- Fixed incorrect over-memory indication in LED settings on ESP32 + +#### Build 2103041 + +- Added destructor for BusPwm (fixes #1789) + +#### Build 2103040 + +- Fixed relay mode inverted when upgrading from 0.11.0 +- Fixed no more than 2 pins per bus configurable in UI +- Changed to non-linear IR brightness steps (PR #1742) +- Fixed various warnings (PR #1744) +- Added UDP DNRGBW Mode (PR #1704) +- Added dynamic LED mapping with ledmap.json file (PR #1738) +- Added support for QuinLED-ESP32-Ethernet board +- Added support for WESP32 ethernet board (PR #1764) +- Added Caching for main UI (PR #1704) +- Added Tetrix mode (PR #1729) +- Added memory check on Bus creation + +#### Build 2102050 + +- Version bump to 0.12.0-a0 "Hikari" +- Added FPS indication in info +- Bumped max outputs from 7 to 10 busses for ESP32 + +#### Build 2101310 + +- First alpha configurable multipin + #### Build 2101130 - Added color transitions for all segments and slots and for segment brightness diff --git a/package.json b/package.json index ba3add2f4..b9269cfde 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.11.1", + "version": "0.12.0-a0", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { diff --git a/pio/gzip-firmware.py b/pio-scripts/gzip-firmware.py similarity index 100% rename from pio/gzip-firmware.py rename to pio-scripts/gzip-firmware.py diff --git a/pio/name-firmware.py b/pio-scripts/name-firmware.py similarity index 100% rename from pio/name-firmware.py rename to pio-scripts/name-firmware.py diff --git a/pio/obj-dump.py b/pio-scripts/obj-dump.py similarity index 100% rename from pio/obj-dump.py rename to pio-scripts/obj-dump.py diff --git a/pio/strip-floats.py b/pio-scripts/strip-floats.py similarity index 100% rename from pio/strip-floats.py rename to pio-scripts/strip-floats.py diff --git a/pio/user_config_copy.py b/pio-scripts/user_config_copy.py similarity index 100% rename from pio/user_config_copy.py rename to pio-scripts/user_config_copy.py diff --git a/platformio.ini b/platformio.ini index 4c78f09aa..19c18bab4 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, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4, custom_LEDPIN_3, custom32_LEDPIN_16, custom32_APA102 +; default_envs = nodemcuv2, esp01_1m_full, esp32dev # Single binaries (uncomment your board) ; default_envs = nodemcuv2 @@ -95,16 +95,6 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT # This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m). # ------------------------------------------------------------------------------ build_flags = - -Wno-switch - -Wno-deprecated-declarations - -Wno-write-strings - -Wno-unused-variable - -Wno-unused-value - -Wno-sign-compare - -Wno-unused-but-set-variable - -Wno-return-type - -Wno-sequence-point - -Wno-narrowing -DMQTT_MAX_PACKET_SIZE=1024 -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DBEARSSL_SSL_BASIC @@ -121,9 +111,6 @@ build_flags = ; -D USERMOD_SENSORSTOMQTT build_unflags = - -Wall - -Wreorder - -Wdeprecated-declarations # enables all features for travis CI build_flags_all_features = @@ -159,15 +146,15 @@ build_flags = -DMIMETYPE_MINIMAL [esp32] -build_flags = -w -g +build_flags = -g -DARDUINO_ARCH_ESP32 -DCONFIG_LITTLEFS_FOR_IDF_3_2 [scripts_defaults] -extra_scripts = pio/name-firmware.py - pio/gzip-firmware.py - pio/strip-floats.py - pio/user_config_copy.py +extra_scripts = pio-scripts/name-firmware.py + pio-scripts/gzip-firmware.py + pio-scripts/strip-floats.py + pio-scripts/user_config_copy.py # ------------------------------------------------------------------------------ # COMMON SETTINGS: @@ -197,7 +184,7 @@ lib_deps = AsyncTCP @ 1.0.3 IRremoteESP8266 @ 2.7.3 https://github.com/lorol/LITTLEFS.git - https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.0 + https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.2 #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line #TFT_eSPI #For use SSD1306 OLED display uncomment following diff --git a/tools/cdata.js b/tools/cdata.js index 34f37255b..201193f6f 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -398,6 +398,14 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()====="; method: "plaintext", filter: "html-minify", }, + { + file: "liveviewws.htm", + name: "PAGE_liveviewws", + prepend: "=====(", + append: ")=====", + method: "plaintext", + filter: "html-minify", + }, { file: "404.htm", name: "PAGE_404", diff --git a/usermods/Animated_Staircase/Animated_Staircase.h b/usermods/Animated_Staircase/Animated_Staircase.h new file mode 100644 index 000000000..9717589da --- /dev/null +++ b/usermods/Animated_Staircase/Animated_Staircase.h @@ -0,0 +1,427 @@ +/* + * Usermod for detecting people entering/leaving a staircase and switching the + * staircase on/off. + * + * Edit the Animated_Staircase_config.h file to compile this usermod for your + * specific configuration. + * + * See the accompanying README.md file for more info. + */ +#pragma once +#include "wled.h" +#include "Animated_Staircase_config.h" +#define USERMOD_ID_ANIMATED_STAIRCASE 1011 + +/* Initial configuration (available in API and stored in flash) */ +bool enabled = true; // Enable this usermod +unsigned long segment_delay_ms = 150; // Time between switching each segment +unsigned long on_time_ms = 5 * 1000; // The time for the light to stay on +#ifndef TOP_PIR_PIN +unsigned int topMaxTimeUs = 1749; // default echo timout, top +#endif +#ifndef BOTTOM_PIR_PIN +unsigned int bottomMaxTimeUs = 1749; // default echo timout, bottom +#endif + +// Time between checking of the sensors +const int scanDelay = 50; + +class Animated_Staircase : public Usermod { + private: + // Lights on or off. + // Flipping this will start a transition. + bool on = false; + + // Swipe direction for current transition +#define SWIPE_UP true +#define SWIPE_DOWN false + bool swipe = SWIPE_UP; + + // Indicates which Sensor was seen last (to determine + // the direction when swiping off) +#define LOWER false +#define UPPER true + bool lastSensor = LOWER; + + // Time of the last transition action + unsigned long lastTime = 0; + + // Time of the last sensor check + unsigned long lastScanTime = 0; + + // Last time the lights were switched on or off + unsigned long lastSwitchTime = 0; + + // segment id between onIndex and offIndex are on. + // controll the swipe by setting/moving these indices around. + // onIndex must be less than or equal to offIndex + byte onIndex = 0; + byte offIndex = 0; + + // The maximum number of configured segments. + // Dynamically updated based on user configuration. + byte maxSegmentId = 1; + byte mainSegmentId = 0; + + bool saveState = false; + + // These values are used by the API to read the + // last sensor state, or trigger a sensor + // through the API + bool topSensorRead = false; + bool topSensorWrite = false; + bool bottomSensorRead = false; + bool bottomSensorWrite = false; + + void updateSegments() { + mainSegmentId = strip.getMainSegmentId(); + WS2812FX::Segment mainsegment = strip.getSegment(mainSegmentId); + WS2812FX::Segment* segments = strip.getSegments(); + for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) { + if (!segments->isActive()) { + maxSegmentId = i - 1; + break; + } + + if (i >= onIndex && i < offIndex) { + segments->setOption(SEG_OPTION_ON, 1, 1); + + // We may need to copy mode and colors from segment 0 to make sure + // changes are propagated even when the config is changed during a wipe + // segments->mode = mainsegment.mode; + // segments->colors[0] = mainsegment.colors[0]; + } else { + segments->setOption(SEG_OPTION_ON, 0, 1); + } + // Always mark segments as "transitional", we are animating the staircase + segments->setOption(SEG_OPTION_TRANSITIONAL, 1, 1); + } + colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE); + } + + /* + * Detects if an object is within ultrasound range. + * signalPin: The pin where the pulse is sent + * echoPin: The pin where the echo is received + * maxTimeUs: Detection timeout in microseconds. If an echo is + * received within this time, an object is detected + * and the function will return true. + * + * The speed of sound is 343 meters per second at 20 degress Celcius. + * Since the sound has to travel back and forth, the detection + * distance for the sensor in cm is (0.0343 * maxTimeUs) / 2. + * + * For practical reasons, here are some useful distances: + * + * Distance = maxtime + * 5 cm = 292 uS + * 10 cm = 583 uS + * 20 cm = 1166 uS + * 30 cm = 1749 uS + * 50 cm = 2915 uS + * 100 cm = 5831 uS + */ + bool ultrasoundRead(uint8_t signalPin, + uint8_t echoPin, + unsigned int maxTimeUs) { + digitalWrite(signalPin, HIGH); + delayMicroseconds(10); + digitalWrite(signalPin, LOW); + return pulseIn(echoPin, HIGH, maxTimeUs) > 0; + } + + void checkSensors() { + if ((millis() - lastScanTime) > scanDelay) { + lastScanTime = millis(); + +#ifdef BOTTOM_PIR_PIN + bottomSensorRead = bottomSensorWrite || (digitalRead(BOTTOM_PIR_PIN) == HIGH); +#else + bottomSensorRead = bottomSensorWrite || ultrasoundRead(BOTTOM_TRIGGER_PIN, BOTTOM_ECHO_PIN, bottomMaxTimeUs); +#endif + +#ifdef TOP_PIR_PIN + topSensorRead = topSensorWrite || (digitalRead(TOP_PIR_PIN) == HIGH); +#else + topSensorRead = topSensorWrite || ultrasoundRead(TOP_TRIGGER_PIN, TOP_ECHO_PIN, topMaxTimeUs); +#endif + + // Values read, reset the flags for next API call + topSensorWrite = false; + bottomSensorWrite = false; + + if (topSensorRead != bottomSensorRead) { + lastSwitchTime = millis(); + + if (on) { + lastSensor = topSensorRead; + } else { + // If the bottom sensor triggered, we need to swipe up, ON + swipe = bottomSensorRead; + + if (swipe) { + Serial.println("ON -> Swipe up."); + } else { + Serial.println("ON -> Swipe down."); + } + + if (onIndex == offIndex) { + // Position the indices for a correct on-swipe + if (swipe == SWIPE_UP) { + onIndex = mainSegmentId; + } else { + onIndex = maxSegmentId+1; + } + offIndex = onIndex; + } + on = true; + } + } + } + } + + void autoPowerOff() { + if (on && ((millis() - lastSwitchTime) > on_time_ms)) { + // Swipe OFF in the direction of the last sensor detection + swipe = lastSensor; + on = false; + + if (swipe) { + Serial.println("OFF -> Swipe up."); + } else { + Serial.println("OFF -> Swipe down."); + } + } + } + + void updateSwipe() { + if ((millis() - lastTime) > segment_delay_ms) { + lastTime = millis(); + + byte oldOnIndex = onIndex; + byte oldOffIndex = offIndex; + + if (on) { + // Turn on all segments + onIndex = MAX(mainSegmentId, onIndex - 1); + offIndex = MIN(maxSegmentId + 1, offIndex + 1); + } else { + if (swipe == SWIPE_UP) { + onIndex = MIN(offIndex, onIndex + 1); + } else { + offIndex = MAX(onIndex, offIndex - 1); + } + } + + updateSegments(); + } + } + + void writeSettingsToJson(JsonObject& root) { + JsonObject staircase = root["staircase"]; + if (staircase.isNull()) { + staircase = root.createNestedObject("staircase"); + } + staircase["enabled"] = enabled; + staircase["segment-delay-ms"] = segment_delay_ms; + staircase["on-time-s"] = on_time_ms / 1000; + +#ifdef TOP_TRIGGER_PIN + staircase["top-echo-us"] = topMaxTimeUs; +#endif +#ifdef BOTTOM_TRIGGER_PIN + staircase["bottom-echo-us"] = bottomMaxTimeUs; +#endif + } + + void writeSensorsToJson(JsonObject& root) { + JsonObject staircase = root["staircase"]; + if (staircase.isNull()) { + staircase = root.createNestedObject("staircase"); + } + staircase["top-sensor"] = topSensorRead; + staircase["bottom-sensor"] = bottomSensorRead; + } + + bool readSettingsFromJson(JsonObject& root) { + JsonObject staircase = root["staircase"]; + bool changed = false; + + bool shouldEnable = staircase["enabled"] | enabled; + if (shouldEnable != enabled) { + enable(shouldEnable); + changed = true; + } + + unsigned long c_segment_delay_ms = staircase["segment-delay-ms"] | segment_delay_ms; + if (c_segment_delay_ms != segment_delay_ms) { + segment_delay_ms = c_segment_delay_ms; + changed = true; + } + + unsigned long c_on_time_ms = (staircase["on-time-s"] | (on_time_ms / 1000)) * 1000; + if (c_on_time_ms != on_time_ms) { + on_time_ms = c_on_time_ms; + changed = true; + } + +#ifdef TOP_TRIGGER_PIN + unsigned int c_topMaxTimeUs = staircase["top-echo-us"] | topMaxTimeUs; + if (c_topMaxTimeUs != topMaxTimeUs) { + topMaxTimeUs = c_topMaxTimeUs; + changed = true; + } +#endif +#ifdef BOTTOM_TRIGGER_PIN + unsigned int c_bottomMaxTimeUs = staircase["bottom-echo-us"] | bottomMaxTimeUs; + if (c_bottomMaxTimeUs != bottomMaxTimeUs) { + bottomMaxTimeUs = c_bottomMaxTimeUs; + changed = true; + } +#endif + + return changed; + } + + void readSensorsFromJson(JsonObject& root) { + JsonObject staircase = root["staircase"]; + bottomSensorWrite = bottomSensorRead || (staircase["bottom-sensor"].as()); + topSensorWrite = topSensorRead || (staircase["top-sensor"].as()); + } + + void enable(bool enable) { + if (enable) { + Serial.println("Animated Staircase enabled."); + Serial.print("Delay between steps: "); + Serial.print(segment_delay_ms, DEC); + Serial.print(" milliseconds.\nStairs switch off after: "); + Serial.print(on_time_ms / 1000, DEC); + Serial.println(" seconds."); + +#ifdef BOTTOM_PIR_PIN + pinMode(BOTTOM_PIR_PIN, INPUT); +#else + pinMode(BOTTOM_TRIGGER_PIN, OUTPUT); + pinMode(BOTTOM_ECHO_PIN, INPUT); +#endif + +#ifdef TOP_PIR_PIN + pinMode(TOP_PIR_PIN, INPUT); +#else + pinMode(TOP_TRIGGER_PIN, OUTPUT); + pinMode(TOP_ECHO_PIN, INPUT); +#endif + } else { + // Restore segment options + WS2812FX::Segment mainsegment = strip.getSegment(mainSegmentId); + WS2812FX::Segment* segments = strip.getSegments(); + for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) { + if (!segments->isActive()) { + maxSegmentId = i - 1; + break; + } + segments->setOption(SEG_OPTION_ON, 1, 1); + } + colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE); + Serial.println("Animated Staircase disabled."); + } + enabled = enable; + } + + public: + void setup() { enable(enabled); } + + void loop() { + // Write changed settings from to flash (see readFromJsonState()) + if (saveState) { + serializeConfig(); + saveState = false; + } + + if (!enabled) { + return; + } + + checkSensors(); + autoPowerOff(); + updateSwipe(); + + } + + uint16_t getId() { return USERMOD_ID_ANIMATED_STAIRCASE; } + + /* + * Shows configuration settings to the json API. This object looks like: + * + * "staircase" : { + * "enabled" : true + * "segment-delay-ms" : 150, + * "on-time-s" : 5 + * } + * + */ + void addToJsonState(JsonObject& root) { + writeSettingsToJson(root); + writeSensorsToJson(root); + Serial.println("Staircase config exposed in API."); + } + + /* + * Reads configuration settings from the json API. + * See void addToJsonState(JsonObject& root) + */ + void readFromJsonState(JsonObject& root) { + // The call to serializeConfig() must be done in the main loop, + // so we set a flag to signal the main loop to save state. + saveState = readSettingsFromJson(root); + readSensorsFromJson(root); + Serial.println("Staircase config read from API."); + } + + /* + * Writes the configuration to internal flash memory. + */ + void addToConfig(JsonObject& root) { + writeSettingsToJson(root); + Serial.println("Staircase config saved."); + } + + /* + * Reads the configuration to internal flash memory before setup() is called. + */ + void readFromConfig(JsonObject& root) { + readSettingsFromJson(root); + Serial.println("Staircase config loaded."); + } + + /* + * Shows the delay between steps and power-off time in the "info" + * tab of the web-UI. + */ + void addToJsonInfo(JsonObject& root) { + JsonObject staircase = root["u"]; + if (staircase.isNull()) { + staircase = root.createNestedObject("u"); + } + + if (enabled) { + JsonArray usermodEnabled = + staircase.createNestedArray("Staircase enabled"); // name + usermodEnabled.add("yes"); // value + + JsonArray segmentDelay = + staircase.createNestedArray("Delay between stairs"); // name + segmentDelay.add(segment_delay_ms); // value + segmentDelay.add(" milliseconds"); // unit + + JsonArray onTime = + staircase.createNestedArray("Power-off stairs after"); // name + onTime.add(on_time_ms / 1000); // value + onTime.add(" seconds"); // unit + } else { + JsonArray usermodEnabled = + staircase.createNestedArray("Staircase enabled"); // name + usermodEnabled.add("no"); // value + } + } +}; \ No newline at end of file diff --git a/usermods/Animated_Staircase/Animated_Staircase_config.h b/usermods/Animated_Staircase/Animated_Staircase_config.h new file mode 100644 index 000000000..980507477 --- /dev/null +++ b/usermods/Animated_Staircase/Animated_Staircase_config.h @@ -0,0 +1,21 @@ +/* + * Animated_Staircase compiletime confguration. + * + * Please see README.md on how to change this file. + */ + +// Please change the pin numbering below to match your board. +#define TOP_PIR_PIN D5 +#define BOTTOM_PIR_PIN D6 + +// Or uncumment and a pir and use an ultrasound HC-SR04 sensor, +// see README.md for details +#ifndef TOP_PIR_PIN +#define TOP_TRIGGER_PIN D2 +#define TOP_ECHO_PIN D3 +#endif + +#ifndef BOTTOM_PIR_PIN +#define BOTTOM_TRIGGER_PIN D4 +#define BOTTOM_ECHO_PIN D5 +#endif \ No newline at end of file diff --git a/usermods/Animated_Staircase/README.md b/usermods/Animated_Staircase/README.md new file mode 100644 index 000000000..6e84b5444 --- /dev/null +++ b/usermods/Animated_Staircase/README.md @@ -0,0 +1,203 @@ +# Usermod Animated Staircase +This usermod makes your staircase look cool by switching it on with an animation. It uses +PIR or ultrasonic sensors at the top and bottom of your stairs to: + +- Light up the steps in your walking direction, leading the way. +- Switch off the steps after you, in the direction of the last detected movement. +- Always switch on when one of the sensors detects movement, even if an effect + is still running. It can therewith handle multiple people on the stairs gracefully. + +The Animated Staircase can be controlled by the WLED API. Change settings such as +speed, on/off time and distance settings by sending an HTTP request, see below. + +## WLED integration +To include this usermod in your WLED setup, you have to be able to [compile WLED from source](https://github.com/Aircoookie/WLED/wiki/Compiling-WLED). + +Before compiling, you have to make the following modifications: + +Edit `usermods_list.cpp`: +1. Open `wled00/usermods_list.cpp` +2. add `#include "../usermods/Animated_Staircase/Animated_Staircase.h"` to the top of the file +3. add `usermods.add(new Animated_Staircase());` to the end of the `void registerUsermods()` function. + +Edit `Animated_Staircase_config.h`: +1. Open `usermods/Animated_Staircase/Animated_Staircase_config.h` +2. To use PIR sensors, change these lines to match your setup: + Using D7 and D6 pin notation as used on several boards: + + ```cpp + #define TOP_PIR_PIN D7 + #define BOTTOM_PIR_PIN D6 + ``` + + Or using GPIO numbering for pins 25 and 26: + ```cpp + #define TOP_PIR_PIN 26 + #define BOTTOM_PIR_PIN 25 + ``` + + To use Ultrasonic HC-SR04 sensors instead of (one of the) PIR sensors, + uncomment one of the PIR sensor lines and adjust the pin numbers for the + connected Ultrasonic sensor. In the example below we use an Ultrasonic + sensor at the bottom of the stairs: + + ```cpp + #define TOP_PIR_PIN 32 + //#define BOTTOM_PIR_PIN D6 /* This PIR sensor is disabled */ + + #ifndef TOP_PIR_PIN + #define TOP_SIGNAL_PIN D2 + #define TOP_ECHO_PIN D3 + #endif + + #ifndef BOTTOM_PIR_PIN /* If the bottom PIR is disabled, */ + #define BOTTOM_SIGNAL_PIN 25 /* This Ultrasonic sensor is used */ + #define BOTTOM_ECHO_PIN 26 + #endif + ``` + +After these modifications, compile and upload your WLED binary to your board +and check the WLED info page to see if this usermod is enabled. + +## Hardware installation +1. Stick the LED strip under each step of the stairs. +2. Connect the ESP8266 pin D4 or ESP32 pin D2 to the first LED data pin at the bottom step + of your stairs. +3. Connect the data-out pin at the end of each strip per step to the data-in pin on the + other end of the next step, creating one large virtual LED strip. +4. Mount sensors of choice at the bottom and top of the stairs and connect them to the ESP. +5. To make sure all LEDs get enough power and have your staircase lighted evenly, power each + step from one side, using at least AWG14 or 2.5mm^2 cable. Don't connect them serial as you + do for the datacable! + +You _may_ need to use 10k pull-down resistors on the selected PIR pins, depending on the sensor. + +## WLED configuration +1. In the WLED UI, confgure a segment for each step. The lowest step of the stairs is the + lowest segment id. +2. Save your segments into a preset. +3. Ideally, add the preset in the config > LED setup menu to the "apply + preset **n** at boot" setting. + +## Changing behavior through API +The Staircase settings can be changed through the WLED JSON api. + +**NOTE:** We are using [curl](https://curl.se/) to send HTTP POSTs to the WLED API. +If you're using Windows and want to use the curl commands, replace the `\` with a `^` +or remove them and put everything on one line. + + +| Setting | Description | Default | +|------------------|---------------------------------------------------------------|---------| +| enabled | Enable or disable the usermod | true | +| segment-delay-ms | Delay (milliseconds) between switching on/off each step | 150 | +| on-time-s | Time (seconds) the stairs stay lit after last detection | 5 | +| bottom-echo-us | Detection range of ultrasonic sensor | 1749 | +| bottomsensor | Manually trigger a down to up animation via API | false | +| topsensor | Manually trigger an up to down animation via API | false | + + +To read the current settings, open a browser to `http://xxx.xxx.xxx.xxx/json/state` (use your WLED +device IP address). The device will respond with a json object containing all WLED settings. +The staircase settings and sensor states are inside the WLED status element: + +```json +{ + "state": { + "staircase": { + "enabled": true, + "segment-delay-ms": 150, + "on-time-s": 5, + "bottomsensor": false, + "topsensor": false + }, +} +``` + +### Enable/disable the usermod +By disabling the usermod you will be able to keep the LED's on, independent from the sensor +activity. This enables to play with the lights without the usermod switching them on or off. + +To disable the usermod: + +```bash +curl -X POST -H "Content-Type: application/json" \ + -d {"staircase":{"enabled":false}} \ + xxx.xxx.xxx.xxx/json/state +``` + +To enable the usermod again, use `"enabled":true`. + +### Changing animation parameters +To change the delay between the steps to (for example) 100 milliseconds and the on-time to +10 seconds: + +```bash +curl -X POST -H "Content-Type: application/json" \ + -d '{"staircase":{"segment-delay-ms":100,"on-time-s":10}}' \ + xxx.xxx.xxx.xxx/json/state +``` + +### Changing detection range of the ultrasonic HC-SR04 sensor +When an ultrasonic sensor is enabled in `Animated_Staircase_config.h`, you'll see a +`bottom-echo-us` setting appear in the json api: + +```json +{ + "state": { + "staircase": { + "enabled": true, + "segment-delay-ms": 150, + "on-time-s": 5, + "bottom-echo-us": 1749 + }, +} +``` + +If the HC-SR04 sensor detects an echo within 1749 microseconds (corresponding to ~30 cm +detection range from the sensor), it will trigger switching on the staircase. This setting +can be changed through the API with an HTTP POST: + +```bash +curl -X POST -H "Content-Type: application/json" \ + -d '{"staircase":{"bottom-echo-us":1166}}' \ + xxx.xxx.xxx.xxx/json/state +``` + +Calculating the detection range can be performed as follows: The speed of sound is 343m/s at 20 +degrees Centigrade. Since the sound has to travel back and forth, the detection range for the +sensor in cm is (0.0343 * maxTimeUs) / 2. To get you started, please find delays and distances below: + +| Distance | Detection time | +|---------:|----------------:| +| 5 cm | 292 uS | +| 10 cm | 583 uS | +| 20 cm | 1166 uS | +| 30 cm | 1749 uS | +| 50 cm | 2915 uS | +| 100 cm | 5831 uS | + +**Please note:** that using an HC-SR04 sensor, particularly when detecting echos at longer +distances creates delays in the WLED software, and _might_ introduce timing hickups in your animations or +a less responsive web interface. It is therefore advised to keep the detection time as short as possible. + +### Animation triggering through the API +Instead of stairs activation by one of the sensors, you can also trigger the animation through +the API. To simulate triggering the bottom sensor, use: + +```bash +curl -X POST -H "Content-Type: application/json" \ + -d '{"staircase":{"bottomsensor":true}}' \ + xxx.xxx.xxx.xxx/json/state +``` + +Likewise, to trigger the top sensor, use: + +```bash +curl -X POST -H "Content-Type: application/json" \ + -d '{"staircase":{"topsensor":true}}' \ + xxx.xxx.xxx.xxx/json/state +``` + +Have fun with this usermod.
+www.rolfje.com diff --git a/usermods/BME280_v2/README.md b/usermods/BME280_v2/README.md new file mode 100644 index 000000000..216ca6300 --- /dev/null +++ b/usermods/BME280_v2/README.md @@ -0,0 +1,40 @@ +Hello! I have written a v2 usermod for the BME280/BMP280 sensor based on the [existing v1 usermod](https://github.com/Aircoookie/WLED/blob/master/usermods/Wemos_D1_mini%2BWemos32_mini_shield/usermod_bme280.cpp). It is not just a refactor, there are many changes which I made to fit my use case, and I hope they will fit the use cases of others as well! Most notably, this usermod is *just* for the BME280 and does not control a display like in the v1 usermod designed for the WeMos shield. + +- Requires libraries `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) and `Wire`. Please add these under `lib_deps` in your `platform.ini` (or `platform_override.ini`). +- Data is published over MQTT so make sure you've enabled the MQTT sync interface. +- This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else listening on the serial TX pin of your board will get confused by log messages! + +To enable, compile with `USERMOD_BME280` defined (i.e. `platformio_override.ini`) +```ini +build_flags = + ${common.build_flags_esp8266} + -D USERMOD_BME280 +``` +or define `USERMOD_BME280` in `my_config.h` +```c++ +#define USERMOD_BME280 +``` + +Changes include: +- Adjustable measure intervals + - Temperature and pressure have separate intervals due to pressure not frequently changing at any constant altitude +- Adjustment of number of decimal places in published sensor values + - Separate adjustment for temperature, humidity and pressure values + - Values are rounded to the specified number of decimal places +- Pressure measured in units of hPa instead of Pa +- Calculation of heat index (apparent temperature) and dew point + - These, along with humidity measurements, are disabled if the sensor is a BMP280 +- 16x oversampling of sensor during measurement +- Values are only published if they are different from the previous value +- Values are published on startup (continually until the MQTT broker acknowledges a successful publication) + +Adjustments are made through preprocessor definitions at the start of the class definition. + +MQTT topics are as follows: +Measurement type | MQTT topic +--- | --- +Temperature | `/temperature` +Humidity | `/humidity` +Pressure | `/pressure` +Heat index | `/heat_index` +Dew point | `/dew_point` \ No newline at end of file diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h new file mode 100644 index 000000000..80a31a4f0 --- /dev/null +++ b/usermods/BME280_v2/usermod_bme280.h @@ -0,0 +1,212 @@ +#pragma once + +#include "wled.h" +#include +#include +#include // BME280 sensor +#include // BME280 extended measurements + +class UsermodBME280 : public Usermod +{ +private: +// User-defined configuration +#define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit +#define TemperatureDecimals 1 // Number of decimal places in published temperaure values +#define HumidityDecimals 0 // Number of decimal places in published humidity values +#define PressureDecimals 2 // Number of decimal places in published pressure values +#define TemperatureInterval 5 // Interval to measure temperature (and humidity, dew point if available) in seconds +#define PressureInterval 300 // Interval to measure pressure in seconds + +// Sanity checks +#if !defined(TemperatureDecimals) || TemperatureDecimals < 0 + #define TemperatureDecimals 0 +#endif +#if !defined(HumidityDecimals) || HumidityDecimals < 0 + #define HumidityDecimals 0 +#endif +#if !defined(PressureDecimals) || PressureDecimals < 0 + #define PressureDecimals 0 +#endif +#if !defined(TemperatureInterval) || TemperatureInterval < 0 + #define TemperatureInterval 1 +#endif +#if !defined(PressureInterval) || PressureInterval < 0 + #define PressureInterval TemperatureInterval +#endif + +#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards + uint8_t SCL_PIN = 22; + uint8_t SDA_PIN = 21; +#else // ESP8266 boards + uint8_t SCL_PIN = 5; + uint8_t SDA_PIN = 4; + //uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 +#endif + + // BME280 sensor settings + BME280I2C::Settings settings{ + BME280::OSR_X16, // Temperature oversampling x16 + BME280::OSR_X16, // Humidity oversampling x16 + BME280::OSR_X16, // Pressure oversampling x16 + // Defaults + BME280::Mode_Forced, + BME280::StandbyTime_1000ms, + BME280::Filter_Off, + BME280::SpiEnable_False, + BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. Default 0x76 + }; + + BME280I2C bme{settings}; + + uint8_t SensorType; + + // Measurement timers + long timer; + long lastTemperatureMeasure = 0; + long lastPressureMeasure = 0; + + // Current sensor values + float SensorTemperature; + float SensorHumidity; + float SensorHeatIndex; + float SensorDewPoint; + float SensorPressure; + // Track previous sensor values + float lastTemperature; + float lastHumidity; + float lastHeatIndex; + float lastDewPoint; + float lastPressure; + + // Store packet IDs of MQTT publications + uint16_t mqttTemperaturePub = 0; + uint16_t mqttPressurePub = 0; + + void UpdateBME280Data(int SensorType) + { + float _temperature, _humidity, _pressure; + #ifdef Celsius + BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); + EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius); + #else + BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit); + EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit); + #endif + BME280::PresUnit presUnit(BME280::PresUnit_hPa); + + bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit); + + SensorTemperature = _temperature; + SensorHumidity = _humidity; + SensorPressure = _pressure; + if (SensorType == 1) + { + SensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit); + SensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit); + } + } + +public: + void setup() + { + Wire.begin(SDA_PIN, SCL_PIN); + + if (!bme.begin()) + { + SensorType = 0; + Serial.println("Could not find BME280I2C sensor!"); + } + else + { + switch (bme.chipModel()) + { + case BME280::ChipModel_BME280: + SensorType = 1; + Serial.println("Found BME280 sensor! Success."); + break; + case BME280::ChipModel_BMP280: + SensorType = 2; + Serial.println("Found BMP280 sensor! No Humidity available."); + break; + default: + SensorType = 0; + Serial.println("Found UNKNOWN sensor! Error!"); + } + } + } + + void loop() + { + // BME280 sensor MQTT publishing + // Check if sensor present and MQTT Connected, otherwise it will crash the MCU + if (SensorType != 0 && mqtt != nullptr) + { + // Timer to fetch new temperature, humidity and pressure data at intervals + timer = millis(); + + if (timer - lastTemperatureMeasure >= TemperatureInterval * 1000 || mqttTemperaturePub == 0) + { + lastTemperatureMeasure = timer; + + UpdateBME280Data(SensorType); + + float Temperature = roundf(SensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + float Humidity, HeatIndex, DewPoint; + + // If temperature has changed since last measure, create string populated with device topic + // from the UI and values read from sensor, then publish to broker + if (Temperature != lastTemperature) + { + String topic = String(mqttDeviceTopic) + "/temperature"; + mqttTemperaturePub = mqtt->publish(topic.c_str(), 0, false, String(Temperature, TemperatureDecimals).c_str()); + } + + lastTemperature = Temperature; // Update last sensor temperature for next loop + + if (SensorType == 1) // Only if sensor is a BME280 + { + Humidity = roundf(SensorHumidity * pow(10, HumidityDecimals)) / pow(10, HumidityDecimals); + HeatIndex = roundf(SensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + DewPoint = roundf(SensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + + if (Humidity != lastHumidity) + { + String topic = String(mqttDeviceTopic) + "/humidity"; + mqtt->publish(topic.c_str(), 0, false, String(Humidity, HumidityDecimals).c_str()); + } + + if (HeatIndex != lastHeatIndex) + { + String topic = String(mqttDeviceTopic) + "/heat_index"; + mqtt->publish(topic.c_str(), 0, false, String(HeatIndex, TemperatureDecimals).c_str()); + } + + if (DewPoint != lastDewPoint) + { + String topic = String(mqttDeviceTopic) + "/dew_point"; + mqtt->publish(topic.c_str(), 0, false, String(DewPoint, TemperatureDecimals).c_str()); + } + + lastHumidity = Humidity; + lastHeatIndex = HeatIndex; + lastDewPoint = DewPoint; + } + } + + if (timer - lastPressureMeasure >= PressureInterval * 1000 || mqttPressurePub == 0) + { + lastPressureMeasure = timer; + + float Pressure = roundf(SensorPressure * pow(10, PressureDecimals)) / pow(10, PressureDecimals); + + if (Pressure != lastPressure) + { + String topic = String(mqttDeviceTopic) + "/pressure"; + mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(Pressure, PressureDecimals).c_str()); + } + + lastPressure = Pressure; + } + } + } +}; \ No newline at end of file diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index d72be934b..1ce0322e8 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -5,11 +5,13 @@ #include //DS18B20 //Pin defaults for QuinLed Dig-Uno +#ifndef TEMPERATURE_PIN #ifdef ARDUINO_ARCH_ESP32 #define TEMPERATURE_PIN 18 #else //ESP8266 boards #define TEMPERATURE_PIN 14 #endif +#endif // the frequency to check temperature, 1 minute #ifndef USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL @@ -58,6 +60,7 @@ class UsermodTemperature : public Usermod { } void getTemperature() { + if (strip.isUpdating()) return; #ifdef USERMOD_DALLASTEMPERATURE_CELSIUS temperature = sensor.getTempC(sensorDeviceAddress); #else @@ -80,30 +83,28 @@ class UsermodTemperature : public Usermod { disabled = !sensor.getAddress(sensorDeviceAddress, 0); if (!disabled) { - DEBUG_PRINTLN("Dallas Temperature found"); + DEBUG_PRINTLN(F("Dallas Temperature found")); // set the resolution for this specific device sensor.setResolution(sensorDeviceAddress, 9, true); // do not block waiting for reading - sensor.setWaitForConversion(false); + sensor.setWaitForConversion(false); + // allocate pin & prevent other use + if (!pinManager.allocatePin(TEMPERATURE_PIN,false)) + disabled = true; } else { - DEBUG_PRINTLN("Dallas Temperature not found"); + DEBUG_PRINTLN(F("Dallas Temperature not found")); } } void loop() { - if (disabled) { - return; - } + if (disabled || strip.isUpdating()) return; unsigned long now = millis(); // check to see if we are due for taking a measurement // lastMeasurement will not be updated until the conversion // is complete the the reading is finished - if (now - lastMeasurement < USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL) - { - return; - } + if (now - lastMeasurement < USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL) return; // we are due for a measurement, if we are not already waiting // for a conversion to complete, then make a new request for temps @@ -125,7 +126,7 @@ class UsermodTemperature : public Usermod { // dont publish super low temperature as the graph will get messed up // the DallasTemperature library returns -127C or -196.6F when problem // reading the sensor - strcat(subuf, "/temperature"); + strcat_P(subuf, PSTR("/temperature")); mqtt->publish(subuf, 0, true, String(temperature).c_str()); } else { // publish something else to indicate status? @@ -136,34 +137,32 @@ class UsermodTemperature : public Usermod { void addToJsonInfo(JsonObject& root) { // dont add temperature to info if we are disabled - if (disabled) { - return; - } + if (disabled) return; - JsonObject user = root["u"]; - if (user.isNull()) user = root.createNestedObject("u"); + JsonObject user = root[F("u")]; + if (user.isNull()) user = root.createNestedObject(F("u")); - JsonArray temp = user.createNestedArray("Temperature"); + JsonArray temp = user.createNestedArray(F("Temperature")); if (!getTemperatureComplete) { // if we haven't read the sensor yet, let the user know // that we are still waiting for the first measurement temp.add((USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT - millis()) / 1000); - temp.add(" sec until read"); + temp.add(F(" sec until read")); return; } if (temperature <= -100) { temp.add(0); - temp.add(" Sensor Error!"); + temp.add(F(" Sensor Error!")); return; } temp.add(temperature); #ifdef USERMOD_DALLASTEMPERATURE_CELSIUS - temp.add("°C"); + temp.add(F("°C")); #else - temp.add("°F"); + temp.add(F("°F")); #endif } diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 786ea6925..1f8553996 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -545,7 +545,7 @@ uint16_t WS2812FX::dissolve(uint32_t color) { } } - if (SEGENV.call > (255 - SEGMENT.speed) + 15) + if (SEGENV.call > (255 - SEGMENT.speed) + 15U) { SEGENV.aux0 = !SEGENV.aux0; SEGENV.call = 0; @@ -1034,7 +1034,7 @@ uint16_t WS2812FX::mode_running_random(void) { } SEGENV.step++; - if (SEGENV.step > ((255-SEGMENT.intensity) >> 4)) + if (SEGENV.step > (uint8_t)((255-SEGMENT.intensity) >> 4)) { SEGENV.step = 0; } @@ -1257,7 +1257,7 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all) for (uint16_t i = idexB; i < idexR; i++) setPixelColor(i, color2); } } else { //regular dot-only mode - uint8_t size = 1 + SEGMENT.intensity >> 3; + uint8_t size = 1 + (SEGMENT.intensity >> 3); if (size > SEGLEN/2) size = 1+ SEGLEN/2; for (uint8_t i=0; i <= size; i++) { setPixelColor(idexR+i, color1); @@ -1568,9 +1568,9 @@ uint16_t WS2812FX::mode_oscillate(void) if (SEGENV.call == 0) { - oscillators[0] = {SEGLEN/4, SEGLEN/8, 1, 1}; - oscillators[1] = {SEGLEN/4*3, SEGLEN/8, 1, 2}; - oscillators[2] = {SEGLEN/4*2, SEGLEN/8, -1, 1}; + oscillators[0] = {(int16_t)(SEGLEN/4), (int8_t)(SEGLEN/8), 1, 1}; + oscillators[1] = {(int16_t)(SEGLEN/4*3), (int8_t)(SEGLEN/8), 1, 2}; + oscillators[2] = {(int16_t)(SEGLEN/4*2), (int8_t)(SEGLEN/8), -1, 1}; } uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); @@ -1919,7 +1919,6 @@ uint16_t WS2812FX::mode_noise16_2() for (uint16_t i = 0; i < SEGLEN; i++) { uint16_t shift_x = SEGENV.step >> 6; // x as a function of time - uint16_t shift_y = SEGENV.step/42; uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field @@ -3198,8 +3197,8 @@ uint16_t WS2812FX::mode_plasma(void) { uint8_t thatPhase = beatsin8(7,-64,64); for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows: - uint8_t colorIndex = cubicwave8((i*(1+ 3*(SEGMENT.speed >> 5)))+(thisPhase) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change. - + cos8((i*(1+ 2*(SEGMENT.speed >> 5)))+(thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish. + uint8_t colorIndex = cubicwave8(((i*(1+ 3*(SEGMENT.speed >> 5)))+(thisPhase)) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change. + + cos8(((i*(1+ 2*(SEGMENT.speed >> 5)))+(thatPhase)) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish. uint8_t thisBright = qsub8(colorIndex, beatsin8(6,0, (255 - SEGMENT.intensity)|0x01 )); CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND); setPixelColor(i, color.red, color.green, color.blue); diff --git a/wled00/FX.h b/wled00/FX.h index 589a7005c..b681a0793 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -29,12 +29,6 @@ #ifndef WS2812FX_h #define WS2812FX_h -#ifdef ESP32_MULTISTRIP - #include "../usermods/esp32_multistrip/NpbWrapper.h" -#else - #include "NpbWrapper.h" -#endif - #include "const.h" #define FASTLED_INTERNAL //remove annoying pragma messages @@ -586,12 +580,11 @@ class WS2812FX { ablMilliampsMax = 850; currentMilliamps = 0; timebase = 0; - bus = new NeoPixelWrapper(); resetSegments(); } void - init(bool supportWhite, uint16_t countPixels, bool skipFirst), + finalizeInit(bool supportWhite, uint16_t countPixels, bool skipFirst), service(void), blur(uint8_t), fill(uint32_t), @@ -631,6 +624,8 @@ class WS2812FX { paletteFade = 0, paletteBlend = 0, milliampsPerLed = 55, +// getStripType(uint8_t strip=0), +// setStripType(uint8_t type, uint8_t strip=0), getBrightness(void), getMode(void), getSpeed(void), @@ -645,12 +640,19 @@ class WS2812FX { get_random_wheel_index(uint8_t); int8_t +// setStripPin(uint8_t strip, int8_t pin), +// getStripPin(uint8_t strip=0), +// setStripPinClk(uint8_t strip, int8_t pin), +// getStripPinClk(uint8_t strip=0), tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec); uint16_t ablMilliampsMax, currentMilliamps, - triwave16(uint16_t); +// setStripLen(uint8_t strip, uint16_t len), +// getStripLen(uint8_t strip=0), + triwave16(uint16_t), + getFps(); uint32_t now, @@ -796,8 +798,6 @@ class WS2812FX { mode_dynamic_smooth(void); private: - NeoPixelWrapper *bus; - uint32_t crgb_to_col(CRGB fastled); CRGB col_to_crgb(uint32_t); CRGBPalette16 currentPalette; @@ -809,11 +809,12 @@ class WS2812FX { uint16_t _usedSegmentData = 0; uint16_t _transitionDur = 750; + uint16_t _cumulativeFps = 2; + void load_gradient_palette(uint8_t); void handle_palette(void); bool - shouldStartBus = false, _useRgbw = false, _skipFirstMode, _triggered; @@ -861,12 +862,6 @@ class WS2812FX { uint32_t _colors_t[3]; uint8_t _bri_t; - #ifdef WLED_USE_ANALOG_LEDS - uint32_t _analogLastShow = 0; - RgbwColor _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 aef500ce1..215df6c49 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -47,7 +47,8 @@ 19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25] */ -void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) +//do not call this method from system context (network callback) +void WS2812FX::finalizeInit(bool supportWhite, uint16_t countPixels, bool skipFirst) { if (supportWhite == _useRgbw && countPixels == _length && _skipFirstMode == skipFirst) return; RESET_RUNTIME; @@ -55,21 +56,36 @@ void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst) _length = countPixels; _skipFirstMode = skipFirst; - uint8_t ty = 1; - if (supportWhite) ty = 2; _lengthRaw = _length; if (_skipFirstMode) { _lengthRaw += LED_SKIP_AMOUNT; } + //if busses failed to load, add default (FS issue...) + if (busses.getNumBusses() == 0) { + uint8_t defPin[] = {LEDPIN}; + BusConfig defCfg = BusConfig(TYPE_WS2812_RGB, defPin, 0, _lengthRaw, COL_ORDER_GRB); + busses.add(defCfg); + } + deserializeMap(); - bus->Begin((NeoPixelType)ty, _lengthRaw); - + //make segment 0 cover the entire strip _segments[0].start = 0; _segments[0].stop = _length; setBrightness(_brightness); + + #ifdef ESP8266 + for (uint8_t i = 0; i < busses.getNumBusses(); i++) { + Bus* b = busses.getBus(i); + if ((!IS_DIGITAL(b->getType()) || IS_2PIN(b->getType()))) continue; + uint8_t pins[5]; + b->getPins(pins); + BusDigital* bd = static_cast(b); + if (pins[0] == 3) bd->reinit(); + } + #endif } void WS2812FX::service() { @@ -168,19 +184,17 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) } } - RgbwColor col; - col.R = r; col.G = g; col.B = b; col.W = w; - uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0; if (SEGLEN) {//from segment //color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments) if (_bri_t < 255) { - col.R = scale8(col.R, _bri_t); - col.G = scale8(col.G, _bri_t); - col.B = scale8(col.B, _bri_t); - col.W = scale8(col.W, _bri_t); + r = scale8(r, _bri_t); + g = scale8(g, _bri_t); + b = scale8(b, _bri_t); + w = scale8(w, _bri_t); } + uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b)); /* Set all the pixels in the group, ensuring _skipFirstMode is honored */ bool reversed = reverseMode ^ IS_REVERSE; @@ -192,12 +206,12 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) if (reverseMode) indexSetRev = REV(indexSet); if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet]; if (indexSetRev >= SEGMENT.start && indexSetRev < SEGMENT.stop) { - bus->SetPixelColor(indexSet + skip, col); + busses.setPixelColor(indexSet + skip, col); if (IS_MIRROR) { //set the corresponding mirrored pixel if (reverseMode) { - bus->SetPixelColor(REV(SEGMENT.start) - indexSet + skip + REV(SEGMENT.stop) + 1, col); + busses.setPixelColor(REV(SEGMENT.start) - indexSet + skip + REV(SEGMENT.stop) + 1, col); } else { - bus->SetPixelColor(SEGMENT.stop - indexSet + skip + SEGMENT.start - 1, col); + busses.setPixelColor(SEGMENT.stop - indexSet + skip + SEGMENT.start - 1, col); } } } @@ -205,11 +219,13 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) } else { //live data, etc. if (reverseMode) i = REV(i); if (i < customMappingSize) i = customMappingTable[i]; - bus->SetPixelColor(i + skip, col); + + uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b)); + busses.setPixelColor(i + skip, col); } if (skip && i == 0) { for (uint16_t j = 0; j < skip; j++) { - bus->SetPixelColor(j, RgbwColor(0, 0, 0, 0)); + busses.setPixelColor(j, BLACK); } } } @@ -260,7 +276,7 @@ void WS2812FX::show(void) { for (uint16_t i = 0; i < _length; i++) //sum up the usage of each LED { - RgbwColor c = bus->GetPixelColorRaw(i); + RgbwColor c = busses.getPixelColor(i); if(useWackyWS2815PowerModel) { @@ -289,25 +305,30 @@ void WS2812FX::show(void) { uint16_t scaleI = scale * 255; uint8_t scaleB = (scaleI > 255) ? 255 : scaleI; uint8_t newBri = scale8(_brightness, scaleB); - bus->SetBrightness(newBri); + busses.setBrightness(newBri); currentMilliamps = (powerSum0 * newBri) / puPerMilliamp; } else { currentMilliamps = powerSum / puPerMilliamp; - bus->SetBrightness(_brightness); + busses.setBrightness(_brightness); } currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate currentMilliamps += _length; //add standby power back to estimate } else { currentMilliamps = 0; - bus->SetBrightness(_brightness); + busses.setBrightness(_brightness); } // some buses send asynchronously and this method will return before // all of the data has been sent. // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods - bus->Show(); - _lastShow = millis(); + busses.show(); + unsigned long now = millis(); + unsigned long diff = now - _lastShow; + uint16_t fpsCurr = 200; + if (diff > 0) fpsCurr = 1000 / diff; + _cumulativeFps = (3 * _cumulativeFps + fpsCurr) >> 2; + _lastShow = now; } /** @@ -315,7 +336,16 @@ void WS2812FX::show(void) { * On some hardware (ESP32), strip updates are done asynchronously. */ bool WS2812FX::isUpdating() { - return !bus->CanShow(); + return !busses.canAllShow(); +} + +/** + * Returns the refresh rate of the LED strip. Useful for finding out whether a given setup is fast enough. + * Only updates on show() or is set to 0 fps if last show is more than 2 secs ago, so accurary varies + */ +uint16_t WS2812FX::getFps() { + if (millis() - _lastShow > 2000) return 0; + return _cumulativeFps +1; } /** @@ -351,7 +381,6 @@ uint8_t WS2812FX::getPaletteCount() bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) { - uint8_t mainSeg = getMainSegmentId(); Segment& seg = _segments[getMainSegmentId()]; uint8_t modePrev = seg.mode, speedPrev = seg.speed, intensityPrev = seg.intensity, palettePrev = seg.palette; @@ -417,17 +446,6 @@ void WS2812FX::setBrightness(uint8_t b) { { _segments[i].setOption(SEG_OPTION_FREEZE, false); } - #if LEDPIN == LED_BUILTIN - shouldStartBus = true; - #endif - } else { - #if LEDPIN == LED_BUILTIN - if (shouldStartBus) { - shouldStartBus = false; - const uint8_t ty = _useRgbw ? 2 : 1; - bus->Begin((NeoPixelType)ty, _lengthRaw); - } - #endif } if (SEGENV.next_time > millis() + 22 && millis() - _lastShow > MIN_SHOW_DELAY) show();//apply brightness change immediately if no refresh soon } @@ -485,7 +503,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i) if (i >= _lengthRaw) return 0; - return bus->GetPixelColorRgbw(i); + return busses.getPixelColor(i); } WS2812FX::Segment& WS2812FX::getSegment(uint8_t id) { @@ -505,12 +523,13 @@ uint32_t WS2812FX::getLastShow(void) { return _lastShow; } +//TODO these need to be on a per-strip basis uint8_t WS2812FX::getColorOrder(void) { - return bus->GetColorOrder(); + return COL_ORDER_GRB; } void WS2812FX::setColorOrder(uint8_t co) { - bus->SetColorOrder(co); + //bus->SetColorOrder(co); } void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) { @@ -962,44 +981,6 @@ 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 c; - uint32_t col = bus->GetPixelColorRgbw(PWM_INDEX); - c.R = col >> 16; c.G = col >> 8; c.B = col; c.W = col >> 24; - - byte b = getBrightness(); - if (c == _analogLastColor && b == _analogLastBri) return; - - // check color values for Warm / Cold white mix (for RGBW) // EsplanexaDevice.cpp - #ifdef WLED_USE_5CH_LEDS - if (c.R == 255 && c.G == 255 && c.B == 255 && c.W == 255) { - bus->SetRgbwPwm(0, 0, 0, 0, c.W * b / 255); - } else if (c.R == 127 && c.G == 127 && c.B == 127 && c.W == 255) { - bus->SetRgbwPwm(0, 0, 0, c.W * b / 512, c.W * b / 255); - } else if (c.R == 0 && c.G == 0 && c.B == 0 && c.W == 255) { - bus->SetRgbwPwm(0, 0, 0, c.W * b / 255, 0); - } else if (c.R == 130 && c.G == 90 && c.B == 0 && c.W == 255) { - bus->SetRgbwPwm(0, 0, 0, c.W * b / 255, c.W * b / 512); - } else if (c.R == 255 && c.G == 153 && c.B == 0 && c.W == 255) { - bus->SetRgbwPwm(0, 0, 0, c.W * b / 255, 0); - } else { // not only white colors - bus->SetRgbwPwm(c.R * b / 255, c.G * b / 255, c.B * b / 255, c.W * b / 255); - } - #else - bus->SetRgbwPwm(c.R * b / 255, c.G * b / 255, c.B * b / 255, c.W * b / 255); - #endif - _analogLastColor = c; - _analogLastBri = b; -} -#else -void WS2812FX::setRgbwPwm() {} -#endif //load custom mapping table from JSON file void WS2812FX::deserializeMap(void) { diff --git a/wled00/NodeStruct.h b/wled00/NodeStruct.h new file mode 100644 index 000000000..a0fd2f634 --- /dev/null +++ b/wled00/NodeStruct.h @@ -0,0 +1,34 @@ +#ifndef WLED_NODESTRUCT_H +#define WLED_NODESTRUCT_H + +/*********************************************************************************************\ +* NodeStruct from the ESP Easy project (https://github.com/letscontrolit/ESPEasy) +\*********************************************************************************************/ + +#include +#include + +#define NODE_TYPE_ID_UNDEFINED 0 +#define NODE_TYPE_ID_ESP8266 82 +#define NODE_TYPE_ID_ESP32 32 + +/*********************************************************************************************\ +* NodeStruct +\*********************************************************************************************/ +struct NodeStruct +{ + String nodeName; + IPAddress ip; + uint8_t unit; + uint8_t age; + uint8_t nodeType; + uint32_t build; + + NodeStruct() : age(0), nodeType(0), build(0) + { + for (uint8_t i = 0; i < 4; ++i) { ip[i] = 0; } + } +}; +typedef std::map NodesMap; + +#endif // WLED_NODESTRUCT_H diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h deleted file mode 100644 index 7e9b8f59c..000000000 --- a/wled00/NpbWrapper.h +++ /dev/null @@ -1,439 +0,0 @@ -//this code is a modified version of https://github.com/Makuna/NeoPixelBus/issues/103 -#ifndef NpbWrapper_h -#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 -//#define USE_TM1814 // Uncomment for using TM1814 LEDs (make sure you have NeoPixelBus v2.5.7 or newer) -//#define USE_P9813 // Uncomment for using P9813 LEDs (make sure you have NeoPixelBus v2.5.8 or newer) -//#define WLED_USE_ANALOG_LEDS //Uncomment for using "dumb" PWM controlled LEDs (see pins below, default R: gpio5, G: 12, B: 15, W: 13) -//#define WLED_USE_H801 //H801 controller. Please uncomment #define WLED_USE_ANALOG_LEDS as well -//#define WLED_USE_5CH_LEDS //5 Channel H801 for cold and warm white -//#define WLED_USE_BWLT11 -//#define WLED_USE_SHOJO_PCB - -#ifndef BTNPIN -#define BTNPIN 0 //button pin. Needs to have pullup (gpio0 recommended) -#endif - -#ifndef TOUCHPIN -//#define TOUCHPIN T0 //touch pin. Behaves the same as button. ESP32 only. -#endif - -#ifndef IRPIN -#define IRPIN 4 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0 -#endif - -#ifndef RLYPIN -#define RLYPIN 12 //pin for relay, will be set HIGH if LEDs are on (-1 to disable). Also usable for standby leds, triggers,... -#endif - -#ifndef AUXPIN -#define AUXPIN -1 //debug auxiliary output pin (-1 to disable) -#endif - -#ifndef RLYMDE -#define RLYMDE 1 //mode for relay, 0: LOW if LEDs are on 1: HIGH if LEDs are on -#endif - -//enable color order override for a specific range of the strip -//This can be useful if you want to chain multiple strings with incompatible color order -//#define COLOR_ORDER_OVERRIDE -#define COO_MIN 0 -#define COO_MAX 35 //not inclusive, this would set the override for LEDs 0-34 -#define COO_ORDER COL_ORDER_GRB - -//END CONFIGURATION - -#if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) - #ifndef CLKPIN - #define CLKPIN 0 - #endif - #ifndef DATAPIN - #define DATAPIN 2 - #endif - #if BTNPIN == CLKPIN || BTNPIN == DATAPIN - #undef BTNPIN // Deactivate button pin if it conflicts with one of the APA102 pins. - #endif -#endif - -#ifdef WLED_USE_ANALOG_LEDS - //PWM pins - PINs 15,13,12,14 (W2 = 04)are used with H801 Wifi LED Controller - #ifdef WLED_USE_H801 - #define RPIN 15 //R pin for analog LED strip - #define GPIN 13 //G pin for analog LED strip - #define BPIN 12 //B pin for analog LED strip - #define WPIN 14 //W pin for analog LED strip - #define W2PIN 04 //W2 pin for analog LED strip - #undef BTNPIN - #undef IRPIN - #define IRPIN 0 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0 - #elif defined(WLED_USE_BWLT11) - //PWM pins - to use with BW-LT11 - #define RPIN 12 //R pin for analog LED strip - #define GPIN 4 //G pin for analog LED strip - #define BPIN 14 //B pin for analog LED strip - #define WPIN 5 //W pin for analog LED strip - #elif defined(WLED_USE_SHOJO_PCB) - //PWM pins - to use with Shojo PCB (https://www.bastelbunker.de/esp-rgbww-wifi-led-controller-vbs-edition/) - #define RPIN 14 //R pin for analog LED strip - #define GPIN 4 //G pin for analog LED strip - #define BPIN 5 //B pin for analog LED strip - #define WPIN 15 //W pin for analog LED strip - #define W2PIN 12 //W2 pin for analog LED strip - #elif defined(WLED_USE_PLJAKOBS_PCB) - // PWM pins - to use with esp_rgbww_controller from patrickjahns/pljakobs (https://github.com/pljakobs/esp_rgbww_controller) - #define RPIN 12 //R pin for analog LED strip - #define GPIN 13 //G pin for analog LED strip - #define BPIN 14 //B pin for analog LED strip - #define WPIN 4 //W pin for analog LED strip - #define W2PIN 5 //W2 pin for analog LED strip - #undef IRPIN - #else - //Enable override of Pins by using the platformio_override.ini file - //PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller - #ifndef RPIN - #define RPIN 5 //R pin for analog LED strip - #endif - #ifndef GPIN - #define GPIN 12 //G pin for analog LED strip - #endif - #ifndef BPIN - #define BPIN 15 //B pin for analog LED strip - #endif - #ifndef WPIN - #define WPIN 13 //W pin for analog LED strip - #endif - #endif - #undef RLYPIN - #define RLYPIN -1 //disable as pin 12 is used by analog LEDs -#endif - -//automatically uses the right driver method for each platform -#ifdef ARDUINO_ARCH_ESP32 - #ifdef USE_APA102 - #define PIXELMETHOD DotStarMethod - #elif defined(USE_WS2801) - #define PIXELMETHOD NeoWs2801Method - #elif defined(USE_LPD8806) - #define PIXELMETHOD Lpd8806Method - #elif defined(USE_TM1814) - #define PIXELMETHOD NeoTm1814Method - #elif defined(USE_P9813) - #define PIXELMETHOD P9813Method - #else - #define PIXELMETHOD NeoEsp32Rmt0Ws2812xMethod - #endif -#else //esp8266 - //autoselect the right method depending on strip pin - #ifdef USE_APA102 - #define PIXELMETHOD DotStarMethod - #elif defined(USE_WS2801) - #define PIXELMETHOD NeoWs2801Method - #elif defined(USE_LPD8806) - #define PIXELMETHOD Lpd8806Method - #elif defined(USE_TM1814) - #define PIXELMETHOD NeoTm1814Method - #elif defined(USE_P9813) - #define PIXELMETHOD P9813Method - #elif LEDPIN == 2 - #define PIXELMETHOD NeoEsp8266Uart1Ws2813Method //if you get an error here, try to change to NeoEsp8266UartWs2813Method or update Neopixelbus - #elif LEDPIN == 3 - #define PIXELMETHOD NeoEsp8266Dma800KbpsMethod - #else - #define PIXELMETHOD NeoEsp8266BitBang800KbpsMethod - #pragma message "Software BitBang will be used because of your selected LED pin. This may cause flicker. Use GPIO 2 or 3 for best results." - #endif -#endif - - -//you can now change the color order in the web settings -#ifdef USE_APA102 - #define PIXELFEATURE3 DotStarBgrFeature - #define PIXELFEATURE4 DotStarLbgrFeature -#elif defined(USE_LPD8806) - #define PIXELFEATURE3 Lpd8806GrbFeature - #define PIXELFEATURE4 Lpd8806GrbFeature -#elif defined(USE_WS2801) - #define PIXELFEATURE3 NeoRbgFeature - #define PIXELFEATURE4 NeoRbgFeature -#elif defined(USE_TM1814) - #define PIXELFEATURE3 NeoWrgbTm1814Feature - #define PIXELFEATURE4 NeoWrgbTm1814Feature -#elif defined(USE_P9813) - #define PIXELFEATURE3 P9813BgrFeature - #define PIXELFEATURE4 NeoGrbwFeature -#else - #define PIXELFEATURE3 NeoGrbFeature - #define PIXELFEATURE4 NeoGrbwFeature -#endif - - -#include -#include "const.h" - -enum NeoPixelType -{ - NeoPixelType_None = 0, - NeoPixelType_Grb = 1, - NeoPixelType_Grbw = 2, - NeoPixelType_End = 3 -}; - -class NeoPixelWrapper -{ -public: - NeoPixelWrapper() : - // initialize each member to null - _pGrb(NULL), - _pGrbw(NULL), - _type(NeoPixelType_None) - { - - } - - ~NeoPixelWrapper() - { - cleanup(); - } - - void Begin(NeoPixelType type, uint16_t countPixels) - { - cleanup(); - _type = type; - - switch (_type) - { - case NeoPixelType_Grb: - #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) - _pGrb = new NeoPixelBrightnessBus(countPixels, CLKPIN, DATAPIN); - #else - _pGrb = new NeoPixelBrightnessBus(countPixels, LEDPIN); - #endif - _pGrb->Begin(); - break; - - case NeoPixelType_Grbw: - #if defined(USE_APA102) || defined(USE_WS2801) || defined(USE_LPD8806) || defined(USE_P9813) - _pGrbw = new NeoPixelBrightnessBus(countPixels, CLKPIN, DATAPIN); - #else - _pGrbw = new NeoPixelBrightnessBus(countPixels, LEDPIN); - #endif - _pGrbw->Begin(); - break; - } - - #ifdef WLED_USE_ANALOG_LEDS - #ifdef ARDUINO_ARCH_ESP32 - ledcSetup(0, 5000, 8); - ledcAttachPin(RPIN, 0); - ledcSetup(1, 5000, 8); - ledcAttachPin(GPIN, 1); - ledcSetup(2, 5000, 8); - ledcAttachPin(BPIN, 2); - if(_type == NeoPixelType_Grbw) - { - ledcSetup(3, 5000, 8); - ledcAttachPin(WPIN, 3); - #ifdef WLED_USE_5CH_LEDS - ledcSetup(4, 5000, 8); - ledcAttachPin(W2PIN, 4); - #endif - } - #else // ESP8266 - //init PWM pins - pinMode(RPIN, OUTPUT); - pinMode(GPIN, OUTPUT); - pinMode(BPIN, OUTPUT); - if(_type == NeoPixelType_Grbw) - { - pinMode(WPIN, OUTPUT); - #ifdef WLED_USE_5CH_LEDS - pinMode(W2PIN, OUTPUT); - #endif - } - analogWriteRange(255); //same range as one RGB channel - analogWriteFreq(880); //PWM frequency proven as good for LEDs - #endif - #endif - } - -#ifdef WLED_USE_ANALOG_LEDS - 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); - ledcWrite(1, g); - ledcWrite(2, b); - switch (_type) { - case NeoPixelType_Grb: break; - #ifdef WLED_USE_5CH_LEDS - case NeoPixelType_Grbw: ledcWrite(3, w); ledcWrite(4, w2); break; - #else - case NeoPixelType_Grbw: ledcWrite(3, w); break; - #endif - } - #else // ESP8266 - analogWrite(RPIN, r); - analogWrite(GPIN, g); - analogWrite(BPIN, b); - switch (_type) { - case NeoPixelType_Grb: break; - #ifdef WLED_USE_5CH_LEDS - case NeoPixelType_Grbw: analogWrite(WPIN, w); analogWrite(W2PIN, w2); break; - #else - case NeoPixelType_Grbw: analogWrite(WPIN, w); break; - #endif - } - #endif - } -#endif - - void Show() - { - switch (_type) - { - case NeoPixelType_Grb: _pGrb->Show(); break; - case NeoPixelType_Grbw: _pGrbw->Show(); break; - } - } - - /** - * This will return true if enough time has passed since the last time Show() was called. - * This also means that calling Show() will not cause any undue waiting. If the method for - * the defined bus is hardware that sends asynchronously, then call CanShow() will let - * you know if it has finished sending the data from the last Show(). - */ - bool CanShow() - { - switch (_type) - { - case NeoPixelType_Grb: return _pGrb->CanShow(); - case NeoPixelType_Grbw: return _pGrbw->CanShow(); - default: return true; - } - } - - void SetPixelColor(uint16_t indexPixel, RgbwColor c) - { - RgbwColor col; - - uint8_t co = _colorOrder; - #ifdef COLOR_ORDER_OVERRIDE - if (indexPixel >= COO_MIN && indexPixel < COO_MAX) co = COO_ORDER; - #endif - - //reorder channels to selected order - switch (co) - { - case 0: col.G = c.G; col.R = c.R; col.B = c.B; break; //0 = GRB, default - case 1: col.G = c.R; col.R = c.G; col.B = c.B; break; //1 = RGB, common for WS2811 - case 2: col.G = c.B; col.R = c.R; col.B = c.G; break; //2 = BRG - case 3: col.G = c.R; col.R = c.B; col.B = c.G; break; //3 = RBG - case 4: col.G = c.B; col.R = c.G; col.B = c.R; break; //4 = BGR - default: col.G = c.G; col.R = c.B; col.B = c.R; break; //5 = GBR - } - col.W = c.W; - - switch (_type) { - case NeoPixelType_Grb: { - _pGrb->SetPixelColor(indexPixel, RgbColor(col.R,col.G,col.B)); - } - break; - case NeoPixelType_Grbw: { - #if defined(USE_LPD8806) || defined(USE_WS2801) - _pGrbw->SetPixelColor(indexPixel, RgbColor(col.R,col.G,col.B)); - #else - _pGrbw->SetPixelColor(indexPixel, col); - #endif - } - break; - } - } - - void SetBrightness(byte b) - { - switch (_type) { - case NeoPixelType_Grb: _pGrb->SetBrightness(b); break; - case NeoPixelType_Grbw:_pGrbw->SetBrightness(b); break; - } - } - - void SetColorOrder(byte colorOrder) { - _colorOrder = colorOrder; - } - - uint8_t GetColorOrder() { - return _colorOrder; - } - - RgbwColor GetPixelColorRaw(uint16_t indexPixel) const - { - switch (_type) { - case NeoPixelType_Grb: return _pGrb->GetPixelColor(indexPixel); break; - case NeoPixelType_Grbw: return _pGrbw->GetPixelColor(indexPixel); break; - } - return 0; - } - - // NOTE: Due to feature differences, some support RGBW but the method name - // here needs to be unique, thus GetPixeColorRgbw - uint32_t GetPixelColorRgbw(uint16_t indexPixel) const - { - RgbwColor col(0,0,0,0); - switch (_type) { - case NeoPixelType_Grb: col = _pGrb->GetPixelColor(indexPixel); break; - case NeoPixelType_Grbw: col = _pGrbw->GetPixelColor(indexPixel); break; - } - - uint8_t co = _colorOrder; - #ifdef COLOR_ORDER_OVERRIDE - if (indexPixel >= COO_MIN && indexPixel < COO_MAX) co = COO_ORDER; - #endif - - switch (co) - { - // W G R B - case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default - case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811 - case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG - case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG - case 4: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //4 = BGR - case 5: return ((col.W << 24) | (col.G << 8) | (col.B << 16) | (col.R)); //5 = GBR - } - return 0; - } - - uint8_t* GetPixels(void) - { - switch (_type) { - case NeoPixelType_Grb: return _pGrb->Pixels(); break; - case NeoPixelType_Grbw: return _pGrbw->Pixels(); break; - } - return 0; - } - - -private: - NeoPixelType _type; - - // have a member for every possible type - NeoPixelBrightnessBus* _pGrb; - NeoPixelBrightnessBus* _pGrbw; - - byte _colorOrder = 0; - - void cleanup() - { - switch (_type) { - case NeoPixelType_Grb: delete _pGrb ; _pGrb = NULL; break; - case NeoPixelType_Grbw: delete _pGrbw; _pGrbw = NULL; break; - } - } -}; -#endif diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h new file mode 100644 index 000000000..d668f1aec --- /dev/null +++ b/wled00/bus_manager.h @@ -0,0 +1,404 @@ +#ifndef BusManager_h +#define BusManager_h + +/* + * Class for addressing various light types + */ + +#include "const.h" +#include "pin_manager.h" +#include "bus_wrapper.h" +#include + +//temporary struct for passing bus configuration to bus +struct BusConfig { + uint8_t type = TYPE_WS2812_RGB; + uint16_t count = 1; + uint16_t start = 0; + uint8_t colorOrder = COL_ORDER_GRB; + bool reversed = false; + uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; + BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false) { + type = busType; count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; + uint8_t nPins = 1; + if (type > 47) nPins = 2; + else if (type > 41 && type < 46) nPins = NUM_PWM_PINS(type); + for (uint8_t i = 0; i < nPins; i++) pins[i] = ppins[i]; + } +}; + +//parent class of BusDigital and BusPwm +class Bus { + public: + Bus(uint8_t type, uint16_t start) { + _type = type; + _start = start; + }; + + virtual void show() {} + virtual bool canShow() { return true; } + + virtual void setPixelColor(uint16_t pix, uint32_t c) {}; + + virtual void setBrightness(uint8_t b) {}; + + virtual uint32_t getPixelColor(uint16_t pix) { return 0; }; + + virtual void cleanup() {}; + + virtual ~Bus() { //throw the bus under the bus + } + + virtual uint8_t getPins(uint8_t* pinArray) { return 0; } + + uint16_t getStart() { + return _start; + } + + void setStart(uint16_t start) { + _start = start; + } + + virtual uint16_t getLength() { + return 1; + } + + virtual void setColorOrder() {} + + virtual uint8_t getColorOrder() { + return COL_ORDER_RGB; + } + + uint8_t getType() { + return _type; + } + + bool isOk() { + return _valid; + } + + bool reversed = false; + + protected: + uint8_t _type = TYPE_NONE; + uint8_t _bri = 255; + uint16_t _start = 0; + bool _valid = false; +}; + + +class BusDigital : public Bus { + public: + BusDigital(BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start) { + if (!IS_DIGITAL(bc.type) || !bc.count) return; + _pins[0] = bc.pins[0]; + if (!pinManager.allocatePin(_pins[0])) return; + if (IS_2PIN(bc.type)) { + _pins[1] = bc.pins[1]; + if (!pinManager.allocatePin(_pins[1])) { + cleanup(); return; + } + } + _len = bc.count; + reversed = bc.reversed; + _iType = PolyBus::getI(bc.type, _pins, nr); + if (_iType == I_NONE) return; + _busPtr = PolyBus::create(_iType, _pins, _len); + _valid = (_busPtr != nullptr); + _colorOrder = bc.colorOrder; + //Serial.printf("Successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n",nr, len, type, pins[0],pins[1],_iType); + }; + + void show() { + PolyBus::show(_busPtr, _iType); + } + + bool canShow() { + return PolyBus::canShow(_busPtr, _iType); + } + + void setBrightness(uint8_t b) { + //Fix for turning off onboard LED breaking bus + #ifdef LED_BUILTIN + if (_bri == 0 && b > 0) { + if (_pins[0] == LED_BUILTIN || _pins[1] == LED_BUILTIN) PolyBus::begin(_busPtr, _iType, _pins); + } + #endif + _bri = b; + PolyBus::setBrightness(_busPtr, _iType, b); + } + + void setPixelColor(uint16_t pix, uint32_t c) { + if (reversed) pix = _len - pix -1; + PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrder); + } + + uint32_t getPixelColor(uint16_t pix) { + if (reversed) pix = _len - pix -1; + return PolyBus::getPixelColor(_busPtr, _iType, pix, _colorOrder); + } + + uint8_t getColorOrder() { + return _colorOrder; + } + + uint16_t getLength() { + return _len; + } + + uint8_t getPins(uint8_t* pinArray) { + uint8_t numPins = IS_2PIN(_type) ? 2 : 1; + for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i]; + return numPins; + } + + void setColorOrder(uint8_t colorOrder) { + if (colorOrder > 5) return; + _colorOrder = colorOrder; + } + + void reinit() { + PolyBus::begin(_busPtr, _iType, _pins); + } + + void cleanup() { + //Serial.println("Digital Cleanup"); + PolyBus::cleanup(_busPtr, _iType); + _iType = I_NONE; + _valid = false; + _busPtr = nullptr; + pinManager.deallocatePin(_pins[0]); + pinManager.deallocatePin(_pins[1]); + } + + ~BusDigital() { + cleanup(); + } + + private: + uint8_t _colorOrder = COL_ORDER_GRB; + uint8_t _pins[2] = {255, 255}; + uint8_t _iType = I_NONE; + uint16_t _len = 0; + void * _busPtr = nullptr; +}; + + +class BusPwm : public Bus { + public: + BusPwm(BusConfig &bc) : Bus(bc.type, bc.start) { + if (!IS_PWM(bc.type)) return; + uint8_t numPins = NUM_PWM_PINS(bc.type); + + #ifdef ESP8266 + analogWriteRange(255); //same range as one RGB channel + analogWriteFreq(WLED_PWM_FREQ); + #else + _ledcStart = pinManager.allocateLedc(numPins); + if (_ledcStart == 255) { //no more free LEDC channels + deallocatePins(); return; + } + #endif + + for (uint8_t i = 0; i < numPins; i++) { + _pins[i] = bc.pins[i]; + if (!pinManager.allocatePin(_pins[i])) { + deallocatePins(); return; + } + #ifdef ESP8266 + pinMode(_pins[i], OUTPUT); + #else + ledcSetup(_ledcStart + i, WLED_PWM_FREQ, 8); + ledcAttachPin(_pins[i], _ledcStart + i); + #endif + } + _valid = true; + }; + + void setPixelColor(uint16_t pix, uint32_t c) { + if (pix != 0 || !_valid) return; //only react to first pixel + uint8_t r = c >> 16; + uint8_t g = c >> 8; + uint8_t b = c ; + uint8_t w = c >> 24; + + switch (_type) { + case TYPE_ANALOG_1CH: //one channel (white), use highest RGBW value + _data[0] = max(r, max(g, max(b, w))); break; + + case TYPE_ANALOG_2CH: //warm white + cold white, we'll need some nice handling here, for now just R+G channels + case TYPE_ANALOG_3CH: //standard dumb RGB + case TYPE_ANALOG_4CH: //RGBW + case TYPE_ANALOG_5CH: //we'll want the white handling from 2CH here + RGB + _data[0] = r; _data[1] = g; _data[2] = b; _data[3] = w; _data[4] = 0; break; + + default: return; + } + } + + //does no index check + uint32_t getPixelColor(uint16_t pix) { + return ((_data[3] << 24) | (_data[0] << 16) | (_data[1] << 8) | (_data[2])); + } + + void show() { + uint8_t numPins = NUM_PWM_PINS(_type); + for (uint8_t i = 0; i < numPins; i++) { + uint8_t scaled = (_data[i] * _bri) / 255; + #ifdef ESP8266 + analogWrite(_pins[i], scaled); + #else + ledcWrite(_ledcStart + i, scaled); + #endif + } + } + + void setBrightness(uint8_t b) { + _bri = b; + } + + uint8_t getPins(uint8_t* pinArray) { + uint8_t numPins = NUM_PWM_PINS(_type); + for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i]; + return numPins; + } + + void cleanup() { + deallocatePins(); + } + + ~BusPwm() { + cleanup(); + } + + private: + uint8_t _pins[5] = {255, 255, 255, 255, 255}; + uint8_t _data[5] = {255, 255, 255, 255, 255}; + #ifdef ARDUINO_ARCH_ESP32 + uint8_t _ledcStart = 255; + #endif + + void deallocatePins() { + uint8_t numPins = NUM_PWM_PINS(_type); + for (uint8_t i = 0; i < numPins; i++) { + if (!pinManager.isPinOk(_pins[i])) continue; + #ifdef ESP8266 + digitalWrite(_pins[i], LOW); //turn off PWM interrupt + #else + if (_ledcStart < 16) ledcDetachPin(_pins[i]); + #endif + pinManager.deallocatePin(_pins[i]); + } + #ifdef ARDUINO_ARCH_ESP32 + pinManager.deallocateLedc(_ledcStart, numPins); + #endif + } +}; + +class BusManager { + public: + BusManager() { + + }; + + //utility to get the approx. memory usage of a given BusConfig + uint32_t memUsage(BusConfig &bc) { + uint8_t type = bc.type; + uint16_t len = bc.count; + if (type < 32) { + #ifdef ESP8266 + if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem + if (type > 29) return len*20; //RGBW + return len*15; + } + if (type > 29) return len*4; //RGBW + return len*3; + #else //ESP32 RMT uses double buffer? + if (type > 29) return len*8; //RGBW + return len*6; + #endif + } + + if (type > 31 && type < 48) return 5; + if (type == 44 || type == 45) return len*4; //RGBW + return len*3; + } + + int add(BusConfig &bc) { + if (numBusses >= WLED_MAX_BUSSES) return -1; + if (IS_DIGITAL(bc.type)) { + busses[numBusses] = new BusDigital(bc, numBusses); + } else { + busses[numBusses] = new BusPwm(bc); + } + numBusses++; + return numBusses -1; + } + + //do not call this method from system context (network callback) + void removeAll() { + //Serial.println("Removing all."); + //prevents crashes due to deleting busses while in use. + while (!canAllShow()) yield(); + for (uint8_t i = 0; i < numBusses; i++) delete busses[i]; + numBusses = 0; + } + + void show() { + for (uint8_t i = 0; i < numBusses; i++) { + busses[i]->show(); + } + } + + void setPixelColor(uint16_t pix, uint32_t c) { + for (uint8_t i = 0; i < numBusses; i++) { + Bus* b = busses[i]; + uint16_t bstart = b->getStart(); + if (pix < bstart || pix >= bstart + b->getLength()) continue; + busses[i]->setPixelColor(pix - bstart, c); + } + } + + void setBrightness(uint8_t b) { + for (uint8_t i = 0; i < numBusses; i++) { + busses[i]->setBrightness(b); + } + } + + uint32_t getPixelColor(uint16_t pix) { + for (uint8_t i = 0; i < numBusses; i++) { + Bus* b = busses[i]; + uint16_t bstart = b->getStart(); + if (pix < bstart || pix >= bstart + b->getLength()) continue; + return b->getPixelColor(pix - bstart); + } + return 0; + } + + bool canAllShow() { + for (uint8_t i = 0; i < numBusses; i++) { + if (!busses[i]->canShow()) return false; + } + return true; + } + + Bus* getBus(uint8_t busNr) { + if (busNr >= numBusses) return nullptr; + return busses[busNr]; + } + + uint8_t getNumBusses() { + return numBusses; + } + + static bool isRgbw(uint8_t type) { + if (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; + if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; + return false; + } + + private: + uint8_t numBusses = 0; + Bus* busses[WLED_MAX_BUSSES]; +}; +#endif \ No newline at end of file diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h new file mode 100644 index 000000000..da3f96e35 --- /dev/null +++ b/wled00/bus_wrapper.h @@ -0,0 +1,882 @@ +#ifndef BusWrapper_h +#define BusWrapper_h + +#include "NeoPixelBrightnessBus.h" + +//Hardware SPI Pins +#define P_8266_HS_MOSI 13 +#define P_8266_HS_CLK 14 +#define P_32_HS_MOSI 13 +#define P_32_HS_CLK 14 +#define P_32_VS_MOSI 23 +#define P_32_VS_CLK 18 + +//The dirty list of possible bus types. Quite a lot... +#define I_NONE 0 +//ESP8266 RGB +#define I_8266_U0_NEO_3 1 +#define I_8266_U1_NEO_3 2 +#define I_8266_DM_NEO_3 3 +#define I_8266_BB_NEO_3 4 +//RGBW +#define I_8266_U0_NEO_4 5 +#define I_8266_U1_NEO_4 6 +#define I_8266_DM_NEO_4 7 +#define I_8266_BB_NEO_4 8 +//400Kbps +#define I_8266_U0_400_3 9 +#define I_8266_U1_400_3 10 +#define I_8266_DM_400_3 11 +#define I_8266_BB_400_3 12 +//TM1418 (RGBW) +#define I_8266_U0_TM1_4 13 +#define I_8266_U1_TM1_4 14 +#define I_8266_DM_TM1_4 15 +#define I_8266_BB_TM1_4 16 + +/*** ESP32 Neopixel methods ***/ +//RGB +#define I_32_R0_NEO_3 17 +#define I_32_R1_NEO_3 18 +#define I_32_R2_NEO_3 19 +#define I_32_R3_NEO_3 20 +#define I_32_R4_NEO_3 21 +#define I_32_R5_NEO_3 22 +#define I_32_R6_NEO_3 23 +#define I_32_R7_NEO_3 24 +#define I_32_I0_NEO_3 25 +#define I_32_I1_NEO_3 26 +//RGBW +#define I_32_R0_NEO_4 27 +#define I_32_R1_NEO_4 28 +#define I_32_R2_NEO_4 29 +#define I_32_R3_NEO_4 30 +#define I_32_R4_NEO_4 31 +#define I_32_R5_NEO_4 32 +#define I_32_R6_NEO_4 33 +#define I_32_R7_NEO_4 34 +#define I_32_I0_NEO_4 35 +#define I_32_I1_NEO_4 36 +//400Kbps +#define I_32_R0_400_3 37 +#define I_32_R1_400_3 38 +#define I_32_R2_400_3 39 +#define I_32_R3_400_3 40 +#define I_32_R4_400_3 41 +#define I_32_R5_400_3 42 +#define I_32_R6_400_3 43 +#define I_32_R7_400_3 44 +#define I_32_I0_400_3 45 +#define I_32_I1_400_3 46 +//TM1418 (RGBW) +#define I_32_R0_TM1_4 47 +#define I_32_R1_TM1_4 48 +#define I_32_R2_TM1_4 49 +#define I_32_R3_TM1_4 50 +#define I_32_R4_TM1_4 51 +#define I_32_R5_TM1_4 52 +#define I_32_R6_TM1_4 53 +#define I_32_R7_TM1_4 54 +#define I_32_I0_TM1_4 55 +#define I_32_I1_TM1_4 56 +//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) + +//APA102 +#define I_HS_DOT_3 57 //hardware SPI +#define I_SS_DOT_3 58 //soft SPI + +//LPD8806 +#define I_HS_LPD_3 59 +#define I_SS_LPD_3 60 + +//WS2801 +#define I_HS_WS1_3 61 +#define I_SS_WS1_3 62 + +//P9813 +#define I_HS_P98_3 63 +#define I_SS_P98_3 64 + + +/*** ESP8266 Neopixel methods ***/ +#ifdef ESP8266 +//RGB +#define B_8266_U0_NEO_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio1 +#define B_8266_U1_NEO_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio2 +#define B_8266_DM_NEO_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio3 +#define B_8266_BB_NEO_3 NeoPixelBrightnessBus //3 chan, esp8266, bb (any pin but 16) +//RGBW +#define B_8266_U0_NEO_4 NeoPixelBrightnessBus //4 chan, esp8266, gpio1 +#define B_8266_U1_NEO_4 NeoPixelBrightnessBus //4 chan, esp8266, gpio2 +#define B_8266_DM_NEO_4 NeoPixelBrightnessBus //4 chan, esp8266, gpio3 +#define B_8266_BB_NEO_4 NeoPixelBrightnessBus //4 chan, esp8266, bb (any pin) +//400Kbps +#define B_8266_U0_400_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio1 +#define B_8266_U1_400_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio2 +#define B_8266_DM_400_3 NeoPixelBrightnessBus //3 chan, esp8266, gpio3 +#define B_8266_BB_400_3 NeoPixelBrightnessBus //3 chan, esp8266, bb (any pin) +//TM1418 (RGBW) +#define B_8266_U0_TM1_4 NeoPixelBrightnessBus +#define B_8266_U1_TM1_4 NeoPixelBrightnessBus +#define B_8266_DM_TM1_4 NeoPixelBrightnessBus +#define B_8266_BB_TM1_4 NeoPixelBrightnessBus +#endif + +/*** ESP32 Neopixel methods ***/ +#ifdef ARDUINO_ARCH_ESP32 +//RGB +#define B_32_R0_NEO_3 NeoPixelBrightnessBus +#define B_32_R1_NEO_3 NeoPixelBrightnessBus +#define B_32_R2_NEO_3 NeoPixelBrightnessBus +#define B_32_R3_NEO_3 NeoPixelBrightnessBus +#define B_32_R4_NEO_3 NeoPixelBrightnessBus +#define B_32_R5_NEO_3 NeoPixelBrightnessBus +#define B_32_R6_NEO_3 NeoPixelBrightnessBus +#define B_32_R7_NEO_3 NeoPixelBrightnessBus +#define B_32_I0_NEO_3 NeoPixelBrightnessBus +#define B_32_I1_NEO_3 NeoPixelBrightnessBus +//RGBW +#define B_32_R0_NEO_4 NeoPixelBrightnessBus +#define B_32_R1_NEO_4 NeoPixelBrightnessBus +#define B_32_R2_NEO_4 NeoPixelBrightnessBus +#define B_32_R3_NEO_4 NeoPixelBrightnessBus +#define B_32_R4_NEO_4 NeoPixelBrightnessBus +#define B_32_R5_NEO_4 NeoPixelBrightnessBus +#define B_32_R6_NEO_4 NeoPixelBrightnessBus +#define B_32_R7_NEO_4 NeoPixelBrightnessBus +#define B_32_I0_NEO_4 NeoPixelBrightnessBus +#define B_32_I1_NEO_4 NeoPixelBrightnessBus +//400Kbps +#define B_32_R0_400_3 NeoPixelBrightnessBus +#define B_32_R1_400_3 NeoPixelBrightnessBus +#define B_32_R2_400_3 NeoPixelBrightnessBus +#define B_32_R3_400_3 NeoPixelBrightnessBus +#define B_32_R4_400_3 NeoPixelBrightnessBus +#define B_32_R5_400_3 NeoPixelBrightnessBus +#define B_32_R6_400_3 NeoPixelBrightnessBus +#define B_32_R7_400_3 NeoPixelBrightnessBus +#define B_32_I0_400_3 NeoPixelBrightnessBus +#define B_32_I1_400_3 NeoPixelBrightnessBus +//TM1418 (RGBW) +#define B_32_R0_TM1_4 NeoPixelBrightnessBus +#define B_32_R1_TM1_4 NeoPixelBrightnessBus +#define B_32_R2_TM1_4 NeoPixelBrightnessBus +#define B_32_R3_TM1_4 NeoPixelBrightnessBus +#define B_32_R4_TM1_4 NeoPixelBrightnessBus +#define B_32_R5_TM1_4 NeoPixelBrightnessBus +#define B_32_R6_TM1_4 NeoPixelBrightnessBus +#define B_32_R7_TM1_4 NeoPixelBrightnessBus +#define B_32_I0_TM1_4 NeoPixelBrightnessBus +#define B_32_I1_TM1_4 NeoPixelBrightnessBus +//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) + +#endif + +//APA102 +#define B_HS_DOT_3 NeoPixelBrightnessBus //hardware SPI +#define B_SS_DOT_3 NeoPixelBrightnessBus //soft SPI + +//LPD8806 +#define B_HS_LPD_3 NeoPixelBrightnessBus +#define B_SS_LPD_3 NeoPixelBrightnessBus + +//WS2801 +#define B_HS_WS1_3 NeoPixelBrightnessBus +#define B_SS_WS1_3 NeoPixelBrightnessBus + +//P9813 +#define B_HS_P98_3 NeoPixelBrightnessBus +#define B_SS_P98_3 NeoPixelBrightnessBus + +//handles pointer type conversion for all possible bus types +class PolyBus { + public: + static void begin(void* busPtr, uint8_t busType, uint8_t* pins) { + switch (busType) { + case I_NONE: break; + #ifdef ESP8266 + case I_8266_U0_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_8266_U1_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_8266_DM_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_8266_BB_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_8266_U0_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_8266_U1_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_8266_DM_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_8266_BB_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_8266_U0_400_3: (static_cast(busPtr))->Begin(); break; + case I_8266_U1_400_3: (static_cast(busPtr))->Begin(); break; + case I_8266_DM_400_3: (static_cast(busPtr))->Begin(); break; + case I_8266_BB_400_3: (static_cast(busPtr))->Begin(); break; + case I_8266_U0_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_8266_U1_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_8266_DM_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_8266_BB_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_HS_DOT_3: (static_cast(busPtr))->Begin(); break; + case I_HS_LPD_3: (static_cast(busPtr))->Begin(); break; + case I_HS_WS1_3: (static_cast(busPtr))->Begin(); break; + case I_HS_P98_3: (static_cast(busPtr))->Begin(); break; + #endif + #ifdef ARDUINO_ARCH_ESP32 + case I_32_R0_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_32_R1_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_32_R2_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_32_R3_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_32_R4_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_32_R5_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_32_R6_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_32_R7_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_32_I0_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_32_I1_NEO_3: (static_cast(busPtr))->Begin(); break; + case I_32_R0_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_32_R1_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_32_R2_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_32_R3_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_32_R4_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_32_R5_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_32_R6_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_32_R7_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_32_I0_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_32_I1_NEO_4: (static_cast(busPtr))->Begin(); break; + case I_32_R0_400_3: (static_cast(busPtr))->Begin(); break; + case I_32_R1_400_3: (static_cast(busPtr))->Begin(); break; + case I_32_R2_400_3: (static_cast(busPtr))->Begin(); break; + case I_32_R3_400_3: (static_cast(busPtr))->Begin(); break; + case I_32_R4_400_3: (static_cast(busPtr))->Begin(); break; + case I_32_R5_400_3: (static_cast(busPtr))->Begin(); break; + case I_32_R6_400_3: (static_cast(busPtr))->Begin(); break; + case I_32_R7_400_3: (static_cast(busPtr))->Begin(); break; + case I_32_I0_400_3: (static_cast(busPtr))->Begin(); break; + case I_32_I1_400_3: (static_cast(busPtr))->Begin(); break; + case I_32_R0_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_32_R1_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_32_R2_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_32_R3_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_32_R4_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_32_R5_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_32_R6_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_32_R7_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_32_I0_TM1_4: (static_cast(busPtr))->Begin(); break; + case I_32_I1_TM1_4: (static_cast(busPtr))->Begin(); break; + // ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin() + case I_HS_DOT_3: (static_cast(busPtr))->Begin(pins[1], -1, pins[0], -1); break; + case I_HS_LPD_3: (static_cast(busPtr))->Begin(pins[1], -1, pins[0], -1); break; + case I_HS_WS1_3: (static_cast(busPtr))->Begin(pins[1], -1, pins[0], -1); break; + case I_HS_P98_3: (static_cast(busPtr))->Begin(pins[1], -1, pins[0], -1); break; + #endif + case I_SS_DOT_3: (static_cast(busPtr))->Begin(); break; + case I_SS_LPD_3: (static_cast(busPtr))->Begin(); break; + case I_SS_WS1_3: (static_cast(busPtr))->Begin(); break; + case I_SS_P98_3: (static_cast(busPtr))->Begin(); break; + } + }; + static void* create(uint8_t busType, uint8_t* pins, uint16_t len) { + void* busPtr = nullptr; + switch (busType) { + case I_NONE: break; + #ifdef ESP8266 + case I_8266_U0_NEO_3: busPtr = new B_8266_U0_NEO_3(len, pins[0]); break; + case I_8266_U1_NEO_3: busPtr = new B_8266_U1_NEO_3(len, pins[0]); break; + case I_8266_DM_NEO_3: busPtr = new B_8266_DM_NEO_3(len, pins[0]); break; + case I_8266_BB_NEO_3: busPtr = new B_8266_BB_NEO_3(len, pins[0]); break; + case I_8266_U0_NEO_4: busPtr = new B_8266_U0_NEO_4(len, pins[0]); break; + case I_8266_U1_NEO_4: busPtr = new B_8266_U1_NEO_4(len, pins[0]); break; + case I_8266_DM_NEO_4: busPtr = new B_8266_DM_NEO_4(len, pins[0]); break; + case I_8266_BB_NEO_4: busPtr = new B_8266_BB_NEO_4(len, pins[0]); break; + case I_8266_U0_400_3: busPtr = new B_8266_U0_400_3(len, pins[0]); break; + case I_8266_U1_400_3: busPtr = new B_8266_U1_400_3(len, pins[0]); break; + case I_8266_DM_400_3: busPtr = new B_8266_DM_400_3(len, pins[0]); break; + case I_8266_BB_400_3: busPtr = new B_8266_BB_400_3(len, pins[0]); break; + case I_8266_U0_TM1_4: busPtr = new B_8266_U0_TM1_4(len, pins[0]); break; + case I_8266_U1_TM1_4: busPtr = new B_8266_U1_TM1_4(len, pins[0]); break; + case I_8266_DM_TM1_4: busPtr = new B_8266_DM_TM1_4(len, pins[0]); break; + case I_8266_BB_TM1_4: busPtr = new B_8266_BB_TM1_4(len, pins[0]); break; + #endif + #ifdef ARDUINO_ARCH_ESP32 + case I_32_R0_NEO_3: busPtr = new B_32_R0_NEO_3(len, pins[0]); break; + case I_32_R1_NEO_3: busPtr = new B_32_R1_NEO_3(len, pins[0]); break; + case I_32_R2_NEO_3: busPtr = new B_32_R2_NEO_3(len, pins[0]); break; + case I_32_R3_NEO_3: busPtr = new B_32_R3_NEO_3(len, pins[0]); break; + case I_32_R4_NEO_3: busPtr = new B_32_R4_NEO_3(len, pins[0]); break; + case I_32_R5_NEO_3: busPtr = new B_32_R5_NEO_3(len, pins[0]); break; + case I_32_R6_NEO_3: busPtr = new B_32_R6_NEO_3(len, pins[0]); break; + case I_32_R7_NEO_3: busPtr = new B_32_R7_NEO_3(len, pins[0]); break; + case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break; + case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); break; + case I_32_R0_NEO_4: busPtr = new B_32_R0_NEO_4(len, pins[0]); break; + case I_32_R1_NEO_4: busPtr = new B_32_R1_NEO_4(len, pins[0]); break; + case I_32_R2_NEO_4: busPtr = new B_32_R2_NEO_4(len, pins[0]); break; + case I_32_R3_NEO_4: busPtr = new B_32_R3_NEO_4(len, pins[0]); break; + case I_32_R4_NEO_4: busPtr = new B_32_R4_NEO_4(len, pins[0]); break; + case I_32_R5_NEO_4: busPtr = new B_32_R5_NEO_4(len, pins[0]); break; + case I_32_R6_NEO_4: busPtr = new B_32_R6_NEO_4(len, pins[0]); break; + case I_32_R7_NEO_4: busPtr = new B_32_R7_NEO_4(len, pins[0]); break; + case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break; + case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); break; + case I_32_R0_400_3: busPtr = new B_32_R0_400_3(len, pins[0]); break; + case I_32_R1_400_3: busPtr = new B_32_R1_400_3(len, pins[0]); break; + case I_32_R2_400_3: busPtr = new B_32_R2_400_3(len, pins[0]); break; + case I_32_R3_400_3: busPtr = new B_32_R3_400_3(len, pins[0]); break; + case I_32_R4_400_3: busPtr = new B_32_R4_400_3(len, pins[0]); break; + case I_32_R5_400_3: busPtr = new B_32_R5_400_3(len, pins[0]); break; + case I_32_R6_400_3: busPtr = new B_32_R6_400_3(len, pins[0]); break; + case I_32_R7_400_3: busPtr = new B_32_R7_400_3(len, pins[0]); break; + case I_32_I0_400_3: busPtr = new B_32_I0_400_3(len, pins[0]); break; + case I_32_I1_400_3: busPtr = new B_32_I1_400_3(len, pins[0]); break; + case I_32_R0_TM1_4: busPtr = new B_32_R0_TM1_4(len, pins[0]); break; + case I_32_R1_TM1_4: busPtr = new B_32_R1_TM1_4(len, pins[0]); break; + case I_32_R2_TM1_4: busPtr = new B_32_R2_TM1_4(len, pins[0]); break; + case I_32_R3_TM1_4: busPtr = new B_32_R3_TM1_4(len, pins[0]); break; + case I_32_R4_TM1_4: busPtr = new B_32_R4_TM1_4(len, pins[0]); break; + case I_32_R5_TM1_4: busPtr = new B_32_R5_TM1_4(len, pins[0]); break; + case I_32_R6_TM1_4: busPtr = new B_32_R6_TM1_4(len, pins[0]); break; + case I_32_R7_TM1_4: busPtr = new B_32_R7_TM1_4(len, pins[0]); break; + case I_32_I0_TM1_4: busPtr = new B_32_I0_TM1_4(len, pins[0]); break; + case I_32_I1_TM1_4: busPtr = new B_32_I1_TM1_4(len, pins[0]); break; + #endif + // for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat) + case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break; + case I_SS_DOT_3: busPtr = new B_SS_DOT_3(len, pins[1], pins[0]); break; + case I_HS_LPD_3: busPtr = new B_HS_LPD_3(len, pins[1], pins[0]); break; + case I_SS_LPD_3: busPtr = new B_SS_LPD_3(len, pins[1], pins[0]); break; + case I_HS_WS1_3: busPtr = new B_HS_WS1_3(len, pins[1], pins[0]); break; + case I_SS_WS1_3: busPtr = new B_SS_WS1_3(len, pins[1], pins[0]); break; + case I_HS_P98_3: busPtr = new B_HS_P98_3(len, pins[1], pins[0]); break; + case I_SS_P98_3: busPtr = new B_SS_P98_3(len, pins[1], pins[0]); break; + } + begin(busPtr, busType, pins); + return busPtr; + }; + static void show(void* busPtr, uint8_t busType) { + switch (busType) { + case I_NONE: break; + #ifdef ESP8266 + case I_8266_U0_NEO_3: (static_cast(busPtr))->Show(); break; + case I_8266_U1_NEO_3: (static_cast(busPtr))->Show(); break; + case I_8266_DM_NEO_3: (static_cast(busPtr))->Show(); break; + case I_8266_BB_NEO_3: (static_cast(busPtr))->Show(); break; + case I_8266_U0_NEO_4: (static_cast(busPtr))->Show(); break; + case I_8266_U1_NEO_4: (static_cast(busPtr))->Show(); break; + case I_8266_DM_NEO_4: (static_cast(busPtr))->Show(); break; + case I_8266_BB_NEO_4: (static_cast(busPtr))->Show(); break; + case I_8266_U0_400_3: (static_cast(busPtr))->Show(); break; + case I_8266_U1_400_3: (static_cast(busPtr))->Show(); break; + case I_8266_DM_400_3: (static_cast(busPtr))->Show(); break; + case I_8266_BB_400_3: (static_cast(busPtr))->Show(); break; + case I_8266_U0_TM1_4: (static_cast(busPtr))->Show(); break; + case I_8266_U1_TM1_4: (static_cast(busPtr))->Show(); break; + case I_8266_DM_TM1_4: (static_cast(busPtr))->Show(); break; + case I_8266_BB_TM1_4: (static_cast(busPtr))->Show(); break; + #endif + #ifdef ARDUINO_ARCH_ESP32 + case I_32_R0_NEO_3: (static_cast(busPtr))->Show(); break; + case I_32_R1_NEO_3: (static_cast(busPtr))->Show(); break; + case I_32_R2_NEO_3: (static_cast(busPtr))->Show(); break; + case I_32_R3_NEO_3: (static_cast(busPtr))->Show(); break; + case I_32_R4_NEO_3: (static_cast(busPtr))->Show(); break; + case I_32_R5_NEO_3: (static_cast(busPtr))->Show(); break; + case I_32_R6_NEO_3: (static_cast(busPtr))->Show(); break; + case I_32_R7_NEO_3: (static_cast(busPtr))->Show(); break; + case I_32_I0_NEO_3: (static_cast(busPtr))->Show(); break; + case I_32_I1_NEO_3: (static_cast(busPtr))->Show(); break; + case I_32_R0_NEO_4: (static_cast(busPtr))->Show(); break; + case I_32_R1_NEO_4: (static_cast(busPtr))->Show(); break; + case I_32_R2_NEO_4: (static_cast(busPtr))->Show(); break; + case I_32_R3_NEO_4: (static_cast(busPtr))->Show(); break; + case I_32_R4_NEO_4: (static_cast(busPtr))->Show(); break; + case I_32_R5_NEO_4: (static_cast(busPtr))->Show(); break; + case I_32_R6_NEO_4: (static_cast(busPtr))->Show(); break; + case I_32_R7_NEO_4: (static_cast(busPtr))->Show(); break; + case I_32_I0_NEO_4: (static_cast(busPtr))->Show(); break; + case I_32_I1_NEO_4: (static_cast(busPtr))->Show(); break; + case I_32_R0_400_3: (static_cast(busPtr))->Show(); break; + case I_32_R1_400_3: (static_cast(busPtr))->Show(); break; + case I_32_R2_400_3: (static_cast(busPtr))->Show(); break; + case I_32_R3_400_3: (static_cast(busPtr))->Show(); break; + case I_32_R4_400_3: (static_cast(busPtr))->Show(); break; + case I_32_R5_400_3: (static_cast(busPtr))->Show(); break; + case I_32_R6_400_3: (static_cast(busPtr))->Show(); break; + case I_32_R7_400_3: (static_cast(busPtr))->Show(); break; + case I_32_I0_400_3: (static_cast(busPtr))->Show(); break; + case I_32_I1_400_3: (static_cast(busPtr))->Show(); break; + case I_32_R0_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_R1_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_R2_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_R3_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_R4_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_R5_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_R6_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_R7_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_I0_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_I1_TM1_4: (static_cast(busPtr))->Show(); break; + #endif + case I_HS_DOT_3: (static_cast(busPtr))->Show(); break; + case I_SS_DOT_3: (static_cast(busPtr))->Show(); break; + case I_HS_LPD_3: (static_cast(busPtr))->Show(); break; + case I_SS_LPD_3: (static_cast(busPtr))->Show(); break; + case I_HS_WS1_3: (static_cast(busPtr))->Show(); break; + case I_SS_WS1_3: (static_cast(busPtr))->Show(); break; + case I_HS_P98_3: (static_cast(busPtr))->Show(); break; + case I_SS_P98_3: (static_cast(busPtr))->Show(); break; + } + }; + static bool canShow(void* busPtr, uint8_t busType) { + switch (busType) { + case I_NONE: return true; + #ifdef ESP8266 + case I_8266_U0_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U1_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_DM_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_BB_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U0_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U1_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_8266_DM_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_8266_BB_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U0_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U1_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_DM_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_BB_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U0_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U1_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_8266_DM_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_8266_BB_TM1_4: return (static_cast(busPtr))->CanShow(); break; + #endif + #ifdef ARDUINO_ARCH_ESP32 + case I_32_R0_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R1_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R2_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R3_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R4_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R5_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R6_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R7_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_I0_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_I1_NEO_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R0_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R1_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R2_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R3_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R4_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R5_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R6_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R7_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_I0_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_I1_NEO_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R0_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R1_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R2_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R3_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R4_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R5_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R6_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R7_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_I0_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_I1_400_3: return (static_cast(busPtr))->CanShow(); break; + case I_32_R0_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R1_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R2_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R3_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R4_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R5_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R6_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_R7_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_I0_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_I1_TM1_4: return (static_cast(busPtr))->CanShow(); break; + #endif + case I_HS_DOT_3: return (static_cast(busPtr))->CanShow(); break; + case I_SS_DOT_3: return (static_cast(busPtr))->CanShow(); break; + case I_HS_LPD_3: return (static_cast(busPtr))->CanShow(); break; + case I_SS_LPD_3: return (static_cast(busPtr))->CanShow(); break; + case I_HS_WS1_3: return (static_cast(busPtr))->CanShow(); break; + case I_SS_WS1_3: return (static_cast(busPtr))->CanShow(); break; + case I_HS_P98_3: return (static_cast(busPtr))->CanShow(); break; + case I_SS_P98_3: return (static_cast(busPtr))->CanShow(); break; + } + return true; + }; + static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co) { + uint8_t r = c >> 16; + uint8_t g = c >> 8; + uint8_t b = c >> 0; + uint8_t w = c >> 24; + RgbwColor col; + + //TODO make color order override possible on a per-strip basis + #ifdef COLOR_ORDER_OVERRIDE + if (indexPixel >= COO_MIN && indexPixel < COO_MAX) co = COO_ORDER; + #endif + + //reorder channels to selected order + switch (co) + { + case 0: col.G = g; col.R = r; col.B = b; break; //0 = GRB, default + case 1: col.G = r; col.R = g; col.B = b; break; //1 = RGB, common for WS2811 + case 2: col.G = b; col.R = r; col.B = g; break; //2 = BRG + case 3: col.G = r; col.R = b; col.B = g; break; //3 = RBG + case 4: col.G = b; col.R = g; col.B = r; break; //4 = BGR + default: col.G = g; col.R = b; col.B = r; break; //5 = GBR + } + col.W = w; + + switch (busType) { + case I_NONE: break; + #ifdef ESP8266 + case I_8266_U0_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_U1_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_DM_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_BB_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_U0_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_8266_U1_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_8266_DM_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_8266_BB_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_8266_U0_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_U1_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_DM_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_BB_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_U0_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_8266_U1_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_8266_DM_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_8266_BB_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + #endif + #ifdef ARDUINO_ARCH_ESP32 + case I_32_R0_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R1_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R2_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R3_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R4_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R5_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R6_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R7_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_I0_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_I1_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R0_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R1_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R2_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R3_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R4_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R5_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R6_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R7_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_I0_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_I1_NEO_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R0_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R1_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R2_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R3_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R4_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R5_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R6_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R7_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_I0_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_I1_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_32_R0_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R1_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R2_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R3_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R4_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R5_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R6_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_R7_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_I0_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_I1_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + #endif + case I_HS_DOT_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_SS_DOT_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_HS_LPD_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_SS_LPD_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_HS_WS1_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_SS_WS1_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_HS_P98_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_SS_P98_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + } + }; + static void setBrightness(void* busPtr, uint8_t busType, uint8_t b) { + switch (busType) { + case I_NONE: break; + #ifdef ESP8266 + case I_8266_U0_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_U1_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_DM_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_BB_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_U0_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_U1_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_DM_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_BB_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_U0_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_U1_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_DM_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_BB_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_U0_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_U1_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_DM_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_BB_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + #endif + #ifdef ARDUINO_ARCH_ESP32 + case I_32_R0_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R1_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R2_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R3_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R4_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R5_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R6_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R7_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_I0_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_I1_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R0_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R1_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R2_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R3_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R4_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R5_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R6_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R7_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_I0_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_I1_NEO_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R0_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R1_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R2_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R3_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R4_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R5_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R6_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R7_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_I0_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_I1_400_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R0_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R1_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R2_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R3_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R4_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R5_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R6_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_R7_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_I0_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_I1_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + #endif + case I_HS_DOT_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_SS_DOT_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_HS_LPD_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_SS_LPD_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_HS_WS1_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_SS_WS1_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_HS_P98_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_SS_P98_3: (static_cast(busPtr))->SetBrightness(b); break; + } + }; + static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { + RgbwColor col(0,0,0,0); + switch (busType) { + case I_NONE: break; + #ifdef ESP8266 + case I_8266_U0_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_U1_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_DM_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_BB_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_U0_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_U1_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_DM_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_BB_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_U0_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_U1_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_DM_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_BB_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_U0_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_U1_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_DM_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_BB_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + #endif + #ifdef ARDUINO_ARCH_ESP32 + case I_32_R0_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R1_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R2_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R3_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R4_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R5_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R6_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R7_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I0_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I1_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R0_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R1_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R2_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R3_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R4_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R5_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R6_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R7_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I0_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I1_NEO_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R0_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R1_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R2_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R3_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R4_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R5_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R6_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R7_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I0_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I1_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R0_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R1_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R2_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R3_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R4_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R5_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R6_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_R7_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I0_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I1_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + #endif + case I_HS_DOT_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_SS_DOT_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_HS_LPD_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_SS_LPD_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_HS_WS1_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_SS_WS1_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_HS_P98_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_SS_P98_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + } + + #ifdef COLOR_ORDER_OVERRIDE + if (indexPixel >= COO_MIN && indexPixel < COO_MAX) co = COO_ORDER; + #endif + + switch (co) + { + // W G R B + case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default + case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811 + case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG + case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG + case 4: return ((col.W << 24) | (col.R << 8) | (col.B << 16) | (col.G)); //4 = BGR + case 5: return ((col.W << 24) | (col.G << 8) | (col.B << 16) | (col.R)); //5 = GBR + } + return 0; + } + + static void cleanup(void* busPtr, uint8_t busType) { + if (busPtr == nullptr) return; + switch (busType) { + case I_NONE: break; + #ifdef ESP8266 + case I_8266_U0_NEO_3: delete (static_cast(busPtr)); break; + case I_8266_U1_NEO_3: delete (static_cast(busPtr)); break; + case I_8266_DM_NEO_3: delete (static_cast(busPtr)); break; + case I_8266_BB_NEO_3: delete (static_cast(busPtr)); break; + case I_8266_U0_NEO_4: delete (static_cast(busPtr)); break; + case I_8266_U1_NEO_4: delete (static_cast(busPtr)); break; + case I_8266_DM_NEO_4: delete (static_cast(busPtr)); break; + case I_8266_BB_NEO_4: delete (static_cast(busPtr)); break; + case I_8266_U0_400_3: delete (static_cast(busPtr)); break; + case I_8266_U1_400_3: delete (static_cast(busPtr)); break; + case I_8266_DM_400_3: delete (static_cast(busPtr)); break; + case I_8266_BB_400_3: delete (static_cast(busPtr)); break; + case I_8266_U0_TM1_4: delete (static_cast(busPtr)); break; + case I_8266_U1_TM1_4: delete (static_cast(busPtr)); break; + case I_8266_DM_TM1_4: delete (static_cast(busPtr)); break; + case I_8266_BB_TM1_4: delete (static_cast(busPtr)); break; + #endif + #ifdef ARDUINO_ARCH_ESP32 + case I_32_R0_NEO_3: delete (static_cast(busPtr)); break; + case I_32_R1_NEO_3: delete (static_cast(busPtr)); break; + case I_32_R2_NEO_3: delete (static_cast(busPtr)); break; + case I_32_R3_NEO_3: delete (static_cast(busPtr)); break; + case I_32_R4_NEO_3: delete (static_cast(busPtr)); break; + case I_32_R5_NEO_3: delete (static_cast(busPtr)); break; + case I_32_R6_NEO_3: delete (static_cast(busPtr)); break; + case I_32_R7_NEO_3: delete (static_cast(busPtr)); break; + case I_32_I0_NEO_3: delete (static_cast(busPtr)); break; + case I_32_I1_NEO_3: delete (static_cast(busPtr)); break; + case I_32_R0_NEO_4: delete (static_cast(busPtr)); break; + case I_32_R1_NEO_4: delete (static_cast(busPtr)); break; + case I_32_R2_NEO_4: delete (static_cast(busPtr)); break; + case I_32_R3_NEO_4: delete (static_cast(busPtr)); break; + case I_32_R4_NEO_4: delete (static_cast(busPtr)); break; + case I_32_R5_NEO_4: delete (static_cast(busPtr)); break; + case I_32_R6_NEO_4: delete (static_cast(busPtr)); break; + case I_32_R7_NEO_4: delete (static_cast(busPtr)); break; + case I_32_I0_NEO_4: delete (static_cast(busPtr)); break; + case I_32_I1_NEO_4: delete (static_cast(busPtr)); break; + case I_32_R0_400_3: delete (static_cast(busPtr)); break; + case I_32_R1_400_3: delete (static_cast(busPtr)); break; + case I_32_R2_400_3: delete (static_cast(busPtr)); break; + case I_32_R3_400_3: delete (static_cast(busPtr)); break; + case I_32_R4_400_3: delete (static_cast(busPtr)); break; + case I_32_R5_400_3: delete (static_cast(busPtr)); break; + case I_32_R6_400_3: delete (static_cast(busPtr)); break; + case I_32_R7_400_3: delete (static_cast(busPtr)); break; + case I_32_I0_400_3: delete (static_cast(busPtr)); break; + case I_32_I1_400_3: delete (static_cast(busPtr)); break; + case I_32_R0_TM1_4: delete (static_cast(busPtr)); break; + case I_32_R1_TM1_4: delete (static_cast(busPtr)); break; + case I_32_R2_TM1_4: delete (static_cast(busPtr)); break; + case I_32_R3_TM1_4: delete (static_cast(busPtr)); break; + case I_32_R4_TM1_4: delete (static_cast(busPtr)); break; + case I_32_R5_TM1_4: delete (static_cast(busPtr)); break; + case I_32_R6_TM1_4: delete (static_cast(busPtr)); break; + case I_32_R7_TM1_4: delete (static_cast(busPtr)); break; + case I_32_I0_TM1_4: delete (static_cast(busPtr)); break; + case I_32_I1_TM1_4: delete (static_cast(busPtr)); break; + #endif + case I_HS_DOT_3: delete (static_cast(busPtr)); break; + case I_SS_DOT_3: delete (static_cast(busPtr)); break; + case I_HS_LPD_3: delete (static_cast(busPtr)); break; + case I_SS_LPD_3: delete (static_cast(busPtr)); break; + case I_HS_WS1_3: delete (static_cast(busPtr)); break; + case I_SS_WS1_3: delete (static_cast(busPtr)); break; + case I_HS_P98_3: delete (static_cast(busPtr)); break; + case I_SS_P98_3: delete (static_cast(busPtr)); break; + } + } + + //gives back the internal type index (I_XX_XXX_X above) for the input + static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0) { + if (!IS_DIGITAL(busType)) return I_NONE; + if (IS_2PIN(busType)) { //SPI LED chips + bool isHSPI = false; + #ifdef ESP8266 + if (pins[0] == P_8266_HS_MOSI && pins[1] == P_8266_HS_CLK) isHSPI = true; + #else + if(!num) isHSPI = true; // temporary hack to limit use of hardware SPI to a single SPI peripheral: only allow ESP32 hardware serial on segment 0 + #endif + uint8_t t = I_NONE; + switch (busType) { + case TYPE_APA102: t = I_SS_DOT_3; break; + case TYPE_LPD8806: t = I_SS_LPD_3; break; + case TYPE_WS2801: t = I_SS_WS1_3; break; + case TYPE_P9813: t = I_SS_P98_3; break; + default: t=I_NONE; + } + if (t > I_NONE && isHSPI) t--; //hardware SPI has one smaller ID than software + return t; + } else { + #ifdef ESP8266 + uint8_t offset = pins[0] -1; //for driver: 0 = uart0, 1 = uart1, 2 = dma, 3 = bitbang + if (offset > 3) offset = 3; + switch (busType) { + case TYPE_WS2812_RGB: + case TYPE_WS2812_WWA: + return I_8266_U0_NEO_3 + offset; + case TYPE_SK6812_RGBW: + return I_8266_U0_NEO_4 + offset; + case TYPE_WS2811_400KHZ: + return I_8266_U0_400_3 + offset; + } + #else //ESP32 + uint8_t offset = num; //RMT bus # == bus index in BusManager + if (offset > 9) return I_NONE; + switch (busType) { + case TYPE_WS2812_RGB: + case TYPE_WS2812_WWA: + return I_32_R0_NEO_3 + offset; + case TYPE_SK6812_RGBW: + return I_32_R0_NEO_4 + offset; + case TYPE_WS2811_400KHZ: + return I_32_R0_400_3 + offset; + } + #endif + } + return I_NONE; + } +}; + +#endif \ No newline at end of file diff --git a/wled00/button.cpp b/wled00/button.cpp index 7adbf68bd..458ff22a7 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -17,9 +17,7 @@ void shortPressAction() bool isButtonPressed() { - #if defined(BTNPIN) && BTNPIN > -1 - if (digitalRead(BTNPIN) == LOW) return true; - #endif + if (btnPin>=0 && digitalRead(btnPin) == LOW) return true; #ifdef TOUCHPIN if (touchRead(TOUCHPIN) <= TOUCH_THRESHOLD) return true; #endif @@ -29,8 +27,7 @@ bool isButtonPressed() void handleButton() { -#if (defined(BTNPIN) && BTNPIN > -1) || defined(TOUCHPIN) - if (!buttonEnabled) return; + if (btnPin<0 || !buttonEnabled) return; if (isButtonPressed()) //pressed { @@ -75,7 +72,6 @@ void handleButton() buttonWaitTime = 0; shortPressAction(); } -#endif } void handleIO() @@ -88,37 +84,39 @@ void handleIO() lastOnTime = millis(); if (offMode) { - #if RLYPIN >= 0 - digitalWrite(RLYPIN, RLYMDE); - #endif + if (rlyPin>=0) { + pinMode(rlyPin, OUTPUT); + digitalWrite(rlyPin, rlyMde); + } offMode = false; } } else if (millis() - lastOnTime > 600) { - if (!offMode) { - #if LEDPIN == LED_BUILTIN - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, HIGH); + if (!offMode) { + #ifdef ESP8266 + //turn off built-in LED if strip is turned off + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); #endif - #if RLYPIN >= 0 - digitalWrite(RLYPIN, !RLYMDE); - #endif - } + if (rlyPin>=0) { + pinMode(rlyPin, OUTPUT); + digitalWrite(rlyPin, !rlyMde); + } + } offMode = true; } - #if AUXPIN >= 0 //output - if (auxActive || auxActiveBefore) + if (auxPin>=1 && (auxActive || auxActiveBefore)) { if (!auxActiveBefore) { auxActiveBefore = true; switch (auxTriggeredState) { - case 0: pinMode(AUXPIN, INPUT); break; - case 1: pinMode(AUXPIN, OUTPUT); digitalWrite(AUXPIN, HIGH); break; - case 2: pinMode(AUXPIN, OUTPUT); digitalWrite(AUXPIN, LOW); break; + case 0: pinMode(auxPin, INPUT); break; + case 1: pinMode(auxPin, OUTPUT); digitalWrite(auxPin, HIGH); break; + case 2: pinMode(auxPin, OUTPUT); digitalWrite(auxPin, LOW); break; } auxStartTime = millis(); } @@ -128,11 +126,10 @@ void handleIO() auxActiveBefore = false; switch (auxDefaultState) { - case 0: pinMode(AUXPIN, INPUT); break; - case 1: pinMode(AUXPIN, OUTPUT); digitalWrite(AUXPIN, HIGH); break; - case 2: pinMode(AUXPIN, OUTPUT); digitalWrite(AUXPIN, LOW); break; + case 0: pinMode(auxPin, INPUT); break; + case 1: pinMode(auxPin, OUTPUT); digitalWrite(auxPin, HIGH); break; + case 2: pinMode(auxPin, OUTPUT); digitalWrite(auxPin, LOW); break; } } } - #endif } diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 43d2615ee..b72fa32c2 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -30,14 +30,12 @@ void deserializeConfig() { return; } - //deserializeJson(doc, json); - - //int rev_major = doc[F("rev")][0]; // 1 - //int rev_minor = doc[F("rev")][1]; // 0 + //int rev_major = doc["rev"][0]; // 1 + //int rev_minor = doc["rev"][1]; // 0 //long vid = doc[F("vid")]; // 2010020 - JsonObject id = doc[F("id")]; + JsonObject id = doc["id"]; getStringFromJson(cmDNS, id[F("mdns")], 33); getStringFromJson(serverDescription, id[F("name")], 33); getStringFromJson(alexaInvocationName, id[F("inv")], 33); @@ -49,9 +47,9 @@ void deserializeConfig() { //If it is present however, we will use it getStringFromJson(clientPass, nw_ins_0["psk"], 65); - JsonArray nw_ins_0_ip = nw_ins_0[F("ip")]; - JsonArray nw_ins_0_gw = nw_ins_0[F("gw")]; - JsonArray nw_ins_0_sn = nw_ins_0[F("sn")]; + JsonArray nw_ins_0_ip = nw_ins_0["ip"]; + JsonArray nw_ins_0_gw = nw_ins_0["gw"]; + JsonArray nw_ins_0_sn = nw_ins_0["sn"]; for (byte i = 0; i < 4; i++) { CJSON(staticIP[i], nw_ins_0_ip[i]); @@ -59,7 +57,7 @@ void deserializeConfig() { CJSON(staticSubnet[i], nw_ins_0_sn[i]); } - JsonObject ap = doc[F("ap")]; + JsonObject ap = doc["ap"]; getStringFromJson(apSSID, ap[F("ssid")], 33); getStringFromJson(apPass, ap["psk"] , 65); //normally not present due to security //int ap_pskl = ap[F("pskl")]; @@ -74,11 +72,11 @@ void deserializeConfig() { #ifdef WLED_USE_ETHERNET JsonObject ethernet = doc[F("eth")]; - CJSON(ethernetType, ethernet[F("type")]); + CJSON(ethernetType, ethernet["type"]); #endif /* - JsonArray ap_ip = ap[F("ip")]; + JsonArray ap_ip = ap["ip"]; for (byte i = 0; i < 4; i++) { apIP[i] = ap_ip; }*/ @@ -89,44 +87,94 @@ void deserializeConfig() { JsonObject hw = doc[F("hw")]; + // initialize LED pins and lengths prior to other HW JsonObject hw_led = hw[F("led")]; + CJSON(ledCount, hw_led[F("total")]); if (ledCount > MAX_LEDS) ledCount = MAX_LEDS; CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); - CJSON(strip.reverseMode, hw_led[F("rev")]); + CJSON(strip.reverseMode, hw_led["rev"]); CJSON(strip.rgbwMode, hw_led[F("rgbwm")]); - JsonObject hw_led_ins_0 = hw_led[F("ins")][0]; - //bool hw_led_ins_0_en = hw_led_ins_0[F("en")]; // true - //int hw_led_ins_0_start = hw_led_ins_0[F("start")]; // 0 - //int hw_led_ins_0_len = hw_led_ins_0[F("len")]; // 1200 + JsonArray ins = hw_led["ins"]; + uint8_t s = 0; //bus iterator + useRGBW = false; + busses.removeAll(); + uint32_t mem = 0; + for (JsonObject elm : ins) { + if (s >= WLED_MAX_BUSSES) break; + uint8_t pins[5] = {255, 255, 255, 255, 255}; + JsonArray pinArr = elm[F("pin")]; + if (pinArr.size() == 0) continue; + pins[0] = pinArr[0]; + uint8_t i = 0; + for (int p : pinArr) { + pins[i] = p; + i++; + if (i>4) break; + } - //int hw_led_ins_0_pin_0 = hw_led_ins_0[F("pin")][0]; // 2 - - strip.setColorOrder(hw_led_ins_0[F("order")]); - //bool hw_led_ins_0_rev = hw_led_ins_0[F("rev")]; // false - skipFirstLed = hw_led_ins_0[F("skip")]; // 0 - useRGBW = (hw_led_ins_0[F("type")] == TYPE_SK6812_RGBW); + uint16_t length = elm[F("len")]; + if (length==0) continue; + uint8_t colorOrder = (int)elm[F("order")]; + //only use skip from the first strip (this shouldn't have been in ins obj. but remains here for compatibility) + if (s==0) skipFirstLed = elm[F("skip")]; + uint16_t start = elm[F("start")] | 0; + if (start >= ledCount) continue; + //limit length of strip if it would exceed total configured LEDs + if (start + length > ledCount) length = ledCount - start; + uint8_t ledType = elm["type"] | TYPE_WS2812_RGB; + bool reversed = elm["rev"]; + //RGBW mode is enabled if at least one of the strips is RGBW + useRGBW = (useRGBW || BusManager::isRgbw(ledType)); + s++; + BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed); + mem += busses.memUsage(bc); + if (mem <= MAX_LED_MEMORY) busses.add(bc); + } + strip.finalizeInit(useRGBW, ledCount, skipFirstLed); JsonObject hw_btn_ins_0 = hw[F("btn")][F("ins")][0]; - buttonEnabled = hw_btn_ins_0[F("en")] | buttonEnabled; - - //int hw_btn_ins_0_pin_0 = hw_btn_ins_0[F("pin")][0]; // 0 + CJSON(buttonEnabled, hw_btn_ins_0["type"]); + int hw_btn_pin = hw_btn_ins_0[F("pin")][0]; + if (pinManager.allocatePin(hw_btn_pin,false)) { + btnPin = hw_btn_pin; + pinMode(btnPin, INPUT_PULLUP); + } else { + btnPin = -1; + } JsonArray hw_btn_ins_0_macros = hw_btn_ins_0[F("macros")]; CJSON(macroButton, hw_btn_ins_0_macros[0]); CJSON(macroLongPress,hw_btn_ins_0_macros[1]); CJSON(macroDoublePress, hw_btn_ins_0_macros[2]); - //int hw_btn_ins_0_type = hw_btn_ins_0[F("type")]; // 0 + //int hw_btn_ins_0_type = hw_btn_ins_0["type"]; // 0 - //int hw_ir_pin = hw[F("ir")][F("pin")]; // 4 - CJSON(irEnabled, hw[F("ir")][F("type")]); // 0 + #ifndef WLED_DISABLE_INFRARED + int hw_ir_pin = hw["ir"]["pin"]; // 4 + if (pinManager.allocatePin(hw_ir_pin,false)) { + irPin = hw_ir_pin; + } else { + irPin = -1; + } + #endif + CJSON(irEnabled, hw["ir"]["type"]); - //int hw_relay_pin = hw[F("relay")][F("pin")]; // 12 - //bool hw_relay_rev = hw[F("relay")][F("rev")]; // false + JsonObject relay = hw[F("relay")]; + + int hw_relay_pin = relay["pin"]; + if (pinManager.allocatePin(hw_relay_pin,true)) { + rlyPin = hw_relay_pin; + pinMode(rlyPin, OUTPUT); + } else { + rlyPin = -1; + } + if (relay.containsKey("rev")) { + rlyMde = !relay["rev"]; + } //int hw_status_pin = hw[F("status")][F("pin")]; // -1 @@ -135,7 +183,7 @@ void deserializeConfig() { CJSON(strip.paletteBlend, light[F("pal-mode")]); float light_gc_bri = light[F("gc")]["bri"]; - float light_gc_col = light[F("gc")][F("col")]; // 2.8 + float light_gc_col = light[F("gc")]["col"]; // 2.8 if (light_gc_bri > 1.5) strip.gammaCorrectBri = true; else if (light_gc_bri > 0.5) strip.gammaCorrectBri = false; if (light_gc_col > 1.5) strip.gammaCorrectCol = true; @@ -175,13 +223,13 @@ void deserializeConfig() { CJSON(udpPort, if_sync[F("port0")]); // 21324 CJSON(udpPort2, if_sync[F("port1")]); // 65506 - JsonObject if_sync_recv = if_sync[F("recv")]; + JsonObject if_sync_recv = if_sync["recv"]; CJSON(receiveNotificationBrightness, if_sync_recv["bri"]); - CJSON(receiveNotificationColor, if_sync_recv[F("col")]); + CJSON(receiveNotificationColor, if_sync_recv["col"]); CJSON(receiveNotificationEffects, if_sync_recv[F("fx")]); receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); - JsonObject if_sync_send = if_sync[F("send")]; + JsonObject if_sync_send = if_sync["send"]; CJSON(notifyDirectDefault, if_sync_send[F("dir")]); notifyDirect = notifyDirectDefault; CJSON(notifyButton, if_sync_send[F("btn")]); @@ -190,9 +238,13 @@ void deserializeConfig() { CJSON(notifyMacro, if_sync_send[F("macro")]); CJSON(notifyTwice, if_sync_send[F("twice")]); - JsonObject if_live = interfaces[F("live")]; - CJSON(receiveDirect, if_live[F("en")]); - CJSON(e131Port, if_live[F("port")]); // 5568 + JsonObject if_nodes = interfaces["nodes"]; + CJSON(nodeListEnabled, if_nodes[F("list")]); + CJSON(nodeBroadcastEnabled, if_nodes[F("bcast")]); + + JsonObject if_live = interfaces["live"]; + CJSON(receiveDirect, if_live["en"]); + CJSON(e131Port, if_live["port"]); // 5568 CJSON(e131Multicast, if_live[F("mc")]); JsonObject if_live_dmx = if_live[F("dmx")]; @@ -212,19 +264,19 @@ void deserializeConfig() { CJSON(macroAlexaOn, interfaces[F("va")][F("macros")][0]); CJSON(macroAlexaOff, interfaces[F("va")][F("macros")][1]); - const char* apikey = interfaces[F("blynk")][F("token")] | "Hidden"; + const char* apikey = interfaces["blynk"][F("token")] | "Hidden"; tdd = strnlen(apikey, 36); if (tdd > 20 || tdd == 0) getStringFromJson(blynkApiKey, apikey, 36); //normally not present due to security - JsonObject if_blynk = interfaces[F("blynk")]; + JsonObject if_blynk = interfaces["blynk"]; getStringFromJson(blynkHost, if_blynk[F("host")], 33); - CJSON(blynkPort, if_blynk[F("port")]); + CJSON(blynkPort, if_blynk["port"]); - JsonObject if_mqtt = interfaces[F("mqtt")]; - CJSON(mqttEnabled, if_mqtt[F("en")]); + JsonObject if_mqtt = interfaces["mqtt"]; + CJSON(mqttEnabled, if_mqtt["en"]); getStringFromJson(mqttServer, if_mqtt[F("broker")], 33); - CJSON(mqttPort, if_mqtt[F("port")]); // 1883 + CJSON(mqttPort, if_mqtt["port"]); // 1883 getStringFromJson(mqttUser, if_mqtt[F("user")], 41); getStringFromJson(mqttPass, if_mqtt["psk"], 41); //normally not present due to security getStringFromJson(mqttClientID, if_mqtt[F("cid")], 41); @@ -233,23 +285,23 @@ void deserializeConfig() { getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // "" JsonObject if_hue = interfaces[F("hue")]; - CJSON(huePollingEnabled, if_hue[F("en")]); - CJSON(huePollLightId, if_hue[F("id")]); + CJSON(huePollingEnabled, if_hue["en"]); + CJSON(huePollLightId, if_hue["id"]); tdd = if_hue[F("iv")] | -1; if (tdd >= 2) huePollIntervalMs = tdd * 100; - JsonObject if_hue_recv = if_hue[F("recv")]; + JsonObject if_hue_recv = if_hue["recv"]; CJSON(hueApplyOnOff, if_hue_recv["on"]); CJSON(hueApplyBri, if_hue_recv["bri"]); - CJSON(hueApplyColor, if_hue_recv[F("col")]); + CJSON(hueApplyColor, if_hue_recv["col"]); - JsonArray if_hue_ip = if_hue[F("ip")]; + JsonArray if_hue_ip = if_hue["ip"]; for (byte i = 0; i < 4; i++) CJSON(hueIP[i], if_hue_ip[i]); JsonObject if_ntp = interfaces[F("ntp")]; - CJSON(ntpEnabled, if_ntp[F("en")]); + CJSON(ntpEnabled, if_ntp["en"]); getStringFromJson(ntpServerName, if_ntp[F("host")], 33); // "1.wled.pool.ntp.org" CJSON(currentTimezone, if_ntp[F("tz")]); CJSON(utcOffsetSecs, if_ntp[F("offset")]); @@ -294,7 +346,7 @@ void deserializeConfig() { CJSON(timerWeekday[it], timer[F("dow")]); if (timerWeekday[it] != dowPrev) { //present in JSON timerWeekday[it] <<= 1; //add active bit - int act = timer[F("en")] | actPrev; + int act = timer["en"] | actPrev; if (act) timerWeekday[it]++; } @@ -341,7 +393,6 @@ void serializeConfig() { DynamicJsonDocument doc(JSON_BUFFER_SIZE); - //{ //scope this to reduce stack size JsonArray rev = doc.createNestedArray("rev"); rev.add(1); //major settings revision rev.add(0); //minor settings revision @@ -390,7 +441,7 @@ void serializeConfig() { #ifdef WLED_USE_ETHERNET JsonObject ethernet = doc.createNestedObject("eth"); - ethernet[F("type")] = ethernetType; + ethernet["type"] = ethernetType; #endif JsonObject hw = doc.createNestedObject("hw"); @@ -399,83 +450,69 @@ void serializeConfig() { hw_led[F("total")] = ledCount; hw_led[F("maxpwr")] = strip.ablMilliampsMax; hw_led[F("ledma")] = strip.milliampsPerLed; - hw_led[F("rev")] = strip.reverseMode; + hw_led["rev"] = strip.reverseMode; hw_led[F("rgbwm")] = strip.rgbwMode; JsonArray hw_led_ins = hw_led.createNestedArray("ins"); - JsonObject hw_led_ins_0 = hw_led_ins.createNestedObject(); - hw_led_ins_0[F("en")] = true; - hw_led_ins_0[F("start")] = 0; - hw_led_ins_0[F("len")] = ledCount; - JsonArray hw_led_ins_0_pin = hw_led_ins_0.createNestedArray("pin"); - hw_led_ins_0_pin.add(LEDPIN); - #ifdef DATAPIN - hw_led_ins_0_pin.add(DATAPIN); - #endif - hw_led_ins_0[F("order")] = strip.getColorOrder(); - hw_led_ins_0[F("rev")] = false; - hw_led_ins_0[F("skip")] = skipFirstLed ? 1 : 0; - - //this is very crude and temporary - byte ledType = TYPE_WS2812_RGB; - if (useRGBW) ledType = TYPE_SK6812_RGBW; - #ifdef USE_WS2801 - ledType = TYPE_WS2801; - #endif - #ifdef USE_APA102 - ledType = TYPE_APA102; - #endif - #ifdef USE_LPD8806 - ledType = TYPE_LPD8806; - #endif - #ifdef USE_P9813 - ledType = TYPE_P9813; - #endif - #ifdef USE_TM1814 - ledType = TYPE_TM1814; - #endif - - hw_led_ins_0[F("type")] = ledType; + for (uint8_t s = 0; s < busses.getNumBusses(); s++) { + Bus *bus = busses.getBus(s); + if (!bus || bus->getLength()==0) break; + JsonObject ins = hw_led_ins.createNestedObject(); + ins["en"] = true; + ins[F("start")] = bus->getStart(); + ins[F("len")] = bus->getLength(); + JsonArray ins_pin = ins.createNestedArray("pin"); + uint8_t pins[5]; + uint8_t nPins = bus->getPins(pins); + for (uint8_t i = 0; i < nPins; i++) ins_pin.add(pins[i]); + ins[F("order")] = bus->getColorOrder(); + ins["rev"] = bus->reversed; + ins[F("skip")] = (skipFirstLed && s == 0) ? 1 : 0; + ins["type"] = bus->getType(); + } JsonObject hw_btn = hw.createNestedObject("btn"); JsonArray hw_btn_ins = hw_btn.createNestedArray("ins"); - #if defined(BTNPIN) && BTNPIN > -1 + // button BTNPIN JsonObject hw_btn_ins_0 = hw_btn_ins.createNestedObject(); - hw_btn_ins_0[F("type")] = (buttonEnabled) ? BTN_TYPE_PUSH : BTN_TYPE_NONE; + hw_btn_ins_0["type"] = (buttonEnabled) ? BTN_TYPE_PUSH : BTN_TYPE_NONE; JsonArray hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin"); - hw_btn_ins_0_pin.add(BTNPIN); + hw_btn_ins_0_pin.add(btnPin); JsonArray hw_btn_ins_0_macros = hw_btn_ins_0.createNestedArray("macros"); hw_btn_ins_0_macros.add(macroButton); hw_btn_ins_0_macros.add(macroLongPress); hw_btn_ins_0_macros.add(macroDoublePress); + + #ifndef WLED_DISABLE_INFRARED + if (irPin>=0) { + JsonObject hw_ir = hw.createNestedObject("ir"); + hw_ir["pin"] = irPin; + hw_ir[F("type"] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled ) + } #endif - #if defined(IRPIN) && IRPIN > -1 - JsonObject hw_ir = hw.createNestedObject("ir"); - hw_ir[F("pin")] = IRPIN; - hw_ir[F("type")] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled ) - #endif + JsonObject hw_relay = hw.createNestedObject(F("relay")); + hw_relay["pin"] = rlyPin; + hw_relay["rev"] = !rlyMde; - #if defined(RLYPIN) && RLYPIN > -1 - JsonObject hw_relay = hw.createNestedObject("relay"); - hw_relay[F("pin")] = RLYPIN; - hw_relay[F("rev")] = (RLYMDE) ? false : true; - JsonObject hw_status = hw.createNestedObject("status"); - hw_status[F("pin")] = -1; - #endif + //JsonObject hw_status = hw.createNestedObject("status"); + //hw_status["pin"] = -1; - JsonObject light = doc.createNestedObject("light"); + JsonObject hw_aux = hw.createNestedObject("aux"); + hw_aux["pin"] = auxPin; + + JsonObject light = doc.createNestedObject(F("light")); light[F("scale-bri")] = briMultiplier; light[F("pal-mode")] = strip.paletteBlend; JsonObject light_gc = light.createNestedObject("gc"); light_gc["bri"] = (strip.gammaCorrectBri) ? 2.8 : 1.0; - light_gc[F("col")] = (strip.gammaCorrectCol) ? 2.8 : 1.0; + light_gc["col"] = (strip.gammaCorrectCol) ? 2.8 : 1.0; JsonObject light_tr = light.createNestedObject("tr"); light_tr[F("mode")] = fadeTransition; @@ -498,7 +535,7 @@ void serializeConfig() { JsonObject def_cy = def.createNestedObject("cy"); def_cy["on"] = presetCyclingEnabled; - JsonArray def_cy_range = def_cy.createNestedArray("range"); + JsonArray def_cy_range = def_cy.createNestedArray(F("range")); def_cy_range.add(presetCycleMin); def_cy_range.add(presetCycleMax); def_cy[F("dur")] = presetCycleTime; @@ -512,7 +549,7 @@ void serializeConfig() { JsonObject if_sync_recv = if_sync.createNestedObject("recv"); if_sync_recv["bri"] = receiveNotificationBrightness; - if_sync_recv[F("col")] = receiveNotificationColor; + if_sync_recv["col"] = receiveNotificationColor; if_sync_recv[F("fx")] = receiveNotificationEffects; JsonObject if_sync_send = if_sync.createNestedObject("send"); @@ -523,9 +560,13 @@ void serializeConfig() { if_sync_send[F("macro")] = notifyMacro; if_sync_send[F("twice")] = notifyTwice; + JsonObject if_nodes = interfaces.createNestedObject("nodes"); + if_nodes[F("list")] = nodeListEnabled; + if_nodes[F("bcast")] = nodeBroadcastEnabled; + JsonObject if_live = interfaces.createNestedObject("live"); - if_live[F("en")] = receiveDirect; - if_live[F("port")] = e131Port; + if_live["en"] = receiveDirect; + if_live["port"] = e131Port; if_live[F("mc")] = e131Multicast; JsonObject if_live_dmx = if_live.createNestedObject("dmx"); @@ -547,29 +588,29 @@ void serializeConfig() { JsonObject if_blynk = interfaces.createNestedObject("blynk"); if_blynk[F("token")] = strlen(blynkApiKey) ? "Hidden":""; if_blynk[F("host")] = blynkHost; - if_blynk[F("port")] = blynkPort; + if_blynk["port"] = blynkPort; JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); - if_mqtt[F("en")] = mqttEnabled; + if_mqtt["en"] = mqttEnabled; if_mqtt[F("broker")] = mqttServer; - if_mqtt[F("port")] = mqttPort; + if_mqtt["port"] = mqttPort; if_mqtt[F("user")] = mqttUser; if_mqtt[F("pskl")] = strlen(mqttPass); if_mqtt[F("cid")] = mqttClientID; - JsonObject if_mqtt_topics = if_mqtt.createNestedObject("topics"); + JsonObject if_mqtt_topics = if_mqtt.createNestedObject(F("topics")); if_mqtt_topics[F("device")] = mqttDeviceTopic; if_mqtt_topics[F("group")] = mqttGroupTopic; JsonObject if_hue = interfaces.createNestedObject("hue"); - if_hue[F("en")] = huePollingEnabled; - if_hue[F("id")] = huePollLightId; + if_hue["en"] = huePollingEnabled; + if_hue["id"] = huePollLightId; if_hue[F("iv")] = huePollIntervalMs / 100; JsonObject if_hue_recv = if_hue.createNestedObject("recv"); if_hue_recv["on"] = hueApplyOnOff; if_hue_recv["bri"] = hueApplyBri; - if_hue_recv[F("col")] = hueApplyColor; + if_hue_recv["col"] = hueApplyColor; JsonArray if_hue_ip = if_hue.createNestedArray("ip"); for (byte i = 0; i < 4; i++) { @@ -577,7 +618,7 @@ void serializeConfig() { } JsonObject if_ntp = interfaces.createNestedObject("ntp"); - if_ntp[F("en")] = ntpEnabled; + if_ntp["en"] = ntpEnabled; if_ntp[F("host")] = ntpServerName; if_ntp[F("tz")] = currentTimezone; if_ntp[F("offset")] = utcOffsetSecs; @@ -593,10 +634,10 @@ void serializeConfig() { ol[F("o5m")] = analogClock5MinuteMarks; ol[F("osec")] = analogClockSecondsTrail; - JsonObject timers = doc.createNestedObject("timers"); + JsonObject timers = doc.createNestedObject(F("timers")); - JsonObject cntdwn = timers.createNestedObject("cntdwn"); - JsonArray goal = cntdwn.createNestedArray("goal"); + JsonObject cntdwn = timers.createNestedObject(F("cntdwn")); + JsonArray goal = cntdwn.createNestedArray(F("goal")); goal.add(countdownYear); goal.add(countdownMonth); goal.add(countdownDay); goal.add(countdownHour); goal.add(countdownMin); goal.add(countdownSec); cntdwn[F("macro")] = macroCountdown; @@ -606,7 +647,7 @@ void serializeConfig() { for (byte i = 0; i < 8; i++) { if (timerMacro[i] == 0 && timerHours[i] == 0 && timerMinutes[i] == 0) continue; JsonObject timers_ins0 = timers_ins.createNestedObject(); - timers_ins0[F("en")] = (timerWeekday[i] & 0x01); + timers_ins0["en"] = (timerWeekday[i] & 0x01); timers_ins0[F("hour")] = timerHours[i]; timers_ins0[F("min")] = timerMinutes[i]; timers_ins0[F("macro")] = timerMacro[i]; @@ -626,7 +667,7 @@ void serializeConfig() { dmx[F("start")] = DMXStart; dmx[F("start-led")] = DMXStartLED; - JsonArray dmx_fixmap = dmx.createNestedArray("fixmap"); + JsonArray dmx_fixmap = dmx.createNestedArray(F("fixmap")); for (byte i = 0; i < 15; i++) dmx_fixmap.add(DMXFixtureMap[i]); #endif @@ -652,7 +693,7 @@ bool deserializeConfigSec() { JsonObject nw_ins_0 = doc["nw"][F("ins")][0]; getStringFromJson(clientPass, nw_ins_0["psk"], 65); - JsonObject ap = doc[F("ap")]; + JsonObject ap = doc["ap"]; getStringFromJson(apPass, ap["psk"] , 65); JsonObject interfaces = doc["if"]; @@ -662,7 +703,7 @@ bool deserializeConfigSec() { if (tdd > 20 || tdd == 0) getStringFromJson(blynkApiKey, apikey, 36); - JsonObject if_mqtt = interfaces[F("mqtt")]; + JsonObject if_mqtt = interfaces["mqtt"]; getStringFromJson(mqttPass, if_mqtt["psk"], 41); getStringFromJson(hueApiKey, interfaces[F("hue")][F("key")], 47); diff --git a/wled00/const.h b/wled00/const.h index 933d301cf..8ce633726 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -13,6 +13,12 @@ //increase if you need more #define WLED_MAX_USERMODS 4 +#ifdef ESP8266 +#define WLED_MAX_BUSSES 3 +#else +#define WLED_MAX_BUSSES 10 +#endif + //Usermod IDs #define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present #define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID @@ -95,6 +101,7 @@ #define TYPE_GS8608 23 //same driver as WS2812, but will require signal 2x per second (else displays test pattern) #define TYPE_WS2811_400KHZ 24 //half-speed WS2812 protocol, used by very old WS2811 units #define TYPE_SK6812_RGBW 30 +#define TYPE_TM1814 31 //"Analog" types (PWM) (32-47) #define TYPE_ONOFF 40 //binary output (relays etc.) #define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel @@ -107,8 +114,11 @@ #define TYPE_APA102 51 #define TYPE_LPD8806 52 #define TYPE_P9813 53 -#define TYPE_TM1814 54 +#define IS_DIGITAL(t) (t & 0x10) //digital are 16-31 and 48-63 +#define IS_PWM(t) (t > 40 && t < 46) +#define NUM_PWM_PINS(t) (t - 40) //for analog PWM 41-45 only +#define IS_2PIN(t) (t > 47) //Color orders #define COL_ORDER_GRB 0 //GRB(w),defaut @@ -128,10 +138,13 @@ #define BTN_TYPE_SWITCH_ACT_HIGH 5 //not implemented //Ethernet board types +#define WLED_NUM_ETH_TYPES 5 + #define WLED_ETH_NONE 0 #define WLED_ETH_WT32_ETH01 1 #define WLED_ETH_ESP32_POE 2 #define WLED_ETH_WESP32 3 +#define WLED_ETH_QUINLED 4 //Hue error codes #define HUE_ERROR_INACTIVE 0 @@ -159,6 +172,9 @@ #define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached #define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist #define ERR_FS_GENERAL 19 // A general unspecified filesystem error occured +#define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented) +#define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented) +#define ERR_UNDERVOLT 32 // An attached voltmeter has measured a voltage below the threshold (not implemented) //Timer mode types #define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness @@ -171,18 +187,40 @@ // maximum number of LEDs - more than 1500 LEDs (or 500 DMA "LEDPIN 3" driven ones) will cause a low memory condition on ESP8266 #ifndef MAX_LEDS -#define MAX_LEDS 1500 +#ifdef ESP8266 +#define MAX_LEDS 8192 //rely on memory limit to limit this to 1600 LEDs +#else +#define MAX_LEDS 8192 +#endif #endif -#define MAX_LEDS_DMA 500 +#ifndef MAX_LED_MEMORY +#ifdef ESP8266 +#define MAX_LED_MEMORY 5000 +#else +#define MAX_LED_MEMORY 64000 +#endif +#endif + +#ifndef MAX_LEDS_PER_BUS +#define MAX_LEDS_PER_BUS 4096 +#endif // string temp buffer (now stored in stack locally) #define OMAX 2048 #define E131_MAX_UNIVERSE_COUNT 9 -#define ABL_MILLIAMPS_DEFAULT 850; // auto lower brightness to stay close to milliampere limit +#define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit +// PWM settings +#ifndef WLED_PWM_FREQ +#ifdef ESP8266 + #define WLED_PWM_FREQ 880 //PWM frequency proven as good for LEDs +#else + #define WLED_PWM_FREQ 19531 +#endif +#endif #define TOUCH_THRESHOLD 32 // limit to recognize a touch, higher value means more sensitive @@ -193,4 +231,24 @@ #define JSON_BUFFER_SIZE 16384 #endif +// Maximum size of node map (list of other WLED instances) +#ifdef ESP8266 + #define WLED_MAX_NODES 15 +#else + #define WLED_MAX_NODES 150 +#endif + +//this is merely a default now and can be changed at runtime +#ifndef LEDPIN +#define LEDPIN 2 +#endif + +#ifdef WLED_ENABLE_DMX +#if (LEDPIN == 2) + #undef LEDPIN + #define LEDPIN 3 + #warning "Pin conflict compiling with DMX and LEDs on pin 2. The default LED pin has been changed to pin 3." +#endif +#endif + #endif diff --git a/wled00/data/index.css b/wled00/data/index.css index 606d69709..827f64dd0 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -90,9 +90,10 @@ button { #namelabel { position: fixed; - bottom: calc(var(--bh) + 5px); + bottom: calc(var(--bh) + 6px); right: 4px; color: var(--c-6); + cursor: pointer; writing-mode: vertical-rl; } @@ -165,6 +166,14 @@ button { color: var(--c-d); } +.search-cancel-icon { + position: absolute; + right: 8px; + top: 9px; + cursor: pointer; + display: none; +} + .flr { float: right; cursor: pointer; @@ -214,7 +223,7 @@ button { .top button { padding: var(--tbp); -} +} .bot button { padding: var(--bbp); @@ -298,7 +307,7 @@ button { top: -1px; z-index: 1; margin-top: 1px; - width: 274px; + width: 272px; margin: auto; border-radius: 25px; } @@ -307,10 +316,6 @@ button { top: 28px; } -#staytop2 { - top: 56px; -} - #fxb0 { margin-bottom: 2px; filter: drop-shadow(0 0 1px #000); @@ -366,10 +371,18 @@ button { z-index: 3; } -#rover { +#rover, #nodes { z-index: 2; } +#ndlt { + margin: 12px 0; +} + +.valtd i { + font-size: 14px; +} + #roverstar { position: fixed; top: calc(var(--th) + 5px); @@ -394,11 +407,15 @@ button { display: inline-block; } -#kv { +#kv, #kn { max-width: 490px; display: inline-block; } +#kn td { + padding-bottom: 12px; +} + #lv { max-width: 600px; display: inline-block; @@ -535,6 +552,7 @@ input[type=range]:active + .sliderbubble { font-size: 19px; background-color: var(--c-3); color: var(--c-f); + cursor: pointer; border: 0px solid white; border-radius: 25px; transition-duration: 0.5s; @@ -655,8 +673,8 @@ input[type=number]:focus, input[type=text]:focus { background: var(--c-6); } -input[type=number]::-webkit-inner-spin-button, -input[type=number]::-webkit-outer-spin-button { +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; } @@ -729,7 +747,7 @@ input[type=number]::-webkit-outer-spin-button { } .pwr { - color: var(--c-6); + color: var(--c-6); transform: translate(2px, 3px); cursor: pointer; } @@ -751,7 +769,7 @@ input[type=number]::-webkit-outer-spin-button { cursor: pointer; } -.check { +.check, .radio { display: inline-block; position: relative; padding-bottom: 32px; @@ -761,7 +779,7 @@ input[type=number]::-webkit-outer-spin-button { } .schkl { - padding: 2px 22px 0px 35px; + padding: 2px 5px 0px 35px; margin: 0 0 0 2px; } @@ -771,7 +789,13 @@ input[type=number]::-webkit-outer-spin-button { margin-top: 8px; } -.check input { +.fxchkl { + position: absolute; + top: 0px; + left: 8px; +} + +.check input, .radio input { position: absolute; opacity: 0; cursor: pointer; @@ -779,7 +803,7 @@ input[type=number]::-webkit-outer-spin-button { width: 0; } -.checkmark { +.checkmark, .radiomark { position: absolute; bottom: 0; left: 0; @@ -789,6 +813,13 @@ input[type=number]::-webkit-outer-spin-button { border-radius: 10px; } +.radiomark { + height: 24px; + width: 24px; + border-radius: 50%; + background-color: transparent; +} + .schk { top: 0; } @@ -815,13 +846,13 @@ input[type=number]::-webkit-outer-spin-button { background-color: var(--c-6); } -.checkmark:after { +.checkmark:after, .radiomark:after { content: ""; position: absolute; display: none; } -.check input:checked ~ .checkmark:after { +.check input:checked ~ .checkmark:after, .radio input:checked ~ .radiomark:after { display: block; } @@ -837,6 +868,16 @@ input[type=number]::-webkit-outer-spin-button { transform: rotate(45deg); } +.radio .radiomark:after { + width: 12px; + height: 12px; + top: 50%; + left: 50%; + margin: -6px; + border-radius: 50%; + background: var(--c-f); +} + .h { font-size: 13px; text-align: center; @@ -860,7 +901,93 @@ input[type=number]::-webkit-outer-spin-button { border-radius: 20px; text-align: left; transition: background-color 0.5s; - filter: brightness(1); + filter: brightness(1); +} + +.list { + position: relative; + transition: background-color 0.5s; + margin: auto auto 10px; + padding-bottom: 10px; + width: 230px; +} + +.lstI { + overflow: hidden; +} + +.fxbtn { + margin: 20px auto; + padding: 8px 0; +} + +.lstI:hover { + background: var(--c-4); +} + +.lstI:last-child { + border: none; +} + +.lstI.sticky, .lstI.selected { + position: sticky; + z-index: 1; +} + +#selectPalette .lstI.selected { + top: 27px; + bottom: -11px; +} + +#selectPalette .lstI.sticky { + top: -11px; +} + +.lstI.selected { + background: var(--c-5); + top: 95px; + bottom: -11px; +} + +.lstI.sticky { + top: 57px; +} + +.lstIname { + margin: 3px 0; + white-space: nowrap; + cursor: pointer; + font-size: 19px; +} + +.lstIprev { + width: 100%; + height: 5px; + margin: auto; + position: absolute; + bottom: 0px; + left: 0px; +} + +input[type="text"].search { + display: block; + width: 230px; + box-sizing: border-box; + padding: 8px 8px 9px 38px; + margin: 6px auto 0 auto; + text-align: left; + background: url("data:image/svg+xml;utf8,") + no-repeat 10px 10px; + background-size: 20px; + background-color: var(--c-3); +} + +input[type="text"].search:focus { + background-color: var(--c-4); +} + +input[type="text"].search:not(:placeholder-shown) { + background-color: var(--c-5); } .pres { @@ -893,7 +1020,7 @@ input[type=number]::-webkit-outer-spin-button { width: 6px; } ::-webkit-scrollbar-track { - background: transparent; + background: transparent; } ::-webkit-scrollbar-thumb { background: var(--c-sb); @@ -934,4 +1061,4 @@ input[type=number]::-webkit-outer-spin-button { #buttonPcm { display: none; } -} \ No newline at end of file +} diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 9bdaae865..8560ee14f 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -23,9 +23,10 @@ - + + - +

Brightness

@@ -66,17 +67,17 @@
-
-
-
-
-
-

-
-
-
-
-
R
+
+
+
+
+
+

+
+
+
+
+
R
@@ -87,13 +88,33 @@
-

Color palette

-
+

- + Color palette +

+
+
+
+ +
+ + Default + +
+
+
+
+ + Loading... + +
+
+ +
@@ -117,21 +138,17 @@

Effect mode

-
- -
-
+
Loading...
-
- +
Loading...
- +
@@ -140,7 +157,7 @@
- +
@@ -169,7 +186,7 @@
-
+