Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### ✨ Added

---
- Support of RGBW in **e1.31** led devices.

### 🔧 Changed

Expand Down
1 change: 1 addition & 0 deletions assets/webconfig/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@
"edt_dev_spec_devices_discovery_inprogress": "Discovery in progress",
"edt_dev_spec_dithering_title": "Dithering",
"edt_dev_spec_dmaNumber_title": "DMA channel",
"edt_dev_spec_dmx_max_title": "DMX maximal number of channels",
"edt_dev_spec_fullBrightnessAtStart_title": "Full brightness at start",
"edt_dev_spec_gamma_title": "Gamma",
"edt_dev_spec_globalBrightnessControlMaxLevel_title": "Max Current Level",
Expand Down
57 changes: 46 additions & 11 deletions libsrc/leddevice/dev_net/LedDeviceUdpE131.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const int DMX_MAX = 512; // 512 usable slots

LedDeviceUdpE131::LedDeviceUdpE131(const QJsonObject &deviceConfig)
: ProviderUdp(deviceConfig)
, _whiteAlgorithm(RGBW::WhiteAlgorithm::INVALID)
{
}

Expand All @@ -58,9 +59,21 @@ bool LedDeviceUdpE131::init(const QJsonObject &deviceConfig)
_port = deviceConfig[CONFIG_PORT].toInt(E131_DEFAULT_PORT);

_e131_universe = deviceConfig["universe"].toInt(1);
_e131_dmx_max = deviceConfig["dmx-max"].toInt(DMX_MAX);
Copy link
Collaborator

@Lord-Grey Lord-Grey Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that _e131_dmx_max must be always <= DMX_MAX?
The ensure that _e131_dmx_max = DMX_MAX, if greater

Suggested change
_e131_dmx_max = deviceConfig["dmx-max"].toInt(DMX_MAX);
_e131_dmx_max = deviceConfig["dmx-max"].toInt(DMX_MAX);
if (_e131_dmx_max > DMX_MAX)
{
_e131_dmx_max > DMX_MAX);
Warning(_log, "Maximum channels configured [%d] cannot exceed maximum channels defined by the E1.31 protocol. Corrected to %d channels.", _e131_dmx_max, DMX_MAX)
}

_e131_source_name = deviceConfig["source-name"].toString("hyperion on "+QHostInfo::localHostName());
QString _json_cid = deviceConfig["cid"].toString("");

// Initialize white algorithm
QString whiteAlgorithmStr = deviceConfig["whiteAlgorithm"].toString("white_off");
_whiteAlgorithm = RGBW::stringToWhiteAlgorithm(whiteAlgorithmStr);
if (_whiteAlgorithm == RGBW::WhiteAlgorithm::INVALID)
{
QString errortext = QString("unknown whiteAlgorithm: %1").arg(whiteAlgorithmStr);
this->setInError(errortext);
return false;
}
Debug(_log, "whiteAlgorithm : %s", QSTRING_CSTR(whiteAlgorithmStr));

