Fix spelling error

This commit is contained in:
Phil Bolduc 2021-09-20 15:53:20 -07:00
commit ea69957ed1
84 changed files with 10463 additions and 6688 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@
node_modules node_modules
.idea .idea
.direnv .direnv
wled-update.sh

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.13.0-b2", "version": "0.13.0-bl2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "wled", "name": "wled",
"version": "0.13.0-b2", "version": "0.13.0-bl3",
"description": "Tools for WLED project", "description": "Tools for WLED project",
"main": "tools/cdata.js", "main": "tools/cdata.js",
"directories": { "directories": {

View File

@ -166,7 +166,7 @@ lib_deps =
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
#TFT_eSPI #TFT_eSPI
#For use SSD1306 OLED display uncomment following #For use SSD1306 OLED display uncomment following
#U8g2@~2.27.2 U8g2@~2.28.8
#For Dallas sensor uncomment following 2 lines #For Dallas sensor uncomment following 2 lines
#OneWire@~2.3.5 #OneWire@~2.3.5
#milesburton/DallasTemperature@^3.9.0 #milesburton/DallasTemperature@^3.9.0

View File

@ -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,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x180000
5 app1 app ota_1 0x190000 0x180000
6 spiffs data spiffs 0x310000 0xF0000

View File

@ -65,7 +65,7 @@ function adoptVersionAndRepo(html) {
return html; return html;
} }
function writeHtmlGzipped(sourceFile, resultFile) { function writeHtmlGzipped(sourceFile, resultFile, page) {
console.info("Reading " + sourceFile); console.info("Reading " + sourceFile);
new inliner(sourceFile, function (error, html) { new inliner(sourceFile, function (error, html) {
console.info("Inlined " + html.length + " characters"); console.info("Inlined " + html.length + " characters");
@ -95,8 +95,8 @@ function writeHtmlGzipped(sourceFile, resultFile) {
*/ */
// Autogenerated from ${sourceFile}, do not edit!! // Autogenerated from ${sourceFile}, do not edit!!
const uint16_t PAGE_index_L = ${result.length}; const uint16_t PAGE_${page}_L = ${result.length};
const uint8_t PAGE_index[] PROGMEM = { const uint8_t PAGE_${page}[] PROGMEM = {
${array} ${array}
}; };
`; `;
@ -194,7 +194,8 @@ function writeChunks(srcDir, specs, resultFile) {
fs.writeFileSync(resultFile, src); 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( writeChunks(
"wled00/data", "wled00/data",

View File

@ -458,11 +458,15 @@ class Animated_Staircase : public Usermod {
useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop; useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop;
topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin; topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin;
// topPIRorTriggerPin = min(33,max(-1,(int)topPIRorTriggerPin)); // bounds check
topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin; topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin;
// topEchoPin = min(39,max(-1,(int)topEchoPin)); // bounds check
useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom; useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom;
bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin; bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin;
// bottomPIRorTriggerPin = min(33,max(-1,(int)bottomPIRorTriggerPin)); // bounds check
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin; bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
// bottomEchoPin = min(39,max(-1,(int)bottomEchoPin)); // bounds check
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist; topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected) topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected)

View File

@ -1,33 +1,33 @@
# JSON IR remote # JSON IR remote
## Purpose ## Purpose
The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling. 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 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. map buttons from any remote to any HTTP request API or JSON API command.
## Usage ## 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. * 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 > LED settings page, set the correct IR pin.
* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote. * On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
## Modification ## 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. * 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. * 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. * 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) * 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) * 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. * 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. * Other properties are ignored, but having a label property may help when editing.
Sample: Sample:
{ {
"0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command "0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
"0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing "0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
"0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command "0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
"0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6, "0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
"label": "Preset 1 or fallback to Saw - Party"}, // c function "label": "Preset 1 or fallback to Saw - Party"}, // c function
} }

View File

@ -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.

View File

@ -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;
}
}

View File

@ -1,409 +1,409 @@
#pragma once #pragma once
#include "wled.h" #include "wled.h"
#ifndef PIR_SENSOR_PIN #ifndef PIR_SENSOR_PIN
// compatible with QuinLED-Dig-Uno // compatible with QuinLED-Dig-Uno
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#define PIR_SENSOR_PIN 23 // Q4 #define PIR_SENSOR_PIN 23 // Q4
#else //ESP8266 boards #else //ESP8266 boards
#define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini) #define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini)
#endif #endif
#endif #endif
/* /*
* This usermod handles PIR sensor states. * This usermod handles PIR sensor states.
* The strip will be switched on and the off timer will be resetted when the sensor goes HIGH. * 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. * 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 * Usermods allow you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality * 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. * 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. * Multiple v2 usermods can be added to one compilation easily.
* *
* Creating a usermod: * 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. * 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. * Please remember to rename the class and file to a descriptive name.
* You may also use multiple .h and .cpp files. * You may also use multiple .h and .cpp files.
* *
* Using a usermod: * Using a usermod:
* 1. Copy the usermod into the sketch folder (same folder as wled00.ino) * 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 * 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 class PIRsensorSwitch : public Usermod
{ {
public: public:
/** /**
* constructor * constructor
*/ */
PIRsensorSwitch() {} PIRsensorSwitch() {}
/** /**
* desctructor * desctructor
*/ */
~PIRsensorSwitch() {} ~PIRsensorSwitch() {}
/** /**
* Enable/Disable the PIR sensor * Enable/Disable the PIR sensor
*/ */
void EnablePIRsensor(bool en) { enabled = en; } void EnablePIRsensor(bool en) { enabled = en; }
/** /**
* Get PIR sensor enabled/disabled state * Get PIR sensor enabled/disabled state
*/ */
bool PIRsensorEnabled() { return enabled; } bool PIRsensorEnabled() { return enabled; }
private: private:
// PIR sensor pin // PIR sensor pin
int8_t PIRsensorPin = PIR_SENSOR_PIN; int8_t PIRsensorPin = PIR_SENSOR_PIN;
// notification mode for colorUpdated() // notification mode for colorUpdated()
const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
// delay before switch off after the sensor state goes LOW // delay before switch off after the sensor state goes LOW
uint32_t m_switchOffDelay = 600000; // 10min uint32_t m_switchOffDelay = 600000; // 10min
// off timer start time // off timer start time
uint32_t m_offTimerStart = 0; uint32_t m_offTimerStart = 0;
// current PIR sensor pin state // current PIR sensor pin state
byte sensorPinState = LOW; byte sensorPinState = LOW;
// PIR sensor enabled // PIR sensor enabled
bool enabled = true; bool enabled = true;
// status of initialisation // status of initialisation
bool initDone = false; bool initDone = false;
// on and off presets // on and off presets
uint8_t m_onPreset = 0; uint8_t m_onPreset = 0;
uint8_t m_offPreset = 0; uint8_t m_offPreset = 0;
// flag to indicate that PIR sensor should activate WLED during nighttime only // flag to indicate that PIR sensor should activate WLED during nighttime only
bool m_nightTimeOnly = false; bool m_nightTimeOnly = false;
// flag to send MQTT message only (assuming it is enabled) // flag to send MQTT message only (assuming it is enabled)
bool m_mqttOnly = false; 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) // 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 m_offOnly = false;
bool PIRtriggered = false; bool PIRtriggered = false;
unsigned long lastLoop = 0; unsigned long lastLoop = 0;
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
static const char _name[]; static const char _name[];
static const char _switchOffDelay[]; static const char _switchOffDelay[];
static const char _enabled[]; static const char _enabled[];
static const char _onPreset[]; static const char _onPreset[];
static const char _offPreset[]; static const char _offPreset[];
static const char _nightTime[]; static const char _nightTime[];
static const char _mqttOnly[]; static const char _mqttOnly[];
static const char _offOnly[]; static const char _offOnly[];
/** /**
* check if it is daytime * check if it is daytime
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime * if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
*/ */
bool isDayTime() { bool isDayTime() {
bool isDayTime = false; bool isDayTime = false;
updateLocalTime(); updateLocalTime();
uint8_t hr = hour(localTime); uint8_t hr = hour(localTime);
uint8_t mi = minute(localTime); uint8_t mi = minute(localTime);
if (sunrise && sunset) { if (sunrise && sunset) {
if (hour(sunrise)<hr && hour(sunset)>hr) { if (hour(sunrise)<hr && hour(sunset)>hr) {
isDayTime = true; isDayTime = true;
} else { } else {
if (hour(sunrise)==hr && minute(sunrise)<mi) { if (hour(sunrise)==hr && minute(sunrise)<mi) {
isDayTime = true; isDayTime = true;
} }
if (hour(sunset)==hr && minute(sunset)>mi) { if (hour(sunset)==hr && minute(sunset)>mi) {
isDayTime = true; isDayTime = true;
} }
} }
} }
return isDayTime; return isDayTime;
} }
/** /**
* switch strip on/off * switch strip on/off
*/ */
void switchStrip(bool switchOn) void switchStrip(bool switchOn)
{ {
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return; if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
PIRtriggered = switchOn; PIRtriggered = switchOn;
if (switchOn && m_onPreset) { if (switchOn && m_onPreset) {
applyPreset(m_onPreset); applyPreset(m_onPreset);
} else if (!switchOn && m_offPreset) { } else if (!switchOn && m_offPreset) {
applyPreset(m_offPreset); applyPreset(m_offPreset);
} else if (switchOn && bri == 0) { } else if (switchOn && bri == 0) {
bri = briLast; bri = briLast;
colorUpdated(NotifyUpdateMode); colorUpdated(NotifyUpdateMode);
} else if (!switchOn && bri != 0) { } else if (!switchOn && bri != 0) {
briLast = bri; briLast = bri;
bri = 0; bri = 0;
colorUpdated(NotifyUpdateMode); colorUpdated(NotifyUpdateMode);
} }
} }
void publishMqtt(const char* state) void publishMqtt(const char* state)
{ {
//Check if MQTT Connected, otherwise it will crash the 8266 //Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){ if (WLED_MQTT_CONNECTED){
char subuf[64]; char subuf[64];
strcpy(subuf, mqttDeviceTopic); strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/motion")); strcat_P(subuf, PSTR("/motion"));
mqtt->publish(subuf, 0, false, state); mqtt->publish(subuf, 0, false, state);
} }
} }
/** /**
* Read and update PIR sensor state. * Read and update PIR sensor state.
* Initilize/reset switch off timer * Initilize/reset switch off timer
*/ */
bool updatePIRsensorState() bool updatePIRsensorState()
{ {
bool pinState = digitalRead(PIRsensorPin); bool pinState = digitalRead(PIRsensorPin);
if (pinState != sensorPinState) { if (pinState != sensorPinState) {
sensorPinState = pinState; // change previous state sensorPinState = pinState; // change previous state
if (sensorPinState == HIGH) { if (sensorPinState == HIGH) {
m_offTimerStart = 0; m_offTimerStart = 0;
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true); if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
publishMqtt("on"); publishMqtt("on");
} else /*if (bri != 0)*/ { } else /*if (bri != 0)*/ {
// start switch off timer // start switch off timer
m_offTimerStart = millis(); m_offTimerStart = millis();
} }
return true; return true;
} }
return false; return false;
} }
/** /**
* switch off the strip if the delay has elapsed * switch off the strip if the delay has elapsed
*/ */
bool handleOffTimer() bool handleOffTimer()
{ {
if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay) if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
{ {
if (enabled == true) if (enabled == true)
{ {
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false); if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
publishMqtt("off"); publishMqtt("off");
} }
m_offTimerStart = 0; m_offTimerStart = 0;
return true; return true;
} }
return false; return false;
} }
public: public:
//Functions called by WLED //Functions called by WLED
/** /**
* setup() is called once at boot. WiFi is not yet connected at this point. * setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar. * You can use it to initialize variables, sensors or similar.
*/ */
void setup() void setup()
{ {
if (enabled) { if (enabled) {
// pin retrieved from cfg.json (readFromConfig()) prior to running setup() // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) { if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
// PIR Sensor mode INPUT_PULLUP // PIR Sensor mode INPUT_PULLUP
pinMode(PIRsensorPin, INPUT_PULLUP); pinMode(PIRsensorPin, INPUT_PULLUP);
sensorPinState = digitalRead(PIRsensorPin); sensorPinState = digitalRead(PIRsensorPin);
} else { } else {
if (PIRsensorPin >= 0) { if (PIRsensorPin >= 0) {
DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed.")); DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
} }
PIRsensorPin = -1; // allocation failed PIRsensorPin = -1; // allocation failed
enabled = false; enabled = false;
} }
} }
initDone = true; initDone = true;
} }
/** /**
* connected() is called every time the WiFi is (re)connected * connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces * Use it to initialize network interfaces
*/ */
void connected() void connected()
{ {
} }
/** /**
* loop() is called continuously. Here you can check for events, read sensors, etc. * loop() is called continuously. Here you can check for events, read sensors, etc.
*/ */
void loop() void loop()
{ {
// only check sensors 4x/s // only check sensors 4x/s
if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return; if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
lastLoop = millis(); lastLoop = millis();
if (!updatePIRsensorState()) { if (!updatePIRsensorState()) {
handleOffTimer(); handleOffTimer();
} }
} }
/** /**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * 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 * Add PIR sensor state and switch off timer duration to jsoninfo
*/ */
void addToJsonInfo(JsonObject &root) void addToJsonInfo(JsonObject &root)
{ {
JsonObject user = root["u"]; JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u"); if (user.isNull()) user = root.createNestedObject("u");
if (enabled) if (enabled)
{ {
// off timer // off timer
String uiDomString = F("PIR <i class=\"icons\">&#xe325;</i>"); String uiDomString = F("PIR <i class=\"icons\">&#xe325;</i>");
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
if (m_offTimerStart > 0) if (m_offTimerStart > 0)
{ {
uiDomString = ""; uiDomString = "";
unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000; unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
if (offSeconds >= 3600) if (offSeconds >= 3600)
{ {
uiDomString += (offSeconds / 3600); uiDomString += (offSeconds / 3600);
uiDomString += F("h "); uiDomString += F("h ");
offSeconds %= 3600; offSeconds %= 3600;
} }
if (offSeconds >= 60) if (offSeconds >= 60)
{ {
uiDomString += (offSeconds / 60); uiDomString += (offSeconds / 60);
offSeconds %= 60; offSeconds %= 60;
} }
else if (uiDomString.length() > 0) else if (uiDomString.length() > 0)
{ {
uiDomString += 0; uiDomString += 0;
} }
if (uiDomString.length() > 0) if (uiDomString.length() > 0)
{ {
uiDomString += F("min "); uiDomString += F("min ");
} }
uiDomString += (offSeconds); uiDomString += (offSeconds);
infoArr.add(uiDomString + F("s")); infoArr.add(uiDomString + F("s"));
} else { } else {
infoArr.add(sensorPinState ? F("sensor on") : F("inactive")); infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
} }
} else { } else {
String uiDomString = F("PIR sensor"); String uiDomString = F("PIR sensor");
JsonArray infoArr = user.createNestedArray(uiDomString); JsonArray infoArr = user.createNestedArray(uiDomString);
infoArr.add(F("disabled")); infoArr.add(F("disabled"));
} }
} }
/** /**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * 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 * 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). * 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 * Values in the state object may be modified by connected clients
*/ */
/* /*
void readFromJsonState(JsonObject &root) void readFromJsonState(JsonObject &root)
{ {
} }
*/ */
/** /**
* provide the changeable values * provide the changeable values
*/ */
void addToConfig(JsonObject &root) void addToConfig(JsonObject &root)
{ {
JsonObject top = root.createNestedObject(FPSTR(_name)); JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000; top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
top["pin"] = PIRsensorPin; top["pin"] = PIRsensorPin;
top[FPSTR(_onPreset)] = m_onPreset; top[FPSTR(_onPreset)] = m_onPreset;
top[FPSTR(_offPreset)] = m_offPreset; top[FPSTR(_offPreset)] = m_offPreset;
top[FPSTR(_nightTime)] = m_nightTimeOnly; top[FPSTR(_nightTime)] = m_nightTimeOnly;
top[FPSTR(_mqttOnly)] = m_mqttOnly; top[FPSTR(_mqttOnly)] = m_mqttOnly;
top[FPSTR(_offOnly)] = m_offOnly; top[FPSTR(_offOnly)] = m_offOnly;
DEBUG_PRINTLN(F("PIR config saved.")); DEBUG_PRINTLN(F("PIR config saved."));
} }
/** /**
* restore the changeable values * restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json * 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. * The function should return true if configuration was successfully loaded or false if there was no configuration.
*/ */
bool readFromConfig(JsonObject &root) bool readFromConfig(JsonObject &root)
{ {
bool oldEnabled = enabled; bool oldEnabled = enabled;
int8_t oldPin = PIRsensorPin; int8_t oldPin = PIRsensorPin;
JsonObject top = root[FPSTR(_name)]; JsonObject top = root[FPSTR(_name)];
if (top.isNull()) { if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false; return false;
} }
PIRsensorPin = top["pin"] | PIRsensorPin; PIRsensorPin = top["pin"] | PIRsensorPin;
enabled = top[FPSTR(_enabled)] | enabled; enabled = top[FPSTR(_enabled)] | enabled;
m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000; m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000;
m_onPreset = top[FPSTR(_onPreset)] | m_onPreset; m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
m_onPreset = max(0,min(250,(int)m_onPreset)); m_onPreset = max(0,min(250,(int)m_onPreset));
m_offPreset = top[FPSTR(_offPreset)] | m_offPreset; m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
m_offPreset = max(0,min(250,(int)m_offPreset)); m_offPreset = max(0,min(250,(int)m_offPreset));
m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly; m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly; m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly; m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
if (!initDone) { if (!initDone) {
// reading config prior to setup() // reading config prior to setup()
DEBUG_PRINTLN(F(" config loaded.")); DEBUG_PRINTLN(F(" config loaded."));
} else { } else {
if (oldPin != PIRsensorPin || oldEnabled != enabled) { if (oldPin != PIRsensorPin || oldEnabled != enabled) {
// check if pin is OK // check if pin is OK
if (oldPin != PIRsensorPin && oldPin >= 0) { if (oldPin != PIRsensorPin && oldPin >= 0) {
// if we are changing pin in settings page // if we are changing pin in settings page
// deallocate old pin // deallocate old pin
pinManager.deallocatePin(oldPin, PinOwner::UM_PIR); pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) { if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
pinMode(PIRsensorPin, INPUT_PULLUP); pinMode(PIRsensorPin, INPUT_PULLUP);
} else { } else {
// allocation failed // allocation failed
PIRsensorPin = -1; PIRsensorPin = -1;
enabled = false; enabled = false;
} }
} }
if (enabled) { if (enabled) {
sensorPinState = digitalRead(PIRsensorPin); sensorPinState = digitalRead(PIRsensorPin);
} }
} }
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_offOnly)].isNull(); return !top[FPSTR(_offOnly)].isNull();
} }
/** /**
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). * 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. * This could be used in the future for the system to determine whether your usermod is installed.
*/ */
uint16_t getId() uint16_t getId()
{ {
return USERMOD_ID_PIRSWITCH; return USERMOD_ID_PIRSWITCH;
} }
}; };
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch"; const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled"; const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec"; const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset"; const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset"; const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only"; const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only"; const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only"; const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";

View File

@ -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
...
```

View File

@ -1,54 +0,0 @@
#include <Arduino.h>
#include "wled.h"
//Intiating code for QuinLED Dig-Uno temp sensor
//Uncomment Celsius if that is your prefered temperature scale
#include <DallasTemperature.h> //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;
}

View File

@ -2,11 +2,14 @@
This usermod allow to use 240x240 display to display following: This usermod allow to use 240x240 display to display following:
* current date and time;
* Network SSID; * Network SSID;
* IP address; * IP address;
* WiFi signal strength;
* Brightness; * Brightness;
* Chosen effect; * Chosen effect;
* Chosen palette; * Chosen palette;
* effect speed and intensity;
* Estimated current in mA; * Estimated current in mA;
## Hardware ## Hardware
@ -46,27 +49,29 @@ Add lines to section:
default_envs = esp32dev default_envs = esp32dev
build_flags = ${common.build_flags_esp32} build_flags = ${common.build_flags_esp32}
-D USERMOD_ST7789_DISPLAY -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. 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 ### 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 <User_Setups/Setup24_ST7789.h>` and commenting out line containing `#include <User_Setup.h>`.
```ini If your display includes backlight enable pin, #define TFT_BL with backlight enable GPIO number.
//#include <User_Setup.h> // Default setup is root library folder
```
* Add following line:
```ini
#include <User_Setups/Setup_ST7789_Display.h> // 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`

View File

