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