Proper Art-Net Settings

This commit is contained in:
Troy
2024-11-07 09:32:23 -05:00
parent 53ecf16ab1
commit 44c6a0d73b
8 changed files with 258 additions and 57 deletions

View File

@@ -476,7 +476,10 @@ BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
_broadcastLock = false;
_valid = true;
USER_PRINTF(" %u.%u.%u.%u] \n", bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
_artnet_outputs = bc.artnet_outputs;
_artnet_leds_per_output = bc.artnet_leds_per_output;
_artnet_fps_limit = bc.artnet_fps_limit;
USER_PRINTF(" %u.%u.%u.%u]\n", bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
}
void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
@@ -499,7 +502,7 @@ uint32_t BusNetwork::getPixelColor(uint16_t pix) const {
void BusNetwork::show() {
if (!_valid || !canShow()) return;
_broadcastLock = true;
realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, _rgbw);
realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, _rgbw, _artnet_outputs, _artnet_leds_per_output, _artnet_fps_limit);
_broadcastLock = false;
}

View File

@@ -54,12 +54,16 @@ struct BusConfig {
uint8_t skipAmount;
bool refreshReq;
uint8_t autoWhite;
uint8_t artnet_outputs, artnet_fps_limit;
uint16_t artnet_leds_per_output;
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; // WLEDMM warning: this means that BusConfig cannot handle nore than 5 pins per bus!
uint16_t frequency;
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U) {
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, uint8_t art_o=1, uint16_t art_l=1, uint8_t art_f=30) {
refreshReq = (bool) GET_BIT(busType,7);
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; autoWhite = aw; frequency = clock_kHz;
artnet_outputs = art_o; artnet_leds_per_output = art_l; artnet_fps_limit = art_f;
uint8_t nPins = 1; // default = only one pin (clockless LEDs like WS281x)
if ((type >= TYPE_NET_DDP_RGB) && (type < (TYPE_NET_DDP_RGB + 16))) nPins = 4; // virtual network bus. 4 "pins" store IP address
else if ((type > 47) && (type < 63)) nPins = 2; // (data + clock / SPI) busses - two pins
@@ -144,6 +148,9 @@ class Bus {
virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; }
virtual uint8_t skippedLeds() const { return 0; }
virtual uint16_t getFrequency() const { return 0U; }
virtual uint8_t get_artnet_fps_limit() const { return 0; }
virtual uint8_t get_artnet_outputs() const { return 0; }
virtual uint16_t get_artnet_leds_per_output() const { return 0; }
inline uint16_t getStart() const { return _start; }
inline void setStart(uint16_t start) { _start = start; }
inline uint8_t getType() const { return _type; }
@@ -348,12 +355,30 @@ class BusNetwork : public Bus {
return !_broadcastLock;
}
uint8_t getPins(uint8_t* pinArray) const;
uint8_t getPins(uint8_t* pinArray) const;
uint16_t getLength() const override {
uint16_t getLength() const override {
return _len;
}
uint8_t get_artnet_fps_limit() const override {
return _artnet_fps_limit;
}
uint8_t get_artnet_outputs() const override {
return _artnet_outputs;
}
uint16_t get_artnet_leds_per_output() const override {
return _artnet_leds_per_output;
}
void setColorOrder(uint8_t colorOrder);
uint8_t getColorOrder() const {
return _colorOrder;
}
void cleanup();
~BusNetwork() {
@@ -361,12 +386,17 @@ class BusNetwork : public Bus {
}
private:
IPAddress _client;
uint8_t _UDPtype;
uint8_t _UDPchannels;
bool _rgbw;
bool _broadcastLock;
byte *_data;
IPAddress _client;
uint8_t _UDPtype;
uint8_t _UDPchannels;
bool _rgbw;
bool _broadcastLock;
byte *_data;
uint8_t _colorOrder = COL_ORDER_RGB;
uint8_t _artnet_fps_limit;
uint8_t _artnet_outputs;
uint16_t _artnet_leds_per_output;
const ColorOrderMap &_colorOrderMap;
};
#ifdef WLED_ENABLE_HUB75MATRIX

View File

@@ -194,13 +194,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM (not yet implemented fully)
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY;
uint8_t artnet_outputs = elm["artnet_outputs"] | 0;
uint16_t artnet_leds_per_output = elm["artnet_leds_per_output"] | 0;
uint8_t artnet_fps_limit = elm["artnet_fps_limit"] | 0;
if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz);
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, artnet_outputs, artnet_leds_per_output, artnet_fps_limit);
mem += BusManager::memUsage(bc);
if (mem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
} else {
if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode);
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, artnet_outputs, artnet_leds_per_output, artnet_fps_limit);
busesChanged = true;
}
s++;
@@ -829,6 +832,9 @@ void serializeConfig() {
ins["ref"] = bus->isOffRefreshRequired();
ins[F("rgbwm")] = bus->getAutoWhiteMode();
ins[F("freq")] = bus->getFrequency();
ins["artnet_outputs"] = bus->get_artnet_outputs();
ins["artnet_fps_limit"] = bus->get_artnet_fps_limit();
ins["artnet_leds_per_output"] = bus->get_artnet_leds_per_output();
}
JsonArray hw_com = hw.createNestedArray(F("com"));

