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
.idea
.direnv
wled-update.sh

2
package-lock.json generated
View File

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

View File

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

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
#TFT_eSPI
#For use SSD1306 OLED display uncomment following
#U8g2@~2.27.2
U8g2@~2.28.8
#For Dallas sensor uncomment following 2 lines
#OneWire@~2.3.5
#milesburton/DallasTemperature@^3.9.0

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

View File

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

View File

@ -1,33 +1,33 @@
# JSON IR remote
## Purpose
The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling.
It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can
map buttons from any remote to any HTTP request API or JSON API command.
## Usage
* Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own.
* On the config > LED settings page, set the correct IR pin.
* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
## Modification
* See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels.
* In the ir.json file, each key will be the hex encoded IR code.
* The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed.
* A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback)
* When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to)
* If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property.
* Other properties are ignored, but having a label property may help when editing.
Sample:
{
"0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
"0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
"0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
"0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
"label": "Preset 1 or fallback to Saw - Party"}, // c function
}
# JSON IR remote
## Purpose
The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling.
It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can
map buttons from any remote to any HTTP request API or JSON API command.
## Usage
* Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own.
* On the config > LED settings page, set the correct IR pin.
* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
## Modification
* See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels.
* In the ir.json file, each key will be the hex encoded IR code.
* The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed.
* A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback)
* When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to)
* If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property.
* Other properties are ignored, but having a label property may help when editing.
Sample:
{
"0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
"0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
"0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
"0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
"label": "Preset 1 or fallback to Saw - Party"}, // c function
}

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

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:
* current date and time;
* Network SSID;
* IP address;
* WiFi signal strength;
* Brightness;
* Chosen effect;
* Chosen palette;
* effect speed and intensity;
* Estimated current in mA;
## Hardware
@ -46,27 +49,29 @@ Add lines to section:
default_envs = esp32dev
build_flags = ${common.build_flags_esp32}
-D USERMOD_ST7789_DISPLAY
-DUSER_SETUP_LOADED=1
-DST7789_DRIVER=1
-DTFT_WIDTH=240
-DTFT_HEIGHT=240
-DCGRAM_OFFSET=1
-DTFT_MOSI=21
-DTFT_SCLK=22
-DTFT_DC=27
-DTFT_RST=26
-DTFT_BL=14
-DLOAD_GLCD=1
;optional for WROVER
;-DCONFIG_SPIRAM_SUPPORT=1
```
Save the `platformio.ini` file. Once this is saved, the required library files should be automatically downloaded for modifications in a later step.
### TFT_eSPI Library Adjustments
We need to modify a file in the `TFT_eSPI` library. If you followed the directions to modify and save the `platformio.ini` file above, the `User_Setup_Select.h` file can be found in the `/.pio/libdeps/esp32dev/TFT_eSPI` folder.
If you are not using PlatformIO you need to modify a file in the `TFT_eSPI` library. If you followed the directions to modify and save the `platformio.ini` file above, the `Setup24_ST7789.h` file can be found in the `/.pio/libdeps/esp32dev/TFT_eSPI/User_Setups/` folder.
Modify the `User_Setup_Select.h` file as follows:
Edit `Setup_ST7789.h` file and uncomment nad changep GPIO pin numbers in lines containing `TFT_MOSI`, `TFT_SCLK`, `TFT_RST`, `TFT_DC`.
* Comment out the following line (which is the 'default' setup file):
Modify the `User_Setup_Select.h` by uncommentig the line containing `#include <User_Setups/Setup24_ST7789.h>` and commenting out line containing `#include <User_Setup.h>`.
```ini
//#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`
If your display includes backlight enable pin, #define TFT_BL with backlight enable GPIO number.

View File

