1 /* vim:ts=4 sts=4 et tw=80
5 * for additional information please
6 * see http://lochraster.org/fnordlichtmini
8 * (c) by Alexander Neumann <alexander@bumpern.de>
10 * This program is free software: you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 3 as published by
12 * the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <avr/pgmspace.h>
27 #include <avr/sleep.h>
28 #include <avr/interrupt.h>
29 #include <util/delay.h>
35 #include "../common/pt/pt.h"
37 #include "remote-proto.h"
38 #include "../common/common.h"
42 /* abbreviations for port, ddr and pin */
43 #define R_PORT _OUTPORT(REMOTE_INT_PORT)
44 #define R_DDR _DDRPORT(REMOTE_INT_PORT)
45 #define R_PIN _INPORT(REMOTE_INT_PORT)
46 #define INTPIN REMOTE_INT_PIN
48 #define M_PORT _OUTPORT(REMOTE_MASTER_PORT)
49 #define M_DDR _DDRPORT(REMOTE_MASTER_PORT)
50 #define M_PIN _INPORT(REMOTE_MASTER_PORT)
51 #define M_PIN1 REMOTE_MASTER_PIN1
52 #define M_PIN2 REMOTE_MASTER_PIN2
56 /* serial communication */
58 struct remote_msg_t msg
;
59 uint8_t buf
[REMOTE_MSG_LEN
];
66 #if CONFIG_MASTER_MODE
67 struct pt master_thread
;
79 struct remote_state_t remote
;
80 struct global_remote_t global_remote
;
82 #if CONFIG_SERIAL && CONFIG_REMOTE
84 /* static parsing routines */
85 static void parse_fade_rgb(struct remote_msg_fade_rgb_t
*msg
);
86 static void parse_fade_hsv(struct remote_msg_fade_hsv_t
*msg
);
87 static void parse_save_rgb(struct remote_msg_save_rgb_t
*msg
);
88 static void parse_save_hsv(struct remote_msg_save_hsv_t
*msg
);
89 static void parse_save_current(struct remote_msg_save_current_t
*msg
);
90 static void parse_config_offsets(struct remote_msg_config_offsets_t
*msg
);
91 static void parse_start_program(struct remote_msg_start_program_t
*msg
);
92 static void parse_stop(struct remote_msg_stop_t
*msg
);
93 static void parse_modify_current(struct remote_msg_modify_current_t
*msg
);
94 static void parse_pull_int(struct remote_msg_pull_int_t
*msg
);
95 static void parse_config_startup(struct remote_msg_config_startup_t
*msg
);
96 static void parse_bootloader(struct remote_msg_bootloader_t
*msg
);
97 static void parse_powerdown(void);
99 void remote_init(void)
101 /* master mode check: check if PIN2 pulls down PIN1 */
102 /* configure M_PIN1 as input with pullup */
103 M_DDR
&= ~_BV(M_PIN1
);
104 M_PORT
|= _BV(M_PIN1
);
105 /* configure M_PIN2 as output, set low */
106 M_DDR
|= _BV(M_PIN2
);
107 M_PORT
&= ~_BV(M_PIN2
);
109 /* initialize offsets */
110 global_remote
.offsets
.saturation
= 255;
111 global_remote
.offsets
.value
= 255;
113 /* initialize int pin, tri-state */
114 R_DDR
&= ~_BV(INTPIN
);
115 R_PORT
&= ~_BV(INTPIN
);
117 remote
.int_state
= INT_IDLE
;
119 PT_INIT(&remote
.master_thread
);
122 #if CONFIG_MASTER_MODE == 1
123 /* check master state: read value of M_PIN1 */
124 if (!(M_PIN
& _BV(M_PIN1
)))
125 global_remote
.master
= true;
126 #elif CONFIG_MASTER_MODE == 2
127 /* statically configure master mode */
128 global_remote
.master
= true;
132 static void remote_parse_msg(struct remote_msg_t
*msg
)
135 if (msg
->address
!= global_remote
.address
&& msg
->address
!= REMOTE_ADDR_BROADCAST
)
140 case REMOTE_CMD_FADE_RGB
:
141 parse_fade_rgb((struct remote_msg_fade_rgb_t
*)msg
);
143 case REMOTE_CMD_FADE_HSV
:
144 parse_fade_hsv((struct remote_msg_fade_hsv_t
*)msg
);
146 case REMOTE_CMD_SAVE_RGB
:
147 parse_save_rgb((struct remote_msg_save_rgb_t
*)msg
);
149 case REMOTE_CMD_SAVE_HSV
:
150 parse_save_hsv((struct remote_msg_save_hsv_t
*)msg
);
152 case REMOTE_CMD_SAVE_CURRENT
:
153 parse_save_current((struct remote_msg_save_current_t
*)msg
);
155 case REMOTE_CMD_CONFIG_OFFSETS
:
156 parse_config_offsets((struct remote_msg_config_offsets_t
*)msg
);
158 case REMOTE_CMD_START_PROGRAM
:
159 parse_start_program((struct remote_msg_start_program_t
*)msg
);
161 case REMOTE_CMD_STOP
:
162 parse_stop((struct remote_msg_stop_t
*)msg
);
164 case REMOTE_CMD_MODIFY_CURRENT
:
165 parse_modify_current((struct remote_msg_modify_current_t
*)msg
);
167 case REMOTE_CMD_PULL_INT
:
168 parse_pull_int((struct remote_msg_pull_int_t
*)msg
);
170 case REMOTE_CMD_CONFIG_STARTUP
:
171 parse_config_startup((struct remote_msg_config_startup_t
*)msg
);
173 case REMOTE_CMD_BOOTLOADER
:
174 parse_bootloader((struct remote_msg_bootloader_t
*)msg
);
176 case REMOTE_CMD_POWERDOWN
:
183 static PT_THREAD(remote_thread(struct pt
*thread
))
188 PT_WAIT_UNTIL(thread
, remote
.buflen
== REMOTE_MSG_LEN
);
190 remote_parse_msg(&remote
.msg
);
197 static __noinline
void send_msg(struct remote_msg_t
*msg
)
199 uint8_t *ptr
= (uint8_t *)msg
;
200 for (uint8_t i
= 0; i
< REMOTE_MSG_LEN
; i
++)
205 static void send_resync(uint8_t addr
)
207 for (uint8_t i
= 0; i
< REMOTE_SYNC_LEN
; i
++)
208 uart_putc(REMOTE_CMD_RESYNC
);
212 #if CONFIG_MASTER_MODE
213 /* parameters for master mode script commands */
214 #define MASTER_PROGRAMS 2
215 static PROGMEM
uint8_t master_parameters
[] = {
216 /* first: colorwheel forward */
217 0, /* program index */
221 0, 0, /* hue start (little endian) */
222 20, 0, /* hue step (little endian) */
224 255, /* saturation */
227 /* first: colorwheel backward */
228 0, /* program index */
232 0, 0, /* hue start (little endian) */
233 20, 0, /* hue step (little endian) */
235 255, /* saturation */
239 static PT_THREAD(remote_master_thread(struct pt
*thread
))
241 static struct remote_msg_start_program_t msg
;
242 static timer_t timer
;
243 static uint16_t sleep
;
250 timer_set(&timer
, MASTER_WAIT_BEFORE_SYNC
);
251 while(!timer_expired(&timer
))
254 /* start program on all devices */
256 msg
.cmd
= REMOTE_CMD_START_PROGRAM
;
259 ptr
= &master_parameters
[0];
261 for (idx
= 0; idx
< MASTER_PROGRAMS
; idx
++) {
262 /* stop current program and fading */
266 /* start program colorwheel on all nodes */
267 msg
.script
= pgm_read_byte(ptr
++);
268 /* load parameters */
269 for (uint8_t i
= 0; i
< sizeof(msg
.params
); i
++)
270 msg
.params
.raw
[i
] = pgm_read_byte(ptr
++);
273 send_resync(MASTER_MODE_FIRST_ADDRESS
);
275 send_msg((struct remote_msg_t
*)&msg
);
277 /* start program locally */
278 script_start(0, msg
.script
, &msg
.params
);
281 sleep
= MASTER_MODE_SLEEP
;
284 timer_set(&timer
, 100);
286 while (!timer_expired(&timer
))
296 void remote_poll(void)
298 if (fifo_fill((fifo_t
*)&global_uart
.rx
) > 0) {
299 uint8_t data
= fifo_dequeue((fifo_t
*)&global_uart
.rx
);
301 /* check if sync sequence has been received before */
302 if (remote
.sync
== REMOTE_SYNC_LEN
) {
303 /* synced, safe address and send next address to following device */
304 global_remote
.address
= data
;
307 /* reset remote buffer */
310 /* enable remote command thread */
312 PT_INIT(&remote
.thread
);
313 #if CONFIG_MASTER_MODE
314 global_remote
.master
= false;
317 /* just pass through data */
320 /* put data into remote buffer
321 * (just processed if remote.synced == 1) */
322 if (remote
.buflen
< sizeof(remote
.buf
))
323 remote
.buf
[remote
.buflen
++] = data
;
326 /* remember the number of sync bytes received so far */
327 if (data
== REMOTE_CMD_RESYNC
)
334 PT_SCHEDULE(remote_thread(&remote
.thread
));
336 /* check int timer */
337 if (remote
.int_state
== INT_PULLED_TIMER
&& timer_expired(&remote
.int_timer
)) {
338 remote_release_int();
341 #if CONFIG_MASTER_MODE
342 if (global_remote
.master
)
343 PT_SCHEDULE(remote_master_thread(&remote
.master_thread
));
347 void remote_pull_int(void)
349 if (remote
.int_state
!= INT_IDLE
)
352 /* pull int pin to ground */
353 R_DDR
|= _BV(INTPIN
);
354 R_PORT
&= ~_BV(INTPIN
);
357 void remote_release_int(void)
359 if (remote
.int_state
== INT_IDLE
)
362 /* reset pin to tri-state */
363 R_DDR
&= ~_BV(INTPIN
);
364 R_PORT
&= ~_BV(INTPIN
);
365 remote
.int_state
= INT_IDLE
;
368 /* offset helper functions */
369 uint8_t remote_apply_offset(uint8_t value
, int8_t offset
)
376 } else if (offset
> 0) {
377 uint16_t temp
= value
;
388 static uint8_t apply_scale(uint8_t value
, uint8_t scale
)
390 uint16_t temp
= value
* scale
;
395 void remote_apply_hsv_offset(struct hsv_color_t
*color
)
397 color
->hue
+= global_remote
.offsets
.hue
;
398 color
->saturation
= apply_scale(color
->saturation
, global_remote
.offsets
.saturation
);
399 color
->value
= apply_scale(color
->value
, global_remote
.offsets
.value
);
402 /* parser functions */
404 void parse_fade_rgb(struct remote_msg_fade_rgb_t
*msg
)
406 pwm_fade_rgb(&msg
->color
, msg
->step
, msg
->delay
);
409 void parse_fade_hsv(struct remote_msg_fade_hsv_t
*msg
)
411 pwm_fade_hsv(&msg
->color
, msg
->step
, msg
->delay
);
414 void parse_save_rgb(struct remote_msg_save_rgb_t
*msg
)
416 if (msg
->slot
>= CONFIG_EEPROM_COLORS
)
419 struct storage_color_t c
;
421 c
.delay
= msg
->delay
;
422 c
.pause
= msg
->pause
;
424 /* mark color as rgb */
425 c
.color
.rgb_marker
= 255;
427 c
.color
.red
= msg
->color
.red
;
428 c
.color
.green
= msg
->color
.green
;
429 c
.color
.blue
= msg
->color
.blue
;
431 storage_save_color(msg
->slot
, &c
);
434 void parse_save_hsv(struct remote_msg_save_hsv_t
*msg
)
436 if (msg
->slot
>= CONFIG_EEPROM_COLORS
)
439 struct storage_color_t c
;
441 c
.delay
= msg
->delay
;
442 c
.pause
= msg
->pause
;
444 c
.color
.hue
= msg
->color
.hue
;
445 c
.color
.saturation
= msg
->color
.saturation
;
446 c
.color
.value
= msg
->color
.value
;
448 storage_save_color(msg
->slot
, &c
);
451 void parse_save_current(struct remote_msg_save_current_t
*msg
)
453 if (msg
->slot
>= CONFIG_EEPROM_COLORS
)
456 struct storage_color_t c
;
458 c
.delay
= msg
->delay
;
459 c
.pause
= msg
->pause
;
461 /* mark color as rgb */
462 c
.color
.rgb_marker
= 255;
464 c
.color
.red
= global_pwm
.current
.red
;
465 c
.color
.green
= global_pwm
.current
.green
;
466 c
.color
.blue
= global_pwm
.current
.blue
;
468 storage_save_color(msg
->slot
, &c
);
471 void parse_config_offsets(struct remote_msg_config_offsets_t
*msg
)
473 global_remote
.offsets
.step
= msg
->step
;
474 global_remote
.offsets
.delay
= msg
->delay
;
475 global_remote
.offsets
.hue
= msg
->hue
;
476 global_remote
.offsets
.saturation
= msg
->saturation
;
477 global_remote
.offsets
.value
= msg
->value
;
480 void parse_start_program(struct remote_msg_start_program_t
*msg
)
484 script_start(0, msg
->script
, &msg
->params
);
487 void parse_stop(struct remote_msg_stop_t
*msg
)
494 void parse_modify_current(struct remote_msg_modify_current_t
*msg
)
496 /* return if a color change is in progress */
498 if (!global_script
.disable
|| !pwm_target_reached())
501 if (!pwm_target_reached())
505 /* apply rgb and hsv offsets */
506 pwm_modify_rgb(&msg
->rgb
, msg
->step
, msg
->delay
);
507 pwm_modify_hsv(&msg
->hsv
, msg
->step
, msg
->delay
);
510 void parse_pull_int(struct remote_msg_pull_int_t
*msg
)
512 /* pull pin to ground */
515 uint8_t delay
= msg
->delay
;
517 /* start timer, delay between 50ms and 2550ms */
520 else if (delay
<= 51)
525 timer_set(&remote
.int_timer
, delay
);
528 remote
.int_state
= INT_PULLED_TIMER
;
531 void parse_config_startup(struct remote_msg_config_startup_t
*msg
)
533 /* set mode and copy data */
534 memcpy(&startup_config
.params
, &msg
->params
, sizeof(struct startup_parameters_t
));
535 /* save config to eeprom */
536 storage_save_config();
539 static void wait_for_uart(void)
541 while (fifo_fill((fifo_t
*)&global_uart
.tx
) != 0 || !uart_send_complete());
544 void parse_bootloader(struct remote_msg_bootloader_t
*msg
)
546 /* check magic bytes */
547 if (msg
->magic
[0] != BOOTLOADER_MAGIC_BYTE1
||
548 msg
->magic
[1] != BOOTLOADER_MAGIC_BYTE2
||
549 msg
->magic
[2] != BOOTLOADER_MAGIC_BYTE3
||
550 msg
->magic
[3] != BOOTLOADER_MAGIC_BYTE4
)
553 /* wait until the tx fifo is empty, then start watchdog, but never kick it
554 * (bootloader and firmware both disable the watchdog) */
556 wdt_enable(WDTO_120MS
);
559 void parse_powerdown(void)
561 /* configure sleep mode */
562 set_sleep_mode(SLEEP_MODE_PWR_DOWN
);
564 /* wait until the tx fifo is empty before sleep */
567 /* save all output registers */
568 uint8_t portb
= PORTB
;
569 uint8_t portc
= PORTC
;
570 uint8_t portd
= PORTD
;
581 /* configure int pin as input (already done by setting the
582 * DDR register to zero) with pullup */
583 R_PORT
= _BV(INTPIN
);
585 /* enable int0 low level interrupt */
586 _IFR_INT0
= _BV(INTF0
);
587 _ICR_INT0
|= _BV(INT0
);
588 /* enter sleep mode */
591 /* wakeup, disable int0 */
592 _ICR_INT0
&= ~_BV(INT0
);
594 /* restore output registers */
603 /* do nothing, just for wakeup after sleep */
604 EMPTY_INTERRUPT(INT0_vect
);