View File

@@ -219,6 +219,30 @@
gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 28 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h
gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)||(t >= 100 && t < 110)) ? "none":"inline"; // hide color order for PWM
gId("dig"+n+"w").style.display = (t > 28 && t < 32) ? "inline":"none"; // show swap channels dropdown
gId("dig"+n+"O").style.display = (t == 82 || t == 83) ? "inline":"none"; // show Art-Net output number
gId("dig"+n+"L").style.display = (t == 82 || t == 83) ? "inline":"none"; // show Art-Net LEDs per output
gId("dig"+n+"F").style.display = (t == 82 || t == 83) ? "inline":"none"; // show Art-Net FPS limiter
if (gId("dig"+n+"F").style.display == "inline") {
total_leds = d.getElementsByName("LC"+n)[0].value;
outputs = d.getElementsByName("AO"+n)[0].value;
leds_per_output = d.getElementsByName("AL"+n)[0].value;
fps_limit = d.getElementsByName("AF"+n)[0].value;
last_octet = d.getElementsByName("L3"+n)[0].value;
if (outputs > 1) {
if (t == 82) gId("dig"+n+"W").innerHTML = "<br />Set your Art-Net Hardware to "+Math.ceil(leds_per_output/170)+" universes per output.";
if (t == 83) gId("dig"+n+"W").innerHTML = "<br />Set your Art-Net Hardware to "+Math.ceil(leds_per_output/128)+" universes per output.";
} else if (outputs == 1) {
gId("dig"+n+"W").innerHTML = "<br />WLED-style Art-Net output enabled.";
} else {
gId("dig"+n+"W").innerHTML = "<br />You need at least 1 output!";
}
if (outputs > 1 && fps_limit > 33333/leds_per_output) gId("dig"+n+"W").innerHTML += "<br />FPS limit may be too high for WS281x pixels.";
if (outputs*leds_per_output != total_leds) gId("dig"+n+"W").innerHTML += "<br />Total LEDs doesn't match outputs * LEDs per output.";
if (last_octet == 255) {
if (total_leds <= 1024) gId("dig"+n+"W").innerHTML += "<br />Art-Net is in broadcast mode.";
if (total_leds > 1024) gId("dig"+n+"W").innerHTML += "<br />You are sending a lot of broadcast data. Be cautious.";
}
}
if (!(t > 28 && t < 32)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping
gId("dig"+n+"c").style.display = ((t >= 40 && t < 48)||(t >= 100 && t < 110)) ? "none":"inline"; // hide count for analog and HUB75
gId("dig"+n+"r").style.display = (t >= 80) && (t < 100) ? "none":"inline"; // hide reversed for virtual, except for HUB75
@@ -314,6 +338,7 @@
gId("lc").textContent = sLC;
gId("pc").textContent = (sLC == sPC) ? "":"(" + sPC + " physical)";
// memory usage and warnings
gId('m0').innerHTML = memu;
bquot = memu / maxM * 100;
@@ -420,6 +445,10 @@ ${i+1}:
<span id="p2d${i}"></span><input type="number" name="L2${i}" class="s" onchange="UI()"/>
<span id="p3d${i}"></span><input type="number" name="L3${i}" class="s" onchange="UI()"/>
<span id="p4d${i}"></span><input type="number" name="L4${i}" class="s" onchange="UI()"/>
<div id="dig${i}O" style="display:inline"><br>Number of Outputs: <input type="number" name="AO${i}" min="1" max="255" value="1" oninput="UI()"></div>
<div id="dig${i}L" style="display:inline"><br>LEDs Per Output: <input type="number" name="AL${i}" min="1" max="65535" value="1" oninput="UI()"></div>
<div id="dig${i}F" style="display:inline"><br>FPS Limit: <input type="number" name="AF${i}" min="1" max="255" value="30" oninput="UI()"></div>
<div id="dig${i}W" style="display:inline"><br>Art-Net Warnings Go Here.</div>
<div id="dig${i}r" style="display:inline"><br><span id="rev${i}">Reversed</span>: <input type="checkbox" name="CV${i}"></div>
<div id="dig${i}s" style="display:inline"><br>Skip first LEDs: <input type="number" name="SL${i}" min="0" max="255" value="0" oninput="UI()"></div>
<div id="dig${i}f" style="display:inline"><br><span id="ref${i}">Off Refresh</span>: <input id="rf${i}" type="checkbox" name="RF${i}"></div>

View File

@@ -241,7 +241,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=tru
//udp.cpp
void notify(byte callMode, bool followUp=false);
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8_t *buffer, uint8_t bri=255, bool isRGBW=false);
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8_t *buffer, uint8_t bri=255, bool isRGBW=false, uint8_t artnet_outouts=1, uint16_t artnet_leds_per_output=1, uint8_t artnet_fps_limit=1);
void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC);
void exitRealtime();
void handleNotifications();