@ -7,27 +7,48 @@
#include <TFT_eSPI.h> #include <TFT_eSPI.h>
#include <SPI.h> #include <SPI.h>
#define USERMOD_ST7789_DISPLAY 97 #ifndef USER_SETUP_LOADED
#ifndef ST7789_DRIVER
#ifndef TFT_DISPOFF #error Please define ST7789_DRIVER
#define TFT_DISPOFF 0x28 #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 #endif
#ifndef TFT_SLPIN #define USERMOD_ID_ST7789_DISPLAY 97
#define TFT_SLPIN 0x10
#endif
#define TFT_MOSI 21 TFT_eSPI tft = TFT_eSPI(TFT_WIDTH, TFT_HEIGHT); // Invoke custom library
#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(240, 240); // Invoke custom library // Extra char (+1) for null
#define LINE_BUFFER_SIZE 20
// How often we are redrawing screen // How often we are redrawing screen
#define USER_LOOP_REFRESH_RATE_MS 1000 #define USER_LOOP_REFRESH_RATE_MS 1000
extern int getSignalQuality(int rssi);
//class name. Use something descriptive and leave the ": public Usermod" part :) //class name. Use something descriptive and leave the ": public Usermod" part :)
class St7789DisplayUsermod : public Usermod { class St7789DisplayUsermod : public Usermod {
@ -45,9 +66,70 @@ class St7789DisplayUsermod : public Usermod {
uint8_t knownBrightness = 0; uint8_t knownBrightness = 0;
uint8_t knownMode = 0; uint8_t knownMode = 0;
uint8_t knownPalette = 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; long lastUpdate = 0;
void center(String &line, uint8_t width) {
int len = line.length();
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
for (byte i=line.length(); i<width; i++) line += ' ';
}
/**
* Display the current date and time in large characters
* on the middle rows. Based 24 or 12 hour depending on
* the useAMPM configuration.
*/
void showTime() {
if (!ntpEnabled) return;
char lineBuffer[LINE_BUFFER_SIZE];
updateLocalTime();
byte minuteCurrent = minute(localTime);
byte hourCurrent = hour(localTime);
byte secondCurrent = second(localTime);
knownMinute = minuteCurrent;
knownHour = hourCurrent;
byte currentMonth = month(localTime);
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(currentMonth), day(localTime));
tft.setTextColor(TFT_SILVER);
tft.setCursor(84, 0);
tft.setTextSize(2);
tft.print(lineBuffer);
byte showHour = hourCurrent;
boolean isAM = false;
if (useAMPM) {
if (showHour == 0) {
showHour = 12;
isAM = true;
} else if (showHour > 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: public:
//Functions called by WLED //Functions called by WLED
@ -57,6 +139,9 @@ class St7789DisplayUsermod : public Usermod {
*/ */
void setup() 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.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.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); tft.fillScreen(TFT_BLACK);
@ -65,10 +150,10 @@ class St7789DisplayUsermod : public Usermod {
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextSize(2); tft.setTextSize(2);
tft.print("Loading..."); tft.print("Loading...");
if (TFT_BL > 0) if (TFT_BL >= 0)
{ // TFT_BL has been set in the TFT_eSPI library {
pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
digitalWrite(TFT_BL, HIGH); // Turn backlight on. digitalWrite(TFT_BL, HIGH); // Turn backlight on.
} }
} }
@ -91,174 +176,189 @@ class St7789DisplayUsermod : public Usermod {
* Instead, use a timer check as shown here. * Instead, use a timer check as shown here.
*/ */
void loop() { void loop() {
// Check if we time interval for redrawing passes. char buff[LINE_BUFFER_SIZE];
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
// Check if we time interval for redrawing passes.
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
{ {
return; return;
} }
lastUpdate = millis(); lastUpdate = millis();
// Turn off display after 5 minutes with no change. // Turn off display after 5 minutes with no change.
if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000) 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; displayTurnedOff = true;
} }
// Check if values which are shown on display changed from the last time. // Check if values which are shown on display changed from the last time.
if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) if ((((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) ||
{ (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) ||
needRedraw = true; (knownBrightness != bri) ||
} (knownEffectSpeed != effectSpeed) ||
else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) (knownEffectIntensity != effectIntensity) ||
{ (knownMode != strip.getMode()) ||
needRedraw = true; (knownPalette != strip.getSegment(0).palette))
}
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)
{ {
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 '"': case '"':
insideQuotes = !insideQuotes; insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownMode))
break; break;
tft.setTextColor(TFT_MAGENTA); case '[':
tft.print(singleJsonSymbol); case ']':
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))
break; break;
tft.print(singleJsonSymbol); case ',':
printedChars++; 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)) tft.setCursor(0, 192);
break; tft.setTextColor(TFT_SILVER);
} sprintf_P(buff, PSTR("FX Spd:%3d Int:%3d"), effectSpeed, effectIntensity);
// Fifth row with estimated mA usage tft.print(buff);
tft.setTextColor(TFT_SILVER);
tft.setCursor(3, 152); // Fifth row with estimated mA usage
// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate). tft.setTextColor(TFT_SILVER);
tft.print("Current: "); tft.setCursor(0, 216);
tft.print(strip.currentMilliamps); // Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
tft.print("mA"); 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. * 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. * 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) 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!")); //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) void addToConfig(JsonObject& root)
{ {
JsonObject top = root.createNestedObject("exampleUsermod"); //JsonObject top = root.createNestedObject("exampleUsermod");
top["great"] = userVar0; //save this var persistently whenever settings are saved //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. * 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 :) * 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"]; //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) //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() 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. //More methods can be added in the future, this example will then be extended.

View File

@ -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

View File

@ -81,7 +81,9 @@ class UsermodTemperature : public Usermod {
temperature = readDallas(); temperature = readDallas();
lastMeasurement = millis(); lastMeasurement = millis();
waitingForConversion = false; 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() { bool findSensor() {

View File

@ -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.

View File

@ -1,166 +0,0 @@
#pragma once
#include "wled.h"
#include <Dusk2Dawn.h>
/*
*
* 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, youll 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;
}
};

View File

@ -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 <NeoPixelBrightnessBus.h>
#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<PIXELFEATURE3, NeoEsp32Rmt0Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt1 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt1Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt2 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt2Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt3 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt3Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt4 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt4Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt5 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt5Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt6 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt6Ws2812xMethod>
#define NeoPixelBrightnessBusGrbRmt7 NeoPixelBrightnessBus<PIXELFEATURE3, NeoEsp32Rmt7Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt0 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt0Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt1 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt1Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt2 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt2Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt3 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt3Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt4 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt4Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt5 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt5Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt6 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt6Ws2812xMethod>
#define NeoPixelBrightnessBusGrbwRmt7 NeoPixelBrightnessBus<PIXELFEATURE4, NeoEsp32Rmt7Ws2812xMethod>
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

View File

@ -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.

View File

@ -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.

View File

@ -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). * 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 * 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). * 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 * Values in the state object may be modified by connected clients
*/ */
void readFromJsonState(JsonObject &root) { //void readFromJsonState(JsonObject &root) {
} //}
/** /**
* provide the changeable values * provide the changeable values
@ -335,11 +335,12 @@ class MultiRelay : public Usermod {
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += "-"; parName += i; parName += "-"; String parName = FPSTR(_relay_str); parName += '-'; parName += i;
top[parName+"pin"] = _relay[i].pin; JsonObject relay = top.createNestedObject(parName);
top[parName+FPSTR(_activeHigh)] = _relay[i].mode; relay["pin"] = _relay[i].pin;
top[parName+FPSTR(_delay_str)] = _relay[i].delay; relay[FPSTR(_activeHigh)] = _relay[i].mode;
top[parName+FPSTR(_external)] = _relay[i].external; relay[FPSTR(_delay_str)] = _relay[i].delay;
relay[FPSTR(_external)] = _relay[i].external;
} }
DEBUG_PRINTLN(F("MultiRelay config saved.")); DEBUG_PRINTLN(F("MultiRelay config saved."));
} }
@ -363,12 +364,19 @@ class MultiRelay : public Usermod {
enabled = top[FPSTR(_enabled)] | enabled; enabled = top[FPSTR(_enabled)] | enabled;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += "-"; parName += i; parName += "-"; String parName = FPSTR(_relay_str); parName += '-'; parName += i;
oldPin[i] = _relay[i].pin; oldPin[i] = _relay[i].pin;
_relay[i].pin = top[parName]["pin"] | _relay[i].pin;
_relay[i].mode = top[parName][FPSTR(_activeHigh)] | _relay[i].mode;
_relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay;
// begin backwards compatibility (beta) remove when 0.13 is released
parName += '-';
_relay[i].pin = top[parName+"pin"] | _relay[i].pin; _relay[i].pin = top[parName+"pin"] | _relay[i].pin;
_relay[i].mode = top[parName+FPSTR(_activeHigh)] | _relay[i].mode; _relay[i].mode = top[parName+FPSTR(_activeHigh)] | _relay[i].mode;
_relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external; _relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay; _relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay;
// end compatibility
_relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min _relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min
} }
@ -396,7 +404,7 @@ class MultiRelay : public Usermod {
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true; return !top[F("relay-0")]["pin"].isNull();
} }
/** /**

View File

@ -1,37 +0,0 @@
# QuinLED-Dig-Quad Preassembled Unofficial Build
This usermod targets the [Preassembled QuinLED-Dig-Quad](https://quinled.info/pre-assembled-quinled-dig-quad/). Tested on board revision v1r6b,
and includes the following features:
* **Multi-channel Support** - enabling use of LED1, LED2, LED3, LED4 pins to work using segments
* **Temperature Sensor Support** - pulls readings from the built-in temperature sensor and adds the reading to the *Info* page in the UI
## Background
As a starting point, you should check out this awesome video from Quindor: [How to compile WLED yourself](https://quinled.info/2020/12/22/livestream-wled-compile/). The usermod you are reading now just provides some shortcuts for parts of what were covered in that video.
## Build Firmware with Multi-channel and Temp Support
1. Copy the `platformio_override.ini` file to the project's root directory
1. If using VS Code with the PlatformIO plugin like in the video, you will now see this new project task listed in the PLATFORMIO panel at the bottom as `env:QL-DigQuad-Pre-v0.1` (you probably need to hit the refresh button)
<img src="images/pio-screenshot.png" width="400px"/>
1. Edit this file from the root directory as needed:
<img src="images/params.png" width="400px"/>
* `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
<img src="images/json-temp.png" width="300px"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 KiB

View File

@ -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

View File

@ -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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,175 +0,0 @@
#include <U8x8lib.h> // 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
}

View File

@ -65,6 +65,7 @@ class AutoSaveUsermod : public Usermod {
month(localTime), day(localTime), month(localTime), day(localTime),
hour(localTime), minute(localTime), second(localTime)); hour(localTime), minute(localTime), second(localTime));
savePreset(autoSavePreset, true, presetNameBuffer); savePreset(autoSavePreset, true, presetNameBuffer);
cacheInvalidate++; // force reload of presets
} }
void inline displayOverlay() { void inline displayOverlay() {

View File

@ -114,6 +114,7 @@ class FourLineDisplayUsermod : public Usermod {
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
#ifndef FLD_SPI_DEFAULT #ifndef FLD_SPI_DEFAULT
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA 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 DisplayType type = SSD1306; // display type
#else #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 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 _flip[];
static const char _sleepMode[]; static const char _sleepMode[];
static const char _clockMode[]; static const char _clockMode[];
static const char _busClkFrequency[];
// If display does not work or looks corrupted check the // If display does not work or looks corrupted check the
// constructor reference: // constructor reference:
@ -248,6 +250,7 @@ class FourLineDisplayUsermod : public Usermod {
initDone = true; initDone = true;
DEBUG_PRINTLN(F("Starting display.")); DEBUG_PRINTLN(F("Starting display."));
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
u8x8->begin(); u8x8->begin();
setFlipMode(flip); setFlipMode(flip);
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 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(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode; top[FPSTR(_sleepMode)] = (bool) sleepMode;
top[FPSTR(_clockMode)] = (bool) clockMode; top[FPSTR(_clockMode)] = (bool) clockMode;
top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
DEBUG_PRINTLN(F("4 Line Display config saved.")); DEBUG_PRINTLN(F("4 Line Display config saved."));
} }
@ -714,6 +718,7 @@ class FourLineDisplayUsermod : public Usermod {
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000; screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
sleepMode = top[FPSTR(_sleepMode)] | sleepMode; sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode; clockMode = top[FPSTR(_clockMode)] | clockMode;
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
if (!initDone) { if (!initDone) {
@ -739,12 +744,13 @@ class FourLineDisplayUsermod : public Usermod {
setup(); setup();
needsRedraw |= true; needsRedraw |= true;
} }
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
setContrast(contrast); setContrast(contrast);
setFlipMode(flip); setFlipMode(flip);
if (needsRedraw && !wakeDisplay()) redraw(true); if (needsRedraw && !wakeDisplay()) redraw(true);
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // 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) // strings to reduce flash memory usage (used more than twice)
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay"; const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast"; const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec"; const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec"; const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip"; const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode"; const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode"; const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";

View File

@ -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. * Theatre-style crawling lights.
* Inspired by the Adafruit examples. * Inspired by the Adafruit examples.
*/ */
uint16_t WS2812FX::mode_theater_chase(void) { 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. * Inspired by the Adafruit examples.
*/ */
uint16_t WS2812FX::mode_theater_chase_rainbow(void) { 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. * Alternating pixels running function.
*/ */
uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) { uint16_t WS2812FX::running(uint32_t color1, uint32_t color2, bool theatre) {
uint8_t pxw = 1 + (SEGMENT.intensity >> 5); uint8_t width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window
uint32_t cycleTime = 35 + (255 - SEGMENT.speed); uint32_t cycleTime = 50 + (255 - SEGMENT.speed);
uint32_t it = now / cycleTime; uint32_t it = now / cycleTime;
if (SEGMENT.speed == 0) it = 0; bool usePalette = color1 == SEGCOLOR(0);
for(uint16_t i = 0; i < SEGLEN; i++) { for(uint16_t i = 0; i < SEGLEN; i++) {
if((i + SEGENV.aux0) % (pxw*2) < pxw) { uint32_t col = color2;
if (color1 == SEGCOLOR(0)) if (usePalette) color1 = color_from_palette(i, true, PALETTE_SOLID_WRAP, 0);
{ if (theatre) {
setPixelColor(SEGLEN -i -1, color_from_palette(SEGLEN -i -1, true, PALETTE_SOLID_WRAP, 0)); if ((i % width) == SEGENV.aux0) col = color1;
} else
{
setPixelColor(SEGLEN -i -1, color1);
}
} else { } 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 ) if (it != SEGENV.step )
{ {
SEGENV.aux0 = (SEGENV.aux0 +1) % (pxw*2); SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1));
SEGENV.step = it; SEGENV.step = it;
} }
return FRAMETIME; return FRAMETIME;
@ -1247,44 +1216,19 @@ uint16_t WS2812FX::mode_loading(void) {
//American Police Light with all LEDs Red and Blue //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 delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
uint16_t idexR = (counter * SEGLEN) >> 16; uint32_t it = now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
if (idexR >= SEGLEN) idexR = 0; uint16_t offset = it % SEGLEN;
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;
}
}
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; 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 //American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::mode_police_all() 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() uint16_t WS2812FX::mode_police()
{ {
fill(SEGCOLOR(1)); fill(SEGCOLOR(1));
return police_base(RED, BLUE, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
return police_base(RED, BLUE, false);
} }
//Police All with custom colors //Police All with custom colors
uint16_t WS2812FX::mode_two_areas() 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)); fill(SEGCOLOR(2));
uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1); 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 * Tricolor chase function
*/ */
uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) { uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) {
uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2; uint32_t cycleTime = 50 + ((255 - SEGMENT.speed)<<1);
uint32_t it = now / cycleTime; uint32_t it = now / cycleTime; // iterator
uint8_t width = (1 + SEGMENT.intensity/32) * 3; //value of 1-8 for each colour uint8_t width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour
uint8_t index = it % width; uint8_t index = it % (width*3);
for(uint16_t i = 0; i < SEGLEN; i++, index++) { for (uint16_t i = 0; i < SEGLEN; i++, index++) {
if(index > width-1) index = 0; if (index > (width*3)-1) index = 0;
uint32_t color = color1; uint32_t color = color1;
if(index > width*2/3-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1); if (index > (width<<1)-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
else if(index > width/3-1) color = color2; else if (index > width-1) color = color2;
setPixelColor(SEGLEN - i -1, color); setPixelColor(SEGLEN - i -1, color);
} }
return FRAMETIME; return FRAMETIME;
} }
@ -1548,7 +1491,7 @@ uint16_t WS2812FX::mode_random_chase(void)
return FRAMETIME; return FRAMETIME;
} }
//7 bytes
typedef struct Oscillator { typedef struct Oscillator {
int16_t pos; int16_t pos;
int8_t size; int8_t size;
@ -1780,7 +1723,7 @@ uint16_t WS2812FX::mode_fire_2012()
// Step 1. Cool down every cell a little // Step 1. Cool down every cell a little
for (uint16_t i = 0; i < SEGLEN; i++) { for (uint16_t i = 0; i < SEGLEN; i++) {
uint8_t temp = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2)); uint8_t temp = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2));
heat[i] = (temp==0 && i<ignition) ? 2 : temp; // prevent ignition area from becoming black heat[i] = (temp==0 && i<ignition) ? 16 : temp; // prevent ignition area from becoming black
} }
// Step 2. Heat from each cell drifts 'up' and diffuses a little // Step 2. Heat from each cell drifts 'up' and diffuses a little
@ -1994,7 +1937,7 @@ uint16_t WS2812FX::mode_colortwinkle()
if (fadeUp) { if (fadeUp) {
CRGB incrementalColor = fastled_col; CRGB incrementalColor = fastled_col;
incrementalColor.nscale8_video( fadeUpAmount); incrementalColor.nscale8_video(fadeUpAmount);
fastled_col += incrementalColor; fastled_col += incrementalColor;
if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) { if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) {
@ -2002,24 +1945,21 @@ uint16_t WS2812FX::mode_colortwinkle()
} }
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
if (col_to_crgb(getPixelColor(i)) == prev) //fix "stuck" pixels if (col_to_crgb(getPixelColor(i)) == prev) { //fix "stuck" pixels
{
fastled_col += fastled_col; fastled_col += fastled_col;
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
} }
} else { } else {
fastled_col.nscale8( 255 - fadeDownAmount); fastled_col.nscale8(255 - fadeDownAmount);
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
} }
} }
for (uint16_t j = 0; j <= SEGLEN / 50; j++) for (uint16_t j = 0; j <= SEGLEN / 50; j++) {
{
if (random8() <= SEGMENT.intensity) { if (random8() <= SEGMENT.intensity) {
for (uint8_t times = 0; times < 5; times++) //attempt to spawn a new pixel 5 times for (uint8_t times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times
{
int i = random16(SEGLEN); int i = random16(SEGLEN);
if(getPixelColor(i) == 0) { if (getPixelColor(i) == 0) {
fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND); fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND);
uint16_t index = i >> 3; uint16_t index = i >> 3;
uint8_t bitNum = i & 0x07; uint8_t bitNum = i & 0x07;
@ -2170,10 +2110,14 @@ typedef struct Ripple {
uint16_t pos; uint16_t pos;
} ripple; } ripple;
#ifdef ESP8266
#define MAX_RIPPLES 56
#else
#define MAX_RIPPLES 100
#endif
uint16_t WS2812FX::ripple_base(bool rainbow) uint16_t WS2812FX::ripple_base(bool rainbow)
{ {
uint16_t maxRipples = 1 + (SEGLEN >> 2); uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266
if (maxRipples > 100) maxRipples = 100;
uint16_t dataSize = sizeof(ripple) * maxRipples; uint16_t dataSize = sizeof(ripple) * maxRipples;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2241,6 +2185,7 @@ uint16_t WS2812FX::ripple_base(bool rainbow)
} }
return FRAMETIME; return FRAMETIME;
} }
#undef MAX_RIPPLES
uint16_t WS2812FX::mode_ripple(void) { uint16_t WS2812FX::mode_ripple(void) {
return ripple_base(false); return ripple_base(false);
@ -2541,7 +2486,6 @@ uint16_t WS2812FX::mode_spots_fade()
//each needs 12 bytes //each needs 12 bytes
//Spark type is used for popcorn and 1D fireworks
typedef struct Ball { typedef struct Ball {
unsigned long lastBounceTime; unsigned long lastBounceTime;
float impactVelocity; float impactVelocity;
@ -2651,7 +2595,7 @@ uint16_t WS2812FX::mode_sinelon_dual(void) {
} }
uint16_t WS2812FX::mode_sinelon_rainbow(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 //Spark type is used for popcorn, 1D fireworks, and drip
typedef struct Spark { typedef struct Spark {
float pos; float pos;
@ -2685,7 +2629,7 @@ typedef struct Spark {
*/ */
uint16_t WS2812FX::mode_popcorn(void) { uint16_t WS2812FX::mode_popcorn(void) {
//allocate segment data //allocate segment data
uint16_t maxNumPopcorn = 24; uint16_t maxNumPopcorn = 22; // max 22 on 16 segment ESP8266
uint16_t dataSize = sizeof(spark) * maxNumPopcorn; uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2744,7 +2688,7 @@ uint16_t WS2812FX::candle(bool multi)
if (multi) if (multi)
{ {
//allocate segment data //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 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/ / 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 / Speed sets frequency of new starbursts, intensity is the intensity of the burst
*/ */
#define STARBURST_MAX_FRAG 12 #ifdef ESP8266
#define STARBURST_MAX_FRAG 4
//each needs 64 byte #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 { typedef struct particle {
CRGB color; CRGB color;
uint32_t birth =0; uint32_t birth =0;
@ -2844,8 +2793,7 @@ typedef struct particle {
} star; } star;
uint16_t WS2812FX::mode_starburst(void) { uint16_t WS2812FX::mode_starburst(void) {
uint8_t numStars = 1 + (SEGLEN >> 3); uint8_t numStars = min(1 + (SEGLEN >> 3), STARBURST_MAX_STARS); // 11 * 58 * 32 = 19k (ESP32), 6 * 34 * 16 = 3.2k (ESP8266)
if (numStars > 15) numStars = 15;
uint16_t dataSize = sizeof(star) * numStars; uint16_t dataSize = sizeof(star) * numStars;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2946,18 +2894,22 @@ uint16_t WS2812FX::mode_starburst(void) {
} }
return FRAMETIME; return FRAMETIME;
} }
#undef STARBURST_MAX_FRAG
#undef STARBURST_MAX_STARS
/* /*
* Exploding fireworks effect * Exploding fireworks effect
* adapted from: http://www.anirama.com/1000leds/1d-fireworks/ * 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) uint16_t WS2812FX::mode_exploding_fireworks(void)
{ {
//allocate segment data //allocate segment data
uint16_t numSparks = 2 + (SEGLEN >> 1); uint16_t numSparks = min(2 + (SEGLEN >> 1), MAX_SPARKS); // max 58 for 32 segment ESP32, 20 for 16 segment ESP8266
if (numSparks > 80) numSparks = 80;
uint16_t dataSize = sizeof(spark) * numSparks; uint16_t dataSize = sizeof(spark) * numSparks;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -3052,7 +3004,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
SEGENV.aux0--; SEGENV.aux0--;
if (SEGENV.aux0 < 4) { if (SEGENV.aux0 < 4) {
SEGENV.aux0 = 0; //back to flare 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; return FRAMETIME;
} }
#undef MAX_SPARKS
/* /*
@ -3069,7 +3022,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
uint16_t WS2812FX::mode_drip(void) uint16_t WS2812FX::mode_drip(void)
{ {
//allocate segment data //allocate segment data
uint16_t numDrops = 4; uint8_t numDrops = 4;
uint16_t dataSize = sizeof(spark) * numDrops; uint16_t dataSize = sizeof(spark) * numDrops;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -3077,7 +3030,7 @@ uint16_t WS2812FX::mode_drip(void)
Spark* drops = reinterpret_cast<Spark*>(SEGENV.data); Spark* drops = reinterpret_cast<Spark*>(SEGENV.data);
numDrops = 1 + (SEGMENT.intensity >> 6); numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
float gravity = -0.0005 - (SEGMENT.speed/50000.0); float gravity = -0.0005 - (SEGMENT.speed/50000.0);
gravity *= SEGLEN; gravity *= SEGLEN;
@ -3107,13 +3060,13 @@ uint16_t WS2812FX::mode_drip(void)
if (drops[j].pos > 0) { // fall until end of segment if (drops[j].pos > 0) { // fall until end of segment
drops[j].pos += drops[j].vel; drops[j].pos += drops[j].vel;
if (drops[j].pos < 0) drops[j].pos = 0; 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 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 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 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 if (drops[j].colIndex > 2) { // during bounce, some water is on the floor
setPixelColor(0,color_blend(SEGCOLOR(0),BLACK,drops[j].col)); 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 * Tetris or Stacking (falling bricks) Effect
* by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home) * by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home)
*/ */
//12 bytes
typedef struct Tetris { typedef struct Tetris {
float pos; float pos;
float speed; float speed;
@ -3163,8 +3117,8 @@ uint16_t WS2812FX::mode_tetrix(void) {
} }
if (SEGENV.step == 0) { //init if (SEGENV.step == 0) { //init
drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>3)+1 : random8(6,40)); // set speed drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>2)+1 : random8(6,64)); // set speed
drop->pos = SEGLEN-1; // start at end of segment 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 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.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 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 / adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino
*/ */
uint16_t WS2812FX::mode_plasma(void) { uint16_t WS2812FX::mode_plasma(void) {
uint8_t thisPhase = beatsin8(6,-64,64); // Setting phase change for a couple of waves. // initialize phases on start
uint8_t thatPhase = beatsin8(7,-64,64); 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: 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. 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. + 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 thisBright = qsub8(colorIndex, beatsin8(7,0, (128 - (SEGMENT.intensity>>1))));
CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND); CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND);
setPixelColor(i, color.red, color.green, color.blue); 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 uint16_t scale = 15 + (SEGMENT.intensity >> 2); //default was 30
//#define scale 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 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
CRGBPalette16* palettes = reinterpret_cast<CRGBPalette16*>(SEGENV.data); CRGBPalette16* palettes = reinterpret_cast<CRGBPalette16*>(SEGENV.data);
@ -3644,7 +3602,7 @@ uint16_t WS2812FX::mode_chunchun(void)
return FRAMETIME; return FRAMETIME;
} }
//13 bytes
typedef struct Spotlight { typedef struct Spotlight {
float speed; float speed;
uint8_t colorIdx; uint8_t colorIdx;
@ -3661,6 +3619,11 @@ typedef struct Spotlight {
#define SPOT_TYPE_3X_DOT 4 #define SPOT_TYPE_3X_DOT 4
#define SPOT_TYPE_4X_DOT 5 #define SPOT_TYPE_4X_DOT 5
#define SPOT_TYPES_COUNT 6 #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. * Spotlights moving back and forth that cast dancing shadows.
@ -3671,7 +3634,7 @@ typedef struct Spotlight {
*/ */
uint16_t WS2812FX::mode_dancing_shadows(void) 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; bool initialize = SEGENV.aux0 != numSpotlights;
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 Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
*/ */
uint16_t WS2812FX::mode_blends(void) { 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 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data); uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128); uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
@ -3825,6 +3788,11 @@ uint16_t WS2812FX::mode_blends(void) {
return FRAMETIME; 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 { typedef struct TvSim {
uint32_t totalTime = 0; uint32_t totalTime = 0;
uint32_t fadeTime = 0; uint32_t fadeTime = 0;
@ -3845,11 +3813,6 @@ typedef struct TvSim {
uint16_t pb = 0; uint16_t pb = 0;
} tvSim; } 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 WS2812FX::mode_tv_simulator(void) {
uint16_t nr, ng, nb, r, g, b, i, hue; uint16_t nr, ng, nb, r, g, b, i, hue;
uint8_t sat, bri, j; uint8_t sat, bri, j;
@ -3957,10 +3920,15 @@ uint16_t WS2812FX::mode_tv_simulator(void) {
*/ */
//CONFIG //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_MAX_SPEED 6 //Higher number, higher speed
#define W_WIDTH_FACTOR 6 //Higher number, smaller waves #define W_WIDTH_FACTOR 6 //Higher number, smaller waves
//24 bytes
class AuroraWave { class AuroraWave {
private: private:
uint16_t ttl; uint16_t ttl;
@ -4055,10 +4023,10 @@ uint16_t WS2812FX::mode_aurora(void) {
if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) { if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
//Intensity slider changed or first call //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; 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 return mode_static(); //allocation failed
} }

View File

@ -24,8 +24,6 @@
Modified for WLED Modified for WLED
*/ */
#include "wled.h"
#ifndef WS2812FX_h #ifndef WS2812FX_h
#define 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 /* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266 #ifdef ESP8266
#define MAX_NUM_SEGMENTS 12 #define MAX_NUM_SEGMENTS 16
/* How many color transitions can run at once */ /* How many color transitions can run at once */
#define MAX_NUM_TRANSITIONS 8 #define MAX_NUM_TRANSITIONS 8
/* How much data bytes all segments combined may allocate */ /* How much data bytes all segments combined may allocate */
#define MAX_SEGMENT_DATA 2048 #define MAX_SEGMENT_DATA 3584
#else #else
#ifndef MAX_NUM_SEGMENTS #ifndef MAX_NUM_SEGMENTS
#define MAX_NUM_SEGMENTS 16 #define MAX_NUM_SEGMENTS 32
#endif #endif
#define MAX_NUM_TRANSITIONS 16 #define MAX_NUM_TRANSITIONS 24
#define MAX_SEGMENT_DATA 8192 #define MAX_SEGMENT_DATA 20480
#endif #endif
#define LED_SKIP_AMOUNT 1 #define LED_SKIP_AMOUNT 1
@ -257,6 +255,7 @@ class WS2812FX {
uint8_t grouping, spacing; uint8_t grouping, spacing;
uint8_t opacity; uint8_t opacity;
uint32_t colors[NUM_COLORS]; uint32_t colors[NUM_COLORS];
char *name;
bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed 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 (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;
if (c == colors[slot]) return false; if (c == colors[slot]) return false;
@ -296,19 +295,19 @@ class WS2812FX {
{ {
return ((options >> n) & 0x01); return ((options >> n) & 0x01);
} }
bool isSelected() inline bool isSelected()
{ {
return getOption(0); return getOption(0);
} }
bool isActive() inline bool isActive()
{ {
return stop > start; return stop > start;
} }
uint16_t length() inline uint16_t length()
{ {
return stop - start; return stop - start;
} }
uint16_t groupLength() inline uint16_t groupLength()
{ {
return grouping + spacing; return grouping + spacing;
} }
@ -345,17 +344,23 @@ class WS2812FX {
// segment runtime parameters // segment runtime parameters
typedef struct Segment_runtime { // 28 bytes typedef struct Segment_runtime { // 28 bytes
unsigned long next_time; unsigned long next_time; // millis() of next update
uint32_t step; uint32_t step; // custom "step" var
uint32_t call; uint32_t call; // call counter
uint16_t aux0; uint16_t aux0; // custom var
uint16_t aux1; uint16_t aux1; // custom var
byte* data = nullptr; byte* data = nullptr;
bool allocateData(uint16_t len){ bool allocateData(uint16_t len){
if (data && _dataLen == len) return true; //already allocated if (data && _dataLen == len) return true; //already allocated
deallocateData(); deallocateData();
if (WS2812FX::instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory 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 if (!data) return false; //allocation failed
WS2812FX::instance->_usedSegmentData += len; WS2812FX::instance->_usedSegmentData += len;
_dataLen = len; _dataLen = len;
@ -363,7 +368,7 @@ class WS2812FX {
return true; return true;
} }
void deallocateData(){ void deallocateData(){
delete[] data; free(data);
data = nullptr; data = nullptr;
WS2812FX::instance->_usedSegmentData -= _dataLen; WS2812FX::instance->_usedSegmentData -= _dataLen;
_dataLen = 0; _dataLen = 0;
@ -389,7 +394,7 @@ class WS2812FX {
* the internal segment state should be reset. * the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function. * Call resetIfRequired before calling the next effect function.
*/ */
void reset() { _requiresReset = true; } inline void reset() { _requiresReset = true; }
private: private:
uint16_t _dataLen = 0; uint16_t _dataLen = 0;
bool _requiresReset = false; bool _requiresReset = false;
@ -605,7 +610,7 @@ class WS2812FX {
} }
void void
finalizeInit(uint16_t countPixels), finalizeInit(void),
service(void), service(void),
blur(uint8_t), blur(uint8_t),
fill(uint32_t), fill(uint32_t),
@ -622,11 +627,12 @@ class WS2812FX {
trigger(void), trigger(void),
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0), setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0),
resetSegments(), resetSegments(),
populateDefaultSegments(),
setPixelColor(uint16_t n, uint32_t c), setPixelColor(uint16_t n, uint32_t c),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
show(void), show(void),
setColorOrder(uint8_t co), setPixelSegment(uint8_t n),
setPixelSegment(uint8_t n); deserializeMap(uint8_t n=0);
bool bool
isRgbw = false, isRgbw = false,
@ -644,8 +650,6 @@ class WS2812FX {
paletteFade = 0, paletteFade = 0,
paletteBlend = 0, paletteBlend = 0,
milliampsPerLed = 55, milliampsPerLed = 55,
// getStripType(uint8_t strip=0),
// setStripType(uint8_t type, uint8_t strip=0),
getBrightness(void), getBrightness(void),
getMode(void), getMode(void),
getSpeed(void), getSpeed(void),
@ -654,24 +658,17 @@ class WS2812FX {
getMaxSegments(void), getMaxSegments(void),
//getFirstSelectedSegment(void), //getFirstSelectedSegment(void),
getMainSegmentId(void), getMainSegmentId(void),
getColorOrder(void),
gamma8(uint8_t), gamma8(uint8_t),
gamma8_cal(uint8_t, float), gamma8_cal(uint8_t, float),
sin_gap(uint16_t), sin_gap(uint16_t),
get_random_wheel_index(uint8_t); get_random_wheel_index(uint8_t);
int8_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); tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec);
uint16_t uint16_t
ablMilliampsMax, ablMilliampsMax,
currentMilliamps, currentMilliamps,
// setStripLen(uint8_t strip, uint16_t len),
// getStripLen(uint8_t strip=0),
triwave16(uint16_t), triwave16(uint16_t),
getFps(); getFps();
@ -849,7 +846,6 @@ class WS2812FX {
color_wipe(bool, bool), color_wipe(bool, bool),
dynamic(bool), dynamic(bool),
scan(bool), scan(bool),
theater_chase(uint32_t, uint32_t, bool),
running_base(bool,bool), running_base(bool,bool),
larson_scanner(bool), larson_scanner(bool),
sinelon_base(bool,bool), sinelon_base(bool,bool),
@ -857,8 +853,8 @@ class WS2812FX {
chase(uint32_t, uint32_t, uint32_t, bool), chase(uint32_t, uint32_t, uint32_t, bool),
gradient_base(bool), gradient_base(bool),
ripple_base(bool), ripple_base(bool),
police_base(uint32_t, uint32_t, bool), police_base(uint32_t, uint32_t, uint16_t),
running(uint32_t, uint32_t), running(uint32_t, uint32_t, bool theatre=false),
tricolor_chase(uint32_t, uint32_t), tricolor_chase(uint32_t, uint32_t),
twinklefox_base(bool), twinklefox_base(bool),
spots_base(uint16_t), spots_base(uint16_t),
@ -869,8 +865,7 @@ class WS2812FX {
void void
blendPixelColor(uint16_t n, uint32_t color, uint8_t blend), 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), startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot);
deserializeMap(void);
uint16_t* customMappingTable = nullptr; uint16_t* customMappingTable = nullptr;
uint16_t customMappingSize = 0; uint16_t customMappingSize = 0;

View File

@ -23,7 +23,7 @@
Modified heavily for WLED Modified heavily for WLED
*/ */
#include "wled.h"
#include "FX.h" #include "FX.h"
#include "palettes.h" #include "palettes.h"
@ -40,7 +40,7 @@
another example. Switches direction every 5 LEDs. another example. Switches direction every 5 LEDs.
{"map":[ {"map":[
0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, 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 //factory defaults LED setup
@ -49,7 +49,7 @@
//#define DEFAULT_LED_TYPE TYPE_WS2812_RGB //#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
#ifndef PIXEL_COUNTS #ifndef PIXEL_COUNTS
#define PIXEL_COUNTS DEFAULT_LED_COUNT #define PIXEL_COUNTS 30
#endif #endif
#ifndef DATA_PINS #ifndef DATA_PINS
@ -65,25 +65,22 @@
#endif #endif
//do not call this method from system context (network callback) //do not call this method from system context (network callback)
void WS2812FX::finalizeInit(uint16_t countPixels) void WS2812FX::finalizeInit(void)
{ {
RESET_RUNTIME; 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) { if (busses.getNumBusses() == 0) {
const uint8_t defDataPins[] = {DATA_PINS}; const uint8_t defDataPins[] = {DATA_PINS};
const uint16_t defCounts[] = {PIXEL_COUNTS}; const uint16_t defCounts[] = {PIXEL_COUNTS};
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0])); const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0])); // min 1
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); // min 1
uint16_t prevLen = 0; 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]}; uint8_t defPin[] = {defDataPins[i]};
uint16_t start = prevLen; uint16_t start = prevLen;
uint16_t count = _length; uint16_t count = (i < defNumCounts) ? defCounts[i] : defCounts[i>0?i-1:0];
if (defNumBusses > 1 && defNumCounts) {
count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
}
prevLen += count; prevLen += count;
BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, COL_ORDER_GRB); BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, COL_ORDER_GRB);
busses.add(defCfg); busses.add(defCfg);
@ -92,65 +89,33 @@ void WS2812FX::finalizeInit(uint16_t countPixels)
deserializeMap(); deserializeMap();
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0}; _length = 0;
uint16_t segStops [MAX_NUM_SEGMENTS] = {0}; for (uint8_t i=0; i<busses.getNumBusses(); i++) {
Bus *bus = busses.getBus(i);
if (bus == nullptr) continue;
if (_length+bus->getLength() > 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); setBrightness(_brightness);
//TODO make sure segments are only refreshed when bus config actually changed (new settings page) #ifdef ESP8266
uint8_t s = 0;
for (uint8_t i = 0; i < busses.getNumBusses(); i++) { for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
Bus* b = busses.getBus(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; if ((!IS_DIGITAL(b->getType()) || IS_2PIN(b->getType()))) continue;
uint8_t pins[5]; uint8_t pins[5];
b->getPins(pins); b->getPins(pins);
BusDigital* bd = static_cast<BusDigital*>(b); BusDigital* bd = static_cast<BusDigital*>(b);
if (pins[0] == 3) bd->reinit(); 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() { void WS2812FX::service() {
@ -218,14 +183,13 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
int16_t realIndex = iGroup; int16_t realIndex = iGroup;
if (IS_REVERSE) { if (IS_REVERSE) {
if (IS_MIRROR) { 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 { } else {
realIndex = SEGMENT.length() - iGroup - 1; realIndex = (SEGMENT.length() - 1) - iGroup;
} }
} }
realIndex += SEGMENT.start; realIndex += SEGMENT.start;
return realIndex; return realIndex;
} }
@ -246,7 +210,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
} }
if (SEGLEN) {//from segment if (SEGLEN) {//from segment
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments) //color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
if (_bri_t < 255) { if (_bri_t < 255) {
r = scale8(r, _bri_t); 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)); 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 realIndex = realPixelIndex(i);
uint16_t len = SEGMENT.length(); uint16_t len = SEGMENT.length();
for (uint16_t j = 0; j < SEGMENT.grouping; j++) { 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 (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) {
if (IS_MIRROR) { //set the corresponding mirrored pixel if (IS_MIRROR) { //set the corresponding mirrored pixel
uint16_t indexMir = SEGMENT.stop - indexSet + SEGMENT.start - 1; 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); busses.setPixelColor(indexMir, col);
} }
/* offset/phase */ /* offset/phase */
indexSet += SEGMENT.offset; indexSet += SEGMENT.offset;
if (indexSet >= SEGMENT.stop) indexSet -= len; if (indexSet >= SEGMENT.stop) indexSet -= len;
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet]; if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
busses.setPixelColor(indexSet, col); 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. } else { //live data, etc.
if (i < customMappingSize) i = customMappingTable[i]; if (i < customMappingSize) i = customMappingTable[i];
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b)); uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
busses.setPixelColor(i, col); busses.setPixelColor(i, col);
} }
@ -563,7 +525,9 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
} }
if (i < customMappingSize) i = customMappingTable[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); return busses.getPixelColor(i);
} }
@ -584,14 +548,14 @@ uint32_t WS2812FX::getLastShow(void) {
return _lastShow; return _lastShow;
} }
//TODO these need to be on a per-strip basis // there is no longer any need for these two
uint8_t WS2812FX::getColorOrder(void) { //uint8_t WS2812FX::getColorOrder(void) {
return COL_ORDER_GRB; // return COL_ORDER_GRB;
} //}
//
void WS2812FX::setColorOrder(uint8_t co) { //void WS2812FX::setColorOrder(uint8_t co) {
//bus->SetColorOrder(co); // //bus->SetColorOrder(co);
} //}
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) { void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) {
if (n >= MAX_NUM_SEGMENTS) return; 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 (seg.stop) setRange(seg.start, seg.stop -1, 0); //turn old segment range off
if (i2 <= i1) //disable segment 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 if (n == mainSegment) //if main segment is deleted, set first active as main segment
{ {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) 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() { void WS2812FX::resetSegments() {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete _segments[i].name;
mainSegment = 0; mainSegment = 0;
memset(_segments, 0, sizeof(_segments)); memset(_segments, 0, sizeof(_segments));
//memset(_segment_runtimes, 0, sizeof(_segment_runtimes)); //memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
@ -656,6 +625,38 @@ void WS2812FX::resetSegments() {
_segment_runtimes[0].reset(); _segment_runtimes[0].reset();
} }
void WS2812FX::populateDefaultSegments() {
uint16_t length = 0;
if (autoSegments) {
for (uint8_t i=0; i<busses.getNumBusses(); i++) {
Bus *bus = busses.getBus(i);
if (bus == nullptr) continue;
_segments[i].start = bus->getStart();
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) //After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
void WS2812FX::setPixelSegment(uint8_t n) 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 //load custom mapping table from JSON file
void WS2812FX::deserializeMap(void) { void WS2812FX::deserializeMap(uint8_t n) {
if (!WLED_FS.exists("/ledmap.json")) return; 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 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(fileName.c_str(), nullptr, &doc)) return; //if file does not exist just exit
if (!readObjectFromFile("/ledmap.json", nullptr, &doc)) return; //if file does not exist just exit
// erase old custom ledmap
if (customMappingTable != nullptr) { if (customMappingTable != nullptr) {
customMappingSize = 0;
delete[] customMappingTable; delete[] customMappingTable;
customMappingTable = nullptr; customMappingTable = nullptr;
customMappingSize = 0;
} }
JsonArray map = doc[F("map")]; JsonArray map = doc[F("map")];

View File

@ -1,5 +1,7 @@
#include "wled.h" #include "wled.h"
#ifndef WLED_DISABLE_BLYNK
#include "src/dependencies/blynk/Blynk/BlynkHandlers.h" #include "src/dependencies/blynk/Blynk/BlynkHandlers.h"
#endif
/* /*
* Remote light control with the free Blynk app * Remote light control with the free Blynk app

View File

@ -10,18 +10,24 @@
#include "bus_wrapper.h" #include "bus_wrapper.h"
#include <Arduino.h> #include <Arduino.h>
#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 //temporary struct for passing bus configuration to bus
struct BusConfig { struct BusConfig {
uint8_t type = TYPE_WS2812_RGB; uint8_t type = TYPE_WS2812_RGB;
uint16_t count = 1; uint16_t count;
uint16_t start = 0; uint16_t start;
uint8_t colorOrder = COL_ORDER_GRB; uint8_t colorOrder;
bool reversed = false; bool reversed;
uint8_t skipAmount; uint8_t skipAmount;
bool rgbwOverride;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; 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) { 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; rgbwOverride = (bool) GET_BIT(busType,7);
colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; 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; uint8_t nPins = 1;
if (type > 47) nPins = 2; if (type > 47) nPins = 2;
else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type); else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type);
@ -74,7 +80,7 @@ class Bus {
} }
virtual uint16_t getLength() { virtual uint16_t getLength() {
return 1; return 1; // is this ok? shouldn't it be 0 in virtual function?
} }
virtual void setColorOrder() {} virtual void setColorOrder() {}
@ -130,12 +136,12 @@ class BusDigital : public Bus {
reversed = bc.reversed; reversed = bc.reversed;
_skip = bc.skipAmount; //sacrificial pixels _skip = bc.skipAmount; //sacrificial pixels
_len = bc.count + _skip; _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; if (_iType == I_NONE) return;
_busPtr = PolyBus::create(_iType, _pins, _len, nr); _busPtr = PolyBus::create(_iType, _pins, _len, nr);
_valid = (_busPtr != nullptr); _valid = (_busPtr != nullptr);
_colorOrder = bc.colorOrder; _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() { inline void show() {
@ -189,7 +195,7 @@ class BusDigital : public Bus {
} }
inline bool isRgbw() { inline bool isRgbw() {
return (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814); return (_rgbw || _type == TYPE_SK6812_RGBW || _type == TYPE_TM1814);
} }
inline uint8_t skippedLeds() { inline uint8_t skippedLeds() {
@ -201,7 +207,6 @@ class BusDigital : public Bus {
} }
void cleanup() { void cleanup() {
//Serial.println("Digital Cleanup");
PolyBus::cleanup(_busPtr, _iType); PolyBus::cleanup(_busPtr, _iType);
_iType = I_NONE; _iType = I_NONE;
_valid = false; _valid = false;
@ -220,6 +225,7 @@ class BusDigital : public Bus {
uint8_t _iType = I_NONE; uint8_t _iType = I_NONE;
uint16_t _len = 0; uint16_t _len = 0;
uint8_t _skip = 0; uint8_t _skip = 0;
bool _rgbw = false;
void * _busPtr = nullptr; void * _busPtr = nullptr;
}; };
@ -227,6 +233,7 @@ class BusDigital : public Bus {
class BusPwm : public Bus { class BusPwm : public Bus {
public: public:
BusPwm(BusConfig &bc) : Bus(bc.type, bc.start) { BusPwm(BusConfig &bc) : Bus(bc.type, bc.start) {
_valid = false;
if (!IS_PWM(bc.type)) return; if (!IS_PWM(bc.type)) return;
uint8_t numPins = NUM_PWM_PINS(bc.type); uint8_t numPins = NUM_PWM_PINS(bc.type);
@ -280,10 +287,12 @@ class BusPwm : public Bus {
//does no index check //does no index check
uint32_t getPixelColor(uint16_t pix) { uint32_t getPixelColor(uint16_t pix) {
if (!_valid) return 0;
return ((_data[3] << 24) | (_data[0] << 16) | (_data[1] << 8) | (_data[2])); return ((_data[3] << 24) | (_data[0] << 16) | (_data[1] << 8) | (_data[2]));
} }
void show() { void show() {
if (!_valid) return;
uint8_t numPins = NUM_PWM_PINS(_type); uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) { for (uint8_t i = 0; i < numPins; i++) {
uint8_t scaled = (_data[i] * _bri) / 255; uint8_t scaled = (_data[i] * _bri) / 255;
@ -302,7 +311,9 @@ class BusPwm : public Bus {
uint8_t getPins(uint8_t* pinArray) { uint8_t getPins(uint8_t* pinArray) {
uint8_t numPins = NUM_PWM_PINS(_type); 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; return numPins;
} }
@ -328,13 +339,13 @@ class BusPwm : public Bus {
void deallocatePins() { void deallocatePins() {
uint8_t numPins = NUM_PWM_PINS(_type); uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) { for (uint8_t i = 0; i < numPins; i++) {
pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
if (!pinManager.isPinOk(_pins[i])) continue; if (!pinManager.isPinOk(_pins[i])) continue;
#ifdef ESP8266 #ifdef ESP8266
digitalWrite(_pins[i], LOW); //turn off PWM interrupt digitalWrite(_pins[i], LOW); //turn off PWM interrupt
#else #else
if (_ledcStart < 16) ledcDetachPin(_pins[i]); if (_ledcStart < 16) ledcDetachPin(_pins[i]);
#endif #endif
pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
} }
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
pinManager.deallocateLedc(_ledcStart, numPins); 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 { class BusManager {
public: public:
BusManager() { BusManager() {
@ -352,7 +443,9 @@ class BusManager {
static uint32_t memUsage(BusConfig &bc) { static uint32_t memUsage(BusConfig &bc) {
uint8_t type = bc.type; uint8_t type = bc.type;
uint16_t len = bc.count; uint16_t len = bc.count;
if (type < 32) { if (type < 4) {
return len * (type+1);
} else if (type < 32) {
#ifdef ESP8266 #ifdef ESP8266
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
if (type > 29) return len*20; //RGBW if (type > 29) return len*20; //RGBW
@ -373,7 +466,9 @@ class BusManager {
int add(BusConfig &bc) { int add(BusConfig &bc) {
if (numBusses >= WLED_MAX_BUSSES) return -1; 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); busses[numBusses] = new BusDigital(bc, numBusses);
} else { } else {
busses[numBusses] = new BusPwm(bc); busses[numBusses] = new BusPwm(bc);
@ -444,6 +539,7 @@ class BusManager {
return len; return len;
} }
// a workaround
static inline bool isRgbw(uint8_t type) { static inline bool isRgbw(uint8_t type) {
return Bus::isRgbw(type); return Bus::isRgbw(type);
} }

View File

@ -383,11 +383,6 @@ class PolyBus {
uint8_t w = c >> 24; uint8_t w = c >> 24;
RgbwColor col; 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 //reorder channels to selected order
switch (co) switch (co)
{ {
@ -559,10 +554,6 @@ class PolyBus {
case I_SS_P98_3: col = (static_cast<B_SS_P98_3*>(busPtr))->GetPixelColor(pix); break; case I_SS_P98_3: col = (static_cast<B_SS_P98_3*>(busPtr))->GetPixelColor(pix); break;
} }
#ifdef COLOR_ORDER_OVERRIDE
if (pix >= COO_MIN && pix < COO_MAX) co = COO_ORDER;
#endif
switch (co) switch (co)
{ {
// W G R B // W G R B
@ -632,14 +623,17 @@ class PolyBus {
} }
//gives back the internal type index (I_XX_XXX_X above) for the input //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_DIGITAL(busType)) return I_NONE;
if (IS_2PIN(busType)) { //SPI LED chips if (IS_2PIN(busType)) { //SPI LED chips
bool isHSPI = false; bool isHSPI = false;
#ifdef ESP8266 #ifdef ESP8266
if (pins[0] == P_8266_HS_MOSI && pins[1] == P_8266_HS_CLK) isHSPI = true; if (pins[0] == P_8266_HS_MOSI && pins[1] == P_8266_HS_CLK) isHSPI = true;
#else #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 #endif
uint8_t t = I_NONE; uint8_t t = I_NONE;
switch (busType) { switch (busType) {
@ -658,9 +652,8 @@ class PolyBus {
switch (busType) { switch (busType) {
case TYPE_WS2812_RGB: case TYPE_WS2812_RGB:
case TYPE_WS2812_WWA: case TYPE_WS2812_WWA:
return I_8266_U0_NEO_3 + offset;
case TYPE_SK6812_RGBW: 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: case TYPE_WS2811_400KHZ:
return I_8266_U0_400_3 + offset; return I_8266_U0_400_3 + offset;
case TYPE_TM1814: case TYPE_TM1814:
@ -678,9 +671,10 @@ class PolyBus {
switch (busType) { switch (busType) {
case TYPE_WS2812_RGB: case TYPE_WS2812_RGB:
case TYPE_WS2812_WWA: case TYPE_WS2812_WWA:
return I_32_RN_NEO_3 + offset; // return I_32_RN_NEO_3 + offset;
case TYPE_SK6812_RGBW: 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: case TYPE_WS2811_400KHZ:
return I_32_RN_400_3 + offset; return I_32_RN_400_3 + offset;
case TYPE_TM1814: case TYPE_TM1814:

View File

@ -10,10 +10,11 @@ static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize fl
void shortPressAction(uint8_t b) void shortPressAction(uint8_t b)
{ {
if (!macroButton[b]) if (!macroButton[b]) {
{ switch (b) {
toggleOnOff(); case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break;
colorUpdated(CALL_MODE_BUTTON); default: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break;
}
} else { } else {
applyPreset(macroButton[b], CALL_MODE_BUTTON); 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) bool isButtonPressed(uint8_t i)
{ {
if (btnPin[i]<0) return false; if (btnPin[i]<0) return false;
@ -186,61 +225,43 @@ void handleButton()
} }
//momentary button logic //momentary button logic
if (isButtonPressed(b)) //pressed if (isButtonPressed(b)) { //pressed
{
if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis(); if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis();
buttonPressedBefore[b] = true; buttonPressedBefore[b] = true;
if (millis() - buttonPressedTime[b] > 600) //long press if (millis() - buttonPressedTime[b] > 600) { //long press
{ if (!buttonLongPressed[b]) longPressAction(b);
if (!buttonLongPressed[b]) else if (b) { // repeatable action (~3 times per s) on button > 0
{ longPressAction(b);
if (macroLongPress[b]) {applyPreset(macroLongPress[b], CALL_MODE_BUTTON);} buttonPressedTime[b] = millis() - 300; // 300ms
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;
} }
buttonLongPressed[b] = true;
} }
}
else if (!isButtonPressed(b) && buttonPressedBefore[b]) //released } else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
{
long dur = millis() - buttonPressedTime[b]; long dur = millis() - buttonPressedTime[b];
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} //too short "press", debounce 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; 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); WLED::instance().initAP(true);
} } else if (!buttonLongPressed[b]) { //short press
else if (!buttonLongPressed[b]) { //short press // if this is second release within 350ms it is a double press (buttonWaitTime!=0)
if (macroDoublePress[b]) if (doublePress) {
{ doublePressAction(b);
if (doublePress) { } else {
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON); buttonWaitTime[b] = millis();
}
// 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);
} }
buttonPressedBefore[b] = false; buttonPressedBefore[b] = false;
buttonLongPressed[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; buttonWaitTime[b] = 0;
shortPressAction(b); shortPressAction(b);
} }

View File

@ -1,4 +1,5 @@
#include "wled.h" #include "wled.h"
#include "wled_ethernet.h"
/* /*
* Serializes and parses the cfg.json and wsec.json settings files, stored in internal FS. * 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(cmDNS, id[F("mdns")], 33);
getStringFromJson(serverDescription, id[F("name")], 33); getStringFromJson(serverDescription, id[F("name")], 33);
getStringFromJson(alexaInvocationName, id[F("inv")], 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]; JsonObject nw_ins_0 = doc["nw"]["ins"][0];
getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33); getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33);
@ -60,7 +64,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(apBehavior, ap[F("behav")]); CJSON(apBehavior, ap[F("behav")]);
/* /*
JsonArray ap_ip = ap["ip"]; JsonArray ap_ip = ap["ip"];
for (byte i = 0; i < 4; i++) { for (byte i = 0; i < 4; i++) {
@ -79,16 +82,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(ledCount, hw_led[F("total")]); CJSON(ledCount, hw_led[F("total")]);
if (ledCount > MAX_LEDS) ledCount = MAX_LEDS; if (ledCount > MAX_LEDS) ledCount = MAX_LEDS;
uint16_t lC = 0;
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]); CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);
JsonArray ins = hw_led["ins"]; JsonArray ins = hw_led["ins"];
if (fromFS || !ins.isNull()) { if (fromFS || !ins.isNull()) {
uint8_t s = 0; //bus iterator uint8_t s = 0; // bus iterator
strip.isRgbw = false;
strip.isOffRefreshRequred = false;
busses.removeAll(); busses.removeAll();
uint32_t mem = 0; uint32_t mem = 0;
for (JsonObject elm : ins) { for (JsonObject elm : ins) {
@ -104,12 +107,20 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
} }
uint16_t length = elm[F("len")] | 1; 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 colorOrder = (int)elm[F("order")];
uint8_t skipFirst = elm[F("skip")]; uint8_t skipFirst = elm[F("skip")];
uint16_t start = elm[F("start")] | 0; uint16_t start = elm[F("start")] | 0;
if (start > lC+length) continue; // something is very wrong :)
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB; uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"]; 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); BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
if (bc.adjustBounds(ledCount)) { if (bc.adjustBounds(ledCount)) {
//RGBW mode is enabled if at least one of the strips is RGBW //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); mem += busses.memUsage(bc);
if (mem <= MAX_LED_MEMORY) busses.add(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 if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
// read multiple button configuration // read multiple button configuration
@ -461,6 +474,7 @@ void serializeConfig() {
id[F("mdns")] = cmDNS; id[F("mdns")] = cmDNS;
id[F("name")] = serverDescription; id[F("name")] = serverDescription;
id[F("inv")] = alexaInvocationName; id[F("inv")] = alexaInvocationName;
id[F("sui")] = simplifiedUI;
JsonObject nw = doc.createNestedObject("nw"); JsonObject nw = doc.createNestedObject("nw");
@ -500,6 +514,25 @@ void serializeConfig() {
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
JsonObject ethernet = doc.createNestedObject("eth"); JsonObject ethernet = doc.createNestedObject("eth");
ethernet["type"] = ethernetType; ethernet["type"] = ethernetType;
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
JsonArray pins = ethernet.createNestedArray("pin");
for (uint8_t p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) pins.add(esp32_nonconfigurable_ethernet_pins[p].pin);
if (ethernetBoards[ethernetType].eth_power>=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 #endif
JsonObject hw = doc.createNestedObject("hw"); JsonObject hw = doc.createNestedObject("hw");
@ -526,6 +559,7 @@ void serializeConfig() {
ins["rev"] = bus->reversed; ins["rev"] = bus->reversed;
ins[F("skip")] = bus->skippedLeds(); ins[F("skip")] = bus->skippedLeds();
ins["type"] = bus->getType(); ins["type"] = bus->getType();
ins[F("rgbw")] = bus->isRgbw();
} }
// button(s) // button(s)
@ -550,7 +584,7 @@ void serializeConfig() {
JsonObject hw_ir = hw.createNestedObject("ir"); JsonObject hw_ir = hw.createNestedObject("ir");
hw_ir["pin"] = irPin; 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")); JsonObject hw_relay = hw.createNestedObject(F("relay"));
hw_relay["pin"] = rlyPin; hw_relay["pin"] = rlyPin;

View File

@ -122,6 +122,8 @@
#define TYPE_NONE 0 //light is not configured #define TYPE_NONE 0 //light is not configured
#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light #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) //Digital types (data pin only) (16-31)
#define TYPE_WS2812_1CH 20 //white-only chips #define TYPE_WS2812_1CH 20 //white-only chips
#define TYPE_WS2812_WWA 21 //amber + warm + cold white #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 // 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 #ifndef MAX_LEDS
#ifdef ESP8266 #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 #else
#define MAX_LEDS 8192 #define MAX_LEDS 8192
#endif #endif
@ -241,14 +243,14 @@
#ifndef MAX_LED_MEMORY #ifndef MAX_LED_MEMORY
#ifdef ESP8266 #ifdef ESP8266
#define MAX_LED_MEMORY 5000 #define MAX_LED_MEMORY 4000
#else #else
#define MAX_LED_MEMORY 64000 #define MAX_LED_MEMORY 64000
#endif #endif
#endif #endif
#ifndef MAX_LEDS_PER_BUS #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 #endif
// string temp buffer (now stored in stack locally) // string temp buffer (now stored in stack locally)
@ -282,7 +284,7 @@
// Maximum size of node map (list of other WLED instances) // Maximum size of node map (list of other WLED instances)
#ifdef ESP8266 #ifdef ESP8266
#define WLED_MAX_NODES 15 #define WLED_MAX_NODES 24
#else #else
#define WLED_MAX_NODES 150 #define WLED_MAX_NODES 150
#endif #endif
@ -304,8 +306,4 @@
#endif #endif
#endif #endif
#ifndef DEFAULT_LED_COUNT
#define DEFAULT_LED_COUNT 30
#endif
#endif #endif

File diff suppressed because one or more lines are too long

View File

@ -23,10 +23,10 @@
<button id="buttonNl" onclick="toggleNl()"><i class="icons">&#xe2a2;</i><p class="tab-label">Timer</p></button> <button id="buttonNl" onclick="toggleNl()"><i class="icons">&#xe2a2;</i><p class="tab-label">Timer</p></button>
<button id="buttonSync" onclick="toggleSync()"><i class="icons">&#xe116;</i><p class="tab-label">Sync</p></button> <button id="buttonSync" onclick="toggleSync()"><i class="icons">&#xe116;</i><p class="tab-label">Sync</p></button>
<button id="buttonSr" onclick="toggleLiveview()"><i class="icons">&#xe410;</i><p class="tab-label">Peek</p></button> <button id="buttonSr" onclick="toggleLiveview()"><i class="icons">&#xe410;</i><p class="tab-label">Peek</p></button>
<button id="buttonI" onclick="toggleInfo()"><i class="icons">&#xe066;</i><p class="tab-label">Info</p></button> <button id="buttonI" onclick="toggleInfo()"><i class="icons">&#xe066;</i><p class="tab-label">Info</p></button>
<button id="buttonNodes" onclick="toggleNodes()"><i class="icons">&#xe22d;</i><p class="tab-label">Nodes</p></button></div> <button id="buttonNodes" onclick="toggleNodes()"><i class="icons">&#xe22d;</i><p class="tab-label">Nodes</p></button>
<button onclick="window.location.href = '/settings';"><i class="icons">&#xe0a2;</i><p class="tab-label">Config</p></button> <button onclick="window.location.href='/settings';"><i class="icons">&#xe0a2;</i><p class="tab-label">Config</p></button>
<button id="buttonPcm" onclick="togglePcMode(true)"><i class="icons">&#xe23d;</i><p class="tab-label">PC Mode</p></button> <button id="buttonPcm" onclick="togglePcMode(true)"><i class="icons">&#xe23d;</i><p class="tab-label">PC Mode</p></button>
</div> </div>
<div id="briwrap"> <div id="briwrap">
<p class="hd">Brightness</p> <p class="hd">Brightness</p>
@ -36,6 +36,7 @@
<input id="sliderBri" onchange="setBri()" oninput="updateTrail(this)" max="255" min="1" type="range" value="128" /> <input id="sliderBri" onchange="setBri()" oninput="updateTrail(this)" max="255" min="1" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<output class="sliderbubble"></output>
</div> </div>
</div> </div>
<iframe id="liveview" src="about:blank"></iframe> <iframe id="liveview" src="about:blank"></iframe>
@ -46,17 +47,26 @@
<div id="Colors" class="tabcontent"> <div id="Colors" class="tabcontent">
<div id="picker" class="noslide"></div> <div id="picker" class="noslide"></div>
<div id="rgbwrap"> <div id="rgbwrap">
<div class="sliderwrap il"> <div id="rwrap" class="il">
<input id="sliderR" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,1)" max="255" min="0" type="range" value="128" /> <div class="sliderwrap il">
<div class="sliderdisplay"></div> <input id="sliderR" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,1)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div><br> </div><br>
<div class="sliderwrap il"> <div id="gwrap" class="il">
<input id="sliderG" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,2)" max="255" min="0" type="range" value="128" /> <div class="sliderwrap il">
<div class="sliderdisplay"></div> <input id="sliderG" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,2)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div><br> </div><br>
<div class="sliderwrap il"> <div id="bwrap" class="il">
<input id="sliderB" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,3)" max="255" min="0" type="range" value="128" /> <div class="sliderwrap il">
<div class="sliderdisplay"></div> <input id="sliderB" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,3)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div><br> </div><br>
</div> </div>
<div id="wwrap"> <div id="wwrap">
@ -64,6 +74,7 @@
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderW" class="noslide" onchange="setColor(0)" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" /> <input id="sliderW" class="noslide" onchange="setColor(0)" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
<output class="sliderbubble"></output>
</div> </div>
</div> </div>
<div id="qcs-w"> <div id="qcs-w">
@ -77,28 +88,30 @@
<div class="qcs" onclick="pC('#0000ff');" title="Blue" style="background-color:#0000ff;"></div> <div class="qcs" onclick="pC('#0000ff');" title="Blue" style="background-color:#0000ff;"></div>
<div class="qcs" onclick="pC('#00ffc8');" title="Cyan" style="background-color:#00ffc8;"></div> <div class="qcs" onclick="pC('#00ffc8');" title="Cyan" style="background-color:#00ffc8;"></div>
<div class="qcs" onclick="pC('#08ff00');" title="Green" style="background-color:#08ff00;"></div> <div class="qcs" onclick="pC('#08ff00');" title="Green" style="background-color:#08ff00;"></div>
<div class="qcs" onclick="pC('rnd');" title="Random" style="background-color:var(--c-3); padding: 4px 8px; transform: translateY(-10px);">R</div> <div class="qcs" onclick="pC('rnd');" title="Random" style="background:linear-gradient(to right, red, orange, yellow, green, blue, purple);transform: translateY(-11px);">R</div>
</div> </div>
<div id="csl"> <div id="csl">
<button class="xxs cl btn" onclick="selectSlot(0);">1</button> <button class="btn xxs" onclick="selectSlot(0);">1</button>
<button class="xxs cl btn" onclick="selectSlot(1);">2</button> <button class="btn xxs" onclick="selectSlot(1);">2</button>
<button class="xxs cl btn" onclick="selectSlot(2);">3</button> <button class="btn xxs" onclick="selectSlot(2);">3</button>
</div> </div>
<div id="hexw"> <div id="hexw">
<input id="hexc" type="text" class="noslide" onkeydown="hexEnter(this)" autocomplete="off" maxlength="8" /> <input id="hexc" type="text" class="noslide" onkeydown="hexEnter(this)" autocomplete="off" maxlength="8" />
<button id="hexcnf" class="xxs btn" onclick="fromHex();"><i class="icons btna-icon">&#xe390;</i></button> <button id="hexcnf" class="btn btn-xs" onclick="fromHex();"><i class="icons btn-icon">&#xe390;</i></button>
</div> </div>
<p class="labels"> <p class="labels"><i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i> Color palette</p>
<i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i>
Color palette
</p>
<div class="il"> <div class="il">
<div class="staytop fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'pallist')" onfocus="search(this,'pallist')" />
<i class="icons clear-icon" onclick="clean(this)">&#xe38f;</i>
<i class="icons search-icon">&#xe0a1;</i>
</div>
<div id="pallist" class="list"> <div id="pallist" class="list">
<div class="lstI" data-id="0"> <div class="lstI" data-id="0">
<label class="check schkl"> <label class="radio schkl">
&nbsp; &nbsp;
<input type="radio" value="${palettes[i].id}" name="palette" onChange="setPalette()"> <input type="radio" value="${palettes[i].id}" name="palette" onChange="setPalette()">
<span class="checkmark schk"></span> <span class="radiomark schk"></span>
</label> </label>
<div class="lstIcontent"> <div class="lstIcontent">
<span class="lstIname"> <span class="lstIname">
@ -120,26 +133,33 @@
<div id="Effects" class="tabcontent"> <div id="Effects" class="tabcontent">
<p class="labels">Effect speed</p> <p class="labels">Effect speed</p>
<div class="staytop"> <div class="staytop" id="staytop">
<i class="icons slider-icon">&#xe325;</i> <i class="icons slider-icon">&#xe325;</i>
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" /> <input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<output class="sliderbubble hidden"></output>
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<output class="sliderbubble"></output>
</div> </div>
<p class="labels">Effect intensity</p> <p class="labels">Effect intensity</p>
<div class="staytop" id="staytop1"> <div class="staytop" id="staytop1">
<i class="icons slider-icon" onclick="tglLabels()">&#xe409;</i> <i class="icons slider-icon" onclick="tglLabels()">&#xe409;</i>
<div class="sliderwrap il"> <div class="sliderwrap il">
<input id="sliderIntensity" class="noslide" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" /> <input id="sliderIntensity" class="noslide" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<output class="sliderbubble hidden"></output>
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
<output class="sliderbubble"></output>
</div> </div>
<p class="labels">Effect mode</p> <div class="il">
<div id="fxlist" class="list"> <p class="labels">Effect mode</p>
Loading... <div class="staytop fnd" id="staytop2">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this,'fxlist')" />
<i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i>
<i class="icons search-icon">&#xe0a1;</i>
</div>
<div id="fxlist" class="list">
Loading...
</div>
</div> </div>
</div> </div>
@ -147,21 +167,18 @@
<div id="segcont"> <div id="segcont">
Loading... Loading...
</div> </div>
<div id="segutil"> <div id="segutil" class="staybot">
</div> </div>
<div id="segutil2"> <div id="segutil2">
<button class="btn btn-s" id="rsbtn" onclick="rSegs()">Reset segments</button> <button class="btn btn-s" id="rsbtn" onclick="rSegs()">Reset segments</button>
</div> </div>
<p>Transition: <input id="tt" class="noslide" type="number" min="0" max="65.5" step="0.1" value="0.7">s</p> <!--p>Transition: <input id="tt" class="noslide" type="number" min="0" max="65.5" step="0.1" value="0.7">s</p-->
</div> </div>
<div id="Presets" class="tabcontent"> <div id="Presets" class="tabcontent">
<div id="putil"> <div id="putil" class="staytop">
</div> </div>
<div id="pql"> <div id="pql">
</div> </div>
<div id="pcont"> <div id="pcont">
Loading... Loading...
@ -183,19 +200,19 @@
<div id="imgw"> <div id="imgw">
<img class="wi" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAFCAYAAAC5Fuf5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABbSURBVChTlY9bDoAwDMNW7n9nwCipytQN4Z8tbrTHmDmF4oPzyldwRqp1SSdnV/NuZuzqerAByxXznBw3igkeFEfXyUuhK/yFM0CxJfyqXZEOc6/Sr9/bf7uIC5Nwd7orMvAPAAAAAElFTkSuQmCC" /> <img class="wi" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAFCAYAAAC5Fuf5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABbSURBVChTlY9bDoAwDMNW7n9nwCipytQN4Z8tbrTHmDmF4oPzyldwRqp1SSdnV/NuZuzqerAByxXznBw3igkeFEfXyUuhK/yFM0CxJfyqXZEOc6/Sr9/bf7uIC5Nwd7orMvAPAAAAAElFTkSuQmCC" />
</div><br> </div><br>
<div id="kv">Loading...</div><br> <div id="kv">Loading...</div><br>
<button class="btn infobtn" onclick="requestJson(null)">Refresh</button> <button class="btn infobtn" onclick="loadInfo()">Refresh</button>
<button class="btn infobtn" onclick="toggleInfo()">Close Info</button><br> <button class="btn infobtn" onclick="toggleInfo()">Close Info</button><br>
<button class="btn infobtn" onclick="toggleNodes()">Instance List</button> <button class="btn infobtn" onclick="toggleNodes()">Instance List</button>
<button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button><br> <button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button><br>
<span class="h">Made with <span id="heart">&#10084;&#xFE0E;</span> by Aircoookie and the WLED community</span> <span class="h">Made with <span id="heart">&#10084;&#xFE0E;</span> by Aircoookie and the WLED community</span>
</div> </div>
<div id="nodes" class="modal"> <div id="nodes" class="modal">
<div id="ndlt">WLED instances</div> <div id="ndlt">WLED instances</div>
<div id="kn">Loading...</div><br> <div id="kn">Loading...</div><br>
<button class="btn infobtn" onclick="loadNodes()">Refresh</button> <button class="btn infobtn" onclick="loadNodes()">Refresh</button>
<button class="btn infobtn" onclick="toggleNodes()">Close list</button><br> <button class="btn infobtn" onclick="toggleNodes()">Close list</button><br>
</div> </div>
<div id="rover" class="modal"> <div id="rover" class="modal">

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
background: #333; background: #333;
color: #fff; color: #fff;
font-family: Verdana, Helvetica, sans-serif; font-family: Verdana, Helvetica, sans-serif;
display: inline-block;
border: 1px solid #333; border: 1px solid #333;
font-size: 6vmin; font-size: 6vmin;
height: var(--h); height: var(--h);

View File

@ -31,14 +31,14 @@ function mMap(){
function S(){GCH(15);GetV();mMap();}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX");}function B(){window.history.back();} function S(){GCH(15);GetV();mMap();}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX");}function B(){window.history.back();}
function GetV(){} function GetV(){}
</script> </script>
<style> <style>@import url("/style.css");</style>
@import url("style.css");
</style>
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div>
<h2>Imma firin ma lazer (if it has DMX support)</h2><!-- TODO: Change to something less-meme-related //--> <h2>Imma firin ma lazer (if it has DMX support)</h2><!-- TODO: Change to something less-meme-related //-->
Proxy Universe <input name=PU type=number min=0 max=63999 required> from E1.31 to DMX (0=disabled)<br> Proxy Universe <input name=PU type=number min=0 max=63999 required> from E1.31 to DMX (0=disabled)<br>

View File

@ -1,436 +1,478 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=500"> <meta name="viewport" content="width=500">
<title>LED Settings</title> <title>LED Settings</title>
<script> <script>
var d=document,laprev=55,maxB=1,maxM=5000,maxPB=4096,bquot=0; //maximum bytes for LED allocation: 5kB for 8266, 32kB for 32 var d=document,laprev=55,maxB=1,maxM=5000,maxPB=4096,bquot=0; //maximum bytes for LED allocation: 5kB for 8266, 32kB for 32
function H() function H()
{ {
window.open("https://github.com/Aircoookie/WLED/wiki/Settings#led-settings"); window.open("https://github.com/Aircoookie/WLED/wiki/Settings#led-settings");
} }
function B() function B()
{ {
window.open("/settings","_self"); window.open("/settings","_self");
} }
function gId(n){return d.getElementById(n);} function gId(n){return d.getElementById(n);}
function off(n){ function off(n){
d.getElementsByName(n)[0].value = -1; d.getElementsByName(n)[0].value = -1;
} }
var timeout; var timeout;
function showToast(text, error = false) function showToast(text, error = false)
{ {
var x = gId("toast"); var x = gId("toast");
x.innerHTML = text; x.innerHTML = text;
x.className = error ? "error":"show"; x.className = error ? "error":"show";
clearTimeout(timeout); clearTimeout(timeout);
x.style.animation = 'none'; x.style.animation = 'none';
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900); timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
} }
function bLimits(b,p,m) { function bLimits(b,p,m) {
maxB = b; maxM = m; maxPB = p; maxB = b; maxM = m; maxPB = p;
} }
function pinsOK() { function pinsOK() {
var LCs = d.getElementsByTagName("input"); var LCs = d.getElementsByTagName("input");
for (i=0; i<LCs.length; i++) { for (i=0; i<LCs.length; i++) {
var nm = LCs[i].name.substring(0,2); var nm = LCs[i].name.substring(0,2);
//check for pin conflicts //check for pin conflicts
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR") if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR")
if (LCs[i].value!="" && LCs[i].value!="-1") { if (LCs[i].value!="" && LCs[i].value!="-1") {
if (d.um_p && d.um_p.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(d.um_p)} can't be used.`);LCs[i].value="";LCs[i].focus();return false;} if (d.um_p && d.um_p.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(d.um_p)} can't be used.`);LCs[i].value="";LCs[i].focus();return false;}
else if (LCs[i].value > 5 && LCs[i].value < 12) {alert("Sorry, pins 6-11 can not be used.");LCs[i].value="";LCs[i].focus();return false;} else if (LCs[i].value > 5 && LCs[i].value < 12) {alert("Sorry, pins 6-11 can not be used.");LCs[i].value="";LCs[i].focus();return false;}
for (j=i+1; j<LCs.length; j++) else if (!(nm == "IR" || nm=="BT") && LCs[i].value > 33) {alert("Sorry, pins >33 are input only.");LCs[i].value="";LCs[i].focus();return false;}
{ for (j=i+1; j<LCs.length; j++)
var n2 = LCs[j].name.substring(0,2); {
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR") var n2 = LCs[j].name.substring(0,2);
if (LCs[j].value!="" && LCs[i].value==LCs[j].value) {alert(`Pin conflict between ${nm}/${n2}!`);LCs[j].value="";LCs[j].focus();return false;} if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR")
} if (LCs[j].value!="" && LCs[i].value==LCs[j].value) {alert(`Pin conflict between ${LCs[i].name}/${LCs[j].name}!`);LCs[j].value="";LCs[j].focus();return false;}
} }
} }
return true; }
} return true;
function trySubmit(e) { }
e.preventDefault(); function trySubmit(e) {
if (!pinsOK()) {e.stopPropagation();return false;} // Prevent form submission and contact with server d.Sf.data.value = '';
if (bquot > 100) {var msg = "Too many LEDs for me to handle!"; if (maxM < 10000) msg += "\n\rConsider using an ESP32."; alert(msg);} e.preventDefault();
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914 if (!pinsOK()) {e.stopPropagation();return false;} // Prevent form submission and contact with server
} if (bquot > 100) {var msg = "Too many LEDs for me to handle!"; if (maxM < 10000) msg += "\n\rConsider using an ESP32."; alert(msg);}
function S(){GetV();setABL();} if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
function enABL() }
{ function S(){GetV();setABL();}
var en = gId('able').checked; function enABL()
d.Sf.LA.value = (en) ? laprev:0; {
gId('abl').style.display = (en) ? 'inline':'none'; var en = gId('able').checked;
gId('psu2').style.display = (en) ? 'inline':'none'; d.Sf.LA.value = (en) ? laprev:0;
if (d.Sf.LA.value > 0) setABL(); gId('abl').style.display = (en) ? 'inline':'none';
} gId('psu2').style.display = (en) ? 'inline':'none';
function enLA() if (d.Sf.LA.value > 0) setABL();
{ }
var val = d.Sf.LAsel.value; function enLA()
d.Sf.LA.value = val; {
gId('LAdis').style.display = (val == 50) ? 'inline':'none'; var val = d.Sf.LAsel.value;
UI(); d.Sf.LA.value = val;
} gId('LAdis').style.display = (val == 50) ? 'inline':'none';
function setABL() UI();
{ }
gId('able').checked = true; function setABL()
d.Sf.LAsel.value = 50; {
switch (parseInt(d.Sf.LA.value)) { gId('able').checked = true;
case 0: gId('able').checked = false; enABL(); break; d.Sf.LAsel.value = 50;
case 30: d.Sf.LAsel.value = 30; break; switch (parseInt(d.Sf.LA.value)) {
case 35: d.Sf.LAsel.value = 35; break; case 0: gId('able').checked = false; enABL(); break;
case 55: d.Sf.LAsel.value = 55; break; case 30: d.Sf.LAsel.value = 30; break;
case 255: d.Sf.LAsel.value = 255; break; case 35: d.Sf.LAsel.value = 35; break;
default: gId('LAdis').style.display = 'inline'; case 55: d.Sf.LAsel.value = 55; break;
} case 255: d.Sf.LAsel.value = 255; break;
gId('m1').innerHTML = maxM; default: gId('LAdis').style.display = 'inline';
d.getElementsByName("Sf")[0].addEventListener("submit", trySubmit); }
UI(); gId('m1').innerHTML = maxM;
} d.getElementsByName("Sf")[0].addEventListener("submit", trySubmit);
//returns mem usage UI();
function getMem(type, len, p0) { }
if (type < 32) { //returns mem usage
if (maxM < 10000 && p0==3) { //8266 DMA uses 5x the mem function getMem(type, len, p0) {
if (type > 29) return len*20; //RGBW if (type < 32) {
return len*15; if (maxM < 10000 && p0==3) { //8266 DMA uses 5x the mem
} else if (maxM >= 10000) //ESP32 RMT uses double buffer? if (type > 29) return len*20; //RGBW
{ return len*15;
if (type > 29) return len*8; //RGBW } else if (maxM >= 10000) //ESP32 RMT uses double buffer?
return len*6; {
} if (type > 29) return len*8; //RGBW
if (type > 29) return len*4; //RGBW return len*6;
return len*3; }
} if (type > 29) return len*4; //RGBW
if (type > 31 && type < 48) return 5; return len*3;
if (type == 44 || type == 45) return len*4; //RGBW }
return len*3; if (type > 31 && type < 48) return 5;
} if (type == 44 || type == 45) return len*4; //RGBW
function UI(change=false) return len*3;
{ }
var isRGBW = false, memu = 0; function UI(change=false)
{
gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none'; var isRGBW = false, memu = 0;
if (d.Sf.LA.value == 255) laprev = 12; gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none';
else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value;
if (d.Sf.LA.value == 255) laprev = 12;
var s = d.getElementsByTagName("select"); else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value;
for (i=0; i<s.length; i++) {
if (s[i].name.substring(0,2)=="LT") { // enable/disable LED fields
n=s[i].name.substring(2); var s = d.getElementsByTagName("select");
var type = parseInt(s[i].value,10); for (i=0; i<s.length; i++) {
gId("p0d"+n).innerHTML = (type > 49) ? "Data:" : (type >41) ? "Pins:" : "Pin:"; // is the field a LED type?
gId("p1d"+n).innerHTML = (type > 49) ? "Clk:" : ""; if (s[i].name.substring(0,2)=="LT") {
var LK = d.getElementsByName("L1"+n)[0]; n=s[i].name.substring(2);
var type = parseInt(s[i].value,10);
memu += getMem(type, d.getElementsByName("LC"+n)[0].value, d.getElementsByName("L0"+n)[0].value); gId("p0d"+n).innerHTML = (type == 2 || type == 3) ? "IP address:" : (type > 49) ? "Data GPIO:" : (type >41) ? "GPIOs:" : "GPIO:";
gId("p1d"+n).innerHTML = (type > 49) ? "Clk GPIO:" : "";
for (p=1; p<5; p++) { var LK = d.getElementsByName("L1"+n)[0]; // clock pin
var LK = d.getElementsByName("L"+p+n)[0];
if (!LK) continue; memu += getMem(type, d.getElementsByName("LC"+n)[0].value, d.getElementsByName("L0"+n)[0].value); // calc memory
if ((type>49 && p==1) || (type>41 && type < 50 && (p+40 < type))) // TYPE_xxxx values from const.h
{ // enumerate pins
LK.style.display = "inline"; for (p=1; p<5; p++) {
LK.required = true; var LK = d.getElementsByName("L"+p+n)[0]; // secondary pins
} else { if (!LK) continue;
LK.style.display = "none"; if (((type == 2 || type == 3) && p<4) || (type>49 && p==1) || (type>41 && type < 50 && (p+40 < type))) // TYPE_xxxx values from const.h
LK.required = false; {
LK.value=""; // display pin field
} LK.style.display = "inline";
} LK.required = true;
if (type == 30 || type == 31 || (type > 40 && type < 46 && type != 43)) isRGBW = true; } else {
gId("dig"+n).style.display = (type > 31 && type < 48) ? "none":"inline"; // hide pin field
gId("psd"+n).innerHTML = (type > 31 && type < 48) ? "Index:":"Start:"; LK.style.display = "none";
} LK.required = false;
} LK.value="";
}
var myC = d.querySelectorAll('.wc'), }
l = myC.length; if (change) {
for (i = 0; i < l; i++) { // // blazoncek experimental extension
myC[i].style.display = (isRGBW) ? 'inline':'none'; // gId("ew"+n).checked = (type == 30 || type == 31 || type == 44 || type == 45); // RGBW checkbox, TYPE_xxxx values from const.h
} gId("ls"+n).value = n+1; // set LED start
if (type > 31 && type < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED
if (d.activeElement == d.getElementsByName("LC")[0]) { }
var o = d.getElementsByClassName("iST"); // // blazoncek experimental extension
var i = o.length; // gId("ew"+n).onclick = (type > 31 && type < 48) ? (function(){return false}) : (function(){}); // prevent change for analog
if (i == 1) d.getElementsByName("LC0")[0].value = d.getElementsByName("LC")[0].value; // isRGBW |= gId("ew"+n).checked;
} isRGBW |= (type == 30 || type == 31 || (type > 40 && type < 46 && type != 43)); // RGBW checkbox, TYPE_xxxx values from const.h
gId("co"+n).style.display = (type == 2 || type == 3 || type == 41 || type == 42) ? "none":"inline"; // hide color order for PWM W & WW/CW
var LCs = d.getElementsByTagName("input"); gId("dig"+n+"c").style.display = (type > 40 && type < 48) ? "none":"inline"; // hide count for analog
var sLC = 0, maxLC = 0; gId("dig"+n+"r").style.display = (type == 2 || type == 3) ? "none":"inline"; // hide reversed for virtual
for (i=0; i<LCs.length; i++) { gId("dig"+n+"s").style.display = (type == 2 || type == 3 || (type > 40 && type < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog
var nm = LCs[i].name.substring(0,2); gId("rev"+n).innerHTML = (type > 40 && type < 48) ? "Inverted":"Reverse (rotated 180°)"; // change reverse text for analog
if (nm=="LC" && LCs[i].name !== "LC") { gId("psd"+n).innerHTML = (type > 40 && type < 48) ? "Index:":"Start:"; // change analog start description
var n=LCs[i].name.substring(2); }
var c=parseInt(LCs[i].value,10); }
if(gId("ls"+n).readOnly) gId("ls"+n).value=sLC; // display white channel calculation method
if(c){sLC+=c;if(c>maxLC)maxLC=c;} var myC = d.querySelectorAll('.wc'),
continue; l = myC.length;
} for (i = 0; i < l; i++) {
if (nm=="L0" || nm=="L1") { myC[i].style.display = (isRGBW) ? 'inline':'none';
var lc=d.getElementsByName("LC"+LCs[i].name.substring(2))[0]; }
lc.max=maxPB; // check for pin conflicts
} var LCs = d.getElementsByTagName("input");
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR") var sLC = 0, maxLC = 0;
if (LCs[i].value!="" && LCs[i].value!="-1") { for (i=0; i<LCs.length; i++) {
var p = []; var nm = LCs[i].name.substring(0,2); // field name
if (d.um_p && Array.isArray(d.um_p)) for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]); var n = LCs[i].name.substring(2); // bus number
for (j=0; j<LCs.length; j++) { // do we have a led count field but not total led count
if (i==j) continue; if (nm=="LC" && LCs[i].name !== "LC") {
var n2 = LCs[j].name.substring(0,2); var c=parseInt(LCs[i].value,10);
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR") /*if(gId("ls"+n).readOnly)*/ gId("ls"+n).value=sLC; // update led start field
if (LCs[j].value!="" && LCs[j].value!="-1") p.push(parseInt(LCs[j].value,10)); if(c){sLC+=c;if(c>maxLC)maxLC=c;} // increase led count
} continue;
if (p.some((e)=>e==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color="#fff"; }
} // do we have led pins for digital leds
} if (nm=="L0" || nm=="L1") {
var lc=d.getElementsByName("LC"+n)[0];
gId('m0').innerHTML = memu; lc.max=maxPB; // update max led count value
bquot = memu / maxM * 100; }
gId('dbar').style.background = `linear-gradient(90deg, ${bquot > 60 ? (bquot > 90 ? "red":"orange"):"#ccc"} 0 ${bquot}%%, #444 ${bquot}%% 100%%)`; // ignore IP address
gId('ledwarning').style.display = (sLC > maxPB || maxLC > 800 || bquot > 80) ? 'inline':'none'; if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3") {
gId('ledwarning').style.color = (sLC > maxPB || maxLC > maxPB || bquot > 100) ? 'red':'orange'; var type = parseInt(d.getElementsByName("LT"+n)[0].value, 10); // LED type SELECT
gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>WARNING: Using over ${maxM}B!</b>)` : "") : "800 LEDs per pin"; if (type==2 || type==3) continue;
}
var val = Math.ceil((100 + sLC * laprev)/500)/2; // check for pin conflicts
val = (val > 5) ? Math.ceil(val) : val; if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR")
var s = ""; if (LCs[i].value!="" && LCs[i].value!="-1") {
var is12V = (d.Sf.LAsel.value == 30); var p = []; // used pin array
var isWS2815 = (d.Sf.LAsel.value == 255); if (d.um_p && Array.isArray(d.um_p)) for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]); // fill with reservations
if (val < 1.02 && !is12V && !isWS2815) for (j=0; j<LCs.length; j++) {
{ if (i==j) continue;
s = "ESP 5V pin with 1A USB supply"; var n2 = LCs[j].name.substring(0,2);
} else if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4" || n2=="RL" || n2=="BT" || n2=="IR") {
{ if (n2.substring(0,1)==="L") {
s += is12V ? "12V ": isWS2815 ? "WS2815 12V " : "5V "; var m = LCs[j].name.substring(2);
s += val; var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10);
s += "A supply connected to LEDs"; if (t2==2 || t2==3) continue;
} }
var val2 = Math.ceil((100 + sLC * laprev)/1500)/2; if (LCs[j].value!="" && LCs[j].value!="-1") p.push(parseInt(LCs[j].value,10)); // add current pin
val2 = (val2 > 5) ? Math.ceil(val2) : val2; }
var s2 = "(for most effects, ~"; }
s2 += val2; // now check for conflicts
s2 += "A is enough)<br>"; if (p.some((e)=>e==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color=parseInt(LCs[i].value,10)>33?"orange":"#fff";
gId('psu').innerHTML = s; }
gId('psu2').innerHTML = isWS2815 ? "" : s2; }
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none"; // update total led count
} if (gId("LC").readOnly) d.getElementsByName("LC")[0].value = sLC;
function lastEnd(i) { // if we are changing total led count update led count for 1st strip
if (i<1) return 0; if (d.activeElement == d.getElementsByName("LC")[0]) {
v = parseInt(d.getElementsByName("LS"+(i-1))[0].value) + parseInt(d.getElementsByName("LC"+(i-1))[0].value); var o = d.getElementsByClassName("iST");
var type = parseInt(d.getElementsByName("LT"+(i-1))[0].value); var i = o.length;
if (type > 31 && type < 48) v = 1; //PWM busses if (i == 1) d.getElementsByName("LC0")[0].value = d.getElementsByName("LC")[0].value;
if (isNaN(v)) return 0; }
return v; // memory usage and warnings
} gId('m0').innerHTML = memu;
function addLEDs(n) bquot = memu / maxM * 100;
{ gId('dbar').style.background = `linear-gradient(90deg, ${bquot > 60 ? (bquot > 90 ? "red":"orange"):"#ccc"} 0 ${bquot}%%, #444 ${bquot}%% 100%%)`;
if (n>1) {maxB=n; gId("+").style.display="inline"; return;} gId('ledwarning').style.display = (sLC > maxPB || maxLC > 800 || bquot > 80) ? 'inline':'none';
gId('ledwarning').style.color = (sLC > maxPB || maxLC > maxPB || bquot > 100) ? 'red':'orange';
var o = d.getElementsByClassName("iST"); gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>WARNING: Using over ${maxM}B!</b>)` : "") : "800 LEDs per GPIO";
var i = o.length; // calculate power
var val = Math.ceil((100 + sLC * laprev)/500)/2;
if ((n==1 && i>=maxB) || (n==-1 && i==0)) return; val = (val > 5) ? Math.ceil(val) : val;
var s = "";
var f = gId("mLC"); var is12V = (d.Sf.LAsel.value == 30);
if (n==1) { var isWS2815 = (d.Sf.LAsel.value == 255);
// npm run build has trouble minimizing spaces inside string if (val < 1.02 && !is12V && !isWS2815)
var cn = `<div class="iST"> {
${i>0?'<hr style="width:260px">':''} s = "ESP 5V pin with 1A USB supply";
${i+1}: } else
<select name="LT${i}" onchange="UI()"> {
<option value="22">WS281x</option> s += is12V ? "12V ": isWS2815 ? "WS2815 12V " : "5V ";
<option value="30">SK6812 RGBW</option> s += val;
<option value="31">TM1814</option> s += "A supply connected to LEDs";
<option value="24">400kHz</option> }
<option value="50">WS2801</option> var val2 = Math.ceil((100 + sLC * laprev)/1500)/2;
<option value="51">APA102</option> val2 = (val2 > 5) ? Math.ceil(val2) : val2;
<option value="52">LPD8806</option> var s2 = "(for most effects, ~";
<option value="53">P9813</option> s2 += val2;
<option value="41">PWM White</option> s2 += "A is enough)<br>";
<option value="42">PWM WWCW</option> gId('psu').innerHTML = s;
<option value="43">PWM RGB</option> gId('psu2').innerHTML = isWS2815 ? "" : s2;
<option value="44">PWM RGBW</option> gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
<option value="45">PWM RGBWC</option> }
</select>&nbsp; function lastEnd(i) {
Color Order: if (i<1) return 0;
<select name="CO${i}"> v = parseInt(d.getElementsByName("LS"+(i-1))[0].value) + parseInt(d.getElementsByName("LC"+(i-1))[0].value);
<option value="0">GRB</option> var type = parseInt(d.getElementsByName("LT"+(i-1))[0].value);
<option value="1">RGB</option> if (type > 31 && type < 48) v = 1; //PWM busses
<option value="2">BRG</option> if (isNaN(v)) return 0;
<option value="3">RBG</option> return v;
<option value="4">BGR</option> }
<option value="5">GBR</option> function addLEDs(n)
</select><br> {
<span id="p0d${i}">Pin:</span> <input type="number" class="xs" name="L0${i}" min="0" max="33" required onchange="UI()"/> if (n>1) {maxB=n; gId("+").style.display="inline"; return;}
<span id="p1d${i}">Clock:</span> <input type="number" class="xs" name="L1${i}" min="0" max="33" onchange="UI()"/>
<span id="p2d${i}"></span><input type="number" class="xs" name="L2${i}" min="0" max="33" onchange="UI()"/> var o = d.getElementsByClassName("iST");
<span id="p3d${i}"></span><input type="number" class="xs" name="L3${i}" min="0" max="33" onchange="UI()"/> var i = o.length;
<span id="p4d${i}"></span><input type="number" class="xs" name="L4${i}" min="0" max="33" onchange="UI()"/>
<br> if ((n==1 && i>=maxB) || (n==-1 && i==0)) return;
<span id="psd${i}">Start:</span> <input type="number" name="LS${i}" id="ls${i}" min="0" max="8191" value="${lastEnd(i)}" required />&nbsp;
<div id="dig${i}" style="display:inline"> var f = gId("mLC");
Count: <input type="number" name="LC${i}" min="0" max="${maxPB}" value="1" required oninput="UI()" /><br> if (n==1) {
Reverse (rotated 180°): <input type="checkbox" name="CV${i}"> // npm run build has trouble minimizing spaces inside string
&nbsp;Skip 1<sup>st</sup> LED: <input id="sl${i}" type="checkbox" name="SL${i}"><br> var cn = `<div class="iST">
</div> <hr style="width:260px">
</div>`; ${i+1}:
f.insertAdjacentHTML("beforeend", cn); <select name="LT${i}" onchange="UI(true)">
} <option value="22" selected>WS281x</option>
if (n==-1) { <option value="30">SK6812 RGBW</option>
o[--i].remove();--i; <option value="31">TM1814</option>
} <option value="24">400kHz</option>
<option value="50">WS2801</option>
gId("+").style.display = (i<maxB-1) ? "inline":"none"; <option value="51">APA102</option>
gId("-").style.display = (i>0) ? "inline":"none"; <option value="52">LPD8806</option>
<option value="53">P9813</option>
UI(); <option value="41">PWM White</option>
} <option value="42">PWM WWCW</option>
function addBtn(i,p,t) { <option value="43">PWM RGB</option>
var c = gId("btns").innerHTML; <option value="44">PWM RGBW</option>
var bt = "BT" + i; <option value="45">PWM RGBWC</option>
var be = "BE" + i; <option value="2">Virtual RGB</option>
c += `Button ${i} pin: <input type="number" class="xs" min="-1" max="40" name="${bt}" onchange="UI()" value="${p}">&nbsp;`; <option value="3">Virtual RGBW</option>
c += `<select name="${be}">` </select>&nbsp;
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`; <div id="co${i}" style="display:inline">Color Order:
c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`; <select name="CO${i}">
c += `<option value="3" ${t==3?"selected":""}>Push inverted</option>`; <option value="0">GRB</option>
c += `<option value="4" ${t==4?"selected":""}>Switch</option>`; <option value="1">RGB</option>
c += `<option value="5" ${t==5?"selected":""}>PIR sensor</option>`; <option value="2">BRG</option>
c += `<option value="6" ${t==6?"selected":""}>Touch</option>`; <option value="3">RBG</option>
c += `<option value="7" ${t==7?"selected":""}>Analog</option>`; <option value="4">BGR</option>
c += `<option value="8" ${t==8?"selected":""}>Analog inverted</option>`; <option value="5">GBR</option>
c += `</select>`; </select></div>
c += `<span style="cursor: pointer;" onclick="off('${bt}')">&nbsp;&#215;</span><br>`; <br>
gId("btns").innerHTML = c; <span id="p0d${i}">GPIO:</span><input type="number" name="L0${i}" min="0" max="33" required class="s" onchange="UI()"/>
} <span id="p1d${i}"></span><input type="number" name="L1${i}" min="0" max="33" class="s" onchange="UI()"/>
function uploadFile(name) { <span id="p2d${i}"></span><input type="number" name="L2${i}" min="0" max="33" class="s" onchange="UI()"/>
var req = new XMLHttpRequest(); <span id="p3d${i}"></span><input type="number" name="L3${i}" min="0" max="33" class="s" onchange="UI()"/>
req.addEventListener('load', function(){showToast(this.responseText)}); <span id="p4d${i}"></span><input type="number" name="L4${i}" min="0" max="33" class="s" onchange="UI()"/>
req.addEventListener('error', function(e){showToast(e.stack,true);}); <br>
req.open("POST", "/upload"); <span id="psd${i}">Start:</span> <input type="number" name="LS${i}" id="ls${i}" class="l" min="0" max="8191" value="${lastEnd(i)}" disabled readonly required />&nbsp;
var formData = new FormData(); <div id="dig${i}c" style="display:inline">Count: <input type="number" name="LC${i}" class="l" min="0" max="${maxPB}" value="1" required oninput="UI()" /></div>
formData.append("data", d.Sf.data.files[0], name); <br>
req.send(formData); <div id="dig${i}r" style="display:inline"><span id="rev${i}">Reversed</span>: <input type="checkbox" name="CV${i}"></div>&nbsp;
d.Sf.data.value = ''; <div id="dig${i}s" style="display:inline">Skip 1<sup>st</sup> LED: <input id="sl${i}" type="checkbox" name="SL${i}"></div>
return false; <br>
} </div>`;
function GetV() f.insertAdjacentHTML("beforeend", cn);
{ }
//values injected by server while sending HTML if (n==-1) {
//maxM=5000;maxPB=1536;d.um_p=[1,6,7,8,9,10,11];addLEDs(3);d.Sf.LC.value=250;addLEDs(1);d.Sf.L00.value=2;d.Sf.L10.value=0;d.Sf.LC0.value=250;d.Sf.LT0.value=22;d.Sf.CO0.value=0;d.Sf.LS0.value=0;d.Sf.LS0.checked=0;d.Sf.MA.value=5400;d.Sf.LA.value=55;d.getElementsByClassName("pow")[0].innerHTML="350mA";d.Sf.CA.value=40;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=3;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=0;d.Sf.BF.value=64;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=12;d.Sf.RM.checked=0;addBtn(0,0,2);addBtn(1,3,4);addBtn(2,-1,0);d.Sf.IR.value=-1; o[--i].remove();--i;
} }
</script>
<style> gId("+").style.display = (i<maxB-1) ? "inline":"none";
@import url("style.css"); gId("-").style.display = (i>0) ? "inline":"none";
</style>
</head> UI();
<body onload="S()"> }
<form id="form_s" name="Sf" method="post"> function addBtn(i,p,t) {
<div class="helpB"><button type="button" onclick="H()">?</button></div> var c = gId("btns").innerHTML;
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> var bt = "BT" + i;
<h2>LED &amp; Hardware setup</h2> var be = "BE" + i;
Total LED count: <input name="LC" id="LC" type="number" min="1" max="8192" oninput="UI()" required><br> c += `Button ${i} GPIO: <input type="number" min="-1" max="40" name="${bt}" onchange="UI()" class="s" value="${p}">`;
<i>Recommended power supply for brightest white:</i><br> c += `<select name="${be}">`
<b><span id="psu">?</span></b><br> c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
<span id="psu2"><br></span> c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`;
<br> c += `<option value="3" ${t==3?"selected":""}>Push inverted</option>`;
Enable automatic brightness limiter: <input type="checkbox" name="ABen" onchange="enABL()" id="able"><br> c += `<option value="4" ${t==4?"selected":""}>Switch</option>`;
<div id="abl"> c += `<option value="5" ${t==5?"selected":""}>PIR sensor</option>`;
Maximum Current: <input name="MA" type="number" class="l" min="250" max="65000" oninput="UI()" required> mA<br> c += `<option value="6" ${t==6?"selected":""}>Touch</option>`;
<div id="ampwarning" style="color: orange; display: none;"> c += `<option value="7" ${t==7?"selected":""}>Analog</option>`;
&#9888; Your power supply provides high current.<br> c += `<option value="8" ${t==8?"selected":""}>Analog inverted</option>`;
To improve the safety of your setup,<br> c += `</select>`;
please use thick cables,<br> c += `<span style="cursor: pointer;" onclick="off('${bt}')">&nbsp;&#215;</span><br>`;
multiple power injection points and a fuse!<br> gId("btns").innerHTML = c;
</div> }
<i>Automatically limits brightness to stay close to the limit.<br> function uploadFile(name) {
Keep at &lt;1A if powering LEDs directly from the ESP 5V pin!<br> var req = new XMLHttpRequest();
If you are using an external power supply, enter its rating.<br> req.addEventListener('load', function(){showToast(this.responseText)});
(Current estimated usage: <span class="pow">unknown</span>)</i><br><br> req.addEventListener('error', function(e){showToast(e.stack,true);});
LED voltage (Max. current for a single LED):<br> req.open("POST", "/upload");
<select name="LAsel" onchange="enLA()"> var formData = new FormData();
<option value="55" selected>5V default (55mA)</option> formData.append("data", d.Sf.data.files[0], name);
<option value="35">5V efficient (35mA)</option> req.send(formData);
<option value="30">12V (30mA)</option> d.Sf.data.value = '';
<option value="255">WS2815 (12mA)</option> return false;
<option value="50">Custom</option> }
</select><br> function GetV()
<span id="LAdis" style="display: none;">Custom max. current per LED: <input name="LA" type="number" min="0" max="255" id="la" oninput="UI()" required> mA<br></span> {
<i>Keep at default if you are unsure about your type of LEDs.</i><br> //values injected by server while sending HTML
</div> //maxM=5000;maxPB=1536;d.um_p=[1,6,7,8,9,10,11];addLEDs(5);d.Sf.LC.value=250;addLEDs(1);d.Sf.L00.value=2;d.Sf.L10.value=0;d.Sf.LC0.value=250;d.Sf.LT0.value=22;d.Sf.CO0.value=0;d.Sf.LS0.value=0;d.Sf.LS0.checked=0;d.Sf.MA.value=5400;d.Sf.LA.value=55;d.getElementsByClassName("pow")[0].innerHTML="350mA";d.Sf.CA.value=40;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=3;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=0;d.Sf.BF.value=64;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=12;d.Sf.RM.checked=0;addBtn(0,0,2);addBtn(1,3,4);addBtn(2,-1,0);d.Sf.IR.value=-1;
<h3>Hardware setup</h3> }
<div id="mLC">LED outputs:</div> </script>
<button type="button" id="+" onclick="addLEDs(1)" style="display:none;border-radius:20px;height:36px;">+</button> <style>@import url("style.css");</style>
<button type="button" id="-" onclick="addLEDs(-1)" style="display:none;border-radius:20px;width:36px;height:36px;">-</button><br> </head>
LED Memory Usage: <span id="m0">0</span> / <span id="m1">?</span> B<br> <body onload="S()">
<div id="dbar" style="display:inline-block; width: 100px; height: 10px; border-radius: 20px;"></div><br> <form id="form_s" name="Sf" method="post">
<div id="ledwarning" style="color: orange; display: none;"> <div class="toprow">
&#9888; You might run into stability or lag issues.<br> <div class="helpB"><button type="button" onclick="H()">?</button></div>
Use less than <span id="wreason">800 LEDs per pin</span> for the best experience!<br> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div> </div>
Make a segment for each output: <input type="checkbox" name="MS"> <br> <h2>LED &amp; Hardware setup</h2>
<hr style="width:260px"> Total LED count: <input name="LC" id="LC" type="number" min="1" max="8192" oninput="UI()" disabled required readonly><br>
<div id="btns"></div> <i>Recommended power supply for brightest white:</i><br>
Touch threshold: <input type="number" class="s" min="0" max="100" name="TT" required><br> <b><span id="psu">?</span></b><br>
IR pin: <input type="number" class="xs" min="-1" max="40" name="IR" onchange="UI()">&nbsp;<select name="IT" onchange="UI()"> <span id="psu2"><br></span>
<option value=0>Remote disabled</option> <br>
<option value=1>24-key RGB</option> Enable automatic brightness limiter: <input type="checkbox" name="ABen" onchange="enABL()" id="able"><br>
<option value=2>24-key with CT</option> <div id="abl">
<option value=3>40-key blue</option> Maximum Current: <input name="MA" type="number" class="l" min="250" max="65000" oninput="UI()" required> mA<br>
<option value=4>44-key RGB</option> <div id="ampwarning" style="color: orange; display: none;">
<option value=5>21-key RGB</option> &#9888; Your power supply provides high current.<br>
<option value=6>6-key black</option> To improve the safety of your setup,<br>
<option value=7>9-key red</option> please use thick cables,<br>
<option value=8>JSON remote</option> multiple power injection points and a fuse!<br>
</select><span style="cursor: pointer;" onclick="off('IR')">&nbsp;&#215;</span><br> </div>
<div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile('/ir.json');"><br></div> <i>Automatically limits brightness to stay close to the limit.<br>
<div id="toast"></div> Keep at &lt;1A if powering LEDs directly from the ESP 5V pin!<br>
<a href="https://github.com/Aircoookie/WLED/wiki/Infrared-Control" target="_blank">IR info</a><br> If you are using an external power supply, enter its rating.<br>
Relay pin: <input type="number" class="xs" min="-1" max="33" name="RL" onchange="UI()"> Invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')">&nbsp;&#215;</span><br> (Current estimated usage: <span class="pow">unknown</span>)</i><br><br>
<hr style="width:260px"> LED voltage (Max. current for a single LED):<br>
<h3>Defaults</h3> <select name="LAsel" onchange="enLA()">
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br> <option value="55" selected>5V default (55mA)</option>
Default brightness: <input name="CA" type="number" class="s" min="0" max="255" required> (0-255)<br><br> <option value="35">5V efficient (35mA)</option>
Apply preset <input name="BP" type="number" class="s" min="0" max="250" required> at boot (0 uses defaults) <option value="30">12V (30mA)</option>
<br><br> <option value="255">WS2815 (12mA)</option>
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br> <option value="50">Custom</option>
Use Gamma correction for brightness: <input type="checkbox" name="GB"> (not recommended)<br><br> </select><br>
Brightness factor: <input name="BF" type="number" class="s" min="1" max="255" required> % <span id="LAdis" style="display: none;">Custom max. current per LED: <input name="LA" type="number" min="0" max="255" id="la" oninput="UI()" required> mA<br></span>
<h3>Transitions</h3> <i>Keep at default if you are unsure about your type of LEDs.</i><br>
Crossfade: <input type="checkbox" name="TF"><br> </div>
Transition Time: <input name="TD" type="number" class="l" min="0" max="65500"> ms<br> <h3>Hardware setup</h3>
Enable Palette transitions: <input type="checkbox" name="PF"> <div id="mLC">LED outputs:</div>
<h3>Timed light</h3> <hr style="width:260px">
Default Duration: <input name="TL" type="number" class="s" min="1" max="255" required> min<br> <button type="button" id="+" onclick="addLEDs(1)">+</button>
Default Target brightness: <input name="TB" type="number" class="s" min="0" max="255" required><br> <button type="button" id="-" onclick="addLEDs(-1)">-</button><br>
Mode: LED Memory Usage: <span id="m0">0</span> / <span id="m1">?</span> B<br>
<select name="TW"> <div id="dbar" style="display:inline-block; width: 100px; height: 10px; border-radius: 20px;"></div><br>
<option value="0">Wait and set</option> <div id="ledwarning" style="color: orange; display: none;">
<option value="1">Fade</option> &#9888; You might run into stability or lag issues.<br>
<option value="2">Fade Color</option> Use less than <span id="wreason">800 LEDs per pin</span> for the best experience!<br>
<option value="3">Sunrise</option> </div>
</select> <hr style="width:260px">
<h3>Advanced</h3> Create a segment for each output: <input type="checkbox" name="MS"> <br>
Palette blending: <hr style="width:260px">
<select name="PB"> <div id="btns"></div>
<option value="0">Linear (wrap if moving)</option> Touch threshold: <input type="number" min="0" max="100" name="TT" required><br>
<option value="1">Linear (always wrap)</option> IR GPIO: <input type="number" min="-1" max="40" name="IR" onchange="UI()" class="s"><select name="IT" onchange="UI()">
<option value="2">Linear (never wrap)</option> <option value=0>Remote disabled</option>
<option value="3">None (not recommended)</option> <option value=1>24-key RGB</option>
</select><br> <option value=2>24-key with CT</option>
<span class="wc"> <option value=3>40-key blue</option>
Auto-calculate white channel from RGB:<br> <option value=4>44-key RGB</option>
<select name="AW"> <option value=5>21-key RGB</option>
<option value=0>None</option> <option value=6>6-key black</option>
<option value=1>Brighter</option> <option value=7>9-key red</option>
<option value=2>Accurate</option> <option value=8>JSON remote</option>
<option value=3>Dual</option> </select><span style="cursor: pointer;" onclick="off('IR')">&nbsp;&#215;</span><br>
<option value=4>Legacy</option> <div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile('/ir.json');"><br></div>
</select> <div id="toast"></div>
<br></span><hr> <a href="https://github.com/Aircoookie/WLED/wiki/Infrared-Control" target="_blank">IR info</a><br>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button> Relay GPIO: <input type="number" min="-1" max="33" name="RL" onchange="UI()" class="s"> invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')">&nbsp;&#215;</span><br>
</form> <hr style="width:260px">
</body> <h3>Defaults</h3>
</html> Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>
Default brightness: <input name="CA" type="number" class="m" min="0" max="255" required> (0-255)<br><br>
Apply preset <input name="BP" type="number" class="m" min="0" max="250" required> at boot (0 uses defaults)
<br><br>
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br>
Use Gamma correction for brightness: <input type="checkbox" name="GB"> (not recommended)<br><br>
Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> %
<h3>Transitions</h3>
Crossfade: <input type="checkbox" name="TF"><br>
Transition Time: <input name="TD" type="number" class="xl" min="0" max="65500"> ms<br>
Enable Palette transitions: <input type="checkbox" name="PF">
<h3>Timed light</h3>
Default Duration: <input name="TL" type="number" class="m" min="1" max="255" required> min<br>
Default Target brightness: <input name="TB" type="number" class="m" min="0" max="255" required><br>
Mode:
<select name="TW">
<option value="0">Wait and set</option>
<option value="1">Fade</option>
<option value="2">Fade Color</option>
<option value="3">Sunrise</option>
</select>
<h3>Advanced</h3>
Palette blending:
<select name="PB">
<option value="0">Linear (wrap if moving)</option>
<option value="1">Linear (always wrap)</option>
<option value="2">Linear (never wrap)</option>
<option value="3">None (not recommended)</option>
</select><br>
<span class="wc">
Auto-calculate white channel from RGB:<br>
<select name="AW">
<option value=0>None</option>
<option value=1>Brighter</option>
<option value=2>Accurate</option>
<option value=3>Dual</option>
<option value=4>Legacy</option>
</select>
<br></span><hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
</body>
</html>

View File

@ -57,8 +57,10 @@
</head> </head>
<body onload="GetV()"> <body onload="GetV()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button><hr>
</div>
<h2>Security & Update setup</h2> <h2>Security & Update setup</h2>
Lock wireless (OTA) software update: <input type="checkbox" name="NO"><br> Lock wireless (OTA) software update: <input type="checkbox" name="NO"><br>
Passphrase: <input type="password" name="OP" maxlength="32"><br> Passphrase: <input type="password" name="OP" maxlength="32"><br>
@ -70,17 +72,18 @@
Factory reset: <input type="checkbox" name="RS"><br> Factory reset: <input type="checkbox" name="RS"><br>
All settings and presets will be erased.<br><br> All settings and presets will be erased.<br><br>
HTTP traffic is unencrypted. An attacker in the same network can intercept form data! HTTP traffic is unencrypted. An attacker in the same network can intercept form data!
<hr>
<h3>Software Update</h3> <h3>Software Update</h3>
<button type="button" onclick="U()">Manual OTA Update</button><br> <button type="button" onclick="U()">Manual OTA Update</button><br>
Enable ArduinoOTA: <input type="checkbox" name="AO"><br> Enable ArduinoOTA: <input type="checkbox" name="AO"><br>
<h3>Backup & Restore</h3> <h3>Backup & Restore</h3>
<a class="btn lnk" href="/presets.json?download" target="download-frame">Backup presets</a><br> <a class="btn lnk" href="/presets.json?download" target="download-frame">Backup presets</a><br>
<div>Restore presets<br><input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/presets.json');"><br></div><br> <div>Restore presets<br><input type="file" name="data" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/presets.json');"><br></div><br>
<a class="btn lnk" href="/cfg.json?download" target="download-frame">Backup configuration</a><br> <a class="btn lnk" href="/cfg.json?download" target="download-frame">Backup configuration</a><br>
<div>Restore configuration<br><input type="file" name="data2" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data2,'/cfg.json');"><br></div> <div>Restore configuration<br><input type="file" name="data2" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data2,'/cfg.json');"><br></div>
<div style="color: #fa0;">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br> <div style="color: #fa0;">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
Incorrect configuration may require a factory reset or re-flashing of your ESP.</div> Incorrect configuration may require a factory reset or re-flashing of your ESP.</div>
For security reasons, passwords are not backed up. For security reasons, passwords are not backed up.
<h3>About</h3> <h3>About</h3>
<a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a> version ##VERSION##<!-- Autoreplaced from package.json --><br><br> <a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a> version ##VERSION##<!-- Autoreplaced from package.json --><br><br>
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br> <a href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br>

View File

@ -35,11 +35,14 @@ function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 556
function S(){GetV();SetVal();} function S(){GetV();SetVal();}
function GetV(){var d=document;} function GetV(){var d=document;}
</script> </script>
<style>@import url("style.css");</style></head> <style>@import url("/style.css");</style>
</head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post" onsubmit="GC()"> <form id="form_s" name="Sf" method="post" onsubmit="GC()">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div>
<h2>Sync setup</h2> <h2>Sync setup</h2>
<h3>WLED Broadcast</h3> <h3>WLED Broadcast</h3>
UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required><br> UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required><br>

View File

@ -46,13 +46,15 @@
var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>"; var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";
for (i=0;i<8;i++) for (i=0;i<8;i++)
{ {
ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"250\"></td>"; ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" class=\"sml\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" class=\"sml\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" class=\"med\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>"; for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>";
ih+="</tr>";
} }
ih+="<tr><td><input name=\"W8\" id=\"W8\" type=\"number\" style=\"display:none\"><input id=\"W80\" type=\"checkbox\"></td><td>Sunrise<input name=\"H8\" value=\"255\" type=\"hidden\"></td><td><input name=\"N8\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T8\" type=\"number\" min=\"0\" max=\"250\"></td>"; ih+="<tr><td><input name=\"W8\" id=\"W8\" type=\"number\" style=\"display:none\"><input id=\"W80\" type=\"checkbox\"></td><td>Sunrise<input name=\"H8\" class=\"sml\" value=\"255\" type=\"hidden\"></td><td><input name=\"N8\" class=\"sml\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T8\" class=\"med\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W8"+j+"\" type=\"checkbox\"></td>"; for (j=1;j<8;j++) ih+="<td><input id=\"W8"+j+"\" type=\"checkbox\"></td>";
ih+="<tr><td><input name=\"W9\" id=\"W9\" type=\"number\" style=\"display:none\"><input id=\"W90\" type=\"checkbox\"></td><td>Sunset<input name=\"H9\" value=\"255\" type=\"hidden\"></td><td><input name=\"N9\" type=\"number\" min=\"-59\" max=\"59\"><td><input name=\"T9\" type=\"number\" min=\"0\" max=\"250\"></td>"; ih+="</tr><tr><td><input name=\"W9\" id=\"W9\" type=\"number\" style=\"display:none\"><input id=\"W90\" type=\"checkbox\"></td><td>Sunset<input name=\"H9\" class=\"sml\" value=\"255\" type=\"hidden\"></td><td><input name=\"N9\" class=\"sml\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T9\" class=\"med\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W9"+j+"\" type=\"checkbox\"></td>"; for (j=1;j<8;j++) ih+="<td><input id=\"W9"+j+"\" type=\"checkbox\"></td>";
ih+="</tr>";
gId("TMT").innerHTML=ih; gId("TMT").innerHTML=ih;
} }
function FC() function FC()
@ -84,25 +86,25 @@
td = tr.insertCell(0); td = tr.insertCell(0);
td.innerHTML = `Button ${i}:`; td.innerHTML = `Button ${i}:`;
td = tr.insertCell(1); td = tr.insertCell(1);
td.innerHTML = `<input name="MP${i}" type="number" min="0" max="250" value="${p}" required>`; td.innerHTML = `<input name="MP${i}" type="number" class="m" min="0" max="250" value="${p}" required>`;
td = tr.insertCell(2); td = tr.insertCell(2);
td.innerHTML = `<input name="ML${i}" type="number" min="0" max="250" value="${l}" required>`; td.innerHTML = `<input name="ML${i}" type="number" class="m" min="0" max="250" value="${l}" required>`;
td = tr.insertCell(3); td = tr.insertCell(3);
td.innerHTML = `<input name="MD${i}" type="number" min="0" max="250" value="${d}" required>`; td.innerHTML = `<input name="MD${i}" type="number" class="m" min="0" max="250" value="${d}" required>`;
} }
function GetV() function GetV()
{ {
//values injected by server while sending HTML //values injected by server while sending HTML
} }
</script> </script>
<style> <style>@import url("/style.css");</style>
@import url("style.css");
</style>
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post" onsubmit="Wd()"> <form id="form_s" name="Sf" method="post" onsubmit="Wd()">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div>
<h2>Time setup</h2> <h2>Time setup</h2>
Get time from NTP server: <input type="checkbox" name="NT"><br> Get time from NTP server: <input type="checkbox" name="NT"><br>
<input name="NS" maxlength="32"><br> <input name="NS" maxlength="32"><br>
@ -131,8 +133,8 @@
</select><br> </select><br>
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br> UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
Current local time is <span class="times">unknown</span>.<br> Current local time is <span class="times">unknown</span>.<br>
Latitude (N): <input name="LT" type="number" min="-66.6" max="66.6" step="0.01"> Latitude (N): <input name="LT" type="number" class="xl" min="-66.6" max="66.6" step="0.01">
Longitude (E): <input name="LN" type="number" min="-180" max="180" step="0.01"> Longitude (E): <input name="LN" type="number" class="xl" min="-180" max="180" step="0.01">
<div id="sun" class="times"></div> <div id="sun" class="times"></div>
<h3>Clock</h3> <h3>Clock</h3>
Clock Overlay: Clock Overlay:
@ -155,8 +157,8 @@
</div> </div>
Countdown Mode: <input type="checkbox" name="CE"><br> Countdown Mode: <input type="checkbox" name="CE"><br>
Countdown Goal:<br> Countdown Goal:<br>
Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input name="CI" type="number" min="1" max="12" required> Day: <input name="CD" type="number" min="1" max="31" required><br> Year: 20 <input name="CY" class="small" type="number" min="0" max="99" required> Month: <input name="CI" class="small" type="number" min="1" max="12" required> Day: <input name="CD" class="small" type="number" min="1" max="31" required><br>
Hour: <input name="CH" type="number" min="0" max="23" required> Minute: <input name="CM" type="number" min="0" max="59" required> Second: <input name="CS" type="number" min="0" max="59" required><br> Hour: <input name="CH" class="small" type="number" min="0" max="23" required> Minute: <input name="CM" class="small" type="number" min="0" max="59" required> Second: <input name="CS" class="small" type="number" min="0" max="59" required><br>
<h3>Macro presets</h3> <h3>Macro presets</h3>
<b>Macros have moved!</b><br> <b>Macros have moved!</b><br>
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br> <i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>

View File

@ -6,7 +6,7 @@
<title>UI Settings</title> <title>UI Settings</title>
<script> <script>
var d = document; var d = document;
var initial_ds, initial_st; var initial_ds, initial_st, initial_su;
var sett = null; var sett = null;
var l = { var l = {
"comp":{ "comp":{
@ -21,8 +21,8 @@
"pcmbot": "Show bottom tab bar in PC mode", "pcmbot": "Show bottom tab bar in PC mode",
"pid": "Show preset IDs", "pid": "Show preset IDs",
"seglen": "Set segment length instead of stop LED", "seglen": "Set segment length instead of stop LED",
"css": "Enable custom CSS", "css": "Enable custom CSS",
"hdays": "Enable custom Holidays list" "hdays": "Enable custom Holidays list"
}, },
"theme":{ "theme":{
"alpha": { "alpha": {
@ -147,10 +147,18 @@
gId('lserr').innerHTML = "&#9888; Settings JSON saving failed. (" + e + ")"; gId('lserr').innerHTML = "&#9888; Settings JSON saving failed. (" + e + ")";
} }
} }
function cLS()
{
localStorage.removeItem('wledP');
localStorage.removeItem('wledPmt');
localStorage.removeItem('wledPalx');
showToast("Cleared.");
}
function Save() { function Save() {
SetLS(); SetLS();
if (d.Sf.DS.value != initial_ds || d.Sf.ST.checked != initial_st) d.Sf.submit(); if (d.Sf.DS.value != initial_ds || d.Sf.ST.checked != initial_st || d.Sf.SU.checked != initial_su) d.Sf.submit();
} }
function S() function S()
@ -158,6 +166,7 @@
GetV(); GetV();
initial_ds = d.Sf.DS.value; initial_ds = d.Sf.DS.value;
initial_st = d.Sf.ST.checked; initial_st = d.Sf.ST.checked;
initial_su = d.Sf.SU.checked;
GetLS(); GetLS();
} }
function H() function H()
@ -208,7 +217,7 @@
</head> </head>
<body onload="S()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div style="position:sticky;top:0;background-color:#222;"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button><br> <button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button><br>
<span id="lssuc" style="color:green; display:none">&#10004; Local UI settings saved!</span> <span id="lssuc" style="color:green; display:none">&#10004; Local UI settings saved!</span>
@ -217,6 +226,7 @@
<h2>Web Setup</h2> <h2>Web Setup</h2>
Server description: <input name="DS" maxlength="32"><br> Server description: <input name="DS" maxlength="32"><br>
Sync button toggles both send and receive: <input type="checkbox" name="ST"><br> Sync button toggles both send and receive: <input type="checkbox" name="ST"><br>
Enable simplified UI: <input type="checkbox" name="SU"><br>
<i>The following UI customization settings are unique both to the WLED device and this browser.<br> <i>The following UI customization settings are unique both to the WLED device and this browser.<br>
You will need to set them again if using a different browser, device or WLED IP address.<br> You will need to set them again if using a different browser, device or WLED IP address.<br>
Refresh the main UI to apply changes.</i><br> Refresh the main UI to apply changes.</i><br>
@ -236,11 +246,12 @@
<span class="l">BG image URL</span>: <input id="theme_bg_url" class="agi" oninput="checkRandomBg()"><br> <span class="l">BG image URL</span>: <input id="theme_bg_url" class="agi" oninput="checkRandomBg()"><br>
<span class="l">Random BG image</span>: <input type="checkbox" id="theme_bg_random" class="agi cb" onchange="setRandomBg()"><br> <span class="l">Random BG image</span>: <input type="checkbox" id="theme_bg_random" class="agi cb" onchange="setRandomBg()"><br>
<input id="theme_base" class="agi" style="display:none"> <input id="theme_base" class="agi" style="display:none">
<span class="l"></span>: <input type="checkbox" id="comp_css" class="agi cb"><br> <span class="l"></span>: <input type="checkbox" id="comp_css" class="agi cb"><br>
<div id="skin">Custom CSS: <input type="file" name="data" accept=".css"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/skin.css');"><br></div> <div id="skin">Custom CSS: <input type="file" name="data" accept=".css"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/skin.css');"><br></div>
<span class="l"></span>: <input type="checkbox" id="comp_hdays" class="agi cb"><br> <span class="l"></span>: <input type="checkbox" id="comp_hdays" class="agi cb"><br>
<div id="holidays">Holidays: <input type="file" name="data2" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data2,'/holidays.json');"><br></div> <div id="holidays">Holidays: <input type="file" name="data2" accept=".json"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data2,'/holidays.json');"><br></div>
<div id="toast"></div> <div id="toast"></div>
<hr><button type="button" onclick="cLS()">Clear local storage</button>
<hr><button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button> <hr><button type="button" onclick="B()">Back</button><button type="button" onclick="Save()">Save</button>
</form> </form>
</body> </body>

View File

@ -81,10 +81,10 @@
case "number": case "number":
c = `value="${o}"`; c = `value="${o}"`;
if (f.substr(-3)==="pin") { if (f.substr(-3)==="pin") {
c += ' max="39" min="-1" style="width:40px;"'; c += ' max="39" min="-1" class="s"';
t = "int"; t = "int";
} else { } else {
c += ' step="any" style="width:100px;"'; c += ' step="any" class="xxl"';
} }
break; break;
default: default:
@ -133,9 +133,7 @@
} }
function GetV() {} function GetV() {}
</script> </script>
<style> <style>@import url("/style.css");</style>
@import url("style.css");
</style>
</head> </head>
<body onload="S()"> <body onload="S()">

View File

@ -18,14 +18,14 @@
//values injected by server while sending HTML //values injected by server while sending HTML
} }
</script> </script>
<style> <style>@import url("/style.css");</style>
@import url("style.css");
</style>
</head> </head>
<body onload="GetV()"> <body onload="GetV()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button><hr> <button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button><hr>
</div>
<h2>WiFi setup</h2> <h2>WiFi setup</h2>
<h3>Connect to existing network</h3> <h3>Connect to existing network</h3>
Network name (SSID, empty to not connect): <br><input name="CS" maxlength="32"><br> Network name (SSID, empty to not connect): <br><input name="CS" maxlength="32"><br>
@ -53,28 +53,29 @@
Hide AP name: <input type="checkbox" name="AH"><br> Hide AP name: <input type="checkbox" name="AH"><br>
AP password (leave empty for open):<br> <input type="password" name="AP" maxlength="63" pattern="(.{8,63})|()" title="Empty or min. 8 characters"><br> AP password (leave empty for open):<br> <input type="password" name="AP" maxlength="63" pattern="(.{8,63})|()" title="Empty or min. 8 characters"><br>
Access Point WiFi channel: <input name="AC" type="number" class="xs" min="1" max="13" required><br> Access Point WiFi channel: <input name="AC" type="number" class="xs" min="1" max="13" required><br>
AP opens: AP opens:
<select name="AB"> <select name="AB">
<option value="0">No connection after boot</option> <option value="0">No connection after boot</option>
<option value="1">Disconnected</option> <option value="1">Disconnected</option>
<option value="2">Always</option> <option value="2">Always</option>
<option value="3">Never (not recommended)</option></select><br> <option value="3">Never (not recommended)</option></select><br>
AP IP: <span class="sip"> Not active </span><br> AP IP: <span class="sip"> Not active </span><br>
<h3>Experimental</h3> <h3>Experimental</h3>
Disable WiFi sleep: <input type="checkbox" name="WS"><br> Disable WiFi sleep: <input type="checkbox" name="WS"><br>
<i>Can help with connectivity issues.<br> <i>Can help with connectivity issues.<br>
Do not enable if WiFi is working correctly, increases power consumption.</i> Do not enable if WiFi is working correctly, increases power consumption.</i>
<div id="ethd"> <div id="ethd">
<h3>Ethernet Type</h3> <h3>Ethernet Type</h3>
<select name="ETH"> <select name="ETH">
<option value="0">None</option> <option value="0">None</option>
<option value="2">ESP32-POE</option> <option value="2">ESP32-POE</option>
<option value="6">ESP32Deux</option> <option value="6">ESP32Deux</option>
<option value="4">QuinLED-ESP32</option> <option value="4">QuinLED-ESP32</option>
<option value="5">TwilightLord-ESP32</option> <option value="5">TwilightLord-ESP32</option>
<option value="3">WESP32</option> <option value="3">WESP32</option>
<option value="1">WT32-ETH01</option> <option value="1">WT32-ETH01</option>
</select><br><br></div> </select><br><br>
</div>
<hr> <hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button> <button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button>
</form> </form>

