diff --git a/.gitignore b/.gitignore
index 02e648b8..d59e422e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@
node_modules
.idea
.direnv
+wled-update.sh
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 3db77199..617b3aa5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "wled",
- "version": "0.13.0-b2",
+ "version": "0.13.0-bl2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 980d3a21..302312ce 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "wled",
- "version": "0.13.0-b2",
+ "version": "0.13.0-bl3",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {
diff --git a/platformio.ini b/platformio.ini
index 0a64a94c..ca3fc3ae 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -166,7 +166,7 @@ lib_deps =
#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
- #U8g2@~2.27.2
+ U8g2@~2.28.8
#For Dallas sensor uncomment following 2 lines
#OneWire@~2.3.5
#milesburton/DallasTemperature@^3.9.0
diff --git a/tools/WLED_ESP32-wrover_4MB.csv b/tools/WLED_ESP32-wrover_4MB.csv
new file mode 100644
index 00000000..a179a89d
--- /dev/null
+++ b/tools/WLED_ESP32-wrover_4MB.csv
@@ -0,0 +1,6 @@
+# Name, Type, SubType, Offset, Size, Flags
+nvs, data, nvs, 0x9000, 0x5000,
+otadata, data, ota, 0xe000, 0x2000,
+app0, app, ota_0, 0x10000, 0x180000,
+app1, app, ota_1, 0x190000,0x180000,
+spiffs, data, spiffs, 0x310000,0xF0000,
diff --git a/tools/cdata.js b/tools/cdata.js
index 8a904843..fee10a54 100644
--- a/tools/cdata.js
+++ b/tools/cdata.js
@@ -65,7 +65,7 @@ function adoptVersionAndRepo(html) {
return html;
}
-function writeHtmlGzipped(sourceFile, resultFile) {
+function writeHtmlGzipped(sourceFile, resultFile, page) {
console.info("Reading " + sourceFile);
new inliner(sourceFile, function (error, html) {
console.info("Inlined " + html.length + " characters");
@@ -95,8 +95,8 @@ function writeHtmlGzipped(sourceFile, resultFile) {
*/
// Autogenerated from ${sourceFile}, do not edit!!
-const uint16_t PAGE_index_L = ${result.length};
-const uint8_t PAGE_index[] PROGMEM = {
+const uint16_t PAGE_${page}_L = ${result.length};
+const uint8_t PAGE_${page}[] PROGMEM = {
${array}
};
`;
@@ -194,7 +194,8 @@ function writeChunks(srcDir, specs, resultFile) {
fs.writeFileSync(resultFile, src);
}
-writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h");
+writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
+writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple');
writeChunks(
"wled00/data",
diff --git a/usermods/Animated_Staircase/Animated_Staircase.h b/usermods/Animated_Staircase/Animated_Staircase.h
index 707b60da..87b5511d 100644
--- a/usermods/Animated_Staircase/Animated_Staircase.h
+++ b/usermods/Animated_Staircase/Animated_Staircase.h
@@ -458,11 +458,15 @@ class Animated_Staircase : public Usermod {
useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop;
topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin;
+// topPIRorTriggerPin = min(33,max(-1,(int)topPIRorTriggerPin)); // bounds check
topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin;
+// topEchoPin = min(39,max(-1,(int)topEchoPin)); // bounds check
useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom;
bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin;
+// bottomPIRorTriggerPin = min(33,max(-1,(int)bottomPIRorTriggerPin)); // bounds check
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
+// bottomEchoPin = min(39,max(-1,(int)bottomEchoPin)); // bounds check
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected)
diff --git a/usermods/JSON_IR_remote/readme.md b/usermods/JSON_IR_remote/readme.md
index ee18aa94..2cba06ed 100644
--- a/usermods/JSON_IR_remote/readme.md
+++ b/usermods/JSON_IR_remote/readme.md
@@ -1,33 +1,33 @@
-# JSON IR remote
-
-## Purpose
-
-The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling.
-It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can
-map buttons from any remote to any HTTP request API or JSON API command.
-
-## Usage
-
-* Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own.
-* On the config > LED settings page, set the correct IR pin.
-* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
-
-## Modification
-
-* See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels.
-* In the ir.json file, each key will be the hex encoded IR code.
-* The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed.
-* A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback)
-* When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to)
-* If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property.
-* Other properties are ignored, but having a label property may help when editing.
-
-
-Sample:
-{
- "0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
- "0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
- "0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
- "0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
- "label": "Preset 1 or fallback to Saw - Party"}, // c function
-}
+# JSON IR remote
+
+## Purpose
+
+The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling.
+It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can
+map buttons from any remote to any HTTP request API or JSON API command.
+
+## Usage
+
+* Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own.
+* On the config > LED settings page, set the correct IR pin.
+* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
+
+## Modification
+
+* See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels.
+* In the ir.json file, each key will be the hex encoded IR code.
+* The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed.
+* A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback)
+* When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to)
+* If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property.
+* Other properties are ignored, but having a label property may help when editing.
+
+
+Sample:
+{
+ "0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
+ "0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
+ "0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
+ "0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
+ "label": "Preset 1 or fallback to Saw - Party"}, // c function
+}
diff --git a/usermods/PIR_sensor_mqtt_v1/README.md b/usermods/PIR_sensor_mqtt_v1/README.md
deleted file mode 100644
index 475c11b0..00000000
--- a/usermods/PIR_sensor_mqtt_v1/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# PIR sensor with MQTT
-
-This simple usermod allows attaching a PIR sensor like the AM312 and publish the readings over MQTT. A message is sent when motion is detected as well as when motion has stopped.
-
-This usermod has only been tested with the AM312 sensor though should work for any other PIR sensor. Note that this does not control the LED strip directly, it only publishes MQTT readings for use with other integrations like Home Assistant.
-
-## Installation
-
-Copy and replace the file `usermod.cpp` in wled00 directory.
diff --git a/usermods/PIR_sensor_mqtt_v1/usermod.cpp b/usermods/PIR_sensor_mqtt_v1/usermod.cpp
deleted file mode 100644
index 426a8033..00000000
--- a/usermods/PIR_sensor_mqtt_v1/usermod.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#include "wled.h"
-/*
- * This v1 usermod file allows you to add own functionality to WLED more easily
- * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
- * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
- * If you just need 8 bytes, use 2551-2559 (you do not need to increase EEPSIZE)
- *
- * Consider the v2 usermod API if you need a more advanced feature set!
- */
-
-//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
-
-// PIR sensor pin
-const int MOTION_PIN = 16;
- // MQTT topic for sensor values
-const char MQTT_TOPIC[] = "/motion";
-
-int prevState = LOW;
-
-//gets called once at boot. Do all initialization that doesn't depend on network here
-void userSetup()
-{
- pinMode(MOTION_PIN, INPUT);
-}
-
-//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
-void userConnected()
-{
-
-}
-
-void publishMqtt(String state)
-{
- //Check if MQTT Connected, otherwise it will crash the 8266
- if (mqtt != nullptr){
- char subuf[38];
- strcpy(subuf, mqttDeviceTopic);
- strcat(subuf, MQTT_TOPIC);
- mqtt->publish(subuf, 0, true, state.c_str());
- }
-}
-
-//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
-void userLoop()
-{
- if (digitalRead(MOTION_PIN) == HIGH && prevState == LOW) { // Motion detected
- publishMqtt("ON");
- prevState = HIGH;
- }
- if (digitalRead(MOTION_PIN) == LOW && prevState == HIGH) { // Motion stopped
- publishMqtt("OFF");
- prevState = LOW;
- }
-}
-
diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
index 2978b491..6682dde3 100644
--- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
+++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
@@ -1,409 +1,409 @@
-#pragma once
-
-#include "wled.h"
-
-#ifndef PIR_SENSOR_PIN
- // compatible with QuinLED-Dig-Uno
- #ifdef ARDUINO_ARCH_ESP32
- #define PIR_SENSOR_PIN 23 // Q4
- #else //ESP8266 boards
- #define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini)
- #endif
-#endif
-
-/*
- * This usermod handles PIR sensor states.
- * The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
- * When the sensor state goes LOW, the off timer is started and when it expires, the strip is switched off.
- *
- *
- * Usermods allow you to add own functionality to WLED more easily
- * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
- *
- * v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
- * Multiple v2 usermods can be added to one compilation easily.
- *
- * Creating a usermod:
- * This file serves as an example. If you want to create a usermod, it is recommended to use usermod_v2_empty.h from the usermods folder as a template.
- * Please remember to rename the class and file to a descriptive name.
- * You may also use multiple .h and .cpp files.
- *
- * Using a usermod:
- * 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
- * 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
- */
-
-class PIRsensorSwitch : public Usermod
-{
-public:
- /**
- * constructor
- */
- PIRsensorSwitch() {}
- /**
- * desctructor
- */
- ~PIRsensorSwitch() {}
-
- /**
- * Enable/Disable the PIR sensor
- */
- void EnablePIRsensor(bool en) { enabled = en; }
- /**
- * Get PIR sensor enabled/disabled state
- */
- bool PIRsensorEnabled() { return enabled; }
-
-private:
- // PIR sensor pin
- int8_t PIRsensorPin = PIR_SENSOR_PIN;
- // notification mode for colorUpdated()
- const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
- // delay before switch off after the sensor state goes LOW
- uint32_t m_switchOffDelay = 600000; // 10min
- // off timer start time
- uint32_t m_offTimerStart = 0;
- // current PIR sensor pin state
- byte sensorPinState = LOW;
- // PIR sensor enabled
- bool enabled = true;
- // status of initialisation
- bool initDone = false;
- // on and off presets
- uint8_t m_onPreset = 0;
- uint8_t m_offPreset = 0;
- // flag to indicate that PIR sensor should activate WLED during nighttime only
- bool m_nightTimeOnly = false;
- // flag to send MQTT message only (assuming it is enabled)
- bool m_mqttOnly = false;
- // flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
- bool m_offOnly = false;
- bool PIRtriggered = false;
-
- unsigned long lastLoop = 0;
-
- // strings to reduce flash memory usage (used more than twice)
- static const char _name[];
- static const char _switchOffDelay[];
- static const char _enabled[];
- static const char _onPreset[];
- static const char _offPreset[];
- static const char _nightTime[];
- static const char _mqttOnly[];
- static const char _offOnly[];
-
- /**
- * check if it is daytime
- * if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
- */
- bool isDayTime() {
- bool isDayTime = false;
- updateLocalTime();
- uint8_t hr = hour(localTime);
- uint8_t mi = minute(localTime);
-
- if (sunrise && sunset) {
- if (hour(sunrise)
hr) {
- isDayTime = true;
- } else {
- if (hour(sunrise)==hr && minute(sunrise)mi) {
- isDayTime = true;
- }
- }
- }
- return isDayTime;
- }
-
- /**
- * switch strip on/off
- */
- void switchStrip(bool switchOn)
- {
- if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
- PIRtriggered = switchOn;
- if (switchOn && m_onPreset) {
- applyPreset(m_onPreset);
- } else if (!switchOn && m_offPreset) {
- applyPreset(m_offPreset);
- } else if (switchOn && bri == 0) {
- bri = briLast;
- colorUpdated(NotifyUpdateMode);
- } else if (!switchOn && bri != 0) {
- briLast = bri;
- bri = 0;
- colorUpdated(NotifyUpdateMode);
- }
- }
-
- void publishMqtt(const char* state)
- {
- //Check if MQTT Connected, otherwise it will crash the 8266
- if (WLED_MQTT_CONNECTED){
- char subuf[64];
- strcpy(subuf, mqttDeviceTopic);
- strcat_P(subuf, PSTR("/motion"));
- mqtt->publish(subuf, 0, false, state);
- }
- }
-
- /**
- * Read and update PIR sensor state.
- * Initilize/reset switch off timer
- */
- bool updatePIRsensorState()
- {
- bool pinState = digitalRead(PIRsensorPin);
- if (pinState != sensorPinState) {
- sensorPinState = pinState; // change previous state
-
- if (sensorPinState == HIGH) {
- m_offTimerStart = 0;
- if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
- publishMqtt("on");
- } else /*if (bri != 0)*/ {
- // start switch off timer
- m_offTimerStart = millis();
- }
- return true;
- }
- return false;
- }
-
- /**
- * switch off the strip if the delay has elapsed
- */
- bool handleOffTimer()
- {
- if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
- {
- if (enabled == true)
- {
- if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
- publishMqtt("off");
- }
- m_offTimerStart = 0;
- return true;
- }
- return false;
- }
-
-public:
- //Functions called by WLED
-
- /**
- * setup() is called once at boot. WiFi is not yet connected at this point.
- * You can use it to initialize variables, sensors or similar.
- */
- void setup()
- {
- if (enabled) {
- // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
- if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
- // PIR Sensor mode INPUT_PULLUP
- pinMode(PIRsensorPin, INPUT_PULLUP);
- sensorPinState = digitalRead(PIRsensorPin);
- } else {
- if (PIRsensorPin >= 0) {
- DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
- }
- PIRsensorPin = -1; // allocation failed
- enabled = false;
- }
- }
- initDone = true;
- }
-
- /**
- * connected() is called every time the WiFi is (re)connected
- * Use it to initialize network interfaces
- */
- void connected()
- {
- }
-
- /**
- * loop() is called continuously. Here you can check for events, read sensors, etc.
- */
- void loop()
- {
- // only check sensors 4x/s
- if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
- lastLoop = millis();
-
- if (!updatePIRsensorState()) {
- handleOffTimer();
- }
- }
-
- /**
- * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
- *
- * Add PIR sensor state and switch off timer duration to jsoninfo
- */
- void addToJsonInfo(JsonObject &root)
- {
- JsonObject user = root["u"];
- if (user.isNull()) user = root.createNestedObject("u");
-
- if (enabled)
- {
- // off timer
- String uiDomString = F("PIR ");
- JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
- if (m_offTimerStart > 0)
- {
- uiDomString = "";
- unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
- if (offSeconds >= 3600)
- {
- uiDomString += (offSeconds / 3600);
- uiDomString += F("h ");
- offSeconds %= 3600;
- }
- if (offSeconds >= 60)
- {
- uiDomString += (offSeconds / 60);
- offSeconds %= 60;
- }
- else if (uiDomString.length() > 0)
- {
- uiDomString += 0;
- }
- if (uiDomString.length() > 0)
- {
- uiDomString += F("min ");
- }
- uiDomString += (offSeconds);
- infoArr.add(uiDomString + F("s"));
- } else {
- infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
- }
- } else {
- String uiDomString = F("PIR sensor");
- JsonArray infoArr = user.createNestedArray(uiDomString);
- infoArr.add(F("disabled"));
- }
- }
-
- /**
- * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
- * Values in the state object may be modified by connected clients
- */
-/*
- void addToJsonState(JsonObject &root)
- {
- }
-*/
-
- /**
- * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
- * Values in the state object may be modified by connected clients
- */
-/*
- void readFromJsonState(JsonObject &root)
- {
- }
-*/
-
- /**
- * provide the changeable values
- */
- void addToConfig(JsonObject &root)
- {
- JsonObject top = root.createNestedObject(FPSTR(_name));
- top[FPSTR(_enabled)] = enabled;
- top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
- top["pin"] = PIRsensorPin;
- top[FPSTR(_onPreset)] = m_onPreset;
- top[FPSTR(_offPreset)] = m_offPreset;
- top[FPSTR(_nightTime)] = m_nightTimeOnly;
- top[FPSTR(_mqttOnly)] = m_mqttOnly;
- top[FPSTR(_offOnly)] = m_offOnly;
- DEBUG_PRINTLN(F("PIR config saved."));
- }
-
- /**
- * restore the changeable values
- * readFromConfig() is called before setup() to populate properties from values stored in cfg.json
- *
- * The function should return true if configuration was successfully loaded or false if there was no configuration.
- */
- bool readFromConfig(JsonObject &root)
- {
- bool oldEnabled = enabled;
- int8_t oldPin = PIRsensorPin;
-
- JsonObject top = root[FPSTR(_name)];
- if (top.isNull()) {
- DEBUG_PRINT(FPSTR(_name));
- DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
- return false;
- }
-
- PIRsensorPin = top["pin"] | PIRsensorPin;
-
- enabled = top[FPSTR(_enabled)] | enabled;
-
- m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000;
-
- m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
- m_onPreset = max(0,min(250,(int)m_onPreset));
-
- m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
- m_offPreset = max(0,min(250,(int)m_offPreset));
-
- m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
- m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
- m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
-
- DEBUG_PRINT(FPSTR(_name));
- if (!initDone) {
- // reading config prior to setup()
- DEBUG_PRINTLN(F(" config loaded."));
- } else {
- if (oldPin != PIRsensorPin || oldEnabled != enabled) {
- // check if pin is OK
- if (oldPin != PIRsensorPin && oldPin >= 0) {
- // if we are changing pin in settings page
- // deallocate old pin
- pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
- if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
- pinMode(PIRsensorPin, INPUT_PULLUP);
- } else {
- // allocation failed
- PIRsensorPin = -1;
- enabled = false;
- }
- }
- if (enabled) {
- sensorPinState = digitalRead(PIRsensorPin);
- }
- }
- DEBUG_PRINTLN(F(" config (re)loaded."));
- }
- // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
- return !top[FPSTR(_offOnly)].isNull();
- }
-
- /**
- * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
- * This could be used in the future for the system to determine whether your usermod is installed.
- */
- uint16_t getId()
- {
- return USERMOD_ID_PIRSWITCH;
- }
-};
-
-// strings to reduce flash memory usage (used more than twice)
-const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
-const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
-const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
-const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
-const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
-const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
-const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
-const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
+#pragma once
+
+#include "wled.h"
+
+#ifndef PIR_SENSOR_PIN
+ // compatible with QuinLED-Dig-Uno
+ #ifdef ARDUINO_ARCH_ESP32
+ #define PIR_SENSOR_PIN 23 // Q4
+ #else //ESP8266 boards
+ #define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini)
+ #endif
+#endif
+
+/*
+ * This usermod handles PIR sensor states.
+ * The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
+ * When the sensor state goes LOW, the off timer is started and when it expires, the strip is switched off.
+ *
+ *
+ * Usermods allow you to add own functionality to WLED more easily
+ * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
+ *
+ * v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
+ * Multiple v2 usermods can be added to one compilation easily.
+ *
+ * Creating a usermod:
+ * This file serves as an example. If you want to create a usermod, it is recommended to use usermod_v2_empty.h from the usermods folder as a template.
+ * Please remember to rename the class and file to a descriptive name.
+ * You may also use multiple .h and .cpp files.
+ *
+ * Using a usermod:
+ * 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
+ * 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
+ */
+
+class PIRsensorSwitch : public Usermod
+{
+public:
+ /**
+ * constructor
+ */
+ PIRsensorSwitch() {}
+ /**
+ * desctructor
+ */
+ ~PIRsensorSwitch() {}
+
+ /**
+ * Enable/Disable the PIR sensor
+ */
+ void EnablePIRsensor(bool en) { enabled = en; }
+ /**
+ * Get PIR sensor enabled/disabled state
+ */
+ bool PIRsensorEnabled() { return enabled; }
+
+private:
+ // PIR sensor pin
+ int8_t PIRsensorPin = PIR_SENSOR_PIN;
+ // notification mode for colorUpdated()
+ const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
+ // delay before switch off after the sensor state goes LOW
+ uint32_t m_switchOffDelay = 600000; // 10min
+ // off timer start time
+ uint32_t m_offTimerStart = 0;
+ // current PIR sensor pin state
+ byte sensorPinState = LOW;
+ // PIR sensor enabled
+ bool enabled = true;
+ // status of initialisation
+ bool initDone = false;
+ // on and off presets
+ uint8_t m_onPreset = 0;
+ uint8_t m_offPreset = 0;
+ // flag to indicate that PIR sensor should activate WLED during nighttime only
+ bool m_nightTimeOnly = false;
+ // flag to send MQTT message only (assuming it is enabled)
+ bool m_mqttOnly = false;
+ // flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
+ bool m_offOnly = false;
+ bool PIRtriggered = false;
+
+ unsigned long lastLoop = 0;
+
+ // strings to reduce flash memory usage (used more than twice)
+ static const char _name[];
+ static const char _switchOffDelay[];
+ static const char _enabled[];
+ static const char _onPreset[];
+ static const char _offPreset[];
+ static const char _nightTime[];
+ static const char _mqttOnly[];
+ static const char _offOnly[];
+
+ /**
+ * check if it is daytime
+ * if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
+ */
+ bool isDayTime() {
+ bool isDayTime = false;
+ updateLocalTime();
+ uint8_t hr = hour(localTime);
+ uint8_t mi = minute(localTime);
+
+ if (sunrise && sunset) {
+ if (hour(sunrise)hr) {
+ isDayTime = true;
+ } else {
+ if (hour(sunrise)==hr && minute(sunrise)mi) {
+ isDayTime = true;
+ }
+ }
+ }
+ return isDayTime;
+ }
+
+ /**
+ * switch strip on/off
+ */
+ void switchStrip(bool switchOn)
+ {
+ if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
+ PIRtriggered = switchOn;
+ if (switchOn && m_onPreset) {
+ applyPreset(m_onPreset);
+ } else if (!switchOn && m_offPreset) {
+ applyPreset(m_offPreset);
+ } else if (switchOn && bri == 0) {
+ bri = briLast;
+ colorUpdated(NotifyUpdateMode);
+ } else if (!switchOn && bri != 0) {
+ briLast = bri;
+ bri = 0;
+ colorUpdated(NotifyUpdateMode);
+ }
+ }
+
+ void publishMqtt(const char* state)
+ {
+ //Check if MQTT Connected, otherwise it will crash the 8266
+ if (WLED_MQTT_CONNECTED){
+ char subuf[64];
+ strcpy(subuf, mqttDeviceTopic);
+ strcat_P(subuf, PSTR("/motion"));
+ mqtt->publish(subuf, 0, false, state);
+ }
+ }
+
+ /**
+ * Read and update PIR sensor state.
+ * Initilize/reset switch off timer
+ */
+ bool updatePIRsensorState()
+ {
+ bool pinState = digitalRead(PIRsensorPin);
+ if (pinState != sensorPinState) {
+ sensorPinState = pinState; // change previous state
+
+ if (sensorPinState == HIGH) {
+ m_offTimerStart = 0;
+ if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
+ publishMqtt("on");
+ } else /*if (bri != 0)*/ {
+ // start switch off timer
+ m_offTimerStart = millis();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * switch off the strip if the delay has elapsed
+ */
+ bool handleOffTimer()
+ {
+ if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
+ {
+ if (enabled == true)
+ {
+ if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
+ publishMqtt("off");
+ }
+ m_offTimerStart = 0;
+ return true;
+ }
+ return false;
+ }
+
+public:
+ //Functions called by WLED
+
+ /**
+ * setup() is called once at boot. WiFi is not yet connected at this point.
+ * You can use it to initialize variables, sensors or similar.
+ */
+ void setup()
+ {
+ if (enabled) {
+ // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
+ if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
+ // PIR Sensor mode INPUT_PULLUP
+ pinMode(PIRsensorPin, INPUT_PULLUP);
+ sensorPinState = digitalRead(PIRsensorPin);
+ } else {
+ if (PIRsensorPin >= 0) {
+ DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
+ }
+ PIRsensorPin = -1; // allocation failed
+ enabled = false;
+ }
+ }
+ initDone = true;
+ }
+
+ /**
+ * connected() is called every time the WiFi is (re)connected
+ * Use it to initialize network interfaces
+ */
+ void connected()
+ {
+ }
+
+ /**
+ * loop() is called continuously. Here you can check for events, read sensors, etc.
+ */
+ void loop()
+ {
+ // only check sensors 4x/s
+ if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
+ lastLoop = millis();
+
+ if (!updatePIRsensorState()) {
+ handleOffTimer();
+ }
+ }
+
+ /**
+ * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
+ *
+ * Add PIR sensor state and switch off timer duration to jsoninfo
+ */
+ void addToJsonInfo(JsonObject &root)
+ {
+ JsonObject user = root["u"];
+ if (user.isNull()) user = root.createNestedObject("u");
+
+ if (enabled)
+ {
+ // off timer
+ String uiDomString = F("PIR ");
+ JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
+ if (m_offTimerStart > 0)
+ {
+ uiDomString = "";
+ unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
+ if (offSeconds >= 3600)
+ {
+ uiDomString += (offSeconds / 3600);
+ uiDomString += F("h ");
+ offSeconds %= 3600;
+ }
+ if (offSeconds >= 60)
+ {
+ uiDomString += (offSeconds / 60);
+ offSeconds %= 60;
+ }
+ else if (uiDomString.length() > 0)
+ {
+ uiDomString += 0;
+ }
+ if (uiDomString.length() > 0)
+ {
+ uiDomString += F("min ");
+ }
+ uiDomString += (offSeconds);
+ infoArr.add(uiDomString + F("s"));
+ } else {
+ infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
+ }
+ } else {
+ String uiDomString = F("PIR sensor");
+ JsonArray infoArr = user.createNestedArray(uiDomString);
+ infoArr.add(F("disabled"));
+ }
+ }
+
+ /**
+ * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
+ * Values in the state object may be modified by connected clients
+ */
+/*
+ void addToJsonState(JsonObject &root)
+ {
+ }
+*/
+
+ /**
+ * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
+ * Values in the state object may be modified by connected clients
+ */
+/*
+ void readFromJsonState(JsonObject &root)
+ {
+ }
+*/
+
+ /**
+ * provide the changeable values
+ */
+ void addToConfig(JsonObject &root)
+ {
+ JsonObject top = root.createNestedObject(FPSTR(_name));
+ top[FPSTR(_enabled)] = enabled;
+ top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
+ top["pin"] = PIRsensorPin;
+ top[FPSTR(_onPreset)] = m_onPreset;
+ top[FPSTR(_offPreset)] = m_offPreset;
+ top[FPSTR(_nightTime)] = m_nightTimeOnly;
+ top[FPSTR(_mqttOnly)] = m_mqttOnly;
+ top[FPSTR(_offOnly)] = m_offOnly;
+ DEBUG_PRINTLN(F("PIR config saved."));
+ }
+
+ /**
+ * restore the changeable values
+ * readFromConfig() is called before setup() to populate properties from values stored in cfg.json
+ *
+ * The function should return true if configuration was successfully loaded or false if there was no configuration.
+ */
+ bool readFromConfig(JsonObject &root)
+ {
+ bool oldEnabled = enabled;
+ int8_t oldPin = PIRsensorPin;
+
+ JsonObject top = root[FPSTR(_name)];
+ if (top.isNull()) {
+ DEBUG_PRINT(FPSTR(_name));
+ DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
+ return false;
+ }
+
+ PIRsensorPin = top["pin"] | PIRsensorPin;
+
+ enabled = top[FPSTR(_enabled)] | enabled;
+
+ m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000;
+
+ m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
+ m_onPreset = max(0,min(250,(int)m_onPreset));
+
+ m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
+ m_offPreset = max(0,min(250,(int)m_offPreset));
+
+ m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
+ m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
+ m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
+
+ DEBUG_PRINT(FPSTR(_name));
+ if (!initDone) {
+ // reading config prior to setup()
+ DEBUG_PRINTLN(F(" config loaded."));
+ } else {
+ if (oldPin != PIRsensorPin || oldEnabled != enabled) {
+ // check if pin is OK
+ if (oldPin != PIRsensorPin && oldPin >= 0) {
+ // if we are changing pin in settings page
+ // deallocate old pin
+ pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
+ if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
+ pinMode(PIRsensorPin, INPUT_PULLUP);
+ } else {
+ // allocation failed
+ PIRsensorPin = -1;
+ enabled = false;
+ }
+ }
+ if (enabled) {
+ sensorPinState = digitalRead(PIRsensorPin);
+ }
+ }
+ DEBUG_PRINTLN(F(" config (re)loaded."));
+ }
+ // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
+ return !top[FPSTR(_offOnly)].isNull();
+ }
+
+ /**
+ * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
+ * This could be used in the future for the system to determine whether your usermod is installed.
+ */
+ uint16_t getId()
+ {
+ return USERMOD_ID_PIRSWITCH;
+ }
+};
+
+// strings to reduce flash memory usage (used more than twice)
+const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
+const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
+const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
+const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
+const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
+const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
+const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
+const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md
deleted file mode 100644
index 60fc31f7..00000000
--- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# QuinLED Dig Uno board
-
-These files allow WLED 0.9.1 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up.
-This code uses Aircookie's WLED software. It has a premade file for user modifications. I use it to publish the temperature from the dallas temperature sensor on the Quinled board. The entries for the top of the WLED00 file, initializes the required libraries, and variables for the sensor. The .ino file waits for 60 seconds, and checks to see if the MQTT server is connected (thanks Aircoookie). It then poles the sensor, and published it using the MQTT service already running, using the main topic programmed in the WLED UI.
-
-Installation of file: Copy and replace file in wled00 directory
-
-## Project link
-
-* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link
-
-### Platformio requirements
-
-Uncomment `DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
-
-```ini
-# platformio.ini
-...
-[platformio]
-...
-; default_envs = esp07
-default_envs = d1_mini
-...
-[common]
-...
-lib_deps_external =
- ...
- #For use SSD1306 OLED display uncomment following
- U8g2@~2.27.3
- #For Dallas sensor uncomment following 2 lines
- DallasTemperature@~3.8.0
- OneWire@~2.3.5
-...
-```
diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp b/usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp
deleted file mode 100644
index 5b4e2e5c..00000000
--- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#include
-#include "wled.h"
-//Intiating code for QuinLED Dig-Uno temp sensor
-//Uncomment Celsius if that is your prefered temperature scale
-#include //Dallastemperature sensor
-#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards
-OneWire oneWire(18);
-#else //ESP8266 boards
-OneWire oneWire(14);
-#endif
-DallasTemperature sensor(&oneWire);
-long temptimer = millis();
-long lastMeasure = 0;
-#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit
-void userSetup()
-{
-// Start the DS18B20 sensor
- sensor.begin();
-}
-
-//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
-void userConnected()
-{
-
-}
-
-void userLoop()
-{
- temptimer = millis();
-
-// Timer to publishe new temperature every 60 seconds
- if (temptimer - lastMeasure > 60000) {
- lastMeasure = temptimer;
-
-//Check if MQTT Connected, otherwise it will crash the 8266
- if (mqtt != nullptr){
- sensor.requestTemperatures();
-
-//Gets prefered temperature scale based on selection in definitions section
- #ifdef Celsius
- float board_temperature = sensor.getTempCByIndex(0);
- #else
- float board_temperature = sensors.getTempFByIndex(0);
- #endif
-
-//Create character string populated with user defined device topic from the UI, and the read temperature. Then publish to MQTT server.
- char subuf[38];
- strcpy(subuf, mqttDeviceTopic);
- strcat(subuf, "/temperature");
- mqtt->publish(subuf, 0, true, String(board_temperature).c_str());
- return;}
- return;}
-return;
-}
diff --git a/usermods/ST7789_display/README.md b/usermods/ST7789_display/README.md
index 653fdd75..f98dcf54 100644
--- a/usermods/ST7789_display/README.md
+++ b/usermods/ST7789_display/README.md
@@ -2,11 +2,14 @@
This usermod allow to use 240x240 display to display following:
+* current date and time;
* Network SSID;
* IP address;
+* WiFi signal strength;
* Brightness;
* Chosen effect;
* Chosen palette;
+* effect speed and intensity;
* Estimated current in mA;
## Hardware
@@ -46,27 +49,29 @@ Add lines to section:
default_envs = esp32dev
build_flags = ${common.build_flags_esp32}
-D USERMOD_ST7789_DISPLAY
-
+ -DUSER_SETUP_LOADED=1
+ -DST7789_DRIVER=1
+ -DTFT_WIDTH=240
+ -DTFT_HEIGHT=240
+ -DCGRAM_OFFSET=1
+ -DTFT_MOSI=21
+ -DTFT_SCLK=22
+ -DTFT_DC=27
+ -DTFT_RST=26
+ -DTFT_BL=14
+ -DLOAD_GLCD=1
+ ;optional for WROVER
+ ;-DCONFIG_SPIRAM_SUPPORT=1
```
Save the `platformio.ini` file. Once this is saved, the required library files should be automatically downloaded for modifications in a later step.
### TFT_eSPI Library Adjustments
-We need to modify a file in the `TFT_eSPI` library. If you followed the directions to modify and save the `platformio.ini` file above, the `User_Setup_Select.h` file can be found in the `/.pio/libdeps/esp32dev/TFT_eSPI` folder.
+If you are not using PlatformIO you need to modify a file in the `TFT_eSPI` library. If you followed the directions to modify and save the `platformio.ini` file above, the `Setup24_ST7789.h` file can be found in the `/.pio/libdeps/esp32dev/TFT_eSPI/User_Setups/` folder.
-Modify the `User_Setup_Select.h` file as follows:
+Edit `Setup_ST7789.h` file and uncomment nad changep GPIO pin numbers in lines containing `TFT_MOSI`, `TFT_SCLK`, `TFT_RST`, `TFT_DC`.
-* Comment out the following line (which is the 'default' setup file):
+Modify the `User_Setup_Select.h` by uncommentig the line containing `#include ` and commenting out line containing `#include `.
-```ini
-//#include // Default setup is root library folder
-```
-
-* Add following line:
-
-```ini
-#include // Setup file for ESP32 ST7789V SPI bus TFT
-```
-
-* Copy file `"Setup_ST7789_Display.h"` from usermod folder to `/.pio/libdeps/esp32dev/TFT_eSPI/User_Setups`
+If your display includes backlight enable pin, #define TFT_BL with backlight enable GPIO number.
\ No newline at end of file
diff --git a/usermods/ST7789_display/ST7789_display.h b/usermods/ST7789_display/ST7789_display.h
index bd501d08..ae8f31f0 100644
--- a/usermods/ST7789_display/ST7789_display.h
+++ b/usermods/ST7789_display/ST7789_display.h
@@ -7,27 +7,48 @@
#include
#include
-#define USERMOD_ST7789_DISPLAY 97
-
-#ifndef TFT_DISPOFF
-#define TFT_DISPOFF 0x28
+#ifndef USER_SETUP_LOADED
+ #ifndef ST7789_DRIVER
+ #error Please define ST7789_DRIVER
+ #endif
+ #ifndef TFT_WIDTH
+ #error Please define TFT_WIDTH
+ #endif
+ #ifndef TFT_HEIGHT
+ #error Please define TFT_HEIGHT
+ #endif
+ #ifndef TFT_MOSI
+ #error Please define TFT_MOSI
+ #endif
+ #ifndef TFT_SCLK
+ #error Please define TFT_SCLK
+ #endif
+ #ifndef TFT_DC
+ #error Please define TFT_DC
+ #endif
+ #ifndef TFT_RST
+ #error Please define TFT_RST
+ #endif
+ #ifndef LOAD_GLCD
+ #error Please define LOAD_GLCD
+ #endif
+#endif
+#ifndef TFT_BL
+ #define TFT_BL -1
#endif
-#ifndef TFT_SLPIN
-#define TFT_SLPIN 0x10
-#endif
+#define USERMOD_ID_ST7789_DISPLAY 97
-#define TFT_MOSI 21
-#define TFT_SCLK 22
-#define TFT_DC 18
-#define TFT_RST 5
-#define TFT_BL 26 // Display backlight control pin
+TFT_eSPI tft = TFT_eSPI(TFT_WIDTH, TFT_HEIGHT); // Invoke custom library
-TFT_eSPI tft = TFT_eSPI(240, 240); // Invoke custom library
+// Extra char (+1) for null
+#define LINE_BUFFER_SIZE 20
// How often we are redrawing screen
#define USER_LOOP_REFRESH_RATE_MS 1000
+extern int getSignalQuality(int rssi);
+
//class name. Use something descriptive and leave the ": public Usermod" part :)
class St7789DisplayUsermod : public Usermod {
@@ -45,9 +66,70 @@ class St7789DisplayUsermod : public Usermod {
uint8_t knownBrightness = 0;
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
- uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2
+ uint8_t knownEffectSpeed = 0;
+ uint8_t knownEffectIntensity = 0;
+ uint8_t knownMinute = 99;
+ uint8_t knownHour = 99;
+
+ const uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2
long lastUpdate = 0;
+ void center(String &line, uint8_t width) {
+ int len = line.length();
+ if (len0; i--) line = ' ' + line;
+ for (byte i=line.length(); i 12) {
+ showHour -= 12;
+ isAM = false;
+ } else {
+ isAM = true;
+ }
+ }
+
+ sprintf_P(lineBuffer, PSTR("%2d:%02d"), (useAMPM ? showHour : hourCurrent), minuteCurrent);
+ tft.setTextColor(TFT_WHITE);
+ tft.setTextSize(4);
+ tft.setCursor(60, 24);
+ tft.print(lineBuffer);
+
+ tft.setTextSize(2);
+ tft.setCursor(186, 24);
+ //sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
+ if (useAMPM) tft.print(isAM ? "AM" : "PM");
+ //else tft.print(lineBuffer);
+ }
+
public:
//Functions called by WLED
@@ -57,6 +139,9 @@ class St7789DisplayUsermod : public Usermod {
*/
void setup()
{
+ PinManagerPinType pins[] = { { TFT_MOSI, true }, { TFT_MISO, false}, { TFT_SCLK, true }, { TFT_CS, true}, { TFT_DC, true}, { TFT_RST, true }, { TFT_BL, true } };
+ if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { return; }
+
tft.init();
tft.setRotation(0); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip.
tft.fillScreen(TFT_BLACK);
@@ -65,10 +150,10 @@ class St7789DisplayUsermod : public Usermod {
tft.setTextDatum(MC_DATUM);
tft.setTextSize(2);
tft.print("Loading...");
- if (TFT_BL > 0)
- { // TFT_BL has been set in the TFT_eSPI library
- pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
- digitalWrite(TFT_BL, HIGH); // Turn backlight on.
+ if (TFT_BL >= 0)
+ {
+ pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
+ digitalWrite(TFT_BL, HIGH); // Turn backlight on.
}
}
@@ -91,174 +176,189 @@ class St7789DisplayUsermod : public Usermod {
* Instead, use a timer check as shown here.
*/
void loop() {
-// Check if we time interval for redrawing passes.
- if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
+ char buff[LINE_BUFFER_SIZE];
+
+ // Check if we time interval for redrawing passes.
+ if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
{
return;
}
- lastUpdate = millis();
+ lastUpdate = millis();
-// Turn off display after 5 minutes with no change.
- if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000)
+ // Turn off display after 5 minutes with no change.
+ if (!displayTurnedOff && millis() - lastRedraw > 5*60*1000)
{
- digitalWrite(TFT_BL, LOW); // Turn backlight off.
+ if (TFT_BL >= 0) digitalWrite(TFT_BL, LOW); // Turn backlight off.
displayTurnedOff = true;
}
-// Check if values which are shown on display changed from the last time.
- if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid)
- {
- needRedraw = true;
- }
- else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP()))
- {
- needRedraw = true;
- }
- else if (knownBrightness != bri)
- {
- needRedraw = true;
- }
- else if (knownMode != strip.getMode())
- {
- needRedraw = true;
- }
- else if (knownPalette != strip.getSegment(0).palette)
- {
- needRedraw = true;
- }
-
- if (!needRedraw)
- {
- return;
- }
- needRedraw = false;
-
- if (displayTurnedOff)
- {
- digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on.
- displayTurnedOff = false;
- }
- lastRedraw = millis();
-
-// Update last known values.
- #if defined(ESP8266)
- knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
- #else
- knownSsid = WiFi.SSID();
- #endif
- knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
- knownBrightness = bri;
- knownMode = strip.getMode();
- knownPalette = strip.getSegment(0).palette;
-
- tft.fillScreen(TFT_BLACK);
- tft.setTextSize(2);
-// First row with Wifi name
- tft.setTextColor(TFT_SILVER);
- tft.setCursor(3, 40);
- tft.print(knownSsid.substring(0, tftcharwidth > 1 ? tftcharwidth - 1 : 0));
-// Print `~` char to indicate that SSID is longer, than our dicplay
- if (knownSsid.length() > tftcharwidth)
- tft.print("~");
-
-// Second row with AP IP and Password or IP
- tft.setTextColor(TFT_GREEN);
- tft.setTextSize(2);
- tft.setCursor(3, 64);
-// Print AP IP and password in AP mode or knownIP if AP not active.
-
- if (apActive)
- {
- tft.setTextColor(TFT_YELLOW);
- tft.print("AP IP: ");
- tft.print(knownIp);
- tft.setCursor(3,86);
- tft.setTextColor(TFT_YELLOW);
- tft.print("AP Pass:");
- tft.print(apPass);
- }
- else
- {
- tft.setTextColor(TFT_GREEN);
- tft.print("IP: ");
- tft.print(knownIp);
- tft.setCursor(3,86);
- //tft.print("Signal Strength: ");
- //tft.print(i.wifi.signal);
- tft.setTextColor(TFT_WHITE);
- tft.print("Bri: ");
- tft.print(((float(bri)/255)*100),0);
- tft.print("%");
- }
-
-// Third row with mode name
- tft.setCursor(3, 108);
- uint8_t qComma = 0;
- bool insideQuotes = false;
- uint8_t printedChars = 0;
- char singleJsonSymbol;
-// Find the mode name in JSON
- for (size_t i = 0; i < strlen_P(JSON_mode_names); i++)
- {
- singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
- switch (singleJsonSymbol)
+ // Check if values which are shown on display changed from the last time.
+ if ((((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) ||
+ (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) ||
+ (knownBrightness != bri) ||
+ (knownEffectSpeed != effectSpeed) ||
+ (knownEffectIntensity != effectIntensity) ||
+ (knownMode != strip.getMode()) ||
+ (knownPalette != strip.getSegment(0).palette))
{
+ needRedraw = true;
+ }
+
+ if (!needRedraw)
+ {
+ return;
+ }
+ needRedraw = false;
+
+ if (displayTurnedOff)
+ {
+ digitalWrite(TFT_BL, HIGH); // Turn backlight on.
+ displayTurnedOff = false;
+ }
+ lastRedraw = millis();
+
+ // Update last known values.
+ #if defined(ESP8266)
+ knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
+ #else
+ knownSsid = WiFi.SSID();
+ #endif
+ knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
+ knownBrightness = bri;
+ knownMode = strip.getMode();
+ knownPalette = strip.getSegment(0).palette;
+ knownEffectSpeed = effectSpeed;
+ knownEffectIntensity = effectIntensity;
+
+ tft.fillScreen(TFT_BLACK);
+
+ showTime();
+
+ tft.setTextSize(2);
+
+ // Wifi name
+ tft.setTextColor(TFT_GREEN);
+ tft.setCursor(0, 60);
+ String line = knownSsid.substring(0, tftcharwidth-1);
+ // Print `~` char to indicate that SSID is longer, than our display
+ if (knownSsid.length() > tftcharwidth) line = line.substring(0, tftcharwidth-1) + '~';
+ center(line, tftcharwidth);
+ tft.print(line.c_str());
+
+ // Print AP IP and password in AP mode or knownIP if AP not active.
+ if (apActive)
+ {
+ tft.setCursor(0, 84);
+ tft.print("AP IP: ");
+ tft.print(knownIp);
+ tft.setCursor(0,108);
+ tft.print("AP Pass:");
+ tft.print(apPass);
+ }
+ else
+ {
+ tft.setCursor(0, 84);
+ line = knownIp.toString();
+ center(line, tftcharwidth);
+ tft.print(line.c_str());
+ // percent brightness
+ tft.setCursor(0, 120);
+ tft.setTextColor(TFT_WHITE);
+ tft.print("Bri: ");
+ tft.print((((int)bri*100)/255));
+ tft.print("%");
+ // signal quality
+ tft.setCursor(124,120);
+ tft.print("Sig: ");
+ if (getSignalQuality(WiFi.RSSI()) < 10) {
+ tft.setTextColor(TFT_RED);
+ } else if (getSignalQuality(WiFi.RSSI()) < 25) {
+ tft.setTextColor(TFT_ORANGE);
+ } else {
+ tft.setTextColor(TFT_GREEN);
+ }
+ tft.print(getSignalQuality(WiFi.RSSI()));
+ tft.setTextColor(TFT_WHITE);
+ tft.print("%");
+ }
+
+ // mode name
+ tft.setTextColor(TFT_CYAN);
+ tft.setCursor(0, 144);
+ uint8_t qComma = 0;
+ bool insideQuotes = false;
+ uint8_t printedChars = 0;
+ char singleJsonSymbol;
+ // Find the mode name in JSON
+ for (size_t i = 0; i < strlen_P(JSON_mode_names); i++)
+ {
+ singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
+ switch (singleJsonSymbol)
+ {
+ case '"':
+ insideQuotes = !insideQuotes;
+ break;
+ case '[':
+ case ']':
+ break;
+ case ',':
+ qComma++;
+ default:
+ if (!insideQuotes || (qComma != knownMode))
+ break;
+ tft.print(singleJsonSymbol);
+ printedChars++;
+ }
+ if ((qComma > knownMode) || (printedChars > tftcharwidth - 1))
+ break;
+ }
+
+ // palette name
+ tft.setTextColor(TFT_YELLOW);
+ tft.setCursor(0, 168);
+ qComma = 0;
+ insideQuotes = false;
+ printedChars = 0;
+ // Looking for palette name in JSON.
+ for (size_t i = 0; i < strlen_P(JSON_palette_names); i++)
+ {
+ singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
+ switch (singleJsonSymbol)
+ {
case '"':
insideQuotes = !insideQuotes;
- break;
- case '[':
- case ']':
- break;
- case ',':
- qComma++;
- default:
- if (!insideQuotes || (qComma != knownMode))
break;
- tft.setTextColor(TFT_MAGENTA);
- tft.print(singleJsonSymbol);
- printedChars++;
- }
- if ((qComma > knownMode) || (printedChars > tftcharwidth - 1))
- break;
- }
-// Fourth row with palette name
- tft.setTextColor(TFT_YELLOW);
- tft.setCursor(3, 130);
- qComma = 0;
- insideQuotes = false;
- printedChars = 0;
-// Looking for palette name in JSON.
- for (size_t i = 0; i < strlen_P(JSON_palette_names); i++)
- {
- singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
- switch (singleJsonSymbol)
- {
- case '"':
- insideQuotes = !insideQuotes;
- break;
- case '[':
- case ']':
- break;
- case ',':
- qComma++;
- default:
- if (!insideQuotes || (qComma != knownPalette))
+ case '[':
+ case ']':
break;
- tft.print(singleJsonSymbol);
- printedChars++;
+ case ',':
+ qComma++;
+ default:
+ if (!insideQuotes || (qComma != knownPalette))
+ break;
+ tft.print(singleJsonSymbol);
+ printedChars++;
+ }
+ // The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode)
+ if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1))
+ break;
}
-// The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode)
- if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1))
- break;
- }
-// Fifth row with estimated mA usage
- tft.setTextColor(TFT_SILVER);
- tft.setCursor(3, 152);
-// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
- tft.print("Current: ");
- tft.print(strip.currentMilliamps);
- tft.print("mA");
+
+ tft.setCursor(0, 192);
+ tft.setTextColor(TFT_SILVER);
+ sprintf_P(buff, PSTR("FX Spd:%3d Int:%3d"), effectSpeed, effectIntensity);
+ tft.print(buff);
+
+ // Fifth row with estimated mA usage
+ tft.setTextColor(TFT_SILVER);
+ tft.setCursor(0, 216);
+ // Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
+ tft.print("Current: ");
+ tft.setTextColor(TFT_ORANGE);
+ tft.print(strip.currentMilliamps);
+ tft.print("mA");
}
+
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
@@ -295,7 +395,7 @@ class St7789DisplayUsermod : public Usermod {
*/
void readFromJsonState(JsonObject& root)
{
- userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
+ //userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
}
@@ -316,8 +416,8 @@ class St7789DisplayUsermod : public Usermod {
*/
void addToConfig(JsonObject& root)
{
- JsonObject top = root.createNestedObject("exampleUsermod");
- top["great"] = userVar0; //save this var persistently whenever settings are saved
+ //JsonObject top = root.createNestedObject("exampleUsermod");
+ //top["great"] = userVar0; //save this var persistently whenever settings are saved
}
@@ -329,10 +429,11 @@ class St7789DisplayUsermod : public Usermod {
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/
- void readFromConfig(JsonObject& root)
+ bool readFromConfig(JsonObject& root)
{
- JsonObject top = root["top"];
- userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
+ //JsonObject top = root["top"];
+ //userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
+ return true;
}
@@ -342,7 +443,7 @@ class St7789DisplayUsermod : public Usermod {
*/
uint16_t getId()
{
- return USERMOD_ST7789_DISPLAY;
+ return USERMOD_ID_ST7789_DISPLAY;
}
//More methods can be added in the future, this example will then be extended.
diff --git a/usermods/ST7789_display/Setup_ST7789_Display.h b/usermods/ST7789_display/Setup_ST7789_Display.h
deleted file mode 100644
index 26d5c17f..00000000
--- a/usermods/ST7789_display/Setup_ST7789_Display.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Setup for the ESP32 board with 1.5" 240x240 display
-
-// See SetupX_Template.h for all options available
-
-#define ST7789_DRIVER
-#define TFT_SDA_READ // Display has a bidirectionsl SDA pin
-
-#define TFT_WIDTH 240
-#define TFT_HEIGHT 240
-
-#define CGRAM_OFFSET // Library will add offsets required
-
-//#define TFT_MISO -1
-
-#define TFT_MOSI 21
-#define TFT_SCLK 22
-//#define TFT_CS 5
-#define TFT_DC 18
-#define TFT_RST 5
-
-#define TFT_BL 26 // Display backlight control pin
-
-#define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options
-
-#define LOAD_GLCD
-#define LOAD_FONT2
-#define LOAD_FONT4
-#define LOAD_FONT6
-#define LOAD_FONT7
-#define LOAD_FONT8
-#define LOAD_GFXFF
-
-//#define SMOOTH_FONT
-
-//#define SPI_FREQUENCY 27000000
- #define SPI_FREQUENCY 40000000 // Maximum for ILI9341
-
-
-#define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V
\ No newline at end of file
diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h
index c4229047..7c209f47 100644
--- a/usermods/Temperature/usermod_temperature.h
+++ b/usermods/Temperature/usermod_temperature.h
@@ -81,7 +81,9 @@ class UsermodTemperature : public Usermod {
temperature = readDallas();
lastMeasurement = millis();
waitingForConversion = false;
- DEBUG_PRINTF("Read temperature %2.1f.\n", temperature);
+ //DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266
+ DEBUG_PRINT(F("Read temperature "));
+ DEBUG_PRINTLN(temperature);
}
bool findSensor() {
diff --git a/usermods/UserModv2_SunRiseAndSet/README.md b/usermods/UserModv2_SunRiseAndSet/README.md
deleted file mode 100644
index e989f089..00000000
--- a/usermods/UserModv2_SunRiseAndSet/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-WLED v2 UserMod for running macros at sunrise and sunset.
-
-At the time of this text, this user mod requires code to be changed to set certain variables:
- 1. To reflect the user's graphical location (latitude/longitude) used for calculating apparent sunrise/sunset
- 2. To specify which macros will be run at sunrise and/or sunset. (defaults to 15 at sunrise and 16 at sunset)
- 3. To optionally provide an offset from sunrise/sunset, in minutes (max of +/- 2 hours), when the macro will be run.
-
-In addition, WLED must be configured to get time from NTP (and the time must be retrieved via NTP.)
-
-Please open the UserMod_SunRiseAndSet.h file for instructions on what needs to be changed, where to copy files, etc.
-
-If this usermod proves useful enough, the code might eventually be updated to allow prompting for the required information
-via the web interface and to store settings in EEPROM instead of hard-coding in the .h file.
-
-This usermod has only been tested on the esp32dev platform, but there's no reason it wouldn't work on other platforms.
diff --git a/usermods/UserModv2_SunRiseAndSet/UserMod_SunRiseAndSet.h b/usermods/UserModv2_SunRiseAndSet/UserMod_SunRiseAndSet.h
deleted file mode 100644
index ef1bb37e..00000000
--- a/usermods/UserModv2_SunRiseAndSet/UserMod_SunRiseAndSet.h
+++ /dev/null
@@ -1,166 +0,0 @@
-#pragma once
-
-#include "wled.h"
-#include
-
-/*
- *
- * REQUIREMENTS:
- * The Dusk2Dawn library must be installed. This can be found at https://github.com/dmkishi/Dusk2Dawn. The 1.0.1 version of this library found via
- * Arduino or platformio library managers is buggy and won't compile. The latest version from github should be used.
- *
- * NTP must be enabled and functional. It simply makes no sense to have events on sunrise/sunset when an accurate time isn't available.
- *
- * The user's geographical latitude and longitude must be configured (in decimal, not degrees/minutes/etc) using m_fLatitude and m_fLongitude
- *
- * if desired, an offset of up to +/- 2 hours can be specified for each of sunrise/sunset using m_sunriseOffset and m_sunsetOffset (defaults to 0)
- *
- * The specific macro to run at sunrise and/or sunset can be changed using m_sunriseMacro and m_sunsetMacro. (defaults to 15 and 16)
- *
- * From the Dusk2Dawn library:
- * HINT: An easy way to find the longitude and latitude for any location is
- * to find the spot in Google Maps, right click the place on the map, and
- * select "What's here?". At the bottom, you’ll see a card with the
- * coordinates.
- *
- * Once configured, copy UserMod_SunRiseAndSet.h to the sketch file (the same folder as wled00.ino exists),
- * and then edit "usermods_list.cpp":
- * Add '#include "UserMod_SunRiseAndSet.h"' in the 'includes' area
- * Add 'usermods.add(new UserMod_SunRiseAndSet());' in the registerUsermods() area
- *
- */
-
-class UserMod_SunRiseAndSet : public Usermod
-{
-private:
-
- /**** USER SETTINGS ****/
-
- float m_fLatitude = 40.6; // latitude where sunrise/set are calculated
- float m_fLongitude = -79.80; // longitude where sunrise/set are calculated
- int8_t m_sunriseOffset = 0; // offset from sunrise, in minutes, when macro should be run (negative for before sunrise, positive for after sunrise)
- int8_t m_sunsetOffset = 0; // offset from sunset, in minutes, when macro should be run (negative for before sunset, positive for after sunset)
- uint8_t m_sunriseMacro = 15; // macro number to run at sunrise
- uint8_t m_sunsetMacro = 16; // macro number to run at sunset
-
- /**** END OF USER SETTINGS. DO NOT EDIT BELOW THIS LINE! ****/
-
-
- Dusk2Dawn *m_pD2D = NULL; // this must be dynamically allocated in order for parameters to be loaded from EEPROM
-
- int m_nUserSunrise = -1; // time, in minutes from midnight, of sunrise
- int m_nUserSunset = -1; // time, in minutes from midnight, of sunset
-
- byte m_nLastRunMinute = -1; // indicates what minute the userloop was last run - used so that the code only runs once per minute
-
-public:
-
- virtual void setup(void)
- {
- /* TODO: From EEPROM, load the following variables:
- *
- * int16_t latitude16 = 4060; // user provided latitude, multiplied by 100 and rounded
- * int16_t longitude16 = -7980; // user provided longitude, multiplied by 100 and rounded.
- * int8_t sunrise_offset = 0; // number of minutes to offset the sunrise macro trigger (positive for minutes after sunrise, negative for minutes before)
- * int8_t sunset_offset = 0; // number of minutes to offset the sunset macro trigger (positive for minutes after sunset, negative for minutes before)
- *
- * then:
- * m_fLatitude = (float)latitude / 100.0;
- * m_fLongitude = (float)longitude / 100.0;
- * m_sunriseOffset = sunrise_offset;
- * m_sunsetOffset = sunset_offset;
- */
-
- if ((0.0 != m_fLatitude) || (0.0 != m_fLongitude))
- {
- m_pD2D = new Dusk2Dawn (m_fLatitude, m_fLongitude, 0 /* UTC */);
- // can't really check for failures. if the alloc fails, the mod just doesn't work.
- }
- }
-
- void loop(void)
- {
- // without NTP, or a configured lat/long, none of this stuff is going to work...
- // As an alternative, need to figure out how to determine if the user has manually set the clock or not.
- if (m_pD2D && (999000000L != ntpLastSyncTime))
- {
- // to prevent needing to import all the timezone stuff from other modules, work completely in UTC
- time_t timeUTC = toki.second();
- tmElements_t tmNow;
- breakTime(timeUTC, tmNow);
- int nCurMinute = tmNow.Minute;
-
- if (m_nLastRunMinute != nCurMinute) //only check once a new minute begins
- {
- m_nLastRunMinute = nCurMinute;
- int numMinutes = (60 * tmNow.Hour) + m_nLastRunMinute; // how many minutes into the day are we?
-
- // check to see if sunrise/sunset should be re-determined. Only do this if neither sunrise nor sunset
- // are set. That happens when the device has just stated, and after both sunrise/sunset have already run.
- if ((-1 == m_nUserSunrise) && (-1 == m_nUserSunset))
- {
- m_nUserSunrise = m_pD2D->sunrise(tmNow.Year + 1970, tmNow.Month, tmNow.Day, false) % 1440;
- m_nUserSunset = m_pD2D->sunset(tmNow.Year + 1970, tmNow.Month, tmNow.Day, false) % 1440;
- if (m_nUserSunrise > numMinutes) // has sunrise already passed? if so, recompute for tomorrow
- {
- breakTime(timeUTC + (60*60*24), tmNow);
- m_nUserSunrise = m_pD2D->sunrise(tmNow.Year + 1970, tmNow.Month, tmNow.Day, false) % 1440;
- if (m_nUserSunset > numMinutes) // if sunset has also passed, recompute that as well
- {
- m_nUserSunset = m_pD2D->sunset(tmNow.Year + 1970, tmNow.Month, tmNow.Day, false) % 1440;
- }
- }
- // offset by user provided values. becuase the offsets are signed bytes, the max offset is just over 2 hours.
- m_nUserSunrise += m_sunriseOffset;
- m_nUserSunset += m_sunsetOffset;
- }
-
- if (numMinutes == m_nUserSunrise) // Good Morning!
- {
- if (m_sunriseMacro)
- applyMacro(m_sunriseMacro); // run macro 15
- m_nUserSunrise = -1;
- }
- else if (numMinutes == m_nUserSunset) // Good Night!
- {
- if (m_sunsetMacro)
- applyMacro(m_sunsetMacro); // run macro 16
- m_nUserSunset = -1;
- }
- } // if (m_nLastRunMinute != nCurMinute)
- } // if (m_pD2D && (999000000L != ntpLastSyncTime))
- }
-
- void addToJsonState(JsonObject& root)
- {
- JsonObject user = root["SunRiseAndSet"];
- if (user.isNull()) user = root.createNestedObject("SunRiseAndSet");
-
- char buf[10];
- if (-1 != m_nUserSunrise)
- {
- snprintf(buf, 10, "%02d:%02d UTC", m_nUserSunrise / 60, m_nUserSunrise % 60);
- user["rise"] = buf;
- }
- if (-1 != m_nUserSunset)
- {
- snprintf(buf, 10, "%02d:%02d UTC", m_nUserSunset / 60, m_nUserSunset % 60);
- user["set"] = buf;
- }
- JsonObject vars = user.createNestedObject("vars");
- vars["lat"] = m_fLatitude;
- vars["long"] = m_fLongitude;
- vars["rise_mac"] = m_sunriseMacro;
- vars["set_mac"] = m_sunsetMacro;
- vars["rise_off"] = m_sunriseOffset;
- vars["set_off"] = m_sunsetOffset;
- }
-
- ~UserMod_SunRiseAndSet(void)
- {
- if (m_pD2D) delete m_pD2D;
- }
-};
-
-
-
diff --git a/usermods/esp32_multistrip/NpbWrapper.h b/usermods/esp32_multistrip/NpbWrapper.h
deleted file mode 100644
index 84cf8ac0..00000000
--- a/usermods/esp32_multistrip/NpbWrapper.h
+++ /dev/null
@@ -1,515 +0,0 @@
-//this code is a modified version of https://github.com/Makuna/NeoPixelBus/issues/103
-#ifndef NpbWrapper_h
-#define NpbWrapper_h
-
-// make sure we're using esp32 platform
-#ifndef ARDUINO_ARCH_ESP32
- #error This version of NbpWrapper.h only works with ESP32 hardware.
-#endif
-
-#ifndef NUM_STRIPS
- #error Need to define number of LED strips using build flag -D NUM_STRIPS=4 for 4 LED strips
-#endif
-
-#ifndef PIXEL_COUNTS
- #error Need to define pixel counts using build flag -D PIXEL_COUNTS="25, 25, 25, 25" for 4 LED strips with 25 LEDs each
-#endif
-
-#ifndef DATA_PINS
- #error Need to define data pins using build flag -D DATA_PINS="1, 2, 3, 4" if LED strips are on data pins 1, 2, 3, and 4
-#endif
-
-// //PIN CONFIGURATION
-#ifndef LEDPIN
- #define LEDPIN 1 // Legacy pin def required by some other portions of code. This pin is not used do drive LEDs.
-#endif
-
-#ifndef IRPIN
- #define IRPIN -1 //infrared pin (-1 to disable) MagicHome: 4, H801 Wifi: 0
-#endif
-
-#ifndef RLYPIN
- #define RLYPIN -1 //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
-
-#include
-#include "const.h"
-
-const uint8_t numStrips = NUM_STRIPS; // max 8 strips allowed on esp32
-const uint16_t pixelCounts[numStrips] = {PIXEL_COUNTS}; // number of pixels on each strip
-const uint8_t dataPins[numStrips] = {DATA_PINS}; // change these pins based on your board
-
-#define PIXELFEATURE3 NeoGrbFeature
-#define PIXELFEATURE4 NeoGrbwFeature
-
-// ESP32 has 8 RMT interfaces available, each of which can drive a strip of pixels
-// Convenience #defines for creating NeoPixelBrightnessBus on each RMT interface for both GRB and GRBW LED strips
-#define NeoPixelBrightnessBusGrbRmt0 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbRmt1 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbRmt2 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbRmt3 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbRmt4 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbRmt5 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbRmt6 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbRmt7 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbwRmt0 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbwRmt1 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbwRmt2 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbwRmt3 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbwRmt4 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbwRmt5 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbwRmt6 NeoPixelBrightnessBus
-#define NeoPixelBrightnessBusGrbwRmt7 NeoPixelBrightnessBus
-
-enum NeoPixelType
-{
- NeoPixelType_None = 0,
- NeoPixelType_Grb = 1,
- NeoPixelType_Grbw = 2,
- NeoPixelType_End = 3
-};
-
-class NeoPixelWrapper
-{
-public:
- NeoPixelWrapper() :
- _type(NeoPixelType_None)
- {
- // On initialization fill in the pixelStripStartIdx array with the beginning index of each strip
- // relative to th entire array.
- uint16_t totalPixels = 0;
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- pixelStripStartIdx[idx] = totalPixels;
- totalPixels += pixelCounts[idx];
- }
- }
-
- ~NeoPixelWrapper()
- {
- cleanup();
- }
-
- void Begin(NeoPixelType type, uint16_t pixelCount)
- {
-
- cleanup();
-
- _type = type;
-
- switch (_type)
- {
- case NeoPixelType_Grb:
- {
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- switch (idx)
- {
- case 0: pGrb0 = new NeoPixelBrightnessBusGrbRmt0(pixelCounts[idx], dataPins[idx]); pGrb0->Begin(); break;
- case 1: pGrb1 = new NeoPixelBrightnessBusGrbRmt1(pixelCounts[idx], dataPins[idx]); pGrb1->Begin(); break;
- case 2: pGrb2 = new NeoPixelBrightnessBusGrbRmt2(pixelCounts[idx], dataPins[idx]); pGrb2->Begin(); break;
- case 3: pGrb3 = new NeoPixelBrightnessBusGrbRmt3(pixelCounts[idx], dataPins[idx]); pGrb3->Begin(); break;
- case 4: pGrb4 = new NeoPixelBrightnessBusGrbRmt4(pixelCounts[idx], dataPins[idx]); pGrb4->Begin(); break;
- case 5: pGrb5 = new NeoPixelBrightnessBusGrbRmt5(pixelCounts[idx], dataPins[idx]); pGrb5->Begin(); break;
- case 6: pGrb6 = new NeoPixelBrightnessBusGrbRmt6(pixelCounts[idx], dataPins[idx]); pGrb6->Begin(); break;
- case 7: pGrb7 = new NeoPixelBrightnessBusGrbRmt7(pixelCounts[idx], dataPins[idx]); pGrb7->Begin(); break;
- }
- }
- break;
- }
-
- case NeoPixelType_Grbw:
- {
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- switch (idx)
- {
- case 0: pGrbw0 = new NeoPixelBrightnessBusGrbwRmt0(pixelCounts[idx], dataPins[idx]); pGrbw0->Begin(); break;
- case 1: pGrbw1 = new NeoPixelBrightnessBusGrbwRmt1(pixelCounts[idx], dataPins[idx]); pGrbw1->Begin(); break;
- case 2: pGrbw2 = new NeoPixelBrightnessBusGrbwRmt2(pixelCounts[idx], dataPins[idx]); pGrbw2->Begin(); break;
- case 3: pGrbw3 = new NeoPixelBrightnessBusGrbwRmt3(pixelCounts[idx], dataPins[idx]); pGrbw3->Begin(); break;
- case 4: pGrbw4 = new NeoPixelBrightnessBusGrbwRmt4(pixelCounts[idx], dataPins[idx]); pGrbw4->Begin(); break;
- case 5: pGrbw5 = new NeoPixelBrightnessBusGrbwRmt5(pixelCounts[idx], dataPins[idx]); pGrbw5->Begin(); break;
- case 6: pGrbw6 = new NeoPixelBrightnessBusGrbwRmt6(pixelCounts[idx], dataPins[idx]); pGrbw6->Begin(); break;
- case 7: pGrbw7 = new NeoPixelBrightnessBusGrbwRmt7(pixelCounts[idx], dataPins[idx]); pGrbw7->Begin(); break;
- }
- }
- break;
- }
- }
- }
-
- void Show()
- {
- switch (_type)
- {
- case NeoPixelType_Grb:
- {
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- switch (idx)
- {
- case 0: pGrb0->Show(); break;
- case 1: pGrb1->Show(); break;
- case 2: pGrb2->Show(); break;
- case 3: pGrb3->Show(); break;
- case 4: pGrb4->Show(); break;
- case 5: pGrb5->Show(); break;
- case 6: pGrb6->Show(); break;
- case 7: pGrb7->Show(); break;
- }
- }
- break;
- }
- case NeoPixelType_Grbw:
- {
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- switch (idx)
- {
- case 0: pGrbw0->Show(); break;
- case 1: pGrbw1->Show(); break;
- case 2: pGrbw2->Show(); break;
- case 3: pGrbw3->Show(); break;
- case 4: pGrbw4->Show(); break;
- case 5: pGrbw5->Show(); break;
- case 6: pGrbw6->Show(); break;
- case 7: pGrbw7->Show(); break;
- }
- }
- break;
- }
- }
- }
-
- bool CanShow()
- {
- bool canShow = true;
- switch (_type)
- {
- case NeoPixelType_Grb:
- {
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- switch (idx)
- {
- case 0: canShow &= pGrb0->CanShow(); break;
- case 1: canShow &= pGrb1->CanShow(); break;
- case 2: canShow &= pGrb2->CanShow(); break;
- case 3: canShow &= pGrb3->CanShow(); break;
- case 4: canShow &= pGrb4->CanShow(); break;
- case 5: canShow &= pGrb5->CanShow(); break;
- case 6: canShow &= pGrb6->CanShow(); break;
- case 7: canShow &= pGrb7->CanShow(); break;
- }
- }
- break;
- }
- case NeoPixelType_Grbw:
- {
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- switch (idx)
- {
- case 0: canShow &= pGrbw0->CanShow(); break;
- case 1: canShow &= pGrbw1->CanShow(); break;
- case 2: canShow &= pGrbw2->CanShow(); break;
- case 3: canShow &= pGrbw3->CanShow(); break;
- case 4: canShow &= pGrbw4->CanShow(); break;
- case 5: canShow &= pGrbw5->CanShow(); break;
- case 6: canShow &= pGrbw6->CanShow(); break;
- case 7: canShow &= pGrbw7->CanShow(); break;
- }
- }
- break;
- }
- }
- return canShow;
- }
-
- void SetPixelColorRaw(uint16_t indexPixel, RgbwColor c)
- {
- // figure out which strip this pixel index is on
- uint8_t stripIdx = 0;
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- if (indexPixel >= pixelStripStartIdx[idx])
- {
- stripIdx = idx;
- }
- else
- {
- break;
- }
- }
- // subtract strip start index so we're addressing just this strip instead of all pixels on all strips
- indexPixel -= pixelStripStartIdx[stripIdx];
- switch (_type)
- {
- case NeoPixelType_Grb:
- {
- RgbColor rgb = RgbColor(c.R, c.G, c.B);
- switch (stripIdx)
- {
- case 0: pGrb0->SetPixelColor(indexPixel, rgb); break;
- case 1: pGrb1->SetPixelColor(indexPixel, rgb); break;
- case 2: pGrb2->SetPixelColor(indexPixel, rgb); break;
- case 3: pGrb3->SetPixelColor(indexPixel, rgb); break;
- case 4: pGrb4->SetPixelColor(indexPixel, rgb); break;
- case 5: pGrb5->SetPixelColor(indexPixel, rgb); break;
- case 6: pGrb6->SetPixelColor(indexPixel, rgb); break;
- case 7: pGrb7->SetPixelColor(indexPixel, rgb); break;
- }
- break;
- }
- case NeoPixelType_Grbw:
- {
- switch (stripIdx)
- {
- case 0: pGrbw0->SetPixelColor(indexPixel, c); break;
- case 1: pGrbw1->SetPixelColor(indexPixel, c); break;
- case 2: pGrbw2->SetPixelColor(indexPixel, c); break;
- case 3: pGrbw3->SetPixelColor(indexPixel, c); break;
- case 4: pGrbw4->SetPixelColor(indexPixel, c); break;
- case 5: pGrbw5->SetPixelColor(indexPixel, c); break;
- case 6: pGrbw6->SetPixelColor(indexPixel, c); break;
- case 7: pGrbw7->SetPixelColor(indexPixel, c); break;
- }
- break;
- }
- }
- }
-
- void SetPixelColor(uint16_t indexPixel, RgbwColor c)
- {
- /*
- Set pixel color with necessary color order conversion.
- */
-
- 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;
-
- SetPixelColorRaw(indexPixel, col);
- }
-
- void SetBrightness(byte b)
- {
- switch (_type)
- {
- case NeoPixelType_Grb:
- {
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- switch (idx)
- {
- case 0: pGrb0->SetBrightness(b); break;
- case 1: pGrb1->SetBrightness(b); break;
- case 2: pGrb2->SetBrightness(b); break;
- case 3: pGrb3->SetBrightness(b); break;
- case 4: pGrb4->SetBrightness(b); break;
- case 5: pGrb5->SetBrightness(b); break;
- case 6: pGrb6->SetBrightness(b); break;
- case 7: pGrb7->SetBrightness(b); break;
- }
- }
- break;
- }
- case NeoPixelType_Grbw:
- {
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- switch (idx)
- {
- case 0: pGrbw0->SetBrightness(b); break;
- case 1: pGrbw1->SetBrightness(b); break;
- case 2: pGrbw2->SetBrightness(b); break;
- case 3: pGrbw3->SetBrightness(b); break;
- case 4: pGrbw4->SetBrightness(b); break;
- case 5: pGrbw5->SetBrightness(b); break;
- case 6: pGrbw6->SetBrightness(b); break;
- case 7: pGrbw7->SetBrightness(b); break;
- }
- }
- break;
- }
- }
- }
-
- void SetColorOrder(byte colorOrder)
- {
- _colorOrder = colorOrder;
- }
-
- uint8_t GetColorOrder()
- {
- return _colorOrder;
- }
-
- RgbwColor GetPixelColorRaw(uint16_t indexPixel) const
- {
- // figure out which strip this pixel index is on
- uint8_t stripIdx = 0;
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- if (indexPixel >= pixelStripStartIdx[idx])
- {
- stripIdx = idx;
- }
- else
- {
- break;
- }
- }
- // subtract strip start index so we're addressing just this strip instead of all pixels on all strips
- indexPixel -= pixelStripStartIdx[stripIdx];
- switch (_type)
- {
- case NeoPixelType_Grb:
- {
- switch (stripIdx)
- {
- case 0: return pGrb0->GetPixelColor(indexPixel);
- case 1: return pGrb1->GetPixelColor(indexPixel);
- case 2: return pGrb2->GetPixelColor(indexPixel);
- case 3: return pGrb3->GetPixelColor(indexPixel);
- case 4: return pGrb4->GetPixelColor(indexPixel);
- case 5: return pGrb5->GetPixelColor(indexPixel);
- case 6: return pGrb6->GetPixelColor(indexPixel);
- case 7: return pGrb7->GetPixelColor(indexPixel);
- }
- break;
- }
- case NeoPixelType_Grbw:
- switch (stripIdx)
- {
- case 0: return pGrbw0->GetPixelColor(indexPixel);
- case 1: return pGrbw1->GetPixelColor(indexPixel);
- case 2: return pGrbw2->GetPixelColor(indexPixel);
- case 3: return pGrbw3->GetPixelColor(indexPixel);
- case 4: return pGrbw4->GetPixelColor(indexPixel);
- case 5: return pGrbw5->GetPixelColor(indexPixel);
- case 6: return pGrbw6->GetPixelColor(indexPixel);
- case 7: return pGrbw7->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 = GetPixelColorRaw(indexPixel);
- 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;
-
- }
-
-
-private:
- NeoPixelType _type;
- byte _colorOrder = 0;
-
- uint16_t pixelStripStartIdx[numStrips];
-
- // pointers for every possible type for up to 8 strips
- NeoPixelBrightnessBusGrbRmt0 *pGrb0;
- NeoPixelBrightnessBusGrbRmt1 *pGrb1;
- NeoPixelBrightnessBusGrbRmt2 *pGrb2;
- NeoPixelBrightnessBusGrbRmt3 *pGrb3;
- NeoPixelBrightnessBusGrbRmt4 *pGrb4;
- NeoPixelBrightnessBusGrbRmt5 *pGrb5;
- NeoPixelBrightnessBusGrbRmt6 *pGrb6;
- NeoPixelBrightnessBusGrbRmt7 *pGrb7;
- NeoPixelBrightnessBusGrbwRmt0 *pGrbw0;
- NeoPixelBrightnessBusGrbwRmt1 *pGrbw1;
- NeoPixelBrightnessBusGrbwRmt2 *pGrbw2;
- NeoPixelBrightnessBusGrbwRmt3 *pGrbw3;
- NeoPixelBrightnessBusGrbwRmt4 *pGrbw4;
- NeoPixelBrightnessBusGrbwRmt5 *pGrbw5;
- NeoPixelBrightnessBusGrbwRmt6 *pGrbw6;
- NeoPixelBrightnessBusGrbwRmt7 *pGrbw7;
-
- void cleanup()
- {
- switch (_type)
- {
- case NeoPixelType_Grb:
- {
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- switch (idx)
- {
- case 0: delete pGrb0; pGrb0 = NULL; break;
- case 1: delete pGrb1; pGrb1 = NULL; break;
- case 2: delete pGrb2; pGrb2 = NULL; break;
- case 3: delete pGrb3; pGrb3 = NULL; break;
- case 4: delete pGrb4; pGrb4 = NULL; break;
- case 5: delete pGrb5; pGrb5 = NULL; break;
- case 6: delete pGrb6; pGrb6 = NULL; break;
- case 7: delete pGrb7; pGrb7 = NULL; break;
- }
- }
- break;
- }
- case NeoPixelType_Grbw:
- {
- for (uint8_t idx = 0; idx < numStrips; idx++)
- {
- switch (idx)
- {
- case 0: delete pGrbw0; pGrbw0 = NULL; break;
- case 1: delete pGrbw1; pGrbw1 = NULL; break;
- case 2: delete pGrbw2; pGrbw2 = NULL; break;
- case 3: delete pGrbw3; pGrbw3 = NULL; break;
- case 4: delete pGrbw4; pGrbw4 = NULL; break;
- case 5: delete pGrbw5; pGrbw5 = NULL; break;
- case 6: delete pGrbw6; pGrbw6 = NULL; break;
- case 7: delete pGrbw7; pGrbw7 = NULL; break;
- }
- }
- }
- }
- }
-};
-#endif
diff --git a/usermods/esp32_multistrip/README.md b/usermods/esp32_multistrip/README.md
deleted file mode 100644
index 87b89528..00000000
--- a/usermods/esp32_multistrip/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# esp32_multistrip
-
-This usermod enables up to 8 data pins to be used from an esp32 module to drive separate LED strands. This only works with one-wire LEDs like the WS2812.
-
-The esp32 RMT hardware is used for data output. See here for hardware driver implementation details: https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
-
-Pass the following variables to the compiler as build flags:
-
- - `ESP32_MULTISTRIP`
- - Define this to use usermod NpbWrapper.h instead of default one in WLED.
- - `NUM_STRIPS`
- - Number of strips in use
- - `PIXEL_COUNTS`
- - List of pixel counts in each strip
- - `DATA_PINS`
- - List of data pins each strip is attached to. There may be board-specific restrictions on which pins can be used for RTM.
-
-From the perspective of WLED software, the LEDs are addressed as one long strand. The modified NbpWrapper.h file addresses the appropriate strand from the overall LED index based on the number of LEDs defined in each strand.
-
-See `platformio_override.ini` for example configuration.
-
-Tested on low cost ESP-WROOM-32 dev boards from Amazon, such as those sold by KeeYees.
diff --git a/usermods/esp32_multistrip/platformio_override.ini b/usermods/esp32_multistrip/platformio_override.ini
deleted file mode 100644
index afdef676..00000000
--- a/usermods/esp32_multistrip/platformio_override.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-; Example platformio_override.ini that shows how to configure your environment to use the multistrip usermod.
-; Copy this file to the base wled directory that contains platformio.ini.
-; Multistrip requires ESP32 because it has many more pins that can be used as LED outputs.
-; Need to define NUM_STRIPS, PIXEL_COUNTS, and DATA_PINS as shown below.
-
-[platformio]
-default_envs = esp32_multistrip
-
-[env:esp32_multistrip]
-extends=env:esp32dev
-build_flags = ${env:esp32dev.build_flags}
- -D ESP32_MULTISTRIP ; define this variable to use ESP32_MULTISTRIP usermod
- -D NUM_STRIPS=4 ; number of pixel strips in use
- -D PIXEL_COUNTS="50, 50, 50, 50" ; number of pixels in each strip
- -D DATA_PINS="25, 26, 32, 33" ; esp32 pins used for each pixel strip. available pins depends on esp32 module.
-
\ No newline at end of file
diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h
index cd3e01c7..c3d55d66 100644
--- a/usermods/multi_relay/usermod_multi_relay.h
+++ b/usermods/multi_relay/usermod_multi_relay.h
@@ -317,15 +317,15 @@ class MultiRelay : public Usermod {
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
- void addToJsonState(JsonObject &root) {
- }
+ //void addToJsonState(JsonObject &root) {
+ //}
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
- void readFromJsonState(JsonObject &root) {
- }
+ //void readFromJsonState(JsonObject &root) {
+ //}
/**
* provide the changeable values
@@ -335,11 +335,12 @@ class MultiRelay : public Usermod {
top[FPSTR(_enabled)] = enabled;
for (uint8_t i=0; i
-
-1. Edit this file from the root directory as needed:
-
-
-
- * `PIXEL_COUNTS` may need to be adjusted for your set-up. E.g. I have lots of LEDs in Channel 1, but that's probably unusual for most
- * `DATA_PINS` may need to be changed to "16,3,1,26" instead of "16,1,3,26" apparently depending on the board revision or some such
-
-1. Build the mod (e.g. click `Build` from the project task circled above) and update your firmware using the `QL-DigQuad-Pre-v0.1` file, e.g. using _Manual OTA_ from the Config menu. Based on the video and my own experience, you might need to build twice 🤷♂️.
-
-## Observing Temperature
-
-Hopefully you can now see the Temperature listed in the Info page. If not, use Chrome Developer Tools to find the current temperature
-
-1. Open the Developer Tools Console
-2. Enter `lastinfo.u.Temperature` to view the Temperature array
-
-
-
diff --git a/usermods/quinled_digquad_preassembled_unofficial_v0.1/images/json-temp.png b/usermods/quinled_digquad_preassembled_unofficial_v0.1/images/json-temp.png
deleted file mode 100644
index 66e50112..00000000
Binary files a/usermods/quinled_digquad_preassembled_unofficial_v0.1/images/json-temp.png and /dev/null differ
diff --git a/usermods/quinled_digquad_preassembled_unofficial_v0.1/images/params.png b/usermods/quinled_digquad_preassembled_unofficial_v0.1/images/params.png
deleted file mode 100644
index 64233f86..00000000
Binary files a/usermods/quinled_digquad_preassembled_unofficial_v0.1/images/params.png and /dev/null differ
diff --git a/usermods/quinled_digquad_preassembled_unofficial_v0.1/images/pio-screenshot.png b/usermods/quinled_digquad_preassembled_unofficial_v0.1/images/pio-screenshot.png
deleted file mode 100644
index e178ed16..00000000
Binary files a/usermods/quinled_digquad_preassembled_unofficial_v0.1/images/pio-screenshot.png and /dev/null differ
diff --git a/usermods/quinled_digquad_preassembled_unofficial_v0.1/platformio_override.ini b/usermods/quinled_digquad_preassembled_unofficial_v0.1/platformio_override.ini
deleted file mode 100644
index 6f416668..00000000
--- a/usermods/quinled_digquad_preassembled_unofficial_v0.1/platformio_override.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-; QuinLED-Dig-Quad Preassembled Unofficial
-
-[env:QL-DigQuad-Pre-v0.1]
-extends = env:esp32dev
-build_flags = ${common.build_flags_esp32}
- -D ESP32_MULTISTRIP
- -D NUM_STRIPS=4
- -D PIXEL_COUNTS="600, 300, 300, 300"
- -D DATA_PINS="16,1,3,26"
- -D RLYPIN=19
- -D BTNPIN=17
- -D USERMOD_DALLASTEMPERATURE
- -D USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL=10000
-lib_deps = ${env.lib_deps}
- milesburton/DallasTemperature@^3.9.0
- OneWire@~2.3.5
\ No newline at end of file
diff --git a/usermods/ssd1306_i2c_oled_u8g2/README.md b/usermods/ssd1306_i2c_oled_u8g2/README.md
deleted file mode 100644
index 70919cc5..00000000
--- a/usermods/ssd1306_i2c_oled_u8g2/README.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# SSD1306 128x32 OLED via I2C with u8g2
-This usermod allows to connect 128x32 Oled display to WLED controlled and show
-the next information:
-- Current SSID
-- IP address if obtained
- * in AP mode and turned off lightning AP password is shown
-- Current effect
-- Current palette
-- On/Off icon (sun/moon)
-
-## Hardware
-![Hardware connection](assets/hw_connection.png)
-
-## Requirements
-Functionality checked with:
-- commit 095429a7df4f9e2b34dd464f7bbfd068df6558eb
-- Wemos d1 mini
-- PlatformIO
-- Generic SSD1306 128x32 I2C OLED display from aliexpress
-
-### Platformio
-Add `U8g2@~2.27.2` dependency to `lib_deps_external` under `[common]` section in `platformio.ini`:
-```ini
-# platformio.ini
-...
-[common]
-...
-lib_deps_external =
- ...
- U8g2@~2.27.2
-...
-```
-
-### Arduino IDE
-Install library `U8g2 by oliver` in `Tools | Include Library | Manage libraries` menu.
\ No newline at end of file
diff --git a/usermods/ssd1306_i2c_oled_u8g2/assets/hw_connection.png b/usermods/ssd1306_i2c_oled_u8g2/assets/hw_connection.png
deleted file mode 100644
index a0e51b4d..00000000
Binary files a/usermods/ssd1306_i2c_oled_u8g2/assets/hw_connection.png and /dev/null differ
diff --git a/usermods/ssd1306_i2c_oled_u8g2/wled06_usermod.ino b/usermods/ssd1306_i2c_oled_u8g2/wled06_usermod.ino
deleted file mode 100644
index 8c749603..00000000
--- a/usermods/ssd1306_i2c_oled_u8g2/wled06_usermod.ino
+++ /dev/null
@@ -1,175 +0,0 @@
-#include // from https://github.com/olikraus/u8g2/
-
-//The SCL and SDA pins are defined here.
-//Lolin32 boards use SCL=5 SDA=4
-#define U8X8_PIN_SCL 5
-#define U8X8_PIN_SDA 4
-
-
-// If display does not work or looks corrupted check the
-// constructor reference:
-// https://github.com/olikraus/u8g2/wiki/u8x8setupcpp
-// or check the gallery:
-// https://github.com/olikraus/u8g2/wiki/gallery
-U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL,
- U8X8_PIN_SDA); // Pins are Reset, SCL, SDA
-
-// gets called once at boot. Do all initialization that doesn't depend on
-// network here
-void userSetup() {
- u8x8.begin();
- u8x8.setPowerSave(0);
- u8x8.setContrast(10); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
- u8x8.setFont(u8x8_font_chroma48medium8_r);
- u8x8.drawString(0, 0, "Loading...");
-}
-
-// gets called every time WiFi is (re-)connected. Initialize own network
-// interfaces here
-void userConnected() {}
-
-// needRedraw marks if redraw is required to prevent often redrawing.
-bool needRedraw = true;
-
-// Next variables hold the previous known values to determine if redraw is
-// required.
-String knownSsid = "";
-IPAddress knownIp;
-uint8_t knownBrightness = 0;
-uint8_t knownMode = 0;
-uint8_t knownPalette = 0;
-
-long lastUpdate = 0;
-long lastRedraw = 0;
-bool displayTurnedOff = false;
-// How often we are redrawing screen
-#define USER_LOOP_REFRESH_RATE_MS 5000
-
-void userLoop() {
-
- // Check if we time interval for redrawing passes.
- if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) {
- return;
- }
- lastUpdate = millis();
-
- // Turn off display after 3 minutes with no change.
- if(!displayTurnedOff && millis() - lastRedraw > 3*60*1000) {
- u8x8.setPowerSave(1);
- displayTurnedOff = true;
- }
-
- // Check if values which are shown on display changed from the last time.
- if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) {
- needRedraw = true;
- } else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) {
- needRedraw = true;
- } else if (knownBrightness != bri) {
- needRedraw = true;
- } else if (knownMode != strip.getMode()) {
- needRedraw = true;
- } else if (knownPalette != strip.getSegment(0).palette) {
- needRedraw = true;
- }
-
- if (!needRedraw) {
- return;
- }
- needRedraw = false;
-
- if (displayTurnedOff)
- {
- u8x8.setPowerSave(0);
- displayTurnedOff = false;
- }
- lastRedraw = millis();
-
- // Update last known values.
- #if defined(ESP8266)
- knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
- #else
- knownSsid = WiFi.SSID();
- #endif
- knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
- knownBrightness = bri;
- knownMode = strip.getMode();
- knownPalette = strip.getSegment(0).palette;
-
- u8x8.clear();
- u8x8.setFont(u8x8_font_chroma48medium8_r);
-
- // First row with Wifi name
- u8x8.setCursor(1, 0);
- u8x8.print(knownSsid.substring(0, u8x8.getCols() > 1 ? u8x8.getCols() - 2 : 0));
- // Print `~` char to indicate that SSID is longer, than owr dicplay
- if (knownSsid.length() > u8x8.getCols())
- u8x8.print("~");
-
- // Second row with IP or Psssword
- u8x8.setCursor(1, 1);
- // Print password in AP mode and if led is OFF.
- if (apActive && bri == 0)
- u8x8.print(apPass);
- else
- u8x8.print(knownIp);
-
- // Third row with mode name
- u8x8.setCursor(2, 2);
- uint8_t qComma = 0;
- bool insideQuotes = false;
- uint8_t printedChars = 0;
- char singleJsonSymbol;
- // Find the mode name in JSON
- for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) {
- singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
- switch (singleJsonSymbol) {
- case '"':
- insideQuotes = !insideQuotes;
- break;
- case '[':
- case ']':
- break;
- case ',':
- qComma++;
- default:
- if (!insideQuotes || (qComma != knownMode))
- break;
- u8x8.print(singleJsonSymbol);
- printedChars++;
- }
- if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
- break;
- }
- // Fourth row with palette name
- u8x8.setCursor(2, 3);
- qComma = 0;
- insideQuotes = false;
- printedChars = 0;
- // Looking for palette name in JSON.
- for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) {
- singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
- switch (singleJsonSymbol) {
- case '"':
- insideQuotes = !insideQuotes;
- break;
- case '[':
- case ']':
- break;
- case ',':
- qComma++;
- default:
- if (!insideQuotes || (qComma != knownPalette))
- break;
- u8x8.print(singleJsonSymbol);
- printedChars++;
- }
- if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2))
- break;
- }
-
- u8x8.setFont(u8x8_font_open_iconic_embedded_1x1);
- u8x8.drawGlyph(0, 0, 80); // wifi icon
- u8x8.drawGlyph(0, 1, 68); // home icon
- u8x8.setFont(u8x8_font_open_iconic_weather_2x2);
- u8x8.drawGlyph(0, 2, 66 + (bri > 0 ? 3 : 0)); // sun/moon icon
-}
diff --git a/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h b/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
index f2864362..74894ac4 100644
--- a/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
+++ b/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
@@ -65,6 +65,7 @@ class AutoSaveUsermod : public Usermod {
month(localTime), day(localTime),
hour(localTime), minute(localTime), second(localTime));
savePreset(autoSavePreset, true, presetNameBuffer);
+ cacheInvalidate++; // force reload of presets
}
void inline displayOverlay() {
diff --git a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h
index 03d91e5a..aaa96764 100644
--- a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h
+++ b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h
@@ -114,6 +114,7 @@ class FourLineDisplayUsermod : public Usermod {
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
#ifndef FLD_SPI_DEFAULT
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
+ uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
DisplayType type = SSD1306; // display type
#else
int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST
@@ -155,6 +156,7 @@ class FourLineDisplayUsermod : public Usermod {
static const char _flip[];
static const char _sleepMode[];
static const char _clockMode[];
+ static const char _busClkFrequency[];
// If display does not work or looks corrupted check the
// constructor reference:
@@ -248,6 +250,7 @@ class FourLineDisplayUsermod : public Usermod {
initDone = true;
DEBUG_PRINTLN(F("Starting display."));
+ if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
u8x8->begin();
setFlipMode(flip);
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
@@ -683,6 +686,7 @@ class FourLineDisplayUsermod : public Usermod {
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode;
top[FPSTR(_clockMode)] = (bool) clockMode;
+ top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
DEBUG_PRINTLN(F("4 Line Display config saved."));
}
@@ -714,6 +718,7 @@ class FourLineDisplayUsermod : public Usermod {
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode;
+ ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
@@ -739,12 +744,13 @@ class FourLineDisplayUsermod : public Usermod {
setup();
needsRedraw |= true;
}
+ if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
setContrast(contrast);
setFlipMode(flip);
if (needsRedraw && !wakeDisplay()) redraw(true);
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
- return !(top["pin"][2]).isNull();
+ return !(top[_busClkFrequency]).isNull();
}
/*
@@ -757,10 +763,11 @@ class FourLineDisplayUsermod : public Usermod {
};
// strings to reduce flash memory usage (used more than twice)
-const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
-const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
-const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
-const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
-const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
-const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
-const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
+const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
+const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
+const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
+const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
+const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
+const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
+const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
+const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index bbca1c97..99b6b0cd 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -388,41 +388,12 @@ uint16_t WS2812FX::mode_rainbow_cycle(void) {
}
-/*
- * theater chase function
- */
-uint16_t WS2812FX::theater_chase(uint32_t color1, uint32_t color2, bool do_palette) {
- byte gap = 2 + ((255 - SEGMENT.intensity) >> 5);
- uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2;
- uint32_t it = now / cycleTime;
- if (it != SEGENV.step) //new color
- {
- SEGENV.aux0 = (SEGENV.aux0 +1) % gap;
- SEGENV.step = it;
- }
-
- for(uint16_t i = 0; i < SEGLEN; i++) {
- if((i % gap) == SEGENV.aux0) {
- if (do_palette)
- {
- setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
- } else {
- setPixelColor(i, color1);
- }
- } else {
- setPixelColor(i, color2);
- }
- }
- return FRAMETIME;
-}
-
-
/*
* Theatre-style crawling lights.
* Inspired by the Adafruit examples.
*/
uint16_t WS2812FX::mode_theater_chase(void) {
- return theater_chase(SEGCOLOR(0), SEGCOLOR(1), true);
+ return running(SEGCOLOR(0), SEGCOLOR(1), true);
}
@@ -431,7 +402,7 @@ uint16_t WS2812FX::mode_theater_chase(void) {
* Inspired by the Adafruit examples.
*/
uint16_t WS2812FX::mode_theater_chase_rainbow(void) {
- return theater_chase(color_wheel(SEGENV.step), SEGCOLOR(1), false);
+ return running(color_wheel(SEGENV.step), SEGCOLOR(1), true);
}
@@ -976,29 +947,27 @@ uint16_t WS2812FX::mode_chase_flash_random(void) {
/*
* Alternating pixels running function.
*/
-uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) {
- uint8_t pxw = 1 + (SEGMENT.intensity >> 5);
- uint32_t cycleTime = 35 + (255 - SEGMENT.speed);
+uint16_t WS2812FX::running(uint32_t color1, uint32_t color2, bool theatre) {
+ uint8_t width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window
+ uint32_t cycleTime = 50 + (255 - SEGMENT.speed);
uint32_t it = now / cycleTime;
- if (SEGMENT.speed == 0) it = 0;
-
+ bool usePalette = color1 == SEGCOLOR(0);
+
for(uint16_t i = 0; i < SEGLEN; i++) {
- if((i + SEGENV.aux0) % (pxw*2) < pxw) {
- if (color1 == SEGCOLOR(0))
- {
- setPixelColor(SEGLEN -i -1, color_from_palette(SEGLEN -i -1, true, PALETTE_SOLID_WRAP, 0));
- } else
- {
- setPixelColor(SEGLEN -i -1, color1);
- }
+ uint32_t col = color2;
+ if (usePalette) color1 = color_from_palette(i, true, PALETTE_SOLID_WRAP, 0);
+ if (theatre) {
+ if ((i % width) == SEGENV.aux0) col = color1;
} else {
- setPixelColor(SEGLEN -i -1, color2);
+ int8_t pos = (i % (width<<1));
+ if ((pos < SEGENV.aux0-width) || ((pos >= SEGENV.aux0) && (pos < SEGENV.aux0+width))) col = color1;
}
+ setPixelColor(i,col);
}
if (it != SEGENV.step )
{
- SEGENV.aux0 = (SEGENV.aux0 +1) % (pxw*2);
+ SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1));
SEGENV.step = it;
}
return FRAMETIME;
@@ -1247,44 +1216,19 @@ uint16_t WS2812FX::mode_loading(void) {
//American Police Light with all LEDs Red and Blue
-uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all)
+uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, uint16_t width)
{
- uint16_t counter = now * ((SEGMENT.speed >> 2) +1);
- uint16_t idexR = (counter * SEGLEN) >> 16;
- if (idexR >= SEGLEN) idexR = 0;
-
- uint16_t topindex = SEGLEN >> 1;
- uint16_t idexB = (idexR > topindex) ? idexR - topindex : idexR + topindex;
- if (SEGENV.call == 0) SEGENV.aux0 = idexR;
- if (idexB >= SEGLEN) idexB = 0; //otherwise overflow on odd number of LEDs
-
- if (all) { //different algo, ensuring immediate fill
- if (idexB > idexR) {
- fill(color2);
- for (uint16_t i = idexR; i < idexB; i++) setPixelColor(i, color1);
- } else {
- fill(color1);
- for (uint16_t i = idexB; i < idexR; i++) setPixelColor(i, color2);
- }
- } else { //regular dot-only mode
- 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);
- setPixelColor(idexB+i, color2);
- }
- if (SEGENV.aux0 != idexR) {
- uint8_t gap = (SEGENV.aux0 < idexR)? idexR - SEGENV.aux0:SEGLEN - SEGENV.aux0 + idexR;
- for (uint8_t i = 0; i <= gap ; i++) {
- if ((idexR - i) < 0) idexR = SEGLEN-1 + i;
- if ((idexB - i) < 0) idexB = SEGLEN-1 + i;
- setPixelColor(idexR-i, color1);
- setPixelColor(idexB-i, color2);
- }
- SEGENV.aux0 = idexR;
- }
- }
+ uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
+ uint32_t it = now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
+ uint16_t offset = it % SEGLEN;
+ if (!width) width = 1;
+ for (uint16_t i = 0; i < width; i++) {
+ uint16_t indexR = (offset + i) % SEGLEN;
+ uint16_t indexB = (offset + i + (SEGLEN>>1)) % SEGLEN;
+ setPixelColor(indexR, color1);
+ setPixelColor(indexB, color2);
+ }
return FRAMETIME;
}
@@ -1292,7 +1236,7 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all)
//American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::mode_police_all()
{
- return police_base(RED, BLUE, true);
+ return police_base(RED, BLUE, (SEGLEN>>1));
}
@@ -1300,15 +1244,15 @@ uint16_t WS2812FX::mode_police_all()
uint16_t WS2812FX::mode_police()
{
fill(SEGCOLOR(1));
-
- return police_base(RED, BLUE, false);
+ return police_base(RED, BLUE, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
}
//Police All with custom colors
uint16_t WS2812FX::mode_two_areas()
{
- return police_base(SEGCOLOR(0), SEGCOLOR(1), true);
+ fill(SEGCOLOR(2));
+ return police_base(SEGCOLOR(0), SEGCOLOR(1), ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
}
@@ -1318,7 +1262,7 @@ uint16_t WS2812FX::mode_two_dots()
fill(SEGCOLOR(2));
uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1);
- return police_base(SEGCOLOR(0), color2, false);
+ return police_base(SEGCOLOR(0), color2, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
}
@@ -1326,21 +1270,20 @@ uint16_t WS2812FX::mode_two_dots()
* Tricolor chase function
*/
uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) {
- uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2;
- uint32_t it = now / cycleTime;
- uint8_t width = (1 + SEGMENT.intensity/32) * 3; //value of 1-8 for each colour
- uint8_t index = it % width;
+ uint32_t cycleTime = 50 + ((255 - SEGMENT.speed)<<1);
+ uint32_t it = now / cycleTime; // iterator
+ uint8_t width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour
+ uint8_t index = it % (width*3);
- for(uint16_t i = 0; i < SEGLEN; i++, index++) {
- if(index > width-1) index = 0;
+ for (uint16_t i = 0; i < SEGLEN; i++, index++) {
+ if (index > (width*3)-1) index = 0;
uint32_t color = color1;
- if(index > width*2/3-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
- else if(index > width/3-1) color = color2;
+ if (index > (width<<1)-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
+ else if (index > width-1) color = color2;
setPixelColor(SEGLEN - i -1, color);
}
-
return FRAMETIME;
}
@@ -1548,7 +1491,7 @@ uint16_t WS2812FX::mode_random_chase(void)
return FRAMETIME;
}
-
+//7 bytes
typedef struct Oscillator {
int16_t pos;
int8_t size;
@@ -1780,7 +1723,7 @@ uint16_t WS2812FX::mode_fire_2012()
// Step 1. Cool down every cell a little
for (uint16_t i = 0; i < SEGLEN; i++) {
uint8_t temp = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2));
- heat[i] = (temp==0 && i> 3;
uint8_t bitNum = i & 0x07;
@@ -2170,10 +2110,14 @@ typedef struct Ripple {
uint16_t pos;
} ripple;
+#ifdef ESP8266
+ #define MAX_RIPPLES 56
+#else
+ #define MAX_RIPPLES 100
+#endif
uint16_t WS2812FX::ripple_base(bool rainbow)
{
- uint16_t maxRipples = 1 + (SEGLEN >> 2);
- if (maxRipples > 100) maxRipples = 100;
+ uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266
uint16_t dataSize = sizeof(ripple) * maxRipples;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@@ -2241,6 +2185,7 @@ uint16_t WS2812FX::ripple_base(bool rainbow)
}
return FRAMETIME;
}
+#undef MAX_RIPPLES
uint16_t WS2812FX::mode_ripple(void) {
return ripple_base(false);
@@ -2541,7 +2486,6 @@ uint16_t WS2812FX::mode_spots_fade()
//each needs 12 bytes
-//Spark type is used for popcorn and 1D fireworks
typedef struct Ball {
unsigned long lastBounceTime;
float impactVelocity;
@@ -2651,7 +2595,7 @@ uint16_t WS2812FX::mode_sinelon_dual(void) {
}
uint16_t WS2812FX::mode_sinelon_rainbow(void) {
- return sinelon_base(true, true);
+ return sinelon_base(false, true);
}
@@ -2670,7 +2614,7 @@ uint16_t WS2812FX::mode_glitter()
-//each needs 12 bytes
+//each needs 11 bytes
//Spark type is used for popcorn, 1D fireworks, and drip
typedef struct Spark {
float pos;
@@ -2685,7 +2629,7 @@ typedef struct Spark {
*/
uint16_t WS2812FX::mode_popcorn(void) {
//allocate segment data
- uint16_t maxNumPopcorn = 24;
+ uint16_t maxNumPopcorn = 22; // max 22 on 16 segment ESP8266
uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@@ -2744,7 +2688,7 @@ uint16_t WS2812FX::candle(bool multi)
if (multi)
{
//allocate segment data
- uint16_t dataSize = (SEGLEN -1) *3;
+ uint16_t dataSize = (SEGLEN -1) *3; // max length of segment on 16 segment ESP8266 is 75 pixels
if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed
}
@@ -2831,9 +2775,14 @@ uint16_t WS2812FX::mode_candle_multi()
/ based on the video: https://www.reddit.com/r/arduino/comments/c3sd46/i_made_this_fireworks_effect_for_my_led_strips/
/ Speed sets frequency of new starbursts, intensity is the intensity of the burst
*/
-#define STARBURST_MAX_FRAG 12
-
-//each needs 64 byte
+#ifdef ESP8266
+ #define STARBURST_MAX_FRAG 4
+ #define STARBURST_MAX_STARS 6
+#else
+ #define STARBURST_MAX_FRAG 10
+ #define STARBURST_MAX_STARS 11
+#endif
+//each needs 18+STARBURST_MAX_FRAG*4 bytes
typedef struct particle {
CRGB color;
uint32_t birth =0;
@@ -2844,8 +2793,7 @@ typedef struct particle {
} star;
uint16_t WS2812FX::mode_starburst(void) {
- uint8_t numStars = 1 + (SEGLEN >> 3);
- if (numStars > 15) numStars = 15;
+ uint8_t numStars = min(1 + (SEGLEN >> 3), STARBURST_MAX_STARS); // 11 * 58 * 32 = 19k (ESP32), 6 * 34 * 16 = 3.2k (ESP8266)
uint16_t dataSize = sizeof(star) * numStars;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@@ -2946,18 +2894,22 @@ uint16_t WS2812FX::mode_starburst(void) {
}
return FRAMETIME;
}
-
+#undef STARBURST_MAX_FRAG
+#undef STARBURST_MAX_STARS
/*
* Exploding fireworks effect
* adapted from: http://www.anirama.com/1000leds/1d-fireworks/
*/
-
+#ifdef ESP8266
+ #define MAX_SPARKS 20 // number of fragments (11 bytes per fragment)
+#else
+ #define MAX_SPARKS 58 // number of fragments
+#endif
uint16_t WS2812FX::mode_exploding_fireworks(void)
{
//allocate segment data
- uint16_t numSparks = 2 + (SEGLEN >> 1);
- if (numSparks > 80) numSparks = 80;
+ uint16_t numSparks = min(2 + (SEGLEN >> 1), MAX_SPARKS); // max 58 for 32 segment ESP32, 20 for 16 segment ESP8266
uint16_t dataSize = sizeof(spark) * numSparks;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@@ -3052,7 +3004,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
SEGENV.aux0--;
if (SEGENV.aux0 < 4) {
SEGENV.aux0 = 0; //back to flare
- SEGENV.step = (SEGMENT.intensity > random8()); //decide firing side
+ SEGENV.step = actuallyReverse ^ (SEGMENT.intensity > random8()); //decide firing side
}
}
@@ -3060,6 +3012,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
return FRAMETIME;
}
+#undef MAX_SPARKS
/*
@@ -3069,7 +3022,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
uint16_t WS2812FX::mode_drip(void)
{
//allocate segment data
- uint16_t numDrops = 4;
+ uint8_t numDrops = 4;
uint16_t dataSize = sizeof(spark) * numDrops;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@@ -3077,7 +3030,7 @@ uint16_t WS2812FX::mode_drip(void)
Spark* drops = reinterpret_cast(SEGENV.data);
- numDrops = 1 + (SEGMENT.intensity >> 6);
+ numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
float gravity = -0.0005 - (SEGMENT.speed/50000.0);
gravity *= SEGLEN;
@@ -3107,13 +3060,13 @@ uint16_t WS2812FX::mode_drip(void)
if (drops[j].pos > 0) { // fall until end of segment
drops[j].pos += drops[j].vel;
if (drops[j].pos < 0) drops[j].pos = 0;
- drops[j].vel += gravity;
+ drops[j].vel += gravity; // gravity is negative
for (uint16_t i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets
uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally
setPixelColor(pos,color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling
}
-
+
if (drops[j].colIndex > 2) { // during bounce, some water is on the floor
setPixelColor(0,color_blend(SEGCOLOR(0),BLACK,drops[j].col));
}
@@ -3142,6 +3095,7 @@ uint16_t WS2812FX::mode_drip(void)
* Tetris or Stacking (falling bricks) Effect
* by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home)
*/
+//12 bytes
typedef struct Tetris {
float pos;
float speed;
@@ -3163,8 +3117,8 @@ uint16_t WS2812FX::mode_tetrix(void) {
}
if (SEGENV.step == 0) { //init
- drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>3)+1 : random8(6,40)); // set speed
- drop->pos = SEGLEN-1; // start at end of segment
+ drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>2)+1 : random8(6,64)); // set speed
+ drop->pos = SEGLEN; // start at end of segment (no need to subtract 1)
drop->col = color_from_palette(random8(0,15)<<4,false,false,0); // limit color choices so there is enough HUE gap
SEGENV.step = 1; // drop state (0 init, 1 forming, 2 falling)
SEGENV.aux0 = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : random8(1,5)) * (1+(SEGLEN>>6)); // size of brick
@@ -3196,13 +3150,17 @@ uint16_t WS2812FX::mode_tetrix(void) {
/ adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino
*/
uint16_t WS2812FX::mode_plasma(void) {
- uint8_t thisPhase = beatsin8(6,-64,64); // Setting phase change for a couple of waves.
- uint8_t thatPhase = beatsin8(7,-64,64);
+ // initialize phases on start
+ if (SEGENV.call == 0) {
+ SEGENV.aux0 = random8(0,2); // add a bit of randomness
+ }
+ uint8_t thisPhase = beatsin8(6+SEGENV.aux0,-64,64);
+ uint8_t thatPhase = beatsin8(7+SEGENV.aux0,-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 thisBright = qsub8(colorIndex, beatsin8(6,0, (255 - SEGMENT.intensity)|0x01 ));
+ uint8_t colorIndex = cubicwave8((i*(2+ 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(7,0, (128 - (SEGMENT.intensity>>1))));
CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND);
setPixelColor(i, color.red, color.green, color.blue);
}
@@ -3531,7 +3489,7 @@ uint16_t WS2812FX::mode_noisepal(void) { // S
uint16_t scale = 15 + (SEGMENT.intensity >> 2); //default was 30
//#define scale 30
- uint16_t dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes
+ uint16_t dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes (2 * 16 * 3 = 96 bytes)
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
CRGBPalette16* palettes = reinterpret_cast(SEGENV.data);
@@ -3644,7 +3602,7 @@ uint16_t WS2812FX::mode_chunchun(void)
return FRAMETIME;
}
-
+//13 bytes
typedef struct Spotlight {
float speed;
uint8_t colorIdx;
@@ -3661,6 +3619,11 @@ typedef struct Spotlight {
#define SPOT_TYPE_3X_DOT 4
#define SPOT_TYPE_4X_DOT 5
#define SPOT_TYPES_COUNT 6
+#ifdef ESP8266
+ #define SPOT_MAX_COUNT 17 //Number of simultaneous waves
+#else
+ #define SPOT_MAX_COUNT 49 //Number of simultaneous waves
+#endif
/*
* Spotlights moving back and forth that cast dancing shadows.
@@ -3671,7 +3634,7 @@ typedef struct Spotlight {
*/
uint16_t WS2812FX::mode_dancing_shadows(void)
{
- uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, 50);
+ uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266
bool initialize = SEGENV.aux0 != numSpotlights;
SEGENV.aux0 = numSpotlights;
@@ -3810,7 +3773,7 @@ uint16_t WS2812FX::mode_washing_machine(void) {
Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
*/
uint16_t WS2812FX::mode_blends(void) {
- uint16_t dataSize = sizeof(uint32_t) * SEGLEN;
+ uint16_t dataSize = sizeof(uint32_t) * SEGLEN; // max segment length of 56 pixels on 16 segment ESP8266
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
uint32_t* pixels = reinterpret_cast(SEGENV.data);
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
@@ -3825,6 +3788,11 @@ uint16_t WS2812FX::mode_blends(void) {
return FRAMETIME;
}
+/*
+ TV Simulator
+ Modified and adapted to WLED by Def3nder, based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch
+*/
+//43 bytes
typedef struct TvSim {
uint32_t totalTime = 0;
uint32_t fadeTime = 0;
@@ -3845,11 +3813,6 @@ typedef struct TvSim {
uint16_t pb = 0;
} tvSim;
-
-/*
- TV Simulator
- Modified and adapted to WLED by Def3nder, based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch
-*/
uint16_t WS2812FX::mode_tv_simulator(void) {
uint16_t nr, ng, nb, r, g, b, i, hue;
uint8_t sat, bri, j;
@@ -3957,10 +3920,15 @@ uint16_t WS2812FX::mode_tv_simulator(void) {
*/
//CONFIG
-#define W_MAX_COUNT 20 //Number of simultaneous waves
+#ifdef ESP8266
+ #define W_MAX_COUNT 9 //Number of simultaneous waves
+#else
+ #define W_MAX_COUNT 20 //Number of simultaneous waves
+#endif
#define W_MAX_SPEED 6 //Higher number, higher speed
#define W_WIDTH_FACTOR 6 //Higher number, smaller waves
+//24 bytes
class AuroraWave {
private:
uint16_t ttl;
@@ -4055,10 +4023,10 @@ uint16_t WS2812FX::mode_aurora(void) {
if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
//Intensity slider changed or first call
- SEGENV.aux1 = ((float)SEGMENT.intensity / 255) * W_MAX_COUNT;
+ SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT);
SEGENV.aux0 = SEGMENT.intensity;
- if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) {
+ if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 26 on 32 segment ESP32, 9 on 16 segment ESP8266
return mode_static(); //allocation failed
}
diff --git a/wled00/FX.h b/wled00/FX.h
index ba4ee858..baf3ec5c 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -24,8 +24,6 @@
Modified for WLED
*/
-#include "wled.h"
-
#ifndef WS2812FX_h
#define WS2812FX_h
@@ -55,17 +53,17 @@
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266
- #define MAX_NUM_SEGMENTS 12
+ #define MAX_NUM_SEGMENTS 16
/* How many color transitions can run at once */
#define MAX_NUM_TRANSITIONS 8
/* How much data bytes all segments combined may allocate */
- #define MAX_SEGMENT_DATA 2048
+ #define MAX_SEGMENT_DATA 3584
#else
-#ifndef MAX_NUM_SEGMENTS
- #define MAX_NUM_SEGMENTS 16
-#endif
- #define MAX_NUM_TRANSITIONS 16
- #define MAX_SEGMENT_DATA 8192
+ #ifndef MAX_NUM_SEGMENTS
+ #define MAX_NUM_SEGMENTS 32
+ #endif
+ #define MAX_NUM_TRANSITIONS 24
+ #define MAX_SEGMENT_DATA 20480
#endif
#define LED_SKIP_AMOUNT 1
@@ -257,6 +255,7 @@ class WS2812FX {
uint8_t grouping, spacing;
uint8_t opacity;
uint32_t colors[NUM_COLORS];
+ char *name;
bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;
if (c == colors[slot]) return false;
@@ -296,19 +295,19 @@ class WS2812FX {
{
return ((options >> n) & 0x01);
}
- bool isSelected()
+ inline bool isSelected()
{
return getOption(0);
}
- bool isActive()
+ inline bool isActive()
{
return stop > start;
}
- uint16_t length()
+ inline uint16_t length()
{
return stop - start;
}
- uint16_t groupLength()
+ inline uint16_t groupLength()
{
return grouping + spacing;
}
@@ -345,17 +344,23 @@ class WS2812FX {
// segment runtime parameters
typedef struct Segment_runtime { // 28 bytes
- unsigned long next_time;
- uint32_t step;
- uint32_t call;
- uint16_t aux0;
- uint16_t aux1;
+ unsigned long next_time; // millis() of next update
+ uint32_t step; // custom "step" var
+ uint32_t call; // call counter
+ uint16_t aux0; // custom var
+ uint16_t aux1; // custom var
byte* data = nullptr;
bool allocateData(uint16_t len){
if (data && _dataLen == len) return true; //already allocated
deallocateData();
if (WS2812FX::instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory
- data = new (std::nothrow) byte[len];
+ // if possible use SPI RAM on ESP32
+ #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
+ if (psramFound())
+ data = (byte*) ps_malloc(len);
+ else
+ #endif
+ data = (byte*) malloc(len);
if (!data) return false; //allocation failed
WS2812FX::instance->_usedSegmentData += len;
_dataLen = len;
@@ -363,7 +368,7 @@ class WS2812FX {
return true;
}
void deallocateData(){
- delete[] data;
+ free(data);
data = nullptr;
WS2812FX::instance->_usedSegmentData -= _dataLen;
_dataLen = 0;
@@ -389,7 +394,7 @@ class WS2812FX {
* the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function.
*/
- void reset() { _requiresReset = true; }
+ inline void reset() { _requiresReset = true; }
private:
uint16_t _dataLen = 0;
bool _requiresReset = false;
@@ -605,7 +610,7 @@ class WS2812FX {
}
void
- finalizeInit(uint16_t countPixels),
+ finalizeInit(void),
service(void),
blur(uint8_t),
fill(uint32_t),
@@ -622,11 +627,12 @@ class WS2812FX {
trigger(void),
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0),
resetSegments(),
+ populateDefaultSegments(),
setPixelColor(uint16_t n, uint32_t c),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
show(void),
- setColorOrder(uint8_t co),
- setPixelSegment(uint8_t n);
+ setPixelSegment(uint8_t n),
+ deserializeMap(uint8_t n=0);
bool
isRgbw = false,
@@ -644,8 +650,6 @@ 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),
@@ -654,24 +658,17 @@ class WS2812FX {
getMaxSegments(void),
//getFirstSelectedSegment(void),
getMainSegmentId(void),
- getColorOrder(void),
gamma8(uint8_t),
gamma8_cal(uint8_t, float),
sin_gap(uint16_t),
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,
-// setStripLen(uint8_t strip, uint16_t len),
-// getStripLen(uint8_t strip=0),
triwave16(uint16_t),
getFps();
@@ -849,7 +846,6 @@ class WS2812FX {
color_wipe(bool, bool),
dynamic(bool),
scan(bool),
- theater_chase(uint32_t, uint32_t, bool),
running_base(bool,bool),
larson_scanner(bool),
sinelon_base(bool,bool),
@@ -857,8 +853,8 @@ class WS2812FX {
chase(uint32_t, uint32_t, uint32_t, bool),
gradient_base(bool),
ripple_base(bool),
- police_base(uint32_t, uint32_t, bool),
- running(uint32_t, uint32_t),
+ police_base(uint32_t, uint32_t, uint16_t),
+ running(uint32_t, uint32_t, bool theatre=false),
tricolor_chase(uint32_t, uint32_t),
twinklefox_base(bool),
spots_base(uint16_t),
@@ -869,8 +865,7 @@ class WS2812FX {
void
blendPixelColor(uint16_t n, uint32_t color, uint8_t blend),
- startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot),
- deserializeMap(void);
+ startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot);
uint16_t* customMappingTable = nullptr;
uint16_t customMappingSize = 0;
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 43cb4846..f240eea5 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -23,7 +23,7 @@
Modified heavily for WLED
*/
-
+#include "wled.h"
#include "FX.h"
#include "palettes.h"
@@ -40,7 +40,7 @@
another example. Switches direction every 5 LEDs.
{"map":[
0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14,
- 19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25]
+ 19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25]}
*/
//factory defaults LED setup
@@ -49,7 +49,7 @@
//#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
#ifndef PIXEL_COUNTS
- #define PIXEL_COUNTS DEFAULT_LED_COUNT
+ #define PIXEL_COUNTS 30
#endif
#ifndef DATA_PINS
@@ -65,25 +65,22 @@
#endif
//do not call this method from system context (network callback)
-void WS2812FX::finalizeInit(uint16_t countPixels)
+void WS2812FX::finalizeInit(void)
{
RESET_RUNTIME;
- _length = countPixels;
+ isRgbw = isOffRefreshRequred = false;
- //if busses failed to load, add default (FS issue...)
+ //if busses failed to load, add default (fresh install, FS issue, ...)
if (busses.getNumBusses() == 0) {
const uint8_t defDataPins[] = {DATA_PINS};
const uint16_t defCounts[] = {PIXEL_COUNTS};
- const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
- const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
+ const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0])); // min 1
+ const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); // min 1
uint16_t prevLen = 0;
- for (uint8_t i = 0; i < defNumBusses; i++) {
+ for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES; i++) {
uint8_t defPin[] = {defDataPins[i]};
uint16_t start = prevLen;
- uint16_t count = _length;
- if (defNumBusses > 1 && defNumCounts) {
- count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
- }
+ uint16_t count = (i < defNumCounts) ? defCounts[i] : defCounts[i>0?i-1:0];
prevLen += count;
BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, COL_ORDER_GRB);
busses.add(defCfg);
@@ -92,65 +89,33 @@ void WS2812FX::finalizeInit(uint16_t countPixels)
deserializeMap();
- uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
- uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
+ _length = 0;
+ for (uint8_t i=0; igetLength() > MAX_LEDS) break;
+ //RGBW mode is enabled if at least one of the strips is RGBW
+ isRgbw |= bus->isRgbw();
+ //refresh is required to remain off if at least one of the strips requires the refresh.
+ isOffRefreshRequred |= BusManager::isOffRefreshRequred(bus->getType());
+ _length += bus->getLength();
+ }
+ ledCount = _length;
+
+ // We will create default segments im populateDefaultSegments()
setBrightness(_brightness);
- //TODO make sure segments are only refreshed when bus config actually changed (new settings page)
- uint8_t s = 0;
+ #ifdef ESP8266
for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
Bus* b = busses.getBus(i);
-
- if (autoSegments) { //make one segment per bus
- segStarts[s] = b->getStart();
- segStops[s] = segStarts[s] + b->getLength();
-
- //check for overlap with previous segments
- for (uint8_t j = 0; j < s; j++) {
- if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) {
- //segments overlap, merge
- segStarts[j] = min(segStarts[s],segStarts[j]);
- segStops [j] = max(segStops [s],segStops [j]); segStops[s] = 0;
- s--;
- }
- }
- s++;
- }
-
- #ifdef ESP8266
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
- }
-
- if (autoSegments) {
- for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
- _segments[i].start = segStarts[i];
- _segments[i].stop = segStops [i];
- }
- } else {
- //expand the main seg to the entire length, but only if there are no other segments
- uint8_t mainSeg = getMainSegmentId();
- bool isMultipleSegs = false;
- for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
- {
- if (i != mainSeg && _segments[i].isActive()) isMultipleSegs = true;
- }
- if (!isMultipleSegs) {
- _segments[mainSeg].start = 0; _segments[mainSeg].stop = _length;
- } else {
- //there are multiple segments, leave them, but prune length to total
- for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
- {
- if (_segments[i].start > _length) _segments[i].stop = 0;
- if (_segments[i].stop > _length) _segments[i].stop = _length;
- }
- }
}
+ #endif
}
void WS2812FX::service() {
@@ -218,14 +183,13 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
int16_t realIndex = iGroup;
if (IS_REVERSE) {
if (IS_MIRROR) {
- realIndex = (SEGMENT.length() -1) / 2 - iGroup; //only need to index half the pixels
+ realIndex = (SEGMENT.length() - 1) / 2 - iGroup; //only need to index half the pixels
} else {
- realIndex = SEGMENT.length() - iGroup - 1;
+ realIndex = (SEGMENT.length() - 1) - iGroup;
}
}
realIndex += SEGMENT.start;
-
return realIndex;
}
@@ -246,7 +210,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
}
if (SEGLEN) {//from segment
-
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
if (_bri_t < 255) {
r = scale8(r, _bri_t);
@@ -256,12 +219,12 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
}
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
- bool reversed = IS_REVERSE;
+ /* Set all the pixels in the group */
uint16_t realIndex = realPixelIndex(i);
uint16_t len = SEGMENT.length();
for (uint16_t j = 0; j < SEGMENT.grouping; j++) {
- int indexSet = realIndex + (reversed ? -j : j);
+ uint16_t indexSet = realIndex + (IS_REVERSE ? -j : j);
if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) {
if (IS_MIRROR) { //set the corresponding mirrored pixel
uint16_t indexMir = SEGMENT.stop - indexSet + SEGMENT.start - 1;
@@ -273,8 +236,8 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
busses.setPixelColor(indexMir, col);
}
/* offset/phase */
- indexSet += SEGMENT.offset;
- if (indexSet >= SEGMENT.stop) indexSet -= len;
+ indexSet += SEGMENT.offset;
+ if (indexSet >= SEGMENT.stop) indexSet -= len;
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
busses.setPixelColor(indexSet, col);
@@ -282,7 +245,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
}
} else { //live data, etc.
if (i < customMappingSize) i = customMappingTable[i];
-
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
busses.setPixelColor(i, col);
}
@@ -563,7 +525,9 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
}
if (i < customMappingSize) i = customMappingTable[i];
+ if (i >= _length) return 0;
+ // TODO: may need to add IS_REVERSE and IS_MIRROR logic
return busses.getPixelColor(i);
}
@@ -584,14 +548,14 @@ uint32_t WS2812FX::getLastShow(void) {
return _lastShow;
}
-//TODO these need to be on a per-strip basis
-uint8_t WS2812FX::getColorOrder(void) {
- return COL_ORDER_GRB;
-}
-
-void WS2812FX::setColorOrder(uint8_t co) {
- //bus->SetColorOrder(co);
-}
+// there is no longer any need for these two
+//uint8_t WS2812FX::getColorOrder(void) {
+// return COL_ORDER_GRB;
+//}
+//
+//void WS2812FX::setColorOrder(uint8_t co) {
+// //bus->SetColorOrder(co);
+//}
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) {
if (n >= MAX_NUM_SEGMENTS) return;
@@ -603,7 +567,11 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
if (seg.stop) setRange(seg.start, seg.stop -1, 0); //turn old segment range off
if (i2 <= i1) //disable segment
{
- seg.stop = 0;
+ seg.stop = 0;
+ if (seg.name) {
+ delete[] seg.name;
+ seg.name = nullptr;
+ }
if (n == mainSegment) //if main segment is deleted, set first active as main segment
{
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
@@ -628,6 +596,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
}
void WS2812FX::resetSegments() {
+ for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete _segments[i].name;
mainSegment = 0;
memset(_segments, 0, sizeof(_segments));
//memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
@@ -656,6 +625,38 @@ void WS2812FX::resetSegments() {
_segment_runtimes[0].reset();
}
+void WS2812FX::populateDefaultSegments() {
+ uint16_t length = 0;
+ if (autoSegments) {
+ for (uint8_t i=0; igetStart();
+ length += bus->getLength();
+ _segments[i].stop = _segments[i].start + bus->getLength();
+ _segments[i].mode = DEFAULT_MODE;
+ _segments[i].colors[0] = DEFAULT_COLOR;
+ _segments[i].speed = DEFAULT_SPEED;
+ _segments[i].intensity = DEFAULT_INTENSITY;
+ _segments[i].grouping = 1;
+ _segments[i].setOption(SEG_OPTION_SELECTED, 1);
+ _segments[i].setOption(SEG_OPTION_ON, 1);
+ _segments[i].opacity = 255;
+ }
+ } else {
+ _segments[0].start = 0;
+ _segments[0].stop = _length;
+ _segments[0].mode = DEFAULT_MODE;
+ _segments[0].colors[0] = DEFAULT_COLOR;
+ _segments[0].speed = DEFAULT_SPEED;
+ _segments[0].intensity = DEFAULT_INTENSITY;
+ _segments[0].grouping = 1;
+ _segments[0].setOption(SEG_OPTION_SELECTED, 1);
+ _segments[0].setOption(SEG_OPTION_ON, 1);
+ _segments[0].opacity = 255;
+ }
+}
+
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
void WS2812FX::setPixelSegment(uint8_t n)
{
@@ -1033,18 +1034,33 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8
//load custom mapping table from JSON file
-void WS2812FX::deserializeMap(void) {
- if (!WLED_FS.exists("/ledmap.json")) return;
+void WS2812FX::deserializeMap(uint8_t n) {
+ String fileName = String(F("/ledmap"));
+ if (n) fileName += String(n);
+ fileName += String(F(".json"));
+ bool isFile = WLED_FS.exists(fileName);
+
+ if (!isFile) {
+ // erase custom mapping if selecting nonexistent ledmap.json (n==0)
+ if (!n && customMappingTable != nullptr) {
+ customMappingSize = 0;
+ delete[] customMappingTable;
+ customMappingTable = nullptr;
+ }
+ return;
+ }
+
DynamicJsonDocument doc(JSON_BUFFER_SIZE); // full sized buffer for larger maps
+ DEBUG_PRINT(F("Reading LED map from "));
+ DEBUG_PRINTLN(fileName);
- DEBUG_PRINTLN(F("Reading LED map from /ledmap.json..."));
-
- if (!readObjectFromFile("/ledmap.json", nullptr, &doc)) return; //if file does not exist just exit
+ if (!readObjectFromFile(fileName.c_str(), nullptr, &doc)) return; //if file does not exist just exit
+ // erase old custom ledmap
if (customMappingTable != nullptr) {
+ customMappingSize = 0;
delete[] customMappingTable;
customMappingTable = nullptr;
- customMappingSize = 0;
}
JsonArray map = doc[F("map")];
diff --git a/wled00/blynk.cpp b/wled00/blynk.cpp
index ce390448..929c3d43 100644
--- a/wled00/blynk.cpp
+++ b/wled00/blynk.cpp
@@ -1,5 +1,7 @@
#include "wled.h"
+#ifndef WLED_DISABLE_BLYNK
#include "src/dependencies/blynk/Blynk/BlynkHandlers.h"
+#endif
/*
* Remote light control with the free Blynk app
diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h
index 55d929e0..0bec1e3f 100644
--- a/wled00/bus_manager.h
+++ b/wled00/bus_manager.h
@@ -10,18 +10,24 @@
#include "bus_wrapper.h"
#include
+#define GET_BIT(var,bit) (((var)>>(bit))&0x01)
+#define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit)))
+#define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit))))
+
//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;
+ uint16_t count;
+ uint16_t start;
+ uint8_t colorOrder;
+ bool reversed;
uint8_t skipAmount;
+ bool rgbwOverride;
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, uint8_t skip=0) {
- type = busType; count = len; start = pstart;
- colorOrder = pcolorOrder; reversed = rev; skipAmount = skip;
+ BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0) {
+ rgbwOverride = (bool) GET_BIT(busType,7);
+ type = busType & 0x7F; // bit 7 may be/is hacked to include RGBW info (1=RGBW, 0=RGB)
+ count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip;
uint8_t nPins = 1;
if (type > 47) nPins = 2;
else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type);
@@ -74,7 +80,7 @@ class Bus {
}
virtual uint16_t getLength() {
- return 1;
+ return 1; // is this ok? shouldn't it be 0 in virtual function?
}
virtual void setColorOrder() {}
@@ -130,12 +136,12 @@ class BusDigital : public Bus {
reversed = bc.reversed;
_skip = bc.skipAmount; //sacrificial pixels
_len = bc.count + _skip;
- _iType = PolyBus::getI(bc.type, _pins, nr);
+ _rgbw = bc.rgbwOverride || Bus::isRgbw(bc.type); // RGBW override in bit 7
+ _iType = PolyBus::getI(bc.type, _pins, nr, _rgbw);
if (_iType == I_NONE) return;
_busPtr = PolyBus::create(_iType, _pins, _len, nr);
_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);
};
inline void show() {
@@ -189,7 +195,7 @@ class BusDigital : public Bus {
}
inline bool isRgbw() {
- return (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814);
+ return (_rgbw || _type == TYPE_SK6812_RGBW || _type == TYPE_TM1814);
}
inline uint8_t skippedLeds() {
@@ -201,7 +207,6 @@ class BusDigital : public Bus {
}
void cleanup() {
- //Serial.println("Digital Cleanup");
PolyBus::cleanup(_busPtr, _iType);
_iType = I_NONE;
_valid = false;
@@ -220,6 +225,7 @@ class BusDigital : public Bus {
uint8_t _iType = I_NONE;
uint16_t _len = 0;
uint8_t _skip = 0;
+ bool _rgbw = false;
void * _busPtr = nullptr;
};
@@ -227,6 +233,7 @@ class BusDigital : public Bus {
class BusPwm : public Bus {
public:
BusPwm(BusConfig &bc) : Bus(bc.type, bc.start) {
+ _valid = false;
if (!IS_PWM(bc.type)) return;
uint8_t numPins = NUM_PWM_PINS(bc.type);
@@ -280,10 +287,12 @@ class BusPwm : public Bus {
//does no index check
uint32_t getPixelColor(uint16_t pix) {
+ if (!_valid) return 0;
return ((_data[3] << 24) | (_data[0] << 16) | (_data[1] << 8) | (_data[2]));
}
void show() {
+ if (!_valid) return;
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
uint8_t scaled = (_data[i] * _bri) / 255;
@@ -302,7 +311,9 @@ class BusPwm : public Bus {
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];
+ for (uint8_t i = 0; i < numPins; i++) {
+ pinArray[i] = _pins[i];
+ }
return numPins;
}
@@ -328,13 +339,13 @@ class BusPwm : public Bus {
void deallocatePins() {
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
+ pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
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], PinOwner::BusPwm);
}
#ifdef ARDUINO_ARCH_ESP32
pinManager.deallocateLedc(_ledcStart, numPins);
@@ -342,6 +353,86 @@ class BusPwm : public Bus {
}
};
+
+class BusVirtual : public Bus {
+ public:
+ BusVirtual(BusConfig &bc) : Bus(bc.type, bc.start) {
+ _valid = false;
+ _data = (byte *)malloc(bc.count * (_rgbw ? 4 : 3));
+ if (_data == nullptr) return;
+ memset(_data, 0, bc.count * (_rgbw ? 4 : 3));
+ _len = bc.count;
+ //_rgbw = bc.rgbwOverride; // RGBW override in bit 7 or can have a special type
+ _rgbw = _type == TYPE_VIRTUAL_RGBW;
+ _colorOrder = bc.colorOrder;
+ _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
+ _broadcastLock = false;
+ _valid = true;
+ };
+
+ void setPixelColor(uint16_t pix, uint32_t c) {
+ if (!_valid || pix >= _len) return;
+ _data[pix] = 0xFF & (c >> 16);
+ _data[pix+1] = 0xFF & (c >> 8);
+ _data[pix+2] = 0xFF & (c );
+ if (_rgbw) _data[pix+3] = 0xFF & (c >> 24);
+ }
+
+ uint32_t getPixelColor(uint16_t pix) {
+ if (!_valid || pix >= _len) return 0;
+ return ((_rgbw?(_data[pix+3] << 24):0) | (_data[pix] << 16) | (_data[pix+1] << 8) | (_data[pix+2]));
+ }
+
+ void show() {
+ if (!_valid || _broadcastLock) return;
+ _broadcastLock = true;
+ realtimeBoroadcast(_client, _len, _data, _rgbw);
+ _broadcastLock = false;
+ }
+
+ inline bool canShow() {
+ return !_broadcastLock;
+ }
+
+ inline void setBrightness(uint8_t b) {
+ // not sure if this is correctly implemented
+ for (uint16_t pix=0; pix<_len; pix++) {
+ _data[pix ] = scale8(_data[pix ], b);
+ _data[pix+1] = scale8(_data[pix+1], b);
+ _data[pix+2] = scale8(_data[pix+2], b);
+ if (_rgbw) _data[pix+3] = scale8(_data[pix+3], b);
+ }
+ }
+
+ inline bool isRgbw() {
+ return _rgbw;
+ }
+
+ inline uint16_t getLength() {
+ return _len;
+ }
+
+ void cleanup() {
+ _type = I_NONE;
+ _valid = false;
+ if (_data != nullptr) free(_data);
+ _data = nullptr;
+ }
+
+ ~BusVirtual() {
+ cleanup();
+ }
+
+ private:
+ IPAddress _client;
+ uint16_t _len = 0;
+ uint8_t _colorOrder;
+ bool _rgbw;
+ bool _broadcastLock;
+ byte* _data;
+};
+
+
class BusManager {
public:
BusManager() {
@@ -352,7 +443,9 @@ class BusManager {
static uint32_t memUsage(BusConfig &bc) {
uint8_t type = bc.type;
uint16_t len = bc.count;
- if (type < 32) {
+ if (type < 4) {
+ return len * (type+1);
+ } else if (type < 32) {
#ifdef ESP8266
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
if (type > 29) return len*20; //RGBW
@@ -373,7 +466,9 @@ class BusManager {
int add(BusConfig &bc) {
if (numBusses >= WLED_MAX_BUSSES) return -1;
- if (IS_DIGITAL(bc.type)) {
+ if (bc.type>1 && bc.type<4) {
+ busses[numBusses] = new BusVirtual(bc);
+ } else if (IS_DIGITAL(bc.type)) {
busses[numBusses] = new BusDigital(bc, numBusses);
} else {
busses[numBusses] = new BusPwm(bc);
@@ -444,6 +539,7 @@ class BusManager {
return len;
}
+ // a workaround
static inline bool isRgbw(uint8_t type) {
return Bus::isRgbw(type);
}
diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h
index 362c0585..1fb5dd30 100644
--- a/wled00/bus_wrapper.h
+++ b/wled00/bus_wrapper.h
@@ -383,11 +383,6 @@ class PolyBus {
uint8_t w = c >> 24;
RgbwColor col;
- //TODO make color order override possible on a per-strip basis
- #ifdef COLOR_ORDER_OVERRIDE
- if (pix >= COO_MIN && pix < COO_MAX) co = COO_ORDER;
- #endif
-
//reorder channels to selected order
switch (co)
{
@@ -559,10 +554,6 @@ class PolyBus {
case I_SS_P98_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
}
- #ifdef COLOR_ORDER_OVERRIDE
- if (pix >= COO_MIN && pix < COO_MAX) co = COO_ORDER;
- #endif
-
switch (co)
{
// W G R B
@@ -632,14 +623,17 @@ class PolyBus {
}
//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) {
+ static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0, bool isRGBW = false) {
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
+ // temporary hack to limit use of hardware SPI to a single SPI peripheral: only allow ESP32 hardware serial on segment 0
+ if (!num) isHSPI = true;
+ //if (num==0 && pins[0] == P_32_VS_MOSI && pins[1] == P_32_VS_CLK) isHSPI = true; // no nultiplexing, up to 80MHz clock
+ //if (num==1 && pins[0] == P_32_HS_MOSI && pins[1] == P_32_HS_CLK) isHSPI = true;
#endif
uint8_t t = I_NONE;
switch (busType) {
@@ -658,9 +652,8 @@ class PolyBus {
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;
+ return (isRGBW ? I_8266_U0_NEO_4 : I_8266_U0_NEO_3) + offset;
case TYPE_WS2811_400KHZ:
return I_8266_U0_400_3 + offset;
case TYPE_TM1814:
@@ -678,9 +671,10 @@ class PolyBus {
switch (busType) {
case TYPE_WS2812_RGB:
case TYPE_WS2812_WWA:
- return I_32_RN_NEO_3 + offset;
+// return I_32_RN_NEO_3 + offset;
case TYPE_SK6812_RGBW:
- return I_32_RN_NEO_4 + offset;
+// return I_32_RN_NEO_4 + offset;
+ return (isRGBW ? I_32_RN_NEO_4 : I_32_RN_NEO_3) + offset;
case TYPE_WS2811_400KHZ:
return I_32_RN_400_3 + offset;
case TYPE_TM1814:
diff --git a/wled00/button.cpp b/wled00/button.cpp
index ba6a0c97..bd409060 100644
--- a/wled00/button.cpp
+++ b/wled00/button.cpp
@@ -10,10 +10,11 @@ static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize fl
void shortPressAction(uint8_t b)
{
- if (!macroButton[b])
- {
- toggleOnOff();
- colorUpdated(CALL_MODE_BUTTON);
+ if (!macroButton[b]) {
+ switch (b) {
+ case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break;
+ default: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break;
+ }
} else {
applyPreset(macroButton[b], CALL_MODE_BUTTON);
}
@@ -26,6 +27,44 @@ void shortPressAction(uint8_t b)
}
}
+void longPressAction(uint8_t b)
+{
+ if (!macroLongPress[b]) {
+ switch (b) {
+ case 0: _setRandomColor(false,true); break;
+ default: bri += 8; colorUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
+ }
+ } else {
+ applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
+ }
+
+ // publish MQTT message
+ if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
+ char subuf[64];
+ sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
+ mqtt->publish(subuf, 0, false, "long");
+ }
+}
+
+void doublePressAction(uint8_t b)
+{
+ if (!macroDoublePress[b]) {
+ switch (b) {
+ case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break;
+ default: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
+ }
+ } else {
+ applyPreset(macroDoublePress[b], CALL_MODE_BUTTON);
+ }
+
+ // publish MQTT message
+ if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
+ char subuf[64];
+ sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
+ mqtt->publish(subuf, 0, false, "double");
+ }
+}
+
bool isButtonPressed(uint8_t i)
{
if (btnPin[i]<0) return false;
@@ -186,61 +225,43 @@ void handleButton()
}
//momentary button logic
- if (isButtonPressed(b)) //pressed
- {
+ if (isButtonPressed(b)) { //pressed
+
if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis();
buttonPressedBefore[b] = true;
- if (millis() - buttonPressedTime[b] > 600) //long press
- {
- if (!buttonLongPressed[b])
- {
- if (macroLongPress[b]) {applyPreset(macroLongPress[b], CALL_MODE_BUTTON);}
- else _setRandomColor(false,true);
-
- // publish MQTT message
- if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
- char subuf[64];
- sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
- mqtt->publish(subuf, 0, false, "long");
- }
-
- buttonLongPressed[b] = true;
+ if (millis() - buttonPressedTime[b] > 600) { //long press
+ if (!buttonLongPressed[b]) longPressAction(b);
+ else if (b) { // repeatable action (~3 times per s) on button > 0
+ longPressAction(b);
+ buttonPressedTime[b] = millis() - 300; // 300ms
}
+ buttonLongPressed[b] = true;
}
- }
- else if (!isButtonPressed(b) && buttonPressedBefore[b]) //released
- {
+
+ } else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
+
long dur = millis() - buttonPressedTime[b];
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} //too short "press", debounce
- bool doublePress = buttonWaitTime[b];
+ bool doublePress = buttonWaitTime[b]; //did we have short press before?
buttonWaitTime[b] = 0;
- if (dur > 6000 && b==0) //long press on button 0
- {
+ if (b == 0 && dur > 6000) { //long press on button 0 (when released)
WLED::instance().initAP(true);
- }
- else if (!buttonLongPressed[b]) { //short press
- if (macroDoublePress[b])
- {
- if (doublePress) {
- applyPreset(macroDoublePress[b], CALL_MODE_BUTTON);
-
- // publish MQTT message
- if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
- char subuf[64];
- sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
- mqtt->publish(subuf, 0, false, "double");
- }
- } else buttonWaitTime[b] = millis();
- } else shortPressAction(b);
+ } else if (!buttonLongPressed[b]) { //short press
+ // if this is second release within 350ms it is a double press (buttonWaitTime!=0)
+ if (doublePress) {
+ doublePressAction(b);
+ } else {
+ buttonWaitTime[b] = millis();
+ }
}
buttonPressedBefore[b] = false;
buttonLongPressed[b] = false;
}
- if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > 450 && !buttonPressedBefore[b])
- {
+ // if 450ms elapsed since last press/release it is a short press
+ if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > 350 && !buttonPressedBefore[b]) {
buttonWaitTime[b] = 0;
shortPressAction(b);
}
diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp
index 7f938688..ebdd01bd 100644
--- a/wled00/cfg.cpp
+++ b/wled00/cfg.cpp
@@ -1,4 +1,5 @@
#include "wled.h"
+#include "wled_ethernet.h"
/*
* Serializes and parses the cfg.json and wsec.json settings files, stored in internal FS.
@@ -29,6 +30,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
getStringFromJson(cmDNS, id[F("mdns")], 33);
getStringFromJson(serverDescription, id[F("name")], 33);
getStringFromJson(alexaInvocationName, id[F("inv")], 33);
+#ifndef WLED_DISABLE_SIMPLE_UI
+ CJSON(simplifiedUI, id[F("sui")]);
+#endif
JsonObject nw_ins_0 = doc["nw"]["ins"][0];
getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33);
@@ -60,7 +64,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(apBehavior, ap[F("behav")]);
-
/*
JsonArray ap_ip = ap["ip"];
for (byte i = 0; i < 4; i++) {
@@ -79,16 +82,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(ledCount, hw_led[F("total")]);
if (ledCount > MAX_LEDS) ledCount = MAX_LEDS;
+ uint16_t lC = 0;
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);
JsonArray ins = hw_led["ins"];
+
if (fromFS || !ins.isNull()) {
- uint8_t s = 0; //bus iterator
- strip.isRgbw = false;
- strip.isOffRefreshRequred = false;
+ uint8_t s = 0; // bus iterator
busses.removeAll();
uint32_t mem = 0;
for (JsonObject elm : ins) {
@@ -104,12 +107,20 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
}
uint16_t length = elm[F("len")] | 1;
+ if (length==0 || length+lC > MAX_LEDS) continue; // zero length or we reached max. number of LEDs, just stop
uint8_t colorOrder = (int)elm[F("order")];
uint8_t skipFirst = elm[F("skip")];
uint16_t start = elm[F("start")] | 0;
+ if (start > lC+length) continue; // something is very wrong :)
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"];
-
+ //if ((bool)elm[F("rgbw")]) SET_BIT(ledType,7); else UNSET_BIT(ledType,7); // hack bit 7 to indicate RGBW (as an override if necessary)
+ s++;
+ lC += length;
+ BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
+ mem += BusManager::memUsage(bc);
+ if (mem <= MAX_LED_MEMORY && busses.getNumBusses() <= WLED_MAX_BUSSES) busses.add(bc); // finalization will be done in WLED::beginStrip()
+/*
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
if (bc.adjustBounds(ledCount)) {
//RGBW mode is enabled if at least one of the strips is RGBW
@@ -120,9 +131,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
mem += busses.memUsage(bc);
if (mem <= MAX_LED_MEMORY) busses.add(bc);
}
+*/
}
- strip.finalizeInit(ledCount);
+ // finalization done in beginStrip()
}
+ if (lC > ledCount) ledCount = lC; // fix incorrect total length (honour analog setup)
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
// read multiple button configuration
@@ -461,6 +474,7 @@ void serializeConfig() {
id[F("mdns")] = cmDNS;
id[F("name")] = serverDescription;
id[F("inv")] = alexaInvocationName;
+ id[F("sui")] = simplifiedUI;
JsonObject nw = doc.createNestedObject("nw");
@@ -500,6 +514,25 @@ void serializeConfig() {
#ifdef WLED_USE_ETHERNET
JsonObject ethernet = doc.createNestedObject("eth");
ethernet["type"] = ethernetType;
+ if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
+ JsonArray pins = ethernet.createNestedArray("pin");
+ for (uint8_t p=0; p=0) pins.add(ethernetBoards[ethernetType].eth_power);
+ if (ethernetBoards[ethernetType].eth_mdc>=0) pins.add(ethernetBoards[ethernetType].eth_mdc);
+ if (ethernetBoards[ethernetType].eth_mdio>=0) pins.add(ethernetBoards[ethernetType].eth_mdio);
+ switch (ethernetBoards[ethernetType].eth_clk_mode) {
+ case ETH_CLOCK_GPIO0_IN:
+ case ETH_CLOCK_GPIO0_OUT:
+ pins.add(0);
+ break;
+ case ETH_CLOCK_GPIO16_OUT:
+ pins.add(16);
+ break;
+ case ETH_CLOCK_GPIO17_OUT:
+ pins.add(17);
+ break;
+ }
+ }
#endif
JsonObject hw = doc.createNestedObject("hw");
@@ -526,6 +559,7 @@ void serializeConfig() {
ins["rev"] = bus->reversed;
ins[F("skip")] = bus->skippedLeds();
ins["type"] = bus->getType();
+ ins[F("rgbw")] = bus->isRgbw();
}
// button(s)
@@ -550,7 +584,7 @@ void serializeConfig() {
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 )
+ hw_ir["type"] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled )
JsonObject hw_relay = hw.createNestedObject(F("relay"));
hw_relay["pin"] = rlyPin;
diff --git a/wled00/const.h b/wled00/const.h
index e5061627..27eff968 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -122,6 +122,8 @@
#define TYPE_NONE 0 //light is not configured
#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light
+#define TYPE_VIRTUAL_RGB 2 //virtual RGB bus (master broadcast bus)
+#define TYPE_VIRTUAL_RGBW 3 //virtual RGBW bus (master broadcast bus)
//Digital types (data pin only) (16-31)
#define TYPE_WS2812_1CH 20 //white-only chips
#define TYPE_WS2812_WWA 21 //amber + warm + cold white
@@ -233,7 +235,7 @@
// 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
#ifdef ESP8266
-#define MAX_LEDS 1664 // can't rely on memory limit to limit this to 1600 LEDs
+#define MAX_LEDS 1200 // can't rely on memory limit to limit this to 1200 LEDs
#else
#define MAX_LEDS 8192
#endif
@@ -241,14 +243,14 @@
#ifndef MAX_LED_MEMORY
#ifdef ESP8266
-#define MAX_LED_MEMORY 5000
+#define MAX_LED_MEMORY 4000
#else
#define MAX_LED_MEMORY 64000
#endif
#endif
#ifndef MAX_LEDS_PER_BUS
-#define MAX_LEDS_PER_BUS 4096
+#define MAX_LEDS_PER_BUS 2048 // may not be enough for fast LEDs (i.e. APA102)
#endif
// string temp buffer (now stored in stack locally)
@@ -282,7 +284,7 @@
// Maximum size of node map (list of other WLED instances)
#ifdef ESP8266
- #define WLED_MAX_NODES 15
+ #define WLED_MAX_NODES 24
#else
#define WLED_MAX_NODES 150
#endif
@@ -304,8 +306,4 @@
#endif
#endif
-#ifndef DEFAULT_LED_COUNT
- #define DEFAULT_LED_COUNT 30
-#endif
-
#endif
diff --git a/wled00/data/index.css b/wled00/data/index.css
index 8439e0a9..5b3ebdb3 100644
--- a/wled00/data/index.css
+++ b/wled00/data/index.css
@@ -1,6 +1,7 @@
@font-face {
font-family: "WIcons";
- src: url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff');
+ /*src: url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff');*/
+ src: url(data:font/woff2;charset=utf-8;base64,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) format('woff');
}
:root {
@@ -16,7 +17,9 @@
--c-c: #ccc;
--c-e: #eee;
--c-d: #ddd;
- --c-r: #831;
+ --c-r: #e42;
+ --c-g: #4e2;
+ --c-l: #48a;
--t-b: 0.5;
--c-o: rgba(34, 34, 34, 0.9);
--c-tb : rgba(34, 34, 34, var(--t-b));
@@ -44,13 +47,13 @@ body {
color: var(--c-f);
text-align: center;
-webkit-touch-callout: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
-webkit-tap-highlight-color: transparent;
- scrollbar-width: 6px;
- scrollbar-color: var(--c-sb) transparent;
+ scrollbar-width: 6px;
+ scrollbar-color: var(--c-sb) transparent;
}
html,
@@ -91,9 +94,9 @@ button {
#namelabel {
position: fixed;
bottom: calc(var(--bh) + 6px);
- right: 4px;
- color: var(--c-6);
- cursor: pointer;
+ right: 6px;
+ color: var(--c-b);
+ cursor: pointer;
writing-mode: vertical-rl;
}
@@ -116,6 +119,9 @@ button {
font-size: 24px;
line-height: 1;
display: inline-block;
+}
+
+.top .icons, .bot .icons {
margin: -2px 0 4px 0;
}
@@ -128,59 +134,87 @@ button {
width: 100%;
}
-.segt {
+.segt, .plentry TABLE {
table-layout: fixed;
- width: 76%;
+ width: 100%;
}
-.segtd {
+.segt TD {
text-align: center;
- text-transform: uppercase;
+ /*text-transform: uppercase;*/
+}
+.segt TD, .plentry TD {
font-size: 14px;
+ padding: 0;
+ vertical-align: middle;
+}
+.segt TD.h, .plentry TD.h {
+ font-size: 13px;
+ padding: 2px 0 0;
}
.keytd {
text-align: left;
- padding-bottom: 8px;
}
.valtd {
text-align: right;
- padding-bottom: 8px;
}
-.slider-icon
-{
+.valtd i {
+ font-size: small;
+}
+
+.slider-icon {
transform: translate(6px,3px);
- color: var(--c-d);
}
-.e-icon
-{
+.e-icon {
transform: translateY(3px);
- color: var(--c-d);
}
.sel-icon {
transform: translateX(3px);
+}
+
+.e-icon, .sel-icon, .slider-icon {
+ cursor: pointer;
color: var(--c-d);
}
-.search-cancel-icon {
- position: absolute;
- right: 8px;
- top: 9px;
- cursor: pointer;
- display: none;
+.edit-icon {
+ position: absolute;
+ right: -26px;
+ top: 10px;
+ display: none;
+}
+
+.search-icon {
+ position: absolute;
+ top: 10px;
+ left: 13px;
+ pointer-events: none;
+ width: 24px;
+ height: 24px;
+ margin-top: -1px;
+}
+
+.clear-icon {
+ position: absolute;
+ display: none;
+ top: 10px;
+ right: 13px;
+ cursor: pointer;
+ margin-top: -1px;
}
.flr {
- float: right;
- cursor: pointer;
- margin: 0;
color: var(--c-f);
transform: rotate(0deg);
transition: transform 0.3s;
+ position: absolute;
+ top: 8px;
+ right: 8px;
}
.exp {
@@ -219,15 +253,18 @@ button {
transition: color 0.3s, background-color 0.3s;
font-size: 17px;
color: var(--c-c);
+ min-width: 44px;
}
.top button {
padding: var(--tbp);
+ margin: 0;
}
.bot button {
padding: var(--bbp);
width:25%;
+ margin: 0;
}
.tab button:hover {
@@ -260,7 +297,6 @@ button {
position: relative;
width: 100%;
width: calc(100%/var(--n));
- padding: 11px 0;
box-sizing: border-box;
border: 0px;
overflow: auto;
@@ -269,10 +305,20 @@ button {
}
#Effects {
- padding-top: 0;
- margin-top: 11px;
- height: calc(100% - 11px);
- -webkit-overflow-scrolling: touch;
+ -webkit-overflow-scrolling: touch;
+}
+
+#segutil, #segutil2, #segcont, #putil, #pcont, #pql {
+ width: 280px;
+ margin: 0 auto;
+}
+
+#segutil .seg {
+ margin: 10px 0;
+}
+
+#segcont, #segutil, #putil {
+ padding: 10px 0 0;
}
.smooth { transition: transform calc(var(--f, 1)*.5s) ease-out }
@@ -299,26 +345,32 @@ button {
pointer-events: none;
}
-.staytop {
+.staytop, .staybot {
display: block;
position: -webkit-sticky;
position: sticky;
- background: var(--c-1);
- top: -1px;
+ top: 0;
z-index: 1;
- margin-top: 1px;
- width: 272px;
+ margin: 0 auto auto;
+}
+
+.staybot {
+ bottom: 0;
+}
+
+#staytop, #staytop1 {
+ background: var(--c-2);
+ width: 310px;
margin: auto;
- border-radius: 25px;
+ border-radius: 15px;
}
#staytop1 {
top: 28px;
}
-#fxb0 {
- margin-bottom: 2px;
- filter: drop-shadow(0 0 1px #000);
+#staytop2 {
+ top: 58px;
}
.first {
@@ -328,6 +380,7 @@ button {
#toast {
opacity: 0;
background-color: var(--c-5);
+ border: 1px solid var(--c-2);
max-width: 90%;
color: var(--c-f);
text-align: center;
@@ -379,10 +432,6 @@ button {
margin: 12px 0;
}
-.valtd i {
- font-size: 14px;
-}
-
#roverstar {
position: fixed;
top: calc(var(--th) + 5px);
@@ -411,8 +460,8 @@ button {
display: inline-block;
}
-#kn td {
- padding-bottom: 12px;
+#kv td {
+ padding-bottom: 8px;
}
#lv {
@@ -454,16 +503,23 @@ img {
}
.sliderbubble {
- width: 36px;
- line-height: 24px;
- background: var(--c-3);
- position: absolute;
- transform: translateX(-50%);
- border-radius: 12px;
- margin-left: 12px;
- margin-top: 3px;
- padding: 0px;
- display: inline;
+ width: 24px;
+ position: relative;
+ display: inline-block;
+ border-radius: 10px;
+ background: var(--c-3);
+ color: var(--c-f);
+ padding: 4px 4px 2px;
+ font-size: 14px;
+ right: 5px;
+ transition: visibility 0.25s ease, opacity 0.25s ease;
+ opacity: 0;
+ visibility: hidden;
+}
+
+output.sliderbubbleshow {
+ visibility: visible;
+ opacity: 1;
}
.hidden {
@@ -509,11 +565,6 @@ input[type=range]::-moz-range-thumb {
background: var(--c-f);
transform: translateY(7px);
}
-input[type=range]:active + .sliderbubble {
- display: inline;
- transform: translateX(-50%);
-}
-
#wwrap {
display: none;
}
@@ -524,18 +575,6 @@ input[type=range]:active + .sliderbubble {
position: relative;
}
-.sbs {
- margin: 0px -20px 5px -6px;
-}
-
-.sws {
- width: 230px;
-}
-
-.sis {
- width: 214px !important;
-}
-
.hd {
display: var(--bhd);
}
@@ -556,107 +595,118 @@ input[type=range]:active + .sliderbubble {
.btn {
padding: 8px;
- margin: 10px;
+ margin: 10px 4px;
width: 230px;
font-size: 19px;
- background-color: var(--c-3);
- color: var(--c-f);
- cursor: pointer;
- border: 0px solid white;
+ color: var(--c-d);
+ cursor: pointer;
border-radius: 25px;
- transition-duration: 0.5s;
+ transition-duration: 0.3s;
-webkit-backface-visibility: hidden;
-webkit-transform:translate3d(0,0,0);
+ backface-visibility: hidden;
+ transform:translate3d(0,0,0);
+ overflow: clip;
+ text-overflow: clip;
+}
+.btn:hover {
+ border: 1px solid var(--c-4);
+ background-color: var(--c-4);
+}
+.btn {
+ border: 1px solid var(--c-3);
+ background-color: var(--c-3);
}
-
.btn-s {
- width: 276px;
- background-color: var(--c-2);
-}
-.btn-i {
- padding-bottom: 4px;
+ width: 100%;
+ margin: 0 0 10px;
}
.btn-icon {
- margin: 0px 8px 4px 0;
+ margin: -4px 4px 0 0;
vertical-align: middle;
+ display: inline-block;
}
-.btna-icon {
- margin: 0px;
+.btn-n {
+ width: 230px;
+ margin: 0 8px 10px 0;
}
.btn-p {
- width: 216px;
+ width: 120px;
+ margin: 5px 0;
+}
+.btn-xs, .btn-pl-del, .btn-pl-add {
+ width: 42px;
+ height: 42px;
}
.btn-xs {
- width: 39px;
margin: 2px 0 0 0;
}
-.btn-pl-add {
- margin-left: 9px;
+.btn-pl-del, .btn-pl-add {
+ margin: 0;
+ white-space: nowrap;
}
-
-
#qcs-w {
margin-top: 10px;
}
.qcs {
- padding: 14px;
margin: 2px;
border-radius: 14px;
display: inline-block;
+ width: 28px;
+ height: 28px;
+ line-height: 28px;
}
.qcsb {
- padding: 13px;
+ width: 26px;
+ height: 26px;
+ line-height: 26px;
border: 1px solid var(--c-f);
}
#hexw {
margin-top: 5px;
display: none;
}
+.sel {
+ margin: 5px 0 10px;
+}
+.sel-pl, .sel-ple {
+ padding: 4px;
+ margin: 0;
+ font-size: 19px;
+ background-color: var(--c-3);
+ color: var(--c-d);
+ cursor: pointer;
+ border: 1px solid var(--c-3);
+ border-radius: 5px;
+ transition-duration: 0.5s;
+ -webkit-backface-visibility: hidden;
+ -webkit-transform:translate3d(0,0,0);
+ backface-visibility: hidden;
+ transform:translate3d(0,0,0);
+}
#tt {
text-align: center;
}
-
.cl {
width: 42px;
}
-
.sel-pl {
- width: 192px;
- background-position: 168px 16px;
- margin: 8px 7px 0 0;
+ width: 100%;
+ background-position: 141px 16px;
}
-
.sel-ple {
- width: 216px;
- background-position: 192px 16px;
-}
-
-select {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- background: url("data:image/svg+xml;utf8,") no-repeat;
- background-size: 12px;
- background-position: 206px 16px;
- padding-left: 12px !important;
- background-repeat: no-repeat;
- outline: none;
-}
-select:-moz-focusring {
- transition-duration: 0s;
- color: transparent;
- text-shadow: 0 0 0 #fff;
+ width: 100%;
+ text-align: center;
}
option {
background-color: var(--c-3);
color: var(--c-f);
}
-
input[type=number], input[type=text] {
background: var(--c-3);
color: var(--c-f);
- border: 0px solid white;
- border-radius: 25px;
+ border: 0px solid var(--c-f);
+ border-radius: 5px;
padding: 8px;
margin: 6px 6px 6px 0;
font-size: 19px;
@@ -664,14 +714,18 @@ input[type=number], input[type=text] {
outline: none;
width: 50px;
-webkit-appearance: textfield;
- -moz-appearance: textfield;
- appearance: textfield;
+ -moz-appearance: textfield;
+ appearance: textfield;
+}
+
+input[type=number] {
+ text-align: right;
}
textarea {
background: var(--c-2);
color: var(--c-f);
- width: 236px;
+ width: 95%;
height: 90px;
border-radius: 5px;
border: 2px solid #555;
@@ -681,7 +735,7 @@ textarea {
}
::selection {
- background: var(--c-b);
+ background: var(--c-b);
}
input[type=text] {
@@ -690,11 +744,12 @@ input[type=text] {
}
.ptxt {
- width: 200px !important;
- margin: 26px 0 6px 12px !important;
+ width: 240px !important;
+ margin: 0 4px 4px !important;
}
.stxt {
+ /*display: none;*/
width: 50px !important;
}
@@ -706,54 +761,33 @@ input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
}
-
-.pln {
- width: 67px !important;
- margin: 0 2px 8px 0 !important;
- text-align: center;
+/*
+.segntxt {
+ max-width: 160px;
+ overflow: hidden;
+ text-overflow: clip;
}
-.plnl {
- width: 86px;
- margin: 0 2px 0 0;
- display: inline-block;
-}
-.pli {
- width: 38px;
- margin: 0 0 0 29px;
- display: inline-block;
-}
-
-.segn {
- border-radius: 5px !important;
- margin: 3px 0 6px 0 !important;
-}
-
-.segname {
- position: absolute;
- top: 0px;
- left: 50%;
- padding: 9px 0;
- transform: translateX(-50%);
+*/
+.segname, .pname {
+ padding: 4px 0;
white-space: nowrap;
cursor: pointer;
-}
-
-.pname {
- width: 208px;
- padding: 8px 0;
text-align: center;
overflow: hidden;
text-overflow: clip;
}
+.segpwr {
+ padding: 4px 0 4px 8px;
+}
+
.pid {
- position: absolute;
- top: 0px;
- left: 0px;
- padding: 11px 0px 0px 11px;
- font-size: 16px;
- width: 20px;
- text-align: center;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ padding: 12px 0px 0px 12px;
+ font-size: 16px;
+ text-align: center;
color: var(--c-b);
}
@@ -766,30 +800,26 @@ input[type=number]::-webkit-outer-spin-button {
}
.xxs {
- width: 40px;
- margin: 6px;
+ border: 2px solid var(--c-e) !important;
+ width: 44px;
+ height: 44px;
+ margin: 5px;
+}
+
+.xxs-w {
+ border-width: 5px !important;
+ margin: 2px;
+ width: 50px;
+ height: 50px;
+}
+
+.qcs, .xxs, #namelabel {
+ text-shadow: -1px -1px 0 var(--c-4), 1px -1px 0 var(--c-4), -1px 1px 0 var(--c-4), 1px 1px 0 var(--c-4);
}
.psts {
- background-color: var(--c-3);
color: var(--c-f);
- cursor: pointer;
- padding: 2px 0 0 0;
- height: 40px;
-}
-
-.cnf {
- color: var(--c-f);
- cursor: pointer;
- background: var(--c-3);
- border-radius: 5px;
-}
-
-.cnf-s {
- position: absolute;
- top: 173px;
- right: 23px;
- padding: 7px 22px;
+ margin: 4px;
}
.pwr {
@@ -802,11 +832,6 @@ input[type=number]::-webkit-outer-spin-button {
color: var(--c-f);
}
-.half {
- padding: 7.5px;
- top: 64px;
-}
-
.del {
position: absolute;
bottom: 8px;
@@ -830,17 +855,11 @@ input[type=number]::-webkit-outer-spin-button {
}
.revchkl {
- padding: 2px 0px 0px 35px;
+ padding: 4px 0px 0px 35px;
margin-bottom: 0px;
margin-top: 8px;
}
-.fxchkl {
- position: absolute;
- top: 0px;
- left: 8px;
-}
-
.check input, .radio input {
position: absolute;
opacity: 0;
@@ -851,39 +870,25 @@ input[type=number]::-webkit-outer-spin-button {
.checkmark, .radiomark {
position: absolute;
+ top: 0;
bottom: 0;
left: 0;
- height: 25px;
- width: 25px;
background-color: var(--c-3);
- border-radius: 10px;
+ border: 1px solid var(--c-2);
}
.radiomark {
height: 24px;
width: 24px;
border-radius: 50%;
- background-color: transparent;
}
-.schk {
- top: 0;
+.checkmark {
+ height: 25px;
+ width: 25px;
+ border-radius: 10px;
}
-.psv {
- left: initial;
- bottom: initial;
- top: 0;
- right: 0;
-}
-
-.psvl {
- padding: 2px 35px 10px 0px;
- margin-top: 10px;
- margin-bottom: 0px;
-}
-
-
.check:hover input ~ .checkmark {
background-color: var(--c-4);
}
@@ -934,114 +939,147 @@ input[type=number]::-webkit-outer-spin-button {
margin-bottom: 5px;
}
-.seg {
+.seg, .pres {
position: relative;
- display: inline-block;
- padding: 8px;
- margin: 10px;
- width: 260px;
- font-size: 19px;
+ display: block;
+ padding: 8px 0;
+ margin: 0 0 10px;
background-color: var(--c-2);
color: var(--c-f);
- border: 0px solid white;
+ border: 0px solid var(--c-f);
border-radius: 20px;
text-align: left;
transition: background-color 0.5s;
filter: brightness(1);
+ font-size: 19px;
}
+.seg:last-child {
+ margin: 0;
+}
+
+.seg .schkl {
+ position: absolute;
+ top: 8px;
+ left: 8px;
+}
+
+.pres {
+ padding-bottom: 4px;
+}
+
+#pcont .pres:hover {
+ background-color: var(--c-3);
+}
+
.list {
position: relative;
+ width: 280px;
transition: background-color 0.5s;
- margin: auto auto 10px;
- padding-bottom: 10px;
- width: 230px;
+ margin: auto auto 20px;
+ font-size: 19px;
+ line-height: 24px;
}
.lstI {
- position: sticky;
- overflow: hidden;
+ display: flex;
+ align-items: center;
+ padding: 8px 10px;
+ cursor: pointer;
+ background-color: var(--c-2);
+ overflow: hidden;
+ position: sticky;
+ border: 1px solid var(--c-2);
+ border-radius: 25px;
+ margin: 10px auto 0;
+ min-height: 24px;
}
-.fxbtn {
- margin: 20px auto;
- padding: 8px 0;
+.selected { /* has to be after .lstI */
+ background: var(--c-5);
}
-.lstI:hover {
+.lstI:hover, #pcont .selected:hover {
background: var(--c-4);
}
-.lstI:last-child {
- border: none;
+#pcont .expand, #pcont .expand:hover {
+ background-color: var(--c-2);
+}
+
+.lstI.selected {
+ top: 0;
+ bottom: 0;
}
.lstI.sticky, .lstI.selected {
z-index: 1;
}
+#fxlist .lstI.selected {
+ top: 142px;
+}
+
#pallist .lstI.selected {
- top: 27px;
- bottom: -11px;
+ top: 84px;
+}
+/* must be after .selected */
+#fxlist .lstI.sticky {
+ top: 99px;
}
-
#pallist .lstI.sticky {
- top: -11px;
+ top: 42px;
}
-.lstI.selected {
- background: var(--c-5);
- top: 95px;
- bottom: -11px;
-}
-
-.lstI.sticky {
- top: 57px;
+.lstIcontent {
+ width: 100%;
+ vertical-align: middle;
+ padding: 0 20px 0 5px;
+ text-align: left;
+ display: inline-block;
+ position: relative;
}
.lstIname {
- margin: 3px 0;
white-space: nowrap;
- cursor: pointer;
- font-size: 19px;
}
.lstIprev {
- width: 220px;
- height: 5px;
- margin: auto;
- position: absolute;
- bottom: 0px;
- left: 5px;
+ width: 100%;
+ height: 8px;
+ position: absolute;
+ bottom: 0;
+ left: 0;
}
-input[type="text"].search {
+.fnd {
+ width: 280px;
+ margin: 0 auto;
+}
+
+input[type="text"].fnd {
display: block;
- width: 230px;
+ width: 100%;
box-sizing: border-box;
- padding: 8px 8px 9px 38px;
- margin: 6px auto 0 auto;
+ padding: 8px 48px 8px 60px;
+ margin: 5px auto 0;
text-align: left;
- background: url("data:image/svg+xml;utf8,")
- no-repeat 10px 10px;
- background-size: 20px;
- background-color: var(--c-3);
+ border-radius: 25px;
+ background: var(--c-2);
+ border: 1px solid var(--c-3);
}
-input[type="text"].search:focus {
+input[type="text"].fnd:focus {
background-color: var(--c-4);
}
-input[type="text"].search:not(:placeholder-shown) {
- background-color: var(--c-5);
-}
-
-.pres {
- margin-bottom: 6px;
+input[type="text"].fnd:not(:placeholder-shown),
+input[type="text"].fnd:hover {
+ background-color: var(--c-3);
}
.segin {
- padding: 8px 8px 4px 8px;
+ padding: 0 8px 8px;
display: none;
}
@@ -1063,9 +1101,10 @@ input[type="text"].search:not(:placeholder-shown) {
}
.hrz {
- width: auto;
- height: 2px;
- background-color: var(--c-b);
+ width: auto;
+ height: 2px;
+ background-color: var(--c-b);
+ margin: 3px 0;
}
::-webkit-scrollbar {
@@ -1083,6 +1122,13 @@ input[type="text"].search:not(:placeholder-shown) {
background: var(--c-sbh);
}
+@media not all and (hover: none) {
+ .sliderwrap:hover + output.sliderbubble {
+ visibility: visible;
+ opacity: 1;
+ }
+}
+
@media all and (max-width: 335px) {
.sliderbubble {
display: none;
@@ -1095,11 +1141,21 @@ input[type="text"].search:not(:placeholder-shown) {
}
}
-@media all and (max-width: 685px) {
+@media all and (max-width: 540px) {
.top button {
width: 16.6%;
padding: 8px 0 4px 0;
}
+}
+
+@media all and (min-width: 541px) and (max-width: 719px) {
+ .top button {
+ width: 14.2%;
+ padding: 8px 0 4px 0;
+ }
+}
+
+@media all and (max-width: 719px) {
.hd {
display: none !important;
}
@@ -1109,8 +1165,14 @@ input[type="text"].search:not(:placeholder-shown) {
}
}
-@media all and (max-width: 1249px) {
- #buttonPcm {
- display: none;
+@media all and (max-width: 798px) {
+ #buttonNodes {
+ display: none;
+ }
+}
+
+@media all and (max-width: 1249px) {
+ #buttonPcm {
+ display: none;
}
}
diff --git a/wled00/data/index.htm b/wled00/data/index.htm
index 530f4cd4..7a797390 100644
--- a/wled00/data/index.htm
+++ b/wled00/data/index.htm
@@ -23,10 +23,10 @@
-
-
-
-
+
+
+
+