From a4fcbb9f67c6fcf32c5bb35aa24b0eb6fadf1384 Mon Sep 17 00:00:00 2001
From: TroyHacks <5659019+troyhacks@users.noreply.github.com>
Date: Fri, 10 Mar 2023 13:29:00 -0500
Subject: [PATCH 1/3] Art-Net transmit support for network LEDs
Like DDP, this allows WLED to address network systems using the Art-Net protocol.
Universe starts at zero, because that's the first universe in Art-Net.
Works with RGB. It's coded to also work with RGBW, but I couldn't find a great place to enable it without mucking with things I don't understand.
---
wled00/bus_manager.cpp | 32 +++++++++++-----------
wled00/data/settings_leds.htm | 2 +-
wled00/udp.cpp | 51 +++++++++++++++++++++++++++++++++++
3 files changed, 68 insertions(+), 17 deletions(-)
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index 6ab90b2f6..9e4d54a4d 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -383,24 +383,24 @@ uint8_t BusOnOff::getPins(uint8_t* pinArray) {
BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
_valid = false;
-// switch (bc.type) {
-// case TYPE_NET_ARTNET_RGB:
-// _rgbw = false;
-// _UDPtype = 2;
-// break;
-// case TYPE_NET_E131_RGB:
-// _rgbw = false;
-// _UDPtype = 1;
-// break;
-// case TYPE_NET_DDP_RGB:
-// _rgbw = false;
-// _UDPtype = 0;
-// break;
-// default: // TYPE_NET_DDP_RGB / TYPE_NET_DDP_RGBW
+ switch (bc.type) {
+ case TYPE_NET_ARTNET_RGB:
+ _rgbw = false;
+ _UDPtype = 2;
+ break;
+ case TYPE_NET_E131_RGB:
+ _rgbw = false;
+ _UDPtype = 1;
+ break;
+ case TYPE_NET_DDP_RGB:
+ _rgbw = false;
+ _UDPtype = 0;
+ break;
+ default: // TYPE_NET_DDP_RGB / TYPE_NET_DDP_RGBW
_rgbw = bc.type == TYPE_NET_DDP_RGBW;
_UDPtype = 0;
-// break;
-// }
+ break;
+ }
_UDPchannels = _rgbw ? 4 : 3;
_data = (byte *)malloc(bc.count * _UDPchannels);
if (_data == nullptr) return;
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm
index 15e39efb2..e6bbde58d 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -345,7 +345,7 @@ ${i+1}:
'}
-
+
Color Order:
diff --git a/wled00/udp.cpp b/wled00/udp.cpp
index effd597a0..b6370a596 100644
--- a/wled00/udp.cpp
+++ b/wled00/udp.cpp
@@ -791,6 +791,57 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
case 2: //ArtNet
{
+ // calculate the number of UDP packets we need to send
+ size_t channelCount = length * (isRGBW?4:3); // 1 channel for every R,G,B,(W?) value
+ size_t ARTNET_CHANNELS_PER_PACKET = isRGBW?512:510; // 512/4=128 RGBW LEDs, 510/3=170 RGB LEDs
+ size_t packetCount = ((channelCount-1)/ARTNET_CHANNELS_PER_PACKET)+1;
+
+ uint32_t channel = 0;
+ size_t bufferOffset = 0;
+
+ sequenceNumber++;
+
+ for (size_t currentPacket = 0; currentPacket < packetCount; currentPacket++) {
+
+ if (sequenceNumber > 255) sequenceNumber = 0;
+
+ if (!ddpUdp.beginPacket(client, ARTNET_DEFAULT_PORT)) {
+ DEBUG_PRINTLN(F("Art-Net WiFiUDP.beginPacket returned an error"));
+ return 1; // borked
+ }
+
+ size_t packetSize = ARTNET_CHANNELS_PER_PACKET;
+
+ if (currentPacket == (packetCount - 1U)) {
+ // last packet
+ if (channelCount % ARTNET_CHANNELS_PER_PACKET) {
+ packetSize = channelCount % ARTNET_CHANNELS_PER_PACKET;
+ }
+ }
+
+ const byte ART_NET_HEADER[12] = {0x41,0x72,0x74,0x2d,0x4e,0x65,0x74,0x00,0x00,0x50,0x00,0x0e};
+
+ ddpUdp.write(ART_NET_HEADER,12); // This doesn't change. Hard coded ID, OpCode, and protocol version.
+ ddpUdp.write(sequenceNumber & 0xFF); // sequence number. 1..255
+ ddpUdp.write(0x00); // physical - more an FYI, not really used for anything. 0..3
+ ddpUdp.write((currentPacket) & 0xFF); // Universe LSB. 1 full packet == 1 full universe, so just use current packet number.
+ ddpUdp.write(0x00); // Universe MSB, unused.
+ ddpUdp.write(0xFF & (packetSize >> 8)); // 16-bit length of channel data, MSB
+ ddpUdp.write(0xFF & (packetSize )); // 16-bit length of channel data, LSB
+
+ for (size_t i = 0; i < packetSize; i += (isRGBW?4:3)) {
+ ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // R
+ ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // G
+ ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // B
+ if (isRGBW) ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // W
+ }
+
+ if (!ddpUdp.endPacket()) {
+ DEBUG_PRINTLN(F("Art-Net WiFiUDP.endPacket returned an error"));
+ return 1; // borked
+ }
+ channel += packetSize;
+ }
} break;
}
return 0;
From 349578fb6ea22b29ef4ce8e19964af466f22e7c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Kristan?=
Date: Sat, 11 Mar 2023 22:33:06 +0100
Subject: [PATCH 2/3] whitespace cleanup
---
wled00/bus_manager.cpp | 30 +++++++++++++-----------------
1 file changed, 13 insertions(+), 17 deletions(-)
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index 9e4d54a4d..a444f669c 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -383,24 +383,20 @@ uint8_t BusOnOff::getPins(uint8_t* pinArray) {
BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
_valid = false;
- switch (bc.type) {
- case TYPE_NET_ARTNET_RGB:
- _rgbw = false;
- _UDPtype = 2;
- break;
- case TYPE_NET_E131_RGB:
- _rgbw = false;
- _UDPtype = 1;
- break;
- case TYPE_NET_DDP_RGB:
- _rgbw = false;
- _UDPtype = 0;
- break;
- default: // TYPE_NET_DDP_RGB / TYPE_NET_DDP_RGBW
+ switch (bc.type) {
+ case TYPE_NET_ARTNET_RGB:
+ _rgbw = false;
+ _UDPtype = 2;
+ break;
+ case TYPE_NET_E131_RGB:
+ _rgbw = false;
+ _UDPtype = 1;
+ break;
+ default: // TYPE_NET_DDP_RGB / TYPE_NET_DDP_RGBW
_rgbw = bc.type == TYPE_NET_DDP_RGBW;
_UDPtype = 0;
- break;
- }
+ break;
+ }
_UDPchannels = _rgbw ? 4 : 3;
_data = (byte *)malloc(bc.count * _UDPchannels);
if (_data == nullptr) return;
@@ -563,4 +559,4 @@ uint16_t BusManager::getTotalLength() {
// Bus static member definition
int16_t Bus::_cct = -1;
uint8_t Bus::_cctBlend = 0;
-uint8_t Bus::_gAWM = 255;
\ No newline at end of file
+uint8_t Bus::_gAWM = 255;
From 9b98cbb8943a50767223b24195fee1de023d5520 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Kristan?=
Date: Sat, 11 Mar 2023 22:35:22 +0100
Subject: [PATCH 3/3] PROGMEM for header
---
wled00/udp.cpp | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/wled00/udp.cpp b/wled00/udp.cpp
index b6370a596..6d7d28202 100644
--- a/wled00/udp.cpp
+++ b/wled00/udp.cpp
@@ -713,7 +713,9 @@ void sendSysInfoUDP()
// buffer - a buffer of at least length*4 bytes long
// isRGBW - true if the buffer contains 4 components per pixel
-uint8_t sequenceNumber = 0; // this needs to be shared across all outputs
+static size_t sequenceNumber = 0; // this needs to be shared across all outputs
+static const size_t ART_NET_HEADER_SIZE = 12;
+static const byte ART_NET_HEADER[] PROGMEM = {0x41,0x72,0x74,0x2d,0x4e,0x65,0x74,0x00,0x00,0x50,0x00,0x0e};
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8_t *buffer, uint8_t bri, bool isRGBW) {
if (!(apActive || interfacesInited) || !client[0] || !length) return 1; // network not initialised or dummy/unset IP address 031522 ajn added check for ap
@@ -792,9 +794,9 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
case 2: //ArtNet
{
// calculate the number of UDP packets we need to send
- size_t channelCount = length * (isRGBW?4:3); // 1 channel for every R,G,B,(W?) value
- size_t ARTNET_CHANNELS_PER_PACKET = isRGBW?512:510; // 512/4=128 RGBW LEDs, 510/3=170 RGB LEDs
- size_t packetCount = ((channelCount-1)/ARTNET_CHANNELS_PER_PACKET)+1;
+ const size_t channelCount = length * (isRGBW?4:3); // 1 channel for every R,G,B,(W?) value
+ const size_t ARTNET_CHANNELS_PER_PACKET = isRGBW?512:510; // 512/4=128 RGBW LEDs, 510/3=170 RGB LEDs
+ const size_t packetCount = ((channelCount-1)/ARTNET_CHANNELS_PER_PACKET)+1;
uint32_t channel = 0;
size_t bufferOffset = 0;
@@ -819,9 +821,9 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
}
}
- const byte ART_NET_HEADER[12] = {0x41,0x72,0x74,0x2d,0x4e,0x65,0x74,0x00,0x00,0x50,0x00,0x0e};
-
- ddpUdp.write(ART_NET_HEADER,12); // This doesn't change. Hard coded ID, OpCode, and protocol version.
+ byte buffer[ART_NET_HEADER_SIZE];
+ memcpy_P(buffer, ART_NET_HEADER, ART_NET_HEADER_SIZE);
+ ddpUdp.write(buffer, ART_NET_HEADER_SIZE); // This doesn't change. Hard coded ID, OpCode, and protocol version.
ddpUdp.write(sequenceNumber & 0xFF); // sequence number. 1..255
ddpUdp.write(0x00); // physical - more an FYI, not really used for anything. 0..3
ddpUdp.write((currentPacket) & 0xFF); // Universe LSB. 1 full packet == 1 full universe, so just use current packet number.