858
wled00/data/simple.css Normal file

File diff suppressed because one or more lines are too long

172
wled00/data/simple.htm Normal file
View File

@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta charset="utf-8">
<meta name="theme-color" content="#222222">
<meta content="yes" name="apple-mobile-web-app-capable">
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAGACGAAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAE1JREFUOI1j/P//PwOxgNGeAUMxE9G6cQCKDWAhpADZ2f8PMjBS3QW08QK20KaZC2gfC9hCnqouoNgARgY7zMxAyNlUdQHlXiAlO2MDAD63EVqNHAe0AAAAAElFTkSuQmCC"/>
<title>WLED</title>
<script>function feedback(){}</script>
<link rel="stylesheet" href="simple.css">
</head>
<body onload="onLoad()">
<div id="cv" class="overlay">Loading WLED UI...</div>
<noscript><div class="overlay" style="opacity:1;">Sorry, WLED UI needs JavaScript!</div></noscript>
<div id="bg"></div>
<div class="wrapper" id="top">
<div class="tab top">
<div class="btnwrap">
<button id="buttonPower" onclick="togglePower()"><i class="icons">&#xe08f;</i><p class="tab-label">Power</p></button>
<button id="buttonI" onclick="toggleInfo()"><i class="icons">&#xe066;</i><p class="tab-label">Info</p></button>
<button id="buttonNodes" onclick="toggleNodes()"><i class="icons">&#xe22d;</i><p class="tab-label">Nodes</p></button></div>
<button onclick="window.location.href='/settings';"><i class="icons">&#xe0a2;</i><p class="tab-label">Config</p></button>
<button id="buttonCP" onclick="tglCP()"><i class="icons">&#xe2b3;</i><p class="tab-label">Expand</p></button>
<button id="buttonBri" onclick="tglBri()"><i class="icons">&#xe2a6;</i><p class="tab-label">Brightness</p></button>
</div>
<div id="briwrap">
<p class="label hd">Global brightness</p>
<div class="il">
<i class="icons slider-icon" onclick="tglTheme()">&#xe2a6;</i>
<div class="sliderwrap il">
<input id="sliderBri" onchange="setBri()" oninput="updateTrail(this)" max="255" min="1" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
</div>
</div>
</div>
<div class ="container">
<div class="tabcontent">
<div id="picker" class="center"></div>
<div id="qcs-w" class="center">
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
<div class="qcs" onclick="pC('#ffa000');" title="Orange" style="background-color:#ffa000;"></div>
<div class="qcs" onclick="pC('#ffc800');" title="Yellow" style="background-color:#ffc800;"></div>
<div class="qcs" onclick="pC('#ffe0a0');" title="Warm White" style="background-color:#ffe0a0;"></div>
<div class="qcs" onclick="pC('#ffffff');" title="White" style="background-color:#ffffff;"></div>
<div class="qcs qcsb" onclick="pC('#000000');" title="Black" style="background-color:#000000;"></div><br>
<div class="qcs" onclick="pC('#ff00ff');" title="Pink" style="background-color:#ff00ff;"></div>
<div class="qcs" onclick="pC('#0000ff');" title="Blue" style="background-color:#0000ff;"></div>
<div class="qcs" onclick="pC('#00ffc8');" title="Cyan" style="background-color:#00ffc8;"></div>
<div class="qcs" onclick="pC('#08ff00');" title="Green" style="background-color:#08ff00;"></div>
<div class="qcs" onclick="pC('rnd');" title="Random" style="background:linear-gradient(to right, red, orange, yellow, green, blue, purple);transform: translateY(-11px);">R</div>
</div>
<div id="csl" class="center" style="display: none;">
<button class="xxs btn" onclick="selectSlot(0);">1</button>
<button class="xxs btn" onclick="selectSlot(1);">2</button>
<button class="xxs btn" onclick="selectSlot(2);">3</button>
</div>
<div id="wwrap" class="center">
<p class="label h">White channel</p>
<i class="icons slider-icon" id="wht" title="White channel">&#xe333;</i>
<div class="sliderwrap il">
<input id="sliderW" onchange="setColor(0)" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
<div id="Segments" class="center">
<div id="segcont"></div>
</div>
<div id="QuickLoad" class="center">
<p class="label h">Quick Load</p>
<div id="pql"></div>
</div>
<div id="Presets" class="center" style="display:none;">
<p class="label h">Presets</p>
<div class="fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'pcont')" onfocus="search(this)" />
<span onclick="clean(this)" class="icons">&#xe38f;</span>
<div class="icons"><svg xmlns='http://www.w3.org/2000/svg' class='fndIcn'><circle cx='8' cy='8' r='6' /><line x1='12' y1='12' x2='24' y2='12' transform='rotate(45,12,12)' /></svg></div>
</div>
<div id="pcont" class="list"></div>
</div>
<div id="Effects" class="center">
<p class="label h">Effect</p>
<!--p class="label h">Effect speed</p-->
<div title="Effect speed">
<i class="icons slider-icon">&#xe325;</i>
<div class="sliderwrap il">
<input id="sliderSpeed" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
<!--p class="label h">Effect intensity</p-->
<div title="Effect intensity">
<i class="icons slider-icon" onclick="tglLabels()">&#xe409;</i>
<div class="sliderwrap il">
<input id="sliderIntensity" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
<div style="padding-bottom:20px;">
<div onclick="tglFxDropdown()" class="c btn" id="fxBtn"><i class="icons">&#xe0e8;</i> Solid</div>
<div onclick="tglPalDropdown()" class="c btn" id="palBtn"><i class="icons">&#xe2b3;</i>Default</div>
<div id="fxDropdown" class="dd-content">
<div class="fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this)" />
<span onclick="clean(this)" class="icons">&#xe38f;</span>
<div class="icons"><svg xmlns='http://www.w3.org/2000/svg' class='fndIcn'><circle cx='8' cy='8' r='6' /><line x1='12' y1='12' x2='24' y2='12' transform='rotate(45,12,12)' /></svg></div>
</div>
<div id="fxlist" class="list">
<div class="lstI" data-id="0" onClick="setX(0)"><a href="#0" onClick="setX(0)">Solid</a></div>
</div>
</div>
<div id="palDropdown" class="dd-content">
<div class="fnd">
<input type="text" class="fnd" placeholder="Search" oninput="search(this,'pallist')" onfocus="search(this)" />
<span onclick="clean(this)" class="icons">&#xe38f;</span>
<div class="icons"><svg xmlns='http://www.w3.org/2000/svg' class='fndIcn'><circle cx='8' cy='8' r='6' /><line x1='12' y1='12' x2='24' y2='12' transform='rotate(45,12,12)' /></svg></div>
</div>
<div id="pallist" class="list">
<div class="lstI" data-id="0" onClick="setPalette(0)"><a href="#0" onClick="setPalette(0)">Default</a><div class="lstIprev"></div></div>
</div>
</div>
<br>
</div>
</div>
</div>
</div>
<div id="connind"></div>
<div id="toast"></div>
<div id="namelabel" onclick="toggleNodes()"></div>
<div id="info" class="modal">
<div id="imgw">
<img class="wi" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAFCAYAAAC5Fuf5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABbSURBVChTlY9bDoAwDMNW7n9nwCipytQN4Z8tbrTHmDmF4oPzyldwRqp1SSdnV/NuZuzqerAByxXznBw3igkeFEfXyUuhK/yFM0CxJfyqXZEOc6/Sr9/bf7uIC5Nwd7orMvAPAAAAAElFTkSuQmCC" />
</div><br>
<div id="kv">Loading...</div><br>
<button class="btn infobtn" onclick="loadInfo()">Refresh</button>
<button class="btn infobtn" onclick="toggleInfo()">Close Info</button><br>
<button class="btn infobtn" onclick="toggleNodes()">Instance List</button>
<button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button><br>
<span class="h">Made with <span id="heart">&#10084;&#xFE0E;</span> by Aircoookie and the WLED community</span>
</div>
<div id="nodes" class="modal">
<div id="ndlt">WLED instances</div>
<div id="kn">Loading...</div><br>
<button class="btn infobtn" onclick="loadNodes()">Refresh</button>
<button class="btn infobtn" onclick="toggleNodes()">Close list</button><br>
</div>
<i id="roverstar" class="icons huge" onclick="setLor(0)">&#xe410;</i><br>
<script src="iro.js"></script>
<script src="rangetouch.js"></script>
<script src="simple.js"></script>
</body>
</html>

