diff --git a/wled00/DDP.cpp b/wled00/DDP.cpp deleted file mode 100644 index dfbb8fcb..00000000 --- a/wled00/DDP.cpp +++ /dev/null @@ -1,128 +0,0 @@ - -/* -** Expected interface: -** -** realtimeBrodacast(IPAddress client, uint16_t busLength, byte rgbwData[busLength][4]); -** -** http://www.3waylabs.com/ddp/ -*/ - -#include -#include -#include "DDP.h" - -#define DDP_HEADER_LEN 10 -#define DDP_SYNCPACKET_LEN 10 - -#define DDP_FLAGS1_VER 0xc0 // version mask -#define DDP_FLAGS1_VER1 0x40 // version=1 -#define DDP_FLAGS1_PUSH 0x01 -#define DDP_FLAGS1_QUERY 0x02 -#define DDP_FLAGS1_REPLY 0x04 -#define DDP_FLAGS1_STORAGE 0x08 -#define DDP_FLAGS1_TIME 0x10 - -#define DDP_ID_DISPLAY 1 -#define DDP_ID_CONFIG 250 -#define DDP_ID_STATUS 251 - -//1440 channels per packet -#define DDP_CHANNELS_PER_PACKET 1440 // 480 leds - - -// -// copies a 4 byte rgbw buffer to a 3 byte rgb buffer (skipping the w channel) -// -// Parameters: -// destination - the buffer to write to must be able to hold length*3 bytes -// source - the buffer to read from -// length - the number of 4 byte channels in the source buffer -// Returns: -// the pointer in the source where we have copied up to -// -uint8_t* copyRgbwToRgb(uint8_t *destination, uint8_t *source, uint16_t length) { - - while (length--) - { - *(destination++) = *(source++); // R - *(destination++) = *(source++); // G - *(destination++) = *(source++); // B - source++; // W - } - - return source; -} - -// -// Send real time DDP UDP updates to the specified client -// -// client - the IP address to send to -// length - the number of pixels -// rgbwData - a buffer of at least length*4 bytes long -// -uint8_t realtimeBrodacast(IPAddress client, uint8_t *rgbwData, uint16_t length) { - - WiFiUDP ddpUdp; - - // calclate the number of UDP packets we need to send - uint16_t channelCount = length * 3; // 1 channel for every R,G,B value - uint16_t packetCount = channelCount / DDP_CHANNELS_PER_PACKET; - if (channelCount % DDP_CHANNELS_PER_PACKET) { - packetCount++; - } - - // allocate a buffer for the UDP packet - size_t bufferSize = (DDP_HEADER_LEN + DDP_CHANNELS_PER_PACKET) * sizeof(uint8_t); - uint8_t* buffer = (uint8_t*)malloc(bufferSize); - if (!buffer) { - return 1; - } - - memset(buffer, 0, bufferSize); - - // set common header values - buffer[0] = DDP_FLAGS1_VER1; - buffer[2] = 1; - buffer[3] = DDP_ID_DISPLAY; - - // there are 3 channels per RGB pixel - uint16_t channel = 0; // TODO: allow specifying the start channel - - for (uint16_t currentPacket = 0; currentPacket < packetCount; currentPacket++) { - - // how much data is after the header - uint16_t packetSize = DDP_CHANNELS_PER_PACKET; - - if (currentPacket == (packetCount - 1)) { - // last packet, set the push flag - // TODO: determine if we want to send an empty push packet to each destination after sending the pixel data - buffer[0] = DDP_FLAGS1_VER1 | DDP_FLAGS1_PUSH; - - if (channelCount % DDP_CHANNELS_PER_PACKET) { - packetSize = channelCount % DDP_CHANNELS_PER_PACKET; - } - } - - // data offset in bytes, 32-bit number, MSB first - buffer[4] = (channel & 0xFF000000) >> 24; - buffer[5] = (channel & 0xFF0000) >> 16; - buffer[6] = (channel & 0xFF00) >> 8; - buffer[7] = (channel & 0xFF); - - // data length in bytes, 16-bit number, MSB first - buffer[8] = (packetSize & 0xFF00) >> 8; - buffer[9] = packetSize & 0xFF; - - // copy the data from the source buffer into our pack - rgbwData = copyRgbwToRgb(&buffer[DDP_HEADER_LEN], rgbwData, packetSize); - - ddpUdp.beginPacket(client, DDP_PORT); - ddpUdp.write(buffer, packetSize); - ddpUdp.endPacket(); - - channel += packetSize; - } - - free(buffer); - return 0; -} diff --git a/wled00/DDP.h b/wled00/DDP.h deleted file mode 100644 index 82dd0cdc..00000000 --- a/wled00/DDP.h +++ /dev/null @@ -1,16 +0,0 @@ -#define DDP_PORT 4048 - -#define DDP_PUSH_FLAG 0x01 -#define DDP_TIMECODE_FLAG 0x10 - -// -// Send real time DDP UDP updates to the specified client -// -// client - the IP address to send to -// busLength - the number of pixels -// rgbwData - a buffer of at least busLength*4 bytes long -// -// Returns -// 0 - Ok -// 1 - could not allocate buffer -uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, byte *rgbwData); diff --git a/wled00/udp.cpp b/wled00/udp.cpp index 23a9de14..25756b66 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -1,4 +1,5 @@ #include "wled.h" +#include "src/dependencies/json/ArduinoJson-v6.h" /* * UDP sync notifier / Realtime / Hyperion / TPM2.NET @@ -511,3 +512,116 @@ void sendSysInfoUDP() notifier2Udp.write(data, sizeof(data)); notifier2Udp.endPacket(); } + + +/*********************************************************************************************\ + * Art-Net, DDP, E131 output - work in progress +\*********************************************************************************************/ + +#define DDP_HEADER_LEN 10 +#define DDP_SYNCPACKET_LEN 10 + +#define DDP_FLAGS1_VER 0xc0 // version mask +#define DDP_FLAGS1_VER1 0x40 // version=1 +#define DDP_FLAGS1_PUSH 0x01 +#define DDP_FLAGS1_QUERY 0x02 +#define DDP_FLAGS1_REPLY 0x04 +#define DDP_FLAGS1_STORAGE 0x08 +#define DDP_FLAGS1_TIME 0x10 + +#define DDP_ID_DISPLAY 1 +#define DDP_ID_CONFIG 250 +#define DDP_ID_STATUS 251 + +// 1440 channels per packet +#define DDP_CHANNELS_PER_PACKET 1440 // 480 leds + +// +// copies a 4 byte rgbw buffer to a 3 byte rgb buffer (skipping the w channel) +// +// Parameters: +// destination - the buffer to write to must be able to hold length*3 bytes +// source - the buffer to read from +// length - the number of 4 byte channels in the source buffer +// Returns: +// the pointer in the source where we have copied up to +// +uint8_t* copyRgbwToRgb(uint8_t *destination, uint8_t *source, uint16_t length) { + + while (length--) + { + *(destination++) = *(source++); // R + *(destination++) = *(source++); // G + *(destination++) = *(source++); // B + source++; // W + } + + return source; +} + +// +// Send real time DDP UDP updates to the specified client +// +// client - the IP address to send to +// length - the number of pixels +// rgbwData - a buffer of at least length*4 bytes long +// +uint8_t realtimeBrodacast(IPAddress client, uint8_t *rgbwData, uint16_t length) { + + WiFiUDP ddpUdp; + + // calclate the number of UDP packets we need to send + uint16_t channelCount = length * 3; // 1 channel for every R,G,B value + uint16_t packetCount = channelCount / DDP_CHANNELS_PER_PACKET; + if (channelCount % DDP_CHANNELS_PER_PACKET) { + packetCount++; + } + + // allocatea buffer on the stack for the UDP packet + uint8_t buffer[DDP_HEADER_LEN + DDP_CHANNELS_PER_PACKET] = { 0 }; + + // set common header values + buffer[0] = DDP_FLAGS1_VER1; + buffer[2] = 1; + buffer[3] = DDP_ID_DISPLAY; + + // there are 3 channels per RGB pixel + uint16_t channel = 0; // TODO: allow specifying the start channel + + for (uint16_t currentPacket = 0; currentPacket < packetCount; currentPacket++) { + + // how much data is after the header + uint16_t packetSize = DDP_CHANNELS_PER_PACKET; + + if (currentPacket == (packetCount - 1)) { + // last packet, set the push flag + // TODO: determine if we want to send an empty push packet to each destination after sending the pixel data + buffer[0] = DDP_FLAGS1_VER1 | DDP_FLAGS1_PUSH; + + if (channelCount % DDP_CHANNELS_PER_PACKET) { + packetSize = channelCount % DDP_CHANNELS_PER_PACKET; + } + } + + // data offset in bytes, 32-bit number, MSB first + buffer[4] = (channel & 0xFF000000) >> 24; + buffer[5] = (channel & 0xFF0000) >> 16; + buffer[6] = (channel & 0xFF00) >> 8; + buffer[7] = (channel & 0xFF); + + // data length in bytes, 16-bit number, MSB first + buffer[8] = (packetSize & 0xFF00) >> 8; + buffer[9] = packetSize & 0xFF; + + // copy the data from the source buffer into our pack + rgbwData = copyRgbwToRgb(&buffer[DDP_HEADER_LEN], rgbwData, packetSize); + + ddpUdp.beginPacket(client, DDP_PORT); + ddpUdp.write(buffer, packetSize); + ddpUdp.endPacket(); + + channel += packetSize; + } + + return 0; +} \ No newline at end of file diff --git a/wled00/udp.h b/wled00/udp.h new file mode 100644 index 00000000..929da5b8 --- /dev/null +++ b/wled00/udp.h @@ -0,0 +1,97 @@ +#ifndef UDP_H +#define UDP_H + +// expected to be included from wled.h where other dependencies are loaded first + +void notify(byte callMode, bool followUp); +void realtimeLock(uint32_t timeoutMs, byte md); +void sendTPM2Ack(); +void handleNotifications(); +void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); + +/*********************************************************************************************\ + Refresh aging for remote units, drop if too old... +\*********************************************************************************************/ +void refreshNodeList(); + +/*********************************************************************************************\ + Broadcast system info to other nodes. (to update node lists) +\*********************************************************************************************/ +void sendSysInfoUDP(); + +/*********************************************************************************************\ + * Art-Net, DDP, E131 output - work in progress +\*********************************************************************************************/ + +// Send real time DDP UDP updates to the specified client +// +// client - the IP address to send to +// rgbwData - a buffer of at least length*4 bytes long +// length - the number of pixels +// +// Returns +// 0 - Ok +// 1 - could not allocate buffer +// +uint8_t realtimeBrodacast(IPAddress client, uint8_t *rgbwData, uint16_t length); + +#define DDP_PORT 4048 + +#define DDP_PUSH_FLAG 0x01 +#define DDP_TIMECODE_FLAG 0x10 + +#ifdef UPD_OUTPUT // just disable out for now +// Base class for all UDP output types. +class UDPOutputData { +public: + UDPOutputData(const JsonDocument& config); + virtual ~UDPOutputData(); + + virtual bool IsPingable() = 0; + + virtual void PrepareData(unsigned char* channelData /*,UDPOutputMessages& msgs*/) = 0; + virtual void PostPrepareData(unsigned char* channelData /*,UDPOutputMessages& msgs*/) { } + + int startChannel; + int channelCount; + IPAddress ipAddress; + + UDPOutputData(UDPOutputData const&) = delete; + void operator=(UDPOutputData const& x) = delete; + +protected: + // functions and settings to detect duplicate frames to avoid sending the same data as last time + void SaveFrame(unsigned char* channelData, int len); + bool NeedToOutputFrame(unsigned char* channelData, int startChannel, int savedIdx, int count); + bool deDuplicate = false; + int skippedFrames; + unsigned char* lastData; +}; + +// Art-Net - https://en.wikipedia.org/wiki/Art-Net +class ArtNetOutputData : public UDPOutputData { + // TODO +}; + +// Distributed Display Protocol (DDP) +class DDPOutputData : public UDPOutputData { +public: + explicit DDPOutputData(const JsonDocument& config); + virtual ~DDPOutputData(); + + virtual bool IsPingable() override { return true; } + virtual void PrepareData(unsigned char* channelData /*,UDPOutputMessages& msgs*/) override; +}; + +// E1.31 (Streaming-ACN) Protocol +class E131OutputData : public UDPOutputData { + // TODO +}; + +class UDPOutput { +public: + void AddOutput(UDPOutputData*); +}; +#endif // UPD_OUTPUT + +#endif \ No newline at end of file diff --git a/wled00/wled.h b/wled00/wled.h index 00920b96..5e2da98f 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -144,6 +144,7 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument; #include "NodeStruct.h" #include "pin_manager.h" #include "bus_manager.h" +#include "udp.h" #ifndef CLIENT_SSID #define CLIENT_SSID DEFAULT_CLIENT_SSID