mirror of
https://github.com/wled/WLED.git
synced 2025-04-26 15:57:18 +00:00
Merge remote-tracking branch 'pbolduc/feature/upd-ddp-send' into virtual-bus
This commit is contained in:
commit
284e748449
@ -386,7 +386,7 @@ class BusVirtual : public Bus {
|
|||||||
void show() {
|
void show() {
|
||||||
if (!_valid || _broadcastLock) return;
|
if (!_valid || _broadcastLock) return;
|
||||||
_broadcastLock = true;
|
_broadcastLock = true;
|
||||||
realtimeBoroadcast(_client, _len, _data, _rgbw);
|
realtimeBroadcast(_client, _len, _data, _rgbw);
|
||||||
_broadcastLock = false;
|
_broadcastLock = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte
|
|||||||
|
|
||||||
//udp.cpp
|
//udp.cpp
|
||||||
void notify(byte callMode, bool followUp=false);
|
void notify(byte callMode, bool followUp=false);
|
||||||
void realtimeBoroadcast(IPAddress client, uint16_t length, byte *buffer, bool isRGBW);
|
uint8_t realtimeBroadcast(IPAddress client, uint16_t length, byte *buffer, bool isRGBW);
|
||||||
void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC);
|
void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC);
|
||||||
void handleNotifications();
|
void handleNotifications();
|
||||||
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w);
|
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w);
|
||||||
|
113
wled00/udp.cpp
113
wled00/udp.cpp
@ -1,4 +1,5 @@
|
|||||||
#include "wled.h"
|
#include "wled.h"
|
||||||
|
#include "src/dependencies/json/ArduinoJson-v6.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UDP sync notifier / Realtime / Hyperion / TPM2.NET
|
* UDP sync notifier / Realtime / Hyperion / TPM2.NET
|
||||||
@ -89,13 +90,6 @@ void notify(byte callMode, bool followUp)
|
|||||||
notificationTwoRequired = (followUp)? false:notifyTwice;
|
notificationTwoRequired = (followUp)? false:notifyTwice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void realtimeBoroadcast(IPAddress client, uint16_t length, byte *buffer, bool isRGBW)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void realtimeLock(uint32_t timeoutMs, byte md)
|
void realtimeLock(uint32_t timeoutMs, byte md)
|
||||||
{
|
{
|
||||||
if (!realtimeMode && !realtimeOverride){
|
if (!realtimeMode && !realtimeOverride){
|
||||||
@ -521,3 +515,108 @@ void sendSysInfoUDP()
|
|||||||
notifier2Udp.write(data, sizeof(data));
|
notifier2Udp.write(data, sizeof(data));
|
||||||
notifier2Udp.endPacket();
|
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
|
||||||
|
|
||||||
|
//
|
||||||
|
// Send real time DDP UDP updates to the specified client
|
||||||
|
//
|
||||||
|
// client - the IP address to send to
|
||||||
|
// length - the number of pixels
|
||||||
|
// 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
|
||||||
|
|
||||||
|
uint8_t realtimeBroadcast(IPAddress client, uint16_t length, uint8_t *buffer, bool isRGBW) {
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there are 3 channels per RGB pixel
|
||||||
|
uint16_t channel = 0; // TODO: allow specifying the start channel
|
||||||
|
// the current position in the buffer
|
||||||
|
uint16_t bufferOffset = 0;
|
||||||
|
|
||||||
|
for (uint16_t currentPacket = 0; currentPacket < packetCount; currentPacket++) {
|
||||||
|
if (sequenceNumber > 15) sequenceNumber = 0;
|
||||||
|
|
||||||
|
int rc = ddpUdp.beginPacket(client, DDP_PORT);
|
||||||
|
if (rc == 0) {
|
||||||
|
//DEBUG_PRINTLN("WiFiUDP.beginPacket returned an error");
|
||||||
|
return 1; // problem
|
||||||
|
}
|
||||||
|
|
||||||
|
// the amount of data is AFTER the header in the current packet
|
||||||
|
uint16_t packetSize = DDP_CHANNELS_PER_PACKET;
|
||||||
|
|
||||||
|
uint8_t flags = DDP_FLAGS1_VER1;
|
||||||
|
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
|
||||||
|
flags = DDP_FLAGS1_VER1 | DDP_FLAGS1_PUSH;
|
||||||
|
if (channelCount % DDP_CHANNELS_PER_PACKET) {
|
||||||
|
packetSize = channelCount % DDP_CHANNELS_PER_PACKET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the header
|
||||||
|
/*0*/ddpUdp.write(flags);
|
||||||
|
/*1*/ddpUdp.write(sequenceNumber++ & 0xF);
|
||||||
|
/*2*/ddpUdp.write(0);
|
||||||
|
/*3*/ddpUdp.write(DDP_ID_DISPLAY);
|
||||||
|
// data offset in bytes, 32-bit number, MSB first
|
||||||
|
/*4*/ddpUdp.write((channel & 0xFF000000) >> 24);
|
||||||
|
/*5*/ddpUdp.write((channel & 0x00FF0000) >> 16);
|
||||||
|
/*6*/ddpUdp.write((channel & 0x0000FF00) >> 8);
|
||||||
|
/*7*/ddpUdp.write((channel & 0x000000FF));
|
||||||
|
// data length in bytes, 16-bit number, MSB first
|
||||||
|
/*8*/ddpUdp.write((packetSize & 0xFF00) >> 8);
|
||||||
|
/*9*/ddpUdp.write(packetSize & 0xFF);
|
||||||
|
|
||||||
|
// write the colors, the write write(const uint8_t *buffer, size_t size)
|
||||||
|
// function is just a loop internally too
|
||||||
|
for (uint16_t i = 0; i < packetSize; i += 3) {
|
||||||
|
ddpUdp.write(buffer[bufferOffset++]); // R
|
||||||
|
ddpUdp.write(buffer[bufferOffset++]); // G
|
||||||
|
ddpUdp.write(buffer[bufferOffset++]); // B
|
||||||
|
if (isRGBW) bufferOffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ddpUdp.endPacket();
|
||||||
|
if (rc == 0) {
|
||||||
|
//DEBUG_PRINTLN("WiFiUDP.endPacket returned an error");
|
||||||
|
return 1; // problem
|
||||||
|
}
|
||||||
|
|
||||||
|
channel += packetSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
93
wled00/udp.h
Normal file
93
wled00/udp.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#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
|
||||||
|
// buffer - a buffer of at least length*3 or length*4 bytes long
|
||||||
|
// length - the number of pixels
|
||||||
|
// isRGBW - true if the buffer contains 4 components per pixel
|
||||||
|
uint8_t realtimeBroadcast(IPAddress client, uint16_t length, uint8_t *buffer, bool isRGBW);
|
||||||
|
|
||||||
|
#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
|
@ -149,6 +149,7 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
|
|||||||
#include "NodeStruct.h"
|
#include "NodeStruct.h"
|
||||||
#include "pin_manager.h"
|
#include "pin_manager.h"
|
||||||
#include "bus_manager.h"
|
#include "bus_manager.h"
|
||||||
|
#include "udp.h"
|
||||||
|
|
||||||
#ifndef CLIENT_SSID
|
#ifndef CLIENT_SSID
|
||||||
#define CLIENT_SSID DEFAULT_CLIENT_SSID
|
#define CLIENT_SSID DEFAULT_CLIENT_SSID
|
||||||
|
Loading…
x
Reference in New Issue
Block a user