1398
wled00/data/simple.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -14,13 +14,21 @@ button, .btn {
color: #fff; color: #fff;
font-family: Verdana, sans-serif; font-family: Verdana, sans-serif;
border: 0.3ch solid #333; border: 0.3ch solid #333;
border-radius: 24px;
display: inline-block; display: inline-block;
font-size: 20px; font-size: 20px;
margin: 12px 8px 8px; margin: 12px 8px 8px;
padding: 1px 6px; padding: 8px 12px;
min-width: 48px;
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
} }
.toprow {
top: 0;
position: sticky;
background-color:#222;
z-index:1;
}
.lnk { .lnk {
border: 0; border: 0;
} }
@ -35,8 +43,12 @@ input {
font-family: Verdana, sans-serif; font-family: Verdana, sans-serif;
border: 0.5ch solid #333; border: 0.5ch solid #333;
} }
input[type="text"] {
font-size: medium;
}
input[type="number"] { input[type="number"] {
width: 4em; width: 4em;
font-size: medium;
margin: 2px; margin: 2px;
} }
input[type="number"].xxl { input[type="number"].xxl {
@ -57,8 +69,12 @@ input[type="number"].s {
input[type="number"].xs { input[type="number"].xs {
width: 42px; width: 42px;
} }
select {
margin: 2px;
font-size: medium;
}
input[type="checkbox"] { input[type="checkbox"] {
transform: scale(1.5); transform: scale(2);
margin-right: 10px; margin-right: 10px;
} }
select { select {

View File

@ -15,9 +15,11 @@ void handleAlexa();
void onAlexaChange(EspalexaDevice* dev); void onAlexaChange(EspalexaDevice* dev);
//blynk.cpp //blynk.cpp
#ifndef WLED_DISABLE_BLYNK
void initBlynk(const char* auth, const char* host, uint16_t port); void initBlynk(const char* auth, const char* host, uint16_t port);
void handleBlynk(); void handleBlynk();
void updateBlynk(); void updateBlynk();
#endif
//button.cpp //button.cpp
void shortPressAction(uint8_t b=0); void shortPressAction(uint8_t b=0);
@ -94,7 +96,7 @@ void sendHuePoll();
void onHueData(void* arg, AsyncClient* client, void *data, size_t len); void onHueData(void* arg, AsyncClient* client, void *data, size_t len);
//ir.cpp //ir.cpp
bool decodeIRCustom(uint32_t code); //bool decodeIRCustom(uint32_t code);
void applyRepeatActions(); void applyRepeatActions();
void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF); void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF);
void changeEffectSpeed(int8_t amount); void changeEffectSpeed(int8_t amount);
@ -195,6 +197,7 @@ bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte
//udp.cpp //udp.cpp
void notify(byte callMode, bool followUp=false); void notify(byte callMode, bool followUp=false);
void realtimeBoroadcast(IPAddress client, uint16_t length, byte *buffer, bool isRGBW);
void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC);
void handleNotifications(); void handleNotifications();
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w);
@ -224,14 +227,11 @@ class UsermodManager {
public: public:
void loop(); void loop();
void setup(); void setup();
void connected(); void connected();
void addToJsonState(JsonObject& obj); void addToJsonState(JsonObject& obj);
void addToJsonInfo(JsonObject& obj); void addToJsonInfo(JsonObject& obj);
void readFromJsonState(JsonObject& obj); void readFromJsonState(JsonObject& obj);
void addToConfig(JsonObject& obj); void addToConfig(JsonObject& obj);
bool readFromConfig(JsonObject& obj); bool readFromConfig(JsonObject& obj);
void onMqttConnect(bool sessionPresent); void onMqttConnect(bool sessionPresent);

View File

@ -380,10 +380,10 @@ String getContentType(AsyncWebServerRequest* request, String filename){
else if(filename.endsWith(".htm")) return "text/html"; else if(filename.endsWith(".htm")) return "text/html";
else if(filename.endsWith(".html")) return "text/html"; else if(filename.endsWith(".html")) return "text/html";
else if(filename.endsWith(".css")) return "text/css"; else if(filename.endsWith(".css")) return "text/css";
// else if(filename.endsWith(".js")) return "application/javascript"; else if(filename.endsWith(".js")) return "application/javascript";
else if(filename.endsWith(".json")) return "application/json"; else if(filename.endsWith(".json")) return "application/json";
else if(filename.endsWith(".png")) return "image/png"; else if(filename.endsWith(".png")) return "image/png";
// else if(filename.endsWith(".gif")) return "image/gif"; else if(filename.endsWith(".gif")) return "image/gif";
else if(filename.endsWith(".jpg")) return "image/jpeg"; else if(filename.endsWith(".jpg")) return "image/jpeg";
else if(filename.endsWith(".ico")) return "image/x-icon"; else if(filename.endsWith(".ico")) return "image/x-icon";
// else if(filename.endsWith(".xml")) return "text/xml"; // else if(filename.endsWith(".xml")) return "text/xml";

View File

@ -42,7 +42,7 @@ function B(){window.history.back()}function U(){document.getElementById("uf").st
.bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%}#msg{display:none} .bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%}#msg{display:none}
</style></head><body><h2>WLED Software Update</h2><form method="POST" </style></head><body><h2>WLED Software Update</h2><form method="POST"
action="/update" id="uf" enctype="multipart/form-data" onsubmit="U()"> action="/update" id="uf" enctype="multipart/form-data" onsubmit="U()">
Installed version: 0.13.0-b2<br>Download the latest binary: <a Installed version: 0.13.0-bl3<br>Download the latest binary: <a
href="https://github.com/Aircoookie/WLED/releases" target="_blank"><img href="https://github.com/Aircoookie/WLED/releases" target="_blank"><img
src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"> src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square">
</a><br><input type="file" class="bt" name="update" required><br><input </a><br><input type="file" class="bt" name="update" required><br><input

File diff suppressed because one or more lines are too long

1819
wled00/html_simple.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -101,7 +101,7 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len)
hueError = HUE_ERROR_JSON_PARSING; return; hueError = HUE_ERROR_JSON_PARSING; return;
} }
int hueErrorCode = root[0][F("error")][F("type")]; int hueErrorCode = root[0][F("error")]["type"];
if (hueErrorCode)//hue bridge returned error if (hueErrorCode)//hue bridge returned error
{ {
hueError = hueErrorCode; hueError = hueErrorCode;

View File

@ -79,6 +79,9 @@ void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
} }
} }
/*
* This is no longer needed due to JSON IR mod
*
//Add what your custom IR codes should trigger here. Guide: https://github.com/Aircoookie/WLED/wiki/Infrared-Control //Add what your custom IR codes should trigger here. Guide: https://github.com/Aircoookie/WLED/wiki/Infrared-Control
//IR codes themselves can be defined directly after "case" or in "ir_codes.h" //IR codes themselves can be defined directly after "case" or in "ir_codes.h"
bool decodeIRCustom(uint32_t code) bool decodeIRCustom(uint32_t code)
@ -94,6 +97,7 @@ bool decodeIRCustom(uint32_t code)
if (code != IRCUSTOM_MACRO1) colorUpdated(CALL_MODE_BUTTON); //don't update color again if we apply macro, it already does it if (code != IRCUSTOM_MACRO1) colorUpdated(CALL_MODE_BUTTON); //don't update color again if we apply macro, it already does it
return true; return true;
} }
*/
void relativeChange(byte* property, int8_t amount, byte lowerBoundary, byte higherBoundary) void relativeChange(byte* property, int8_t amount, byte lowerBoundary, byte higherBoundary)
{ {
@ -162,9 +166,10 @@ void decodeIR(uint32_t code)
return; return;
} }
lastValidCode = 0; irTimesRepeated = 0; lastValidCode = 0; irTimesRepeated = 0;
if (decodeIRCustom(code)) return; //if (decodeIRCustom(code)) return;
if (irEnabled == 8) { // any remote configurable with ir.json file if (irEnabled == 8) { // any remote configurable with ir.json file
decodeIRJson(code); decodeIRJson(code);
colorUpdated(CALL_MODE_BUTTON);
return; return;
} }
if (code > 0xFFFFFF) return; //invalid code if (code > 0xFFFFFF) return; //invalid code
@ -566,14 +571,17 @@ Sample:
void decodeIRJson(uint32_t code) void decodeIRJson(uint32_t code)
{ {
char objKey[10]; char objKey[10];
const char* cmd;
String cmdStr; String cmdStr;
DynamicJsonDocument irDoc(JSON_BUFFER_SIZE); DynamicJsonDocument irDoc(JSON_BUFFER_SIZE);
JsonObject fdo; JsonObject fdo;
JsonObject jsonCmdObj; JsonObject jsonCmdObj;
sprintf(objKey, "\"0x%X\":", code); sprintf_P(objKey, PSTR("\"0x%lX\":"), (unsigned long)code);
// attempt to read command from ir.json
// this may fail for two reasons: ir.json does not exist or IR code not found
// if the IR code is not found readObjectFromFile() will clean() irDoc JSON document
// so we can differentiate between the two
readObjectFromFile("/ir.json", objKey, &irDoc); readObjectFromFile("/ir.json", objKey, &irDoc);
fdo = irDoc.as<JsonObject>(); fdo = irDoc.as<JsonObject>();
lastValidCode = 0; lastValidCode = 0;
@ -583,8 +591,7 @@ void decodeIRJson(uint32_t code)
return; return;
} }
cmd = fdo["cmd"]; //string cmdStr = fdo["cmd"].as<String>();;
cmdStr = String(cmd);
jsonCmdObj = fdo["cmd"]; //object jsonCmdObj = fdo["cmd"]; //object
if (!cmdStr.isEmpty()) if (!cmdStr.isEmpty())
@ -617,8 +624,8 @@ void decodeIRJson(uint32_t code)
if (!cmdStr.startsWith("win&")) { if (!cmdStr.startsWith("win&")) {
cmdStr = "win&" + cmdStr; cmdStr = "win&" + cmdStr;
} }
handleSet(nullptr, cmdStr, false); handleSet(nullptr, cmdStr, false);
} }
} else if (!jsonCmdObj.isNull()) { } else if (!jsonCmdObj.isNull()) {
// command is JSON object // command is JSON object
//allow applyPreset() to reuse JSON buffer, or it would alloc. a second buffer and run out of mem. //allow applyPreset() to reuse JSON buffer, or it would alloc. a second buffer and run out of mem.
@ -653,9 +660,7 @@ void handleIR()
{ {
if (results.value != 0) // only print results if anything is received ( != 0 ) if (results.value != 0) // only print results if anything is received ( != 0 )
{ {
Serial.print("IR recv\r\n0x"); DEBUG_PRINTF("IR recv: 0x%lX\n", (unsigned long)results.value);
Serial.println((uint32_t)results.value, HEX);
Serial.println();
} }
decodeIR(results.value); decodeIR(results.value);
irrecv->resume(); irrecv->resume();

View File

@ -17,11 +17,36 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t start = elem[F("start")] | seg.start; uint16_t start = elem[F("start")] | seg.start;
int stop = elem["stop"] | -1; int stop = elem["stop"] | -1;
if (stop < 0) { if (stop < 0) {
uint16_t len = elem[F("len")]; uint16_t len = elem[F("len")];
stop = (len > 0) ? start + len : seg.stop; stop = (len > 0) ? start + len : seg.stop;
} }
if (elem["n"]) {
// name field exists
if (seg.name) { //clear old name
delete[] seg.name;
seg.name = nullptr;
}
const char * name = elem["n"].as<const char*>();
size_t len = 0;
if (name != nullptr) len = strlen(name);
if (len > 0 && len < 33) {
seg.name = new char[len+1];
if (seg.name) strlcpy(seg.name, name, 33);
} else {
// but is empty (already deleted above)
elem.remove("n");
}
} else if (start != seg.start || stop != seg.stop) {
// clearing or setting segment without name field
if (seg.name) {
delete[] seg.name;
seg.name = nullptr;
}
}
uint16_t grp = elem["grp"] | seg.grouping; uint16_t grp = elem["grp"] | seg.grouping;
uint16_t spc = elem[F("spc")] | seg.spacing; uint16_t spc = elem[F("spc")] | seg.spacing;
strip.setSegment(id, start, stop, grp, spc); strip.setSegment(id, start, stop, grp, spc);
@ -54,38 +79,48 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
{ {
int rgbw[] = {0,0,0,0}; int rgbw[] = {0,0,0,0};
bool colValid = false; bool colValid = false;
JsonArray colX = colarr[i]; if (colarr[i].is<unsigned long>()) {
if (colX.isNull()) { // unsigned long RGBW (@blazoncek v2 experimental API implementation)
byte brgbw[] = {0,0,0,0}; uint32_t colX = colarr[i];
const char* hexCol = colarr[i]; rgbw[0] = (colX >> 16) & 0xFF;
if (hexCol == nullptr) { //Kelvin color temperature (or invalid), e.g 2400 rgbw[1] = (colX >> 8) & 0xFF;
int kelvin = colarr[i] | -1; rgbw[2] = (colX ) & 0xFF;
if (kelvin < 0) continue; rgbw[3] = (colX >> 24) & 0xFF;
if (kelvin == 0) seg.setColor(i, 0, id);
if (kelvin > 0) colorKtoRGB(kelvin, brgbw);
colValid = true;
} else { //HEX string, e.g. "FFAA00"
colValid = colorFromHexString(brgbw, hexCol);
}
for (uint8_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
} else { //Array of ints (RGB or RGBW color), e.g. [255,160,0]
byte sz = colX.size();
if (sz == 0) continue; //do nothing on empty array
byte cp = copyArray(colX, rgbw, 4);
if (cp == 1 && rgbw[0] == 0)
seg.setColor(i, 0, id);
colValid = true; colValid = true;
} } else {
JsonArray colX = colarr[i];
if (colX.isNull()) {
byte brgbw[] = {0,0,0,0};
const char* hexCol = colarr[i];
if (hexCol == nullptr) { //Kelvin color temperature (or invalid), e.g 2400
int kelvin = colarr[i] | -1;
if (kelvin < 0) continue;
if (kelvin == 0) seg.setColor(i, 0, id);
if (kelvin > 0) colorKtoRGB(kelvin, brgbw);
colValid = true;
} else { //HEX string, e.g. "FFAA00"
colValid = colorFromHexString(brgbw, hexCol);
}
for (uint8_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
} else { //Array of ints (RGB or RGBW color), e.g. [255,160,0]
byte sz = colX.size();
if (sz == 0) continue; //do nothing on empty array
if (!colValid) continue; byte cp = copyArray(colX, rgbw, 4);
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment if (cp == 1 && rgbw[0] == 0)
{ seg.setColor(i, 0, id);
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];} colValid = true;
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];} }
} else { //normal case, apply directly to segment
seg.setColor(i, ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF))), id); if (!colValid) continue;
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{
if (i == 0) {col[0] = rgbw[0]; col[1] = rgbw[1]; col[2] = rgbw[2]; col[3] = rgbw[3];}
if (i == 1) {colSec[0] = rgbw[0]; colSec[1] = rgbw[1]; colSec[2] = rgbw[2]; colSec[3] = rgbw[3];}
} else { //normal case, apply directly to segment
seg.setColor(i, ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF))), id);
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
}
} }
} }
} }
@ -136,16 +171,16 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
strip.fill(0); strip.fill(0);
} }
uint16_t start = 0, stop = 0; //uint16_t start = 0, stop = 0;
byte set = 0; //0 nothing set, 1 start set, 2 range set byte set = 0; //0 nothing set, 1 start set, 2 range set
for (uint16_t i = 0; i < iarr.size(); i++) { for (uint16_t i = 0; i < iarr.size(); i++) {
if(iarr[i].is<JsonInteger>()) { if(iarr[i].is<JsonInteger>()) {
if (!set) { if (!set) {
start = iarr[i]; //start = iarr[i];
set = 1; set = 1;
} else { } else {
stop = iarr[i]; //stop = iarr[i];
set = 2; set = 2;
} }
} else { //color } else { //color
@ -161,13 +196,6 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
for (uint8_t c = 0; c < 4; c++) rgbw[c] = brgbw[c]; for (uint8_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
} }
} }
if (set < 2) stop = start + 1;
for (uint16_t i = start; i < stop; i++) {
strip.setPixelColor(i, rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
}
if (!set) start++;
set = 0;
} }
} }
strip.setPixelSegment(255); strip.setPixelSegment(255);
@ -180,6 +208,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
bool deserializeState(JsonObject root, byte callMode, byte presetId) bool deserializeState(JsonObject root, byte callMode, byte presetId)
{ {
DEBUG_PRINTLN(F("Deserializing state"));
strip.applyToAllSelected = false; strip.applyToAllSelected = false;
bool stateResponse = root[F("v")] | false; bool stateResponse = root[F("v")] | false;
@ -200,14 +230,6 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
transitionDelayTemp = transitionDelay; transitionDelayTemp = transitionDelay;
} }
} }
tr = root[F("tt")] | -1;
if (tr >= 0)
{
transitionDelayTemp = tr;
transitionDelayTemp *= 100;
jsonTransitionOnce = true;
}
strip.setTransition(transitionDelayTemp); strip.setTransition(transitionDelayTemp);
tr = root[F("tb")] | -1; tr = root[F("tb")] | -1;
@ -287,16 +309,24 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
usermods.readFromJsonState(root); usermods.readFromJsonState(root);
int8_t ledmap = root[F("ledmap")] | -1;
if (ledmap >= 0) {
strip.deserializeMap(ledmap);
}
int ps = root[F("psave")] | -1; int ps = root[F("psave")] | -1;
if (ps > 0) { if (ps > 0) {
DEBUG_PRINTLN(F("Saving preset"));
savePreset(ps, true, nullptr, root); savePreset(ps, true, nullptr, root);
} else { } else {
ps = root[F("pdel")] | -1; //deletion ps = root[F("pdel")] | -1; //deletion
if (ps > 0) { if (ps > 0) {
DEBUG_PRINTLN(F("Deleting preset"));
deletePreset(ps); deletePreset(ps);
} }
ps = root["ps"] | -1; //load preset (clears state request!) ps = root["ps"] | -1; //load preset (clears state request!)
if (ps >= 0) { if (ps >= 0) {
DEBUG_PRINTLN(F("Applying preset"));
if (!presetId) unloadPlaylist(); //stop playlist if preset changed manually if (!presetId) unloadPlaylist(); //stop playlist if preset changed manually
applyPreset(ps, callMode); applyPreset(ps, callMode);
return stateResponse; return stateResponse;
@ -339,12 +369,15 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
byte segbri = seg.opacity; byte segbri = seg.opacity;
root["bri"] = (segbri) ? segbri : 255; root["bri"] = (segbri) ? segbri : 255;
char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255) if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name);
for (uint8_t i = 0; i < 3; i++) // to conserve RAM we will serialize the col array manually
{ // this will reduce RAM footprint from ~300 bytes to 84 bytes per segment
char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255)
const char *format = strip.isRgbw ? PSTR("[%u,%u,%u,%u]") : PSTR("[%u,%u,%u]");
for (uint8_t i = 0; i < 3; i++)
{
byte segcol[4]; byte* c = segcol; byte segcol[4]; byte* c = segcol;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{ {
c = (i == 0)? col:colSec; c = (i == 0)? col:colSec;
@ -352,14 +385,11 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
segcol[0] = (byte)(seg.colors[i] >> 16); segcol[1] = (byte)(seg.colors[i] >> 8); segcol[0] = (byte)(seg.colors[i] >> 16); segcol[1] = (byte)(seg.colors[i] >> 8);
segcol[2] = (byte)(seg.colors[i]); segcol[3] = (byte)(seg.colors[i] >> 24); segcol[2] = (byte)(seg.colors[i]); segcol[3] = (byte)(seg.colors[i] >> 24);
} }
char tmpcol[22]; char tmpcol[22];
if (strip.isRgbw) sprintf_P(tmpcol, PSTR("[%u,%u,%u,%u]"), c[0], c[1], c[2], c[3]); sprintf_P(tmpcol, format, (unsigned)c[0], (unsigned)c[1], (unsigned)c[2], (unsigned)c[3]);
else sprintf_P(tmpcol, PSTR("[%u,%u,%u]"), c[0], c[1], c[2]); strcat(colstr, i<2 ? strcat_P(tmpcol, PSTR(",")) : tmpcol);
}
strcat(colstr, i<2 ? strcat(tmpcol,",") : tmpcol); strcat_P(colstr, PSTR("]"));
}
strcat(colstr,"]");
root["col"] = serialized(colstr); root["col"] = serialized(colstr);
root["fx"] = seg.mode; root["fx"] = seg.mode;
@ -377,10 +407,11 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root["on"] = (bri > 0); root["on"] = (bri > 0);
root["bri"] = briLast; root["bri"] = briLast;
root[F("transition")] = transitionDelay/100; //in 100ms root[F("transition")] = transitionDelay/100; //in 100ms
root[F("tdd")] = transitionDelayDefault/100; //in 100ms
} }
if (!forPreset) { if (!forPreset) {
if (errorFlag) root[F("error")] = errorFlag; if (errorFlag) {root[F("error")] = errorFlag; errorFlag = ERR_NONE;}
root[F("ps")] = currentPreset; root[F("ps")] = currentPreset;
root[F("pl")] = currentPlaylist; root[F("pl")] = currentPlaylist;
@ -411,6 +442,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
for (byte s = 0; s < strip.getMaxSegments(); s++) for (byte s = 0; s < strip.getMaxSegments(); s++)
{ {
WS2812FX::Segment sg = strip.getSegment(s); WS2812FX::Segment sg = strip.getSegment(s);
// TODO: add logic to stop at 16 segments if using versionAPI==1 on ESP8266
if (sg.isActive()) if (sg.isActive())
{ {
JsonObject seg0 = seg.createNestedObject(); JsonObject seg0 = seg.createNestedObject();
@ -452,6 +484,15 @@ void serializeInfo(JsonObject root)
leds[F("count")] = ledCount; leds[F("count")] = ledCount;
leds[F("rgbw")] = strip.isRgbw; leds[F("rgbw")] = strip.isRgbw;
leds[F("wv")] = strip.isRgbw && (strip.rgbwMode == RGBW_MODE_MANUAL_ONLY || strip.rgbwMode == RGBW_MODE_DUAL); //should a white channel slider be displayed? leds[F("wv")] = strip.isRgbw && (strip.rgbwMode == RGBW_MODE_MANUAL_ONLY || strip.rgbwMode == RGBW_MODE_DUAL); //should a white channel slider be displayed?
JsonArray leds_pin = leds.createNestedArray("pin");
for (uint8_t s=0; s<busses.getNumBusses(); s++) {
Bus *bus = busses.getBus(s);
uint8_t pins[5];
bus->getPins(pins);
leds_pin.add(pins[0]); // need to elaborate this for multipin strips
}
leds[F("pwr")] = strip.currentMilliamps; leds[F("pwr")] = strip.currentMilliamps;
leds[F("fps")] = strip.getFps(); leds[F("fps")] = strip.getFps();
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0; leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
@ -530,9 +571,11 @@ void serializeInfo(JsonObject root)
#endif #endif
root[F("freeheap")] = ESP.getFreeHeap(); root[F("freeheap")] = ESP.getFreeHeap();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) root[F("psram")] = ESP.getFreePsram();
#endif
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967; root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
usermods.addToJsonInfo(root); usermods.addToJsonInfo(root);
byte os = 0; byte os = 0;
@ -655,37 +698,37 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
curPalette.add("r"); curPalette.add("r");
break; break;
case 2: //primary color only case 2: //primary color only
curPalette.add(F("c1")); curPalette.add("c1");
break; break;
case 3: //primary + secondary case 3: //primary + secondary
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c2")); curPalette.add("c2");
break; break;
case 4: //primary + secondary + tertiary case 4: //primary + secondary + tertiary
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c1")); curPalette.add("c1");
break; break;
case 5: {//primary + secondary (+tert if not off), more distinct case 5: {//primary + secondary (+tert if not off), more distinct
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c1")); curPalette.add("c1");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c2")); curPalette.add("c2");
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c3")); curPalette.add("c3");
curPalette.add(F("c1")); curPalette.add("c1");
break;} break;}
case 6: //Party colors case 6: //Party colors
setPaletteColors(curPalette, PartyColors_p); setPaletteColors(curPalette, PartyColors_p);
@ -793,8 +836,7 @@ void serveJson(AsyncWebServerRequest* request)
} }
} }
DEBUG_PRINT("JSON buffer size: "); DEBUG_PRINTF("JSON buffer size: %u for request: %d\n", doc.memoryUsage(), subJson);
DEBUG_PRINTLN(doc.memoryUsage());
response->setLength(); response->setLength();
request->send(response); request->send(response);
@ -804,13 +846,13 @@ void serveJson(AsyncWebServerRequest* request)
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient) bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
{ {
#ifdef WLED_ENABLE_WEBSOCKETS
AsyncWebSocketClient * wsc = nullptr; AsyncWebSocketClient * wsc = nullptr;
if (!request) { //not HTTP, use Websockets if (!request) { //not HTTP, use Websockets
#ifdef WLED_ENABLE_WEBSOCKETS
wsc = ws.client(wsClient); wsc = ws.client(wsClient);
if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free
#endif
} }
#endif
uint16_t used = ledCount; uint16_t used = ledCount;
uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS

View File

@ -190,8 +190,10 @@ void updateInterfaces(uint8_t callMode)
espalexaDevice->setColor(col[0], col[1], col[2]); espalexaDevice->setColor(col[0], col[1], col[2]);
} }
#endif #endif
#ifndef WLED_DISABLE_BLYNK
if (callMode != CALL_MODE_BLYNK && if (callMode != CALL_MODE_BLYNK &&
callMode != CALL_MODE_NO_NOTIFY) updateBlynk(); callMode != CALL_MODE_NO_NOTIFY) updateBlynk();
#endif
doPublishMqtt = true; doPublishMqtt = true;
lastInterfaceUpdate = millis(); lastInterfaceUpdate = millis();
} }
@ -285,7 +287,9 @@ void handleNightlight()
setLedsStandard(); setLedsStandard();
} }
} }
#ifndef WLED_DISABLE_BLYNK
updateBlynk(); updateBlynk();
#endif
if (macroNl > 0) if (macroNl > 0)
applyPreset(macroNl); applyPreset(macroNl);
nightlightActiveOld = false; nightlightActiveOld = false;

