-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from netmindz/DDP
DDP Output All looks good!
- Loading branch information
Showing
5 changed files
with
276 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
@title StarMod | ||
@file UserModArtNet.h | ||
@date 20230730 | ||
@repo https://github.com/ewoudwijma/StarMod | ||
@Authors https://github.com/ewoudwijma/StarMod/commits/main | ||
@Copyright (c) 2023 Github StarMod Commit Authors | ||
@license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 | ||
*/ | ||
|
||
#define ARTNET_DEFAULT_PORT 6454 | ||
|
||
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}; | ||
|
||
class UserModArtNet:public Module { | ||
|
||
public: | ||
|
||
IPAddress targetIp; | ||
|
||
UserModArtNet() :Module("ArtNet") { | ||
print->print("%s %s\n", __PRETTY_FUNCTION__, name); | ||
|
||
print->print("%s %s %s\n", __PRETTY_FUNCTION__, name, success?"success":"failed"); | ||
}; | ||
|
||
//setup filesystem | ||
void setup() { | ||
Module::setup(); | ||
print->print("%s %s\n", __PRETTY_FUNCTION__, name); | ||
targetIp = IPAddress(192,168,178,161); // TODO allow setting at runtime | ||
print->print("%s %s %s\n", __PRETTY_FUNCTION__, name, success?"success":"failed"); | ||
} | ||
|
||
void connected() { | ||
print->print("%s %s - Connected\n", __PRETTY_FUNCTION__, name); | ||
isConnected = true; | ||
} | ||
|
||
void loop(){ | ||
// Module::loop(); | ||
|
||
if(!isConnected) return; | ||
|
||
// calculate the number of UDP packets we need to send | ||
bool isRGBW = false; | ||
|
||
const size_t channelCount = ledsV.nrOfLedsP * (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; | ||
|
||
sequenceNumber++; | ||
|
||
WiFiUDP ddpUdp; | ||
|
||
int bri = mdl->getValue("bri"); | ||
|
||
for (size_t currentPacket = 0; currentPacket < packetCount; currentPacket++) { | ||
|
||
if (sequenceNumber > 255) sequenceNumber = 0; | ||
|
||
if (!ddpUdp.beginPacket(targetIp, ARTNET_DEFAULT_PORT)) { | ||
print->print("Art-Net WiFiUDP.beginPacket returned an error"); | ||
return; // 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; | ||
} | ||
} | ||
|
||
byte header_buffer[ART_NET_HEADER_SIZE]; | ||
memcpy_P(header_buffer, ART_NET_HEADER, ART_NET_HEADER_SIZE); | ||
ddpUdp.write(header_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. | ||
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 < ledsV.nrOfLedsP; i++) { | ||
CRGB pixel = ledsP[i]; | ||
ddpUdp.write(scale8(pixel.r, bri)); // R | ||
ddpUdp.write(scale8(pixel.g, bri)); // G | ||
ddpUdp.write(scale8(pixel.b, bri)); // B | ||
// if (isRGBW) ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // W | ||
} | ||
|
||
if (!ddpUdp.endPacket()) { | ||
print->print("Art-Net WiFiUDP.endPacket returned an error"); | ||
return; // borked | ||
} | ||
channel += packetSize; | ||
} | ||
|
||
} | ||
|
||
private: | ||
bool isConnected = false; | ||
size_t sequenceNumber = 0; | ||
|
||
}; | ||
|
||
static UserModArtNet *artnetmod; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/* | ||
@title StarMod | ||
@file UserModArtNet.h | ||
@date 20230730 | ||
@repo https://github.com/ewoudwijma/StarMod | ||
@Authors https://github.com/ewoudwijma/StarMod/commits/main | ||
@Copyright (c) 2023 Github StarMod Commit Authors | ||
@license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 | ||
*/ | ||
#define DDP_DEFAULT_PORT 4048 | ||
#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 | ||
|
||
#define DDP_TYPE_RGB24 0x0B // 00 001 011 (RGB , 8 bits per channel, 3 channels) | ||
#define DDP_TYPE_RGBW32 0x1B // 00 011 011 (RGBW, 8 bits per channel, 4 channels) | ||
|
||
class UserModDDP:public Module { | ||
|
||
public: | ||
|
||
IPAddress targetIp; | ||
|
||
UserModDDP() :Module("DDP") { | ||
print->print("%s %s\n", __PRETTY_FUNCTION__, name); | ||
|
||
print->print("%s %s %s\n", __PRETTY_FUNCTION__, name, success?"success":"failed"); | ||
}; | ||
|
||
//setup filesystem | ||
void setup() { | ||
Module::setup(); | ||
print->print("%s %s\n", __PRETTY_FUNCTION__, name); | ||
targetIp = IPAddress(192,168,178,161); // TODO allow setting at runtime | ||
print->print("%s %s %s\n", __PRETTY_FUNCTION__, name, success?"success":"failed"); | ||
} | ||
|
||
void connected() { | ||
print->print("%s %s - Connected\n", __PRETTY_FUNCTION__, name); | ||
isConnected = true; | ||
} | ||
|
||
void loop(){ | ||
// Module::loop(); | ||
|
||
if(!isConnected) return; | ||
|
||
// calculate the number of UDP packets we need to send | ||
bool isRGBW = false; | ||
|
||
const size_t channelCount = ledsV.nrOfLedsP * (isRGBW? 4:3); // 1 channel for every R,G,B,(W?) value | ||
const size_t packetCount = ((channelCount-1) / DDP_CHANNELS_PER_PACKET) +1; | ||
|
||
uint32_t channel = 0; | ||
size_t bufferOffset = 0; | ||
|
||
sequenceNumber++; | ||
|
||
WiFiUDP ddpUdp; | ||
|
||
int bri = mdl->getValue("bri"); | ||
|
||
for (size_t currentPacket = 0; currentPacket < packetCount; currentPacket++) { | ||
|
||
if (sequenceNumber > 15) sequenceNumber = 0; | ||
|
||
if (!ddpUdp.beginPacket(targetIp, DDP_DEFAULT_PORT)) { // port defined in ESPAsyncE131.h | ||
print->print("DDP WiFiUDP.beginPacket returned an error"); | ||
return; // borked | ||
} | ||
|
||
// the amount of data is AFTER the header in the current packet | ||
size_t packetSize = DDP_CHANNELS_PER_PACKET; | ||
|
||
uint8_t flags = DDP_FLAGS1_VER1; | ||
if (currentPacket == (packetCount - 1U)) { | ||
// 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++ & 0x0F); // sequence may be unnecessary unless we are sending twice (as requested in Sync settings) | ||
/*2*/ddpUdp.write(isRGBW ? DDP_TYPE_RGBW32 : DDP_TYPE_RGB24); | ||
/*3*/ddpUdp.write(DDP_ID_DISPLAY); | ||
// data offset in bytes, 32-bit number, MSB first | ||
/*4*/ddpUdp.write(0xFF & (channel >> 24)); | ||
/*5*/ddpUdp.write(0xFF & (channel >> 16)); | ||
/*6*/ddpUdp.write(0xFF & (channel >> 8)); | ||
/*7*/ddpUdp.write(0xFF & (channel )); | ||
// data length in bytes, 16-bit number, MSB first | ||
/*8*/ddpUdp.write(0xFF & (packetSize >> 8)); | ||
/*9*/ddpUdp.write(0xFF & (packetSize )); | ||
|
||
for (size_t i = 0; i < ledsV.nrOfLedsP; i++) { | ||
CRGB pixel = ledsP[i]; | ||
ddpUdp.write(scale8(pixel.r, bri)); // R | ||
ddpUdp.write(scale8(pixel.g, bri)); // G | ||
ddpUdp.write(scale8(pixel.b, bri)); // B | ||
// if (isRGBW) ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // W | ||
} | ||
|
||
if (!ddpUdp.endPacket()) { | ||
print->print("DDP WiFiUDP.endPacket returned an error"); | ||
return; // problem | ||
} | ||
channel += packetSize; | ||
} | ||
} | ||
|
||
private: | ||
bool isConnected = false; | ||
size_t sequenceNumber = 0; | ||
|
||
}; | ||
|
||
static UserModDDP *ddpmod; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters