From 52df963be93eebdaa8307779147e1ce2a96dde3a Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 28 Sep 2020 16:29:01 +0200 Subject: [PATCH 1/2] DDP Support --- wled00/const.h | 1 + wled00/data/settings_sync.htm | 1 + wled00/e131.cpp | 51 ++++++++- wled00/fcn_declare.h | 2 +- wled00/html_settings.h | 24 ++-- wled00/json.cpp | 1 + wled00/src/dependencies/e131/ESPAsyncE131.cpp | 86 ++++++++------- wled00/src/dependencies/e131/ESPAsyncE131.h | 104 ++++++++++++------ wled00/wled.h | 2 +- 9 files changed, 178 insertions(+), 94 deletions(-) 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). From ee8596d175e7b2e4617572eab5eca401f1bbd73b Mon Sep 17 00:00:00 2001 From: cschwinne Date: Tue, 29 Sep 2020 01:13:29 +0200 Subject: [PATCH 2/2] DDP working --- CHANGELOG.md | 7 ++++++- wled00/data/settings_sync.htm | 2 +- wled00/e131.cpp | 4 ++-- wled00/html_settings.h | 2 +- wled00/set.cpp | 1 - wled00/src/dependencies/e131/ESPAsyncE131.cpp | 2 +- wled00/wled.h | 2 +- 7 files changed, 12 insertions(+), 8 deletions(-) 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/data/settings_sync.htm b/wled00/data/settings_sync.htm index 99af3615..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;} diff --git a/wled00/e131.cpp b/wled00/e131.cpp index bfc13515..12e2ba85 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -24,8 +24,8 @@ void handleDDPPacket(e131_packet_t* p) { } } - uint16_t offsetLeds = p->channelOffset /3; - uint16_t packetLeds = p->dataLen /3; + 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 diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 6bebd2eb..98e0c922 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -357,7 +357,7 @@ onclick="Save()">Save)====="; // Autogenerated from wled00/data/settings_sync.htm, do not edit!! const char PAGE_settings_sync[] PROGMEM = R"=====(Sync Settings