View File

@ -124,22 +124,22 @@ void publishMqtt()
sprintf_P(s, PSTR("%u"), bri); sprintf_P(s, PSTR("%u"), bri);
strlcpy(subuf, mqttDeviceTopic, 33); strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/g")); strcat_P(subuf, PSTR("/g"));
mqtt->publish(subuf, 0, true, s); mqtt->publish(subuf, 0, true, s); // retain message
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2])); sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
strlcpy(subuf, mqttDeviceTopic, 33); strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/c")); strcat_P(subuf, PSTR("/c"));
mqtt->publish(subuf, 0, true, s); mqtt->publish(subuf, 0, true, s); // retain message
strlcpy(subuf, mqttDeviceTopic, 33); strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/status")); strcat_P(subuf, PSTR("/status"));
mqtt->publish(subuf, 0, true, "online"); mqtt->publish(subuf, 0, true, "online"); // retain message for a LWT
char apires[1024]; char apires[1024]; // allocating 1024 bytes from stack can be risky
XML_response(nullptr, apires); XML_response(nullptr, apires);
strlcpy(subuf, mqttDeviceTopic, 33); strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/v")); strcat_P(subuf, PSTR("/v"));
mqtt->publish(subuf, 0, false, apires); mqtt->publish(subuf, 0, false, apires); // do not retain message
} }
@ -169,7 +169,7 @@ bool initMqtt()
strlcpy(mqttStatusTopic, mqttDeviceTopic, 33); strlcpy(mqttStatusTopic, mqttDeviceTopic, 33);
strcat_P(mqttStatusTopic, PSTR("/status")); strcat_P(mqttStatusTopic, PSTR("/status"));
mqtt->setWill(mqttStatusTopic, 0, true, "offline"); mqtt->setWill(mqttStatusTopic, 0, true, "offline"); // LWT message
mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME); mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME);
mqtt->connect(); mqtt->connect();
return true; return true;