View File

@@ -95,8 +95,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
}
uint8_t colorOrder, type, skip, awmode, channelSwap;
uint16_t length, start;
uint8_t colorOrder, type, skip, awmode, channelSwap, artnet_outputs, artnet_fps_limit;
uint16_t length, start, artnet_leds_per_output;
uint8_t pins[5] = {255, 255, 255, 255, 255};
autoSegments = request->hasArg(F("MS"));
@@ -110,6 +110,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
bool busesChanged = false;
for (uint8_t s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) {
// "48+s" means the ASCII character "0", so 48+1 = ASCII for "1", etc - and "[3]=0" means null-terminate the string.
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin
char lc[4] = "LC"; lc[2] = 48+s; lc[3] = 0; //strip length
char co[4] = "CO"; co[2] = 48+s; co[3] = 0; //strip color order
@@ -121,6 +122,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //channel swap
char sp[4] = "SP"; sp[2] = 48+s; sp[3] = 0; //bus clock speed (DotStar & PWM)
char ao[4] = "AO"; ao[2] = 48+s; ao[3] = 0; //Art-Net outputs
char al[4] = "AL"; al[2] = 48+s; al[3] = 0; //Art-Net LEDs per output
char af[4] = "AF"; af[2] = 48+s; af[3] = 0; //Art-Net FPS limit
if (!request->hasArg(lp)) {
DEBUG_PRINT(F("No data for "));
DEBUG_PRINTLN(s);
@@ -167,10 +171,13 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
channelSwap = Bus::hasWhite(type) ? request->arg(wo).toInt() : 0;
type |= request->hasArg(rf) << 7; // off refresh override
artnet_outputs = (request->hasArg(ao)) ? request->arg(ao).toInt() : 1;
artnet_leds_per_output = (request->hasArg(al)) ? request->arg(al).toInt() : length;
artnet_fps_limit = (request->hasArg(af)) ? request->arg(af).toInt() : 33333/length;
// actual finalization is done in WLED::loop() (removing old busses and adding new)
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz);
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz, artnet_outputs, artnet_leds_per_output, artnet_fps_limit);
busesChanged = true;
}
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed

View File

