Lamp-Da 0.1
A compact lantern project
Loading...
Searching...
No Matches
strip.h
Go to the documentation of this file.
1
5#ifndef COMPONENT_STRIP_H
6#define COMPONENT_STRIP_H
7
8// this file is active only if LMBD_LAMP_TYPE=indexable
9#ifdef LMBD_LAMP_TYPE__INDEXABLE
10
11#include <cstdint>
12#include <cstring>
13#include <array>
14
16
17// special case: include the implementation
18#include "src/generated/hal/strip_impl.hpp"
19//
20
21#include "src/system/ext/scale8.h"
22#include "src/system/ext/random8.h"
23
27
28#include "src/user/constants.h"
29
30namespace lampda {
31
32static constexpr size_t stripNbBuffers = 3;
33static constexpr float baseCurrentConsumption = 0.4f;
34static constexpr float maxCurrentConsumption = 2.7f - baseCurrentConsumption;
35static constexpr float ampPerLed = maxCurrentConsumption / (float)LED_COUNT;
36
37namespace modes::hardware {
38struct LampTy;
39}
40
41namespace component {
42
43using StripImpl_t = hal::strip::LampdaStrip<LED_COUNT, 3>;
44
46class LedStrip : private StripImpl_t
47{
48 using BufferTy = std::array<uint32_t, LED_COUNT>;
49 friend struct modes::hardware::LampTy;
50
52 static constexpr bool useColorDithering = true;
54 static constexpr bool useTemporalDithering = false;
55 static constexpr uint16_t refreshFramesCount = 10;
56
57public:
58 LedStrip(int16_t pin, neoPixelType type = NEO_RGB + NEO_KHZ800) : StripImpl_t(pin, type), shownCount(0)
59 {
60 assert(_colorErrors.size() > 0);
61
62 COLOR c;
63 c.color = 0;
64 for (uint16_t i = 0; i < LED_COUNT; ++i)
65 {
66 if constexpr (useTemporalDithering)
67 {
68 _colorErrors[i] = c;
69 }
70
71 _colors[i] = c;
72 }
73 }
74
75 void show()
76 {
77 if (hasSomeChanges)
78 {
79 // only show if some changes were made
80 show_now();
81 }
82 hasSomeChanges = false;
83 }
84
85 float estimateCurrentDraw() const
86 {
87 float estimatedCurrentDraw = 0.0;
88
89 const uint8_t b = getBrightness();
90 const float currentPerLed = lmpd_map<float>(b, 0, 255, 0.0f, ampPerLed);
91
92 for (uint16_t i = 0; i < LED_COUNT; ++i)
93 {
94 COLOR c;
95 c.color = getRawPixelColor(i);
96
97 const float res = max<float>(c.blue, max<float>(c.red, c.green));
98 if (res <= 0)
99 {
100 continue;
101 }
102
103 estimatedCurrentDraw += currentPerLed;
104 }
105 return max<float>(baseCurrentConsumption, estimatedCurrentDraw);
106 }
107
109 void setBrightness(uint8_t b) { brightness = b; }
110
112 uint8_t getBrightness() const { return brightness; }
113
114 // Accessor to set a color
115 void setPixelColor(uint16_t n, COLOR c)
116 {
117 if (n >= LED_COUNT)
118 return;
119
120 _colors[n] = c;
121 }
122
127 void begin() { StripImpl_t::begin(); }
128
131 uint32_t getRawPixelColor(uint16_t n) const
132 {
133 // ,o colors outside of strip
134 if (n >= LED_COUNT)
135 return 0;
136
137 COLOR c;
138 c.color = StripImpl_t::getPixelColor(n);
139
140 // We use brightnessAtShowTime here, or the colors can break when brightness changed
141 c.blue = restore_color_with_brightness(c.blue, brightnessAtShowTime);
142 c.green = restore_color_with_brightness(c.green, brightnessAtShowTime);
143 c.red = restore_color_with_brightness(c.red, brightnessAtShowTime);
144
145 return c.color;
146 }
147
149 void write_to_led_driver(const uint8_t writeBrightness)
150 {
151 // Adjust brightness to the desired output
152 const uint8_t capedShown = shownCount % refreshFramesCount;
153 for (uint16_t i = 0; i < LED_COUNT; ++i)
154 {
155 if constexpr (useTemporalDithering)
156 {
157 const COLOR c = convert_color_with_brightness(_colors[i], writeBrightness, i + capedShown, _colorErrors[i]);
158 // set strip color
159 StripImpl_t::setPixelColor(i, c.color);
160 }
161 else
162 {
163 const COLOR c = convert_color_with_brightness(_colors[i], writeBrightness, i + capedShown, _colorErrors[0]);
164 // set strip color
165 StripImpl_t::setPixelColor(i, c.color);
166 }
167 }
168 }
169
171 void show_now()
172 {
173 // copy the pattern to show to the display buffer
174 brightnessAtShowTime = brightness;
175 write_to_led_driver(brightnessAtShowTime);
176 // show on hardware
177 StripImpl_t::show();
178 hasSomeChanges = false;
179 // increment show count
180 auto refCount = shownCount;
181 shownCount = refCount + 1;
182 }
183
195 static uint8_t restore_color_with_brightness(uint8_t colorIn, const uint8_t brightness)
196 {
197 // only special case
198 const uint16_t colorShifted = (uint16_t)colorIn << 8;
199 if (colorShifted <= brightness or brightness == 0)
200 return 0;
201
202 uint8_t fullColor = (colorShifted - brightness) / brightness;
203 return fullColor;
204 }
205
214 static std::pair<uint8_t, uint8_t> get_brightness_color_and_error(const uint8_t colorIn,
215 uint16_t errorIn,
216 const uint8_t brightness,
217 const uint16_t index)
218 {
219 // blue noise look up table
220 static constexpr std::array<uint8_t, 64> BLUE_NOISE_LUT = {
221 0, 32, 8, 40, 2, 34, 10, 42, 48, 16, 56, 24, 50, 18, 58, 26, 12, 44, 4, 36, 14, 46,
222 6, 38, 60, 28, 52, 20, 62, 30, 54, 22, 3, 35, 11, 43, 1, 33, 9, 41, 51, 19, 59, 27,
223 49, 17, 57, 25, 15, 47, 7, 39, 13, 45, 5, 37, 63, 31, 55, 23, 61, 29, 53, 21};
224
225 // only special case
226 if (colorIn == 0)
227 return {0, 0};
228
229 // scale color by brightness
230 const uint16_t scaledColor = colorIn * brightness + brightness;
231
232 // When using standard and temporal dithering, shift the pattern
233 if constexpr (useColorDithering and useTemporalDithering)
234 {
235 const uint8_t allowedError = UINT8_MAX - (scaledColor & 0xFF);
236 errorIn += min<uint16_t>(allowedError, BLUE_NOISE_LUT[index % BLUE_NOISE_LUT.size()]);
237 }
238
239 // add error
240 const uint32_t fullColor = min<uint32_t>(scaledColor + errorIn, 0xFF00);
241
242 // compute final color and new error
243 uint32_t finalColorRaw = fullColor >> 8;
244 const uint8_t finalError = fullColor & 0xFF;
245
246 // When using only color dithering, add a blue noise error to the final color
247 if constexpr (useColorDithering and not useTemporalDithering)
248 {
249 finalColorRaw += (finalError > BLUE_NOISE_LUT[index % BLUE_NOISE_LUT.size()] ? 1 : 0);
250 }
251
252 const uint8_t finalColor = (finalColorRaw > 255) ? 255 : finalColorRaw;
253 return {finalColor, finalError};
254 }
255
265 const uint8_t brightness,
266 const uint16_t index,
267 COLOR& error)
268 {
269 // no temporal dithering: no error propagation
270 if constexpr (not useTemporalDithering)
271 {
272 error.red = 0;
273 error.green = 0;
274 error.blue = 0;
275 }
276 // convert colors and errors, with small offsets for the different chanels
277 const auto& [red, redError] = get_brightness_color_and_error(c.red, error.red, brightness, index);
278 const auto& [green, greenError] = get_brightness_color_and_error(c.green, error.green, brightness, index + 1);
279 const auto& [blue, blueError] = get_brightness_color_and_error(c.blue, error.blue, brightness, index + 2);
280
281 // store error components
282 error.red = redError;
283 error.green = greenError;
284 error.blue = blueError;
285
286 COLOR result;
287 result.red = red;
288 result.green = green;
289 result.blue = blue;
290 return result;
291 }
292
293 void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b)
294 {
295 COLOR c;
296 c.red = r;
297 c.green = g;
298 c.blue = b;
299
300 setPixelColor(n, c);
301 }
302
303 void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w)
304 {
305 COLOR c;
306 c.red = r;
307 c.green = g;
308 c.blue = b;
309 c.white = w;
310
311 setPixelColor(n, c);
312 }
313
314 void setPixelColor(uint16_t n, uint32_t c)
315 {
316 COLOR co;
317 co.color = c;
318
319 setPixelColor(n, co);
320 }
321
322 static uint16_t to_strip(uint16_t screenX, uint16_t screenY)
323 {
324 if (screenX > stripXCoordinates)
325 screenX = stripXCoordinates;
326 if (screenY > stripYCoordinates)
327 screenY = stripYCoordinates;
328
329 return lmpd_constrain<uint16_t>(screenX + screenY * stripXCoordinates, 0, LED_COUNT - 1);
330 }
331
332 void setPixelColorXY(uint16_t x, uint16_t y, COLOR c) { setPixelColor(LedStrip::to_strip(x, y), c); }
333
334 void setPixelColorXY(uint16_t x, uint16_t y, uint32_t c) { setPixelColor(LedStrip::to_strip(x, y), c); }
335
336 /*
337 * Put a value 0 to 255 in to get a color value.
338 * The colours are a transition r -> g -> b -> back to r
339 * Inspired by the Adafruit examples.
340 */
341 uint32_t color_wheel(uint8_t pos)
342 {
343 pos = 255 - pos;
344 if (pos < 85)
345 {
346 return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
347 }
348 else if (pos < 170)
349 {
350 pos -= 85;
351 return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3);
352 }
353 else
354 {
355 pos -= 170;
356 return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0);
357 }
358 }
359
360 void blur(uint8_t blur_amount)
361 {
362 if (blur_amount == 0)
363 return; // optimization: 0 means "don't blur"
364 uint8_t keep = 255 - blur_amount;
365 uint8_t seep = blur_amount >> 1;
366 COLOR carryover;
367 carryover.color = 0;
368 for (unsigned i = 0; i < LED_COUNT; i++)
369 {
370 COLOR cur;
371 cur.color = getPixelColor(i);
372 COLOR c = cur;
373 COLOR part = utils::color_fade(c, seep);
374 cur = utils::color_add(utils::color_fade(c, keep), carryover, true);
375 if (i > 0)
376 {
377 c.color = getPixelColor(i - 1);
378 setPixelColor(i - 1, utils::color_add(c, part, true));
379 }
380 setPixelColor(i, cur);
381 carryover = part;
382 }
383 }
384
385 uint32_t getPixelColor(uint16_t n) const { return _colors[lmpd_constrain<uint16_t>(n, 0, LED_COUNT - 1)].color; }
386 uint32_t getPixelColorXY(int16_t x, int16_t y) const { return getPixelColor(LedStrip::to_strip(x, y)); }
387
388 // Adds the specified color with the existing pixel color perserving color
389 // balance.
390 void addPixelColor(uint16_t n, uint32_t color, bool fast = false)
391 {
392 COLOR c1;
393 c1.color = getPixelColor(n);
394 COLOR c2;
395 c2.color = color;
396
397 setPixelColor(n, utils::color_add(c1, c2, fast));
398 }
399
400 void addPixelColorXY(uint16_t x, uint16_t y, uint32_t color, bool fast = false)
401 {
402 addPixelColor(LedStrip::to_strip(x, y), color, fast);
403 }
404
405 // signal the strip that it can display the update
406 void signal_display() { hasSomeChanges = true; }
407
408 uint32_t* get_buffer_ptr(const uint8_t index) { return _buffers[index].data(); }
409
410 void buffer_current_colors(const uint8_t index)
411 {
412 static_assert(sizeof(BufferTy) == sizeof(_colors));
413 memcpy(_buffers[index].data(), _colors, sizeof(_colors));
414 }
415
416 void fill_buffer(const uint8_t index, const uint32_t value)
417 {
418 memset(_buffers[index].data(), value, sizeof(BufferTy));
419 }
420
422 COLOR _colors[LED_COUNT];
423
425 std::array<COLOR, useTemporalDithering ? LED_COUNT : 1> _colorErrors;
426
428 BufferTy _buffers[stripNbBuffers];
429
430private:
431 volatile bool hasSomeChanges;
432
434 volatile uint8_t brightness;
435
437 volatile uint8_t brightnessAtShowTime;
438
440 volatile uint8_t shownCount;
441};
442
443} // namespace component
444} // namespace lampda
445
446#endif
447
448#endif
protected inheritence to avoid uncontroled hardware calls
Definition: strip.h:47
uint8_t getBrightness() const
Brightness is an internal flag.
Definition: strip.h:112
COLOR _colors[LED_COUNT]
store the display colors, with no brightness scaling
Definition: strip.h:422
std::array< COLOR, useTemporalDithering ? LED_COUNT :1 > _colorErrors
used for temporal dithering
Definition: strip.h:425
void setBrightness(uint8_t b)
Brightness is an internal counter.
Definition: strip.h:109
uint32_t getRawPixelColor(uint16_t n) const
Return the raw color value stored in the send buffer.
Definition: strip.h:131
BufferTy _buffers[stripNbBuffers]
buffers for computations
Definition: strip.h:428
static std::pair< uint8_t, uint8_t > get_brightness_color_and_error(const uint8_t colorIn, uint16_t errorIn, const uint8_t brightness, const uint16_t index)
Convert a color to the desired brightness level, with an error adjustment.
Definition: strip.h:214
static uint8_t restore_color_with_brightness(uint8_t colorIn, const uint8_t brightness)
Convert a color to the standard range, assuming it starts as brightness level.
Definition: strip.h:195
void begin()
Definition: strip.h:127
void show_now()
Show the current data, independant of changes.
Definition: strip.h:171
static COLOR convert_color_with_brightness(const COLOR &c, const uint8_t brightness, const uint16_t index, COLOR &error)
Convert a color to the correct brightness, with temporal dithering.
Definition: strip.h:264
This class is a lightweight port of the Adafruit_Neopixel library, with compile time buffers to have ...
Definition: strip_impl.h:30
Program scope.
Definition: control_fixed_modes.hpp:12
Implement a led strip object.
uint8_t neoPixelType
3rd arg to Adafruit_NeoPixel constructor
Definition: strip_impl.h:17
Main interface between the user and the hardware of the lamp.
Definition: lamp_type.hpp:118
Define the system hardware constants.
Use this to convert color to bytes.
Definition: utils.h:128
User defined constants, relative to specific lamp types.
Define useful functions.
Define vectors and rotation matrices, and the possibility to rotate vectors.