View File

@ -1,6 +1,17 @@
#include "src/dependencies/timezone/Timezone.h" #include "src/dependencies/timezone/Timezone.h"
#include "wled.h" #include "wled.h"
#include "wled_math.h" #ifndef WLED_USE_REAL_MATH
#include "wled_math.h"
#else
#define sin_t sin
#define cos_t cos
#define tan_t tan
#define asin_t asin
#define acos_t acos
#define atan_t atan
#define fmod_t fmod
#define floor_t floor
#endif
/* /*
* Acquires time from NTP server * Acquires time from NTP server

View File

@ -1,6 +1,7 @@
#include "pin_manager.h" #include "pin_manager.h"
#include "wled.h" #include "wled.h"
#ifdef WLED_DEBUG
static void DebugPrintOwnerTag(PinOwner tag) static void DebugPrintOwnerTag(PinOwner tag)
{ {
uint32_t q = static_cast<uint8_t>(tag); uint32_t q = static_cast<uint8_t>(tag);
@ -10,6 +11,7 @@ static void DebugPrintOwnerTag(PinOwner tag)
DEBUG_PRINT(F("(no owner)")); DEBUG_PRINT(F("(no owner)"));
} }
} }
#endif
/// Actual allocation/deallocation routines /// Actual allocation/deallocation routines
bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag) bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
@ -19,12 +21,14 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
// if a non-zero ownerTag, only allow de-allocation if the owner's tag is provided // if a non-zero ownerTag, only allow de-allocation if the owner's tag is provided
if ((ownerTag[gpio] != PinOwner::None) && (ownerTag[gpio] != tag)) { if ((ownerTag[gpio] != PinOwner::None) && (ownerTag[gpio] != tag)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN DEALLOC: IO ")); DEBUG_PRINT(F("PIN DEALLOC: IO "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" allocated by ")); DEBUG_PRINT(F(" allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]); DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINT(F(", but attempted de-allocation by ")); DEBUG_PRINT(F(", but attempted de-allocation by "));
DebugPrintOwnerTag(tag); DebugPrintOwnerTag(tag);
#endif
return false; return false;
} }
@ -34,6 +38,7 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
ownerTag[gpio] = PinOwner::None; ownerTag[gpio] = PinOwner::None;
return true; return true;
} }
bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag) bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag)
{ {
bool shouldFail = false; bool shouldFail = false;
@ -46,17 +51,21 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
continue; continue;
} }
if (!isPinOk(gpio, mptArray[i].isOutput)) { if (!isPinOk(gpio, mptArray[i].isOutput)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Invalid pin attempted to be allocated: ")); DEBUG_PRINT(F("PIN ALLOC: Invalid pin attempted to be allocated: "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINTLN(F("")); DEBUG_PRINTLN(F(""));
#endif
shouldFail = true; shouldFail = true;
} }
if (isPinAllocated(gpio)) { if (isPinAllocated(gpio)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: FAIL: IO ")); DEBUG_PRINT(F("PIN ALLOC: FAIL: IO "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" already allocated by ")); DEBUG_PRINT(F(" already allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]); DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINTLN(F("")); DEBUG_PRINTLN(F(""));
#endif
shouldFail = true; shouldFail = true;
} }
} }
@ -79,15 +88,18 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
} }
return true; return true;
} }
bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag) bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
{ {
if (!isPinOk(gpio, output)) return false; if (!isPinOk(gpio, output)) return false;
if (isPinAllocated(gpio)) { if (isPinAllocated(gpio)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Pin ")); DEBUG_PRINT(F("PIN ALLOC: Pin "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" already allocated by ")); DEBUG_PRINT(F(" already allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]); DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINTLN(F("")); DEBUG_PRINTLN(F(""));
#endif
return false; return false;
} }
@ -106,7 +118,7 @@ bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag)
if (!isPinOk(gpio, false)) return true; if (!isPinOk(gpio, false)) return true;
if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false; if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false;
byte by = gpio >> 3; byte by = gpio >> 3;
byte bi = gpio - 8*by; byte bi = gpio - (by<<3);
return bitRead(pinAlloc[by], bi); return bitRead(pinAlloc[by], bi);
} }

View File

@ -7,8 +7,8 @@
#include "const.h" // for USERMOD_* values #include "const.h" // for USERMOD_* values
typedef struct PinManagerPinType { typedef struct PinManagerPinType {
int8_t pin; int8_t pin;
uint8_t isOutput; bool isOutput;
} managed_pin_type; } managed_pin_type;
/* /*
@ -23,7 +23,6 @@ typedef struct PinManagerPinType {
enum struct PinOwner : uint8_t { enum struct PinOwner : uint8_t {
None = 0, // default == legacy == unspecified owner None = 0, // default == legacy == unspecified owner
// High bit is set for all built-in pin owners // High bit is set for all built-in pin owners
// StatusLED -- THIS SHOULD NEVER BE ALLOCATED -- see handleStatusLED()
Ethernet = 0x81, Ethernet = 0x81,
BusDigital = 0x82, BusDigital = 0x82,
BusDigital2 = 0x83, BusDigital2 = 0x83,
@ -88,7 +87,9 @@ class PinManagerClass {
#endif #endif
inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); } inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); }
// will return true for reserved pins
bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None); bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None);
// will return false for reserved pins
bool isPinOk(byte gpio, bool output = true); bool isPinOk(byte gpio, bool output = true);
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32

View File

@ -90,7 +90,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
} }
strip.isRgbw = false;
uint8_t colorOrder, type, skip; uint8_t colorOrder, type, skip;
uint16_t length, start; uint16_t length, start;
uint8_t pins[5] = {255, 255, 255, 255, 255}; uint8_t pins[5] = {255, 255, 255, 255, 255};
@ -105,6 +104,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
char ls[4] = "LS"; ls[2] = 48+s; ls[3] = 0; //strip start LED char ls[4] = "LS"; ls[2] = 48+s; ls[3] = 0; //strip start LED
char cv[4] = "CV"; cv[2] = 48+s; cv[3] = 0; //strip reverse char cv[4] = "CV"; cv[2] = 48+s; cv[3] = 0; //strip reverse
char sl[4] = "SL"; sl[2] = 48+s; sl[3] = 0; //skip 1st LED char sl[4] = "SL"; sl[2] = 48+s; sl[3] = 0; //skip 1st LED
//char ew[4] = "EW"; ew[2] = 48+s; ew[3] = 0; //strip RGBW override
if (!request->hasArg(lp)) { if (!request->hasArg(lp)) {
DEBUG_PRINTLN(F("No data.")); break; DEBUG_PRINTLN(F("No data.")); break;
} }
@ -114,25 +114,24 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
pins[i] = (request->arg(lp).length() > 0) ? request->arg(lp).toInt() : 255; pins[i] = (request->arg(lp).length() > 0) ? request->arg(lp).toInt() : 255;
} }
type = request->arg(lt).toInt(); type = request->arg(lt).toInt();
strip.isRgbw = strip.isRgbw || BusManager::isRgbw(type); //if (request->hasArg(ew)) SET_BIT(type,7); else UNSET_BIT(type,7); // hack bit 7 to indicate RGBW (as a LED type override if necessary)
skip = request->hasArg(sl) ? LED_SKIP_AMOUNT : 0; skip = request->hasArg(sl) ? LED_SKIP_AMOUNT : 0;
colorOrder = request->arg(co).toInt();
start = (request->hasArg(ls)) ? request->arg(ls).toInt() : t;
if (request->hasArg(lc) && request->arg(lc).toInt() > 0) { if (request->hasArg(lc) && request->arg(lc).toInt() > 0) {
length = request->arg(lc).toInt(); t += length = request->arg(lc).toInt();
} else { } else {
break; // no parameter break; // no parameter
} }
colorOrder = request->arg(co).toInt(); // actual finalization is done in WLED::loop() (removing old busses and adding new)
start = (request->hasArg(ls)) ? request->arg(ls).toInt() : 0;
if (busConfigs[s] != nullptr) delete busConfigs[s]; if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder, request->hasArg(cv), skip); busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder, request->hasArg(cv), skip);
doInitBusses = true; doInitBusses = true;
} }
t = request->arg(F("LC")).toInt(); ledCount = request->arg(F("LC")).toInt();
if (t > 0 && t <= MAX_LEDS) ledCount = t;
// upate other pins // upate other pins
int hw_ir_pin = request->arg(F("IR")).toInt(); int hw_ir_pin = request->arg(F("IR")).toInt();
@ -181,7 +180,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
fadeTransition = request->hasArg(F("TF")); fadeTransition = request->hasArg(F("TF"));
t = request->arg(F("TD")).toInt(); t = request->arg(F("TD")).toInt();
if (t > 0) transitionDelay = t; if (t >= 0) transitionDelay = t;
transitionDelayDefault = t; transitionDelayDefault = t;
strip.paletteFade = request->hasArg(F("PF")); strip.paletteFade = request->hasArg(F("PF"));
@ -202,6 +201,13 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
{ {
strlcpy(serverDescription, request->arg(F("DS")).c_str(), 33); strlcpy(serverDescription, request->arg(F("DS")).c_str(), 33);
syncToggleReceive = request->hasArg(F("ST")); syncToggleReceive = request->hasArg(F("ST"));
#ifndef WLED_DISABLE_SIMPLE_UI
if (simplifiedUI ^ request->hasArg(F("SU"))) {
// UI selection changed, invalidate browser cache
cacheInvalidate++;
}
simplifiedUI = request->hasArg(F("SU"));
#endif
} }
//SYNC //SYNC
@ -595,9 +601,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
} }
uint16_t startI = mainseg.start; uint16_t startI = mainseg.start;
uint16_t stopI = mainseg.stop; uint16_t stopI = mainseg.stop;
uint8_t grpI = mainseg.grouping; uint8_t grpI = mainseg.grouping;
uint16_t spcI = mainseg.spacing; uint16_t spcI = mainseg.spacing;
pos = req.indexOf(F("&S=")); //segment start pos = req.indexOf(F("&S=")); //segment start
if (pos > 0) { if (pos > 0) {
startI = getNumVal(&req, pos); startI = getNumVal(&req, pos);
@ -617,6 +623,30 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
} }
strip.setSegment(selectedSeg, startI, stopI, grpI, spcI); strip.setSegment(selectedSeg, startI, stopI, grpI, spcI);
pos = req.indexOf(F("RV=")); //Segment reverse
if (pos > 0) mainseg.setOption(SEG_OPTION_REVERSED, req.charAt(pos+3) != '0');
pos = req.indexOf(F("MI=")); //Segment mirror
if (pos > 0) mainseg.setOption(SEG_OPTION_MIRROR, req.charAt(pos+3) != '0');
pos = req.indexOf(F("SB=")); //Segment brightness/opacity
if (pos > 0) {
byte segbri = getNumVal(&req, pos);
mainseg.setOption(SEG_OPTION_ON, segbri, selectedSeg);
if (segbri) {
mainseg.setOpacity(segbri, selectedSeg);
}
}
pos = req.indexOf(F("SW=")); //segment power
if (pos > 0) {
switch (getNumVal(&req, pos)) {
case 0: mainseg.setOption(SEG_OPTION_ON, false); break;
case 1: mainseg.setOption(SEG_OPTION_ON, true); break;
default: mainseg.setOption(SEG_OPTION_ON, !mainseg.getOption(SEG_OPTION_ON)); break;
}
}
pos = req.indexOf(F("PS=")); //saves current in preset pos = req.indexOf(F("PS=")); //saves current in preset
if (pos > 0) savePreset(getNumVal(&req, pos)); if (pos > 0) savePreset(getNumVal(&req, pos));
@ -707,7 +737,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
strip.applyToAllSelected = true; strip.applyToAllSelected = true;
strip.setColor(2, t[0], t[1], t[2], t[3]); strip.setColor(2, t[0], t[1], t[2], t[3]);
} else { } else {
strip.getSegment(selectedSeg).setColor(2,((t[0] << 16) + (t[1] << 8) + t[2] + (t[3] << 24)), selectedSeg); mainseg.setColor(2,((t[0] << 16) + (t[1] << 8) + t[2] + (t[3] << 24)), selectedSeg);
} }
} }
@ -811,24 +841,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("TT=")); pos = req.indexOf(F("TT="));
if (pos > 0) transitionDelay = getNumVal(&req, pos); if (pos > 0) transitionDelay = getNumVal(&req, pos);
//Segment reverse
pos = req.indexOf(F("RV="));
if (pos > 0) strip.getSegment(selectedSeg).setOption(SEG_OPTION_REVERSED, req.charAt(pos+3) != '0');
//Segment reverse
pos = req.indexOf(F("MI="));
if (pos > 0) strip.getSegment(selectedSeg).setOption(SEG_OPTION_MIRROR, req.charAt(pos+3) != '0');
//Segment brightness/opacity
pos = req.indexOf(F("SB="));
if (pos > 0) {
byte segbri = getNumVal(&req, pos);
strip.getSegment(selectedSeg).setOption(SEG_OPTION_ON, segbri, selectedSeg);
if (segbri) {
strip.getSegment(selectedSeg).setOpacity(segbri, selectedSeg);
}
}
//set time (unix timestamp) //set time (unix timestamp)
pos = req.indexOf(F("ST=")); pos = req.indexOf(F("ST="));
if (pos > 0) { if (pos > 0) {

View File

@ -91,6 +91,12 @@ void notify(byte callMode, bool followUp)
} }
void realtimeBoroadcast(IPAddress client, uint16_t length, byte *buffer, bool isRGBW)
{
}
void realtimeLock(uint32_t timeoutMs, byte md) void realtimeLock(uint32_t timeoutMs, byte md)
{ {
if (!realtimeMode && !realtimeOverride){ if (!realtimeMode && !realtimeOverride){
@ -102,6 +108,10 @@ void realtimeLock(uint32_t timeoutMs, byte md)
realtimeTimeout = millis() + timeoutMs; realtimeTimeout = millis() + timeoutMs;
if (timeoutMs == 255001 || timeoutMs == 65000) realtimeTimeout = UINT32_MAX; if (timeoutMs == 255001 || timeoutMs == 65000) realtimeTimeout = UINT32_MAX;
// if strip is off (bri==0) and not already in RTM
if (bri == 0 && !realtimeMode) {
strip.setBrightness(scaledBri(briLast));
}
realtimeMode = md; realtimeMode = md;
if (arlsForceMaxBri && !realtimeOverride) strip.setBrightness(scaledBri(255)); if (arlsForceMaxBri && !realtimeOverride) strip.setBrightness(scaledBri(255));
@ -567,7 +577,7 @@ uint8_t* copyRgbwToRgb(uint8_t *destination, uint8_t *source, uint16_t length) {
// buffer - a buffer of at least length*4 bytes long // buffer - a buffer of at least length*4 bytes long
// isRGBW - true if the buffer contains 4 components per pixel // isRGBW - true if the buffer contains 4 components per pixel
// //
void realtimeBoroadcast(IPAddress client, uint16_t length, uint8_t *buffer, bool isRGBW) { void realtimeBroadcast(IPAddress client, uint16_t length, uint8_t *buffer, bool isRGBW) {
WiFiUDP ddpUdp; WiFiUDP ddpUdp;

View File

@ -29,7 +29,7 @@ void sendSysInfoUDP();
// buffer - a buffer of at least length*3 or length*4 bytes long // buffer - a buffer of at least length*3 or length*4 bytes long
// length - the number of pixels // length - the number of pixels
// isRGBW - true if the buffer contains 4 components per pixel // isRGBW - true if the buffer contains 4 components per pixel
void realtimeBoroadcast(IPAddress client, uint16_t length, uint8_t *buffer, bool isRGBW); void realtimeBroadcast(IPAddress client, uint16_t length, uint8_t *buffer, bool isRGBW);
#define DDP_PORT 4048 #define DDP_PORT 4048

View File

@ -86,6 +86,10 @@
#include "../usermods/rgb-rotary-encoder/rgb-rotary-encoder.h" #include "../usermods/rgb-rotary-encoder/rgb-rotary-encoder.h"
#endif #endif
#ifdef USERMOD_ST7789_DISPLAY
#include "../usermods/ST7789_display/ST7789_Display.h"
#endif
void registerUsermods() void registerUsermods()
{ {
/* /*
@ -167,4 +171,8 @@ void registerUsermods()
#ifdef RGB_ROTARY_ENCODER #ifdef RGB_ROTARY_ENCODER
usermods.add(new RgbRotaryEncoderUsermod()); usermods.add(new RgbRotaryEncoderUsermod());
#endif #endif
#ifdef USERMOD_ST7789_DISPLAY
usermods.add(new St7789DisplayUsermod());
#endif
} }

View File

@ -8,6 +8,90 @@
#include "soc/rtc_cntl_reg.h" #include "soc/rtc_cntl_reg.h"
#endif #endif
#ifdef WLED_USE_ETHERNET
// The following six pins are neither configurable nor
// can they be re-assigned through IOMUX / GPIO matrix.
// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface
const managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT] = {
{ 21, true }, // RMII EMAC TX EN == When high, clocks the data on TXD0 and TXD1 to transmitter
{ 19, true }, // RMII EMAC TXD0 == First bit of transmitted data
{ 22, true }, // RMII EMAC TXD1 == Second bit of transmitted data
{ 25, false }, // RMII EMAC RXD0 == First bit of received data
{ 26, false }, // RMII EMAC RXD1 == Second bit of received data
{ 27, true }, // RMII EMAC CRS_DV == Carrier Sense and RX Data Valid
};
const ethernet_settings ethernetBoards[] = {
// None
{
},
// WT32-EHT01
// Please note, from my testing only these pins work for LED outputs:
// IO2, IO4, IO12, IO14, IO15
// These pins do not appear to work from my testing:
// IO35, IO36, IO39
{
1, // eth_address,
16, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO0_IN // eth_clk_mode
},
// ESP32-POE
{
0, // eth_address,
12, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
},
// WESP32
{
0, // eth_address,
-1, // eth_power,
16, // eth_mdc,
17, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO0_IN // eth_clk_mode
},
// QuinLed-ESP32-Ethernet
{
0, // eth_address,
5, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
},
// TwilightLord-ESP32 Ethernet Shield
{
0, // eth_address,
5, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
},
// ESP3DEUXQuattro
{
1, // eth_address,
-1, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
}
};
#endif
/* /*
* Main WLED class implementation. Mostly initialization and connection logic * Main WLED class implementation. Mostly initialization and connection logic
*/ */
@ -120,6 +204,10 @@ void WiFiEvent(WiFiEvent_t event)
void WLED::loop() void WLED::loop()
{ {
#ifdef WLED_DEBUG
static unsigned long maxUsermodMillis = 0;
#endif
handleTime(); handleTime();
handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too
handleConnection(); handleConnection();
@ -130,7 +218,15 @@ void WLED::loop()
handleDMX(); handleDMX();
#endif #endif
userLoop(); userLoop();
#ifdef WLED_DEBUG
unsigned long usermodMillis = millis();
#endif
usermods.loop(); usermods.loop();
#ifdef WLED_DEBUG
usermodMillis = millis() - usermodMillis;
if (usermodMillis > maxUsermodMillis) maxUsermodMillis = usermodMillis;
#endif
yield(); yield();
handleIO(); handleIO();
@ -159,7 +255,9 @@ void WLED::loop()
yield(); yield();
handleHue(); handleHue();
#ifndef WLED_DISABLE_BLYNK
handleBlynk(); handleBlynk();
#endif
yield(); yield();
@ -197,10 +295,12 @@ void WLED::loop()
DEBUG_PRINTLN(F("Re-init busses.")); DEBUG_PRINTLN(F("Re-init busses."));
busses.removeAll(); busses.removeAll();
uint32_t mem = 0; uint32_t mem = 0;
strip.isRgbw = false;
for (uint8_t i = 0; i < WLED_MAX_BUSSES; i++) { for (uint8_t i = 0; i < WLED_MAX_BUSSES; i++) {
if (busConfigs[i] == nullptr) break; if (busConfigs[i] == nullptr) break;
mem += BusManager::memUsage(*busConfigs[i]);
if (mem <= MAX_LED_MEMORY) busses.add(*busConfigs[i]);
/*
// this is done in strip.finalizeInit()
if (busConfigs[i]->adjustBounds(ledCount)) { if (busConfigs[i]->adjustBounds(ledCount)) {
mem += busses.memUsage(*busConfigs[i]); mem += busses.memUsage(*busConfigs[i]);
if (mem <= MAX_LED_MEMORY) { if (mem <= MAX_LED_MEMORY) {
@ -211,20 +311,20 @@ void WLED::loop()
strip.isOffRefreshRequred |= BusManager::isOffRefreshRequred(busConfigs[i]->type); strip.isOffRefreshRequred |= BusManager::isOffRefreshRequred(busConfigs[i]->type);
} }
} }
*/
delete busConfigs[i]; busConfigs[i] = nullptr; delete busConfigs[i]; busConfigs[i] = nullptr;
} }
strip.finalizeInit(ledCount); strip.finalizeInit();
yield(); yield();
serializeConfig(); serializeConfig();
} }
yield(); yield();
handleWs(); handleWs();
handleStatusLED();
// DEBUG serial logging // DEBUG serial logging
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
if (millis() - debugTime > 9999) { if (millis() - debugTime > 29999) {
DEBUG_PRINTLN(F("---DEBUG INFO---")); DEBUG_PRINTLN(F("---DEBUG INFO---"));
DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis()); DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis());
DEBUG_PRINT(F("Unix time: ")); toki.printTime(toki.getTime()); DEBUG_PRINT(F("Unix time: ")); toki.printTime(toki.getTime());
@ -236,17 +336,19 @@ void WLED::loop()
} else } else
DEBUG_PRINTLN(F("No PSRAM")); DEBUG_PRINTLN(F("No PSRAM"));
#endif #endif
DEBUG_PRINT(F("Wifi state: ")); DEBUG_PRINTLN(WiFi.status()); DEBUG_PRINT(F("Wifi state: ")); DEBUG_PRINTLN(WiFi.status());
if (WiFi.status() != lastWifiState) { if (WiFi.status() != lastWifiState) {
wifiStateChangedTime = millis(); wifiStateChangedTime = millis();
} }
lastWifiState = WiFi.status(); lastWifiState = WiFi.status();
DEBUG_PRINT(F("State time: ")); DEBUG_PRINTLN(wifiStateChangedTime); DEBUG_PRINT(F("State time: ")); DEBUG_PRINTLN(wifiStateChangedTime);
DEBUG_PRINT(F("NTP last sync: ")); DEBUG_PRINTLN(ntpLastSyncTime); DEBUG_PRINT(F("NTP last sync: ")); DEBUG_PRINTLN(ntpLastSyncTime);
DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP()); DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP());
DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 10); DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 60);
DEBUG_PRINT(F("Max UM time[ms]: ")); DEBUG_PRINTLN(maxUsermodMillis);
loops = 0; loops = 0;
maxUsermodMillis = 0;
debugTime = millis(); debugTime = millis();
} }
loops++; loops++;
@ -315,14 +417,6 @@ void WLED::setup()
DEBUG_PRINTLN(F("Reading config")); DEBUG_PRINTLN(F("Reading config"));
deserializeConfigFromFS(); deserializeConfigFromFS();
#if STATUSLED
if (!pinManager.isPinAllocated(STATUSLED)) {
// NOTE: Special case: The status LED should *NOT* be allocated.
// See comments in handleStatusLed().
pinMode(STATUSLED, OUTPUT);
}
#endif
DEBUG_PRINTLN(F("Initializing strip")); DEBUG_PRINTLN(F("Initializing strip"));
beginStrip(); beginStrip();
@ -336,9 +430,12 @@ void WLED::setup()
WiFi.onEvent(WiFiEvent); WiFi.onEvent(WiFiEvent);
#endif #endif
#ifdef WLED_ENABLE_ADALIGHT #ifdef WLED_ENABLE_ADALIGHT // reserve GPIO3 (RX) pin for ADALight
if (!pinManager.isPinAllocated(3)) { if (!pinManager.isPinAllocated(3)) {
Serial.println(F("Ada")); Serial.println(F("Ada"));
pinManager.allocatePin(3,false);
} else {
DEBUG_PRINTLN(F("ADALight disabled due to GPIO3 being used."));
} }
#endif #endif
@ -388,22 +485,20 @@ void WLED::setup()
void WLED::beginStrip() void WLED::beginStrip()
{ {
// Initialize NeoPixel Strip and button // Initialize NeoPixel Strip and button
strip.finalizeInit(); // busses created during deserializeConfig()
if (ledCount > MAX_LEDS || ledCount == 0) strip.populateDefaultSegments();
ledCount = 30;
strip.finalizeInit(ledCount);
strip.setBrightness(0); strip.setBrightness(0);
strip.setShowCallback(handleOverlayDraw); strip.setShowCallback(handleOverlayDraw);
if (bootPreset > 0) { if (turnOnAtBoot) {
applyPreset(bootPreset, CALL_MODE_INIT);
} else if (turnOnAtBoot) {
if (briS > 0) bri = briS; if (briS > 0) bri = briS;
else if (bri == 0) bri = 128; else if (bri == 0) bri = 128;
} else { } else {
briLast = briS; bri = 0; briLast = briS; bri = 0;
} }
if (bootPreset > 0) {
applyPreset(bootPreset, CALL_MODE_INIT);
}
colorUpdated(CALL_MODE_INIT); colorUpdated(CALL_MODE_INIT);
// init relay pin // init relay pin
@ -726,39 +821,3 @@ void WLED::handleConnection()
} }
} }
} }
// If status LED pin is allocated for other uses, does nothing
// else blink at 1Hz when WLED_CONNECTED is false (no WiFi, ?? no Ethernet ??)
// else blink at 2Hz when MQTT is enabled but not connected
// else turn the status LED off
void WLED::handleStatusLED()
{
#if STATUSLED
static unsigned long ledStatusLastMillis = 0;
static unsigned short ledStatusType = 0; // current status type - corresponds to number of blinks per second
static bool ledStatusState = 0; // the current LED state
if (pinManager.isPinAllocated(STATUSLED)) {
return; //lower priority if something else uses the same pin
}
ledStatusType = WLED_CONNECTED ? 0 : 2;
if (mqttEnabled && ledStatusType != 2) { // Wi-Fi takes precendence over MQTT
ledStatusType = WLED_MQTT_CONNECTED ? 0 : 4;
}
if (ledStatusType) {
if (millis() - ledStatusLastMillis >= (1000/ledStatusType)) {
ledStatusLastMillis = millis();
ledStatusState = ledStatusState ? 0 : 1;
digitalWrite(STATUSLED, ledStatusState);
}
} else {
#ifdef STATUSLEDINVERTED
digitalWrite(STATUSLED, HIGH);
#else
digitalWrite(STATUSLED, LOW);
#endif
}
#endif
}