@@ -759,10 +759,17 @@ void sendSysInfoUDP()
// isRGBW - true if the buffer contains 4 components per pixel
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};
static const byte ART_NET_HEADER[12] PROGMEM = {0x41,0x72,0x74,0x2d,0x4e,0x65,0x74,0x00,0x00,0x50,0x00,0x0e};
static uint_fast16_t framenumber = 0;
#if defined(ARDUINO_ARCH_ESP32P4)
extern "C" {
int p4_mul16x16(uint8_t* outpacket, uint8_t* brightness, uint16_t num_loops, uint8_t* pixelbuffer);
}
#endif
uint8_t IRAM_ATTR realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8_t *buffer, uint8_t bri, bool isRGBW, uint8_t outputs, uint16_t leds_per_output, uint8_t fps_limit) {
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
WiFiUDP ddpUdp;
@@ -838,58 +845,170 @@ 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
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;
static uint_fast16_t artnetlimiter = millis()+(1000/fps_limit);
while (artnetlimiter > micros()) {
if (ArtNetSkipFrame) {
return 0; // Let WLED keep generating effect frames and we output an Art-Net frame when fps_limit is reached.
} else {
delayMicroseconds(10); // Make WLED obey fps_limit and just delay here until we're ready to send a frame.
}
}
uint32_t channel = 0;
size_t bufferOffset = 0;
/*
WLED rendering Art-Net data considers itself to be 1 hardware output with many universes - but
many Art-Net controllers like the H807SA can be manually set to "X universes per output" or in
some cases "X channels per port" - which is the same thing, just expressed differently.
We need to know the LEDs per output so we can break the pixel data across physically attached universes.
The H807SA obeys the "510 channels for RGB" rule like WLED and xLights - some other controllers do not care,
but we're not supporting those here. If you run into one of these, override ARTNET_CHANNELS_PER_PACKET to 512.
*/
#ifdef ARTNET_TIMER
uint_fast16_t datatotal = 0;
uint_fast16_t packetstotal = 0;
#endif
uint_fast16_t timer = micros();
AsyncUDP artnetudp;// AsyncUDP so we can just blast packets.
const uint_fast16_t ARTNET_CHANNELS_PER_PACKET = isRGBW?512:510; // 512/4=128 RGBW LEDs, 510/3=170 RGB LEDs
uint_fast16_t bufferOffset = 0;
uint_fast16_t hardware_output_universe = 0;
sequenceNumber++;
for (size_t currentPacket = 0; currentPacket < packetCount; currentPacket++) {
if (sequenceNumber == 0 || sequenceNumber > 255) sequenceNumber = 1;
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
for (uint_fast16_t hardware_output = 0; hardware_output < outputs; hardware_output++) {
if (bufferOffset > length * (isRGBW?4:3)) {
// This stop is reached if we don't have enough pixels for the defined Art-Net output.
return 1; // stop when we hit end of LEDs
}
size_t packetSize = ARTNET_CHANNELS_PER_PACKET;
// hardware_output_universe = hardware_outputs_universe_start[hardware_output];
if (currentPacket == (packetCount - 1U)) {
// last packet
if (channelCount % ARTNET_CHANNELS_PER_PACKET) {
packetSize = channelCount % ARTNET_CHANNELS_PER_PACKET;
uint_fast16_t channels_remaining = leds_per_output * (isRGBW?4:3);
while (channels_remaining > 0) {
uint_fast16_t packetSize = ARTNET_CHANNELS_PER_PACKET;
if (channels_remaining < ARTNET_CHANNELS_PER_PACKET) {
packetSize = channels_remaining;
channels_remaining = 0;
} else {
channels_remaining -= packetSize;
}
#ifdef ARTNET_TIMER
packetstotal++;
datatotal += packetSize + 18;
#endif
// set the parts of the Art-Net packet header that change:
packet_buffer[12] = sequenceNumber;
// packet_buffer[13] = 0; // "The physical input port from which DMX512 data was input. This field is used by the receiving device to discriminate between packets with identical Port-Address that have been generated by different input ports and so need to be merged."
packet_buffer[14] = hardware_output_universe;
packet_buffer[15] = hardware_output_universe >> 8; // needed for universes > 255
packet_buffer[16] = packetSize >> 8;
packet_buffer[17] = packetSize;
#ifdef ARTNET_TESTING_ZEROS
bri = 0; // Set all brightness to 0 but keep all calculations the same and keep sending packets.
#endif
#if defined(ARDUINO_ARCH_ESP32P4)
p4_mul16x16(packet_buffer+18, &bri, (packetSize >> 4)+1, buffer+bufferOffset);
#else
if (bri == 255) { // speed hack - don't adjust brightness if full brightness
memcpy(packet_buffer+18, buffer+bufferOffset, packetSize);
} else {
for (uint_fast16_t i = 0; i < packetSize; i+=(isRGBW?4:3)) {
// set brightness values in the packet - seems slightly faster than scale8()?
// for some reason, doing 3 (or 4) at a time is 200 micros faster than 1 at a time.
packet_buffer[i+18] = (buffer[bufferOffset+i] * bri) >> 8;
packet_buffer[i+19] = (buffer[bufferOffset+i+1] * bri) >> 8;
packet_buffer[i+20] = (buffer[bufferOffset+i+2] * bri) >> 8;
if (isRGBW) packet_buffer[i+21] = (buffer[bufferOffset+i+3] * bri) >> 8;
}
}
#endif
bufferOffset += packetSize;
if (!artnetudp.writeTo(packet_buffer,packetSize+18, client, ARTNET_DEFAULT_PORT)) {
DEBUG_PRINTLN(F("Art-Net artnetudp.writeTo() returned an error"));
return 1; // borked
}
hardware_output_universe++;
}
}
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
// Send Art-Net sync. Just reuse the packet and adjust.
// This should get re-written on the next run.
// After the first sync packet, and assuming 1 sync packet every 4
// seconds at least, should keep Art-Net nodes in synchronous mode.
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
}
// This is very much untested and generally not needed unless you
// have several Art-Net devices being broadcast to, and should only
// be called in that situation.
// Art-Net broadcast mode (setting Art-Net to 255.255.255.255) should ONLY
// be used if you know what you're doing, as that is a lot of pixels being
// sent to EVERYTHING on your network, including WiFi devices - and can
// overwhelm them if you have a lot of Art-Net data being broadcast.
if (!ddpUdp.endPacket()) {
DEBUG_PRINTLN(F("Art-Net WiFiUDP.endPacket returned an error"));
#ifdef ARTNET_SYNC_ENABLED
// This block sends Art-Net "ArtSync" packets. Can't do this with AsyncUDP because it doesn't support source port binding.
// Tested on Art-Net qualifier software but not on real hardware with known support for ArtSync.
// Doesn't seem to do anything on my gear, so it's disabled.
// packet_buffer[8] = 0x00; // ArtSync opcode low byte (low byte is same as ArtDmx, 0x00)
packet_buffer[9] = 0x52; // ArtSync opcode high byte
packet_buffer[12] = 0x00; // Aux1 - Transmit as 0. This is normally the sequence number in ArtDMX packets.
// packet_buffer[13] = 0x00; // Aux2 - Transmit as 0 - this should be 0 anyway in the packet already
#ifdef ARTNET_SYNC_STRICT
WiFiUDP artnetsync;
artnetsync.begin(ETH.localIP(), ARTNET_DEFAULT_PORT);
artnetsync.beginPacket(IPADDR_BROADCAST,ARTNET_DEFAULT_PORT);
artnetsync.write(packet_buffer,14);
if (!artnetsync.endPacket()) {
DEBUG_PRINTLN(F("Art-Net Sync Broadcast Strict returned an error"));
return 1; // borked
}
channel += packetSize;
}
} break;
#else
if (!artnetudp.broadcastTo(packet_buffer,14,ARTNET_DEFAULT_PORT)) {
DEBUG_PRINTLN(F("Art-Net Sync Broadcast returned an error"));
return 1; // borked
}
#endif
#ifdef ARTNET_TIMER
packetstotal++;
datatotal += 14;
#endif
#endif
artnetlimiter = micros()+(1000000/fps_limit)-(micros()-timer);
// This is the proper stop if pixels = Art-Net output.
#ifdef ARTNET_TIMER
float mbps = (datatotal*8)/((micros()-timer)*0.95367431640625f);
// the "micros()" calc is just to limit the print to a more random debug output so it doesn't overwhelm the terminal
if (micros() % 100 < 3) USER_PRINTF("UDP for %u pixels took %lu micros. %u data in %u total packets. %2.2f mbit/sec at %u FPS.\n",length, micros()-timer, datatotal, packetstotal, mbps, strip.getFps());
#endif
break;
}
}
return 0;
}

