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..99af3615 100644
--- a/wled00/data/settings_sync.htm
+++ b/wled00/data/settings_sync.htm
@@ -47,6 +47,7 @@ Type:
Port:
diff --git a/wled00/e131.cpp b/wled00/e131.cpp
index 2656f9e2..bfc13515 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;
+ }
+ }
+ }
+
+ uint16_t offsetLeds = p->channelOffset /3;
+ uint16_t packetLeds = 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..6bebd2eb 100644
--- a/wled00/html_settings.h
+++ b/wled00/html_settings.h
@@ -383,18 +383,18 @@ Send notifications twice:
Reboot required to apply changes.Realtime
Receive UDP realtime:
Network DMX input
Type:
Port:
-
Multicast:
Start universe:
Reboot required.
- Check out LedFx
-!
Skip out-of-sequence packets:
-DMX start address:
-
DMX mode:
+
Port:
Multicast:
+Start universe:
+Reboot required. Check out LedFx!
Skip out-of-sequence packets:
DMX start address:
DMX mode:
E1.31 info
Timeout: ms
Force max brightness:
diff --git a/wled00/json.cpp b/wled00/json.cpp
index f99df312..d8d07ed8 100644
--- a/wled00/json.cpp
+++ b/wled00/json.cpp
@@ -392,6 +392,7 @@ void serializeInfo(JsonObject root)
case REALTIME_MODE_ADALIGHT: root["lm"] = F("USB Adalight/TPM2"); break;
case REALTIME_MODE_ARTNET: root["lm"] = F("Art-Net"); break;
case REALTIME_MODE_TPM2NET: root["lm"] = F("tpm2.net"); break;
+ case REALTIME_MODE_DDP: root["lm"] = F("DDP"); break;
}
if (realtimeIP[0] == 0)
diff --git a/wled00/src/dependencies/e131/ESPAsyncE131.cpp b/wled00/src/dependencies/e131/ESPAsyncE131.cpp
index 90532132..ad9ec0ac 100644
--- a/wled00/src/dependencies/e131/ESPAsyncE131.cpp
+++ b/wled00/src/dependencies/e131/ESPAsyncE131.cpp
@@ -38,15 +38,15 @@ ESPAsyncE131::ESPAsyncE131(e131_packet_callback_function callback) {
/////////////////////////////////////////////////////////
bool ESPAsyncE131::begin(bool multicast, uint16_t port, uint16_t universe, uint8_t n) {
- bool success = false;
+ bool success = false;
- if (multicast) {
+ if (multicast) {
success = initMulticast(port, universe, n);
} else {
- success = initUnicast(port);
+ success = initUnicast(port);
}
- return success;
+ return success;
}
/////////////////////////////////////////////////////////
@@ -56,40 +56,38 @@ bool ESPAsyncE131::begin(bool multicast, uint16_t port, uint16_t universe, uint8
/////////////////////////////////////////////////////////
bool ESPAsyncE131::initUnicast(uint16_t port) {
- bool success = false;
+ bool success = false;
- if (udp.listen(port)) {
- udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this,
- std::placeholders::_1));
- success = true;
- }
- return success;
+ if (udp.listen(port)) {
+ udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this, std::placeholders::_1));
+ success = true;
+ }
+ return success;
}
bool ESPAsyncE131::initMulticast(uint16_t port, uint16_t universe, uint8_t n) {
- bool success = false;
+ bool success = false;
- IPAddress address = IPAddress(239, 255, ((universe >> 8) & 0xff),
- ((universe >> 0) & 0xff));
+ IPAddress address = IPAddress(239, 255, ((universe >> 8) & 0xff),
+ ((universe >> 0) & 0xff));
- if (udp.listenMulticast(address, port)) {
- ip4_addr_t ifaddr;
- ip4_addr_t multicast_addr;
+ if (udp.listenMulticast(address, port)) {
+ ip4_addr_t ifaddr;
+ ip4_addr_t multicast_addr;
- ifaddr.addr = static_cast(WiFi.localIP());
- for (uint8_t i = 1; i < n; i++) {
- multicast_addr.addr = static_cast(IPAddress(239, 255,
- (((universe + i) >> 8) & 0xff), (((universe + i) >> 0)
- & 0xff)));
- igmp_joingroup(&ifaddr, &multicast_addr);
- }
-
- udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this,
- std::placeholders::_1));
-
- success = true;
+ ifaddr.addr = static_cast(WiFi.localIP());
+ for (uint8_t i = 1; i < n; i++) {
+ multicast_addr.addr = static_cast(IPAddress(239, 255,
+ (((universe + i) >> 8) & 0xff), (((universe + i) >> 0)
+ & 0xff)));
+ igmp_joingroup(&ifaddr, &multicast_addr);
}
- return success;
+
+ udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this, std::placeholders::_1));
+
+ success = true;
+ }
+ return success;
}
/////////////////////////////////////////////////////////
@@ -99,15 +97,16 @@ bool ESPAsyncE131::initMulticast(uint16_t port, uint16_t universe, uint8_t n) {
/////////////////////////////////////////////////////////
void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) {
- bool error = false, isArtnet = false;
+ bool error = false;
+ uint8_t protocol = P_E131;
- sbuff = reinterpret_cast(_packet.data());
+ sbuff = reinterpret_cast(_packet.data());
//E1.31 packet identifier ("ACS-E1.17")
- if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id)))
- isArtnet = true; //not E1.31
+ if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id)))
+ protocol = P_ARTNET;
- if (isArtnet) {
+ if (protocol = P_ARTNET) {
if (memcmp(sbuff->art_id, ESPAsyncE131::ART_ID, sizeof(sbuff->art_id)))
error = true; //not "Art-Net"
if (sbuff->art_opcode != ARTNET_OPCODE_OPDMX)
@@ -121,9 +120,18 @@ void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) {
error = true;
if (sbuff->property_values[0] != 0)
error = true;
- }
+ }
+
+ if (error && _packet.localPort() == DDP_DEFAULT_PORT) { //DDP packet
+ int pushSeq = 0;
+ error = false;
+ protocol = P_DDP;
+ //bool push = sbuff->flags & DDP_PUSH_FLAG;
+ //if (push) pushSeq = sbuff->sequenceNum;
+ //else if (sbuff->sequenceNum > pushSeq -5) error = true;
+ }
- if (!error) {
- _callback(sbuff, _packet.remoteIP(), isArtnet);
- }
+ if (!error) {
+ _callback(sbuff, _packet.remoteIP(), protocol);
+ }
}
\ No newline at end of file
diff --git a/wled00/src/dependencies/e131/ESPAsyncE131.h b/wled00/src/dependencies/e131/ESPAsyncE131.h
index 0c120a3e..e9ce5c12 100644
--- a/wled00/src/dependencies/e131/ESPAsyncE131.h
+++ b/wled00/src/dependencies/e131/ESPAsyncE131.h
@@ -5,6 +5,9 @@
* Copyright (c) 2019 Shelby Merrick
* http://www.forkineye.com
*
+* Project: ESPAsyncDDP - Asynchronous DDP library for Arduino ESP8266 and ESP32
+* Copyright (c) 2019 Daniel Kulp
+*
* This program is provided free for you to use in any way that you wish,
* subject to the laws and regulations where you are using it. Due diligence
* is strongly suggested before using this code. Please give credit where due.
@@ -45,9 +48,17 @@ typedef struct ip_addr ip4_addr_t;
// Defaults
#define E131_DEFAULT_PORT 5568
#define ARTNET_DEFAULT_PORT 6454
+#define DDP_DEFAULT_PORT 4048
+
+#define DDP_PUSH_FLAG 0x01
+#define DDP_TIMECODE_FLAG 0x10
#define ARTNET_OPCODE_OPDMX 0x5000
+#define P_E131 0
+#define P_ARTNET 1
+#define P_DDP 2
+
// E1.31 Packet Offsets
#define E131_ROOT_PREAMBLE_SIZE 0
#define E131_ROOT_POSTAMBLE_SIZE 2
@@ -76,57 +87,78 @@ typedef struct ip_addr ip4_addr_t;
// E1.31 Packet Structure
typedef union {
struct { //E1.31 packet
- // Root Layer
- uint16_t preamble_size;
- uint16_t postamble_size;
- uint8_t acn_id[12];
- uint16_t root_flength;
- uint32_t root_vector;
- uint8_t cid[16];
+ // Root Layer
+ uint16_t preamble_size;
+ uint16_t postamble_size;
+ uint8_t acn_id[12];
+ uint16_t root_flength;
+ uint32_t root_vector;
+ uint8_t cid[16];
- // Frame Layer
- uint16_t frame_flength;
- uint32_t frame_vector;
- uint8_t source_name[64];
- uint8_t priority;
- uint16_t reserved;
- uint8_t sequence_number;
- uint8_t options;
- uint16_t universe;
+ // Frame Layer
+ uint16_t frame_flength;
+ uint32_t frame_vector;
+ uint8_t source_name[64];
+ uint8_t priority;
+ uint16_t reserved;
+ uint8_t sequence_number;
+ uint8_t options;
+ uint16_t universe;
- // DMP Layer
- uint16_t dmp_flength;
- uint8_t dmp_vector;
- uint8_t type;
- uint16_t first_address;
- uint16_t address_increment;
- uint16_t property_value_count;
- uint8_t property_values[513];
+ // DMP Layer
+ uint16_t dmp_flength;
+ uint8_t dmp_vector;
+ uint8_t type;
+ uint16_t first_address;
+ uint16_t address_increment;
+ uint16_t property_value_count;
+ uint8_t property_values[513];
} __attribute__((packed));
struct { //Art-Net packet
- uint8_t art_id[8];
- uint16_t art_opcode;
- uint16_t art_protocol_ver;
- uint8_t art_sequence_number;
- uint8_t art_physical;
- uint16_t art_universe;
- uint16_t art_length;
+ uint8_t art_id[8];
+ uint16_t art_opcode;
+ uint16_t art_protocol_ver;
+ uint8_t art_sequence_number;
+ uint8_t art_physical;
+ uint16_t art_universe;
+ uint16_t art_length;
- uint8_t art_data[512];
- } __attribute__((packed));
+ uint8_t art_data[512];
+ } __attribute__((packed));
- uint8_t raw[638];
+ struct { //DDP Header
+ uint8_t flags;
+ uint8_t sequenceNum;
+ uint8_t dataType;
+ uint8_t destination;
+ uint32_t channelOffset;
+ uint16_t dataLen;
+ uint8_t data[1];
+ } __attribute__((packed));
+
+ /*struct { //DDP Time code Header (unsupported)
+ uint8_t flags;
+ uint8_t sequenceNum;
+ uint8_t dataType;
+ uint8_t destination;
+ uint32_t channelOffset;
+ uint16_t dataLen;
+ uint32_t timeCode;
+ uint8_t data[1];
+ } __attribute__((packed));*/
+
+ uint8_t raw[1458];
} e131_packet_t;
// new packet callback
-typedef void (*e131_packet_callback_function) (e131_packet_t* p, IPAddress clientIP, bool isArtnet);
+typedef void (*e131_packet_callback_function) (e131_packet_t* p, IPAddress clientIP, byte protocol);
class ESPAsyncE131 {
private:
// Constants for packet validation
static const uint8_t ACN_ID[];
- static const uint8_t ART_ID[];
+ static const uint8_t ART_ID[];
static const uint32_t VECTOR_ROOT = 4;
static const uint32_t VECTOR_FRAME = 2;
static const uint8_t VECTOR_DMP = 2;
diff --git a/wled00/wled.h b/wled00/wled.h
index 9968d1be..348bed20 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
-#define VERSION 2009260
+#define VERSION 2009280
// ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS).