/*************************************************************************** * Copyright (C) 11/2014 by Olaf Rempel * * razzor@kopf-tisch.de * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; version 2 of the License, * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include /* memcpy() */ #include "event.h" #include "input.h" #include "pwm.h" #include "rfm12.h" #include "timer.h" #include "uart.h" #include "funk_proto.h" #include "target.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) /* *********************************************************************** */ static const struct _description_response control_desc[] PROGMEM = { { DESCRIPTION_TYPE_CONTROL, 0xFF, 0xFF, EVENT_TYPE_PWM_COMMAND, EVENT_NUM_PWM_CH0, "Door Light Cmd" }, { DESCRIPTION_TYPE_CONTROL, 0xFF, 0xFF, EVENT_TYPE_PWM_COMMAND, EVENT_NUM_PWM_CH1, "Wall Light Cmd" }, { DESCRIPTION_TYPE_CONTROL, 0xFF, 0xFF, EVENT_TYPE_PWM_VALUE, EVENT_NUM_PWM_CH0, "Door Light Val" }, { DESCRIPTION_TYPE_CONTROL, 0xFF, 0xFF, EVENT_TYPE_PWM_VALUE, EVENT_NUM_PWM_CH1, "Wall Light Val" }, }; static const struct _description_response event_desc[] PROGMEM = { { DESCRIPTION_TYPE_EVENT, 0xFF, 0xFF, EVENT_TYPE_INPUT_BUTTON, EVENT_NUM_INPUT_BUTTON, "Wall Button" }, { DESCRIPTION_TYPE_EVENT, 0xFF, 0xFF, EVENT_TYPE_INPUT_SWITCH, EVENT_NUM_INPUT_DOOR, "Door Switch" }, }; /* *********************************************************************** */ #define LED_RX 0 #define LED_TX 1 static uint8_t led[2]; volatile static uint8_t clock_tick; ISR(TIMER0_OVF_vect) { /* 1ms interrupt */ TCNT0 = TIMER_RELOAD; clock_tick = 1; } /* TIMER0_OVF_vect */ static void led_tick(void) { if (led[LED_RX] > 0) { led[LED_RX]--; LED_GN_ON(); } else { LED_GN_OFF(); } if (led[LED_TX] > 0) { led[LED_TX]--; LED_RT_ON(); } else { LED_RT_OFF(); } } /* led_tick */ /* * For newer devices the watchdog timer remains active even after a * system reset. So disable it as soon as possible. * automagically called on startup */ void disable_wdt_timer(void) __attribute__((naked, section(".init3"))); void disable_wdt_timer(void) { MCUSR = 0; WDTCSR = (1<type != EVENT_TYPE_EMPTY) { #if 0 /* output all events on UART */ uart_putstr_p(PSTR("evt: ")); uart_put_hex(event->type); uart_putc(' '); uart_put_hex(event->num); uart_putc(' '); uart_put_hex(event->value >> 8); uart_put_hex(event->value & 0xFF); uart_putstr("\r\n"); #endif switch (event->type) { case EVENT_TYPE_PWM_COMMAND: case EVENT_TYPE_PWM_VALUE: pwm_event_handler(event); break; case EVENT_TYPE_INPUT_INCDEC: if (event->num == EVENT_NUM_INPUT_QUAD) { if (event->value == EVENT_VALUE_INPUT_QUAD_DEC) { event_queue(EVENT_TYPE_PWM_COMMAND, EVENT_NUM_PWM_CH1, EVENT_VALUE_PWM_DEC ); } else if (event->value == EVENT_VALUE_INPUT_QUAD_INC) { event_queue(EVENT_TYPE_PWM_COMMAND, EVENT_NUM_PWM_CH1, EVENT_VALUE_PWM_INC ); } } break; case EVENT_TYPE_INPUT_BUTTON: if (event->num == EVENT_NUM_INPUT_BUTTON) { if (event->value == EVENT_VALUE_INPUT_BUTTON_PRESSED) { event_queue(EVENT_TYPE_PWM_COMMAND, EVENT_NUM_PWM_CH1, PWM_MODE_TOGGLE ); } } break; case EVENT_TYPE_INPUT_SWITCH: if (event->num == EVENT_NUM_INPUT_DOOR) { if (event->value == EVENT_VALUE_INPUT_DOOR_CLOSED) { event_queue(EVENT_TYPE_PWM_COMMAND, EVENT_NUM_PWM_CH0, EVENT_VALUE_PWM_KEEP ); event_queue(EVENT_TYPE_TIMER_SET, TIMER_DOOR_CLOSE_DELAY, 2000 ); } else if (event->value == EVENT_VALUE_INPUT_DOOR_OPEN) { event_queue(EVENT_TYPE_PWM_COMMAND, EVENT_NUM_PWM_CH0, EVENT_VALUE_PWM_FADE_MAX ); event_queue(EVENT_TYPE_TIMER_SET, TIMER_DOOR_CLOSE_DELAY, 0 ); } } break; case EVENT_TYPE_TIMER_SET: timer_event_handler(event); break; case EVENT_TYPE_TIMER_ELAPSED: if (event->num == TIMER_DOOR_CLOSE_DELAY) { event_queue(EVENT_TYPE_PWM_COMMAND, EVENT_NUM_PWM_CH0, EVENT_VALUE_PWM_FADE_MIN ); } break; default: break; } event_clear(); } /* get RX data */ struct rfm12_packet *req_pkt = rfm12_get_rxpkt(); struct rfm12_packet *rsp_pkt = rfm12_get_txpkt(); if (req_pkt != NULL) { led[LED_RX] = 5; if (rsp_pkt == NULL) { rfm12_clear_rx(); continue; } struct rfm12_msg *req_msg = (struct rfm12_msg *)req_pkt->data; struct rfm12_msg *rsp_msg = (struct rfm12_msg *)rsp_pkt->data; #if 0 /* retransmitted request -> retransmit response */ if ((req_pkt->source_address == rsp_pkt->dest_address) && ((req_msg->command & MSG_CMD_MASK) == (rsp_msg->command & MSG_CMD_MASK)) && (req_msg->seqnum == rsp_msg->seqnum) ) { /* RX packet no longer needed */ rfm12_clear_rx(); /* transmit response */ if (rfm12_start_tx()) { led[LED_TX] = 5; } continue; } #endif rsp_pkt->dest_address = req_pkt->source_address; rsp_pkt->data_length = 3; rsp_msg->command = req_msg->command | MSG_TYPE_RESPONSE; rsp_msg->seqnum = req_msg->seqnum; rsp_msg->cause = CAUSE_SUCCESS; uint8_t i; switch (req_msg->command) { case MSG_CMD_SWITCHAPP_REQUEST: if (req_msg->p.switchapp.app == BOOTMODE_BOOTLOADER) { wdt_enable(WDTO_15MS); } else if (req_msg->p.switchapp.app != BOOTMODE_APPLICATION) { rsp_msg->cause = CAUSE_INVALID_PARAMETER; } break; case MSG_CMD_DESCRIPTION_REQUEST: if ((req_msg->p.desc_req.type == DESCRIPTION_TYPE_CONTROL) && (req_msg->p.desc_req.num < ARRAY_SIZE(control_desc)) ) { memcpy_P(&rsp_msg->p.desc_rsp, &control_desc[req_msg->p.desc_req.num], sizeof(struct _description_response)); rsp_msg->p.desc_rsp.num = req_msg->p.desc_req.num; rsp_msg->p.desc_rsp.max_num = ARRAY_SIZE(control_desc) -1; rsp_pkt->data_length += sizeof(struct _description_response); } else if ((req_msg->p.desc_req.type == DESCRIPTION_TYPE_EVENT) && (req_msg->p.desc_req.num < ARRAY_SIZE(event_desc)) ) { memcpy_P(&rsp_msg->p.desc_rsp, &event_desc[req_msg->p.desc_req.num], sizeof(struct _description_response)); rsp_msg->p.desc_rsp.num = req_msg->p.desc_req.num; rsp_msg->p.desc_rsp.max_num = ARRAY_SIZE(event_desc) -1; rsp_pkt->data_length += sizeof(struct _description_response); } else { rsp_msg->cause = CAUSE_INVALID_PARAMETER; } break; case MSG_CMD_CONTROL_REQUEST: rsp_msg->cause = CAUSE_INVALID_PARAMETER; for (i = 0; i < ARRAY_SIZE(control_desc); i++) { if ((pgm_read_byte(&control_desc[i].data_type) == req_msg->p.ctrl_req.data_type) && (pgm_read_byte(&control_desc[i].data_num) == req_msg->p.ctrl_req.data_num) ) { event_queue(req_msg->p.ctrl_req.data_type, req_msg->p.ctrl_req.data_num, req_msg->p.ctrl_req.data_value); rsp_msg->cause = CAUSE_SUCCESS; rsp_msg->p.ctrl_rsp.data_type = req_msg->p.ctrl_req.data_type; rsp_msg->p.ctrl_rsp.data_num = req_msg->p.ctrl_req.data_num; rsp_pkt->data_length += sizeof(struct _control_response); break; } } break; default: rsp_msg->cause = CAUSE_NOT_SUPPORTED; break; } /* RX packet no longer needed */ rfm12_clear_rx(); /* transmit response */ if (rfm12_start_tx()) { led[LED_TX] = 5; } } } return 0; } /* main */