Merge pull request #1658 from Aircoookie/mergedev-210115
Update multistrip dev branch
35
CHANGELOG.md
@ -2,16 +2,41 @@
|
||||
|
||||
### Development versions after 0.11.1 release
|
||||
|
||||
#### Build 2101130
|
||||
|
||||
- Added color transitions for all segments and slots and for segment brightness
|
||||
- Fixed bug that prevented setting a boot preset higher than 25
|
||||
|
||||
#### Build 2101040
|
||||
|
||||
- Replaced Red & Blue effect with Aurora effect (PR #1589)
|
||||
- Fixed HTTP changing segments uncommanded (#1618)
|
||||
- Updated copyright year and contributor page link
|
||||
|
||||
#### Build 2012311
|
||||
|
||||
- Fixed Countdown mode
|
||||
|
||||
#### Build 2012310
|
||||
|
||||
- (Hopefully actually) fixed display of usermod values in info screen
|
||||
|
||||
#### Build 2012240
|
||||
|
||||
- Fixed display of usermod values in info screen
|
||||
- 4 more effects now use FRAMETIME
|
||||
- Remove unsupported environments from platformio.ini
|
||||
|
||||
#### Build 2012210
|
||||
|
||||
- Split index.htm in separate CSS + JS files (PR #1542)
|
||||
- Minify UI HTML, saving >1.5kB flash
|
||||
- Fixed JShint warnings
|
||||
- Split index.htm in separate CSS + JS files (PR #1542)
|
||||
- Minify UI HTML, saving >1.5kB flash
|
||||
- Fixed JShint warnings
|
||||
|
||||
#### Build 2012180
|
||||
|
||||
- Boot brightness 0 will now use the brightness from preset
|
||||
- Add iOS scrolling momentum (from PR #1528)
|
||||
- Boot brightness 0 will now use the brightness from preset
|
||||
- Add iOS scrolling momentum (from PR #1528)
|
||||
|
||||
### WLED release 0.11.1
|
||||
|
||||
|
2889
package-lock.json
generated
@ -16,8 +16,6 @@ default_envs = travis_esp8266, travis_esp32
|
||||
|
||||
# Single binaries (uncomment your board)
|
||||
; default_envs = nodemcuv2
|
||||
; default_envs = esp01
|
||||
; default_envs = esp01_1m_ota
|
||||
; default_envs = esp01_1m_full
|
||||
; default_envs = esp07
|
||||
; default_envs = d1_mini
|
||||
@ -120,6 +118,7 @@ build_flags =
|
||||
-D DECODE_SAMSUNG=true
|
||||
-D DECODE_LG=true
|
||||
-DWLED_USE_MY_CONFIG
|
||||
; -D USERMOD_SENSORSTOMQTT
|
||||
|
||||
build_unflags =
|
||||
-Wall
|
||||
@ -139,8 +138,6 @@ build_flags_all_features =
|
||||
build_flags_esp8266 = ${common.build_flags} ${esp8266.build_flags}
|
||||
build_flags_esp32 = ${common.build_flags} ${esp32.build_flags}
|
||||
|
||||
ldscript_512k = eagle.flash.512k.ld ;for older versions change this to eagle.flash.512k0.ld
|
||||
ldscript_1m0m = eagle.flash.1m.ld ;for older versions change this to eagle.flash.1m0.ld
|
||||
ldscript_1m128k = eagle.flash.1m128.ld
|
||||
ldscript_2m512k = eagle.flash.2m512.ld
|
||||
ldscript_2m1m = eagle.flash.2m1m.ld
|
||||
@ -210,6 +207,10 @@ lib_deps =
|
||||
#milesburton/DallasTemperature@^3.9.0
|
||||
#For BME280 sensor uncomment following
|
||||
#BME280@~3.0.0
|
||||
; adafruit/Adafruit BMP280 Library @ 2.1.0
|
||||
; adafruit/Adafruit CCS811 Library @ 1.0.4
|
||||
; adafruit/Adafruit Si7021 Library @ 1.4.0
|
||||
|
||||
lib_ignore =
|
||||
AsyncTCP
|
||||
|
||||
@ -227,26 +228,6 @@ board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
|
||||
# Unsupported environment due to insufficient flash
|
||||
[env:esp01]
|
||||
board = esp01
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_512k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK
|
||||
-D WLED_DISABLE_CRONIXIE -D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED -D WLED_DISABLE_MQTT -D WLED_DISABLE_WEBSOCKETS
|
||||
|
||||
# Unsupported environment due to insufficient flash
|
||||
[env:esp01_1m_ota]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_wled_default}
|
||||
platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m0m}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABLE_BLYNK -D WLED_DISABLE_CRONIXIE
|
||||
-D WLED_DISABLE_HUESYNC -D WLED_DISABLE_INFRARED -D WLED_DISABLE_MQTT -D WLED_DISABLE_WEBSOCKETS
|
||||
|
||||
[env:esp01_1m_full]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_wled_default}
|
||||
@ -303,7 +284,7 @@ board = esp32-poe
|
||||
platform = espressif32@2.0
|
||||
upload_speed = 921600
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D RLYPIN=-1 -D WLED_USE_ETHERNET
|
||||
build_flags = ${common.build_flags_esp32} -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
|
||||
lib_ignore =
|
||||
ESPAsyncTCP
|
||||
ESPAsyncUDP
|
||||
|
@ -3,14 +3,24 @@ This usermod allows use of the TTGO T-Display ESP32 module with integrated 240x1
|
||||
for controlling WLED and showing the following information:
|
||||
* Current SSID
|
||||
* IP address if obtained
|
||||
* in AP mode and turned off lightning AP password is shown
|
||||
* If connected to a network, current brightness % is shown
|
||||
* in AP mode AP IP and password are shown
|
||||
* Current effect
|
||||
* Current palette
|
||||
* Estimated current in mA is shown (NOTE: for this to be a reasonable value, the correct LED type must be specified in the LED Prefs section)
|
||||
|
||||
Button pin is mapped to the onboard button next to the side actuated reset button of the TTGO T-Display board.
|
||||
|
||||
I have designed a 3D printed case around this board and an ["ElectroCookie"](https://amzn.to/2WCNeeA) project board, a [level shifter](https://amzn.to/3hbKu18), a [buck regulator](https://amzn.to/3mLMy0W), and a DC [power jack](https://amzn.to/3phj9NZ). I use 12V WS2815 LED strips for my projects, and power them with 12V power supplies, so the regulator drops the voltage to the 5V level I need to power the ESP module and the level shifter. If there is any interest in this case, which elevates the board and display on some custom extended headers to make place the screen at the top of the enclosure (with accessible buttons), let me know, and I could post the STL files. It is a bit tricky to get the height correct, so I also designed a one-time use 3D printed solder fixture to set the board in the right location and at the correct height for the housing. (It is one-time use because it has to be cut off after soldering to be able to remove it). I didn't think the effort to make it in multiple pieces was worthwhile.
|
||||
|
||||
Usermod based on a rework of the ssd1306_i2c_oled_u8g2 usermod from the WLED repo.
|
||||
|
||||
## Hardware
|
||||
![Hardware](assets/ttgo_hardware1.png)
|
||||
![Hardware](assets/ttgo-tdisplay-enclosure1a.png)
|
||||
![Hardware](assets/ttgo-tdisplay-enclosure2a.png)
|
||||
![Hardware](assets/ttgo-tdisplay-enclosure3a.png)
|
||||
![Hardware](assets/ttgo-tdisplay-enclosure3a.png)
|
||||
|
||||
## Github reference for TTGO-Tdisplay
|
||||
|
||||
@ -20,7 +30,11 @@ Usermod based on a rework of the ssd1306_i2c_oled_u8g2 usermod from the WLED rep
|
||||
Functionality checked with:
|
||||
* TTGO T-Display
|
||||
* PlatformIO
|
||||
* Group of 4 individual Neopixels from Adafruit, and a full string of 68 LEDs.
|
||||
* Group of 4 individual Neopixels from Adafruit, and a several full strings of 12v WS2815 LEDs.
|
||||
* The hardware design shown above should be limited to shorter strings. For larger strings, I use a different setup with a dedicated 12v power supply and power them directly off the supply (in addition to dropping the 12v supply down to 5v with a buck regulator for the ESP module and level shifter).
|
||||
|
||||
## Setup Needed:
|
||||
* As with all usermods, copy the usermod.cpp file from the TTGO-T-Display usermod folder to the wled00 folder (replacing the default usermod.cpp file).
|
||||
|
||||
## Platformio Requirements
|
||||
### Platformio.ini changes
|
||||
|
BIN
usermods/TTGO-T-Display/assets/ttgo-tdisplay-enclosure1a.png
Normal file
After Width: | Height: | Size: 708 KiB |
BIN
usermods/TTGO-T-Display/assets/ttgo-tdisplay-enclosure2a.png
Normal file
After Width: | Height: | Size: 848 KiB |
BIN
usermods/TTGO-T-Display/assets/ttgo-tdisplay-enclosure3a.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
usermods/TTGO-T-Display/assets/ttgo-tdisplay-enclosure4a.png
Normal file
After Width: | Height: | Size: 876 KiB |
@ -56,7 +56,7 @@ void userSetup() {
|
||||
tft.setTextColor(TFT_WHITE);
|
||||
tft.setCursor(1, 10);
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.setTextSize(2);
|
||||
tft.setTextSize(3);
|
||||
tft.print("Loading...");
|
||||
|
||||
if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h
|
||||
@ -142,22 +142,41 @@ void userLoop() {
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setTextSize(2);
|
||||
// First row with Wifi name
|
||||
tft.setCursor(1, 10);
|
||||
tft.setCursor(1, 1);
|
||||
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 IP or Psssword
|
||||
tft.setCursor(1, 40);
|
||||
// Print password in AP mode and if led is OFF.
|
||||
if (apActive && bri == 0)
|
||||
tft.print(apPass);
|
||||
else
|
||||
// Second row with AP IP and Password or IP
|
||||
tft.setTextSize(2);
|
||||
tft.setCursor(1, 24);
|
||||
// Print AP IP and password in AP mode or knownIP if AP not active.
|
||||
// if (apActive && bri == 0)
|
||||
// tft.print(apPass);
|
||||
// else
|
||||
// tft.print(knownIp);
|
||||
|
||||
if (apActive) {
|
||||
tft.print("AP IP: ");
|
||||
tft.print(knownIp);
|
||||
tft.setCursor(1,46);
|
||||
tft.print("AP Pass:");
|
||||
tft.print(apPass);
|
||||
}
|
||||
else {
|
||||
tft.print("IP: ");
|
||||
tft.print(knownIp);
|
||||
tft.setCursor(1,46);
|
||||
//tft.print("Signal Strength: ");
|
||||
//tft.print(i.wifi.signal);
|
||||
tft.print("Brightness: ");
|
||||
tft.print(((float(bri)/255)*100));
|
||||
tft.print("%");
|
||||
}
|
||||
|
||||
// Third row with mode name
|
||||
tft.setCursor(1, 70);
|
||||
tft.setCursor(1, 68);
|
||||
uint8_t qComma = 0;
|
||||
bool insideQuotes = false;
|
||||
uint8_t printedChars = 0;
|
||||
@ -184,7 +203,7 @@ void userLoop() {
|
||||
break;
|
||||
}
|
||||
// Fourth row with palette name
|
||||
tft.setCursor(1, 100);
|
||||
tft.setCursor(1, 90);
|
||||
qComma = 0;
|
||||
insideQuotes = false;
|
||||
printedChars = 0;
|
||||
@ -210,5 +229,10 @@ void userLoop() {
|
||||
if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1))
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// Fifth row with estimated mA usage
|
||||
tft.setCursor(1, 112);
|
||||
// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate).
|
||||
tft.print(strip.currentMilliamps);
|
||||
tft.print("mA (estimated)");
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
# 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"/>
|
||||
|
After Width: | Height: | Size: 296 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 321 KiB |
@ -0,0 +1,16 @@
|
||||
; 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
|
87
usermods/sensors_to_mqtt/readme.md
Normal file
@ -0,0 +1,87 @@
|
||||
# Sensors To Home Assistant (or mqtt)
|
||||
|
||||
This usermod will publish values of the BMP280, CCS811 and Si7021 sensors to Home Assistant via MQTT.
|
||||
|
||||
Its using home assistant automatic device discovery feature.
|
||||
|
||||
The use of Home Assistant is not mandatory; it will publish the sensor values via MQTT just fine without it.
|
||||
|
||||
Its resusing the mqtt connection set in the WLED web user interface.
|
||||
|
||||
## Maintainer
|
||||
|
||||
twitter.com/mpronk89
|
||||
|
||||
## Features
|
||||
|
||||
- Reads BMP280, CCS811 and Si7021 senors
|
||||
- Publishes via MQTT, configured via webui of wled
|
||||
- Announces device in Home Assistant for easy setup
|
||||
- Efficient energy usage
|
||||
- Updates every 60 seconds
|
||||
|
||||
## Example mqtt topics:
|
||||
|
||||
`$mqttDeviceTopic` is set in webui of WLED!
|
||||
|
||||
```
|
||||
temperature: $mqttDeviceTopic/temperature
|
||||
pressure: $mqttDeviceTopic/pressure
|
||||
humidity: $mqttDeviceTopic/humidity
|
||||
tvoc: $mqttDeviceTopic/tvoc
|
||||
eCO2: $mqttDeviceTopic/eco2
|
||||
IAQ: $mqttDeviceTopic/iaq
|
||||
```
|
||||
|
||||
# Installation
|
||||
|
||||
## Hardware
|
||||
|
||||
### Requirements
|
||||
|
||||
1. BMP280/CCS811/Si7021 sensor. E.g. https://aliexpress.com/item/32979998543.html
|
||||
2. A microcontroller which can talk i2c, e.g. esp32
|
||||
|
||||
### installation
|
||||
|
||||
Attach the sensor to the i2c interface.
|
||||
|
||||
Default PINs esp32:
|
||||
|
||||
```
|
||||
SCL_PIN = 22;
|
||||
SDA_PIN = 21;
|
||||
```
|
||||
|
||||
Default PINs ESP8266:
|
||||
|
||||
```
|
||||
SCL_PIN = 5;
|
||||
SDA_PIN = 4;
|
||||
```
|
||||
|
||||
## Enable in WLED
|
||||
|
||||
1. Copy `usermod_v2_SensorsToMqtt.h` into the `wled00` directory.
|
||||
2. Add to `build_flags` in platformio.ini:
|
||||
|
||||
```
|
||||
-D USERMOD_SENSORSTOMQTT
|
||||
```
|
||||
|
||||
3. And add to `lib_deps` in platformio.ini:
|
||||
|
||||
```
|
||||
adafruit/Adafruit BMP280 Library @ 2.1.0
|
||||
adafruit/Adafruit CCS811 Library @ 1.0.4
|
||||
adafruit/Adafruit Si7021 Library @ 1.4.0
|
||||
```
|
||||
|
||||
The #ifdefs in `usermods_list.cpp` should do the rest :)
|
||||
|
||||
# Credits
|
||||
|
||||
- Aircoookie for making WLED
|
||||
- Other usermod creators for example code
|
||||
- Bouke_Regnerus for https://community.home-assistant.io/t/example-indoor-air-quality-text-sensor-using-ccs811-sensor/125854
|
||||
- You, for reading this
|
284
usermods/sensors_to_mqtt/usermod_v2_SensorsToMqtt.h
Normal file
@ -0,0 +1,284 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_Sensor.h>
|
||||
#include <Adafruit_BMP280.h>
|
||||
#include <Adafruit_CCS811.h>
|
||||
#include <Adafruit_Si7021.h>
|
||||
|
||||
Adafruit_BMP280 bmp;
|
||||
Adafruit_Si7021 si7021;
|
||||
Adafruit_CCS811 ccs811;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards
|
||||
uint8_t SCL_PIN = 22;
|
||||
uint8_t SDA_PIN = 21;
|
||||
#else //ESP8266 boards
|
||||
uint8_t SCL_PIN = 5;
|
||||
uint8_t SDA_PIN = 4;
|
||||
#endif
|
||||
|
||||
class UserMod_SensorsToMQTT : public Usermod
|
||||
{
|
||||
private:
|
||||
bool initialized = false;
|
||||
bool mqttInitialized = false;
|
||||
float SensorPressure = 0;
|
||||
float SensorTemperature = 0;
|
||||
float SensorHumidity = 0;
|
||||
char *SensorIaq = "Unknown";
|
||||
String mqttTemperatureTopic = "";
|
||||
String mqttHumidityTopic = "";
|
||||
String mqttPressureTopic = "";
|
||||
String mqttTvocTopic = "";
|
||||
String mqttEco2Topic = "";
|
||||
String mqttIaqTopic = "";
|
||||
unsigned int SensorTvoc = 0;
|
||||
unsigned int SensorEco2 = 0;
|
||||
unsigned long nextMeasure = 0;
|
||||
|
||||
void _initialize()
|
||||
{
|
||||
initialized = bmp.begin(BMP280_ADDRESS_ALT);
|
||||
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
|
||||
Adafruit_BMP280::SAMPLING_X16, /* Temp. oversampling */
|
||||
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
|
||||
Adafruit_BMP280::FILTER_X16, /* Filtering. */
|
||||
Adafruit_BMP280::STANDBY_MS_2000); /* Refresh values every 20 seconds */
|
||||
|
||||
initialized &= si7021.begin();
|
||||
initialized &= ccs811.begin();
|
||||
ccs811.setDriveMode(CCS811_DRIVE_MODE_10SEC); /* Refresh values every 10s */
|
||||
Serial.print(initialized);
|
||||
}
|
||||
|
||||
void _mqttInitialize()
|
||||
{
|
||||
mqttTemperatureTopic = String(mqttDeviceTopic) + "/temperature";
|
||||
mqttPressureTopic = String(mqttDeviceTopic) + "/pressure";
|
||||
mqttHumidityTopic = String(mqttDeviceTopic) + "/humidity";
|
||||
mqttTvocTopic = String(mqttDeviceTopic) + "/tvoc";
|
||||
mqttEco2Topic = String(mqttDeviceTopic) + "/eco2";
|
||||
mqttIaqTopic = String(mqttDeviceTopic) + "/iaq";
|
||||
|
||||
String t = String("homeassistant/sensor/") + mqttClientID + "/temperature/config";
|
||||
|
||||
_createMqttSensor("temperature", mqttTemperatureTopic, "temperature", "°C");
|
||||
_createMqttSensor("pressure", mqttPressureTopic, "pressure", "hPa");
|
||||
_createMqttSensor("humidity", mqttHumidityTopic, "humidity", "%");
|
||||
_createMqttSensor("tvoc", mqttTvocTopic, "", "ppb");
|
||||
_createMqttSensor("eco2", mqttEco2Topic, "", "ppm");
|
||||
_createMqttSensor("iaq", mqttIaqTopic, "", "");
|
||||
}
|
||||
|
||||
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
|
||||
{
|
||||
String t = String("homeassistant/sensor/") + mqttClientID + "/" + name + "/config";
|
||||
|
||||
StaticJsonDocument<300> doc;
|
||||
|
||||
doc["name"] = name;
|
||||
doc["state_topic"] = topic;
|
||||
doc["unique_id"] = String(mqttClientID) + name;
|
||||
if (unitOfMeasurement != "")
|
||||
doc["unit_of_measurement"] = unitOfMeasurement;
|
||||
if (deviceClass != "")
|
||||
doc["device_class"] = deviceClass;
|
||||
doc["expire_after"] = 1800;
|
||||
|
||||
JsonObject device = doc.createNestedObject("device"); // attach the sensor to the same device
|
||||
device["identifiers"] = String("wled-sensor-") + mqttClientID;
|
||||
device["manufacturer"] = "Aircoookie";
|
||||
device["model"] = "WLED";
|
||||
device["sw_version"] = VERSION;
|
||||
device["name"] = mqttClientID;
|
||||
|
||||
String temp;
|
||||
serializeJson(doc, temp);
|
||||
Serial.println(t);
|
||||
Serial.println(temp);
|
||||
|
||||
mqtt->publish(t.c_str(), 0, true, temp.c_str());
|
||||
}
|
||||
|
||||
void _updateSensorData()
|
||||
{
|
||||
SensorTemperature = bmp.readTemperature();
|
||||
SensorHumidity = si7021.readHumidity();
|
||||
SensorPressure = (bmp.readPressure() / 100.0F);
|
||||
ccs811.setEnvironmentalData(SensorHumidity, SensorTemperature);
|
||||
ccs811.readData();
|
||||
SensorTvoc = ccs811.getTVOC();
|
||||
SensorEco2 = ccs811.geteCO2();
|
||||
SensorIaq = _getIaqIndex(SensorHumidity, SensorTvoc, SensorEco2);
|
||||
|
||||
Serial.printf("%f c, %f humidity, %f hPA, %u tvoc, %u Eco2, %s iaq\n",
|
||||
SensorTemperature, SensorHumidity, SensorPressure,
|
||||
SensorTvoc, SensorEco2, SensorIaq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Credits: Bouke_Regnerus @ https://community.home-assistant.io/t/example-indoor-air-quality-text-sensor-using-ccs811-sensor/125854
|
||||
*/
|
||||
char *_getIaqIndex(float humidity, int tvoc, int eco2)
|
||||
{
|
||||
int iaq_index = 0;
|
||||
|
||||
/*
|
||||
* Transform indoor humidity values to IAQ points according to Indoor Air Quality UK:
|
||||
* http://www.iaquk.org.uk/
|
||||
*/
|
||||
if (humidity < 10 or humidity > 90)
|
||||
{
|
||||
iaq_index += 1;
|
||||
}
|
||||
else if (humidity < 20 or humidity > 80)
|
||||
{
|
||||
iaq_index += 2;
|
||||
}
|
||||
else if (humidity < 30 or humidity > 70)
|
||||
{
|
||||
iaq_index += 3;
|
||||
}
|
||||
else if (humidity < 40 or humidity > 60)
|
||||
{
|
||||
iaq_index += 4;
|
||||
}
|
||||
else if (humidity >= 40 and humidity <= 60)
|
||||
{
|
||||
iaq_index += 5;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform eCO2 values to IAQ points according to Indoor Air Quality UK:
|
||||
* http://www.iaquk.org.uk/
|
||||
*/
|
||||
if (eco2 <= 600)
|
||||
{
|
||||
iaq_index += 5;
|
||||
}
|
||||
else if (eco2 <= 800)
|
||||
{
|
||||
iaq_index += 4;
|
||||
}
|
||||
else if (eco2 <= 1500)
|
||||
{
|
||||
iaq_index += 3;
|
||||
}
|
||||
else if (eco2 <= 1800)
|
||||
{
|
||||
iaq_index += 2;
|
||||
}
|
||||
else if (eco2 > 1800)
|
||||
{
|
||||
iaq_index += 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform TVOC values to IAQ points according to German environmental guidelines:
|
||||
* https://www.repcomsrl.com/wp-content/uploads/2017/06/Environmental_Sensing_VOC_Product_Brochure_EN.pdf
|
||||
*/
|
||||
if (tvoc <= 65)
|
||||
{
|
||||
iaq_index += 5;
|
||||
}
|
||||
else if (tvoc <= 220)
|
||||
{
|
||||
iaq_index += 4;
|
||||
}
|
||||
else if (tvoc <= 660)
|
||||
{
|
||||
iaq_index += 3;
|
||||
}
|
||||
else if (tvoc <= 2200)
|
||||
{
|
||||
iaq_index += 2;
|
||||
}
|
||||
else if (tvoc > 2200)
|
||||
{
|
||||
iaq_index += 1;
|
||||
}
|
||||
|
||||
if (iaq_index <= 6)
|
||||
{
|
||||
return "Unhealty";
|
||||
}
|
||||
else if (iaq_index <= 9)
|
||||
{
|
||||
return "Poor";
|
||||
}
|
||||
else if (iaq_index <= 12)
|
||||
{
|
||||
return "Moderate";
|
||||
}
|
||||
else if (iaq_index <= 14)
|
||||
{
|
||||
return "Good";
|
||||
}
|
||||
else if (iaq_index > 14)
|
||||
{
|
||||
return "Excellent";
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
Serial.println("Starting!");
|
||||
Wire.begin(SDA_PIN, SCL_PIN);
|
||||
Serial.println("Initializing sensors.. ");
|
||||
_initialize();
|
||||
}
|
||||
|
||||
// gets called every time WiFi is (re-)connected.
|
||||
void connected()
|
||||
{
|
||||
nextMeasure = millis() + 5000; // Schedule next measure in 5 seconds
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
unsigned long tempTimer = millis();
|
||||
|
||||
if (tempTimer > nextMeasure)
|
||||
{
|
||||
nextMeasure = tempTimer + 60000; // Schedule next measure in 60 seconds
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
Serial.println("Error! Sensors not initialized in loop()!");
|
||||
_initialize();
|
||||
return; // lets try again next loop
|
||||
}
|
||||
|
||||
if (mqtt != nullptr && mqtt->connected())
|
||||
{
|
||||
if (!mqttInitialized)
|
||||
{
|
||||
_mqttInitialize();
|
||||
mqttInitialized = true;
|
||||
}
|
||||
|
||||
// Update sensor data
|
||||
_updateSensorData();
|
||||
|
||||
// Create string populated with user defined device topic from the UI,
|
||||
// and the read temperature, humidity and pressure.
|
||||
// Then publish to MQTT server.
|
||||
mqtt->publish(mqttTemperatureTopic.c_str(), 0, true, String(SensorTemperature).c_str());
|
||||
mqtt->publish(mqttPressureTopic.c_str(), 0, true, String(SensorPressure).c_str());
|
||||
mqtt->publish(mqttHumidityTopic.c_str(), 0, true, String(SensorHumidity).c_str());
|
||||
mqtt->publish(mqttTvocTopic.c_str(), 0, true, String(SensorTvoc).c_str());
|
||||
mqtt->publish(mqttEco2Topic.c_str(), 0, true, String(SensorEco2).c_str());
|
||||
mqtt->publish(mqttIaqTopic.c_str(), 0, true, String(SensorIaq).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Missing MQTT connection. Not publishing data");
|
||||
mqttInitialized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
263
wled00/FX.cpp
@ -30,7 +30,6 @@
|
||||
#define IBN 5100
|
||||
#define PALETTE_SOLID_WRAP (paletteBlend == 1 || paletteBlend == 3)
|
||||
|
||||
|
||||
/*
|
||||
* No blinking. Just plain old static light.
|
||||
*/
|
||||
@ -594,7 +593,7 @@ uint16_t WS2812FX::mode_sparkle(void) {
|
||||
|
||||
|
||||
/*
|
||||
* Lights all LEDs in the color. Flashes single white pixels randomly.
|
||||
* Lights all LEDs in the color. Flashes single col 1 pixels randomly. (List name: Sparkle Dark)
|
||||
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
|
||||
*/
|
||||
uint16_t WS2812FX::mode_flash_sparkle(void) {
|
||||
@ -602,12 +601,14 @@ uint16_t WS2812FX::mode_flash_sparkle(void) {
|
||||
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
|
||||
}
|
||||
|
||||
if(random8(5) == 0) {
|
||||
SEGENV.aux0 = random16(SEGLEN); // aux0 stores the random led index
|
||||
setPixelColor(SEGENV.aux0, SEGCOLOR(1));
|
||||
return 20;
|
||||
}
|
||||
return 20 + (uint16_t)(255-SEGMENT.speed);
|
||||
if (now - SEGENV.aux0 > SEGENV.step) {
|
||||
if(random8((255-SEGMENT.intensity) >> 4) == 0) {
|
||||
setPixelColor(random16(SEGLEN), SEGCOLOR(1)); //flash
|
||||
}
|
||||
SEGENV.step = now;
|
||||
SEGENV.aux0 = 255-SEGMENT.speed;
|
||||
}
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
@ -620,13 +621,16 @@ uint16_t WS2812FX::mode_hyper_sparkle(void) {
|
||||
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
|
||||
}
|
||||
|
||||
if(random8(5) < 2) {
|
||||
for(uint16_t i = 0; i < MAX(1, SEGLEN/3); i++) {
|
||||
setPixelColor(random16(SEGLEN), SEGCOLOR(1));
|
||||
if (now - SEGENV.aux0 > SEGENV.step) {
|
||||
if(random8((255-SEGMENT.intensity) >> 4) == 0) {
|
||||
for(uint16_t i = 0; i < MAX(1, SEGLEN/3); i++) {
|
||||
setPixelColor(random16(SEGLEN), SEGCOLOR(1));
|
||||
}
|
||||
}
|
||||
return 20;
|
||||
SEGENV.step = now;
|
||||
SEGENV.aux0 = 255-SEGMENT.speed;
|
||||
}
|
||||
return 20 + (uint16_t)(255-SEGMENT.speed);
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
@ -637,22 +641,25 @@ uint16_t WS2812FX::mode_multi_strobe(void) {
|
||||
for(uint16_t i = 0; i < SEGLEN; i++) {
|
||||
setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1));
|
||||
}
|
||||
//blink(SEGCOLOR(0), SEGCOLOR(1), true, true);
|
||||
|
||||
uint16_t delay = 50 + 20*(uint16_t)(255-SEGMENT.speed);
|
||||
uint16_t count = 2 * ((SEGMENT.speed / 10) + 1);
|
||||
if(SEGENV.step < count) {
|
||||
if((SEGENV.step & 1) == 0) {
|
||||
for(uint16_t i = 0; i < SEGLEN; i++) {
|
||||
setPixelColor(i, SEGCOLOR(0));
|
||||
}
|
||||
delay = 20;
|
||||
SEGENV.aux0 = 50 + 20*(uint16_t)(255-SEGMENT.speed);
|
||||
uint16_t count = 2 * ((SEGMENT.intensity / 10) + 1);
|
||||
if(SEGENV.aux1 < count) {
|
||||
if((SEGENV.aux1 & 1) == 0) {
|
||||
fill(SEGCOLOR(0));
|
||||
SEGENV.aux0 = 15;
|
||||
} else {
|
||||
delay = 50;
|
||||
SEGENV.aux0 = 50;
|
||||
}
|
||||
}
|
||||
SEGENV.step = (SEGENV.step + 1) % (count + 1);
|
||||
return delay;
|
||||
|
||||
if (now - SEGENV.aux0 > SEGENV.step) {
|
||||
SEGENV.aux1++;
|
||||
if (SEGENV.aux1 > count) SEGENV.aux1 = 0;
|
||||
SEGENV.step = now;
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -995,14 +1002,6 @@ uint16_t WS2812FX::mode_running_color(void) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Alternating red/blue pixels running.
|
||||
*/
|
||||
uint16_t WS2812FX::mode_running_red_blue(void) {
|
||||
return running(RED, BLUE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Alternating red/green pixels running.
|
||||
*/
|
||||
@ -1620,40 +1619,42 @@ uint16_t WS2812FX::mode_oscillate(void)
|
||||
uint16_t WS2812FX::mode_lightning(void)
|
||||
{
|
||||
uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash
|
||||
uint16_t ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1)
|
||||
uint16_t ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1)
|
||||
uint8_t bri = 255/random8(1, 3);
|
||||
|
||||
if (SEGENV.step == 0)
|
||||
if (SEGENV.aux1 == 0) //init, leader flash
|
||||
{
|
||||
SEGENV.aux0 = random8(3, 3 + SEGMENT.intensity/20); //number of flashes
|
||||
bri = 52;
|
||||
SEGENV.aux1 = 1;
|
||||
SEGENV.aux1 = random8(4, 4 + SEGMENT.intensity/20); //number of flashes
|
||||
SEGENV.aux1 *= 2;
|
||||
|
||||
bri = 52; //leader has lower brightness
|
||||
SEGENV.aux0 = 200; //200ms delay after leader
|
||||
}
|
||||
|
||||
fill(SEGCOLOR(1));
|
||||
|
||||
if (SEGENV.aux1) {
|
||||
if (SEGENV.aux1 > 3 && !(SEGENV.aux1 & 0x01)) { //flash on even number >2
|
||||
for (int i = ledstart; i < ledstart + ledlen; i++)
|
||||
{
|
||||
if (SEGMENT.palette == 0)
|
||||
{
|
||||
setPixelColor(i,bri,bri,bri,bri);
|
||||
} else {
|
||||
setPixelColor(i,color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, bri));
|
||||
}
|
||||
setPixelColor(i,color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, bri));
|
||||
}
|
||||
SEGENV.aux1--;
|
||||
|
||||
SEGENV.step = millis();
|
||||
//return random8(4, 10); // each flash only lasts one frame/every 24ms... originally 4-10 milliseconds
|
||||
} else {
|
||||
if (millis() - SEGENV.step > SEGENV.aux0) {
|
||||
SEGENV.aux1--;
|
||||
if (SEGENV.aux1 < 2) SEGENV.aux1 = 0;
|
||||
|
||||
SEGENV.aux0 = (50 + random8(100)); //delay between flashes
|
||||
if (SEGENV.aux1 == 2) {
|
||||
SEGENV.aux0 = (random8(255 - SEGMENT.speed) * 100); // delay between strikes
|
||||
}
|
||||
SEGENV.step = millis();
|
||||
}
|
||||
SEGENV.aux1 = 0;
|
||||
SEGENV.step++;
|
||||
return random8(4, 10); // each flash only lasts 4-10 milliseconds
|
||||
}
|
||||
|
||||
SEGENV.aux1 = 1;
|
||||
if (SEGENV.step == 1) return (200); // longer delay until next flash after the leader
|
||||
|
||||
if (SEGENV.step <= SEGENV.aux0) return (50 + random8(100)); // shorter delay between strokes
|
||||
|
||||
SEGENV.step = 0;
|
||||
return (random8(255 - SEGMENT.speed) * 100); // delay between strikes
|
||||
return FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
@ -3870,3 +3871,153 @@ uint16_t WS2812FX::mode_tv_simulator(void) {
|
||||
return FRAMETIME;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
Aurora effect
|
||||
*/
|
||||
|
||||
//CONFIG
|
||||
#define BACKLIGHT 5
|
||||
#define W_MAX_COUNT 20 //Number of simultaneous waves
|
||||
#define W_MAX_SPEED 6 //Higher number, higher speed
|
||||
#define W_WIDTH_FACTOR 6 //Higher number, smaller waves
|
||||
|
||||
class AuroraWave {
|
||||
private:
|
||||
uint16_t ttl;
|
||||
CRGB basecolor;
|
||||
float basealpha;
|
||||
uint16_t age;
|
||||
uint16_t width;
|
||||
float center;
|
||||
bool goingleft;
|
||||
float speed_factor;
|
||||
bool alive = true;
|
||||
|
||||
public:
|
||||
void init(uint32_t segment_length, CRGB color) {
|
||||
ttl = random(500, 1501);
|
||||
basecolor = color;
|
||||
basealpha = random(60, 101) / (float)100;
|
||||
age = 0;
|
||||
width = random(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier
|
||||
if (!width) width = 1;
|
||||
center = random(101) / (float)100 * segment_length;
|
||||
goingleft = random(0, 2) == 0;
|
||||
speed_factor = (random(10, 31) / (float)100 * W_MAX_SPEED / 255);
|
||||
alive = true;
|
||||
}
|
||||
|
||||
CRGB getColorForLED(int ledIndex) {
|
||||
if(ledIndex < center - width || ledIndex > center + width) return 0; //Position out of range of this wave
|
||||
|
||||
CRGB rgb;
|
||||
|
||||
//Offset of this led from center of wave
|
||||
//The further away from the center, the dimmer the LED
|
||||
float offset = ledIndex - center;
|
||||
if (offset < 0) offset = -offset;
|
||||
float offsetFactor = offset / width;
|
||||
|
||||
//The age of the wave determines it brightness.
|
||||
//At half its maximum age it will be the brightest.
|
||||
float ageFactor = 0.1;
|
||||
if((float)age / ttl < 0.5) {
|
||||
ageFactor = (float)age / (ttl / 2);
|
||||
} else {
|
||||
ageFactor = (float)(ttl - age) / ((float)ttl * 0.5);
|
||||
}
|
||||
|
||||
//Calculate color based on above factors and basealpha value
|
||||
float factor = (1 - offsetFactor) * ageFactor * basealpha;
|
||||
rgb.r = basecolor.r * factor;
|
||||
rgb.g = basecolor.g * factor;
|
||||
rgb.b = basecolor.b * factor;
|
||||
|
||||
return rgb;
|
||||
};
|
||||
|
||||
//Change position and age of wave
|
||||
//Determine if its sill "alive"
|
||||
void update(uint32_t segment_length, uint32_t speed) {
|
||||
if(goingleft) {
|
||||
center -= speed_factor * speed;
|
||||
} else {
|
||||
center += speed_factor * speed;
|
||||
}
|
||||
|
||||
age++;
|
||||
|
||||
if(age > ttl) {
|
||||
alive = false;
|
||||
} else {
|
||||
if(goingleft) {
|
||||
if(center + width < 0) {
|
||||
alive = false;
|
||||
}
|
||||
} else {
|
||||
if(center - width > segment_length) {
|
||||
alive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool stillAlive() {
|
||||
return alive;
|
||||
};
|
||||
};
|
||||
|
||||
uint16_t WS2812FX::mode_aurora(void) {
|
||||
//aux1 = Wavecount
|
||||
//aux2 = Intensity in last loop
|
||||
|
||||
AuroraWave* waves;
|
||||
|
||||
if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
|
||||
//Intensity slider changed or first call
|
||||
SEGENV.aux1 = ((float)SEGMENT.intensity / 255) * W_MAX_COUNT;
|
||||
SEGENV.aux0 = SEGMENT.intensity;
|
||||
|
||||
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) {
|
||||
return mode_static(); //allocation failed
|
||||
}
|
||||
|
||||
waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
|
||||
|
||||
for(int i = 0; i < SEGENV.aux1; i++) {
|
||||
waves[i].init(SEGLEN, col_to_crgb(color_from_palette(random8(), false, false, random(0, 3))));
|
||||
}
|
||||
} else {
|
||||
waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
|
||||
}
|
||||
|
||||
for(int i = 0; i < SEGENV.aux1; i++) {
|
||||
//Update values of wave
|
||||
waves[i].update(SEGLEN, SEGMENT.speed);
|
||||
|
||||
if(!(waves[i].stillAlive())) {
|
||||
//If a wave dies, reinitialize it starts over.
|
||||
waves[i].init(SEGLEN, col_to_crgb(color_from_palette(random8(), false, false, random(0, 3))));
|
||||
}
|
||||
}
|
||||
|
||||
//Loop through LEDs to determine color
|
||||
for(int i = 0; i < SEGLEN; i++) {
|
||||
CRGB mixedRgb = CRGB(BACKLIGHT, BACKLIGHT, BACKLIGHT);
|
||||
|
||||
//For each LED we must check each wave if it is "active" at this position.
|
||||
//If there are multiple waves active on a LED we multiply their values.
|
||||
for(int j = 0; j < SEGENV.aux1; j++) {
|
||||
CRGB rgb = waves[j].getColorForLED(i);
|
||||
|
||||
if(rgb != CRGB(0)) {
|
||||
mixedRgb += rgb;
|
||||
}
|
||||
}
|
||||
|
||||
setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2], BACKLIGHT);
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
167
wled00/FX.h
@ -62,16 +62,15 @@
|
||||
/* 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 12
|
||||
/* 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
|
||||
#else
|
||||
#define MAX_NUM_SEGMENTS 16
|
||||
#endif
|
||||
|
||||
/* How much data bytes all segments combined may allocate */
|
||||
#ifdef ESP8266
|
||||
#define MAX_SEGMENT_DATA 2048
|
||||
#else
|
||||
#define MAX_SEGMENT_DATA 8192
|
||||
#define MAX_NUM_SEGMENTS 16
|
||||
#define MAX_NUM_TRANSITIONS 16
|
||||
#define MAX_SEGMENT_DATA 8192
|
||||
#endif
|
||||
|
||||
#define LED_SKIP_AMOUNT 1
|
||||
@ -79,7 +78,7 @@
|
||||
|
||||
#define NUM_COLORS 3 /* number of colors per segment */
|
||||
#define SEGMENT _segments[_segment_index]
|
||||
#define SEGCOLOR(x) gamma32(_segments[_segment_index].colors[x])
|
||||
#define SEGCOLOR(x) _colors_t[x]
|
||||
#define SEGENV _segment_runtimes[_segment_index]
|
||||
#define SEGLEN _virtualSegmentLength
|
||||
#define SEGACT SEGMENT.stop
|
||||
@ -119,7 +118,6 @@
|
||||
#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE )
|
||||
#define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED )
|
||||
|
||||
|
||||
#define MODE_COUNT 118
|
||||
|
||||
#define FX_MODE_STATIC 0
|
||||
@ -160,7 +158,7 @@
|
||||
#define FX_MODE_TRAFFIC_LIGHT 35
|
||||
#define FX_MODE_COLOR_SWEEP_RANDOM 36
|
||||
#define FX_MODE_RUNNING_COLOR 37
|
||||
#define FX_MODE_RUNNING_RED_BLUE 38
|
||||
#define FX_MODE_AURORA 38
|
||||
#define FX_MODE_RUNNING_RANDOM 39
|
||||
#define FX_MODE_LARSON_SCANNER 40
|
||||
#define FX_MODE_COMET 41
|
||||
@ -241,11 +239,14 @@
|
||||
#define FX_MODE_TV_SIMULATOR 116
|
||||
#define FX_MODE_DYNAMIC_SMOOTH 117
|
||||
|
||||
|
||||
class WS2812FX {
|
||||
typedef uint16_t (WS2812FX::*mode_ptr)(void);
|
||||
|
||||
// pre show callback
|
||||
typedef void (*show_callback) (void);
|
||||
|
||||
static WS2812FX* instance;
|
||||
|
||||
// segment parameters
|
||||
public:
|
||||
@ -260,14 +261,40 @@ class WS2812FX {
|
||||
uint8_t grouping, spacing;
|
||||
uint8_t opacity;
|
||||
uint32_t colors[NUM_COLORS];
|
||||
void setOption(uint8_t n, bool val)
|
||||
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;
|
||||
ColorTransition::startTransition(opacity, colors[slot], instance->_transitionDur, segn, slot);
|
||||
colors[slot] = c; return true;
|
||||
}
|
||||
void setOpacity(uint8_t o, uint8_t segn) {
|
||||
if (segn >= MAX_NUM_SEGMENTS) return;
|
||||
if (opacity == o) return;
|
||||
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
|
||||
opacity = o;
|
||||
}
|
||||
/*uint8_t actualOpacity() { //respects On/Off state
|
||||
if (!getOption(SEG_OPTION_ON)) return 0;
|
||||
return opacity;
|
||||
}*/
|
||||
void setOption(uint8_t n, bool val, uint8_t segn = 255)
|
||||
{
|
||||
//bool prevOn = false;
|
||||
//if (n == SEG_OPTION_ON) prevOn = getOption(SEG_OPTION_ON);
|
||||
if (val) {
|
||||
options |= 0x01 << n;
|
||||
} else
|
||||
{
|
||||
options &= ~(0x01 << n);
|
||||
}
|
||||
//transitions on segment on/off don't work correctly at this point
|
||||
/*if (n == SEG_OPTION_ON && segn < MAX_NUM_SEGMENTS && getOption(SEG_OPTION_ON) != prevOn) {
|
||||
if (getOption(SEG_OPTION_ON)) {
|
||||
ColorTransition::startTransition(0, colors[0], instance->_transitionDur, segn, 0);
|
||||
} else {
|
||||
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
bool getOption(uint8_t n)
|
||||
{
|
||||
@ -310,10 +337,10 @@ class WS2812FX {
|
||||
bool allocateData(uint16_t len){
|
||||
if (data && _dataLen == len) return true; //already allocated
|
||||
deallocateData();
|
||||
if (WS2812FX::_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory
|
||||
if (WS2812FX::instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory
|
||||
data = new (std::nothrow) byte[len];
|
||||
if (!data) return false; //allocation failed
|
||||
WS2812FX::_usedSegmentData += len;
|
||||
WS2812FX::instance->_usedSegmentData += len;
|
||||
_dataLen = len;
|
||||
memset(data, 0, len);
|
||||
return true;
|
||||
@ -321,7 +348,7 @@ class WS2812FX {
|
||||
void deallocateData(){
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
WS2812FX::_usedSegmentData -= _dataLen;
|
||||
WS2812FX::instance->_usedSegmentData -= _dataLen;
|
||||
_dataLen = 0;
|
||||
}
|
||||
|
||||
@ -351,7 +378,86 @@ class WS2812FX {
|
||||
bool _requiresReset = false;
|
||||
} segment_runtime;
|
||||
|
||||
typedef struct ColorTransition { // 12 bytes
|
||||
uint32_t colorOld = 0;
|
||||
uint32_t transitionStart;
|
||||
uint16_t transitionDur;
|
||||
uint8_t segment = 0xFF; //lower 6 bits: the segment this transition is for (255 indicates transition not in use/available) upper 2 bits: color channel
|
||||
uint8_t briOld = 0;
|
||||
static void startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot) {
|
||||
if (segn >= MAX_NUM_SEGMENTS || slot >= NUM_COLORS || dur == 0) return;
|
||||
if (instance->_brightness == 0) return; //do not need transitions if master bri is off
|
||||
uint8_t tIndex = 0xFF; //none found
|
||||
uint16_t tProgression = 0;
|
||||
uint8_t s = segn + (slot << 6); //merge slot and segment into one byte
|
||||
|
||||
for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) {
|
||||
uint8_t tSeg = instance->transitions[i].segment;
|
||||
//see if this segment + color already has a running transition
|
||||
if (tSeg == s) {
|
||||
tIndex = i; break;
|
||||
}
|
||||
if (tSeg == 0xFF) { //free transition
|
||||
tIndex = i; tProgression = 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
if (tIndex == 0xFF) { //no slot found yet
|
||||
for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) {
|
||||
//find most progressed transition to overwrite
|
||||
uint16_t prog = instance->transitions[i].progress();
|
||||
if (prog > tProgression) {
|
||||
tIndex = i; tProgression = prog;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColorTransition& t = instance->transitions[tIndex];
|
||||
if (t.segment == s) //this is an active transition on the same segment+color
|
||||
{
|
||||
t.briOld = t.currentBri();
|
||||
t.colorOld = t.currentColor(oldCol);
|
||||
} else {
|
||||
t.briOld = oldBri;
|
||||
t.colorOld = oldCol;
|
||||
uint8_t prevSeg = t.segment & 0x3F;
|
||||
if (prevSeg < MAX_NUM_SEGMENTS) instance->_segments[prevSeg].setOption(SEG_OPTION_TRANSITIONAL, false);
|
||||
}
|
||||
t.transitionDur = dur;
|
||||
t.transitionStart = millis();
|
||||
t.segment = s;
|
||||
instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, true);
|
||||
//refresh immediately, required for Solid mode
|
||||
if (instance->_segment_runtimes[segn].next_time > t.transitionStart + 22) instance->_segment_runtimes[segn].next_time = t.transitionStart;
|
||||
}
|
||||
uint16_t progress(bool allowEnd = false) { //transition progression between 0-65535
|
||||
uint32_t timeNow = millis();
|
||||
if (timeNow - transitionStart > transitionDur) {
|
||||
if (allowEnd) {
|
||||
uint8_t segn = segment & 0x3F;
|
||||
if (segn < MAX_NUM_SEGMENTS) instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, false);
|
||||
segment = 0xFF;
|
||||
}
|
||||
return 0xFFFF;
|
||||
}
|
||||
uint32_t elapsed = timeNow - transitionStart;
|
||||
uint32_t prog = elapsed * 0xFFFF / transitionDur;
|
||||
return (prog > 0xFFFF) ? 0xFFFF : prog;
|
||||
}
|
||||
uint32_t currentColor(uint32_t colorNew) {
|
||||
return instance->color_blend(colorOld, colorNew, progress(true), true);
|
||||
}
|
||||
uint8_t currentBri() {
|
||||
uint8_t segn = segment & 0x3F;
|
||||
if (segn >= MAX_NUM_SEGMENTS) return 0;
|
||||
uint8_t briNew = instance->_segments[segn].opacity;
|
||||
uint32_t prog = progress() + 1;
|
||||
return ((briNew * prog) + (briOld * (0x10000 - prog))) >> 16;
|
||||
}
|
||||
} color_transition;
|
||||
|
||||
WS2812FX() {
|
||||
WS2812FX::instance = this;
|
||||
//assign each member of the _mode[] array to its respective function reference
|
||||
_mode[FX_MODE_STATIC] = &WS2812FX::mode_static;
|
||||
_mode[FX_MODE_BLINK] = &WS2812FX::mode_blink;
|
||||
@ -389,7 +495,7 @@ class WS2812FX {
|
||||
_mode[FX_MODE_TRAFFIC_LIGHT] = &WS2812FX::mode_traffic_light;
|
||||
_mode[FX_MODE_COLOR_SWEEP_RANDOM] = &WS2812FX::mode_color_sweep_random;
|
||||
_mode[FX_MODE_RUNNING_COLOR] = &WS2812FX::mode_running_color;
|
||||
_mode[FX_MODE_RUNNING_RED_BLUE] = &WS2812FX::mode_running_red_blue;
|
||||
_mode[FX_MODE_AURORA] = &WS2812FX::mode_aurora;
|
||||
_mode[FX_MODE_RUNNING_RANDOM] = &WS2812FX::mode_running_random;
|
||||
_mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner;
|
||||
_mode[FX_MODE_COMET] = &WS2812FX::mode_comet;
|
||||
@ -494,6 +600,7 @@ class WS2812FX {
|
||||
setBrightness(uint8_t b),
|
||||
setRange(uint16_t i, uint16_t i2, uint32_t col),
|
||||
setShowCallback(show_callback cb),
|
||||
setTransition(uint16_t t),
|
||||
setTransitionMode(bool t),
|
||||
calcGammaTable(float),
|
||||
trigger(void),
|
||||
@ -548,7 +655,8 @@ class WS2812FX {
|
||||
timebase,
|
||||
color_wheel(uint8_t),
|
||||
color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255),
|
||||
color_blend(uint32_t,uint32_t,uint8_t),
|
||||
color_blend(uint32_t,uint32_t,uint16_t,bool b16=false),
|
||||
currentColor(uint32_t colorNew, uint8_t tNr),
|
||||
gamma32(uint32_t),
|
||||
getLastShow(void),
|
||||
getPixelColor(uint16_t),
|
||||
@ -603,7 +711,7 @@ class WS2812FX {
|
||||
mode_colorful(void),
|
||||
mode_traffic_light(void),
|
||||
mode_running_color(void),
|
||||
mode_running_red_blue(void),
|
||||
mode_aurora(void),
|
||||
mode_running_random(void),
|
||||
mode_larson_scanner(void),
|
||||
mode_comet(void),
|
||||
@ -696,7 +804,8 @@ class WS2812FX {
|
||||
uint16_t _length, _lengthRaw, _virtualSegmentLength;
|
||||
uint16_t _rand16seed;
|
||||
uint8_t _brightness;
|
||||
static uint16_t _usedSegmentData;
|
||||
uint16_t _usedSegmentData = 0;
|
||||
uint16_t _transitionDur = 750;
|
||||
|
||||
void load_gradient_palette(uint8_t);
|
||||
void handle_palette(void);
|
||||
@ -736,10 +845,15 @@ class WS2812FX {
|
||||
CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat);
|
||||
CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff);
|
||||
|
||||
void blendPixelColor(uint16_t n, uint32_t color, uint8_t blend);
|
||||
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);
|
||||
|
||||
uint32_t _lastPaletteChange = 0;
|
||||
uint32_t _lastShow = 0;
|
||||
|
||||
uint32_t _colors_t[3];
|
||||
uint8_t _bri_t;
|
||||
|
||||
#ifdef WLED_USE_ANALOG_LEDS
|
||||
uint32_t _analogLastShow = 0;
|
||||
@ -756,7 +870,12 @@ class WS2812FX {
|
||||
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element
|
||||
friend class Segment_runtime;
|
||||
|
||||
uint16_t realPixelIndex(uint16_t i);
|
||||
ColorTransition transitions[MAX_NUM_TRANSITIONS]; //12 bytes per element
|
||||
friend class ColorTransition;
|
||||
|
||||
uint16_t
|
||||
realPixelIndex(uint16_t i),
|
||||
transitionProgress(uint8_t tNr);
|
||||
};
|
||||
|
||||
//10 names per line
|
||||
@ -764,7 +883,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([
|
||||
"Solid","Blink","Breathe","Wipe","Wipe Random","Random Colors","Sweep","Dynamic","Colorloop","Rainbow",
|
||||
"Scan","Scan Dual","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd",
|
||||
"Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","Blink Rainbow","Android","Chase","Chase Random",
|
||||
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Red & Blue","Stream",
|
||||
"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Running 2","Aurora","Stream",
|
||||
"Scanner","Lighthouse","Fireworks","Rain","Merry Christmas","Fire Flicker","Gradient","Loading","Police","Police All",
|
||||
"Two Dots","Two Areas","Circus","Halloween","Tri Chase","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet",
|
||||
"Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise",
|
||||
@ -782,7 +901,7 @@ const char JSON_palette_names[] PROGMEM = R"=====([
|
||||
"Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64",
|
||||
"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn",
|
||||
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura",
|
||||
"Aurora","Atlantica","C9 2","C9 New","Temperature"
|
||||
"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2"
|
||||
])=====";
|
||||
|
||||
#endif
|
||||
|
@ -85,23 +85,31 @@ void WS2812FX::service() {
|
||||
// segment's buffers are cleared
|
||||
SEGENV.resetIfRequired();
|
||||
|
||||
if (SEGMENT.isActive())
|
||||
if (!SEGMENT.isActive()) continue;
|
||||
|
||||
if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) //last is temporary
|
||||
{
|
||||
if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) //last is temporary
|
||||
{
|
||||
if (SEGMENT.grouping == 0) SEGMENT.grouping = 1; //sanity check
|
||||
doShow = true;
|
||||
uint16_t delay = FRAMETIME;
|
||||
if (SEGMENT.grouping == 0) SEGMENT.grouping = 1; //sanity check
|
||||
doShow = true;
|
||||
uint16_t delay = FRAMETIME;
|
||||
|
||||
if (!SEGMENT.getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen
|
||||
_virtualSegmentLength = SEGMENT.virtualLength();
|
||||
handle_palette();
|
||||
delay = (this->*_mode[SEGMENT.mode])(); //effect function
|
||||
if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++;
|
||||
if (!SEGMENT.getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen
|
||||
_virtualSegmentLength = SEGMENT.virtualLength();
|
||||
_bri_t = SEGMENT.opacity; _colors_t[0] = SEGMENT.colors[0]; _colors_t[1] = SEGMENT.colors[1]; _colors_t[2] = SEGMENT.colors[2];
|
||||
if (!IS_SEGMENT_ON) _bri_t = 0;
|
||||
for (uint8_t t = 0; t < MAX_NUM_TRANSITIONS; t++) {
|
||||
if ((transitions[t].segment & 0x3F) != i) continue;
|
||||
uint8_t slot = transitions[t].segment >> 6;
|
||||
if (slot == 0) _bri_t = transitions[t].currentBri();
|
||||
_colors_t[slot] = transitions[t].currentColor(SEGMENT.colors[slot]);
|
||||
}
|
||||
|
||||
SEGENV.next_time = nowUp + delay;
|
||||
for (uint8_t c = 0; c < 3; c++) _colors_t[c] = gamma32(_colors_t[c]);
|
||||
handle_palette();
|
||||
delay = (this->*_mode[SEGMENT.mode])(); //effect function
|
||||
if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++;
|
||||
}
|
||||
|
||||
SEGENV.next_time = nowUp + delay;
|
||||
}
|
||||
}
|
||||
_virtualSegmentLength = 0;
|
||||
@ -165,17 +173,12 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0;
|
||||
if (SEGLEN) {//from segment
|
||||
|
||||
//color_blend(getpixel, col, SEGMENT.opacity); (pseudocode for future blending of segments)
|
||||
if (IS_SEGMENT_ON)
|
||||
{
|
||||
if (SEGMENT.opacity < 255) {
|
||||
col.R = scale8(col.R, SEGMENT.opacity);
|
||||
col.G = scale8(col.G, SEGMENT.opacity);
|
||||
col.B = scale8(col.B, SEGMENT.opacity);
|
||||
col.W = scale8(col.W, SEGMENT.opacity);
|
||||
}
|
||||
} else {
|
||||
col = BLACK;
|
||||
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
|
||||
if (_bri_t < 255) {
|
||||
col.R = scale8(col.R, _bri_t);
|
||||
col.G = scale8(col.G, _bri_t);
|
||||
col.B = scale8(col.B, _bri_t);
|
||||
col.W = scale8(col.W, _bri_t);
|
||||
}
|
||||
|
||||
/* Set all the pixels in the group, ensuring _skipFirstMode is honored */
|
||||
@ -347,7 +350,7 @@ uint8_t WS2812FX::getPaletteCount()
|
||||
return 13 + GRADIENT_PALETTE_COUNT;
|
||||
}
|
||||
|
||||
//TODO transitions
|
||||
//TODO effect transitions
|
||||
|
||||
|
||||
bool WS2812FX::setEffectConfig(uint8_t m, uint8_t s, uint8_t in, uint8_t p) {
|
||||
@ -394,12 +397,16 @@ void WS2812FX::setColor(uint8_t slot, uint32_t c) {
|
||||
if (applyToAllSelected) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
|
||||
{
|
||||
if (_segments[i].isSelected()) _segments[i].colors[slot] = c;
|
||||
if (_segments[i].isSelected()) {
|
||||
_segments[i].setColor(slot, c, i);
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!applyToAllSelected || !applied) {
|
||||
_segments[getMainSegmentId()].colors[slot] = c;
|
||||
uint8_t mainseg = getMainSegmentId();
|
||||
_segments[mainseg].setColor(slot, c, mainseg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,6 +609,11 @@ void WS2812FX::setShowCallback(show_callback cb)
|
||||
_callback = cb;
|
||||
}
|
||||
|
||||
void WS2812FX::setTransition(uint16_t t)
|
||||
{
|
||||
_transitionDur = t;
|
||||
}
|
||||
|
||||
void WS2812FX::setTransitionMode(bool t)
|
||||
{
|
||||
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled
|
||||
@ -617,24 +629,26 @@ void WS2812FX::setTransitionMode(bool t)
|
||||
/*
|
||||
* color blend function
|
||||
*/
|
||||
uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blend) {
|
||||
uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
|
||||
if(blend == 0) return color1;
|
||||
if(blend == 255) return color2;
|
||||
uint16_t blendmax = b16 ? 0xFFFF : 0xFF;
|
||||
if(blend == blendmax) return color2;
|
||||
uint8_t shift = b16 ? 16 : 8;
|
||||
|
||||
uint32_t w1 = (color1 >> 24) & 0xff;
|
||||
uint32_t r1 = (color1 >> 16) & 0xff;
|
||||
uint32_t g1 = (color1 >> 8) & 0xff;
|
||||
uint32_t b1 = color1 & 0xff;
|
||||
uint32_t w1 = (color1 >> 24) & 0xFF;
|
||||
uint32_t r1 = (color1 >> 16) & 0xFF;
|
||||
uint32_t g1 = (color1 >> 8) & 0xFF;
|
||||
uint32_t b1 = color1 & 0xFF;
|
||||
|
||||
uint32_t w2 = (color2 >> 24) & 0xff;
|
||||
uint32_t r2 = (color2 >> 16) & 0xff;
|
||||
uint32_t g2 = (color2 >> 8) & 0xff;
|
||||
uint32_t b2 = color2 & 0xff;
|
||||
uint32_t w2 = (color2 >> 24) & 0xFF;
|
||||
uint32_t r2 = (color2 >> 16) & 0xFF;
|
||||
uint32_t g2 = (color2 >> 8) & 0xFF;
|
||||
uint32_t b2 = color2 & 0xFF;
|
||||
|
||||
uint32_t w3 = ((w2 * blend) + (w1 * (255 - blend))) >> 8;
|
||||
uint32_t r3 = ((r2 * blend) + (r1 * (255 - blend))) >> 8;
|
||||
uint32_t g3 = ((g2 * blend) + (g1 * (255 - blend))) >> 8;
|
||||
uint32_t b3 = ((b2 * blend) + (b1 * (255 - blend))) >> 8;
|
||||
uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift;
|
||||
uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift;
|
||||
uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift;
|
||||
uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift;
|
||||
|
||||
return ((w3 << 24) | (r3 << 16) | (g3 << 8) | (b3));
|
||||
}
|
||||
@ -1041,4 +1055,4 @@ uint32_t WS2812FX::gamma32(uint32_t color)
|
||||
return ((w << 24) | (r << 16) | (g << 8) | (b));
|
||||
}
|
||||
|
||||
uint16_t WS2812FX::_usedSegmentData = 0;
|
||||
WS2812FX* WS2812FX::instance = nullptr;
|
@ -71,6 +71,11 @@ void deserializeConfig() {
|
||||
if (apHide > 1) apHide = 1;
|
||||
|
||||
CJSON(apBehavior, ap[F("behav")]);
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
JsonObject ethernet = doc[F("eth")];
|
||||
CJSON(ethernetType, ethernet[F("type")]);
|
||||
#endif
|
||||
|
||||
/*
|
||||
JsonArray ap_ip = ap[F("ip")];
|
||||
@ -272,6 +277,7 @@ void deserializeConfig() {
|
||||
CJSON(countdownMin, cntdwn_goal[4]);
|
||||
CJSON(countdownSec, cntdwn_goal[5]);
|
||||
CJSON(macroCountdown, cntdwn[F("macro")]);
|
||||
setCountdown();
|
||||
|
||||
JsonArray timers = tm[F("ins")];
|
||||
uint8_t it = 0;
|
||||
@ -382,6 +388,11 @@ void serializeConfig() {
|
||||
wifi[F("sleep")] = !noWifiSleep;
|
||||
wifi[F("phy")] = 1;
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
JsonObject ethernet = doc.createNestedObject("eth");
|
||||
ethernet[F("type")] = ethernetType;
|
||||
#endif
|
||||
|
||||
JsonObject hw = doc.createNestedObject("hw");
|
||||
|
||||
JsonObject hw_led = hw.createNestedObject("led");
|
||||
|
@ -33,6 +33,11 @@ void colorFromUint24(uint32_t in, bool secondary)
|
||||
}
|
||||
}
|
||||
|
||||
//store color components in uint32_t
|
||||
uint32_t colorFromRgbw(byte* rgbw) {
|
||||
return (rgbw[0] << 16) + (rgbw[1] << 8) + rgbw[2] + (rgbw[3] << 24);
|
||||
}
|
||||
|
||||
//relatively change white brightness, minumum A=5
|
||||
void relativeChangeWhite(int8_t amount, byte lowerBoundary)
|
||||
{
|
||||
|
@ -132,6 +132,10 @@
|
||||
#define BTN_TYPE_SWITCH 4 //not implemented
|
||||
#define BTN_TYPE_SWITCH_ACT_HIGH 5 //not implemented
|
||||
|
||||
//Ethernet board types
|
||||
#define WLED_ETH_NONE 0
|
||||
#define WLED_ETH_WT32_ETH01 1
|
||||
#define WLED_ETH_ESP32_POE 2
|
||||
|
||||
//Hue error codes
|
||||
#define HUE_ERROR_INACTIVE 0
|
||||
|
@ -454,9 +454,8 @@ function populateInfo(i)
|
||||
else if (pwr > 0) {pwr = 50 * Math.round(pwr/50); pwru = pwr + " mA";}
|
||||
var urows="";
|
||||
if (i.u) {
|
||||
for (var k = 0; k < i.u.length; k++)
|
||||
for (const [k, val] of Object.entries(i.u))
|
||||
{
|
||||
var val = i.u[k];
|
||||
if (val[1]) {
|
||||
urows += inforow(k,val[0],val[1]);
|
||||
} else {
|
||||
@ -1335,10 +1334,13 @@ function lock(e) {
|
||||
|
||||
function move(e) {
|
||||
if(!locked || pcMode) return;
|
||||
var dx = unify(e).clientX - x0, s = Math.sign(dx),
|
||||
f = +(s*dx/w).toFixed(2);
|
||||
var clientX = unify(e).clientX;
|
||||
var dx = clientX - x0;
|
||||
var s = Math.sign(dx);
|
||||
var f = +(s*dx/w).toFixed(2);
|
||||
|
||||
if((iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) &&
|
||||
if((clientX != 0) &&
|
||||
(iSlide > 0 || s < 0) && (iSlide < N - 1 || s > 0) &&
|
||||
f > 0.12 &&
|
||||
d.getElementsByClassName("tabcontent")[iSlide].scrollTop == scrollS) {
|
||||
_C.style.setProperty('--i', iSlide -= s);
|
||||
|
@ -46,9 +46,9 @@
|
||||
Enable ArduinoOTA: <input type="checkbox" name="AO"><br>
|
||||
<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-&-About" target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits" target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
A huge thank you to everyone who helped me create WLED!<br><br>
|
||||
(c) 2016-2020 Christian Schwinne <br>
|
||||
(c) 2016-2021 Christian Schwinne <br>
|
||||
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br>
|
||||
Server message: <span class="sip"> Response error! </span><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button>
|
||||
|
@ -53,7 +53,7 @@ Type:
|
||||
<div id=xp>Port: <input name="EP" type="number" min="1" max="65535" value="5568" class="d5" required><br></div>
|
||||
Multicast: <input type="checkbox" name="EM"><br>
|
||||
Start universe: <input name="EU" type="number" min="0" max="63999" required><br>
|
||||
<i>Reboot required.</i> Check out <a href="https://github.com/ahodges9/LedFx" target="_blank">LedFx</a>!<br>
|
||||
<i>Reboot required.</i> Check out <a href="https://github.com/LedFx/LedFx" target="_blank">LedFx</a>!<br>
|
||||
Skip out-of-sequence packets: <input type="checkbox" name="ES"><br>
|
||||
DMX start address: <input name="DA" type="number" min="0" max="510" required><br>
|
||||
DMX mode:
|
||||
|
@ -63,7 +63,13 @@
|
||||
<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>
|
||||
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="1">WT32-ETH01</option>
|
||||
<option value="2">ESP32-POE</option></select><br><br></div>
|
||||
<hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Connect</button>
|
||||
</form>
|
||||
|
@ -34,6 +34,7 @@ void serializeConfigSec();
|
||||
//colors.cpp
|
||||
void colorFromUint32(uint32_t in, bool secondary = false);
|
||||
void colorFromUint24(uint32_t in, bool secondary = false);
|
||||
uint32_t colorFromRgbw(byte* rgbw);
|
||||
void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0);
|
||||
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
|
||||
void colorKtoRGB(uint16_t kelvin, byte* rgb);
|
||||
@ -109,7 +110,7 @@ void setValuesFromMainSeg();
|
||||
void resetTimebase();
|
||||
void toggleOnOff();
|
||||
void setAllLeds();
|
||||
void setLedsStandard(bool justColors = false);
|
||||
void setLedsStandard();
|
||||
bool colorChanged();
|
||||
void colorUpdated(int callMode);
|
||||
void updateInterfaces(uint8_t callMode);
|
||||
|
@ -61,9 +61,11 @@ 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><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">
|
||||
Save & Connect</button></form></body></html>)=====";
|
||||
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="1">WT32-ETH01</option><option value="2">ESP32-POE
|
||||
</option></select><br><br></div><hr><button type="button" onclick="B()">Back
|
||||
</button><button type="submit">Save & Connect</button></form></body></html>)=====";
|
||||
|
||||
|
||||
// Autogenerated from wled00/data/settings_leds.htm, do not edit!!
|
||||
@ -236,7 +238,7 @@ Reboot required to apply changes.</i><h3>Realtime</h3>Receive UDP realtime:
|
||||
id="xp">Port: <input name="EP" type="number" min="1" max="65535" value="5568"
|
||||
class="d5" required><br></div>Multicast: <input type="checkbox" name="EM"><br>
|
||||
Start universe: <input name="EU" type="number" min="0" max="63999" required><br>
|
||||
<i>Reboot required.</i> Check out <a href="https://github.com/ahodges9/LedFx"
|
||||
<i>Reboot required.</i> Check out <a href="https://github.com/LedFx/LedFx"
|
||||
target="_blank">LedFx</a>!<br>Skip out-of-sequence packets: <input
|
||||
type="checkbox" name="ES"><br>DMX start address: <input name="DA" type="number"
|
||||
min="0" max="510" required><br>DMX mode: <select name="DM"><option value="0">
|
||||
@ -364,10 +366,10 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form
|
||||
</button><br>Enable ArduinoOTA: <input type="checkbox" name="AO"><br><h3>About
|
||||
</h3><a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a>
|
||||
version 0.11.1<br><br><a
|
||||
href="https://github.com/Aircoookie/WLED/wiki/Contributors-&-About"
|
||||
href="https://github.com/Aircoookie/WLED/wiki/Contributors-and-credits"
|
||||
target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
A huge thank you to everyone who helped me create WLED!<br><br>
|
||||
(c) 2016-2020 Christian Schwinne<br><i>Licensed under the <a
|
||||
(c) 2016-2021 Christian Schwinne<br><i>Licensed under the <a
|
||||
href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">
|
||||
MIT license</a></i><br><br>Server message: <span class="sip">Response error!
|
||||
</span><hr><button type="button" onclick="B()">Back</button><button
|
||||
|
1345
wled00/html_ui.h
@ -23,13 +23,13 @@ void deserializeSegment(JsonObject elem, byte it)
|
||||
|
||||
int segbri = elem["bri"] | -1;
|
||||
if (segbri == 0) {
|
||||
seg.setOption(SEG_OPTION_ON, 0);
|
||||
seg.setOption(SEG_OPTION_ON, 0, id);
|
||||
} else if (segbri > 0) {
|
||||
seg.opacity = segbri;
|
||||
seg.setOption(SEG_OPTION_ON, 1);
|
||||
seg.setOpacity(segbri, id);
|
||||
seg.setOption(SEG_OPTION_ON, 1, id);
|
||||
}
|
||||
|
||||
seg.setOption(SEG_OPTION_ON, elem["on"] | seg.getOption(SEG_OPTION_ON));
|
||||
seg.setOption(SEG_OPTION_ON, elem["on"] | seg.getOption(SEG_OPTION_ON), id);
|
||||
|
||||
JsonArray colarr = elem[F("col")];
|
||||
if (!colarr.isNull())
|
||||
@ -45,7 +45,7 @@ void deserializeSegment(JsonObject elem, byte it)
|
||||
if (hexCol == nullptr) { //Kelvin color temperature (or invalid), e.g 2400
|
||||
int kelvin = colarr[i] | -1;
|
||||
if (kelvin < 0) continue;
|
||||
if (kelvin == 0) seg.colors[i] = 0;
|
||||
if (kelvin == 0) seg.setColor(i, 0, id);
|
||||
if (kelvin > 0) colorKtoRGB(kelvin, brgbw);
|
||||
colValid = true;
|
||||
} else { //HEX string, e.g. "FFAA00"
|
||||
@ -57,7 +57,7 @@ void deserializeSegment(JsonObject elem, byte it)
|
||||
if (sz == 0) continue; //do nothing on empty array
|
||||
|
||||
byte cp = copyArray(colX, rgbw, 4);
|
||||
if (cp == 1 && rgbw[0] == 0) seg.colors[i] = 0;
|
||||
if (cp == 1 && rgbw[0] == 0) seg.setColor(i, 0, id);
|
||||
colValid = true;
|
||||
}
|
||||
|
||||
@ -66,8 +66,8 @@ void deserializeSegment(JsonObject elem, byte it)
|
||||
{
|
||||
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 (=> no transition!)
|
||||
seg.colors[i] = ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF)));
|
||||
} 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
|
||||
}
|
||||
}
|
||||
@ -168,6 +168,7 @@ bool deserializeState(JsonObject root)
|
||||
{
|
||||
transitionDelay = tr;
|
||||
transitionDelay *= 100;
|
||||
transitionDelayTemp = transitionDelay;
|
||||
}
|
||||
|
||||
tr = root[F("tt")] | -1;
|
||||
@ -177,6 +178,7 @@ bool deserializeState(JsonObject root)
|
||||
transitionDelayTemp *= 100;
|
||||
jsonTransitionOnce = true;
|
||||
}
|
||||
strip.setTransition(transitionDelayTemp);
|
||||
|
||||
int cy = root[F("pl")] | -2;
|
||||
if (cy > -2) presetCyclingEnabled = (cy >= 0);
|
||||
|
@ -51,24 +51,20 @@ void setAllLeds() {
|
||||
}
|
||||
if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY)
|
||||
{
|
||||
colorRGBtoRGBW(colT);
|
||||
colorRGBtoRGBW(colSecT);
|
||||
colorRGBtoRGBW(col);
|
||||
colorRGBtoRGBW(colSec);
|
||||
}
|
||||
strip.setColor(0, col[0], col[1], col[2], col[3]);
|
||||
strip.setColor(1, colSec[0], colSec[1], colSec[2], colSec[3]);
|
||||
if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY)
|
||||
{
|
||||
col[3] = 0; colSec[3] = 0;
|
||||
}
|
||||
strip.setColor(0, colT[0], colT[1], colT[2], colT[3]);
|
||||
strip.setColor(1, colSecT[0], colSecT[1], colSecT[2], colSecT[3]);
|
||||
}
|
||||
|
||||
|
||||
void setLedsStandard(bool justColors)
|
||||
void setLedsStandard()
|
||||
{
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
colOld[i] = col[i];
|
||||
colT[i] = col[i];
|
||||
colSecOld[i] = colSec[i];
|
||||
colSecT[i] = colSec[i];
|
||||
}
|
||||
if (justColors) return;
|
||||
briOld = bri;
|
||||
briT = bri;
|
||||
setAllLeds();
|
||||
@ -145,7 +141,7 @@ void colorUpdated(int callMode)
|
||||
}
|
||||
if (briT == 0)
|
||||
{
|
||||
setLedsStandard(true); //do not color transition if starting from off
|
||||
//setLedsStandard(true); //do not color transition if starting from off!
|
||||
if (callMode != NOTIFIER_CALL_MODE_NOTIFICATION) resetTimebase(); //effect start from beginning
|
||||
}
|
||||
|
||||
@ -160,15 +156,11 @@ void colorUpdated(int callMode)
|
||||
//set correct delay if not using notification delay
|
||||
if (callMode != NOTIFIER_CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay;
|
||||
jsonTransitionOnce = false;
|
||||
strip.setTransition(transitionDelayTemp);
|
||||
if (transitionDelayTemp == 0) {setLedsStandard(); strip.trigger(); return;}
|
||||
|
||||
if (transitionActive)
|
||||
{
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
colOld[i] = colT[i];
|
||||
colSecOld[i] = colSecT[i];
|
||||
}
|
||||
briOld = briT;
|
||||
tperLast = 0;
|
||||
}
|
||||
@ -177,6 +169,7 @@ void colorUpdated(int callMode)
|
||||
transitionStartTime = millis();
|
||||
} else
|
||||
{
|
||||
strip.setTransition(0);
|
||||
setLedsStandard();
|
||||
strip.trigger();
|
||||
}
|
||||
@ -222,11 +215,6 @@ void handleTransitions()
|
||||
}
|
||||
if (tper - tperLast < 0.004) return;
|
||||
tperLast = tper;
|
||||
for (byte i=0; i<4; i++)
|
||||
{
|
||||
colT[i] = colOld[i]+((col[i] - colOld[i])*tper);
|
||||
colSecT[i] = colSecOld[i]+((colSec[i] - colSecOld[i])*tper);
|
||||
}
|
||||
briT = briOld +((bri - briOld )*tper);
|
||||
|
||||
setAllLeds();
|
||||
|
@ -70,7 +70,7 @@ void parseLxJson(int lxValue, byte segId, bool secondary)
|
||||
} else {
|
||||
DEBUG_PRINT(F("LX: segment "));
|
||||
DEBUG_PRINTLN(segId);
|
||||
strip.getSegment(segId).colors[secondary] = ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF)));
|
||||
strip.getSegment(segId).setColor(secondary, ((rgbw[3] << 24) | ((rgbw[0]&0xFF) << 16) | ((rgbw[1]&0xFF) << 8) | ((rgbw[2]&0xFF))), segId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ bool checkCountdown()
|
||||
if (countdownMode) localTime = n - countdownTime + utcOffsetSecs;
|
||||
if (!countdownOverTriggered)
|
||||
{
|
||||
if (macroCountdown != 0) applyMacro(macroCountdown);
|
||||
if (macroCountdown != 0) applyPreset(macroCountdown);
|
||||
countdownOverTriggered = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
#ifndef PalettesWLED_h
|
||||
#define PalettesWLED_h
|
||||
|
||||
#define GRADIENT_PALETTE_COUNT 42
|
||||
#define GRADIENT_PALETTE_COUNT 43
|
||||
|
||||
const byte ib_jul01_gp[] PROGMEM = {
|
||||
0, 194, 1, 1,
|
||||
@ -631,6 +631,14 @@ const byte temperature_gp[] PROGMEM = {
|
||||
240, 80, 3, 3,
|
||||
255, 80, 3, 3};
|
||||
|
||||
const byte Aurora2[] PROGMEM = {
|
||||
0, 17, 177, 13, //Greenish
|
||||
64, 121, 242, 5, //Greenish
|
||||
128, 25, 173, 121, //Turquoise
|
||||
192, 250, 77, 127, //Pink
|
||||
255, 171, 101, 221 //Purple
|
||||
};
|
||||
|
||||
// Single array of defined cpt-city color palettes.
|
||||
// This will let us programmatically choose one based on
|
||||
// a number, rather than having to activate each explicitly
|
||||
@ -677,7 +685,8 @@ const byte* const gGradientPalettes[] PROGMEM = {
|
||||
Atlantica_gp, //51-38 Atlantica
|
||||
C9_2_gp, //52-39 C9 2
|
||||
C9_new_gp, //53-40 C9 New
|
||||
temperature_gp //54-41 Temperature
|
||||
temperature_gp, //54-41 Temperature
|
||||
Aurora2 //55-42 Aurora 2
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
bool applyPreset(byte index)
|
||||
{
|
||||
if (index == 0) return false;
|
||||
if (fileDoc) {
|
||||
errorFlag = readObjectFromFileUsingId("/presets.json", index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
|
||||
JsonObject fdo = fileDoc->as<JsonObject>();
|
||||
|
@ -52,6 +52,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
|
||||
noWifiSleep = request->hasArg(F("WS"));
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
ethernetType = request->arg(F("ETH")).toInt();
|
||||
#endif
|
||||
|
||||
char k[3]; k[2] = 0;
|
||||
for (int i = 0; i<4; i++)
|
||||
{
|
||||
@ -90,7 +94,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
saveCurrPresetCycConf = request->hasArg(F("PC"));
|
||||
turnOnAtBoot = request->hasArg(F("BO"));
|
||||
t = request->arg(F("BP")).toInt();
|
||||
if (t <= 25) bootPreset = t;
|
||||
if (t <= 250) bootPreset = t;
|
||||
strip.gammaCorrectBri = request->hasArg(F("GB"));
|
||||
strip.gammaCorrectCol = request->hasArg(F("GC"));
|
||||
|
||||
@ -236,6 +240,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
countdownHour = request->arg(F("CH")).toInt();
|
||||
countdownMin = request->arg(F("CM")).toInt();
|
||||
countdownSec = request->arg(F("CS")).toInt();
|
||||
setCountdown();
|
||||
|
||||
macroAlexaOn = request->arg(F("A0")).toInt();
|
||||
macroAlexaOff = request->arg(F("A1")).toInt();
|
||||
@ -380,7 +385,14 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
DEBUG_PRINT(F("API req: "));
|
||||
DEBUG_PRINTLN(req);
|
||||
|
||||
strip.applyToAllSelected = true;
|
||||
strip.applyToAllSelected = false;
|
||||
//snapshot to check if request changed values later, temporary.
|
||||
byte prevCol[4] = {col[0], col[1], col[2], col[3]};
|
||||
byte prevColSec[4] = {colSec[0], colSec[1], colSec[2], colSec[3]};
|
||||
byte prevEffect = effectCurrent;
|
||||
byte prevSpeed = effectSpeed;
|
||||
byte prevIntensity = effectIntensity;
|
||||
byte prevPalette = effectPalette;
|
||||
|
||||
//segment select (sets main segment)
|
||||
byte prevMain = strip.getMainSegmentId();
|
||||
@ -388,16 +400,16 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
if (pos > 0) {
|
||||
strip.mainSegment = getNumVal(&req, pos);
|
||||
}
|
||||
byte main = strip.getMainSegmentId();
|
||||
if (main != prevMain) setValuesFromMainSeg();
|
||||
byte selectedSeg = strip.getMainSegmentId();
|
||||
if (selectedSeg != prevMain) setValuesFromMainSeg();
|
||||
|
||||
pos = req.indexOf(F("SS="));
|
||||
if (pos > 0) {
|
||||
byte t = getNumVal(&req, pos);
|
||||
if (t < strip.getMaxSegments()) main = t;
|
||||
if (t < strip.getMaxSegments()) selectedSeg = t;
|
||||
}
|
||||
|
||||
WS2812FX::Segment& mainseg = strip.getSegment(main);
|
||||
WS2812FX::Segment& mainseg = strip.getSegment(selectedSeg);
|
||||
pos = req.indexOf(F("SV=")); //segment selected
|
||||
if (pos > 0) {
|
||||
byte t = getNumVal(&req, pos);
|
||||
@ -431,9 +443,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
if (pos > 0) {
|
||||
spcI = getNumVal(&req, pos);
|
||||
}
|
||||
strip.setSegment(main, startI, stopI, grpI, spcI);
|
||||
|
||||
main = strip.getMainSegmentId();
|
||||
strip.setSegment(selectedSeg, startI, stopI, grpI, spcI);
|
||||
|
||||
//set presets
|
||||
pos = req.indexOf(F("P1=")); //sets first preset for cycle
|
||||
@ -531,7 +541,12 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
if (pos > 0) {
|
||||
byte t[4];
|
||||
colorFromDecOrHexString(t, (char*)req.substring(pos + 3).c_str());
|
||||
strip.setColor(2, t[0], t[1], t[2], t[3]);
|
||||
if (selectedSeg != strip.getMainSegmentId()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
//set to random hue SR=0->1st SR=1->2nd
|
||||
@ -646,19 +661,19 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
|
||||
//Segment reverse
|
||||
pos = req.indexOf(F("RV="));
|
||||
if (pos > 0) strip.getSegment(main).setOption(SEG_OPTION_REVERSED, req.charAt(pos+3) != '0');
|
||||
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(main).setOption(SEG_OPTION_MIRROR, req.charAt(pos+3) != '0');
|
||||
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(main).setOption(SEG_OPTION_ON, segbri);
|
||||
strip.getSegment(selectedSeg).setOption(SEG_OPTION_ON, segbri, selectedSeg);
|
||||
if (segbri) {
|
||||
strip.getSegment(main).opacity = segbri;
|
||||
strip.getSegment(selectedSeg).setOpacity(segbri, selectedSeg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -715,12 +730,44 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
}
|
||||
//you can add more if you need
|
||||
|
||||
//apply to all selected manually to prevent #1618. Temporary
|
||||
bool col0Changed = false, col1Changed = false;
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (col[i] != prevCol[i]) col0Changed = true;
|
||||
if (colSec[i] != prevColSec[i]) col1Changed = true;
|
||||
}
|
||||
for (uint8_t i = 0; i < strip.getMaxSegments(); i++)
|
||||
{
|
||||
WS2812FX::Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isSelected()) continue;
|
||||
if (effectCurrent != prevEffect) seg.mode = effectCurrent;
|
||||
if (effectSpeed != prevSpeed) seg.speed = effectSpeed;
|
||||
if (effectIntensity != prevIntensity) seg.intensity = effectIntensity;
|
||||
if (effectPalette != prevPalette) seg.palette = effectPalette;
|
||||
}
|
||||
|
||||
if (col0Changed) {
|
||||
if (selectedSeg == strip.getMainSegmentId()) {
|
||||
strip.applyToAllSelected = true;
|
||||
strip.setColor(0, colorFromRgbw(col));
|
||||
}
|
||||
}
|
||||
if (col1Changed) {
|
||||
if (selectedSeg == strip.getMainSegmentId()) {
|
||||
strip.applyToAllSelected = true;
|
||||
strip.setColor(1, colorFromRgbw(colSec));
|
||||
}
|
||||
}
|
||||
//end of temporary fix code
|
||||
|
||||
if (!apply) return true; //when called by JSON API, do not call colorUpdated() here
|
||||
|
||||
//internal call, does not send XML response
|
||||
pos = req.indexOf(F("IN"));
|
||||
if (pos < 1) XML_response(request);
|
||||
|
||||
strip.applyToAllSelected = false;
|
||||
|
||||
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
|
||||
colorUpdated((pos > 0) ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE);
|
||||
|
||||
|
@ -19,6 +19,9 @@
|
||||
#ifdef USERMOD_BUZZER
|
||||
#include "../usermods/buzzer/usermod_v2_buzzer.h"
|
||||
#endif
|
||||
#ifdef USERMOD_SENSORSTOMQTT
|
||||
#include "usermod_v2_SensorsToMqtt.h"
|
||||
#endif
|
||||
|
||||
// BME280 v2 usermod. Define "USERMOD_BME280" in my_config.h
|
||||
#ifdef USERMOD_BME280
|
||||
@ -27,7 +30,7 @@
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
/*
|
||||
/*
|
||||
* Add your usermod class name here
|
||||
* || || ||
|
||||
* \/ \/ \/
|
||||
|
@ -10,6 +10,49 @@ WLED::WLED()
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
// settings for various ethernet boards
|
||||
typedef struct EthernetSettings {
|
||||
uint8_t eth_address;
|
||||
int eth_power;
|
||||
int eth_mdc;
|
||||
int eth_mdio;
|
||||
eth_phy_type_t eth_type;
|
||||
eth_clock_mode_t eth_clk_mode;
|
||||
} ethernet_settings;
|
||||
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// turns all LEDs off and restarts ESP
|
||||
void WLED::reset()
|
||||
{
|
||||
@ -393,7 +436,18 @@ void WLED::initConnection()
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
ETH.begin();
|
||||
// Only initialize ethernet board if not NONE
|
||||
if (ethernetType != WLED_ETH_NONE) {
|
||||
ethernet_settings es = ethernetBoards[ethernetType];
|
||||
ETH.begin(
|
||||
(uint8_t) es.eth_address,
|
||||
(int) es.eth_power,
|
||||
(int) es.eth_mdc,
|
||||
(int) es.eth_mdio,
|
||||
(eth_phy_type_t) es.eth_type,
|
||||
(eth_clock_mode_t) es.eth_clk_mode
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
WiFi.disconnect(true); // close old connections
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2012210
|
||||
#define VERSION 2101130
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@ -204,6 +204,9 @@ WLED_GLOBAL IPAddress staticIP _INIT_N((( 0, 0, 0, 0))); // static IP
|
||||
WLED_GLOBAL IPAddress staticGateway _INIT_N((( 0, 0, 0, 0))); // gateway (router) IP
|
||||
WLED_GLOBAL IPAddress staticSubnet _INIT_N(((255, 255, 255, 0))); // most common subnet in home networks
|
||||
WLED_GLOBAL bool noWifiSleep _INIT(false); // disabling modem sleep modes will increase heat output and power usage, but may help with connection issues
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
WLED_GLOBAL int ethernetType _INIT(WLED_ETH_ESP32_POE); // ethernet board type
|
||||
#endif
|
||||
|
||||
// LED CONFIG
|
||||
WLED_GLOBAL uint16_t ledCount _INIT(30); // overcurrent prevented by ABL
|
||||
@ -342,11 +345,7 @@ WLED_GLOBAL bool interfacesInited _INIT(false);
|
||||
WLED_GLOBAL bool wasConnected _INIT(false);
|
||||
|
||||
// color
|
||||
WLED_GLOBAL byte colOld[] _INIT_N(({ 0, 0, 0, 0 })); // color before transition
|
||||
WLED_GLOBAL byte colT[] _INIT_N(({ 0, 0, 0, 0 })); // color that is currently displayed on the LEDs
|
||||
WLED_GLOBAL byte colIT[] _INIT_N(({ 0, 0, 0, 0 })); // color that was last sent to LEDs
|
||||
WLED_GLOBAL byte colSecT[] _INIT_N(({ 0, 0, 0, 0 }));
|
||||
WLED_GLOBAL byte colSecOld[] _INIT_N(({ 0, 0, 0, 0 }));
|
||||
WLED_GLOBAL byte colSecIT[] _INIT_N(({ 0, 0, 0, 0 }));
|
||||
|
||||
WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same
|
||||
|
@ -219,6 +219,12 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('v',SET_F("AC"),apChannel);
|
||||
sappend('c',SET_F("WS"),noWifiSleep);
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
sappend('i',SET_F("ETH"),ethernetType);
|
||||
#else
|
||||
//hide ethernet setting if not compiled in
|
||||
oappend(SET_F("document.getElementById('ethd').style.display='none';"));
|
||||
#endif
|
||||
|
||||
if (Network.isConnected()) //is connected
|
||||
{
|
||||
|