2 ******************************************************************************
4 * @file lednotification.c
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
6 * @brief led notification library.
8 * @see The GNU Public License (GPL) Version 3
10 *****************************************************************************/
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "inc/lednotification.h"
32 #include <pios_notify.h>
33 #include <pios_ws2811.h>
37 // Maximum number of notifications enqueued when a higher priority notification is added
38 #define MAX_BACKGROUND_NOTIFICATIONS 6
39 #define MAX_HANDLED_LED 1
41 #define BACKGROUND_SEQUENCE 0
43 #define GET_CURRENT_MILLIS (xTaskGetTickCount() * portTICK_RATE_MS)
45 // Private data types definition
46 // this is the status for a single notification led set
48 int8_t queued_priorities
[MAX_BACKGROUND_NOTIFICATIONS
]; // slot 0 is reserved for background
49 LedSequence_t queued_sequences
[MAX_BACKGROUND_NOTIFICATIONS
]; // slot 0 is reserved for background
50 uint32_t next_run_time
;
51 uint32_t sequence_starting_time
;
53 int8_t active_sequence_num
; // active queued sequence or BACKGROUND_SEQUENCE
54 bool running
; // is this led running?
55 bool step_phase_on
; // true = step on phase, false = step off phase
56 uint8_t next_sequence_step
; // (step number to be executed) << 1 || (0x00 = on phase, 0x01 = off phase)
57 uint8_t next_step_rep
; // next repetition number for next step (valid if step.repeats >1)
58 uint8_t next_sequence_rep
; // next sequence repetition counter (valid if sequence.repeats > 1)
59 uint8_t led_set_start
; // first target led for this set
60 uint8_t led_set_end
; // last target led for this set
61 } NotifierLedStatus_t
;
63 static bool led_status_initialized
= false;
65 NotifierLedStatus_t led_status
[MAX_HANDLED_LED
];
67 static void InitExtLed()
69 memset(led_status
, 0, sizeof(NotifierLedStatus_t
) * MAX_HANDLED_LED
);
70 const uint32_t now
= GET_CURRENT_MILLIS
;
71 for (uint8_t l
= 0; l
< MAX_HANDLED_LED
; l
++) {
72 led_status
[l
].led_set_start
= 0;
73 led_status
[l
].led_set_end
= PIOS_WS2811_NUMLEDS
- 1;
74 led_status
[l
].next_run_time
= now
+ 500; // start within half a second
75 for (uint8_t i
= 0; i
< MAX_BACKGROUND_NOTIFICATIONS
; i
++) {
76 led_status
[l
].queued_priorities
[i
] = NOTIFY_PRIORITY_BACKGROUND
;
82 * restart current sequence
84 static void restart_sequence(NotifierLedStatus_t
*status
, bool immediate
)
86 status
->next_sequence_step
= 0;
87 status
->next_step_rep
= 0;
88 status
->step_phase_on
= true;
89 status
->running
= true;
91 uint32_t currentTime
= GET_CURRENT_MILLIS
;
92 status
->next_run_time
= currentTime
;
94 status
->sequence_starting_time
= status
->next_run_time
;
98 * modify background sequence or enqueue a new sequence to play
100 static void push_queued_sequence(ExtLedNotification_t
*new_notification
, NotifierLedStatus_t
*status
)
102 int8_t updated_sequence
;
104 int8_t lowest_priority_index
= -1;
105 int8_t lowest_priority
= new_notification
->priority
;
107 if (new_notification
->priority
== NOTIFY_PRIORITY_BACKGROUND
) {
108 lowest_priority_index
= BACKGROUND_SEQUENCE
;
110 // slot 0 is reserved for Background sequence
111 for (int8_t i
= 1; i
< MAX_BACKGROUND_NOTIFICATIONS
; i
++) {
112 if (status
->queued_priorities
[i
] < lowest_priority
) {
113 lowest_priority_index
= i
;
114 lowest_priority
= status
->queued_priorities
[i
];
119 // no items with priority lower than the one we are trying to enqueue. skip
120 if (lowest_priority_index
< 0) {
124 status
->queued_priorities
[lowest_priority_index
] = new_notification
->priority
;
125 status
->queued_sequences
[lowest_priority_index
] = new_notification
->sequence
;
126 updated_sequence
= lowest_priority_index
;;
129 // check whether we should preempt the current notification and play this new one
130 if (status
->queued_priorities
[status
->active_sequence_num
] < new_notification
->priority
) {
131 status
->active_sequence_num
= updated_sequence
;
134 if (status
->active_sequence_num
== updated_sequence
) {
135 restart_sequence(status
, true);
139 static bool pop_queued_sequence(NotifierLedStatus_t
*status
)
141 if (status
->active_sequence_num
> BACKGROUND_SEQUENCE
) {
142 // set the last active slot as empty
143 status
->queued_priorities
[status
->active_sequence_num
] = NOTIFY_PRIORITY_BACKGROUND
;
145 // search the highest priority item
146 int8_t highest_priority_index
= BACKGROUND_SEQUENCE
;
147 int8_t highest_priority
= NOTIFY_PRIORITY_BACKGROUND
;
149 for (int8_t i
= 1; i
< MAX_BACKGROUND_NOTIFICATIONS
; i
++) {
150 if (status
->queued_priorities
[i
] > highest_priority
) {
151 highest_priority_index
= i
;
152 highest_priority
= status
->queued_priorities
[i
];
155 // set the next sequence to activate (or BACKGROUND_SEQUENCE when all slots are empty)
156 status
->active_sequence_num
= highest_priority_index
;
157 return highest_priority_index
!= BACKGROUND_SEQUENCE
;
159 // background sequence was completed
164 * advance current sequence pointers for next step
166 static void advance_sequence(NotifierLedStatus_t
*status
)
168 LedSequence_t
*activeSequence
= &status
->queued_sequences
[status
->active_sequence_num
];
170 uint8_t step
= status
->next_sequence_step
;
171 LedStep_t
*currentStep
= &activeSequence
->steps
[step
];
173 // Next step will be the OFF phase, so just update the time and
174 if (status
->step_phase_on
) {
175 // next will be the off phase
176 status
->next_run_time
+= currentStep
->time_on
;
177 status
->step_phase_on
= false;
178 // check if off phase should be skipped
179 if (currentStep
->time_off
!= 0) {
184 // next step is ON phase. check whether to repeat current step or move to next one
185 status
->next_run_time
+= currentStep
->time_off
;
186 status
->step_phase_on
= true;
188 if (status
->next_step_rep
+ 1 < currentStep
->repeats
) {
189 // setup next repetition
190 status
->next_step_rep
++;
195 LedStep_t
*nextStep
= (step
+ 1 < NOTIFY_SEQUENCE_MAX_STEPS
) ? &activeSequence
->steps
[step
+ 1] : 0;
197 // next step is null, check whether sequence must be repeated or it must move to lower priority queued or background sequences
198 if (NOTIFY_IS_NULL_STEP(nextStep
)) {
199 if (activeSequence
->repeats
== -1 || status
->next_sequence_rep
+ 1 < activeSequence
->repeats
) {
200 status
->next_sequence_rep
++;
201 // restart the sequence
202 restart_sequence(status
, false);
205 if (status
->active_sequence_num
!= BACKGROUND_SEQUENCE
) {
206 // no repeat, pop enqueued or background sequences
207 pop_queued_sequence(status
);
208 restart_sequence(status
, false);
209 status
->next_sequence_rep
= 0;
211 status
->running
= false;
214 status
->next_step_rep
= 0;
215 status
->next_sequence_step
++;
222 static void run_led(NotifierLedStatus_t
*status
)
224 const uint32_t currentTime
= GET_CURRENT_MILLIS
;
226 if (!status
->running
|| currentTime
< status
->next_run_time
) {
229 status
->next_run_time
= currentTime
;
230 uint8_t step
= status
->next_sequence_step
;
232 LedSequence_t
*activeSequence
= &status
->queued_sequences
[status
->active_sequence_num
];
233 const Color_t color
= status
->step_phase_on
? activeSequence
->steps
[step
].color
: Color_Off
;
235 for (uint8_t i
= status
->led_set_start
; i
<= status
->led_set_end
; i
++) {
236 PIOS_WS2811_setColorRGB(color
, i
, false);
238 PIOS_WS2811_Update();
239 advance_sequence(status
);
242 void LedNotificationExtLedsRun()
244 // handle incoming sequences
245 if (!led_status_initialized
) {
247 led_status_initialized
= true;
249 static ExtLedNotification_t
*newNotification
;
250 newNotification
= PIOS_NOTIFY_GetNewExtLedSequence(true);
251 if (newNotification
) {
252 push_queued_sequence(newNotification
, &led_status
[0]);
256 for (uint8_t i
= 0; i
< MAX_HANDLED_LED
; i
++) {
257 run_led(&led_status
[i
]);