@ -7,27 +7,48 @@
#include <TFT_eSPI.h>
#include <SPI.h>
#define USERMOD_ST7789_DISPLAY 97
#ifndef TFT_DISPOFF
#define TFT_DISPOFF 0x28
#ifndef USER_SETUP_LOADED
#ifndef ST7789_DRIVER
#error Please define ST7789_DRIVER
#endif
#ifndef TFT_WIDTH
#error Please define TFT_WIDTH
#endif
#ifndef TFT_HEIGHT
#error Please define TFT_HEIGHT
#endif
#ifndef TFT_MOSI
#error Please define TFT_MOSI
#endif
#ifndef TFT_SCLK
#error Please define TFT_SCLK
#endif
#ifndef TFT_DC
#error Please define TFT_DC
#endif
#ifndef TFT_RST
#error Please define TFT_RST
#endif
#ifndef LOAD_GLCD
#error Please define LOAD_GLCD
#endif
#endif
#ifndef TFT_BL
#define TFT_BL -1
#endif
#ifndef TFT_SLPIN
#define TFT_SLPIN 0x10
#endif
#define USERMOD_ID_ST7789_DISPLAY 97
#define TFT_MOSI 21
#define TFT_SCLK 22
#define TFT_DC 18
#define TFT_RST 5
#define TFT_BL 26 // Display backlight control pin
TFT_eSPI tft = TFT_eSPI(TFT_WIDTH, TFT_HEIGHT); // Invoke custom library
TFT_eSPI tft = TFT_eSPI(240, 240); // Invoke custom library
// Extra char (+1) for null
#define LINE_BUFFER_SIZE 20
// How often we are redrawing screen
#define USER_LOOP_REFRESH_RATE_MS 1000
extern int getSignalQuality(int rssi);
//class name. Use something descriptive and leave the ": public Usermod" part :)
class St7789DisplayUsermod : public Usermod {
@ -45,9 +66,70 @@ class St7789DisplayUsermod : public Usermod {
uint8_t knownBrightness = 0;
uint8_t knownMode = 0;
uint8_t knownPalette = 0;
uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2
uint8_t knownEffectSpeed = 0;
uint8_t knownEffectIntensity = 0;
uint8_t knownMinute = 99;
uint8_t knownHour = 99;
const uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2
long lastUpdate = 0;
void center(String &line, uint8_t width) {
int len = line.length();
if (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:
//Functions called by WLED
@ -57,6 +139,9 @@ class St7789DisplayUsermod : public Usermod {
*/
void setup()
{
PinManagerPinType pins[] = { { TFT_MOSI, true }, { TFT_MISO, false}, { TFT_SCLK, true }, { TFT_CS, true}, { TFT_DC, true}, { TFT_RST, true }, { TFT_BL, true } };
if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { return; }
tft.init();
tft.setRotation(0); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip.
tft.fillScreen(TFT_BLACK);
@ -65,10 +150,10 @@ class St7789DisplayUsermod : public Usermod {
tft.setTextDatum(MC_DATUM);
tft.setTextSize(2);
tft.print("Loading...");
if (TFT_BL > 0)
{ // TFT_BL has been set in the TFT_eSPI library
pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
digitalWrite(TFT_BL, HIGH); // Turn backlight on.
if (TFT_BL >= 0)
{
pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
digitalWrite(TFT_BL, HIGH); // Turn backlight on.
}
}
@ -91,174 +176,189 @@ class St7789DisplayUsermod : public Usermod {
* Instead, use a timer check as shown here.
*/
void loop() {
// Check if we time interval for redrawing passes.
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
char buff[LINE_BUFFER_SIZE];
// Check if we time interval for redrawing passes.
if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS)
{
return;
}
lastUpdate = millis();
lastUpdate = millis();
// Turn off display after 5 minutes with no change.
if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000)
// Turn off display after 5 minutes with no change.
if (!displayTurnedOff && millis() - lastRedraw > 5*60*1000)
{
digitalWrite(TFT_BL, LOW); // Turn backlight off.
if (TFT_BL >= 0) digitalWrite(TFT_BL, LOW); // Turn backlight off.
displayTurnedOff = true;
}
// Check if values which are shown on display changed from the last time.
if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid)
{
needRedraw = true;
}
else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP()))
{
needRedraw = true;
}
else if (knownBrightness != bri)
{
needRedraw = true;
}
else if (knownMode != strip.getMode())
{
needRedraw = true;
}
else if (knownPalette != strip.getSegment(0).palette)
{
needRedraw = true;
}
if (!needRedraw)
{
return;
}
needRedraw = false;
if (displayTurnedOff)
{
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on.
displayTurnedOff = false;
}
lastRedraw = millis();
// Update last known values.
#if defined(ESP8266)
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
#else
knownSsid = WiFi.SSID();
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);
// First row with Wifi name
tft.setTextColor(TFT_SILVER);
tft.setCursor(3, 40);
tft.print(knownSsid.substring(0, tftcharwidth > 1 ? tftcharwidth - 1 : 0));
// Print `~` char to indicate that SSID is longer, than our dicplay
if (knownSsid.length() > tftcharwidth)
tft.print("~");
// Second row with AP IP and Password or IP
tft.setTextColor(TFT_GREEN);
tft.setTextSize(2);
tft.setCursor(3, 64);
// Print AP IP and password in AP mode or knownIP if AP not active.
if (apActive)
{
tft.setTextColor(TFT_YELLOW);
tft.print("AP IP: ");
tft.print(knownIp);
tft.setCursor(3,86);
tft.setTextColor(TFT_YELLOW);
tft.print("AP Pass:");
tft.print(apPass);
}
else
{
tft.setTextColor(TFT_GREEN);
tft.print("IP: ");
tft.print(knownIp);
tft.setCursor(3,86);
//tft.print("Signal Strength: ");
//tft.print(i.wifi.signal);
tft.setTextColor(TFT_WHITE);
tft.print("Bri: ");
tft.print(((float(bri)/255)*100),0);
tft.print("%");
}
// Third row with mode name
tft.setCursor(3, 108);
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
char singleJsonSymbol;
// Find the mode name in JSON
for (size_t i = 0; i < strlen_P(JSON_mode_names); i++)
{
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
switch (singleJsonSymbol)
// Check if values which are shown on display changed from the last time.
if ((((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) ||
(knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) ||
(knownBrightness != bri) ||
(knownEffectSpeed != effectSpeed) ||
(knownEffectIntensity != effectIntensity) ||
(knownMode != strip.getMode()) ||
(knownPalette != strip.getSegment(0).palette))
{
needRedraw = true;
}
if (!needRedraw)
{
return;
}
needRedraw = false;
if (displayTurnedOff)
{
digitalWrite(TFT_BL, HIGH); // Turn backlight on.
displayTurnedOff = false;
}
lastRedraw = millis();
// Update last known values.
#if defined(ESP8266)
knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID();
#else
knownSsid = WiFi.SSID();
#endif
knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP();
knownBrightness = bri;
knownMode = strip.getMode();
knownPalette = strip.getSegment(0).palette;
knownEffectSpeed = effectSpeed;
knownEffectIntensity = effectIntensity;
tft.fillScreen(TFT_BLACK);
showTime();
tft.setTextSize(2);
// Wifi name
tft.setTextColor(TFT_GREEN);
tft.setCursor(0, 60);
String line = knownSsid.substring(0, tftcharwidth-1);
// Print `~` char to indicate that SSID is longer, than our display
if (knownSsid.length() > tftcharwidth) line = line.substring(0, tftcharwidth-1) + '~';
center(line, tftcharwidth);
tft.print(line.c_str());
// Print AP IP and password in AP mode or knownIP if AP not active.
if (apActive)
{
tft.setCursor(0, 84);
tft.print("AP IP: ");
tft.print(knownIp);
tft.setCursor(0,108);
tft.print("AP Pass:");
tft.print(apPass);
}
else
{
tft.setCursor(0, 84);
line = knownIp.toString();
center(line, tftcharwidth);
tft.print(line.c_str());
// percent brightness
tft.setCursor(0, 120);
tft.setTextColor(TFT_WHITE);
tft.print("Bri: ");
tft.print((((int)bri*100)/255));
tft.print("%");
// signal quality
tft.setCursor(124,120);
tft.print("Sig: ");
if (getSignalQuality(WiFi.RSSI()) < 10) {
tft.setTextColor(TFT_RED);
} else if (getSignalQuality(WiFi.RSSI()) < 25) {
tft.setTextColor(TFT_ORANGE);
} else {
tft.setTextColor(TFT_GREEN);
}
tft.print(getSignalQuality(WiFi.RSSI()));
tft.setTextColor(TFT_WHITE);
tft.print("%");
}
// mode name
tft.setTextColor(TFT_CYAN);
tft.setCursor(0, 144);
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
char singleJsonSymbol;
// Find the mode name in JSON
for (size_t i = 0; i < strlen_P(JSON_mode_names); i++)
{
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
switch (singleJsonSymbol)
{
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownMode))
break;
tft.print(singleJsonSymbol);
printedChars++;
}
if ((qComma > knownMode) || (printedChars > tftcharwidth - 1))
break;
}
// palette name
tft.setTextColor(TFT_YELLOW);
tft.setCursor(0, 168);
qComma = 0;
insideQuotes = false;
printedChars = 0;
// Looking for palette name in JSON.
for (size_t i = 0; i < strlen_P(JSON_palette_names); i++)
{
singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
switch (singleJsonSymbol)
{
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownMode))
break;
tft.setTextColor(TFT_MAGENTA);
tft.print(singleJsonSymbol);
printedChars++;
}
if ((qComma > knownMode) || (printedChars > tftcharwidth - 1))
break;
}
// Fourth row with palette name
tft.setTextColor(TFT_YELLOW);
tft.setCursor(3, 130);
qComma = 0;
insideQuotes = false;
printedChars = 0;
// Looking for palette name in JSON.
for (size_t i = 0; i < strlen_P(JSON_palette_names); i++)
{
singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i);
switch (singleJsonSymbol)
{
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
case ']':
break;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownPalette))
case '[':
case ']':
break;
tft.print(singleJsonSymbol);
printedChars++;
case ',':
qComma++;
default:
if (!insideQuotes || (qComma != knownPalette))
break;
tft.print(singleJsonSymbol);
printedChars++;
}
// The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode)
if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1))
break;
}
// The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode)
if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1))
break;
}
// Fifth row with estimated mA usage
tft.setTextColor(TFT_SILVER);
tft.setCursor(3, 152);
// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
tft.print("Current: ");
tft.print(strip.currentMilliamps);
tft.print("mA");
tft.setCursor(0, 192);
tft.setTextColor(TFT_SILVER);
sprintf_P(buff, PSTR("FX Spd:%3d Int:%3d"), effectSpeed, effectIntensity);
tft.print(buff);
// Fifth row with estimated mA usage
tft.setTextColor(TFT_SILVER);
tft.setCursor(0, 216);
// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
tft.print("Current: ");
tft.setTextColor(TFT_ORANGE);
tft.print(strip.currentMilliamps);
tft.print("mA");
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
@ -295,7 +395,7 @@ class St7789DisplayUsermod : public Usermod {
*/
void readFromJsonState(JsonObject& root)
{
userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
//userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
}
@ -316,8 +416,8 @@ class St7789DisplayUsermod : public Usermod {
*/
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject("exampleUsermod");
top["great"] = userVar0; //save this var persistently whenever settings are saved
//JsonObject top = root.createNestedObject("exampleUsermod");
//top["great"] = userVar0; //save this var persistently whenever settings are saved
}
@ -329,10 +429,11 @@ class St7789DisplayUsermod : public Usermod {
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/
void readFromConfig(JsonObject& root)
bool readFromConfig(JsonObject& root)
{
JsonObject top = root["top"];
userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
//JsonObject top = root["top"];
//userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
return true;
}
@ -342,7 +443,7 @@ class St7789DisplayUsermod : public Usermod {
*/
uint16_t getId()
{
return USERMOD_ST7789_DISPLAY;
return USERMOD_ID_ST7789_DISPLAY;
}
//More methods can be added in the future, this example will then be extended.

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();
lastMeasurement = millis();
waitingForConversion = false;
DEBUG_PRINTF("Read temperature %2.1f.\n", temperature);
//DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266
DEBUG_PRINT(F("Read temperature "));
DEBUG_PRINTLN(temperature);
}
bool findSensor() {

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).
* Values in the state object may be modified by connected clients
*/
void addToJsonState(JsonObject &root) {
}
//void addToJsonState(JsonObject &root) {
//}
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject &root) {
}
//void readFromJsonState(JsonObject &root) {
//}
/**
* provide the changeable values
@ -335,11 +335,12 @@ class MultiRelay : public Usermod {
top[FPSTR(_enabled)] = enabled;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += "-"; parName += i; parName += "-";
top[parName+"pin"] = _relay[i].pin;
top[parName+FPSTR(_activeHigh)] = _relay[i].mode;
top[parName+FPSTR(_delay_str)] = _relay[i].delay;
top[parName+FPSTR(_external)] = _relay[i].external;
String parName = FPSTR(_relay_str); parName += '-'; parName += i;
JsonObject relay = top.createNestedObject(parName);
relay["pin"] = _relay[i].pin;
relay[FPSTR(_activeHigh)] = _relay[i].mode;
relay[FPSTR(_delay_str)] = _relay[i].delay;
relay[FPSTR(_external)] = _relay[i].external;
}
DEBUG_PRINTLN(F("MultiRelay config saved."));
}
@ -363,12 +364,19 @@ class MultiRelay : public Usermod {
enabled = top[FPSTR(_enabled)] | enabled;
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;
_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].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;
// end compatibility
_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."));
}
// 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),
hour(localTime), minute(localTime), second(localTime));
savePreset(autoSavePreset, true, presetNameBuffer);
cacheInvalidate++; // force reload of presets
}
void inline displayOverlay() {

View File

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

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.
* Inspired by the Adafruit examples.
*/
uint16_t WS2812FX::mode_theater_chase(void) {
return theater_chase(SEGCOLOR(0), SEGCOLOR(1), true);
return running(SEGCOLOR(0), SEGCOLOR(1), true);
}
@ -431,7 +402,7 @@ uint16_t WS2812FX::mode_theater_chase(void) {
* Inspired by the Adafruit examples.
*/
uint16_t WS2812FX::mode_theater_chase_rainbow(void) {
return theater_chase(color_wheel(SEGENV.step), SEGCOLOR(1), false);
return running(color_wheel(SEGENV.step), SEGCOLOR(1), true);
}
@ -976,29 +947,27 @@ uint16_t WS2812FX::mode_chase_flash_random(void) {
/*
* Alternating pixels running function.
*/
uint16_t WS2812FX::running(uint32_t color1, uint32_t color2) {
uint8_t pxw = 1 + (SEGMENT.intensity >> 5);
uint32_t cycleTime = 35 + (255 - SEGMENT.speed);
uint16_t WS2812FX::running(uint32_t color1, uint32_t color2, bool theatre) {
uint8_t width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window
uint32_t cycleTime = 50 + (255 - SEGMENT.speed);
uint32_t it = now / cycleTime;
if (SEGMENT.speed == 0) it = 0;
bool usePalette = color1 == SEGCOLOR(0);
for(uint16_t i = 0; i < SEGLEN; i++) {
if((i + SEGENV.aux0) % (pxw*2) < pxw) {
if (color1 == SEGCOLOR(0))
{
setPixelColor(SEGLEN -i -1, color_from_palette(SEGLEN -i -1, true, PALETTE_SOLID_WRAP, 0));
} else
{
setPixelColor(SEGLEN -i -1, color1);
}
uint32_t col = color2;
if (usePalette) color1 = color_from_palette(i, true, PALETTE_SOLID_WRAP, 0);
if (theatre) {
if ((i % width) == SEGENV.aux0) col = color1;
} else {
setPixelColor(SEGLEN -i -1, color2);
int8_t pos = (i % (width<<1));
if ((pos < SEGENV.aux0-width) || ((pos >= SEGENV.aux0) && (pos < SEGENV.aux0+width))) col = color1;
}
setPixelColor(i,col);
}
if (it != SEGENV.step )
{
SEGENV.aux0 = (SEGENV.aux0 +1) % (pxw*2);
SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1));
SEGENV.step = it;
}
return FRAMETIME;
@ -1247,44 +1216,19 @@ uint16_t WS2812FX::mode_loading(void) {
//American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all)
uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, uint16_t width)
{
uint16_t counter = now * ((SEGMENT.speed >> 2) +1);
uint16_t idexR = (counter * SEGLEN) >> 16;
if (idexR >= SEGLEN) idexR = 0;
uint16_t topindex = SEGLEN >> 1;
uint16_t idexB = (idexR > topindex) ? idexR - topindex : idexR + topindex;
if (SEGENV.call == 0) SEGENV.aux0 = idexR;
if (idexB >= SEGLEN) idexB = 0; //otherwise overflow on odd number of LEDs
if (all) { //different algo, ensuring immediate fill
if (idexB > idexR) {
fill(color2);
for (uint16_t i = idexR; i < idexB; i++) setPixelColor(i, color1);
} else {
fill(color1);
for (uint16_t i = idexB; i < idexR; i++) setPixelColor(i, color2);
}
} else { //regular dot-only mode
uint8_t size = 1 + (SEGMENT.intensity >> 3);
if (size > SEGLEN/2) size = 1+ SEGLEN/2;
for (uint8_t i=0; i <= size; i++) {
setPixelColor(idexR+i, color1);
setPixelColor(idexB+i, color2);
}
if (SEGENV.aux0 != idexR) {
uint8_t gap = (SEGENV.aux0 < idexR)? idexR - SEGENV.aux0:SEGLEN - SEGENV.aux0 + idexR;
for (uint8_t i = 0; i <= gap ; i++) {
if ((idexR - i) < 0) idexR = SEGLEN-1 + i;
if ((idexB - i) < 0) idexB = SEGLEN-1 + i;
setPixelColor(idexR-i, color1);
setPixelColor(idexB-i, color2);
}
SEGENV.aux0 = idexR;
}
}
uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
uint32_t it = now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
uint16_t offset = it % SEGLEN;
if (!width) width = 1;
for (uint16_t i = 0; i < width; i++) {
uint16_t indexR = (offset + i) % SEGLEN;
uint16_t indexB = (offset + i + (SEGLEN>>1)) % SEGLEN;
setPixelColor(indexR, color1);
setPixelColor(indexB, color2);
}
return FRAMETIME;
}
@ -1292,7 +1236,7 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, bool all)
//American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::mode_police_all()
{
return police_base(RED, BLUE, true);
return police_base(RED, BLUE, (SEGLEN>>1));
}
@ -1300,15 +1244,15 @@ uint16_t WS2812FX::mode_police_all()
uint16_t WS2812FX::mode_police()
{
fill(SEGCOLOR(1));
return police_base(RED, BLUE, false);
return police_base(RED, BLUE, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
}
//Police All with custom colors
uint16_t WS2812FX::mode_two_areas()
{
return police_base(SEGCOLOR(0), SEGCOLOR(1), true);
fill(SEGCOLOR(2));
return police_base(SEGCOLOR(0), SEGCOLOR(1), ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
}
@ -1318,7 +1262,7 @@ uint16_t WS2812FX::mode_two_dots()
fill(SEGCOLOR(2));
uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1);
return police_base(SEGCOLOR(0), color2, false);
return police_base(SEGCOLOR(0), color2, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
}
@ -1326,21 +1270,20 @@ uint16_t WS2812FX::mode_two_dots()
* Tricolor chase function
*/
uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) {
uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*2;
uint32_t it = now / cycleTime;
uint8_t width = (1 + SEGMENT.intensity/32) * 3; //value of 1-8 for each colour
uint8_t index = it % width;
uint32_t cycleTime = 50 + ((255 - SEGMENT.speed)<<1);
uint32_t it = now / cycleTime; // iterator
uint8_t width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour
uint8_t index = it % (width*3);
for(uint16_t i = 0; i < SEGLEN; i++, index++) {
if(index > width-1) index = 0;
for (uint16_t i = 0; i < SEGLEN; i++, index++) {
if (index > (width*3)-1) index = 0;
uint32_t color = color1;
if(index > width*2/3-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
else if(index > width/3-1) color = color2;
if (index > (width<<1)-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1);
else if (index > width-1) color = color2;
setPixelColor(SEGLEN - i -1, color);
}
return FRAMETIME;
}
@ -1548,7 +1491,7 @@ uint16_t WS2812FX::mode_random_chase(void)
return FRAMETIME;
}
//7 bytes
typedef struct Oscillator {
int16_t pos;
int8_t size;
@ -1780,7 +1723,7 @@ uint16_t WS2812FX::mode_fire_2012()
// Step 1. Cool down every cell a little
for (uint16_t i = 0; i < SEGLEN; i++) {
uint8_t temp = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2));
heat[i] = (temp==0 && i<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
@ -1994,7 +1937,7 @@ uint16_t WS2812FX::mode_colortwinkle()
if (fadeUp) {
CRGB incrementalColor = fastled_col;
incrementalColor.nscale8_video( fadeUpAmount);
incrementalColor.nscale8_video(fadeUpAmount);
fastled_col += incrementalColor;
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);
if (col_to_crgb(getPixelColor(i)) == prev) //fix "stuck" pixels
{
if (col_to_crgb(getPixelColor(i)) == prev) { //fix "stuck" pixels
fastled_col += fastled_col;
setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
}
} else {
fastled_col.nscale8( 255 - fadeDownAmount);
fastled_col.nscale8(255 - fadeDownAmount);
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) {
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);
if(getPixelColor(i) == 0) {
if (getPixelColor(i) == 0) {
fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND);
uint16_t index = i >> 3;
uint8_t bitNum = i & 0x07;
@ -2170,10 +2110,14 @@ typedef struct Ripple {
uint16_t pos;
} ripple;
#ifdef ESP8266
#define MAX_RIPPLES 56
#else
#define MAX_RIPPLES 100
#endif
uint16_t WS2812FX::ripple_base(bool rainbow)
{
uint16_t maxRipples = 1 + (SEGLEN >> 2);
if (maxRipples > 100) maxRipples = 100;
uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266
uint16_t dataSize = sizeof(ripple) * maxRipples;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2241,6 +2185,7 @@ uint16_t WS2812FX::ripple_base(bool rainbow)
}
return FRAMETIME;
}
#undef MAX_RIPPLES
uint16_t WS2812FX::mode_ripple(void) {
return ripple_base(false);
@ -2541,7 +2486,6 @@ uint16_t WS2812FX::mode_spots_fade()
//each needs 12 bytes
//Spark type is used for popcorn and 1D fireworks
typedef struct Ball {
unsigned long lastBounceTime;
float impactVelocity;
@ -2651,7 +2595,7 @@ uint16_t WS2812FX::mode_sinelon_dual(void) {
}
uint16_t WS2812FX::mode_sinelon_rainbow(void) {
return sinelon_base(true, true);
return sinelon_base(false, true);
}
@ -2670,7 +2614,7 @@ uint16_t WS2812FX::mode_glitter()
//each needs 12 bytes
//each needs 11 bytes
//Spark type is used for popcorn, 1D fireworks, and drip
typedef struct Spark {
float pos;
@ -2685,7 +2629,7 @@ typedef struct Spark {
*/
uint16_t WS2812FX::mode_popcorn(void) {
//allocate segment data
uint16_t maxNumPopcorn = 24;
uint16_t maxNumPopcorn = 22; // max 22 on 16 segment ESP8266
uint16_t dataSize = sizeof(spark) * maxNumPopcorn;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2744,7 +2688,7 @@ uint16_t WS2812FX::candle(bool multi)
if (multi)
{
//allocate segment data
uint16_t dataSize = (SEGLEN -1) *3;
uint16_t dataSize = (SEGLEN -1) *3; // max length of segment on 16 segment ESP8266 is 75 pixels
if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed
}
@ -2831,9 +2775,14 @@ uint16_t WS2812FX::mode_candle_multi()
/ based on the video: https://www.reddit.com/r/arduino/comments/c3sd46/i_made_this_fireworks_effect_for_my_led_strips/
/ Speed sets frequency of new starbursts, intensity is the intensity of the burst
*/
#define STARBURST_MAX_FRAG 12
//each needs 64 byte
#ifdef ESP8266
#define STARBURST_MAX_FRAG 4
#define STARBURST_MAX_STARS 6
#else
#define STARBURST_MAX_FRAG 10
#define STARBURST_MAX_STARS 11
#endif
//each needs 18+STARBURST_MAX_FRAG*4 bytes
typedef struct particle {
CRGB color;
uint32_t birth =0;
@ -2844,8 +2793,7 @@ typedef struct particle {
} star;
uint16_t WS2812FX::mode_starburst(void) {
uint8_t numStars = 1 + (SEGLEN >> 3);
if (numStars > 15) numStars = 15;
uint8_t numStars = min(1 + (SEGLEN >> 3), STARBURST_MAX_STARS); // 11 * 58 * 32 = 19k (ESP32), 6 * 34 * 16 = 3.2k (ESP8266)
uint16_t dataSize = sizeof(star) * numStars;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -2946,18 +2894,22 @@ uint16_t WS2812FX::mode_starburst(void) {
}
return FRAMETIME;
}
#undef STARBURST_MAX_FRAG
#undef STARBURST_MAX_STARS
/*
* Exploding fireworks effect
* adapted from: http://www.anirama.com/1000leds/1d-fireworks/
*/
#ifdef ESP8266
#define MAX_SPARKS 20 // number of fragments (11 bytes per fragment)
#else
#define MAX_SPARKS 58 // number of fragments
#endif
uint16_t WS2812FX::mode_exploding_fireworks(void)
{
//allocate segment data
uint16_t numSparks = 2 + (SEGLEN >> 1);
if (numSparks > 80) numSparks = 80;
uint16_t numSparks = min(2 + (SEGLEN >> 1), MAX_SPARKS); // max 58 for 32 segment ESP32, 20 for 16 segment ESP8266
uint16_t dataSize = sizeof(spark) * numSparks;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -3052,7 +3004,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
SEGENV.aux0--;
if (SEGENV.aux0 < 4) {
SEGENV.aux0 = 0; //back to flare
SEGENV.step = (SEGMENT.intensity > random8()); //decide firing side
SEGENV.step = actuallyReverse ^ (SEGMENT.intensity > random8()); //decide firing side
}
}
@ -3060,6 +3012,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
return FRAMETIME;
}
#undef MAX_SPARKS
/*
@ -3069,7 +3022,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void)
uint16_t WS2812FX::mode_drip(void)
{
//allocate segment data
uint16_t numDrops = 4;
uint8_t numDrops = 4;
uint16_t dataSize = sizeof(spark) * numDrops;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -3077,7 +3030,7 @@ uint16_t WS2812FX::mode_drip(void)
Spark* drops = reinterpret_cast<Spark*>(SEGENV.data);
numDrops = 1 + (SEGMENT.intensity >> 6);
numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3
float gravity = -0.0005 - (SEGMENT.speed/50000.0);
gravity *= SEGLEN;
@ -3107,13 +3060,13 @@ uint16_t WS2812FX::mode_drip(void)
if (drops[j].pos > 0) { // fall until end of segment
drops[j].pos += drops[j].vel;
if (drops[j].pos < 0) drops[j].pos = 0;
drops[j].vel += gravity;
drops[j].vel += gravity; // gravity is negative
for (uint16_t i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets
uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally
setPixelColor(pos,color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling
}
if (drops[j].colIndex > 2) { // during bounce, some water is on the floor
setPixelColor(0,color_blend(SEGCOLOR(0),BLACK,drops[j].col));
}
@ -3142,6 +3095,7 @@ uint16_t WS2812FX::mode_drip(void)
* Tetris or Stacking (falling bricks) Effect
* by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home)
*/
//12 bytes
typedef struct Tetris {
float pos;
float speed;
@ -3163,8 +3117,8 @@ uint16_t WS2812FX::mode_tetrix(void) {
}
if (SEGENV.step == 0) { //init
drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>3)+1 : random8(6,40)); // set speed
drop->pos = SEGLEN-1; // start at end of segment
drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>2)+1 : random8(6,64)); // set speed
drop->pos = SEGLEN; // start at end of segment (no need to subtract 1)
drop->col = color_from_palette(random8(0,15)<<4,false,false,0); // limit color choices so there is enough HUE gap
SEGENV.step = 1; // drop state (0 init, 1 forming, 2 falling)
SEGENV.aux0 = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : random8(1,5)) * (1+(SEGLEN>>6)); // size of brick
@ -3196,13 +3150,17 @@ uint16_t WS2812FX::mode_tetrix(void) {
/ adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino
*/
uint16_t WS2812FX::mode_plasma(void) {
uint8_t thisPhase = beatsin8(6,-64,64); // Setting phase change for a couple of waves.
uint8_t thatPhase = beatsin8(7,-64,64);
// initialize phases on start
if (SEGENV.call == 0) {
SEGENV.aux0 = random8(0,2); // add a bit of randomness
}
uint8_t thisPhase = beatsin8(6+SEGENV.aux0,-64,64);
uint8_t thatPhase = beatsin8(7+SEGENV.aux0,-64,64);
for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows:
uint8_t colorIndex = cubicwave8(((i*(1+ 3*(SEGMENT.speed >> 5)))+(thisPhase)) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change.
+ cos8(((i*(1+ 2*(SEGMENT.speed >> 5)))+(thatPhase)) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish.
uint8_t thisBright = qsub8(colorIndex, beatsin8(6,0, (255 - SEGMENT.intensity)|0x01 ));
uint8_t colorIndex = cubicwave8((i*(2+ 3*(SEGMENT.speed >> 5))+thisPhase) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change.
+ cos8((i*(1+ 2*(SEGMENT.speed >> 5))+thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish.
uint8_t thisBright = qsub8(colorIndex, beatsin8(7,0, (128 - (SEGMENT.intensity>>1))));
CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND);
setPixelColor(i, color.red, color.green, color.blue);
}
@ -3531,7 +3489,7 @@ uint16_t WS2812FX::mode_noisepal(void) { // S
uint16_t scale = 15 + (SEGMENT.intensity >> 2); //default was 30
//#define scale 30
uint16_t dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes
uint16_t dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes (2 * 16 * 3 = 96 bytes)
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
CRGBPalette16* palettes = reinterpret_cast<CRGBPalette16*>(SEGENV.data);
@ -3644,7 +3602,7 @@ uint16_t WS2812FX::mode_chunchun(void)
return FRAMETIME;
}
//13 bytes
typedef struct Spotlight {
float speed;
uint8_t colorIdx;
@ -3661,6 +3619,11 @@ typedef struct Spotlight {
#define SPOT_TYPE_3X_DOT 4
#define SPOT_TYPE_4X_DOT 5
#define SPOT_TYPES_COUNT 6
#ifdef ESP8266
#define SPOT_MAX_COUNT 17 //Number of simultaneous waves
#else
#define SPOT_MAX_COUNT 49 //Number of simultaneous waves
#endif
/*
* Spotlights moving back and forth that cast dancing shadows.
@ -3671,7 +3634,7 @@ typedef struct Spotlight {
*/
uint16_t WS2812FX::mode_dancing_shadows(void)
{
uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, 50);
uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266
bool initialize = SEGENV.aux0 != numSpotlights;
SEGENV.aux0 = numSpotlights;
@ -3810,7 +3773,7 @@ uint16_t WS2812FX::mode_washing_machine(void) {
Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
*/
uint16_t WS2812FX::mode_blends(void) {
uint16_t dataSize = sizeof(uint32_t) * SEGLEN;
uint16_t dataSize = sizeof(uint32_t) * SEGLEN; // max segment length of 56 pixels on 16 segment ESP8266
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
@ -3825,6 +3788,11 @@ uint16_t WS2812FX::mode_blends(void) {
return FRAMETIME;
}
/*
TV Simulator
Modified and adapted to WLED by Def3nder, based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch
*/
//43 bytes
typedef struct TvSim {
uint32_t totalTime = 0;
uint32_t fadeTime = 0;
@ -3845,11 +3813,6 @@ typedef struct TvSim {
uint16_t pb = 0;
} tvSim;
/*
TV Simulator
Modified and adapted to WLED by Def3nder, based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch
*/
uint16_t WS2812FX::mode_tv_simulator(void) {
uint16_t nr, ng, nb, r, g, b, i, hue;
uint8_t sat, bri, j;
@ -3957,10 +3920,15 @@ uint16_t WS2812FX::mode_tv_simulator(void) {
*/
//CONFIG
#define W_MAX_COUNT 20 //Number of simultaneous waves
#ifdef ESP8266
#define W_MAX_COUNT 9 //Number of simultaneous waves
#else
#define W_MAX_COUNT 20 //Number of simultaneous waves
#endif
#define W_MAX_SPEED 6 //Higher number, higher speed
#define W_WIDTH_FACTOR 6 //Higher number, smaller waves
//24 bytes
class AuroraWave {
private:
uint16_t ttl;
@ -4055,10 +4023,10 @@ uint16_t WS2812FX::mode_aurora(void) {
if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
//Intensity slider changed or first call
SEGENV.aux1 = ((float)SEGMENT.intensity / 255) * W_MAX_COUNT;
SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT);
SEGENV.aux0 = SEGMENT.intensity;
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) {
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 26 on 32 segment ESP32, 9 on 16 segment ESP8266
return mode_static(); //allocation failed
}

View File

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

View File

@ -23,7 +23,7 @@
Modified heavily for WLED
*/
#include "wled.h"
#include "FX.h"
#include "palettes.h"
@ -40,7 +40,7 @@
another example. Switches direction every 5 LEDs.
{"map":[
0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14,
19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25]
19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25]}
*/
//factory defaults LED setup
@ -49,7 +49,7 @@
//#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
#ifndef PIXEL_COUNTS
#define PIXEL_COUNTS DEFAULT_LED_COUNT
#define PIXEL_COUNTS 30
#endif
#ifndef DATA_PINS
@ -65,25 +65,22 @@
#endif
//do not call this method from system context (network callback)
void WS2812FX::finalizeInit(uint16_t countPixels)
void WS2812FX::finalizeInit(void)
{
RESET_RUNTIME;
_length = countPixels;
isRgbw = isOffRefreshRequred = false;
//if busses failed to load, add default (FS issue...)
//if busses failed to load, add default (fresh install, FS issue, ...)
if (busses.getNumBusses() == 0) {
const uint8_t defDataPins[] = {DATA_PINS};
const uint16_t defCounts[] = {PIXEL_COUNTS};
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0])); // min 1
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); // min 1
uint16_t prevLen = 0;
for (uint8_t i = 0; i < defNumBusses; i++) {
for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES; i++) {
uint8_t defPin[] = {defDataPins[i]};
uint16_t start = prevLen;
uint16_t count = _length;
if (defNumBusses > 1 && defNumCounts) {
count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
}
uint16_t count = (i < defNumCounts) ? defCounts[i] : defCounts[i>0?i-1:0];
prevLen += count;
BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, COL_ORDER_GRB);
busses.add(defCfg);
@ -92,65 +89,33 @@ void WS2812FX::finalizeInit(uint16_t countPixels)
deserializeMap();
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
_length = 0;
for (uint8_t i=0; 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);
//TODO make sure segments are only refreshed when bus config actually changed (new settings page)
uint8_t s = 0;
#ifdef ESP8266
for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
Bus* b = busses.getBus(i);
if (autoSegments) { //make one segment per bus
segStarts[s] = b->getStart();
segStops[s] = segStarts[s] + b->getLength();
//check for overlap with previous segments
for (uint8_t j = 0; j < s; j++) {
if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) {
//segments overlap, merge
segStarts[j] = min(segStarts[s],segStarts[j]);
segStops [j] = max(segStops [s],segStops [j]); segStops[s] = 0;
s--;
}
}
s++;
}
#ifdef ESP8266
if ((!IS_DIGITAL(b->getType()) || IS_2PIN(b->getType()))) continue;
uint8_t pins[5];
b->getPins(pins);
BusDigital* bd = static_cast<BusDigital*>(b);
if (pins[0] == 3) bd->reinit();
#endif
}
if (autoSegments) {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
_segments[i].start = segStarts[i];
_segments[i].stop = segStops [i];
}
} else {
//expand the main seg to the entire length, but only if there are no other segments
uint8_t mainSeg = getMainSegmentId();
bool isMultipleSegs = false;
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (i != mainSeg && _segments[i].isActive()) isMultipleSegs = true;
}
if (!isMultipleSegs) {
_segments[mainSeg].start = 0; _segments[mainSeg].stop = _length;
} else {
//there are multiple segments, leave them, but prune length to total
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
if (_segments[i].start > _length) _segments[i].stop = 0;
if (_segments[i].stop > _length) _segments[i].stop = _length;
}
}
}
#endif
}
void WS2812FX::service() {
@ -218,14 +183,13 @@ uint16_t WS2812FX::realPixelIndex(uint16_t i) {
int16_t realIndex = iGroup;
if (IS_REVERSE) {
if (IS_MIRROR) {
realIndex = (SEGMENT.length() -1) / 2 - iGroup; //only need to index half the pixels
realIndex = (SEGMENT.length() - 1) / 2 - iGroup; //only need to index half the pixels
} else {
realIndex = SEGMENT.length() - iGroup - 1;
realIndex = (SEGMENT.length() - 1) - iGroup;
}
}
realIndex += SEGMENT.start;
return realIndex;
}
@ -246,7 +210,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
}
if (SEGLEN) {//from segment
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
if (_bri_t < 255) {
r = scale8(r, _bri_t);
@ -256,12 +219,12 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
}
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
bool reversed = IS_REVERSE;
/* Set all the pixels in the group */
uint16_t realIndex = realPixelIndex(i);
uint16_t len = SEGMENT.length();
for (uint16_t j = 0; j < SEGMENT.grouping; j++) {
int indexSet = realIndex + (reversed ? -j : j);
uint16_t indexSet = realIndex + (IS_REVERSE ? -j : j);
if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) {
if (IS_MIRROR) { //set the corresponding mirrored pixel
uint16_t indexMir = SEGMENT.stop - indexSet + SEGMENT.start - 1;
@ -273,8 +236,8 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
busses.setPixelColor(indexMir, col);
}
/* offset/phase */
indexSet += SEGMENT.offset;
if (indexSet >= SEGMENT.stop) indexSet -= len;
indexSet += SEGMENT.offset;
if (indexSet >= SEGMENT.stop) indexSet -= len;
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
busses.setPixelColor(indexSet, col);
@ -282,7 +245,6 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
}
} else { //live data, etc.
if (i < customMappingSize) i = customMappingTable[i];
uint32_t col = ((w << 24) | (r << 16) | (g << 8) | (b));
busses.setPixelColor(i, col);
}
@ -563,7 +525,9 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
}
if (i < customMappingSize) i = customMappingTable[i];
if (i >= _length) return 0;
// TODO: may need to add IS_REVERSE and IS_MIRROR logic
return busses.getPixelColor(i);
}
@ -584,14 +548,14 @@ uint32_t WS2812FX::getLastShow(void) {
return _lastShow;
}
//TODO these need to be on a per-strip basis
uint8_t WS2812FX::getColorOrder(void) {
return COL_ORDER_GRB;
}
void WS2812FX::setColorOrder(uint8_t co) {
//bus->SetColorOrder(co);
}
// there is no longer any need for these two
//uint8_t WS2812FX::getColorOrder(void) {
// return COL_ORDER_GRB;
//}
//
//void WS2812FX::setColorOrder(uint8_t co) {
// //bus->SetColorOrder(co);
//}
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing) {
if (n >= MAX_NUM_SEGMENTS) return;
@ -603,7 +567,11 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
if (seg.stop) setRange(seg.start, seg.stop -1, 0); //turn old segment range off
if (i2 <= i1) //disable segment
{
seg.stop = 0;
seg.stop = 0;
if (seg.name) {
delete[] seg.name;
seg.name = nullptr;
}
if (n == mainSegment) //if main segment is deleted, set first active as main segment
{
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
@ -628,6 +596,7 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
}
void WS2812FX::resetSegments() {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete _segments[i].name;
mainSegment = 0;
memset(_segments, 0, sizeof(_segments));
//memset(_segment_runtimes, 0, sizeof(_segment_runtimes));
@ -656,6 +625,38 @@ void WS2812FX::resetSegments() {
_segment_runtimes[0].reset();
}
void WS2812FX::populateDefaultSegments() {
uint16_t length = 0;
if (autoSegments) {
for (uint8_t i=0; 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)
void WS2812FX::setPixelSegment(uint8_t n)
{
@ -1033,18 +1034,33 @@ uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8
//load custom mapping table from JSON file
void WS2812FX::deserializeMap(void) {
if (!WLED_FS.exists("/ledmap.json")) return;
void WS2812FX::deserializeMap(uint8_t n) {
String fileName = String(F("/ledmap"));
if (n) fileName += String(n);
fileName += String(F(".json"));
bool isFile = WLED_FS.exists(fileName);
if (!isFile) {
// erase custom mapping if selecting nonexistent ledmap.json (n==0)
if (!n && customMappingTable != nullptr) {
customMappingSize = 0;
delete[] customMappingTable;
customMappingTable = nullptr;
}
return;
}
DynamicJsonDocument doc(JSON_BUFFER_SIZE); // full sized buffer for larger maps
DEBUG_PRINT(F("Reading LED map from "));
DEBUG_PRINTLN(fileName);
DEBUG_PRINTLN(F("Reading LED map from /ledmap.json..."));
if (!readObjectFromFile("/ledmap.json", nullptr, &doc)) return; //if file does not exist just exit
if (!readObjectFromFile(fileName.c_str(), nullptr, &doc)) return; //if file does not exist just exit
// erase old custom ledmap
if (customMappingTable != nullptr) {
customMappingSize = 0;
delete[] customMappingTable;
customMappingTable = nullptr;
customMappingSize = 0;
}
JsonArray map = doc[F("map")];

View File

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

View File

@ -10,18 +10,24 @@
#include "bus_wrapper.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
struct BusConfig {
uint8_t type = TYPE_WS2812_RGB;
uint16_t count = 1;
uint16_t start = 0;
uint8_t colorOrder = COL_ORDER_GRB;
bool reversed = false;
uint16_t count;
uint16_t start;
uint8_t colorOrder;
bool reversed;
uint8_t skipAmount;
bool rgbwOverride;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255};
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip=0) {
type = busType; count = len; start = pstart;
colorOrder = pcolorOrder; reversed = rev; skipAmount = skip;
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0) {
rgbwOverride = (bool) GET_BIT(busType,7);
type = busType & 0x7F; // bit 7 may be/is hacked to include RGBW info (1=RGBW, 0=RGB)
count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip;
uint8_t nPins = 1;
if (type > 47) nPins = 2;
else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type);
@ -74,7 +80,7 @@ class Bus {
}
virtual uint16_t getLength() {
return 1;
return 1; // is this ok? shouldn't it be 0 in virtual function?
}
virtual void setColorOrder() {}
@ -130,12 +136,12 @@ class BusDigital : public Bus {
reversed = bc.reversed;
_skip = bc.skipAmount; //sacrificial pixels
_len = bc.count + _skip;
_iType = PolyBus::getI(bc.type, _pins, nr);
_rgbw = bc.rgbwOverride || Bus::isRgbw(bc.type); // RGBW override in bit 7
_iType = PolyBus::getI(bc.type, _pins, nr, _rgbw);
if (_iType == I_NONE) return;
_busPtr = PolyBus::create(_iType, _pins, _len, nr);
_valid = (_busPtr != nullptr);
_colorOrder = bc.colorOrder;
//Serial.printf("Successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n",nr, len, type, pins[0],pins[1],_iType);
};
inline void show() {
@ -189,7 +195,7 @@ class BusDigital : public Bus {
}
inline bool isRgbw() {
return (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814);
return (_rgbw || _type == TYPE_SK6812_RGBW || _type == TYPE_TM1814);
}
inline uint8_t skippedLeds() {
@ -201,7 +207,6 @@ class BusDigital : public Bus {
}
void cleanup() {
//Serial.println("Digital Cleanup");
PolyBus::cleanup(_busPtr, _iType);
_iType = I_NONE;
_valid = false;
@ -220,6 +225,7 @@ class BusDigital : public Bus {
uint8_t _iType = I_NONE;
uint16_t _len = 0;
uint8_t _skip = 0;
bool _rgbw = false;
void * _busPtr = nullptr;
};
@ -227,6 +233,7 @@ class BusDigital : public Bus {
class BusPwm : public Bus {
public:
BusPwm(BusConfig &bc) : Bus(bc.type, bc.start) {
_valid = false;
if (!IS_PWM(bc.type)) return;
uint8_t numPins = NUM_PWM_PINS(bc.type);
@ -280,10 +287,12 @@ class BusPwm : public Bus {
//does no index check
uint32_t getPixelColor(uint16_t pix) {
if (!_valid) return 0;
return ((_data[3] << 24) | (_data[0] << 16) | (_data[1] << 8) | (_data[2]));
}
void show() {
if (!_valid) return;
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
uint8_t scaled = (_data[i] * _bri) / 255;
@ -302,7 +311,9 @@ class BusPwm : public Bus {
uint8_t getPins(uint8_t* pinArray) {
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i];
for (uint8_t i = 0; i < numPins; i++) {
pinArray[i] = _pins[i];
}
return numPins;
}
@ -328,13 +339,13 @@ class BusPwm : public Bus {
void deallocatePins() {
uint8_t numPins = NUM_PWM_PINS(_type);
for (uint8_t i = 0; i < numPins; i++) {
pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
if (!pinManager.isPinOk(_pins[i])) continue;
#ifdef ESP8266
digitalWrite(_pins[i], LOW); //turn off PWM interrupt
#else
if (_ledcStart < 16) ledcDetachPin(_pins[i]);
#endif
pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
}
#ifdef ARDUINO_ARCH_ESP32
pinManager.deallocateLedc(_ledcStart, numPins);
@ -342,6 +353,86 @@ class BusPwm : public Bus {
}
};
class BusVirtual : public Bus {
public:
BusVirtual(BusConfig &bc) : Bus(bc.type, bc.start) {
_valid = false;
_data = (byte *)malloc(bc.count * (_rgbw ? 4 : 3));
if (_data == nullptr) return;
memset(_data, 0, bc.count * (_rgbw ? 4 : 3));
_len = bc.count;
//_rgbw = bc.rgbwOverride; // RGBW override in bit 7 or can have a special type
_rgbw = _type == TYPE_VIRTUAL_RGBW;
_colorOrder = bc.colorOrder;
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
_broadcastLock = false;
_valid = true;
};
void setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return;
_data[pix] = 0xFF & (c >> 16);
_data[pix+1] = 0xFF & (c >> 8);
_data[pix+2] = 0xFF & (c );
if (_rgbw) _data[pix+3] = 0xFF & (c >> 24);
}
uint32_t getPixelColor(uint16_t pix) {
if (!_valid || pix >= _len) return 0;
return ((_rgbw?(_data[pix+3] << 24):0) | (_data[pix] << 16) | (_data[pix+1] << 8) | (_data[pix+2]));
}
void show() {
if (!_valid || _broadcastLock) return;
_broadcastLock = true;
realtimeBoroadcast(_client, _len, _data, _rgbw);
_broadcastLock = false;
}
inline bool canShow() {
return !_broadcastLock;
}
inline void setBrightness(uint8_t b) {
// not sure if this is correctly implemented
for (uint16_t pix=0; pix<_len; pix++) {
_data[pix ] = scale8(_data[pix ], b);
_data[pix+1] = scale8(_data[pix+1], b);
_data[pix+2] = scale8(_data[pix+2], b);
if (_rgbw) _data[pix+3] = scale8(_data[pix+3], b);
}
}
inline bool isRgbw() {
return _rgbw;
}
inline uint16_t getLength() {
return _len;
}
void cleanup() {
_type = I_NONE;
_valid = false;
if (_data != nullptr) free(_data);
_data = nullptr;
}
~BusVirtual() {
cleanup();
}
private:
IPAddress _client;
uint16_t _len = 0;
uint8_t _colorOrder;
bool _rgbw;
bool _broadcastLock;
byte* _data;
};
class BusManager {
public:
BusManager() {
@ -352,7 +443,9 @@ class BusManager {
static uint32_t memUsage(BusConfig &bc) {
uint8_t type = bc.type;
uint16_t len = bc.count;
if (type < 32) {
if (type < 4) {
return len * (type+1);
} else if (type < 32) {
#ifdef ESP8266
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
if (type > 29) return len*20; //RGBW
@ -373,7 +466,9 @@ class BusManager {
int add(BusConfig &bc) {
if (numBusses >= WLED_MAX_BUSSES) return -1;
if (IS_DIGITAL(bc.type)) {
if (bc.type>1 && bc.type<4) {
busses[numBusses] = new BusVirtual(bc);
} else if (IS_DIGITAL(bc.type)) {
busses[numBusses] = new BusDigital(bc, numBusses);
} else {
busses[numBusses] = new BusPwm(bc);
@ -444,6 +539,7 @@ class BusManager {
return len;
}
// a workaround
static inline bool isRgbw(uint8_t type) {
return Bus::isRgbw(type);
}

View File

@ -383,11 +383,6 @@ class PolyBus {
uint8_t w = c >> 24;
RgbwColor col;
//TODO make color order override possible on a per-strip basis
#ifdef COLOR_ORDER_OVERRIDE
if (pix >= COO_MIN && pix < COO_MAX) co = COO_ORDER;
#endif
//reorder channels to selected order
switch (co)
{
@ -559,10 +554,6 @@ class PolyBus {
case I_SS_P98_3: col = (static_cast<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)
{
// W G R B
@ -632,14 +623,17 @@ class PolyBus {
}
//gives back the internal type index (I_XX_XXX_X above) for the input
static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0) {
static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0, bool isRGBW = false) {
if (!IS_DIGITAL(busType)) return I_NONE;
if (IS_2PIN(busType)) { //SPI LED chips
bool isHSPI = false;
#ifdef ESP8266
if (pins[0] == P_8266_HS_MOSI && pins[1] == P_8266_HS_CLK) isHSPI = true;
#else
if(!num) isHSPI = true; // temporary hack to limit use of hardware SPI to a single SPI peripheral: only allow ESP32 hardware serial on segment 0
// temporary hack to limit use of hardware SPI to a single SPI peripheral: only allow ESP32 hardware serial on segment 0
if (!num) isHSPI = true;
//if (num==0 && pins[0] == P_32_VS_MOSI && pins[1] == P_32_VS_CLK) isHSPI = true; // no nultiplexing, up to 80MHz clock
//if (num==1 && pins[0] == P_32_HS_MOSI && pins[1] == P_32_HS_CLK) isHSPI = true;
#endif
uint8_t t = I_NONE;
switch (busType) {
@ -658,9 +652,8 @@ class PolyBus {
switch (busType) {
case TYPE_WS2812_RGB:
case TYPE_WS2812_WWA:
return I_8266_U0_NEO_3 + offset;
case TYPE_SK6812_RGBW:
return I_8266_U0_NEO_4 + offset;
return (isRGBW ? I_8266_U0_NEO_4 : I_8266_U0_NEO_3) + offset;
case TYPE_WS2811_400KHZ:
return I_8266_U0_400_3 + offset;
case TYPE_TM1814:
@ -678,9 +671,10 @@ class PolyBus {
switch (busType) {
case TYPE_WS2812_RGB:
case TYPE_WS2812_WWA:
return I_32_RN_NEO_3 + offset;
// return I_32_RN_NEO_3 + offset;
case TYPE_SK6812_RGBW:
return I_32_RN_NEO_4 + offset;
// return I_32_RN_NEO_4 + offset;
return (isRGBW ? I_32_RN_NEO_4 : I_32_RN_NEO_3) + offset;
case TYPE_WS2811_400KHZ:
return I_32_RN_400_3 + offset;
case TYPE_TM1814:

View File

@ -10,10 +10,11 @@ static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize fl
void shortPressAction(uint8_t b)
{
if (!macroButton[b])
{
toggleOnOff();
colorUpdated(CALL_MODE_BUTTON);
if (!macroButton[b]) {
switch (b) {
case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break;
default: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break;
}
} else {
applyPreset(macroButton[b], CALL_MODE_BUTTON);
}
@ -26,6 +27,44 @@ void shortPressAction(uint8_t b)
}
}
void longPressAction(uint8_t b)
{
if (!macroLongPress[b]) {
switch (b) {
case 0: _setRandomColor(false,true); break;
default: bri += 8; colorUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
}
} else {
applyPreset(macroLongPress[b], CALL_MODE_BUTTON);
}
// publish MQTT message
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
char subuf[64];
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
mqtt->publish(subuf, 0, false, "long");
}
}
void doublePressAction(uint8_t b)
{
if (!macroDoublePress[b]) {
switch (b) {
case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break;
default: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
}
} else {
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON);
}
// publish MQTT message
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
char subuf[64];
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
mqtt->publish(subuf, 0, false, "double");
}
}
bool isButtonPressed(uint8_t i)
{
if (btnPin[i]<0) return false;
@ -186,61 +225,43 @@ void handleButton()
}
//momentary button logic
if (isButtonPressed(b)) //pressed
{
if (isButtonPressed(b)) { //pressed
if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis();
buttonPressedBefore[b] = true;
if (millis() - buttonPressedTime[b] > 600) //long press
{
if (!buttonLongPressed[b])
{
if (macroLongPress[b]) {applyPreset(macroLongPress[b], CALL_MODE_BUTTON);}
else _setRandomColor(false,true);
// publish MQTT message
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
char subuf[64];
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
mqtt->publish(subuf, 0, false, "long");
}
buttonLongPressed[b] = true;
if (millis() - buttonPressedTime[b] > 600) { //long press
if (!buttonLongPressed[b]) longPressAction(b);
else if (b) { // repeatable action (~3 times per s) on button > 0
longPressAction(b);
buttonPressedTime[b] = millis() - 300; // 300ms
}
buttonLongPressed[b] = true;
}
}
else if (!isButtonPressed(b) && buttonPressedBefore[b]) //released
{
} else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
long dur = millis() - buttonPressedTime[b];
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} //too short "press", debounce
bool doublePress = buttonWaitTime[b];
bool doublePress = buttonWaitTime[b]; //did we have short press before?
buttonWaitTime[b] = 0;
if (dur > 6000 && b==0) //long press on button 0
{
if (b == 0 && dur > 6000) { //long press on button 0 (when released)
WLED::instance().initAP(true);
}
else if (!buttonLongPressed[b]) { //short press
if (macroDoublePress[b])
{
if (doublePress) {
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON);
// publish MQTT message
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
char subuf[64];
sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
mqtt->publish(subuf, 0, false, "double");
}
} else buttonWaitTime[b] = millis();
} else shortPressAction(b);
} else if (!buttonLongPressed[b]) { //short press
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
if (doublePress) {
doublePressAction(b);
} else {
buttonWaitTime[b] = millis();
}
}
buttonPressedBefore[b] = false;
buttonLongPressed[b] = false;
}
if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > 450 && !buttonPressedBefore[b])
{
// if 450ms elapsed since last press/release it is a short press
if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > 350 && !buttonPressedBefore[b]) {
buttonWaitTime[b] = 0;
shortPressAction(b);
}

View File

@ -1,4 +1,5 @@
#include "wled.h"
#include "wled_ethernet.h"
/*
* Serializes and parses the cfg.json and wsec.json settings files, stored in internal FS.
@ -29,6 +30,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
getStringFromJson(cmDNS, id[F("mdns")], 33);
getStringFromJson(serverDescription, id[F("name")], 33);
getStringFromJson(alexaInvocationName, id[F("inv")], 33);
#ifndef WLED_DISABLE_SIMPLE_UI
CJSON(simplifiedUI, id[F("sui")]);
#endif
JsonObject nw_ins_0 = doc["nw"]["ins"][0];
getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33);
@ -60,7 +64,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(apBehavior, ap[F("behav")]);
/*
JsonArray ap_ip = ap["ip"];
for (byte i = 0; i < 4; i++) {
@ -79,16 +82,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(ledCount, hw_led[F("total")]);
if (ledCount > MAX_LEDS) ledCount = MAX_LEDS;
uint16_t lC = 0;
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);
JsonArray ins = hw_led["ins"];
if (fromFS || !ins.isNull()) {
uint8_t s = 0; //bus iterator
strip.isRgbw = false;
strip.isOffRefreshRequred = false;
uint8_t s = 0; // bus iterator
busses.removeAll();
uint32_t mem = 0;
for (JsonObject elm : ins) {
@ -104,12 +107,20 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
}
uint16_t length = elm[F("len")] | 1;
if (length==0 || length+lC > MAX_LEDS) continue; // zero length or we reached max. number of LEDs, just stop
uint8_t colorOrder = (int)elm[F("order")];
uint8_t skipFirst = elm[F("skip")];
uint16_t start = elm[F("start")] | 0;
if (start > lC+length) continue; // something is very wrong :)
uint8_t ledType = elm["type"] | TYPE_WS2812_RGB;
bool reversed = elm["rev"];
//if ((bool)elm[F("rgbw")]) SET_BIT(ledType,7); else UNSET_BIT(ledType,7); // hack bit 7 to indicate RGBW (as an override if necessary)
s++;
lC += length;
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
mem += BusManager::memUsage(bc);
if (mem <= MAX_LED_MEMORY && busses.getNumBusses() <= WLED_MAX_BUSSES) busses.add(bc); // finalization will be done in WLED::beginStrip()
/*
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst);
if (bc.adjustBounds(ledCount)) {
//RGBW mode is enabled if at least one of the strips is RGBW
@ -120,9 +131,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
mem += busses.memUsage(bc);
if (mem <= MAX_LED_MEMORY) busses.add(bc);
}
*/
}
strip.finalizeInit(ledCount);
// finalization done in beginStrip()
}
if (lC > ledCount) ledCount = lC; // fix incorrect total length (honour analog setup)
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
// read multiple button configuration
@ -461,6 +474,7 @@ void serializeConfig() {
id[F("mdns")] = cmDNS;
id[F("name")] = serverDescription;
id[F("inv")] = alexaInvocationName;
id[F("sui")] = simplifiedUI;
JsonObject nw = doc.createNestedObject("nw");
@ -500,6 +514,25 @@ void serializeConfig() {
#ifdef WLED_USE_ETHERNET
JsonObject ethernet = doc.createNestedObject("eth");
ethernet["type"] = ethernetType;
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
JsonArray pins = ethernet.createNestedArray("pin");
for (uint8_t p=0; p<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
JsonObject hw = doc.createNestedObject("hw");
@ -526,6 +559,7 @@ void serializeConfig() {
ins["rev"] = bus->reversed;
ins[F("skip")] = bus->skippedLeds();
ins["type"] = bus->getType();
ins[F("rgbw")] = bus->isRgbw();
}
// button(s)
@ -550,7 +584,7 @@ void serializeConfig() {
JsonObject hw_ir = hw.createNestedObject("ir");
hw_ir["pin"] = irPin;
hw_ir[F("type")] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled )
hw_ir["type"] = irEnabled; // the byte 'irEnabled' does contain the IR-Remote Type ( 0=disabled )
JsonObject hw_relay = hw.createNestedObject(F("relay"));
hw_relay["pin"] = rlyPin;

View File

@ -122,6 +122,8 @@
#define TYPE_NONE 0 //light is not configured
#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light
#define TYPE_VIRTUAL_RGB 2 //virtual RGB bus (master broadcast bus)
#define TYPE_VIRTUAL_RGBW 3 //virtual RGBW bus (master broadcast bus)
//Digital types (data pin only) (16-31)
#define TYPE_WS2812_1CH 20 //white-only chips
#define TYPE_WS2812_WWA 21 //amber + warm + cold white
@ -233,7 +235,7 @@
// maximum number of LEDs - more than 1500 LEDs (or 500 DMA "LEDPIN 3" driven ones) will cause a low memory condition on ESP8266
#ifndef MAX_LEDS
#ifdef ESP8266
#define MAX_LEDS 1664 // can't rely on memory limit to limit this to 1600 LEDs
#define MAX_LEDS 1200 // can't rely on memory limit to limit this to 1200 LEDs
#else
#define MAX_LEDS 8192
#endif
@ -241,14 +243,14 @@
#ifndef MAX_LED_MEMORY
#ifdef ESP8266
#define MAX_LED_MEMORY 5000
#define MAX_LED_MEMORY 4000
#else
#define MAX_LED_MEMORY 64000
#endif
#endif
#ifndef MAX_LEDS_PER_BUS
#define MAX_LEDS_PER_BUS 4096
#define MAX_LEDS_PER_BUS 2048 // may not be enough for fast LEDs (i.e. APA102)
#endif
// string temp buffer (now stored in stack locally)
@ -282,7 +284,7 @@
// Maximum size of node map (list of other WLED instances)
#ifdef ESP8266
#define WLED_MAX_NODES 15
#define WLED_MAX_NODES 24
#else
#define WLED_MAX_NODES 150
#endif
@ -304,8 +306,4 @@
#endif
#endif
#ifndef DEFAULT_LED_COUNT
#define DEFAULT_LED_COUNT 30
#endif
#endif

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="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="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="buttonPcm" onclick="togglePcMode(true)"><i class="icons">&#xe23d;</i><p class="tab-label">PC Mode</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>
<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>
</div>
<div id="briwrap">
<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" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
</div>
</div>
<iframe id="liveview" src="about:blank"></iframe>
@ -46,17 +47,26 @@
<div id="Colors" class="tabcontent">
<div id="picker" class="noslide"></div>
<div id="rgbwrap">
<div class="sliderwrap il">
<input id="sliderR" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,1)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
<div id="rwrap" class="il">
<div class="sliderwrap il">
<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 class="sliderwrap il">
<input id="sliderG" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,2)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
<div id="gwrap" class="il">
<div class="sliderwrap il">
<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 class="sliderwrap il">
<input id="sliderB" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,3)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
<div id="bwrap" class="il">
<div class="sliderwrap il">
<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>
<div id="wwrap">
@ -64,6 +74,7 @@
<div class="sliderwrap il">
<input id="sliderW" class="noslide" onchange="setColor(0)" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
<output class="sliderbubble"></output>
</div>
</div>
<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('#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-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 id="csl">
<button class="xxs cl btn" onclick="selectSlot(0);">1</button>
<button class="xxs cl btn" onclick="selectSlot(1);">2</button>
<button class="xxs cl btn" onclick="selectSlot(2);">3</button>
<button class="btn xxs" onclick="selectSlot(0);">1</button>
<button class="btn xxs" onclick="selectSlot(1);">2</button>
<button class="btn xxs" onclick="selectSlot(2);">3</button>
</div>
<div id="hexw">
<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>
<p class="labels">
<i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i>
Color palette
</p>
<p class="labels"><i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i> Color palette</p>
<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 class="lstI" data-id="0">
<label class="check schkl">
<label class="radio schkl">
&nbsp;
<input type="radio" value="${palettes[i].id}" name="palette" onChange="setPalette()">
<span class="checkmark schk"></span>
<span class="radiomark schk"></span>
</label>
<div class="lstIcontent">
<span class="lstIname">
@ -120,26 +133,33 @@
<div id="Effects" class="tabcontent">
<p class="labels">Effect speed</p>
<div class="staytop">
<div class="staytop" id="staytop">
<i class="icons slider-icon">&#xe325;</i>
<div class="sliderwrap il">
<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>
<output class="sliderbubble"></output>
</div>
<p class="labels">Effect intensity</p>
<div class="staytop" id="staytop1">
<i class="icons slider-icon" onclick="tglLabels()">&#xe409;</i>
<div class="sliderwrap il">
<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>
<output class="sliderbubble"></output>
</div>
<p class="labels">Effect mode</p>
<div id="fxlist" class="list">
Loading...
<div class="il">
<p class="labels">Effect mode</p>
<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>
@ -147,21 +167,18 @@
<div id="segcont">
Loading...
</div>
<div id="segutil">
<div id="segutil" class="staybot">
</div>
<div id="segutil2">
<button class="btn btn-s" id="rsbtn" onclick="rSegs()">Reset segments</button>
</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 id="Presets" class="tabcontent">
<div id="putil">
<div id="putil" class="staytop">
</div>
<div id="pql">
</div>
<div id="pcont">
Loading...
@ -183,19 +200,19 @@
<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="requestJson(null)">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 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 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>
<div id="rover" class="modal">

File diff suppressed because it is too large Load Diff

View File

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

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 GetV(){var d=document;}
</script>
<style>@import url("style.css");</style></head>
<style>@import url("/style.css");</style>
</head>
<body onload="S()">
<form id="form_s" name="Sf" method="post" onsubmit="GC()">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div>
<h2>Sync setup</h2>
<h3>WLED Broadcast</h3>
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>";
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>";
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>";
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>";
ih+="</tr>";
gId("TMT").innerHTML=ih;
}
function FC()
@ -84,25 +86,25 @@
td = tr.insertCell(0);
td.innerHTML = `Button ${i}:`;
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.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.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()
{
//values injected by server while sending HTML
}
</script>
<style>
@import url("style.css");
</style>
<style>@import url("/style.css");</style>
</head>
<body onload="S()">
<form id="form_s" name="Sf" method="post" onsubmit="Wd()">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div>
<h2>Time setup</h2>
Get time from NTP server: <input type="checkbox" name="NT"><br>
<input name="NS" maxlength="32"><br>
@ -131,8 +133,8 @@
</select><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>
Latitude (N): <input name="LT" type="number" min="-66.6" max="66.6" step="0.01">
Longitude (E): <input name="LN" type="number" min="-180" max="180" 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" class="xl" min="-180" max="180" step="0.01">
<div id="sun" class="times"></div>
<h3>Clock</h3>
Clock Overlay:
@ -155,8 +157,8 @@
</div>
Countdown Mode: <input type="checkbox" name="CE"><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>
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>
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" 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>
<b>Macros have moved!</b><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>
<script>
var d = document;
var initial_ds, initial_st;
var initial_ds, initial_st, initial_su;
var sett = null;
var l = {
"comp":{
@ -21,8 +21,8 @@
"pcmbot": "Show bottom tab bar in PC mode",
"pid": "Show preset IDs",
"seglen": "Set segment length instead of stop LED",
"css": "Enable custom CSS",
"hdays": "Enable custom Holidays list"
"css": "Enable custom CSS",
"hdays": "Enable custom Holidays list"
},
"theme":{
"alpha": {
@ -147,10 +147,18 @@
gId('lserr').innerHTML = "&#9888; Settings JSON saving failed. (" + e + ")";
}
}
function cLS()
{
localStorage.removeItem('wledP');
localStorage.removeItem('wledPmt');
localStorage.removeItem('wledPalx');
showToast("Cleared.");
}
function Save() {
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()
@ -158,6 +166,7 @@
GetV();
initial_ds = d.Sf.DS.value;
initial_st = d.Sf.ST.checked;
initial_su = d.Sf.SU.checked;
GetLS();
}
function H()
@ -208,7 +217,7 @@
</head>
<body onload="S()">
<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>
<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>
@ -217,6 +226,7 @@
<h2>Web Setup</h2>
Server description: <input name="DS" maxlength="32"><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>
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>
@ -236,11 +246,12 @@
<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>
<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>
<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>
<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>
</form>
</body>

View File

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

View File

@ -18,14 +18,14 @@
//values injected by server while sending HTML
}
</script>
<style>
@import url("style.css");
</style>
<style>@import url("/style.css");</style>
</head>
<body onload="GetV()">
<form id="form_s" name="Sf" method="post">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button><hr>
</div>
<h2>WiFi setup</h2>
<h3>Connect to existing network</h3>
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>
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>
AP opens:
<select name="AB">
<option value="0">No connection after boot</option>
<option value="1">Disconnected</option>
<option value="2">Always</option>
<option value="3">Never (not recommended)</option></select><br>
AP opens:
<select name="AB">
<option value="0">No connection after boot</option>
<option value="1">Disconnected</option>
<option value="2">Always</option>
<option value="3">Never (not recommended)</option></select><br>
AP IP: <span class="sip"> Not active </span><br>
<h3>Experimental</h3>
Disable WiFi sleep: <input type="checkbox" name="WS"><br>
<i>Can help with connectivity issues.<br>
Do not enable if WiFi is working correctly, increases power consumption.</i>
<div id="ethd">
Do not enable if WiFi is working correctly, increases power consumption.</i>
<div id="ethd">
<h3>Ethernet Type</h3>
<select name="ETH">
<option value="0">None</option>
<option value="2">ESP32-POE</option>
<option value="6">ESP32Deux</option>
<option value="4">QuinLED-ESP32</option>
<option value="5">TwilightLord-ESP32</option>
<option value="3">WESP32</option>
<option value="1">WT32-ETH01</option>
</select><br><br></div>
<option value="0">None</option>
<option value="2">ESP32-POE</option>
<option value="6">ESP32Deux</option>
<option value="4">QuinLED-ESP32</option>
<option value="5">TwilightLord-ESP32</option>
<option value="3">WESP32</option>
<option value="1">WT32-ETH01</option>
</select><br><br>
</div>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button>
</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;
font-family: Verdana, sans-serif;
border: 0.3ch solid #333;
border-radius: 24px;
display: inline-block;
font-size: 20px;
margin: 12px 8px 8px;
padding: 1px 6px;
padding: 8px 12px;
min-width: 48px;
cursor: pointer;
text-decoration: none;
}
.toprow {
top: 0;
position: sticky;
background-color:#222;
z-index:1;
}
.lnk {
border: 0;
}
@ -35,8 +43,12 @@ input {
font-family: Verdana, sans-serif;
border: 0.5ch solid #333;
}
input[type="text"] {
font-size: medium;
}
input[type="number"] {
width: 4em;
font-size: medium;
margin: 2px;
}
input[type="number"].xxl {
@ -57,8 +69,12 @@ input[type="number"].s {
input[type="number"].xs {
width: 42px;
}
select {
margin: 2px;
font-size: medium;
}
input[type="checkbox"] {
transform: scale(1.5);
transform: scale(2);
margin-right: 10px;
}
select {

View File

@ -15,9 +15,11 @@ void handleAlexa();
void onAlexaChange(EspalexaDevice* dev);
//blynk.cpp
#ifndef WLED_DISABLE_BLYNK
void initBlynk(const char* auth, const char* host, uint16_t port);
void handleBlynk();
void updateBlynk();
#endif
//button.cpp
void shortPressAction(uint8_t b=0);
@ -94,7 +96,7 @@ void sendHuePoll();
void onHueData(void* arg, AsyncClient* client, void *data, size_t len);
//ir.cpp
bool decodeIRCustom(uint32_t code);
//bool decodeIRCustom(uint32_t code);
void applyRepeatActions();
void relativeChange(byte* property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF);
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
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 handleNotifications();
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w);
@ -224,14 +227,11 @@ class UsermodManager {
public:
void loop();
void setup();
void connected();
void addToJsonState(JsonObject& obj);
void addToJsonInfo(JsonObject& obj);
void readFromJsonState(JsonObject& obj);
void addToConfig(JsonObject& obj);
bool readFromConfig(JsonObject& obj);
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(".html")) return "text/html";
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(".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(".ico")) return "image/x-icon";
// 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}
</style></head><body><h2>WLED Software Update</h2><form method="POST"
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
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

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;
}
int hueErrorCode = root[0][F("error")][F("type")];
int hueErrorCode = root[0][F("error")]["type"];
if (hueErrorCode)//hue bridge returned error
{
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
//IR codes themselves can be defined directly after "case" or in "ir_codes.h"
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
return true;
}
*/
void relativeChange(byte* property, int8_t amount, byte lowerBoundary, byte higherBoundary)
{
@ -162,9 +166,10 @@ void decodeIR(uint32_t code)
return;
}
lastValidCode = 0; irTimesRepeated = 0;
if (decodeIRCustom(code)) return;
//if (decodeIRCustom(code)) return;
if (irEnabled == 8) { // any remote configurable with ir.json file
decodeIRJson(code);
colorUpdated(CALL_MODE_BUTTON);
return;
}
if (code > 0xFFFFFF) return; //invalid code
@ -566,14 +571,17 @@ Sample:
void decodeIRJson(uint32_t code)
{
char objKey[10];
const char* cmd;
String cmdStr;
DynamicJsonDocument irDoc(JSON_BUFFER_SIZE);
JsonObject fdo;
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);
fdo = irDoc.as<JsonObject>();
lastValidCode = 0;
@ -583,8 +591,7 @@ void decodeIRJson(uint32_t code)
return;
}
cmd = fdo["cmd"]; //string
cmdStr = String(cmd);
cmdStr = fdo["cmd"].as<String>();;
jsonCmdObj = fdo["cmd"]; //object
if (!cmdStr.isEmpty())
@ -617,8 +624,8 @@ void decodeIRJson(uint32_t code)
if (!cmdStr.startsWith("win&")) {
cmdStr = "win&" + cmdStr;
}
handleSet(nullptr, cmdStr, false);
}
handleSet(nullptr, cmdStr, false);
}
} else if (!jsonCmdObj.isNull()) {
// command is JSON object
//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 )
{
Serial.print("IR recv\r\n0x");
Serial.println((uint32_t)results.value, HEX);
Serial.println();
DEBUG_PRINTF("IR recv: 0x%lX\n", (unsigned long)results.value);
}
decodeIR(results.value);
irrecv->resume();

View File

@ -17,11 +17,36 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t start = elem[F("start")] | seg.start;
int stop = elem["stop"] | -1;
if (stop < 0) {
uint16_t len = elem[F("len")];
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 spc = elem[F("spc")] | seg.spacing;
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};
bool colValid = false;
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
byte cp = copyArray(colX, rgbw, 4);
if (cp == 1 && rgbw[0] == 0)
seg.setColor(i, 0, id);
if (colarr[i].is<unsigned long>()) {
// unsigned long RGBW (@blazoncek v2 experimental API implementation)
uint32_t colX = colarr[i];
rgbw[0] = (colX >> 16) & 0xFF;
rgbw[1] = (colX >> 8) & 0xFF;
rgbw[2] = (colX ) & 0xFF;
rgbw[3] = (colX >> 24) & 0xFF;
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;
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
byte cp = copyArray(colX, rgbw, 4);
if (cp == 1 && rgbw[0] == 0)
seg.setColor(i, 0, id);
colValid = true;
}
if (!colValid) continue;
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);
}
uint16_t start = 0, stop = 0;
//uint16_t start = 0, stop = 0;
byte set = 0; //0 nothing set, 1 start set, 2 range set
for (uint16_t i = 0; i < iarr.size(); i++) {
if(iarr[i].is<JsonInteger>()) {
if (!set) {
start = iarr[i];
//start = iarr[i];
set = 1;
} else {
stop = iarr[i];
//stop = iarr[i];
set = 2;
}
} 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];
}
}
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);
@ -180,6 +208,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
bool deserializeState(JsonObject root, byte callMode, byte presetId)
{
DEBUG_PRINTLN(F("Deserializing state"));
strip.applyToAllSelected = false;
bool stateResponse = root[F("v")] | false;
@ -200,14 +230,6 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
transitionDelayTemp = transitionDelay;
}
}
tr = root[F("tt")] | -1;
if (tr >= 0)
{
transitionDelayTemp = tr;
transitionDelayTemp *= 100;
jsonTransitionOnce = true;
}
strip.setTransition(transitionDelayTemp);
tr = root[F("tb")] | -1;
@ -287,16 +309,24 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
usermods.readFromJsonState(root);
int8_t ledmap = root[F("ledmap")] | -1;
if (ledmap >= 0) {
strip.deserializeMap(ledmap);
}
int ps = root[F("psave")] | -1;
if (ps > 0) {
DEBUG_PRINTLN(F("Saving preset"));
savePreset(ps, true, nullptr, root);
} else {
ps = root[F("pdel")] | -1; //deletion
if (ps > 0) {
DEBUG_PRINTLN(F("Deleting preset"));
deletePreset(ps);
}
ps = root["ps"] | -1; //load preset (clears state request!)
if (ps >= 0) {
DEBUG_PRINTLN(F("Applying preset"));
if (!presetId) unloadPlaylist(); //stop playlist if preset changed manually
applyPreset(ps, callMode);
return stateResponse;
@ -339,12 +369,15 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
byte segbri = seg.opacity;
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;
if (id == strip.getMainSegmentId() && i < 2) //temporary, to make transition work on main segment
{
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[2] = (byte)(seg.colors[i]); segcol[3] = (byte)(seg.colors[i] >> 24);
}
char tmpcol[22];
if (strip.isRgbw) sprintf_P(tmpcol, PSTR("[%u,%u,%u,%u]"), c[0], c[1], c[2], c[3]);
else sprintf_P(tmpcol, PSTR("[%u,%u,%u]"), c[0], c[1], c[2]);
strcat(colstr, i<2 ? strcat(tmpcol,",") : tmpcol);
}
strcat(colstr,"]");
sprintf_P(tmpcol, format, (unsigned)c[0], (unsigned)c[1], (unsigned)c[2], (unsigned)c[3]);
strcat(colstr, i<2 ? strcat_P(tmpcol, PSTR(",")) : tmpcol);
}
strcat_P(colstr, PSTR("]"));
root["col"] = serialized(colstr);
root["fx"] = seg.mode;
@ -377,10 +407,11 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root["on"] = (bri > 0);
root["bri"] = briLast;
root[F("transition")] = transitionDelay/100; //in 100ms
root[F("tdd")] = transitionDelayDefault/100; //in 100ms
}
if (!forPreset) {
if (errorFlag) root[F("error")] = errorFlag;
if (errorFlag) {root[F("error")] = errorFlag; errorFlag = ERR_NONE;}
root[F("ps")] = currentPreset;
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++)
{
WS2812FX::Segment sg = strip.getSegment(s);
// TODO: add logic to stop at 16 segments if using versionAPI==1 on ESP8266
if (sg.isActive())
{
JsonObject seg0 = seg.createNestedObject();
@ -452,6 +484,15 @@ void serializeInfo(JsonObject root)
leds[F("count")] = ledCount;
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?
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("fps")] = strip.getFps();
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
@ -530,9 +571,11 @@ void serializeInfo(JsonObject root)
#endif
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;
usermods.addToJsonInfo(root);
byte os = 0;
@ -655,37 +698,37 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
curPalette.add("r");
break;
case 2: //primary color only
curPalette.add(F("c1"));
curPalette.add("c1");
break;
case 3: //primary + secondary
curPalette.add(F("c1"));
curPalette.add(F("c1"));
curPalette.add(F("c2"));
curPalette.add(F("c2"));
curPalette.add("c1");
curPalette.add("c1");
curPalette.add("c2");
curPalette.add("c2");
break;
case 4: //primary + secondary + tertiary
curPalette.add(F("c3"));
curPalette.add(F("c2"));
curPalette.add(F("c1"));
curPalette.add("c3");
curPalette.add("c2");
curPalette.add("c1");
break;
case 5: {//primary + secondary (+tert if not off), more distinct
curPalette.add(F("c1"));
curPalette.add(F("c1"));
curPalette.add(F("c1"));
curPalette.add(F("c1"));
curPalette.add(F("c1"));
curPalette.add(F("c2"));
curPalette.add(F("c2"));
curPalette.add(F("c2"));
curPalette.add(F("c2"));
curPalette.add(F("c2"));
curPalette.add(F("c3"));
curPalette.add(F("c3"));
curPalette.add(F("c3"));
curPalette.add(F("c3"));
curPalette.add(F("c3"));
curPalette.add(F("c1"));
curPalette.add("c1");
curPalette.add("c1");
curPalette.add("c1");
curPalette.add("c1");
curPalette.add("c1");
curPalette.add("c2");
curPalette.add("c2");
curPalette.add("c2");
curPalette.add("c2");
curPalette.add("c2");
curPalette.add("c3");
curPalette.add("c3");
curPalette.add("c3");
curPalette.add("c3");
curPalette.add("c3");
curPalette.add("c1");
break;}
case 6: //Party colors
setPaletteColors(curPalette, PartyColors_p);
@ -793,8 +836,7 @@ void serveJson(AsyncWebServerRequest* request)
}
}
DEBUG_PRINT("JSON buffer size: ");
DEBUG_PRINTLN(doc.memoryUsage());
DEBUG_PRINTF("JSON buffer size: %u for request: %d\n", doc.memoryUsage(), subJson);
response->setLength();
request->send(response);
@ -804,13 +846,13 @@ void serveJson(AsyncWebServerRequest* request)
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
{
#ifdef WLED_ENABLE_WEBSOCKETS
AsyncWebSocketClient * wsc = nullptr;
if (!request) { //not HTTP, use Websockets
#ifdef WLED_ENABLE_WEBSOCKETS
wsc = ws.client(wsClient);
if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free
#endif
}
#endif
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

View File

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

View File

@ -124,22 +124,22 @@ void publishMqtt()
sprintf_P(s, PSTR("%u"), bri);
strlcpy(subuf, mqttDeviceTopic, 33);
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]));
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/c"));
mqtt->publish(subuf, 0, true, s);
mqtt->publish(subuf, 0, true, s); // retain message
strlcpy(subuf, mqttDeviceTopic, 33);
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);
strlcpy(subuf, mqttDeviceTopic, 33);
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);
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->connect();
return true;

View File

@ -1,6 +1,17 @@
#include "src/dependencies/timezone/Timezone.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

View File

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

View File

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

View File

@ -90,7 +90,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
}
strip.isRgbw = false;
uint8_t colorOrder, type, skip;
uint16_t length, start;
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 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 ew[4] = "EW"; ew[2] = 48+s; ew[3] = 0; //strip RGBW override
if (!request->hasArg(lp)) {
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;
}
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;
colorOrder = request->arg(co).toInt();
start = (request->hasArg(ls)) ? request->arg(ls).toInt() : t;
if (request->hasArg(lc) && request->arg(lc).toInt() > 0) {
length = request->arg(lc).toInt();
t += length = request->arg(lc).toInt();
} else {
break; // no parameter
}
colorOrder = request->arg(co).toInt();
start = (request->hasArg(ls)) ? request->arg(ls).toInt() : 0;
// actual finalization is done in WLED::loop() (removing old busses and adding new)
if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder, request->hasArg(cv), skip);
doInitBusses = true;
}
t = request->arg(F("LC")).toInt();
if (t > 0 && t <= MAX_LEDS) ledCount = t;
ledCount = request->arg(F("LC")).toInt();
// upate other pins
int hw_ir_pin = request->arg(F("IR")).toInt();
@ -181,7 +180,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
fadeTransition = request->hasArg(F("TF"));
t = request->arg(F("TD")).toInt();
if (t > 0) transitionDelay = t;
if (t >= 0) transitionDelay = t;
transitionDelayDefault = t;
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);
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
@ -595,9 +601,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
uint16_t startI = mainseg.start;
uint16_t stopI = mainseg.stop;
uint8_t grpI = mainseg.grouping;
uint16_t spcI = mainseg.spacing;
uint16_t stopI = mainseg.stop;
uint8_t grpI = mainseg.grouping;
uint16_t spcI = mainseg.spacing;
pos = req.indexOf(F("&S=")); //segment start
if (pos > 0) {
startI = getNumVal(&req, pos);
@ -617,6 +623,30 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
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
if (pos > 0) savePreset(getNumVal(&req, pos));
@ -707,7 +737,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
strip.applyToAllSelected = true;
strip.setColor(2, t[0], t[1], t[2], t[3]);
} 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="));
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)
pos = req.indexOf(F("ST="));
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)
{
if (!realtimeMode && !realtimeOverride){
@ -102,6 +108,10 @@ void realtimeLock(uint32_t timeoutMs, byte md)
realtimeTimeout = millis() + timeoutMs;
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;
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
// 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;

View File

@ -29,7 +29,7 @@ void sendSysInfoUDP();
// buffer - a buffer of at least length*3 or length*4 bytes long
// length - the number of pixels
// 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

View File

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

View File

@ -8,6 +8,90 @@
#include "soc/rtc_cntl_reg.h"
#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
*/
@ -120,6 +204,10 @@ void WiFiEvent(WiFiEvent_t event)
void WLED::loop()
{
#ifdef WLED_DEBUG
static unsigned long maxUsermodMillis = 0;
#endif
handleTime();
handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too
handleConnection();
@ -130,7 +218,15 @@ void WLED::loop()
handleDMX();
#endif
userLoop();
#ifdef WLED_DEBUG
unsigned long usermodMillis = millis();
#endif
usermods.loop();
#ifdef WLED_DEBUG
usermodMillis = millis() - usermodMillis;
if (usermodMillis > maxUsermodMillis) maxUsermodMillis = usermodMillis;
#endif
yield();
handleIO();
@ -159,7 +255,9 @@ void WLED::loop()
yield();
handleHue();
#ifndef WLED_DISABLE_BLYNK
handleBlynk();
#endif
yield();
@ -197,10 +295,12 @@ void WLED::loop()
DEBUG_PRINTLN(F("Re-init busses."));
busses.removeAll();
uint32_t mem = 0;
strip.isRgbw = false;
for (uint8_t i = 0; i < WLED_MAX_BUSSES; i++) {
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)) {
mem += busses.memUsage(*busConfigs[i]);
if (mem <= MAX_LED_MEMORY) {
@ -211,20 +311,20 @@ void WLED::loop()
strip.isOffRefreshRequred |= BusManager::isOffRefreshRequred(busConfigs[i]->type);
}
}
*/
delete busConfigs[i]; busConfigs[i] = nullptr;
}
strip.finalizeInit(ledCount);
strip.finalizeInit();
yield();
serializeConfig();
}
yield();
handleWs();
handleStatusLED();
// DEBUG serial logging
#ifdef WLED_DEBUG
if (millis() - debugTime > 9999) {
if (millis() - debugTime > 29999) {
DEBUG_PRINTLN(F("---DEBUG INFO---"));
DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis());
DEBUG_PRINT(F("Unix time: ")); toki.printTime(toki.getTime());
@ -236,17 +336,19 @@ void WLED::loop()
} else
DEBUG_PRINTLN(F("No PSRAM"));
#endif
DEBUG_PRINT(F("Wifi state: ")); DEBUG_PRINTLN(WiFi.status());
DEBUG_PRINT(F("Wifi state: ")); DEBUG_PRINTLN(WiFi.status());
if (WiFi.status() != lastWifiState) {
wifiStateChangedTime = millis();
}
lastWifiState = WiFi.status();
DEBUG_PRINT(F("State time: ")); DEBUG_PRINTLN(wifiStateChangedTime);
DEBUG_PRINT(F("NTP last sync: ")); DEBUG_PRINTLN(ntpLastSyncTime);
DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP());
DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 10);
DEBUG_PRINT(F("State time: ")); DEBUG_PRINTLN(wifiStateChangedTime);
DEBUG_PRINT(F("NTP last sync: ")); DEBUG_PRINTLN(ntpLastSyncTime);
DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP());
DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 60);
DEBUG_PRINT(F("Max UM time[ms]: ")); DEBUG_PRINTLN(maxUsermodMillis);
loops = 0;
maxUsermodMillis = 0;
debugTime = millis();
}
loops++;
@ -315,14 +417,6 @@ void WLED::setup()
DEBUG_PRINTLN(F("Reading config"));
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"));
beginStrip();
@ -336,9 +430,12 @@ void WLED::setup()
WiFi.onEvent(WiFiEvent);
#endif
#ifdef WLED_ENABLE_ADALIGHT
#ifdef WLED_ENABLE_ADALIGHT // reserve GPIO3 (RX) pin for ADALight
if (!pinManager.isPinAllocated(3)) {
Serial.println(F("Ada"));
pinManager.allocatePin(3,false);
} else {
DEBUG_PRINTLN(F("ADALight disabled due to GPIO3 being used."));
}
#endif
@ -388,22 +485,20 @@ void WLED::setup()
void WLED::beginStrip()
{
// Initialize NeoPixel Strip and button
if (ledCount > MAX_LEDS || ledCount == 0)
ledCount = 30;
strip.finalizeInit(ledCount);
strip.finalizeInit(); // busses created during deserializeConfig()
strip.populateDefaultSegments();
strip.setBrightness(0);
strip.setShowCallback(handleOverlayDraw);
if (bootPreset > 0) {
applyPreset(bootPreset, CALL_MODE_INIT);
} else if (turnOnAtBoot) {
if (turnOnAtBoot) {
if (briS > 0) bri = briS;
else if (bri == 0) bri = 128;
} else {
briLast = briS; bri = 0;
}
if (bootPreset > 0) {
applyPreset(bootPreset, CALL_MODE_INIT);
}
colorUpdated(CALL_MODE_INIT);
// 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
@title WLED project sketch
@version 0.13.0-b2
@version 0.13.0-bl2
@author Christian Schwinne
*/
// 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
//#define WLED_USE_MY_CONFIG
@ -31,9 +31,11 @@
#ifndef WLED_DISABLE_MQTT
#define WLED_ENABLE_MQTT // saves 12kb
#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_LOXONE // uses 1.2kb
#ifndef WLED_DISABLE_LOXONE
#define WLED_ENABLE_LOXONE // uses 1.2kb
#endif
#ifndef WLED_DISABLE_WEBSOCKETS
#define WLED_ENABLE_WEBSOCKETS
#endif
@ -136,6 +138,9 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#include "fcn_declare.h"
#include "html_ui.h"
#ifndef WLED_DISABLE_SIMPLE_UI
#include "html_simple.h"
#endif
#include "html_settings.h"
#include "html_other.h"
#include "FX.h"
@ -225,11 +230,7 @@ WLED_GLOBAL bool rlyMde _INIT(true);
WLED_GLOBAL bool rlyMde _INIT(RLYMDE);
#endif
#ifndef IRPIN
#ifdef WLED_DISABLE_INFRARED
WLED_GLOBAL int8_t irPin _INIT(-1);
#else
WLED_GLOBAL int8_t irPin _INIT(4);
#endif
WLED_GLOBAL int8_t irPin _INIT(-1);
#else
WLED_GLOBAL int8_t irPin _INIT(IRPIN);
#endif
@ -264,9 +265,9 @@ WLED_GLOBAL bool noWifiSleep _INIT(false);
#endif
// LED CONFIG
WLED_GLOBAL uint16_t ledCount _INIT(DEFAULT_LED_COUNT); // overcurrent prevented by ABL
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 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 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 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
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 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
WLED_GLOBAL NodesMap Nodes;
@ -566,7 +569,7 @@ WLED_GLOBAL JsonDocument* fileDoc;
WLED_GLOBAL bool doCloseFile _INIT(false);
// presets
WLED_GLOBAL int16_t currentPreset _INIT(-1);
WLED_GLOBAL int8_t currentPreset _INIT(-1);
WLED_GLOBAL byte errorFlag _INIT(0);
@ -599,7 +602,6 @@ WLED_GLOBAL bool doInitBusses _INIT(false);
// Usermod manager
WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
// enable additional debug output
#ifdef WLED_DEBUG
#ifndef ESP8266
@ -629,7 +631,7 @@ WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
WLED_GLOBAL unsigned long debugTime _INIT(0);
WLED_GLOBAL int lastWifiState _INIT(3);
WLED_GLOBAL unsigned long wifiStateChangedTime _INIT(0);
WLED_GLOBAL int loops _INIT(0);
WLED_GLOBAL unsigned long loops _INIT(0);
#endif
#ifdef ARDUINO_ARCH_ESP32
@ -666,6 +668,5 @@ public:
void initAP(bool resetAP = false);
void initConnection();
void initInterfaces();
void handleStatusLED();
};
#endif // WLED_H

View File

@ -1,20 +1,9 @@
#ifndef WLED_ETHERNET_H
#define WLED_ETHERNET_H
#ifdef WLED_USE_ETHERNET
#include "pin_manager.h"
// 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[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
};
#ifdef WLED_USE_ETHERNET
// 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
@ -37,74 +26,10 @@ typedef struct EthernetSettings {
eth_clock_mode_t eth_clk_mode;
} ethernet_settings;
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
},
extern const ethernet_settings ethernetBoards[];
// 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
}
};
#define WLED_ETH_RSVD_PINS_COUNT 6
extern const managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT];
#endif
#endif

View File

@ -80,6 +80,43 @@ float asin_t(float x) {
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
#define A 0.0776509570923569
#define B -0.287434475393028
@ -135,4 +172,4 @@ float fmod_t(float num, float denom) {
return res;
}
#endif
#endif

View File

@ -114,7 +114,7 @@ void handleSerial()
if (!realtimeOverride) setRealtimePixel(pixel++, red, green, blue, 0);
if (--count > 0) state = AdaState::Data_Red;
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);
if (!realtimeOverride) strip.show();

View File

@ -4,6 +4,9 @@
* Integrated HTTP web server page declarations
*/
bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request);
void setStaticContentCacheHeaders(AsyncWebServerResponse *response);
//Is this an IP?
bool isIp(String str) {
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){
if(!index){
if (!index) {
request->_tempFile = WLED_FS.open(filename, "w");
DEBUG_PRINT("Uploading ");
DEBUG_PRINTLN(filename);
@ -25,9 +28,10 @@ void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t
if (len) {
request->_tempFile.write(data,len);
}
if(final){
if (final) {
request->_tempFile.close();
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);}
);
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 (!otaLock){
#ifdef WLED_ENABLE_FS_EDITOR
@ -287,6 +300,8 @@ bool handleIfNoneMatchCacheHeader(AsyncWebServerRequest* request)
void setStaticContentCacheHeaders(AsyncWebServerResponse *response)
{
char tmp[12];
// https://medium.com/@codebyamir/a-web-developers-guide-to-browser-caching-cc41f3b73e7c
#ifndef WLED_DEBUG
//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
@ -294,7 +309,8 @@ void setStaticContentCacheHeaders(AsyncWebServerResponse *response)
#else
response->addHeader(F("Cache-Control"),"no-store,max-age=0"); // prevent caching if debug build
#endif
response->addHeader(F("ETag"), String(VERSION));
sprintf_P(tmp, PSTR("%8d-%02x"), VERSION, cacheInvalidate);
response->addHeader(F("ETag"), tmp);
}
void serveIndex(AsyncWebServerRequest* request)
@ -303,7 +319,13 @@ void serveIndex(AsyncWebServerRequest* request)
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");
setStaticContentCacheHeaders(response);

View File

@ -16,9 +16,11 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
if(type == WS_EVT_CONNECT){
//client connected
sendDataWs(client);
DEBUG_PRINTLN(F("WS client connected."));
} else if(type == WS_EVT_DISCONNECT){
//client disconnected
if (client->id() == wsLiveClientId) wsLiveClientId = 0;
DEBUG_PRINTLN(F("WS client disconnected."));
} else if(type == WS_EVT_DATA){
//data packet
AwsFrameInfo * info = (AwsFrameInfo*)arg;
@ -39,6 +41,11 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
JsonObject root = jsonBuffer.as<JsonObject>();
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 the received value is just "{"v":true}", send only to this client
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){
//error was received from the other end
DEBUG_PRINTLN(F("WS error."));
} else if(type == WS_EVT_PONG){
//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);
buffer = ws.makeBuffer(len);
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);
}
DEBUG_PRINT(F("Sending WS data "));
if (client) {
client->text(buffer);
DEBUG_PRINTLN(F("to a single client."));
} else {
ws.textAll(buffer);
DEBUG_PRINTLN(F("to multiple clients."));
}
}

View File

@ -1,4 +1,5 @@
#include "wled.h"
#include "wled_ethernet.h"
/*
* Sending XML status files to client
@ -158,24 +159,16 @@ void sappends(char stype, const char* key, char* val)
{
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(key);
oappend(".value=\"");
//convert "%" to "%%" to make EspAsyncWebServer happy
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(buf.c_str());
oappend("\";");
break; }
break;}
case 'm': //message
oappend(SET_F("d.getElementsByClassName"));
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
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) {
if (subPage == 1)
{
sappends('s',SET_F("CS"),clientSSID);
byte l = strlen(clientPass);
@ -264,50 +304,72 @@ void getSettingsJS(byte subPage, char* dest)
}
}
if (subPage == 2) {
if (subPage == 2)
{
char nS[8];
// 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);
JsonObject mods = doc.createNestedObject(F("um"));
usermods.addToConfig(mods);
oappend(SET_F("d.um_p=["));
if (!mods.isNull()) {
uint8_t i=0;
for (JsonPair kv : mods) {
if (!kv.value().isNull()) {
// element is an JsonObject
JsonObject obj = kv.value();
if (obj["pin"] != nullptr) {
if (obj["pin"].is<JsonArray>()) {
JsonArray pins = obj["pin"].as<JsonArray>();
for (JsonVariant pv : pins) {
if (i++) oappend(SET_F(","));
oappendi(pv.as<int>());
}
} else {
if (i++) oappend(SET_F(","));
oappendi(obj["pin"].as<int>());
}
if (!mods.isNull()) fillUMPins(mods);
#ifdef WLED_ENABLE_DMX
oappend(SET_F(",2")); // DMX hardcoded pin
#endif
#ifdef WLED_ENABLE_ADALIGHT
// inform settings page that pin 3 is used by ADALights if not aleready used by strip (previous setup)
// NOTE: this will prohibit pin 3 use on new installs
{
bool pin3used = false;
for (uint8_t s=0; s < busses.getNumBusses(); s++) {
Bus* bus = busses.getBus(s);
uint8_t pins[5];
uint8_t nPins = bus->getPins(pins);
for (uint8_t i = 0; i < nPins; i++) {
if (pins[i] == 3) {
pin3used = true;
break;
}
}
if (pin3used) break;
}
if (i) oappend(SET_F(","));
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
if (!pin3used && pinManager.isPinAllocated(3)) oappend(SET_F(",3")); // ADALight (RX) pin
}
#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("];"));
// set limits
@ -317,10 +379,6 @@ void getSettingsJS(byte subPage, char* dest)
oappend(itoa(MAX_LED_MEMORY,nS,10));
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('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 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 ew[4] = "EW"; ew[2] = 48+s; ew[3] = 0; //strip RGBW override
oappend(SET_F("addLEDs(1);"));
uint8_t pins[5];
uint8_t nPins = bus->getPins(pins);
for (uint8_t i = 0; i < nPins; 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',co,bus->getColorOrder());
sappend('v',ls,bus->getStart());
sappend('c',cv,bus->reversed);
sappend('c',sl,bus->skippedLeds());
// sappend('c',ew,bus->isRgbw());
}
sappend('v',SET_F("MA"),strip.ablMilliampsMax);
sappend('v',SET_F("LA"),strip.milliampsPerLed);
@ -391,6 +451,7 @@ void getSettingsJS(byte subPage, char* dest)
{
sappends('s',SET_F("DS"),serverDescription);
sappend('c',SET_F("ST"),syncToggleReceive);
sappend('c',SET_F("SU"),simplifiedUI);
}
if (subPage == 4)