ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
pixoo.cpp
Go to the documentation of this file.
1#include "pixoo.h"
3
4#include "esphome/core/log.h"
5
6#include <cmath>
7#include <cstring>
8#include <utility>
9
10namespace esphome::pixoo {
11
12ESPHOME_LOG_TAG(TAG, "pixoo");
13
14// Divoom LED-board packet protocol.
15static constexpr uint8_t PACKET_HEAD = 0xAA;
16static constexpr uint8_t PACKET_TAIL = 0xBB;
17static constexpr uint8_t CMD_DATA = 0x00;
18static constexpr uint8_t CMD_LIGHT = 0x01;
19static constexpr uint8_t CMD_UNUSED = 0x21;
20static constexpr uint8_t CMD_SET_RGB_IOUT = 0x22;
21static constexpr size_t PACKET_HEADER_LEN = 4; // head + len(2) + cmd
22static constexpr size_t PACKET_STATIC_LEN = 5; // header + tail
23static constexpr uint8_t DEFAULT_IOUT = 75; // per-channel LED current / white balance default
24
25// Pack a `0xAA len cmd data 0xBB` packet into buf; returns the packet length.
26static inline size_t build_packet(uint8_t *buf, uint8_t cmd, const uint8_t *data, uint16_t len) {
27 buf[0] = PACKET_HEAD;
28 buf[1] = static_cast<uint8_t>(len & 0xFF);
29 buf[2] = static_cast<uint8_t>((len >> 8) & 0xFF);
30 buf[3] = cmd;
31 if (data != nullptr && len > 0)
32 std::memcpy(buf + PACKET_HEADER_LEN, data, len);
33 buf[PACKET_HEADER_LEN + len] = PACKET_TAIL;
34 return len + PACKET_STATIC_LEN;
35}
36
37// Fill `total` bytes at buf with a single UNUSED padding packet.
38static inline void pad_unused(uint8_t *buf, size_t total) {
39 const uint16_t len = static_cast<uint16_t>(total - PACKET_STATIC_LEN);
40 buf[0] = PACKET_HEAD;
41 buf[1] = static_cast<uint8_t>(len & 0xFF);
42 buf[2] = static_cast<uint8_t>((len >> 8) & 0xFF);
43 buf[3] = CMD_UNUSED;
44 buf[total - 1] = PACKET_TAIL;
45}
46
48
50 const uint32_t num_pixels = static_cast<uint32_t>(this->model_) * this->model_;
51 this->data_size_ = num_pixels * 3;
52 // The frame is a DATA packet (header + RGB888 + tail) followed by a DMA-chunk-sized UNUSED
53 // packet, so the LED board completes its final DMA block.
54 this->frame_size_ = this->data_size_ + PACKET_STATIC_LEN + DMA_CHUNK;
55
56 if (!this->buffer_.init(this->data_size_)) {
57 this->mark_failed(LOG_STR("Failed to allocate draw buffer"));
58 return;
59 }
60
61 // The frame is shipped in one SPI transfer, so keep it in DMA-capable internal RAM.
63 this->frame_buffer_ = allocator.allocate(this->frame_size_);
64 if (this->frame_buffer_ == nullptr) {
65 this->buffer_.free();
66 this->mark_failed(LOG_STR("Failed to allocate frame buffer"));
67 return;
68 }
69 std::memset(this->frame_buffer_, 0, this->frame_size_);
70 // Pre-build the constant DATA-packet framing; only the RGB888 payload changes per frame.
71 this->frame_buffer_[0] = PACKET_HEAD;
72 this->frame_buffer_[1] = static_cast<uint8_t>(this->data_size_ & 0xFF);
73 this->frame_buffer_[2] = static_cast<uint8_t>((this->data_size_ >> 8) & 0xFF);
74 this->frame_buffer_[3] = CMD_DATA;
75 this->frame_buffer_[PACKET_HEADER_LEN + this->data_size_] = PACKET_TAIL;
76 pad_unused(this->frame_buffer_ + this->data_size_ + PACKET_STATIC_LEN, DMA_CHUNK);
77
78 this->spi_setup();
79
80 this->buffer_.fill(0x00);
81
82 // Set the per-channel LED current. Brightness is controlled separately via the light platform.
83 const uint8_t iout[3] = {DEFAULT_IOUT, DEFAULT_IOUT, DEFAULT_IOUT};
84 this->send_command_(CMD_SET_RGB_IOUT, iout, 3);
85
86 // Frames are pushed synchronously inside update(), so there is no loop() work to do and the
87 // component is idle between updates. Marking it done (LOOP_DONE) lets LVGL's
88 // update_when_display_idle option treat the panel as idle and drive frames on demand.
89 this->disable_loop();
90}
91
92void Pixoo::send_command_(uint8_t cmd, const uint8_t *data, uint16_t len) {
93 std::memset(this->cmd_buffer_, 0, DMA_CHUNK);
94 const size_t used = build_packet(this->cmd_buffer_, cmd, data, len);
95 if (DMA_CHUNK - used >= PACKET_STATIC_LEN)
96 pad_unused(this->cmd_buffer_ + used, DMA_CHUNK - used);
97 this->enable();
98 this->write_array(this->cmd_buffer_, DMA_CHUNK);
99 this->disable();
100}
101
102void Pixoo::set_panel_brightness(float brightness) {
103 const uint8_t pct = static_cast<uint8_t>(lroundf(clamp(brightness, 0.0f, 1.0f) * 100.0f));
104 this->send_command_(CMD_LIGHT, &pct, 1);
105}
106
108 this->do_update_();
109 for (size_t i = 0; i < this->data_size_; i++)
110 this->frame_buffer_[PACKET_HEADER_LEN + i] = this->buffer_[i];
111 this->enable();
112 this->write_array(this->frame_buffer_, this->frame_size_);
113 this->disable();
114}
115
116void Pixoo::set_pixel_(uint32_t index, Color color) {
117 const size_t off = static_cast<size_t>(index) * 3;
118 this->buffer_[off] = color.r;
119 this->buffer_[off + 1] = color.g;
120 this->buffer_[off + 2] = color.b;
121}
122
123void HOT Pixoo::draw_pixel_at(int x, int y, Color color) {
124 if (this->is_point_clipped(x, y))
125 return;
126 const int side = static_cast<int>(this->model_);
127 switch (this->rotation_) {
129 break;
131 std::swap(x, y);
132 x = side - x - 1;
133 break;
135 x = side - x - 1;
136 y = side - y - 1;
137 break;
139 std::swap(x, y);
140 y = side - y - 1;
141 break;
142 }
143 if (x < 0 || x >= side || y < 0 || y >= side)
144 return;
145 this->set_pixel_(static_cast<uint32_t>(y) * side + x, color);
146}
147
148// flatten keeps the RGB565 decode inlined in the pixel loop, as in Display's own per pixel loop.
149void __attribute__((flatten))
150Pixoo::draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorBitness bitness,
151 bool big_endian, int x_offset, int y_offset, int x_pad) {
152 // Fast path for the common LVGL/image blit: RGB565, no rotation, no active clipping.
153 // Anything else defers to the base implementation, which decodes per pixel and routes through
154 // draw_pixel_at() so rotation, clipping and other color formats stay correct.
156 this->is_clipping()) {
158 return;
159 }
160 const int side = static_cast<int>(this->model_);
161 const size_t line_stride = static_cast<size_t>(x_offset) + w + x_pad;
162 for (int y = 0; y != h; y++) {
163 const int dst_y = y_start + y;
164 if (dst_y < 0 || dst_y >= side)
165 continue;
166 size_t source_idx = (static_cast<size_t>(y_offset) + y) * line_stride + x_offset;
167 for (int x = 0; x != w; x++, source_idx++) {
168 const int dst_x = x_start + x;
169 if (dst_x < 0 || dst_x >= side)
170 continue;
171 this->set_pixel_(static_cast<uint32_t>(dst_y) * side + dst_x,
175 }
176 }
177}
178
179void Pixoo::fill(Color color) {
180 if (this->is_clipping()) {
182 return;
183 }
184 for (size_t i = 0; i < this->data_size_; i += 3) {
185 this->buffer_[i] = color.r;
186 this->buffer_[i + 1] = color.g;
187 this->buffer_[i + 2] = color.b;
188 }
189}
190
192 LOG_DISPLAY("", "Divoom Pixoo", this);
193 ESP_LOGCONFIG(TAG, " Model: %ux%u", (unsigned) this->model_, (unsigned) this->model_);
194 LOG_UPDATE_INTERVAL(this);
195}
196
197} // namespace esphome::pixoo
uint8_t h
Definition bl0906.h:2
void mark_failed()
Mark this component as failed.
void disable_loop()
Disable this component's loop.
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:2222
T * allocate(size_t n)
Definition helpers.h:2249
static Color to_color(uint32_t colorcode, ColorBitness color_bitness=ColorBitness::COLOR_BITNESS_888, bool right_bit_aligned=true)
Unpacks a color of the given bitness, red in the most significant bits.
static ESPHOME_ALWAYS_INLINE uint32_t read_packed(const uint8_t *ptr, size_t index, bool big_endian)
Reads one packed pixel of the given bitness from a source buffer into the value to_color() takes.
int int int const uint8_t ColorOrder ColorBitness bitness
Definition display.h:389
bool is_clipping() const
Definition display.h:802
int int int const uint8_t ColorOrder ColorBitness bool int int y_offset
Definition display.h:389
virtual void fill(Color color)
Fill the entire screen with the given color.
Definition display.cpp:16
int int int const uint8_t ColorOrder ColorBitness bool int int int x_pad
Definition display.h:389
int int int const uint8_t * ptr
Definition display.h:388
bool ESPHOME_ALWAYS_INLINE is_point_clipped(int x, int y) const
Whether (x, y) falls outside the active clipping rectangle.
Definition display.h:807
int int int const uint8_t ColorOrder ColorBitness bool big_endian
Definition display.h:389
int int int const uint8_t ColorOrder ColorBitness bool int x_offset
Definition display.h:389
DisplayRotation rotation_
Definition display.h:860
virtual void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad)
Draws a block for draw_pixels_at(); the parameters are the same.
Definition display.cpp:119
uint8_t cmd_buffer_[DMA_CHUNK]
Definition pixoo.h:62
void send_command_(uint8_t cmd, const uint8_t *data, uint16_t len)
Definition pixoo.cpp:92
void fill(Color color) override
Definition pixoo.cpp:179
void setup() override
Definition pixoo.cpp:49
split_buffer::SplitBuffer buffer_
Definition pixoo.h:60
PixooModel model_
Definition pixoo.h:55
void set_panel_brightness(float brightness)
Definition pixoo.cpp:102
void set_pixel_(uint32_t index, Color color)
Definition pixoo.cpp:116
void draw_pixel_at(int x, int y, Color color) override
Definition pixoo.cpp:123
static constexpr size_t DMA_CHUNK
Definition pixoo.h:53
float get_setup_priority() const override
Definition pixoo.cpp:47
uint8_t * frame_buffer_
Definition pixoo.h:61
void update() override
Definition pixoo.cpp:107
void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override
void dump_config() override
Definition pixoo.cpp:191
bool init(size_t total_length, size_t max_buffer_size=SIZE_MAX)
void fill(uint8_t value) const
Fill the entire buffer with a single byte value.
struct @66::@67 __attribute__
Wake the main loop task from an ISR. ISR-safe.
Definition main_task.h:45
@ DISPLAY_ROTATION_0_DEGREES
Definition display.h:136
@ DISPLAY_ROTATION_270_DEGREES
Definition display.h:139
@ DISPLAY_ROTATION_180_DEGREES
Definition display.h:138
@ DISPLAY_ROTATION_90_DEGREES
Definition display.h:137
ESPHOME_LOG_TAG(TAG, "pixoo")
constexpr float PROCESSOR
For components that use data from sensors like displays.
Definition component.h:47
const void size_t len
Definition hal.h:68
static void uint32_t
uint8_t g
Definition color.h:34
uint8_t b
Definition color.h:38
uint8_t r
Definition color.h:30
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6