From 52df963be93eebdaa8307779147e1ce2a96dde3a Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 28 Sep 2020 16:29:01 +0200 Subject: [PATCH] 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).