diff --git a/CHANGELOG.md b/CHANGELOG.md
index d587cb5d..50e55ed8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,14 @@
### Development versions after the 0.10.2 release
+#### Build 2009290
+
+- Added basic DDP protocol support
+- Added Washing Machine effect (PR #1208)
+
#### Build 2009260
-- Added Loxone parser
+- 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
diff --git a/wled00/const.h b/wled00/const.h
index cc65791e..a6810120 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 a144e76a..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;}
@@ -47,6 +47,7 @@ Type:
Port:
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 fb15e95a..4c22152b 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -44,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);
diff --git a/wled00/html_settings.h b/wled00/html_settings.h
index 258a2111..98e0c922 100644
--- a/wled00/html_settings.h
+++ b/wled00/html_settings.h
@@ -357,7 +357,7 @@ onclick="Save()">Save