diff --git a/CHANGELOG.md b/CHANGELOG.md
index 48406c74..9acec991 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,29 @@
### Development versions after the 0.10.2 release
+#### Build 2010020
+
+- Fixed interaction of `T` and `NL` HTTP API commands (#1214)
+- Fixed an issue where Sunrise mode nightlight does not activate if toggled on simultaneously
+
+#### Build 2009291
+
+- Fixed MQTT bootloop (no F() macro, #1199)
+
+#### Build 2009290
+
+- Added basic DDP protocol support
+- Added Washing Machine effect (PR #1208)
+
+#### Build 2009260
+
+- Added Loxone parser (PR #1185)
+- Added support for kelvin input via `K=` HTTP and `"col":[[val]]` JSON API calls
+- Added supplementary UDP socket (#1205)
+- TMP2.net receivable by default
+- UDP sockets accept HTTP and JSON API commands
+- Fixed missing timezones (#1201)
+
#### Build 2009202
- Fixed LPD8806 compilation
diff --git a/platformio.ini b/platformio.ini
index 55045408..7b53b2cd 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -377,3 +377,43 @@ build_flags = ${common.build_flags_esp8266} ${common.debug_flags} ${common.build
extends = env:esp32dev
; build_type = debug
build_flags = ${common.build_flags_esp32} ${common.debug_flags} ${common.build_flags_all_features}
+
+# ------------------------------------------------------------------------------
+# codm pixel controller board configurations
+# ------------------------------------------------------------------------------
+
+[env:codm-controller-0.4]
+board = esp_wroom_02
+platform = ${common.platform_wled_default}
+board_build.ldscript = ${common.ldscript_2m1m}
+build_flags = ${common.build_flags_esp8266} -D LEDPIN=3
+
+[env:codm-controller-0.4-WS2801]
+board = esp_wroom_02
+platform = ${common.platform_latest}
+board_build.ldscript = ${common.ldscript_2m1m}
+build_flags = ${common.build_flags_esp8266} -D USE_WS2801 -D CLKPIN=13 -D DATAPIN=3
+
+[env:codm-controller-0.4-APA102]
+board = esp_wroom_02
+platform = ${common.platform_latest}
+board_build.ldscript = ${common.ldscript_2m1m}
+build_flags = ${common.build_flags_esp8266} -D USE_APA102 -D CLKPIN=13 -D DATAPIN=3
+
+[env:codm-controller-0.5]
+board = esp_wroom_02
+platform = ${common.platform_wled_default}
+board_build.ldscript = ${common.ldscript_2m1m}
+build_flags = ${common.build_flags_esp8266}
+
+[env:codm-controller-0.5-WS2801]
+board = esp_wroom_02
+platform = ${common.platform_latest}
+board_build.ldscript = ${common.ldscript_2m1m}
+build_flags = ${common.build_flags_esp8266} -D USE_WS2801 #-D CLKPIN=0 -D DATAPIN=2
+
+[env:codm-controller-0.5-APA102]
+board = esp_wroom_02
+platform = ${common.platform_latest}
+board_build.ldscript = ${common.ldscript_2m1m}
+build_flags = ${common.build_flags_esp8266} -D USE_APA102 #-D CLKPIN=0 -D DATAPIN=2
diff --git a/readme.md b/readme.md
index 2080fd45..ce83eb75 100644
--- a/readme.md
+++ b/readme.md
@@ -1,10 +1,12 @@
+
+
# Welcome to my project WLED! ✨
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index d8265c29..b3888488 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -3712,3 +3712,22 @@ uint16_t WS2812FX::mode_dancing_shadows(void)
return FRAMETIME;
}
+
+/*
+ Imitates a washing machine, rotating same waves forward, then pause, then backward.
+ By Stefan Seegel
+*/
+uint16_t WS2812FX::mode_washing_machine(void) {
+ float speed = tristate_square8(now >> 7, 90, 15);
+ float quot = 32.0f - ((float)SEGMENT.speed / 16.0f);
+ speed /= quot;
+
+ SEGENV.step += (speed * 128.0f);
+
+ for (int i=0; i> 7));
+ setPixelColor(i, color_from_palette(col, false, PALETTE_SOLID_WRAP, 3));
+ }
+
+ return FRAMETIME;
+}
diff --git a/wled00/FX.h b/wled00/FX.h
index 3a1a99e4..e3c170f7 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -107,7 +107,7 @@
#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE )
#define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED )
-#define MODE_COUNT 113
+#define MODE_COUNT 114
#define FX_MODE_STATIC 0
#define FX_MODE_BLINK 1
@@ -222,6 +222,7 @@
#define FX_MODE_FLOW 110
#define FX_MODE_CHUNCHUN 111
#define FX_MODE_DANCING_SHADOWS 112
+#define FX_MODE_WASHING_MACHINE 113
class WS2812FX {
typedef uint16_t (WS2812FX::*mode_ptr)(void);
@@ -426,6 +427,7 @@ class WS2812FX {
_mode[FX_MODE_FLOW] = &WS2812FX::mode_flow;
_mode[FX_MODE_CHUNCHUN] = &WS2812FX::mode_chunchun;
_mode[FX_MODE_DANCING_SHADOWS] = &WS2812FX::mode_dancing_shadows;
+ _mode[FX_MODE_WASHING_MACHINE] = &WS2812FX::mode_washing_machine;
_brightness = DEFAULT_BRIGHTNESS;
currentPalette = CRGBPalette16(CRGB::Black);
@@ -485,6 +487,9 @@ class WS2812FX {
gamma8(uint8_t),
get_random_wheel_index(uint8_t);
+ int8_t
+ tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec);
+
uint16_t
ablMilliampsMax,
currentMilliamps,
@@ -625,7 +630,8 @@ class WS2812FX {
mode_phased_noise(void),
mode_flow(void),
mode_chunchun(void),
- mode_dancing_shadows(void);
+ mode_dancing_shadows(void),
+ mode_washing_machine(void);
private:
NeoPixelWrapper *bus;
@@ -712,7 +718,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst",
"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow",
"Heartbeat","Pacifica","Candle Multi", "Solid Glitter","Sunrise","Phased","Twinkleup","Noise Pal", "Sine","Phased Noise",
-"Flow","Chunchun","Dancing Shadows"
+"Flow","Chunchun","Dancing Shadows","Washing Machine"
])=====";
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 20bed951..56fdb98a 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -699,6 +699,32 @@ uint16_t WS2812FX::triwave16(uint16_t in)
return 0xFFFF - (in - 0x8000)*2;
}
+/*
+ * Generates a tristate square wave w/ attac & decay
+ * @param x input value 0-255
+ * @param pulsewidth 0-127
+ * @param attdec attac & decay, max. pulsewidth / 2
+ * @returns signed waveform value
+ */
+int8_t WS2812FX::tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) {
+ int8_t a = 127;
+ if (x > 127) {
+ a = -127;
+ x -= 127;
+ }
+
+ if (x < attdec) { //inc to max
+ return (int16_t) x * a / attdec;
+ }
+ else if (x < pulsewidth - attdec) { //max
+ return a;
+ }
+ else if (x < pulsewidth) { //dec to 0
+ return (int16_t) (pulsewidth - x) * a / attdec;
+ }
+ return 0;
+}
+
/*
* Put a value 0 to 255 in to get a color value.
* The colours are a transition r -> g -> b -> back to r
@@ -844,7 +870,7 @@ void WS2812FX::handle_palette(void)
load_gradient_palette(paletteIndex -13);
}
- if (singleSegmentMode && paletteFade) //only blend if just one segment uses FastLED mode
+ if (singleSegmentMode && paletteFade && SEGENV.call > 0) //only blend if just one segment uses FastLED mode
{
nblendPaletteTowardPalette(currentPalette, targetPalette, 48);
} else
diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h
index dbb867b6..d867ef86 100644
--- a/wled00/NpbWrapper.h
+++ b/wled00/NpbWrapper.h
@@ -88,11 +88,20 @@
#define W2PIN 5 //W2 pin for analog LED strip
#undef IR_PIN
#else
+ //Enable override of Pins by using the platformio_override.ini file
//PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller
- #define RPIN 5 //R pin for analog LED strip
- #define GPIN 12 //G pin for analog LED strip
- #define BPIN 15 //B pin for analog LED strip
- #define WPIN 13 //W pin for analog LED strip
+ #ifndef RPIN
+ #define RPIN 5 //R pin for analog LED strip
+ #endif
+ #ifndef GPIN
+ #define GPIN 12 //G pin for analog LED strip
+ #endif
+ #ifndef BPIN
+ #define BPIN 15 //B pin for analog LED strip
+ #endif
+ #ifndef WPIN
+ #define WPIN 13 //W pin for analog LED strip
+ #endif
#endif
#undef RLYPIN
#define RLYPIN -1 //disable as pin 12 is used by analog LEDs
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index f76499a0..361e3e06 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -62,7 +62,31 @@ void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb
if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
}
-void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb
+void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
+{
+ float r = 0, g = 0, b = 0;
+ float temp = kelvin / 100;
+ if (temp <= 66) {
+ r = 255;
+ g = round(99.4708025861 * log(temp) - 161.1195681661);
+ if (temp <= 19) {
+ b = 0;
+ } else {
+ b = round(138.5177312231 * log((temp - 10)) - 305.0447927307);
+ }
+ } else {
+ r = round(329.698727446 * pow((temp - 60), -0.1332047592));
+ g = round(288.1221695283 * pow((temp - 60), -0.0755148492));
+ b = 255;
+ }
+ g += 15; //mod by Aircoookie, a bit less accurate but visibly less pinkish
+ rgb[0] = (uint8_t) constrain(r, 0, 255);
+ rgb[1] = (uint8_t) constrain(g, 0, 255);
+ rgb[2] = (uint8_t) constrain(b, 0, 255);
+ rgb[3] = 0;
+}
+
+void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb, bins
{
//this is only an approximation using WS2812B with gamma correction enabled
if (mired > 475) {
diff --git a/wled00/const.h b/wled00/const.h
index 73de807e..88ad4974 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -57,6 +57,7 @@
#define REALTIME_MODE_ADALIGHT 5
#define REALTIME_MODE_ARTNET 6
#define REALTIME_MODE_TPM2NET 7
+#define REALTIME_MODE_DDP 8
//realtime override modes
#define REALTIME_OVERRIDE_NONE 0
diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm
index 7c13650e..89030c3c 100644
--- a/wled00/data/settings_sync.htm
+++ b/wled00/data/settings_sync.htm
@@ -5,7 +5,7 @@ function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync-
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.DA.value == 1) d.Sf.DA.value = 0; if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} }
function SP(){var p = d.Sf.DI.value; d.getElementById("xp").style.display = (p > 0)?"none":"block"; if (p > 0) d.Sf.EP.value = p;}
-function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; }; SP();}
+function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; case 4048: d.Sf.DI.value = 4048; break; }; SP();}
function S(){GetV();SetVal();}
function GetV(){var d=document;}
@@ -31,13 +31,15 @@ Infrared remote:
IR info
WLED Broadcast
UDP Port:
+2nd Port:
Receive Brightness, Color, and Effects
Send notifications on direct change:
Send notifications on button press:
Send Alexa notifications:
Send Philips Hue change notifications:
Send Macro notifications:
-Send notifications twice:
+Send notifications twice:
+Reboot required to apply changes.
Realtime
Receive UDP realtime:
Network DMX input
@@ -45,6 +47,7 @@ Type:
Port:
diff --git a/wled00/data/settings_time.htm b/wled00/data/settings_time.htm
index c0231277..33b80af2 100644
--- a/wled00/data/settings_time.htm
+++ b/wled00/data/settings_time.htm
@@ -103,7 +103,11 @@
-
+
+
+
+
+
UTC offset: seconds (max. 18 hours)
Current local time is unknown.
diff --git a/wled00/e131.cpp b/wled00/e131.cpp
index 2656f9e2..12e2ba85 100644
--- a/wled00/e131.cpp
+++ b/wled00/e131.cpp
@@ -7,25 +7,66 @@
* E1.31 handler
*/
-void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet){
- //E1.31 protocol support
+//DDP protocol support, called by handleE131Packet
+//handles RGB data only
+void handleDDPPacket(e131_packet_t* p) {
+ int lastPushSeq = e131LastSequenceNumber[0];
+
+ //reject late packets belonging to previous frame (assuming 4 packets max. before push)
+ if (e131SkipOutOfSequence && lastPushSeq) {
+ int sn = p->sequenceNum & 0xF;
+ if (sn) {
+ if (lastPushSeq > 5) {
+ if (sn > (lastPushSeq -5) && sn < lastPushSeq) return;
+ } else {
+ if (sn > (10 + lastPushSeq) || sn < lastPushSeq) return;
+ }
+ }
+ }
+
+ uint32_t offsetLeds = htonl(p->channelOffset) /3;
+ uint16_t packetLeds = htons(p->dataLen) /3;
+ uint8_t* data = p->data;
+ uint16_t c = 0;
+ if (p->flags & DDP_TIMECODE_FLAG) c = 4; //packet has timecode flag, we do not support it, but data starts 4 bytes later
+
+ realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP);
+
+ for (uint16_t i = offsetLeds; i < offsetLeds + packetLeds; i++) {
+ setRealtimePixel(i, data[c++], data[c++], data[c++], 0);
+ }
+
+ bool push = p->flags & DDP_PUSH_FLAG;
+ if (push) {
+ e131NewData = true;
+ byte sn = p->sequenceNum & 0xF;
+ if (sn) e131LastSequenceNumber[0] = sn;
+ }
+}
+
+//E1.31 and Art-Net protocol support
+void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
uint16_t uni = 0, dmxChannels = 0;
uint8_t* e131_data = nullptr;
uint8_t seq = 0, mde = REALTIME_MODE_E131;
- if (isArtnet)
+ if (protocol == P_ARTNET)
{
uni = p->art_universe;
dmxChannels = htons(p->art_length);
e131_data = p->art_data;
seq = p->art_sequence_number;
mde = REALTIME_MODE_ARTNET;
- } else {
+ } else if (protocol == P_E131) {
uni = htons(p->universe);
dmxChannels = htons(p->property_value_count) -1;
e131_data = p->property_values;
seq = p->sequence_number;
+ } else { //DDP
+ realtimeIP = clientIP;
+ handleDDPPacket(p);
+ return;
}
#ifdef WLED_ENABLE_DMX
@@ -133,7 +174,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet){
}
} else {
// All subsequent universes start at the first channel.
- dmxOffset = isArtnet ? 0 : 1;
+ dmxOffset = (protocol == P_ARTNET) ? 0 : 1;
uint16_t ledsInFirstUniverse = (MAX_CHANNELS_PER_UNIVERSE - DMXAddress) / 3;
previousLeds = ledsInFirstUniverse + (previousUniverses - 1) * MAX_LEDS_PER_UNIVERSE;
}
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h
index eb8ec876..1d8f32ef 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -30,6 +30,7 @@ void colorFromUint32(uint32_t in, bool secondary = false);
void colorFromUint24(uint32_t in, bool secondary = false);
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);
void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb
void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO
@@ -43,7 +44,7 @@ void initDMX();
void handleDMX();
//e131.cpp
-void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet);
+void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol);
//file.cpp
bool handleFileRead(AsyncWebServerRequest*, String path);
@@ -106,6 +107,10 @@ void updateInterfaces(uint8_t callMode);
void handleTransitions();
void handleNightlight();
+//lx_parser.cpp
+bool parseLx(int lxValue, byte* rgbw);
+void parseLxJson(int lxValue, byte segId, bool secondary);
+
//mqtt.cpp
bool initMqtt();
void publishMqtt();
diff --git a/wled00/html_settings.h b/wled00/html_settings.h
index 22ed90ca..98e0c922 100644
--- a/wled00/html_settings.h
+++ b/wled00/html_settings.h
@@ -233,23 +233,23 @@ var t = typeof s[i];
if (gId(fk)) { //already exists
if(t === 'boolean')
{
- gId(fk).checked = s[i];
+gId(fk).checked = s[i];
} else {
- gId(fk).value = s[i];
+gId(fk).value = s[i];
}
if (gId(fk).previousElementSibling.matches('.l')) {
- gId(fk).previousElementSibling.innerHTML = lb;
+gId(fk).previousElementSibling.innerHTML = lb;
}
} else {
if(t === 'boolean')
{
- str += `${lb}:
`;
+str += `${lb}:
`;
} else if (t === 'number')
{
- str += `${lb}:
`;
+str += `${lb}:
`;
} else if (t === 'string')
{
- str += `${lb}:
`;
+str += `${lb}:
`;
}
}
}
@@ -357,7 +357,7 @@ onclick="Save()">Save