View File

@@ -424,6 +424,7 @@ void getSettingsJS(AsyncWebServerRequest* request, byte subPage, char* dest) //W
for (uint8_t s=0; s < busses.getNumBusses(); s++) {
Bus* bus = busses.getBus(s);
if (bus == nullptr) continue;
// "48+s" means the ASCII character "0", so 48+1 = ASCII for "1", etc - and "[3]=0" means null-terminate the string.
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin
char lc[4] = "LC"; lc[2] = 48+s; lc[3] = 0; //strip length
char co[4] = "CO"; co[2] = 48+s; co[3] = 0; //strip color order
@@ -435,6 +436,9 @@ void getSettingsJS(AsyncWebServerRequest* request, byte subPage, char* dest) //W
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //swap channels
char sp[4] = "SP"; sp[2] = 48+s; sp[3] = 0; //bus clock speed
char ao[4] = "AO"; ao[2] = 48+s; ao[3] = 0; //Art-Net outputs
char al[4] = "AL"; al[2] = 48+s; al[3] = 0; //Art-Net LEDs per output
char af[4] = "AF"; af[2] = 48+s; af[3] = 0; //Art-Net FPS limit
oappend(SET_F("addLEDs(1);"));
uint8_t pins[5];
uint8_t nPins = bus->getPins(pins);
@@ -451,6 +455,9 @@ void getSettingsJS(AsyncWebServerRequest* request, byte subPage, char* dest) //W
sappend('c',rf,bus->isOffRefreshRequired());
sappend('v',aw,bus->getAutoWhiteMode());
sappend('v',wo,bus->getColorOrder() >> 4);
sappend('v',ao,bus->get_artnet_outputs());
sappend('v',al,bus->get_artnet_leds_per_output());
sappend('v',af,bus->get_artnet_fps_limit());
uint16_t speed = bus->getFrequency();
if (bus->getType() > TYPE_ONOFF && bus->getType() < 48) {
switch (speed) {