Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
23 changes: 12 additions & 11 deletions components/ddp/ddp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,19 @@ bool DDPComponent::process_(const uint8_t *payload, uint16_t size) {
return false;
}

// ignore packet if data offset != [00 00 00 00]. This likely means the device is receiving a DDP packet not meant for it.
// There may be a better way to handle this header field. One user was receiving packets with non-zero data offset that were
// screwing up the light effect (flickering).
if (payload[4] || payload[5] || payload[6] || payload[7]) {
ESP_LOGE(TAG, "Ignoring DDP Packet with non-zero data offset.");
return false;
}
// Extract 32-bit data offset from header bytes 4-7 (Big Endian)
uint32_t data_offset = ((uint32_t)payload[4] << 24) |
((uint32_t)payload[5] << 16) |
((uint32_t)payload[6] << 8) |
(uint32_t)payload[7];

// Convert byte offset to LED index (3 bytes per RGB pixel)
uint16_t led_offset = data_offset / 3;

ESP_LOGV(TAG,
"DDP packet received (size=%d): - %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x [%02x %02x %02x]",
size, payload[0], payload[1], payload[2], payload[3], payload[4], payload[5], payload[6], payload[7], payload[8],
payload[9], payload[10], payload[11], payload[12]);
"DDP packet received (size=%d, led_offset=%u): - %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x [%02x %02x %02x]",
size, led_offset, payload[0], payload[1], payload[2], payload[3], payload[4], payload[5], payload[6], payload[7],
payload[8], payload[9], payload[10], payload[11], payload[12]);