if (_json_cid.isEmpty())
{
_e131_cid = QUuid::createUuid();
Expand Down Expand Up @@ -138,35 +151,57 @@ void LedDeviceUdpE131::prepare(unsigned this_universe, unsigned this_dmxChannelC

int LedDeviceUdpE131::write(const std::vector<ColorRgb> &ledValues)
{
int retVal = 0;
int retVal = 0;
int thisChannelCount = 0;
int dmxChannelCount = _ledRGBCount;
const uint8_t * rawdata = reinterpret_cast<const uint8_t *>(ledValues.data());
int dmxChannelCount = (_whiteAlgorithm == RGBW::WhiteAlgorithm::WHITE_OFF) ? _ledRGBCount : _ledRGBWCount; // if white_off we expect 3 channels per LED otherwise 4
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dmxChannelCount can be calculated once in init, next to allocating the vector

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suggestion has been addressed by Jules. Need to check if it builds still.


// Create a temporary buffer for RGB or RGBW data
std::vector<uint8_t> tempBuffer(dmxChannelCount);
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tempBuffer is allocated on every write() call. Consider making this a member variable (like _temp_rgbw) and resizing it only when needed, to avoid repeated allocations for every frame.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@Lord-Grey Lord-Grey Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might have a look at libsrc/leddevice/dev_ftdi/LedDeviceWs2812_ftdi.cpp
where an _ledBuffer is prepared durting init

uint8_t* rawDataPtr = tempBuffer.data();

int currentChannel = 0;
for (const ColorRgb& color : ledValues)
{
if (_whiteAlgorithm == RGBW::WhiteAlgorithm::WHITE_OFF)
{
rawDataPtr[currentChannel++] = color.red;
rawDataPtr[currentChannel++] = color.green;
rawDataPtr[currentChannel++] = color.blue;
}
else
{
RGBW::Rgb_to_Rgbw(color, &_temp_rgbw, _whiteAlgorithm);
rawDataPtr[currentChannel++] = _temp_rgbw.red;
rawDataPtr[currentChannel++] = _temp_rgbw.green;
rawDataPtr[currentChannel++] = _temp_rgbw.blue;
rawDataPtr[currentChannel++] = _temp_rgbw.white;
}
}

_e131_seq++;

for (int rawIdx = 0; rawIdx < dmxChannelCount; rawIdx++)
{
if (rawIdx % DMX_MAX == 0) // start of new packet
if (rawIdx % _e131_dmx_max == 0) // start of new packet
{
thisChannelCount = (dmxChannelCount - rawIdx < DMX_MAX) ? dmxChannelCount % DMX_MAX : DMX_MAX;
// is this the last packet? ? ^^ last packet : ^^ earlier packets
thisChannelCount = (dmxChannelCount - rawIdx < _e131_dmx_max) ? dmxChannelCount % _e131_dmx_max : _e131_dmx_max;
// is this the last packet? ? ^^ last packet : ^^ earlier packets

prepare(_e131_universe + rawIdx / DMX_MAX, thisChannelCount);
prepare(_e131_universe + rawIdx / _e131_dmx_max, thisChannelCount);
e131_packet.sequence_number = _e131_seq;
}

e131_packet.property_values[1 + rawIdx%DMX_MAX] = rawdata[rawIdx];
e131_packet.property_values[1 + rawIdx % _e131_dmx_max] = rawDataPtr[rawIdx];

// is this the last byte of last packet || last byte of other packets
if ( (rawIdx == dmxChannelCount-1) || (rawIdx %DMX_MAX == DMX_MAX-1) )
// is this the last byte of last packet || last byte of other packets
if ((rawIdx == dmxChannelCount - 1) || (rawIdx % _e131_dmx_max == _e131_dmx_max - 1))
{
#undef e131debug
#if e131debug
Debug (_log, "send packet: rawidx %d dmxchannelcount %d universe: %d, packetsz %d"
, rawIdx
, dmxChannelCount
, _e131_universe + rawIdx / DMX_MAX
, _e131_universe + rawIdx / _e131_dmx_max
, E131_DMP_DATA + 1 + thisChannelCount
);
#endif
Expand Down
7 changes: 7 additions & 0 deletions libsrc/leddevice/dev_net/LedDeviceUdpE131.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

// hyperion includes
#include "ProviderUdp.h"
#include "utils/ColorRgbw.h"
#include "utils/RgbToRgbw.h"

#include <QUuid>

Expand Down Expand Up @@ -139,9 +141,14 @@ class LedDeviceUdpE131 : public ProviderUdp
e131_packet_t e131_packet;
uint8_t _e131_seq = 0;
uint8_t _e131_universe = 1;
int _e131_dmx_max = 512;
uint8_t _acn_id[12] = {0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 };
QString _e131_source_name;
QUuid _e131_cid;

// RGBW specific members
RGBW::WhiteAlgorithm _whiteAlgorithm;
ColorRgbw _temp_rgbw;
};

#endif // LEDEVICEUDPE131_H
41 changes: 39 additions & 2 deletions libsrc/leddevice/schemas/schema-e131.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
"default": 1,
"propertyOrder": 3
},
"dmx-max": {
"type": "integer",
"title": "edt_dev_spec_dmx_max_title",
"default": 512,
"propertyOrder": 4
},
"latchTime": {
"type": "integer",
"title": "edt_dev_spec_latchtime_title",
Expand All @@ -30,12 +36,43 @@
"minimum": 0,
"maximum": 1000,
"access": "expert",
"propertyOrder": 4
"propertyOrder": 5
},
"cid": {
"type": "string",
"title": "edt_dev_spec_cid_title",
"propertyOrder": 5
"propertyOrder": 6
},

"whiteAlgorithm": {
"type": "string",
"title":"edt_dev_spec_whiteLedAlgor_title",
"enum" : [
"subtract_minimum",
"sub_min_cool_adjust",
"sub_min_warm_adjust",
"cold_white",
"neutral_white",
"auto",
"auto_max",
"auto_accurate",
"white_off"
],
"default": "white_off",
"options" : {
"enum_titles" : [
"edt_dev_enum_subtract_minimum",
"edt_dev_enum_sub_min_cool_adjust",
"edt_dev_enum_sub_min_warm_adjust",
"edt_dev_enum_cold_white",
"edt_dev_enum_neutral_white",
"edt_dev_enum_auto",
"edt_dev_enum_auto_max",
"edt_dev_enum_auto_accurate",
"edt_dev_enum_white_off"
]
},
"propertyOrder" : 7
}
},
"additionalProperties": true
Expand Down