View File

@ -3,12 +3,12 @@
/* /*
Main sketch, global variable declarations Main sketch, global variable declarations
@title WLED project sketch @title WLED project sketch
@version 0.13.0-b2 @version 0.13.0-bl2
@author Christian Schwinne @author Christian Schwinne
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2109100 #define VERSION 2109191
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG
@ -31,9 +31,11 @@
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
#define WLED_ENABLE_MQTT // saves 12kb #define WLED_ENABLE_MQTT // saves 12kb
#endif #endif
#define WLED_ENABLE_ADALIGHT // saves 500b only //#define WLED_ENABLE_ADALIGHT // saves 500b only (uses GPIO3 (RX) for serial)
//#define WLED_ENABLE_DMX // uses 3.5kb (use LEDPIN other than 2) //#define WLED_ENABLE_DMX // uses 3.5kb (use LEDPIN other than 2)
#define WLED_ENABLE_LOXONE // uses 1.2kb #ifndef WLED_DISABLE_LOXONE
#define WLED_ENABLE_LOXONE // uses 1.2kb
#endif
#ifndef WLED_DISABLE_WEBSOCKETS #ifndef WLED_DISABLE_WEBSOCKETS
#define WLED_ENABLE_WEBSOCKETS #define WLED_ENABLE_WEBSOCKETS
#endif #endif
@ -136,6 +138,9 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#include "fcn_declare.h" #include "fcn_declare.h"
#include "html_ui.h" #include "html_ui.h"
#ifndef WLED_DISABLE_SIMPLE_UI
#include "html_simple.h"
#endif
#include "html_settings.h" #include "html_settings.h"
#include "html_other.h" #include "html_other.h"
#include "FX.h" #include "FX.h"
@ -225,11 +230,7 @@ WLED_GLOBAL bool rlyMde _INIT(true);
WLED_GLOBAL bool rlyMde _INIT(RLYMDE); WLED_GLOBAL bool rlyMde _INIT(RLYMDE);
#endif #endif
#ifndef IRPIN #ifndef IRPIN
#ifdef WLED_DISABLE_INFRARED WLED_GLOBAL int8_t irPin _INIT(-1);
WLED_GLOBAL int8_t irPin _INIT(-1);
#else
WLED_GLOBAL int8_t irPin _INIT(4);
#endif
#else #else
WLED_GLOBAL int8_t irPin _INIT(IRPIN); WLED_GLOBAL int8_t irPin _INIT(IRPIN);
#endif #endif
@ -264,9 +265,9 @@ WLED_GLOBAL bool noWifiSleep _INIT(false);
#endif #endif
// LED CONFIG // LED CONFIG
WLED_GLOBAL uint16_t ledCount _INIT(DEFAULT_LED_COUNT); // overcurrent prevented by ABL WLED_GLOBAL uint16_t ledCount _INIT(0); // overcurrent prevented by ABL (filled in cfg.cpp, set.cpp or FX_fcn.cpp)
WLED_GLOBAL bool turnOnAtBoot _INIT(true); // turn on LEDs at power-up WLED_GLOBAL bool turnOnAtBoot _INIT(true); // turn on LEDs at power-up
WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load after power-up WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load after power-up
//if true, a segment per bus will be created on boot and LED settings save //if true, a segment per bus will be created on boot and LED settings save
//if false, only one segment spanning the total LEDs is created, //if false, only one segment spanning the total LEDs is created,
@ -288,6 +289,8 @@ WLED_GLOBAL byte briMultiplier _INIT(100); // % of brightness to set (t
// User Interface CONFIG // User Interface CONFIG
WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module
WLED_GLOBAL bool syncToggleReceive _INIT(false); // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise WLED_GLOBAL bool syncToggleReceive _INIT(false); // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise
WLED_GLOBAL bool simplifiedUI _INIT(false); // enable simplified UI
WLED_GLOBAL byte cacheInvalidate _INIT(0); // used to invalidate browser cache when switching from regular to simplified UI
// Sync CONFIG // Sync CONFIG
WLED_GLOBAL NodesMap Nodes; WLED_GLOBAL NodesMap Nodes;
@ -566,7 +569,7 @@ WLED_GLOBAL JsonDocument* fileDoc;
WLED_GLOBAL bool doCloseFile _INIT(false); WLED_GLOBAL bool doCloseFile _INIT(false);
// presets // presets
WLED_GLOBAL int16_t currentPreset _INIT(-1); WLED_GLOBAL int8_t currentPreset _INIT(-1);
WLED_GLOBAL byte errorFlag _INIT(0); WLED_GLOBAL byte errorFlag _INIT(0);
@ -599,7 +602,6 @@ WLED_GLOBAL bool doInitBusses _INIT(false);
// Usermod manager // Usermod manager
WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager()); WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
// enable additional debug output // enable additional debug output
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
#ifndef ESP8266 #ifndef ESP8266
@ -629,7 +631,7 @@ WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
WLED_GLOBAL unsigned long debugTime _INIT(0); WLED_GLOBAL unsigned long debugTime _INIT(0);
WLED_GLOBAL int lastWifiState _INIT(3); WLED_GLOBAL int lastWifiState _INIT(3);
WLED_GLOBAL unsigned long wifiStateChangedTime _INIT(0); WLED_GLOBAL unsigned long wifiStateChangedTime _INIT(0);
WLED_GLOBAL int loops _INIT(0); WLED_GLOBAL unsigned long loops _INIT(0);
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
@ -666,6 +668,5 @@ public:
void initAP(bool resetAP = false); void initAP(bool resetAP = false);
void initConnection(); void initConnection();
void initInterfaces(); void initInterfaces();
void handleStatusLED();
}; };
#endif // WLED_H #endif // WLED_H

View File

@ -1,20 +1,9 @@
#ifndef WLED_ETHERNET_H #ifndef WLED_ETHERNET_H
#define WLED_ETHERNET_H #define WLED_ETHERNET_H
#ifdef WLED_USE_ETHERNET
#include "pin_manager.h" #include "pin_manager.h"
// The following six pins are neither configurable nor #ifdef WLED_USE_ETHERNET
// can they be re-assigned through IOMUX / GPIO matrix.
// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface
const managed_pin_type esp32_nonconfigurable_ethernet_pins[6] = {
{ 21, true }, // RMII EMAC TX EN == When high, clocks the data on TXD0 and TXD1 to transmitter
{ 19, true }, // RMII EMAC TXD0 == First bit of transmitted data
{ 22, true }, // RMII EMAC TXD1 == Second bit of transmitted data
{ 25, false }, // RMII EMAC RXD0 == First bit of received data
{ 26, false }, // RMII EMAC RXD1 == Second bit of received data
{ 27, true }, // RMII EMAC CRS_DV == Carrier Sense and RX Data Valid
};
// For ESP32, the remaining five pins are at least somewhat configurable. // For ESP32, the remaining five pins are at least somewhat configurable.
// eth_address is in range [0..31], indicates which PHY (MAC?) address should be allocated to the interface // eth_address is in range [0..31], indicates which PHY (MAC?) address should be allocated to the interface
@ -37,74 +26,10 @@ typedef struct EthernetSettings {
eth_clock_mode_t eth_clk_mode; eth_clock_mode_t eth_clk_mode;
} ethernet_settings; } ethernet_settings;
ethernet_settings ethernetBoards[] = { extern const ethernet_settings ethernetBoards[];
// None
{
},
// WT32-EHT01
// (*) NOTE: silkscreen on board revision v1.2 may be wrong:
// silkscreen on v1.2 says IO35, but appears to be IO5
// silkscreen on v1.2 says RXD, and appears to be IO35
{
1, // eth_address,
16, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO0_IN // eth_clk_mode
},
// ESP32-POE #define WLED_ETH_RSVD_PINS_COUNT 6
{ extern const managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT];
0, // eth_address,
12, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
},
// WESP32
{
0, // eth_address,
-1, // eth_power,
16, // eth_mdc,
17, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO0_IN // eth_clk_mode
},
// QuinLed-ESP32-Ethernet
{
0, // eth_address,
5, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
},
// TwilightLord-ESP32 Ethernet Shield
{
0, // eth_address,
5, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
},
// ESP3DEUXQuattro
{
1, // eth_address,
-1, // eth_power,
23, // eth_mdc,
18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode
}
};
#endif #endif
#endif #endif

View File

@ -80,6 +80,43 @@ float asin_t(float x) {
return res; return res;
} }
/*
//https://stackoverflow.com/a/42542593
#define A 0.0776509570923569
#define B -0.287434475393028
#define C ((HALF_PI/2) - A - B)
//polynominal factors for approximation between 1 and 5
#define C0 0.089494f
#define C1 0.974207f
#define C2 -0.326175f
#define C3 0.05375f
#define C4 -0.003445f
float atan_t(float x) {
bool neg = (x < 0);
#ifdef WLED_DEBUG_MATH
float xinput = x;
#endif
x = std::abs(x);
float res;
if (x > 5.0f) { //atan(x) converges to pi/2 - (1/x) for large values
res = HALF_PI - (1.0f/x);
}
else if (x > 1.0f) { //1 < x < 5
float xx = x * x;
res = (C4*xx*xx)+(C3*xx*x)+(C2*xx)+(C1*x)+C0;
} else { //this approximation is only for x <= 1
float xx = x * x;
res = ((A*xx + B)*xx + C)*x;
}
if (neg) res = -res;
#ifdef WLED_DEBUG_MATH
Serial.printf("atan,%f,%f,%f\n",xinput,res,atan(xinput));
#endif
return res;
}
*/
//https://stackoverflow.com/a/42542593 //https://stackoverflow.com/a/42542593
#define A 0.0776509570923569 #define A 0.0776509570923569
#define B -0.287434475393028 #define B -0.287434475393028
@ -135,4 +172,4 @@ float fmod_t(float num, float denom) {
return res; return res;
} }
#endif #endif

