dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rgbleds.c
Go to the documentation of this file.
1 
12 /*
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  * for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, see <http://www.gnu.org/licenses/>
25  *
26  * Additional note on redistribution: The copyright and license notices above
27  * must be maintained in each individual source file that is a derivative work
28  * of this source file; otherwise redistribution is prohibited.
29  */
30 
31 #include "openpilot.h"
32 
33 #include <pios_thread.h>
34 
35 #include "rgbleds.h"
36 #include "misc_math.h"
37 
38 #ifdef SYSTEMMOD_RGBLED_SUPPORT
39 #include "rgbledsettings.h"
40 #include "stabilizationdesired.h"
41 #include "manualcontrolcommand.h"
42 
43 // all values are from 0 to 1
44 static void rgb_to_hsv_f(const float *rgb, float *hsv)
45 {
46  float r = rgb[0];
47  float g = rgb[1];
48  float b = rgb[2];
49 
50  float h;
51 
52  float min_val, max_val, delta;
53 
54  uint8_t sector;
55 
56  if (r > g) {
57  if (g < b) {
58  min_val = g;
59  } else {
60  min_val = b;
61  }
62 
63  if (r > b) {
64  max_val = r;
65 
66  delta = max_val - min_val;
67 
68  // between yellow & magenta
69 
70  if (g >= b) {
71  sector = 0;
72  } else {
73  sector = 6;
74  }
75 
76  h = g - b;
77  } else {
78  max_val = b;
79 
80  delta = max_val - min_val;
81 
82  // between magenta & cyan
83  sector = 4;
84  h = r - g;
85  }
86  } else {
87  if (r < b) {
88  min_val = r;
89  } else {
90  min_val = b;
91  }
92 
93  if (g > b) {
94  max_val = g;
95 
96  delta = max_val - min_val;
97 
98  // between cyan & yellow
99  sector = 2;
100  h = b - r;
101  } else {
102  max_val = b;
103 
104  delta = max_val - min_val;
105 
106  // between magenta & cyan
107  sector = 4;
108  h = r - g;
109  }
110  }
111 
112  if (delta <= 0.00001f) {
113  hsv[0] = 0; // hue undefined-- but pick 0 arbitrarily
114  hsv[1] = 0; // saturation 0
115  hsv[2] = max_val; // and brightness
116 
117  return;
118  }
119 
120  h = (h / delta) + sector;
121 
122  hsv[0] = h * (1.0f / 6.0f);
123  hsv[1] = delta / max_val;
124  hsv[2] = max_val;
125 }
126 
127 static void hsv_to_rgb_f(const float *hsv, float *rgb)
128 {
129  float h = hsv[0], s = hsv[1], v = hsv[2];
130 
131  int i;
132  float f, p, q, t;
133  if (s < 0.000001f) {
134  // achromatic (grey)
135 
136  rgb[0] = v;
137  rgb[1] = v;
138  rgb[2] = v;
139 
140  return;
141  }
142 
143  h *= 6; // sector 0 to 5
144  i = h;
145 
146  f = h - i; // fractional part of h
147 
148  p = v * ( 1 - s );
149  q = v * ( 1 - s * f );
150  t = v * ( 1 - s * ( 1 - f ) );
151 
152  switch (i) {
153  case 0:
154  case 6:
155  rgb[0] = v;
156  rgb[1] = t;
157  rgb[2] = p;
158  break;
159  case 1:
160  rgb[0] = q;
161  rgb[1] = v;
162  rgb[2] = p;
163  break;
164  case 2:
165  rgb[0] = p;
166  rgb[1] = v;
167  rgb[2] = t;
168  break;
169  case 3:
170  rgb[0] = p;
171  rgb[1] = q;
172  rgb[2] = v;
173  break;
174  case 4:
175  rgb[0] = t;
176  rgb[1] = p;
177  rgb[2] = v;
178  break;
179  default: // case 5:
180  rgb[0] = v;
181  rgb[1] = p;
182  rgb[2] = q;
183  break;
184  }
185 }
186 
187 static uint8_t float_to_q8(float f) {
188  if (f <= 0) {
189  return 0;
190  }
191 
192  if (f >= 1) {
193  return 255;
194  }
195 
196  return 255 * f + 0.5f;
197 }
198 
199 static inline uint8_t linear_interp_u16(uint8_t val_a, uint8_t val_b,
200  uint16_t fraction)
201 {
202  uint32_t tmp = ((uint32_t) val_a) * (65535-fraction);
203  tmp += ((uint32_t) val_b) * fraction;
204 
205  tmp += 32767;
206 
207  return tmp >> 16;
208 }
209 
210 static void interp_in_hsv(bool backwards, const uint8_t *rgb_start,
211  const uint8_t *rgb_end, uint8_t *rgb_out, uint16_t fraction) {
212  float fraction_f = fraction / 65535.0f;
213 
214  if (fraction_f > 1.0f) {
215  fraction_f = 1.0f;
216  }
217 
218  // Convert everything to HSV coordinates.
219  float rgbf[3];
220 
221  rgbf[0] = rgb_start[0] / 255.0f;
222  rgbf[1] = rgb_start[1] / 255.0f;
223  rgbf[2] = rgb_start[2] / 255.0f;
224 
225  float hsv_start[3], hsv_end[3], hsv_now[3];
226 
227  rgb_to_hsv_f(rgbf, hsv_start);
228 
229  rgbf[0] = rgb_end[0] / 255.0f;
230  rgbf[1] = rgb_end[1] / 255.0f;
231  rgbf[2] = rgb_end[2] / 255.0f;
232 
233  rgb_to_hsv_f(rgbf, hsv_end);
234 
235  if (backwards) {
236  if (hsv_start[0] > hsv_end[0]) {
237  hsv_end[0] += 1.0f;
238  } else {
239  hsv_start[0] += 1.0f;
240  }
241  }
242 
243  hsv_now[0] = hsv_start[0] * (1.0f - fraction_f) +
244  hsv_end[0] * (fraction_f);
245  hsv_now[1] = hsv_start[1] * (1.0f - fraction_f) +
246  hsv_end[1] * (fraction_f);
247  hsv_now[2] = hsv_start[2] * (1.0f - fraction_f) +
248  hsv_end[2] * (fraction_f);
249 
250  if (hsv_now[0] >= 1.0f) {
251  hsv_now[0] -= 1.0f;
252  }
253 
254  hsv_to_rgb_f(hsv_now, rgbf);
255 
256  rgb_out[0] = float_to_q8(rgbf[0]);
257  rgb_out[1] = float_to_q8(rgbf[1]);
258  rgb_out[2] = float_to_q8(rgbf[2]);
259 }
260 
261 static inline uint16_t float_to_u16(float in)
262 {
263  if (in >= 1) {
264  return 65535;
265  } else if (in <= 0) {
266  return 0;
267  }
268 
269  return in * 65535;
270 }
271 
272 static uint16_t accessory_desired_get_u16(int idx)
273 {
274  if (idx < 0) {
275  return 0;
276  }
277 
278  if (idx >= MANUALCONTROLCOMMAND_ACCESSORY_NUMELEM) {
279  return 0;
280  }
281 
282  float accessories[MANUALCONTROLCOMMAND_ACCESSORY_NUMELEM];
283 
284  ManualControlCommandAccessoryGet(accessories);
285 
286  return float_to_u16(accessories[idx]);
287 }
288 
289 static uint16_t sinusodialize(uint16_t fraction) {
290  int16_t s = sin_approx(fraction/2);
291 
292  if (s > 4095) return 65535;
293  if (s < -4096) return 0;
294 
295  return (s + 4096) * 8;
296 }
297 
298 void systemmod_process_rgb_leds(bool led_override, bool led_override_active,
299  uint8_t blink_prio, bool is_armed, bool force_dim) {
300  if (!pios_ws2811) return;
301 
302  uint16_t num_leds = PIOS_WS2811_get_num_leds(pios_ws2811);
303 
304  if (num_leds == 0) return;
305 
306  RGBLEDSettingsData rgbSettings;
307  RGBLEDSettingsGet(&rgbSettings);
308 
309  uint8_t range_color[3];
310 
311  uint16_t fraction = 0;
312 
313  uint32_t tmpui32;
314  float tmp_float;
315 
316  RGBLEDSettingsRangeColorBlendSourceOptions blend_source =
317  rgbSettings.RangeColorBlendSource;
318  RGBLEDSettingsRangeColorBlendModeOptions blend_mode =
319  rgbSettings.RangeColorBlendMode;
320 
321 #ifdef PIOS_INCLUDE_USB
322  if (rgbSettings.DimWhenUSB == RGBLEDSETTINGS_DIMWHENUSB_TRUE) {
323  if (PIOS_USB_CableConnected(0)) {
324  force_dim = true;
325  }
326  }
327 #endif
328 
329  if (!is_armed) {
330  blend_source = rgbSettings.RangeColorBlendUnarmedSource;
331  }
332 
333  // future: would be nice to have per-index variance too for "motion"
334  switch (blend_source) {
335  case RGBLEDSETTINGS_RANGECOLORBLENDSOURCE_ALWAYSUSEBASECOLOR:
336  break;
337 
338  case RGBLEDSETTINGS_RANGECOLORBLENDSOURCE_TIMEHALFSECONDPERIOD:
339  tmpui32 = PIOS_Thread_Systime();
340  fraction = (tmpui32 % 500) * 65535 / 500;
341  if (blend_mode == RGBLEDSETTINGS_RANGECOLORBLENDMODE_SAWTOOTH) {
342  /* Force to sinusodial modes for time */
343  blend_mode = RGBLEDSETTINGS_RANGECOLORBLENDMODE_SINE;
344  }
345  break;
346 
347  case RGBLEDSETTINGS_RANGECOLORBLENDSOURCE_TIMESECONDPERIOD:
348  tmpui32 = PIOS_Thread_Systime();
349  fraction = (tmpui32 % 1000) * 65535 / 1000;
350  if (blend_mode == RGBLEDSETTINGS_RANGECOLORBLENDMODE_SAWTOOTH) {
351  /* Force to sinusodial modes for time */
352  blend_mode = RGBLEDSETTINGS_RANGECOLORBLENDMODE_SINE;
353  }
354  break;
355 
356  case RGBLEDSETTINGS_RANGECOLORBLENDSOURCE_TIME2SECONDPERIOD:
357  tmpui32 = PIOS_Thread_Systime();
358  fraction = (tmpui32 % 2000) * 65535 / 2000;
359  if (blend_mode == RGBLEDSETTINGS_RANGECOLORBLENDMODE_SAWTOOTH) {
360  /* Force to sinusodial modes for time */
361  blend_mode = RGBLEDSETTINGS_RANGECOLORBLENDMODE_SINE;
362  }
363  break;
364 
365  case RGBLEDSETTINGS_RANGECOLORBLENDSOURCE_TIME4SECONDPERIOD:
366  tmpui32 = PIOS_Thread_Systime();
367  fraction = (tmpui32 % 4000) * 65535 / 4000;
368  if (blend_mode == RGBLEDSETTINGS_RANGECOLORBLENDMODE_SAWTOOTH) {
369  /* Force to sinusodial modes for time */
370  blend_mode = RGBLEDSETTINGS_RANGECOLORBLENDMODE_SINE;
371  }
372  break;
373 
374  case RGBLEDSETTINGS_RANGECOLORBLENDSOURCE_THROTTLE:
375  StabilizationDesiredThrustGet(&tmp_float);
376  fraction = float_to_u16(fabsf(tmp_float));
377  break;
378 
379  case RGBLEDSETTINGS_RANGECOLORBLENDSOURCE_ACCESSORY0:
380  fraction = accessory_desired_get_u16(0);
381  break;
382 
383  case RGBLEDSETTINGS_RANGECOLORBLENDSOURCE_ACCESSORY1:
384  fraction = accessory_desired_get_u16(1);
385  break;
386 
387  case RGBLEDSETTINGS_RANGECOLORBLENDSOURCE_ACCESSORY2:
388  fraction = accessory_desired_get_u16(2);
389  break;
390  }
391 
392  switch (blend_mode) {
393  default:
394  case RGBLEDSETTINGS_RANGECOLORBLENDMODE_SAWTOOTH:
395  break; // Do nothing. It's sawtooth already
396 
397  case RGBLEDSETTINGS_RANGECOLORBLENDMODE_SINE:
398  fraction = sinusodialize(fraction);
399  break;
400 
401  case RGBLEDSETTINGS_RANGECOLORBLENDMODE_TRIANGLE:
402  if (fraction >= 32768) {
403  // Relies on unsigned wraparound magic
404  fraction = 65535 - fraction * 2;
405  } else {
406  fraction *= 2;
407  }
408  break;
409  case RGBLEDSETTINGS_RANGECOLORBLENDMODE_SQUARE:
410  if (fraction > 32768) {
411  fraction = 65535;
412  } else {
413  fraction = 0;
414  }
415  break;
416  }
417 
418  switch (rgbSettings.RangeColorBlendType) {
419  default:
420  case RGBLEDSETTINGS_RANGECOLORBLENDTYPE_LINEARRGBFADE:
421  range_color[0] = linear_interp_u16(
422  rgbSettings.RangeBaseColor[0],
423  rgbSettings.RangeEndColor[0],
424  fraction);
425  range_color[1] = linear_interp_u16(
426  rgbSettings.RangeBaseColor[1],
427  rgbSettings.RangeEndColor[1],
428  fraction);
429  range_color[2] = linear_interp_u16(
430  rgbSettings.RangeBaseColor[2],
431  rgbSettings.RangeEndColor[2],
432  fraction);
433  case RGBLEDSETTINGS_RANGECOLORBLENDTYPE_LINEARINHSV:
434  interp_in_hsv(false, rgbSettings.RangeBaseColor,
435  rgbSettings.RangeEndColor,
436  range_color,
437  fraction);
438  break;
439  case RGBLEDSETTINGS_RANGECOLORBLENDTYPE_LINEARINHSVBACKWARDSHUE:
440  interp_in_hsv(true, rgbSettings.RangeBaseColor,
441  rgbSettings.RangeEndColor,
442  range_color,
443  fraction);
444  break;
445  }
446 
447  uint8_t alarm_color[3] = { 0, 0, 0 };
448 
449  if (led_override && led_override_active) {
450  // pick a meaningful color based on prio
451 
452  if (blink_prio >= SYSTEMALARMS_ALARM_CRITICAL) {
453  alarm_color[0] = 255; // RED
454  alarm_color[1] = 32;
455  alarm_color[2] = 32;
456  } else if (blink_prio >= SYSTEMALARMS_ALARM_WARNING) {
457  alarm_color[0] = 255; // YELLOW
458  alarm_color[1] = 200;
459  alarm_color[2] = 32;
460  } else {
461  alarm_color[0] = 32; // GREEN
462  alarm_color[1] = 200;
463  alarm_color[2] = 32;
464  }
465  }
466 
467  if (force_dim) {
468  range_color[0] = (range_color[0] + 2) / 3;
469  range_color[1] = (range_color[1] + 2) / 3;
470  range_color[2] = (range_color[2] + 2) / 3;
471  alarm_color[0] = (alarm_color[0] + 2) / 3;
472  alarm_color[1] = (alarm_color[1] + 2) / 3;
473  alarm_color[2] = (alarm_color[2] + 2) / 3;
474  rgbSettings.DefaultColor[0] = (rgbSettings.DefaultColor[0] + 2) / 3;
475  rgbSettings.DefaultColor[1] = (rgbSettings.DefaultColor[1] + 2) / 3;
476  rgbSettings.DefaultColor[2] = (rgbSettings.DefaultColor[2] + 2) / 3;
477  }
478 
479  for (int i = 0; i < num_leds; i++) {
480  if (led_override) {
481  if ((i >= rgbSettings.AnnunciateRangeBegin) &&
482  (i <= rgbSettings.AnnunciateRangeEnd)) {
484  alarm_color[0],
485  alarm_color[1],
486  alarm_color[2]);
487 
488  continue;
489  }
490  }
491 
492  if ((i >= rgbSettings.RangeBegin) &&
493  (i <= rgbSettings.RangeEnd)) {
495  range_color[0], range_color[1],
496  range_color[2]);
497  continue;
498  }
499 
501  rgbSettings.DefaultColor[0],
502  rgbSettings.DefaultColor[1],
503  rgbSettings.DefaultColor[2]);
504  }
505 
506 #ifdef SYSTEMMOD_RGBLED_VIDEO_HACK
507  extern volatile bool video_active;
508 
509  if (!video_active) {
511  }
512 #else
514 #endif
515 }
516 
517 #endif
518 
volatile bool video_active
uint32_t PIOS_Thread_Systime(void)
Definition: pios_thread.c:212
int PIOS_WS2811_get_num_leds(ws2811_dev_t dev)
Find out how many LEDs are configured on an interface.
Definition: pios_ws2811.c:227
static int16_t sin_approx(int32_t x)
Fast approximation of sine; 3rd order taylor expansion. Based on http://www.coranac.com/2009/07/sines/.
Definition: misc_math.h:90
ws2811_dev_t pios_ws2811
Definition: pios_board.c:66
uint8_t i
Definition: msp_messages.h:97
void systemmod_process_rgb_leds(bool led_override, bool led_override_active, uint8_t blink_prio, bool is_armed, bool force_dim)
tuple f
Definition: px_mkfw.py:81
void PIOS_WS2811_set(ws2811_dev_t dev, int idx, uint8_t r, uint8_t g, uint8_t b)
Set a given LED to a color value.
Definition: pios_ws2811.c:176
Includes PiOS and core architecture components.
void PIOS_WS2811_trigger_update(ws2811_dev_t dev)
Trigger an update of the LED strand.
Definition: pios_ws2811.c:197
bool PIOS_USB_CableConnected(uintptr_t id)
uint8_t p
Definition: msp_messages.h:96