From f52881022e5d07553117dfe0bf0aa96613b67cf6 Mon Sep 17 00:00:00 2001 From: 4lloyd Date: Sun, 3 Apr 2022 00:42:46 +0200 Subject: [PATCH 1/6] Add ArtPoll support --- wled00/e131.cpp | 199 ++++++++++++++++++ wled00/fcn_declare.h | 3 + wled00/src/dependencies/e131/ESPAsyncE131.cpp | 4 +- wled00/src/dependencies/e131/ESPAsyncE131.h | 44 ++++ wled00/src/dependencies/network/Network.cpp | 28 +++ wled00/src/dependencies/network/Network.h | 1 + wled00/wled.h | 3 + 7 files changed, 280 insertions(+), 2 deletions(-) diff --git a/wled00/e131.cpp b/wled00/e131.cpp index 6f7f193bdc..9a1ea9c318 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -58,6 +58,10 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){ if (protocol == P_ARTNET) { + if (p->art_opcode == ARTNET_OPCODE_OPPOLL) { + handleArtnetPollReply(clientIP); + return; + } uni = p->art_universe; dmxChannels = htons(p->art_length); e131_data = p->art_data; @@ -214,3 +218,198 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){ e131NewData = true; } + +void handleArtnetPollReply(IPAddress ipAddress) { + prepareArtnetPollReply(); + + uint16_t startUniverse = e131Universe; + uint16_t endUniverse = e131Universe; + + switch (DMXMode) { + case DMX_MODE_DISABLED: + return; // nothing to do + break; + + case DMX_MODE_SINGLE_RGB: + case DMX_MODE_SINGLE_DRGB: + case DMX_MODE_EFFECT: + break; // 1 universe is enough + + case DMX_MODE_MULTIPLE_DRGB: + case DMX_MODE_MULTIPLE_RGB: + case DMX_MODE_MULTIPLE_RGBW: + { + bool is4Chan = (DMXMode == DMX_MODE_MULTIPLE_RGBW); + const uint16_t dmxChannelsPerLed = is4Chan ? 4 : 3; + + const uint16_t totalLen = strip.getLengthTotal(); + const uint16_t availDMXLen = MAX_CHANNELS_PER_UNIVERSE - DMXAddress + 1; + + if ((totalLen * dmxChannelsPerLed) > availDMXLen) { + const uint16_t ledsPerUniverse = is4Chan ? MAX_4_CH_LEDS_PER_UNIVERSE : MAX_3_CH_LEDS_PER_UNIVERSE; + const uint16_t dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0; + const uint16_t remainLED = totalLen - ((availDMXLen - dimmerOffset) / dmxChannelsPerLed); + + endUniverse += (remainLED / ledsPerUniverse); + + if ((remainLED % ledsPerUniverse) > 0) { + endUniverse++; + } + + if ((endUniverse - startUniverse) > E131_MAX_UNIVERSE_COUNT) { + endUniverse = startUniverse + E131_MAX_UNIVERSE_COUNT - 1; + } + } + break; + } + default: + DEBUG_PRINTLN(F("unknown E1.31 DMX mode")); + return; // nothing to do + break; + } + + for (uint16_t i = startUniverse; i <= endUniverse; ++i) { + sendArtnetPollReply(ipAddress, i); + yield(); + } +} + +void prepareArtnetPollReply() { + // Art-Net + artnetPollReply.reply_id[0] = 0x41; + artnetPollReply.reply_id[1] = 0x72; + artnetPollReply.reply_id[2] = 0x74; + artnetPollReply.reply_id[3] = 0x2d; + artnetPollReply.reply_id[4] = 0x4e; + artnetPollReply.reply_id[5] = 0x65; + artnetPollReply.reply_id[6] = 0x74; + artnetPollReply.reply_id[7] = 0x00; + + artnetPollReply.reply_opcode = ARTNET_OPCODE_OPPOLLREPLY; + + IPAddress localIP = Network.localIP(); + for (uint8_t i = 0; i < 4; i++) { + artnetPollReply.reply_ip[i] = localIP[i]; + } + + artnetPollReply.reply_port = ARTNET_DEFAULT_PORT; + + char wledVersion[] = TOSTRING(WLED_VERSION); + char * numberEnd = wledVersion; + artnetPollReply.reply_version_h = (uint8_t)strtol(numberEnd, &numberEnd, 10); + numberEnd++; + artnetPollReply.reply_version_l = (uint8_t)strtol(numberEnd, &numberEnd, 10); + + // Switch values depend on universe, set before sending + artnetPollReply.reply_net_sw = 0x00; + artnetPollReply.reply_sub_sw = 0x00; + + artnetPollReply.reply_oem_h = 0x00; + artnetPollReply.reply_oem_l = 0x00; + + artnetPollReply.reply_ubea_ver = 0x00; + + // Indicators in Normal Mode + // All or part of Port-Address programmed by network or Web browser + artnetPollReply.reply_status_1 = 0xE0; + + artnetPollReply.reply_esta_man = 0x0000; + + strlcpy((char *)(artnetPollReply.reply_short_name), serverDescription, 18); + strlcpy((char *)(artnetPollReply.reply_long_name), serverDescription, 64); + + artnetPollReply.reply_node_report[0] = '\0'; + + artnetPollReply.reply_num_ports_h = 0x00; + artnetPollReply.reply_num_ports_l = 0x01; // One output port + + artnetPollReply.reply_port_types[0] = 0x80; // Output DMX data + artnetPollReply.reply_port_types[1] = 0x00; + artnetPollReply.reply_port_types[2] = 0x00; + artnetPollReply.reply_port_types[3] = 0x00; + + // No inputs + artnetPollReply.reply_good_input[0] = 0x00; + artnetPollReply.reply_good_input[1] = 0x00; + artnetPollReply.reply_good_input[2] = 0x00; + artnetPollReply.reply_good_input[3] = 0x00; + + // One output + artnetPollReply.reply_good_output_a[0] = 0x80; // Data is being transmitted + artnetPollReply.reply_good_output_a[1] = 0x00; + artnetPollReply.reply_good_output_a[2] = 0x00; + artnetPollReply.reply_good_output_a[3] = 0x00; + + // Values depend on universe, set before sending + artnetPollReply.reply_sw_in[0] = 0x00; + artnetPollReply.reply_sw_in[1] = 0x00; + artnetPollReply.reply_sw_in[2] = 0x00; + artnetPollReply.reply_sw_in[3] = 0x00; + + // Values depend on universe, set before sending + artnetPollReply.reply_sw_out[0] = 0x00; + artnetPollReply.reply_sw_out[1] = 0x00; + artnetPollReply.reply_sw_out[2] = 0x00; + artnetPollReply.reply_sw_out[3] = 0x00; + + artnetPollReply.reply_sw_video = 0x00; + artnetPollReply.reply_sw_macro = 0x00; + artnetPollReply.reply_sw_remote = 0x00; + + artnetPollReply.reply_spare[0] = 0x00; + artnetPollReply.reply_spare[1] = 0x00; + artnetPollReply.reply_spare[2] = 0x00; + + // A DMX to / from Art-Net device + artnetPollReply.reply_style = 0x00; + + Network.localMAC(artnetPollReply.reply_mac); + + for (uint8_t i = 0; i < 4; i++) { + artnetPollReply.reply_bind_ip[i] = localIP[i]; + } + + artnetPollReply.reply_bind_index = 1; + + // Product supports web browser configuration + // Node’s IP is DHCP or manually configured + // Node is DHCP capable + // Node supports 15 bit Port-Address (Art-Net 3 or 4) + // Node is able to switch between ArtNet and sACN + artnetPollReply.reply_status_2 = (staticIP[0] == 0) ? 0x1F : 0x1D; + + // RDM is disabled + // Output style is continuous + artnetPollReply.reply_good_output_b[0] = 0xC0; + artnetPollReply.reply_good_output_b[1] = 0xC0; + artnetPollReply.reply_good_output_b[2] = 0xC0; + artnetPollReply.reply_good_output_b[3] = 0xC0; + + // Fail-over state: Hold last state + // Node does not support fail-over + artnetPollReply.reply_status_3 = 0x00; + + for (uint8_t i = 0; i < 21; i++) { + artnetPollReply.reply_filler[i] = 0x00; + } +} + +void sendArtnetPollReply(IPAddress ipAddress, uint16_t portAddress) { + artnetPollReply.reply_net_sw = (uint8_t)((portAddress >> 8) & 0x007F); + artnetPollReply.reply_sub_sw = (uint8_t)((portAddress >> 4) & 0x000F); + artnetPollReply.reply_sw_out[0] = (uint8_t)(portAddress & 0x000F); + + sprintf((char *)artnetPollReply.reply_node_report, "#0001 [%04u] OK - WLED version: " TOSTRING(WLED_VERSION), pollReplyCount); + + if (pollReplyCount < 9999) { + pollReplyCount++; + } else { + pollReplyCount = 0; + } + + pollReplyUDP.beginPacket(ipAddress, ARTNET_DEFAULT_PORT); + pollReplyUDP.write(artnetPollReply.raw, sizeof(ArtPollReply)); + pollReplyUDP.endPacket(); + + artnetPollReply.reply_bind_index++; +} \ No newline at end of file diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 5762708975..2315441cde 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -80,6 +80,9 @@ void handleDMX(); //e131.cpp void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol); +void handleArtnetPollReply(IPAddress ipAddress); +void prepareArtnetPollReply(); +void sendArtnetPollReply(IPAddress ipAddress, uint16_t portAddress); //file.cpp bool handleFileRead(AsyncWebServerRequest*, String path); diff --git a/wled00/src/dependencies/e131/ESPAsyncE131.cpp b/wled00/src/dependencies/e131/ESPAsyncE131.cpp index b4065b06fc..75d6b8dc29 100644 --- a/wled00/src/dependencies/e131/ESPAsyncE131.cpp +++ b/wled00/src/dependencies/e131/ESPAsyncE131.cpp @@ -110,8 +110,8 @@ void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) { 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) - error = true; //not a DMX packet + if (sbuff->art_opcode != ARTNET_OPCODE_OPDMX && sbuff->art_opcode != ARTNET_OPCODE_OPPOLL) + error = true; //not a DMX or poll packet } else { //E1.31 error handling if (htonl(sbuff->root_vector) != ESPAsyncE131::VECTOR_ROOT) error = true; diff --git a/wled00/src/dependencies/e131/ESPAsyncE131.h b/wled00/src/dependencies/e131/ESPAsyncE131.h index 4cf522d8a9..66b2ee9a7b 100644 --- a/wled00/src/dependencies/e131/ESPAsyncE131.h +++ b/wled00/src/dependencies/e131/ESPAsyncE131.h @@ -54,6 +54,8 @@ typedef struct ip_addr ip4_addr_t; #define DDP_TIMECODE_FLAG 0x10 #define ARTNET_OPCODE_OPDMX 0x5000 +#define ARTNET_OPCODE_OPPOLL 0x2000 +#define ARTNET_OPCODE_OPPOLLREPLY 0x2100 #define P_E131 0 #define P_ARTNET 1 @@ -151,6 +153,48 @@ typedef union { uint8_t raw[1458]; } e131_packet_t; +typedef union { + struct { + uint8_t reply_id[8]; + uint16_t reply_opcode; + uint8_t reply_ip[4]; + uint16_t reply_port; + uint8_t reply_version_h; + uint8_t reply_version_l; + uint8_t reply_net_sw; + uint8_t reply_sub_sw; + uint8_t reply_oem_h; + uint8_t reply_oem_l; + uint8_t reply_ubea_ver; + uint8_t reply_status_1; + uint16_t reply_esta_man; + uint8_t reply_short_name[18]; + uint8_t reply_long_name[64]; + uint8_t reply_node_report[64]; + uint8_t reply_num_ports_h; + uint8_t reply_num_ports_l; + uint8_t reply_port_types[4]; + uint8_t reply_good_input[4]; + uint8_t reply_good_output_a[4]; + uint8_t reply_sw_in[4]; + uint8_t reply_sw_out[4]; + uint8_t reply_sw_video; + uint8_t reply_sw_macro; + uint8_t reply_sw_remote; + uint8_t reply_spare[3]; + uint8_t reply_style; + uint8_t reply_mac[6]; + uint8_t reply_bind_ip[4]; + uint8_t reply_bind_index; + uint8_t reply_status_2; + uint8_t reply_good_output_b[4]; + uint8_t reply_status_3; + uint8_t reply_filler[21]; + } __attribute__((packed)); + + uint8_t raw[239]; +} ArtPollReply; + // new packet callback typedef void (*e131_packet_callback_function) (e131_packet_t* p, IPAddress clientIP, byte protocol); diff --git a/wled00/src/dependencies/network/Network.cpp b/wled00/src/dependencies/network/Network.cpp index 38ff70df0c..d86bf127fd 100644 --- a/wled00/src/dependencies/network/Network.cpp +++ b/wled00/src/dependencies/network/Network.cpp @@ -43,6 +43,34 @@ IPAddress NetworkClass::gatewayIP() return INADDR_NONE; } +void NetworkClass::localMAC(uint8_t* MAC) +{ +#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET) + // ETH.macAddress(MAC); // Does not work because of missing ETHClass:: in ETH.ccp + + // Start work around + String macString = ETH.macAddress(); + char macChar[18]; + char * octetEnd = macChar; + + strlcpy(macChar, macString.c_str(), 18); + + for (uint8_t i = 0; i < 6; i++) { + MAC[i] = (uint8_t)strtol(octetEnd, &octetEnd, 16); + octetEnd++; + } + // End work around + + for (uint8_t i = 0; i < 6; i++) { + if (MAC[i] != 0x00) { + return; + } + } +#endif + WiFi.macAddress(MAC); + return; +} + bool NetworkClass::isConnected() { #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET) diff --git a/wled00/src/dependencies/network/Network.h b/wled00/src/dependencies/network/Network.h index 4dd8678adc..9201d514ea 100644 --- a/wled00/src/dependencies/network/Network.h +++ b/wled00/src/dependencies/network/Network.h @@ -14,6 +14,7 @@ class NetworkClass IPAddress localIP(); IPAddress subnetMask(); IPAddress gatewayIP(); + void localMAC(uint8_t* MAC); bool isConnected(); bool isEthernet(); }; diff --git a/wled00/wled.h b/wled00/wled.h index 11866f0461..2504b12944 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -358,6 +358,9 @@ WLED_GLOBAL byte DMXOldDimmer _INIT(0); // only update WLED_GLOBAL byte e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; // to detect packet loss WLED_GLOBAL bool e131Multicast _INIT(false); // multicast or unicast WLED_GLOBAL bool e131SkipOutOfSequence _INIT(false); // freeze instead of flickering +WLED_GLOBAL uint16_t pollReplyCount _INIT(0); // count number of replies for node report +WLED_GLOBAL ArtPollReply artnetPollReply; // Artnet PollReply data +WLED_GLOBAL WiFiUDP pollReplyUDP; // UDP for sending PollReply WLED_GLOBAL bool mqttEnabled _INIT(false); WLED_GLOBAL char mqttDeviceTopic[33] _INIT(""); // main MQTT topic (individual per device, default is wled/mac) From 14bfb3d7bd9403eebe05f9aac7283f7e76c26bc5 Mon Sep 17 00:00:00 2001 From: 4lloyd Date: Sun, 3 Apr 2022 23:56:40 +0200 Subject: [PATCH 2/6] Improved calculations --- wled00/e131.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/wled00/e131.cpp b/wled00/e131.cpp index 9a1ea9c318..628bb1d0e0 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -241,14 +241,13 @@ void handleArtnetPollReply(IPAddress ipAddress) { { bool is4Chan = (DMXMode == DMX_MODE_MULTIPLE_RGBW); const uint16_t dmxChannelsPerLed = is4Chan ? 4 : 3; - + const uint16_t ledsPerUniverse = is4Chan ? MAX_4_CH_LEDS_PER_UNIVERSE : MAX_3_CH_LEDS_PER_UNIVERSE; + const uint16_t dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0; + const uint16_t ledsInFirstUniverse = ((MAX_CHANNELS_PER_UNIVERSE - DMXAddress + 1) - dimmerOffset) / dmxChannelsPerLed; const uint16_t totalLen = strip.getLengthTotal(); - const uint16_t availDMXLen = MAX_CHANNELS_PER_UNIVERSE - DMXAddress + 1; - if ((totalLen * dmxChannelsPerLed) > availDMXLen) { - const uint16_t ledsPerUniverse = is4Chan ? MAX_4_CH_LEDS_PER_UNIVERSE : MAX_3_CH_LEDS_PER_UNIVERSE; - const uint16_t dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0; - const uint16_t remainLED = totalLen - ((availDMXLen - dimmerOffset) / dmxChannelsPerLed); + if (totalLen > ledsInFirstUniverse) { + const int16_t remainLED = totalLen - ledsInFirstUniverse; endUniverse += (remainLED / ledsPerUniverse); From 8a8f55fa644aac1005a927599062f11cc4010659 Mon Sep 17 00:00:00 2001 From: 4lloyd Date: Mon, 4 Apr 2022 13:07:28 +0200 Subject: [PATCH 3/6] Add support for legacy DMX start address 0 --- wled00/e131.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wled00/e131.cpp b/wled00/e131.cpp index 628bb1d0e0..cb5f2ce427 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -243,7 +243,8 @@ void handleArtnetPollReply(IPAddress ipAddress) { const uint16_t dmxChannelsPerLed = is4Chan ? 4 : 3; const uint16_t ledsPerUniverse = is4Chan ? MAX_4_CH_LEDS_PER_UNIVERSE : MAX_3_CH_LEDS_PER_UNIVERSE; const uint16_t dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0; - const uint16_t ledsInFirstUniverse = ((MAX_CHANNELS_PER_UNIVERSE - DMXAddress + 1) - dimmerOffset) / dmxChannelsPerLed; + const uint16_t dmxLenOffset = (DMXAddress == 0) ? 0 : 1; // For legacy DMX start address 0 + const uint16_t ledsInFirstUniverse = ((MAX_CHANNELS_PER_UNIVERSE - DMXAddress + dmxLenOffset) - dimmerOffset) / dmxChannelsPerLed; const uint16_t totalLen = strip.getLengthTotal(); if (totalLen > ledsInFirstUniverse) { From 9ca352dfaba00c05042351e567de3101934bd8ec Mon Sep 17 00:00:00 2001 From: 4lloyd Date: Mon, 4 Apr 2022 13:16:49 +0200 Subject: [PATCH 4/6] Small efficiency improvement --- wled00/e131.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/e131.cpp b/wled00/e131.cpp index cb5f2ce427..cd58e0d86c 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -241,14 +241,14 @@ void handleArtnetPollReply(IPAddress ipAddress) { { bool is4Chan = (DMXMode == DMX_MODE_MULTIPLE_RGBW); const uint16_t dmxChannelsPerLed = is4Chan ? 4 : 3; - const uint16_t ledsPerUniverse = is4Chan ? MAX_4_CH_LEDS_PER_UNIVERSE : MAX_3_CH_LEDS_PER_UNIVERSE; const uint16_t dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0; const uint16_t dmxLenOffset = (DMXAddress == 0) ? 0 : 1; // For legacy DMX start address 0 - const uint16_t ledsInFirstUniverse = ((MAX_CHANNELS_PER_UNIVERSE - DMXAddress + dmxLenOffset) - dimmerOffset) / dmxChannelsPerLed; + const uint16_t ledsInFirstUniverse = (((MAX_CHANNELS_PER_UNIVERSE - DMXAddress) + dmxLenOffset) - dimmerOffset) / dmxChannelsPerLed; const uint16_t totalLen = strip.getLengthTotal(); if (totalLen > ledsInFirstUniverse) { - const int16_t remainLED = totalLen - ledsInFirstUniverse; + const uint16_t ledsPerUniverse = is4Chan ? MAX_4_CH_LEDS_PER_UNIVERSE : MAX_3_CH_LEDS_PER_UNIVERSE; + const uint16_t remainLED = totalLen - ledsInFirstUniverse; endUniverse += (remainLED / ledsPerUniverse); From 4de6c87de5ed522d6c63637dd8babafcb5f348fa Mon Sep 17 00:00:00 2001 From: 4lloyd Date: Mon, 4 Apr 2022 16:55:00 +0200 Subject: [PATCH 5/6] ESP8266 doesn't like yield --- wled00/e131.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/wled00/e131.cpp b/wled00/e131.cpp index cd58e0d86c..976fa50687 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -270,7 +270,6 @@ void handleArtnetPollReply(IPAddress ipAddress) { for (uint16_t i = startUniverse; i <= endUniverse; ++i) { sendArtnetPollReply(ipAddress, i); - yield(); } } From 1d0f594a516e8f6280a7780294a682f7517cf4f3 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Wed, 14 Sep 2022 23:48:44 +0200 Subject: [PATCH 6/6] Optimized ArtPoll memory use --- wled00/e131.cpp | 152 +++++++++++++++++++++---------------------- wled00/fcn_declare.h | 4 +- wled00/wled.h | 4 +- 3 files changed, 79 insertions(+), 81 deletions(-) diff --git a/wled00/e131.cpp b/wled00/e131.cpp index 976fa50687..98e397f403 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -220,7 +220,8 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){ } void handleArtnetPollReply(IPAddress ipAddress) { - prepareArtnetPollReply(); + ArtPollReply artnetPollReply; + prepareArtnetPollReply(&artnetPollReply); uint16_t startUniverse = e131Universe; uint16_t endUniverse = e131Universe; @@ -269,136 +270,135 @@ void handleArtnetPollReply(IPAddress ipAddress) { } for (uint16_t i = startUniverse; i <= endUniverse; ++i) { - sendArtnetPollReply(ipAddress, i); + sendArtnetPollReply(&artnetPollReply, ipAddress, i); } } -void prepareArtnetPollReply() { +void prepareArtnetPollReply(ArtPollReply *reply) { // Art-Net - artnetPollReply.reply_id[0] = 0x41; - artnetPollReply.reply_id[1] = 0x72; - artnetPollReply.reply_id[2] = 0x74; - artnetPollReply.reply_id[3] = 0x2d; - artnetPollReply.reply_id[4] = 0x4e; - artnetPollReply.reply_id[5] = 0x65; - artnetPollReply.reply_id[6] = 0x74; - artnetPollReply.reply_id[7] = 0x00; + reply->reply_id[0] = 0x41; + reply->reply_id[1] = 0x72; + reply->reply_id[2] = 0x74; + reply->reply_id[3] = 0x2d; + reply->reply_id[4] = 0x4e; + reply->reply_id[5] = 0x65; + reply->reply_id[6] = 0x74; + reply->reply_id[7] = 0x00; - artnetPollReply.reply_opcode = ARTNET_OPCODE_OPPOLLREPLY; + reply->reply_opcode = ARTNET_OPCODE_OPPOLLREPLY; IPAddress localIP = Network.localIP(); for (uint8_t i = 0; i < 4; i++) { - artnetPollReply.reply_ip[i] = localIP[i]; + reply->reply_ip[i] = localIP[i]; } - artnetPollReply.reply_port = ARTNET_DEFAULT_PORT; + reply->reply_port = ARTNET_DEFAULT_PORT; - char wledVersion[] = TOSTRING(WLED_VERSION); - char * numberEnd = wledVersion; - artnetPollReply.reply_version_h = (uint8_t)strtol(numberEnd, &numberEnd, 10); + char * numberEnd = versionString; + reply->reply_version_h = (uint8_t)strtol(numberEnd, &numberEnd, 10); numberEnd++; - artnetPollReply.reply_version_l = (uint8_t)strtol(numberEnd, &numberEnd, 10); + reply->reply_version_l = (uint8_t)strtol(numberEnd, &numberEnd, 10); // Switch values depend on universe, set before sending - artnetPollReply.reply_net_sw = 0x00; - artnetPollReply.reply_sub_sw = 0x00; + reply->reply_net_sw = 0x00; + reply->reply_sub_sw = 0x00; - artnetPollReply.reply_oem_h = 0x00; - artnetPollReply.reply_oem_l = 0x00; + reply->reply_oem_h = 0x00; // TODO add assigned oem code + reply->reply_oem_l = 0x00; - artnetPollReply.reply_ubea_ver = 0x00; + reply->reply_ubea_ver = 0x00; // Indicators in Normal Mode // All or part of Port-Address programmed by network or Web browser - artnetPollReply.reply_status_1 = 0xE0; + reply->reply_status_1 = 0xE0; - artnetPollReply.reply_esta_man = 0x0000; + reply->reply_esta_man = 0x0000; - strlcpy((char *)(artnetPollReply.reply_short_name), serverDescription, 18); - strlcpy((char *)(artnetPollReply.reply_long_name), serverDescription, 64); + strlcpy((char *)(reply->reply_short_name), serverDescription, 18); + strlcpy((char *)(reply->reply_long_name), serverDescription, 64); - artnetPollReply.reply_node_report[0] = '\0'; + reply->reply_node_report[0] = '\0'; - artnetPollReply.reply_num_ports_h = 0x00; - artnetPollReply.reply_num_ports_l = 0x01; // One output port + reply->reply_num_ports_h = 0x00; + reply->reply_num_ports_l = 0x01; // One output port - artnetPollReply.reply_port_types[0] = 0x80; // Output DMX data - artnetPollReply.reply_port_types[1] = 0x00; - artnetPollReply.reply_port_types[2] = 0x00; - artnetPollReply.reply_port_types[3] = 0x00; + reply->reply_port_types[0] = 0x80; // Output DMX data + reply->reply_port_types[1] = 0x00; + reply->reply_port_types[2] = 0x00; + reply->reply_port_types[3] = 0x00; // No inputs - artnetPollReply.reply_good_input[0] = 0x00; - artnetPollReply.reply_good_input[1] = 0x00; - artnetPollReply.reply_good_input[2] = 0x00; - artnetPollReply.reply_good_input[3] = 0x00; + reply->reply_good_input[0] = 0x00; + reply->reply_good_input[1] = 0x00; + reply->reply_good_input[2] = 0x00; + reply->reply_good_input[3] = 0x00; // One output - artnetPollReply.reply_good_output_a[0] = 0x80; // Data is being transmitted - artnetPollReply.reply_good_output_a[1] = 0x00; - artnetPollReply.reply_good_output_a[2] = 0x00; - artnetPollReply.reply_good_output_a[3] = 0x00; + reply->reply_good_output_a[0] = 0x80; // Data is being transmitted + reply->reply_good_output_a[1] = 0x00; + reply->reply_good_output_a[2] = 0x00; + reply->reply_good_output_a[3] = 0x00; // Values depend on universe, set before sending - artnetPollReply.reply_sw_in[0] = 0x00; - artnetPollReply.reply_sw_in[1] = 0x00; - artnetPollReply.reply_sw_in[2] = 0x00; - artnetPollReply.reply_sw_in[3] = 0x00; + reply->reply_sw_in[0] = 0x00; + reply->reply_sw_in[1] = 0x00; + reply->reply_sw_in[2] = 0x00; + reply->reply_sw_in[3] = 0x00; // Values depend on universe, set before sending - artnetPollReply.reply_sw_out[0] = 0x00; - artnetPollReply.reply_sw_out[1] = 0x00; - artnetPollReply.reply_sw_out[2] = 0x00; - artnetPollReply.reply_sw_out[3] = 0x00; + reply->reply_sw_out[0] = 0x00; + reply->reply_sw_out[1] = 0x00; + reply->reply_sw_out[2] = 0x00; + reply->reply_sw_out[3] = 0x00; - artnetPollReply.reply_sw_video = 0x00; - artnetPollReply.reply_sw_macro = 0x00; - artnetPollReply.reply_sw_remote = 0x00; + reply->reply_sw_video = 0x00; + reply->reply_sw_macro = 0x00; + reply->reply_sw_remote = 0x00; - artnetPollReply.reply_spare[0] = 0x00; - artnetPollReply.reply_spare[1] = 0x00; - artnetPollReply.reply_spare[2] = 0x00; + reply->reply_spare[0] = 0x00; + reply->reply_spare[1] = 0x00; + reply->reply_spare[2] = 0x00; // A DMX to / from Art-Net device - artnetPollReply.reply_style = 0x00; + reply->reply_style = 0x00; - Network.localMAC(artnetPollReply.reply_mac); + Network.localMAC(reply->reply_mac); for (uint8_t i = 0; i < 4; i++) { - artnetPollReply.reply_bind_ip[i] = localIP[i]; + reply->reply_bind_ip[i] = localIP[i]; } - artnetPollReply.reply_bind_index = 1; + reply->reply_bind_index = 1; // Product supports web browser configuration // Node’s IP is DHCP or manually configured // Node is DHCP capable // Node supports 15 bit Port-Address (Art-Net 3 or 4) // Node is able to switch between ArtNet and sACN - artnetPollReply.reply_status_2 = (staticIP[0] == 0) ? 0x1F : 0x1D; + reply->reply_status_2 = (staticIP[0] == 0) ? 0x1F : 0x1D; // RDM is disabled // Output style is continuous - artnetPollReply.reply_good_output_b[0] = 0xC0; - artnetPollReply.reply_good_output_b[1] = 0xC0; - artnetPollReply.reply_good_output_b[2] = 0xC0; - artnetPollReply.reply_good_output_b[3] = 0xC0; + reply->reply_good_output_b[0] = 0xC0; + reply->reply_good_output_b[1] = 0xC0; + reply->reply_good_output_b[2] = 0xC0; + reply->reply_good_output_b[3] = 0xC0; // Fail-over state: Hold last state // Node does not support fail-over - artnetPollReply.reply_status_3 = 0x00; + reply->reply_status_3 = 0x00; for (uint8_t i = 0; i < 21; i++) { - artnetPollReply.reply_filler[i] = 0x00; + reply->reply_filler[i] = 0x00; } } -void sendArtnetPollReply(IPAddress ipAddress, uint16_t portAddress) { - artnetPollReply.reply_net_sw = (uint8_t)((portAddress >> 8) & 0x007F); - artnetPollReply.reply_sub_sw = (uint8_t)((portAddress >> 4) & 0x000F); - artnetPollReply.reply_sw_out[0] = (uint8_t)(portAddress & 0x000F); +void sendArtnetPollReply(ArtPollReply *reply, IPAddress ipAddress, uint16_t portAddress) { + reply->reply_net_sw = (uint8_t)((portAddress >> 8) & 0x007F); + reply->reply_sub_sw = (uint8_t)((portAddress >> 4) & 0x000F); + reply->reply_sw_out[0] = (uint8_t)(portAddress & 0x000F); - sprintf((char *)artnetPollReply.reply_node_report, "#0001 [%04u] OK - WLED version: " TOSTRING(WLED_VERSION), pollReplyCount); + sprintf((char *)reply->reply_node_report, "#0001 [%04u] OK - WLED v" TOSTRING(WLED_VERSION), pollReplyCount); if (pollReplyCount < 9999) { pollReplyCount++; @@ -406,9 +406,9 @@ void sendArtnetPollReply(IPAddress ipAddress, uint16_t portAddress) { pollReplyCount = 0; } - pollReplyUDP.beginPacket(ipAddress, ARTNET_DEFAULT_PORT); - pollReplyUDP.write(artnetPollReply.raw, sizeof(ArtPollReply)); - pollReplyUDP.endPacket(); + notifierUdp.beginPacket(ipAddress, ARTNET_DEFAULT_PORT); + notifierUdp.write(reply->raw, sizeof(ArtPollReply)); + notifierUdp.endPacket(); - artnetPollReply.reply_bind_index++; + reply->reply_bind_index++; } \ No newline at end of file diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 2315441cde..5417e7a48b 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -81,8 +81,8 @@ void handleDMX(); //e131.cpp void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol); void handleArtnetPollReply(IPAddress ipAddress); -void prepareArtnetPollReply(); -void sendArtnetPollReply(IPAddress ipAddress, uint16_t portAddress); +void prepareArtnetPollReply(ArtPollReply* reply); +void sendArtnetPollReply(ArtPollReply* reply, IPAddress ipAddress, uint16_t portAddress); //file.cpp bool handleFileRead(AsyncWebServerRequest*, String path); diff --git a/wled00/wled.h b/wled00/wled.h index 2504b12944..1785a1f1a9 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -358,9 +358,7 @@ WLED_GLOBAL byte DMXOldDimmer _INIT(0); // only update WLED_GLOBAL byte e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; // to detect packet loss WLED_GLOBAL bool e131Multicast _INIT(false); // multicast or unicast WLED_GLOBAL bool e131SkipOutOfSequence _INIT(false); // freeze instead of flickering -WLED_GLOBAL uint16_t pollReplyCount _INIT(0); // count number of replies for node report -WLED_GLOBAL ArtPollReply artnetPollReply; // Artnet PollReply data -WLED_GLOBAL WiFiUDP pollReplyUDP; // UDP for sending PollReply +WLED_GLOBAL uint16_t pollReplyCount _INIT(0); // count number of replies for ArtPoll node report WLED_GLOBAL bool mqttEnabled _INIT(false); WLED_GLOBAL char mqttDeviceTopic[33] _INIT(""); // main MQTT topic (individual per device, default is wled/mac)