Firmware SDK
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
bc_led_strip.c
1 #include <bc_led_strip.h>
2 
3 #define BC_LED_STRIP_NULL_TASK BC_SCHEDULER_MAX_TASKS + 1
4 
5 static uint32_t _bc_led_strip_wheel(int position);
6 static void _bc_led_strip_get_heat_map_color(float value, float *red, float *green, float *blue);
7 
8 void bc_led_strip_init(bc_led_strip_t *self, const bc_led_strip_driver_t *driver, const bc_led_strip_buffer_t *buffer)
9 {
10  memset(self, 0x00, sizeof(bc_led_strip_t));
11  self->_buffer = buffer;
12  self->_driver = driver;
13  self->_effect.task_id = BC_LED_STRIP_NULL_TASK;
14  self->_driver->init(self->_buffer);
15  self->_brightness = 255;
16 }
17 
18 void bc_led_strip_set_event_handler(bc_led_strip_t *self, void (*event_handler)(bc_led_strip_t *, bc_led_strip_event_t, void *), void *event_param)
19 {
20  self->_event_handler = event_handler;
21  self->_event_param = event_param;
22 }
23 
24 int bc_led_strip_get_pixel_count(bc_led_strip_t *self)
25 {
26  return self->_buffer->count;
27 }
28 
29 bc_led_strip_type_t bc_led_strip_get_strip_type(bc_led_strip_t *self)
30 {
31  return self->_buffer->type;
32 }
33 
34 void bc_led_strip_set_pixel(bc_led_strip_t *self, int position, uint32_t color)
35 {
36  if (position < 0 || position >= self->_buffer->count)
37  {
38  return;
39  }
40 
41  if (self->_brightness != 255)
42  {
43  bc_led_strip_set_pixel_rgbw(self, position, color >> 24, color >> 16, color >> 8, color);
44  }
45  else
46  {
47  self->_driver->set_pixel(position, color);
48  }
49 }
50 
51 void bc_led_strip_set_pixel_rgbw(bc_led_strip_t *self, int position, uint8_t r, uint8_t g, uint8_t b, uint8_t w)
52 {
53  if (position < 0 || position >= self->_buffer->count)
54  {
55  return;
56  }
57 
58  if (self->_brightness != 255)
59  {
60  r = ((uint16_t) r * self->_brightness) >> 8;
61  g = ((uint16_t) g * self->_brightness) >> 8;
62  b = ((uint16_t) b * self->_brightness) >> 8;
63  w = ((uint16_t) w * self->_brightness) >> 8;
64  }
65  self->_driver->set_pixel_rgbw(position, r, g, b, w);
66 }
67 
68 bool bc_led_strip_set_rgbw_framebuffer(bc_led_strip_t *self, uint8_t *framebuffer, size_t length)
69 {
70  if (length > (size_t) (self->_buffer->type * self->_buffer->count))
71  {
72  return false;
73  }
74 
75  int position = 0;
76 
77  if (self->_buffer->type == BC_LED_STRIP_TYPE_RGBW)
78  {
79  for (size_t i = 0; i < length; i += self->_buffer->type)
80  {
81  self->_driver->set_pixel_rgbw(position++, framebuffer[i], framebuffer[i + 1], framebuffer[i + 2], framebuffer[i + 3]);
82  }
83  }
84  else
85  {
86  for (size_t i = 0; i < length; i += self->_buffer->type)
87  {
88  self->_driver->set_pixel_rgbw(position++, framebuffer[i], framebuffer[i + 1], framebuffer[i + 2], 0);
89  }
90  }
91 
92  return true;
93 }
94 
95 void bc_led_strip_fill(bc_led_strip_t *self, uint32_t color)
96 {
97  for (int i = 0; i < self->_buffer->count; i++)
98  {
99  bc_led_strip_set_pixel(self, i, color);
100  }
101 }
102 
103 bool bc_led_strip_write(bc_led_strip_t *self)
104 {
105  return self->_driver->write();
106 }
107 
108 bool bc_led_strip_is_ready(bc_led_strip_t *self)
109 {
110  return self->_driver->is_ready();
111 }
112 
113 void bc_led_strip_set_brightness(bc_led_strip_t *self, uint8_t brightness)
114 {
115  self->_brightness = brightness;
116 }
117 
118 void bc_led_strip_effect_stop(bc_led_strip_t *self)
119 {
120  if (self->_effect.task_id != BC_LED_STRIP_NULL_TASK)
121  {
122  bc_scheduler_unregister(self->_effect.task_id);
123 
124  self->_effect.task_id = BC_LED_STRIP_NULL_TASK;
125  }
126 }
127 
128 static void _bc_led_strip_effect_done(bc_led_strip_t *self)
129 {
130  bc_led_strip_effect_stop(self);
131 
132  if (self->_event_handler != NULL)
133  {
134  self->_event_handler(self, BC_LED_STRIP_EVENT_EFFECT_DONE, self->_event_param);
135  }
136 }
137 
138 static void _bc_led_strip_effect_test_task(void *param)
139 {
140  bc_led_strip_t *self = (bc_led_strip_t *)param;
141 
142  if (!self->_driver->is_ready())
143  {
145 
146  return;
147  }
148 
149  uint8_t intensity = 255 * (self->_effect.led + 1) / (self->_buffer->count + 1);
150 
151  if (self->_effect.round == 0)
152  {
153  self->_driver->set_pixel_rgbw(self->_effect.led, intensity, 0, 0, 0);
154  }
155  else if (self->_effect.round == 1)
156  {
157  self->_driver->set_pixel_rgbw(self->_effect.led, 0, intensity, 0, 0);
158  }
159  else if (self->_effect.round == 2)
160  {
161  self->_driver->set_pixel_rgbw(self->_effect.led, 0, 0, intensity, 0);
162  }
163  else if (self->_effect.round == 3)
164  {
165  if (self->_buffer->type == BC_LED_STRIP_TYPE_RGBW)
166  {
167  self->_driver->set_pixel_rgbw(self->_effect.led, 0, 0, 0, intensity);
168  }
169  else
170  {
171  self->_driver->set_pixel_rgbw(self->_effect.led, intensity, intensity, intensity, 0);
172  }
173  }
174  else
175  {
176  self->_driver->set_pixel_rgbw(self->_effect.led, 0, 0, 0, 0);
177  }
178 
179  self->_effect.led++;
180 
181  if (self->_effect.led == self->_buffer->count)
182  {
183  self->_effect.led = 0;
184 
185  self->_effect.round++;
186  }
187 
188  self->_driver->write();
189 
190  if (self->_effect.round == 5)
191  {
192  _bc_led_strip_effect_done(self);
193  return;
194  }
195 
196  bc_scheduler_plan_current_relative(self->_effect.wait);
197 }
198 
199 void bc_led_strip_effect_test(bc_led_strip_t *self)
200 {
201  bc_led_strip_effect_stop(self);
202 
203  self->_effect.led = 0;
204  self->_effect.round = 0;
205  self->_effect.wait = 2000 / self->_buffer->count;
206 
207  bc_led_strip_fill(self, 0x00000000);
208 
209  self->_effect.task_id = bc_scheduler_register(_bc_led_strip_effect_test_task, self, 0);
210 }
211 
212 static void _bc_led_strip_effect_rainbow_task(void *param)
213 {
214  bc_led_strip_t *self = (bc_led_strip_t *)param;
215 
216  if (!self->_driver->is_ready())
217  {
219 
220  return;
221  }
222 
223  for(int i = 0; i< self->_buffer->count; i++) {
224  bc_led_strip_set_pixel(self, i, _bc_led_strip_wheel((i + self->_effect.round) & 255));
225  }
226 
227  self->_effect.round++;
228 
229  self->_driver->write();
230 
231  bc_scheduler_plan_current_relative(self->_effect.wait);
232 }
233 
234 void bc_led_strip_effect_rainbow(bc_led_strip_t *self, bc_tick_t wait)
235 {
236  bc_led_strip_effect_stop(self);
237 
238  self->_effect.round = 0;
239  self->_effect.wait = wait;
240 
241  self->_effect.task_id = bc_scheduler_register(_bc_led_strip_effect_rainbow_task, self, 0);
242 }
243 
244 static void _bc_led_strip_effect_rainbow_cycle_task(void *param)
245 {
246  bc_led_strip_t *self = (bc_led_strip_t *)param;
247 
248  if (!self->_driver->is_ready())
249  {
251 
252  return;
253  }
254 
255  for(int i = 0; i< self->_buffer->count; i++) {
256  bc_led_strip_set_pixel(self, i, _bc_led_strip_wheel(((i * 256 / self->_buffer->count) + self->_effect.round) & 255));
257  }
258 
259  self->_effect.round++;
260 
261  self->_driver->write();
262 
263  bc_scheduler_plan_current_relative(self->_effect.wait);
264 }
265 
266 void bc_led_strip_effect_rainbow_cycle(bc_led_strip_t *self, bc_tick_t wait)
267 {
268  bc_led_strip_effect_stop(self);
269 
270  self->_effect.round = 0;
271  self->_effect.wait = wait;
272 
273  self->_effect.task_id = bc_scheduler_register(_bc_led_strip_effect_rainbow_cycle_task, self, 0);
274 }
275 
276 static void _bc_led_strip_effect_color_wipe_task(void *param)
277 {
278  bc_led_strip_t *self = (bc_led_strip_t *)param;
279 
280  if (!self->_driver->is_ready())
281  {
283 
284  return;
285  }
286 
287  bc_led_strip_set_pixel(self, self->_effect.led++, self->_effect.color);
288 
289  if (self->_effect.led == self->_buffer->count)
290  {
291  _bc_led_strip_effect_done(self);
292  return;
293  }
294 
295  self->_driver->write();
296 
297  bc_scheduler_plan_current_relative(self->_effect.wait);
298 
299 }
300 
301 void bc_led_strip_effect_color_wipe(bc_led_strip_t *self, uint32_t color, bc_tick_t wait)
302 {
303  bc_led_strip_effect_stop(self);
304 
305  self->_effect.led = 0;
306  self->_effect.wait = wait;
307  self->_effect.color = color;
308 
309  self->_effect.task_id = bc_scheduler_register(_bc_led_strip_effect_color_wipe_task, self, 0);
310 }
311 
312 static void _bc_led_strip_effect_theater_chase_task(void *param)
313 {
314  bc_led_strip_t *self = (bc_led_strip_t *)param;
315 
316  if (!self->_driver->is_ready())
317  {
319 
320  return;
321  }
322 
323  for (int i = self->_effect.led; i < self->_buffer->count; i += 3) {
324  self->_driver->set_pixel(i, 0); //turn every third pixel off
325  }
326 
327  self->_effect.led++;
328 
329  if (self->_effect.led == 3)
330  {
331  self->_effect.led = 0;
332  }
333 
334  for (int i = self->_effect.led ; i < self->_buffer->count; i += 3) {
335  bc_led_strip_set_pixel(self, i, self->_effect.color); //turn every third pixel on
336  }
337 
338  self->_driver->write();
339 
340  bc_scheduler_plan_current_relative(self->_effect.wait);
341 
342 }
343 
344 void bc_led_strip_effect_theater_chase(bc_led_strip_t *self, uint32_t color, bc_tick_t wait)
345 {
346  bc_led_strip_effect_stop(self);
347 
348  self->_effect.led = 0;
349  self->_effect.round = 0;
350  self->_effect.color = color;
351  self->_effect.wait = wait;
352 
353  self->_effect.task_id = bc_scheduler_register(_bc_led_strip_effect_theater_chase_task, self, 0);
354 }
355 
356 static void _bc_led_strip_effect_theater_chase_rainbow_task(void *param)
357 {
358  bc_led_strip_t *self = (bc_led_strip_t *)param;
359 
360  if (!self->_driver->is_ready())
361  {
363 
364  return;
365  }
366 
367  for (int i = self->_effect.led; i < self->_buffer->count; i += 3) {
368  self->_driver->set_pixel(i, 0); //turn every third pixel off
369  }
370 
371  self->_effect.led++;
372 
373  if (self->_effect.led == 3)
374  {
375  self->_effect.led = 0;
376  }
377 
378  for (int i = self->_effect.led; i < self->_buffer->count; i += 3) {
379  bc_led_strip_set_pixel(self, i, _bc_led_strip_wheel((i + self->_effect.round) % 255) ); //turn every third pixel on
380  }
381 
382  self->_driver->write();
383 
384  self->_effect.round++;
385 
386  bc_scheduler_plan_current_relative(self->_effect.wait);
387 
388 }
389 
390 void bc_led_strip_effect_theater_chase_rainbow(bc_led_strip_t *self, bc_tick_t wait)
391 {
392  bc_led_strip_effect_stop(self);
393 
394  self->_effect.led = 0;
395  self->_effect.round = 0;
396  self->_effect.wait = wait;
397 
398  self->_effect.task_id = bc_scheduler_register(_bc_led_strip_effect_theater_chase_rainbow_task, self, 0);
399 }
400 
401 static void bc_led_strip_effect_stroboscope_task(void *param)
402 {
403  bc_led_strip_t *self = (bc_led_strip_t *)param;
404 
405  if (!self->_driver->is_ready())
406  {
408 
409  return;
410  }
411 
412  uint32_t color = (self->_effect.round & 1) == 0 ? self->_effect.color : 0;
413 
414  bc_led_strip_fill(self, color);
415 
416  bc_led_strip_write(self);
417 
418  self->_effect.round++;
419 
420  bc_scheduler_plan_current_relative(self->_effect.wait);
421 }
422 
423 void bc_led_strip_effect_stroboscope(bc_led_strip_t *self, uint32_t color, bc_tick_t wait)
424 {
425  bc_led_strip_effect_stop(self);
426 
427  self->_effect.wait = wait;
428 
429  self->_effect.round = 0;
430 
431  self->_effect.color = color;
432 
433  self->_effect.task_id = bc_scheduler_register(bc_led_strip_effect_stroboscope_task, self, 0);
434 }
435 
436 void _bc_led_strip_effect_icicle_task(void *param)
437 {
438  bc_led_strip_t *self = (bc_led_strip_t *)param;
439 
440  if (!self->_driver->is_ready())
441  {
443 
444  return;
445  }
446 
447  const int length = 10;
448 
449  for (int i = self->_effect.led; (i < self->_effect.led + length) && i < self->_buffer->count; i++) {
450 
451  if (i < 0)
452  {
453  continue;
454  }
455 
456  bc_led_strip_set_pixel(self, i, 0x00);
457  }
458 
459  self->_effect.led++;
460 
461  if (self->_effect.led == self->_buffer->count)
462  {
463  self->_effect.led = -length;
464  }
465 
466 
467  uint8_t r, g, b, w, dr, dg, db, dw;
468 
469  dr = ((uint8_t) (self->_effect.color >> 24)) / length;
470  dg = ((uint8_t) (self->_effect.color >> 16)) / length;
471  db = ((uint8_t) (self->_effect.color >> 8)) / length;
472  dw = ((uint8_t) (self->_effect.color)) / length;
473 
474  r = dr;
475  g = dg;
476  b = db;
477  w = dw;
478 
479  for (int i = self->_effect.led; (i < self->_effect.led + length) && i < self->_buffer->count; i++) {
480 
481  if (i < 0)
482  {
483  continue;
484  }
485 
486  bc_led_strip_set_pixel_rgbw(self, i, r, g, b, w); // 0x20000000
487 
488  r += dr;
489  g += dg;
490  b += db;
491  w += dw;
492  }
493 
494  bc_led_strip_write(self);
495 
496  bc_scheduler_plan_current_relative(self->_effect.wait);
497 }
498 
499 void bc_led_strip_effect_icicle(bc_led_strip_t *self, uint32_t color, bc_tick_t wait)
500 {
501  bc_led_strip_effect_stop(self);
502 
503  self->_effect.color = color;
504 
505  self->_effect.wait = wait;
506 
507  self->_effect.led = -10;
508 
509  self->_effect.task_id = bc_scheduler_register(_bc_led_strip_effect_icicle_task, self, 0);
510 }
511 
512 static void _bc_led_strip_effect_pulse_color_task(void *param)
513 {
514  bc_led_strip_t *self = (bc_led_strip_t *)param;
515 
516  if (!self->_driver->is_ready())
517  {
519 
520  return;
521  }
522 
523  uint8_t r = self->_effect.color >> 24;
524  uint8_t g = self->_effect.color >> 16;
525  uint8_t b = self->_effect.color >> 8;
526  uint8_t w = self->_effect.color;
527 
528  uint8_t brightness = (abs(19 - self->_effect.round) + 1) * (255 / 20);
529 
530  if (++self->_effect.round == 38)
531  {
532  self->_effect.round = 0;
533  }
534 
535  r = ((uint16_t) r * brightness) >> 8;
536  g = ((uint16_t) g * brightness) >> 8;
537  b = ((uint16_t) b * brightness) >> 8;
538  w = ((uint16_t) w * brightness) >> 8;
539 
540  for (int i = 0; i < self->_buffer->count; i++)
541  {
542  bc_led_strip_set_pixel_rgbw(self, i, r, g, b, w);
543  }
544 
545  self->_driver->write();
546 
547  bc_scheduler_plan_current_relative(self->_effect.wait);
548 }
549 
550 void bc_led_strip_effect_pulse_color(bc_led_strip_t *self, uint32_t color, bc_tick_t wait)
551 {
552  bc_led_strip_effect_stop(self);
553 
554  self->_effect.round = 0;
555  self->_effect.wait = wait;
556  self->_effect.color = color;
557 
558  self->_effect.task_id = bc_scheduler_register(_bc_led_strip_effect_pulse_color_task, self, 0);
559 }
560 
561 void bc_led_strip_thermometer(bc_led_strip_t *self, float temperature, float min, float max, uint8_t white_dots, float set_point, uint32_t color)
562 {
563  temperature -= min;
564 
565  int max_i = ((float)self->_buffer->count / (float)(fabs(max) + fabs(min))) * temperature;
566 
567  if (max_i > self->_buffer->count)
568  {
569  max_i = self->_buffer->count;
570  }
571 
572  if (max_i < 0)
573  {
574  max_i = 0;
575  }
576 
577  float red;
578  float green;
579  float blue;
580 
581  for (int i = 0; i < max_i; i++)
582  {
583  _bc_led_strip_get_heat_map_color((float)i / self->_buffer->count, &red, &green, &blue);
584 
585  self->_driver->set_pixel_rgbw(i, self->_brightness * red, self->_brightness * green, self->_brightness * blue, 0);
586  }
587 
588  if (self->_buffer->type == BC_LED_STRIP_TYPE_RGBW)
589  {
590  for (int i = max_i; i < self->_buffer->count; i++)
591  {
592  self->_driver->set_pixel_rgbw(i, 0, 0, 0, white_dots);
593  }
594  }
595  else
596  {
597  for (int i = max_i; i < self->_buffer->count; i++)
598  {
599  self->_driver->set_pixel_rgbw(i, white_dots, white_dots, white_dots, 0);
600  }
601  }
602 
603  if ((min < set_point) && (max > set_point))
604  {
605  set_point -= min;
606 
607  int color_i = ((float)self->_buffer->count / (float)(fabs(max) + fabs(min))) * set_point;
608 
609  self->_driver->set_pixel(color_i, color);
610  }
611 
612  bc_led_strip_write(self);
613 }
614 
615 static uint32_t _bc_led_strip_wheel(int position) {
616  if(position < 85)
617  {
618  return ((position * 3) << 24) | ((255 - position * 3) << 16);
619  }
620  else if (position < 170)
621  {
622  position -= 85;
623  return ((255 - position * 3) << 24) | ((position * 3) << 8);
624  }
625  else
626  {
627  position -= 170;
628  return ((position * 3) << 16) | ((255 - position * 3) << 8);
629  }
630 }
631 
632 static void _bc_led_strip_get_heat_map_color(float value, float *red, float *green, float *blue)
633 {
634  const int NUM_COLORS = 4;
635  const float color[4][3] = { {0,0,1}, {0,1,0}, {1,1,0}, {1,0,0} };
636 
637  int idx1; // Our desired color will be between these two indexes in "color".
638  int idx2;
639  float fractBetween = 0; // Fraction between "idx1" and "idx2" where our value is.
640 
641  if(value <= 0)
642  {
643  idx1 = idx2 = 0;
644  }
645  else if(value >= 1)
646  {
647  idx1 = idx2 = NUM_COLORS - 1;
648  }
649  else
650  {
651  value = value * (NUM_COLORS - 1); // Will multiply value by 3.
652  idx1 = floor(value); // Our desired color will be after this index.
653  idx2 = idx1 + 1; // ... and before this index (inclusive).
654  fractBetween = value - (float)idx1; // Distance between the two indexes (0-1).
655  }
656 
657  *red = (color[idx2][0] - color[idx1][0]) * fractBetween + color[idx1][0];
658  *green = (color[idx2][1] - color[idx1][1]) * fractBetween + color[idx1][1];
659  *blue = (color[idx2][2] - color[idx1][2]) * fractBetween + color[idx1][2];
660 }
uint64_t bc_tick_t
Timestamp data type.
Definition: bc_tick.h:16
bc_scheduler_task_id_t bc_scheduler_register(void(*task)(void *), void *param, bc_tick_t tick)
Register task in scheduler.
Definition: bc_scheduler.c:56
void bc_scheduler_plan_current_relative(bc_tick_t tick)
Schedule current task to tick relative from current spin.
Definition: bc_scheduler.c:149
void bc_scheduler_plan_current_now(void)
Schedule current task for immediate execution.
Definition: bc_scheduler.c:139
void bc_scheduler_unregister(bc_scheduler_task_id_t task_id)
Unregister specified task.
Definition: bc_scheduler.c:80