// first 10 bytes are the header, so consider them used from the get-go
// if timecode field is used, takes up an additional 4 bytes of header.
Expand All @@ -85,7 +86,7 @@ bool DDPComponent::process_(const uint8_t *payload, uint16_t size) {
if (used >= size) {
return false;
}
uint16_t new_used = light_effect->process_(payload, size, used);
uint16_t new_used = light_effect->process_(payload, size, used, led_offset);
if (new_used == 0) {
return false;
} else {
Expand Down
35 changes: 24 additions & 11 deletions components/ddp/ddp_addressable_light_effect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ void DDPAddressableLightEffect::start() {

// not automatically active just because enabled
this->set_effect_active_(this->get_addressable_(), false);

}

void DDPAddressableLightEffect::stop() {
Expand Down Expand Up @@ -95,7 +94,7 @@ void DDPAddressableLightEffect::apply(light::AddressableLight &it, const Color &

}

uint16_t DDPAddressableLightEffect::process_(const uint8_t *payload, uint16_t size, uint16_t used) {
uint16_t DDPAddressableLightEffect::process_(const uint8_t *payload, uint16_t size, uint16_t used, uint16_t offset) {

// disable gamma on first received packet, not just based on effect being enabled.
// that way home assistant light can still be used as normal when DDP packets are not
Expand All @@ -117,12 +116,18 @@ uint16_t DDPAddressableLightEffect::process_(const uint8_t *payload, uint16_t si
// effect is active once a ddp packet is received.
this->set_effect_active_(it, true);

// Calculate remaining pixels from the offset position to prevent overflow
int remaining_leds = 0;
if (offset < it->size()) {
remaining_leds = it->size() - offset;
}

uint16_t num_pixels = std::min<int>(it->size(), (size - used) / 3);
// Limit processed pixels by remaining strip space and packet data size
uint16_t num_pixels = std::min<int>(remaining_leds, (size - used) / 3);

if ( num_pixels < 1 ) { return 0; }

ESP_LOGV(TAG, "Applying DDP data for '%s' (size: %d - used: %d - num_pixels: %d)", get_name(), size, used, num_pixels);
ESP_LOGV(TAG, "Applying DDP data for '%s' (size: %d - used: %d - num_pixels: %d - offset: %d)", get_name(), size, used, num_pixels, offset);

// will be multiplied by RGB values in scale_* scaling modes
float multiplier = 1.0f;
Expand All @@ -131,11 +136,11 @@ uint16_t DDPAddressableLightEffect::process_(const uint8_t *payload, uint16_t si
// max out brightness in all but multiply mode, in which brightness is used.
switch (this->scaling_mode_) {
case DDP_SCALE_PACKET:
multiplier = this->scan_packet_and_return_multiplier_(payload,10,size);
multiplier = this->scan_packet_and_return_multiplier_(payload, 10, size);
set_max_brightness_();
break;
case DDP_SCALE_STRIP:
multiplier = this->scan_packet_and_return_multiplier_(payload, used, used + (num_pixels*3));
multiplier = this->scan_packet_and_return_multiplier_(payload, used, used + (num_pixels * 3));
set_max_brightness_();
break;
case DDP_NO_SCALING: // no scaling requires brightness maxed so that ddp values will be displayed raw.
Expand All @@ -145,13 +150,19 @@ uint16_t DDPAddressableLightEffect::process_(const uint8_t *payload, uint16_t si
break; // Multiply mode is default ESPHome behavior, no need to do anything to handle it.
}

uint16_t target_led = offset;

// loop through all pixels being displayed now.
for (uint16_t i = used; i < used+(num_pixels*3); i+=3) {
for (uint16_t i = used; i < used + (num_pixels * 3); i += 3) {

if (target_led >= it->size()) {
break;
}

// get RGB value of current pixel.
uint8_t red = payload[i];
uint8_t green = payload[i+1];
uint8_t blue = payload[i+2];
uint8_t green = payload[i + 1];
uint8_t blue = payload[i + 2];

// set multiplier for this pixel if in pixel scaling mode
if ( this->scaling_mode_ == DDP_SCALE_PIXEL ) {
Expand Down Expand Up @@ -183,12 +194,14 @@ uint16_t DDPAddressableLightEffect::process_(const uint8_t *payload, uint16_t si
}

// assign pixel color; clear white channel for RGBW strips
auto output = (*it)[(i-used)/3];
auto output = (*it)[target_led];
output.set_rgbw(red, green, blue, 0);

target_led++;
}

it->schedule_show();
return (num_pixels*3);
return (num_pixels * 3);
}

float DDPAddressableLightEffect::scan_packet_and_return_multiplier_(const uint8_t *payload, uint16_t start, uint16_t end) {
Expand Down
2 changes: 1 addition & 1 deletion components/ddp/ddp_addressable_light_effect.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class DDPAddressableLightEffect : public DDPLightEffectBase, public light::Addre
#endif

protected:
uint16_t process_(const uint8_t *payload, uint16_t size, uint16_t used) override;
uint16_t process_(const uint8_t *payload, uint16_t size, uint16_t used, uint16_t offset = 0) override;

float scan_packet_and_return_multiplier_(const uint8_t *payload, uint16_t start, uint16_t end);
float multiplier_from_max_val_(uint8_t max_val);
Expand Down
7 changes: 6 additions & 1 deletion components/ddp/ddp_light_effect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ void DDPLightEffect::apply() {

}

uint16_t DDPLightEffect::process_(const uint8_t *payload, uint16_t size, uint16_t used) {
uint16_t DDPLightEffect::process_(const uint8_t *payload, uint16_t size, uint16_t used, uint16_t offset) {

// Ignore fragments meant for subsequent devices when operating in single bulb mode
if (offset > 0) {
return (size - used);
}

// at least for now, we require 3 bytes of data (r, g, b).
// If there aren't 3 unused bytes, return 0 to indicate error.
Expand Down
2 changes: 1 addition & 1 deletion components/ddp/ddp_light_effect.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class DDPLightEffect : public DDPLightEffectBase, public light::LightEffect {
void apply() override;

protected:
uint16_t process_(const uint8_t *payload, uint16_t size, uint16_t used) override;
uint16_t process_(const uint8_t *payload, uint16_t size, uint16_t used, uint16_t offset = 0) override;
};

} // namespace ddp
Expand Down
2 changes: 1 addition & 1 deletion components/ddp/ddp_light_effect_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class DDPLightEffectBase {

DDPScalingMode scaling_mode_{DDP_NO_SCALING};

virtual uint16_t process_(const uint8_t *payload, uint16_t size, uint16_t used) = 0;
virtual uint16_t process_(const uint8_t *payload, uint16_t size, uint16_t used, uint16_t offset = 0) = 0;

friend class DDPComponent;
};
Expand Down