View File

@ -114,7 +114,7 @@ void handleSerial()
if (!realtimeOverride) setRealtimePixel(pixel++, red, green, blue, 0); if (!realtimeOverride) setRealtimePixel(pixel++, red, green, blue, 0);
if (--count > 0) state = AdaState::Data_Red; if (--count > 0) state = AdaState::Data_Red;
else { else {
if (!realtimeMode && bri == 0) strip.setBrightness(briLast); // if (!realtimeMode && bri == 0) strip.setBrightness(briLast); //realtimeLock() handles turning strip on/off
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_ADALIGHT); realtimeLock(realtimeTimeoutMs, REALTIME_MODE_ADALIGHT);
if (!realtimeOverride) strip.show(); if (!realtimeOverride) strip.show();

View File

@ -4,6 +4,9 @@
* Integrated HTTP web server page declarations * Integrated HTTP web server page declarations
*/ */
bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request);
void setStaticContentCacheHeaders(AsyncWebServerResponse *response);
//Is this an IP? //Is this an IP?
bool isIp(String str) { bool isIp(String str) {
for (size_t i = 0; i < str.length(); i++) { for (size_t i = 0; i < str.length(); i++) {
@ -16,7 +19,7 @@ bool isIp(String str) {
} }
void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){ void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
if(!index){ if (!index) {
request->_tempFile = WLED_FS.open(filename, "w"); request->_tempFile = WLED_FS.open(filename, "w");
DEBUG_PRINT("Uploading "); DEBUG_PRINT("Uploading ");
DEBUG_PRINTLN(filename); DEBUG_PRINTLN(filename);
@ -25,9 +28,10 @@ void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t
if (len) { if (len) {
request->_tempFile.write(data,len); request->_tempFile.write(data,len);
} }
if(final){ if (final) {
request->_tempFile.close(); request->_tempFile.close();
request->send(200, "text/plain", F("File Uploaded!")); request->send(200, "text/plain", F("File Uploaded!"));
cacheInvalidate++;
} }
} }
@ -163,6 +167,15 @@ void initServer()
size_t len, bool final) {handleUpload(request, filename, index, data, len, final);} size_t len, bool final) {handleUpload(request, filename, index, data, len, final);}
); );
server.on("/simple.htm", HTTP_GET, [](AsyncWebServerRequest *request){
if (handleFileRead(request, "/simple.htm")) return;
if (handleIfNoneMatchCacheHeader(request)) return;
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_simple, PAGE_simple_L);
response->addHeader(F("Content-Encoding"),"gzip");
setStaticContentCacheHeaders(response);
request->send(response);
});
//if OTA is allowed //if OTA is allowed
if (!otaLock){ if (!otaLock){
#ifdef WLED_ENABLE_FS_EDITOR #ifdef WLED_ENABLE_FS_EDITOR
@ -287,6 +300,8 @@ bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request)
void setStaticContentCacheHeaders(AsyncWebServerResponse *response) void setStaticContentCacheHeaders(AsyncWebServerResponse *response)
{ {
char tmp[12];
// https://medium.com/@codebyamir/a-web-developers-guide-to-browser-caching-cc41f3b73e7c
#ifndef WLED_DEBUG #ifndef WLED_DEBUG
//this header name is misleading, "no-cache" will not disable cache, //this header name is misleading, "no-cache" will not disable cache,
//it just revalidates on every load using the "If-None-Match" header with the last ETag value //it just revalidates on every load using the "If-None-Match" header with the last ETag value
@ -294,7 +309,8 @@ void setStaticContentCacheHeaders(AsyncWebServerResponse *response)
#else #else
response->addHeader(F("Cache-Control"),"no-store,max-age=0"); // prevent caching if debug build response->addHeader(F("Cache-Control"),"no-store,max-age=0"); // prevent caching if debug build
#endif #endif
response->addHeader(F("ETag"), String(VERSION)); sprintf_P(tmp, PSTR("%8d-%02x"), VERSION, cacheInvalidate);
response->addHeader(F("ETag"), tmp);
} }
void serveIndex(AsyncWebServerRequest* request) void serveIndex(AsyncWebServerRequest* request)
@ -303,7 +319,13 @@ void serveIndex(AsyncWebServerRequest* request)
if (handleIfNoneMatchCacheHeader(request)) return; if (handleIfNoneMatchCacheHeader(request)) return;
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L); AsyncWebServerResponse *response;
#ifndef WLED_DISABLE_SIMPLE_UI
if (simplifiedUI)
response = request->beginResponse_P(200, "text/html", PAGE_simple, PAGE_simple_L);
else
#endif
response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L);
response->addHeader(F("Content-Encoding"),"gzip"); response->addHeader(F("Content-Encoding"),"gzip");
setStaticContentCacheHeaders(response); setStaticContentCacheHeaders(response);

View File

@ -16,9 +16,11 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
if(type == WS_EVT_CONNECT){ if(type == WS_EVT_CONNECT){
//client connected //client connected
sendDataWs(client); sendDataWs(client);
DEBUG_PRINTLN(F("WS client connected."));
} else if(type == WS_EVT_DISCONNECT){ } else if(type == WS_EVT_DISCONNECT){
//client disconnected //client disconnected
if (client->id() == wsLiveClientId) wsLiveClientId = 0; if (client->id() == wsLiveClientId) wsLiveClientId = 0;
DEBUG_PRINTLN(F("WS client disconnected."));
} else if(type == WS_EVT_DATA){ } else if(type == WS_EVT_DATA){
//data packet //data packet
AwsFrameInfo * info = (AwsFrameInfo*)arg; AwsFrameInfo * info = (AwsFrameInfo*)arg;
@ -39,6 +41,11 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
JsonObject root = jsonBuffer.as<JsonObject>(); JsonObject root = jsonBuffer.as<JsonObject>();
if (error || root.isNull()) return; if (error || root.isNull()) return;
#ifdef WLED_DEBUG
DEBUG_PRINT(F("Incoming WS: "));
serializeJson(root,Serial);
DEBUG_PRINTLN();
#endif
if (root["v"] && root.size() == 1) { if (root["v"] && root.size() == 1) {
//if the received value is just "{"v":true}", send only to this client //if the received value is just "{"v":true}", send only to this client
verboseResponse = true; verboseResponse = true;
@ -76,12 +83,15 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
} }
} }
} }
DEBUG_PRINTLN(F("WS multipart message."));
} }
} else if(type == WS_EVT_ERROR){ } else if(type == WS_EVT_ERROR){
//error was received from the other end //error was received from the other end
DEBUG_PRINTLN(F("WS error."));
} else if(type == WS_EVT_PONG){ } else if(type == WS_EVT_PONG){
//pong message was received (in response to a ping request maybe) //pong message was received (in response to a ping request maybe)
DEBUG_PRINTLN(F("WS pong."));
} }
} }
@ -100,13 +110,22 @@ void sendDataWs(AsyncWebSocketClient * client)
size_t len = measureJson(doc); size_t len = measureJson(doc);
buffer = ws.makeBuffer(len); buffer = ws.makeBuffer(len);
if (!buffer) return; //out of memory if (!buffer) return; //out of memory
/*
#ifdef WLED_DEBUG
DEBUG_PRINT(F("Outgoing WS: "));
serializeJson(doc,Serial);
DEBUG_PRINTLN();
#endif
*/
serializeJson(doc, (char *)buffer->get(), len +1); serializeJson(doc, (char *)buffer->get(), len +1);
} }
DEBUG_PRINT(F("Sending WS data "));
if (client) { if (client) {
client->text(buffer); client->text(buffer);
DEBUG_PRINTLN(F("to a single client."));
} else { } else {
ws.textAll(buffer); ws.textAll(buffer);
DEBUG_PRINTLN(F("to multiple clients."));
} }
} }

View File

@ -1,4 +1,5 @@
#include "wled.h" #include "wled.h"
#include "wled_ethernet.h"
/* /*
* Sending XML status files to client * Sending XML status files to client
@ -158,24 +159,16 @@ void sappends(char stype, const char* key, char* val)
{ {
switch(stype) switch(stype)
{ {
case 's': { //string (we can interpret val as char*) case 's': {//string (we can interpret val as char*)
String buf = val;
//convert "%" to "%%" to make EspAsyncWebServer happy
buf.replace("%","%%");
oappend("d.Sf."); oappend("d.Sf.");
oappend(key); oappend(key);
oappend(".value=\""); oappend(".value=\"");
//convert "%" to "%%" to make EspAsyncWebServer happy oappend(buf.c_str());
char buf[130];
uint8_t len = strlen(val) +1;
uint8_t s = 0;
for (uint8_t i = 0; i < len; i++) {
buf[i+s] = val[i];
if (val[i] == '%') {
s++; buf[i+s] = '%';
}
}
oappend(buf);
oappend("\";"); oappend("\";");
break; } break;}
case 'm': //message case 'm': //message
oappend(SET_F("d.getElementsByClassName")); oappend(SET_F("d.getElementsByClassName"));
oappend(key); oappend(key);
@ -186,6 +179,52 @@ void sappends(char stype, const char* key, char* val)
} }
} }
void extractPin(JsonObject &obj, const char *key) {
if (obj[key].is<JsonArray>()) {
JsonArray pins = obj[key].as<JsonArray>();
for (JsonVariant pv : pins) {
if (pv.as<int>() > -1) { oappend(","); oappendi(pv.as<int>()); }
}
} else {
if (obj[key].as<int>() > -1) { oappend(","); oappendi(obj[key].as<int>()); }
}
}
// oappens used pins by recursively scanning JsonObject
void fillUMPins(JsonObject &mods)
{
for (JsonPair kv : mods) {
// kv.key() is usermod name or subobject key
// kv.value() is object itself
JsonObject obj = kv.value();
if (!obj.isNull()) {
// element is an JsonObject
if (!obj["pin"].isNull()) {
extractPin(obj, "pin");
} else {
// scan keys (just one level deep as is possible with usermods)
for (JsonPair so : obj) {
const char *key = so.key().c_str();
if (strstr(key, "pin")) {
// we found a key containing "pin" substring
if (strlen(strstr(key, "pin")) == 3) {
// and it is at the end, we found another pin
extractPin(obj, key);
continue;
}
}
if (!obj[so.key()].is<JsonObject>()) continue;
JsonObject subObj = obj[so.key()];
if (!subObj["pin"].isNull()) {
// get pins from subobject
extractPin(subObj, "pin");
}
}
}
}
}
}
//get values for settings form in javascript //get values for settings form in javascript
void getSettingsJS(byte subPage, char* dest) void getSettingsJS(byte subPage, char* dest)
@ -198,7 +237,8 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage <1 || subPage >8) return; if (subPage <1 || subPage >8) return;
if (subPage == 1) { if (subPage == 1)
{
sappends('s',SET_F("CS"),clientSSID); sappends('s',SET_F("CS"),clientSSID);
byte l = strlen(clientPass); byte l = strlen(clientPass);
@ -264,50 +304,72 @@ void getSettingsJS(byte subPage, char* dest)
} }
} }
if (subPage == 2) { if (subPage == 2)
{
char nS[8]; char nS[8];
// add reserved and usermod pins as d.um_p array // add reserved and usermod pins as d.um_p array
oappend(SET_F("d.um_p=[6,7,8,9,10,11"));
DynamicJsonDocument doc(JSON_BUFFER_SIZE/2); DynamicJsonDocument doc(JSON_BUFFER_SIZE/2);
JsonObject mods = doc.createNestedObject(F("um")); JsonObject mods = doc.createNestedObject(F("um"));
usermods.addToConfig(mods); usermods.addToConfig(mods);
oappend(SET_F("d.um_p=[")); if (!mods.isNull()) fillUMPins(mods);
if (!mods.isNull()) {
uint8_t i=0; #ifdef WLED_ENABLE_DMX
for (JsonPair kv : mods) { oappend(SET_F(",2")); // DMX hardcoded pin
if (!kv.value().isNull()) { #endif
// element is an JsonObject
JsonObject obj = kv.value(); #ifdef WLED_ENABLE_ADALIGHT
if (obj["pin"] != nullptr) { // inform settings page that pin 3 is used by ADALights if not aleready used by strip (previous setup)
if (obj["pin"].is<JsonArray>()) { // NOTE: this will prohibit pin 3 use on new installs
JsonArray pins = obj["pin"].as<JsonArray>(); {
for (JsonVariant pv : pins) { bool pin3used = false;
if (i++) oappend(SET_F(",")); for (uint8_t s=0; s < busses.getNumBusses(); s++) {
oappendi(pv.as<int>()); Bus* bus = busses.getBus(s);
} uint8_t pins[5];
} else { uint8_t nPins = bus->getPins(pins);
if (i++) oappend(SET_F(",")); for (uint8_t i = 0; i < nPins; i++) {
oappendi(obj["pin"].as<int>()); if (pins[i] == 3) {
} pin3used = true;
break;
} }
} }
if (pin3used) break;
} }
if (i) oappend(SET_F(",")); if (!pin3used && pinManager.isPinAllocated(3)) oappend(SET_F(",3")); // ADALight (RX) pin
oappend(SET_F("6,7,8,9,10,11")); // flash memory pins
#ifdef WLED_ENABLE_DMX
oappend(SET_F(",2")); // DMX hardcoded pin
#endif
//Adalight / Serial in requires pin 3 to be unused. However, Serial input can not be prevented by WLED
#ifdef WLED_DEBUG
oappend(SET_F(",1")); // debug output (TX) pin
#endif
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) oappend(SET_F(",16,17")); // GPIO16 & GPIO17 reserved for SPI RAM
#endif
//TODO: add reservations for Ethernet shield pins
#ifdef WLED_USE_ETHERNET
#endif
} }
#endif
#ifdef WLED_DEBUG
oappend(SET_F(",1")); // debug output (TX) pin
#endif
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) oappend(SET_F(",16,17")); // GPIO16 & GPIO17 reserved for SPI RAM
#endif
#ifdef WLED_USE_ETHERNET
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
for (uint8_t p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { oappend(","); oappend(itoa(esp32_nonconfigurable_ethernet_pins[p].pin,nS,10)); }
if (ethernetBoards[ethernetType].eth_power>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_power,nS,10)); }
if (ethernetBoards[ethernetType].eth_mdc>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdc,nS,10)); }
if (ethernetBoards[ethernetType].eth_mdio>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdio,nS,10)); }
switch (ethernetBoards[ethernetType].eth_clk_mode) {
case ETH_CLOCK_GPIO0_IN:
case ETH_CLOCK_GPIO0_OUT:
oappend(SET_F(",0"));
break;
case ETH_CLOCK_GPIO16_OUT:
oappend(SET_F(",16"));
break;
case ETH_CLOCK_GPIO17_OUT:
oappend(SET_F(",17"));
break;
}
}
#endif
oappend(SET_F("];")); oappend(SET_F("];"));
// set limits // set limits
@ -317,10 +379,6 @@ void getSettingsJS(byte subPage, char* dest)
oappend(itoa(MAX_LED_MEMORY,nS,10)); oappend(itoa(MAX_LED_MEMORY,nS,10));
oappend(SET_F(");")); oappend(SET_F(");"));
oappend(SET_F("d.Sf.LC.max=")); //TODO Formula for max LEDs on ESP8266 depending on types. 500 DMA or 1500 UART (about 4kB mem usage)
oappendi(MAX_LEDS);
oappend(";");
sappend('v',SET_F("LC"),ledCount); sappend('v',SET_F("LC"),ledCount);
sappend('c',SET_F("MS"),autoSegments); sappend('c',SET_F("MS"),autoSegments);
@ -333,19 +391,21 @@ void getSettingsJS(byte subPage, char* dest)
char ls[4] = "LS"; ls[2] = 48+s; ls[3] = 0; //strip start LED char ls[4] = "LS"; ls[2] = 48+s; ls[3] = 0; //strip start LED
char cv[4] = "CV"; cv[2] = 48+s; cv[3] = 0; //strip reverse char cv[4] = "CV"; cv[2] = 48+s; cv[3] = 0; //strip reverse
char sl[4] = "SL"; sl[2] = 48+s; sl[3] = 0; //skip 1st LED char sl[4] = "SL"; sl[2] = 48+s; sl[3] = 0; //skip 1st LED
// char ew[4] = "EW"; ew[2] = 48+s; ew[3] = 0; //strip RGBW override
oappend(SET_F("addLEDs(1);")); oappend(SET_F("addLEDs(1);"));
uint8_t pins[5]; uint8_t pins[5];
uint8_t nPins = bus->getPins(pins); uint8_t nPins = bus->getPins(pins);
for (uint8_t i = 0; i < nPins; i++) { for (uint8_t i = 0; i < nPins; i++) {
lp[1] = 48+i; lp[1] = 48+i;
if (pinManager.isPinOk(pins[i])) sappend('v', lp, pins[i]); if (pinManager.isPinOk(pins[i])) sappend('v',lp,pins[i]);
} }
sappend('v', lc, bus->getLength()); sappend('v',lc,bus->getLength());
sappend('v',lt,bus->getType()); sappend('v',lt,bus->getType());
sappend('v',co,bus->getColorOrder()); sappend('v',co,bus->getColorOrder());
sappend('v',ls,bus->getStart()); sappend('v',ls,bus->getStart());
sappend('c',cv,bus->reversed); sappend('c',cv,bus->reversed);
sappend('c',sl,bus->skippedLeds()); sappend('c',sl,bus->skippedLeds());
// sappend('c',ew,bus->isRgbw());
} }
sappend('v',SET_F("MA"),strip.ablMilliampsMax); sappend('v',SET_F("MA"),strip.ablMilliampsMax);
sappend('v',SET_F("LA"),strip.milliampsPerLed); sappend('v',SET_F("LA"),strip.milliampsPerLed);
@ -391,6 +451,7 @@ void getSettingsJS(byte subPage, char* dest)
{ {
sappends('s',SET_F("DS"),serverDescription); sappends('s',SET_F("DS"),serverDescription);
sappend('c',SET_F("ST"),syncToggleReceive); sappend('c',SET_F("ST"),syncToggleReceive);
sappend('c',SET_F("SU"),simplifiedUI);
} }
if (subPage == 4) if (subPage == 4)