/*************************************************************************** * 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/pwm_cmd" }, { DESCRIPTION_TYPE_CONTROL, 0xFF, 0xFF, EVENT_TYPE_PWM_COMMAND, EVENT_NUM_PWM_CH1, "wall/pwm_cmd" }, { DESCRIPTION_TYPE_CONTROL, 0xFF, 0xFF, EVENT_TYPE_PWM_VALUE, EVENT_NUM_PWM_CH0, "door/pwm_set" }, { DESCRIPTION_TYPE_CONTROL, 0xFF, 0xFF, EVENT_TYPE_PWM_VALUE, EVENT_NUM_PWM_CH1, "wall/pwm_set" }, }; static const struct _description_response event_desc[] PROGMEM = { // { DESCRIPTION_TYPE_EVENT, 0xFF, 0xFF, EVENT_TYPE_INPUT_BUTTON, EVENT_NUM_INPUT_BUTTON, "wall/input" }, // { DESCRIPTION_TYPE_EVENT, 0xFF, 0xFF, EVENT_TYPE_INPUT_SWITCH, EVENT_NUM_INPUT_DOOR, "door/input" }, { DESCRIPTION_TYPE_EVENT, 0xFF, 0xFF, EVENT_TYPE_PWM_STATUS, EVENT_NUM_PWM_CH0, "door/pwm" }, { DESCRIPTION_TYPE_EVENT, 0xFF, 0xFF, EVENT_TYPE_PWM_STATUS, EVENT_NUM_PWM_CH1, "wall/pwm" }, }; #define EVT_DATA_STATE_FREE 0x00 #define EVT_DATA_STATE_REGISTERED 0x01 #define EVT_DATA_STATE_UPDATE 0x02 #define EVT_DATA_STATE_WAIT4CONF 0x04 struct _event_data { uint8_t state; // free/registered/wait4conf/retrigger uint8_t dest_address; uint16_t data_value; // type/num via event_desc with same idx }; static struct _event_data event_data[2]; static uint8_t event_idx; static uint8_t rtx_count; /* *********************************************************************** */ #define LED_RX 0 #define LED_TX 1 static uint8_t led[2]; 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, EVENT_VALUE_PWM_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, EVENT_NUM_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, EVENT_NUM_TIMER_DOOR_CLOSE_DELAY, 0); } } break; case EVENT_TYPE_TIMER_SET: timer_event_handler(event); break; case EVENT_TYPE_TIMER_ELAPSED: if ((event->num == EVENT_NUM_TIMER_DEBOUNCE1) || (event->num == EVENT_NUM_TIMER_DEBOUNCE2) ) { input_event_handler(event); } else if (event->num == EVENT_NUM_TIMER_DOOR_CLOSE_DELAY) { event_queue(EVENT_TYPE_PWM_COMMAND, EVENT_NUM_PWM_CH0, EVENT_VALUE_PWM_FADE_MIN); } break; default: break; } uint8_t i; for (i = 0; i < ARRAY_SIZE(event_desc); i++) { if ((pgm_read_byte(&event_desc[i].data_type) == event->type) && (pgm_read_byte(&event_desc[i].data_num) == event->num) ) { event_data[i].data_value = event->value; if (event_data[i].state & EVT_DATA_STATE_REGISTERED) { event_data[i].state |= EVT_DATA_STATE_UPDATE; } } } event_clear(); } /* get RX data */ struct rfm12_packet *rx_pkt = rfm12_get_rxpkt(); if (rx_pkt != NULL) { led[LED_RX] = 5; struct rfm12_msg *rx_msg = (struct rfm12_msg *)rx_pkt->data; if ((rx_msg->command & MSG_TYPE_MASK) == MSG_TYPE_REQUEST) { struct rfm12_packet *rsp_pkt = rfm12_get_txpkt(); if (rsp_pkt == NULL) { rfm12_clear_rx(); continue; } struct rfm12_msg *rsp_msg = (struct rfm12_msg *)rsp_pkt->data; rsp_pkt->dest_address = rx_pkt->source_address; rsp_pkt->data_length = 3; rsp_msg->command = rx_msg->command | MSG_TYPE_RESPONSE; rsp_msg->seqnum = rx_msg->seqnum; rsp_msg->cause = CAUSE_SUCCESS; uint8_t i; switch (rx_msg->command) { case MSG_CMD_SWITCHAPP_REQUEST: if (rx_msg->p.switchapp.app == BOOTMODE_BOOTLOADER) { wdt_enable(WDTO_15MS); } else if (rx_msg->p.switchapp.app != BOOTMODE_APPLICATION) { rsp_msg->cause = CAUSE_INVALID_PARAMETER; } break; case MSG_CMD_DESCRIPTION_REQUEST: if ((rx_msg->p.desc_req.type == DESCRIPTION_TYPE_CONTROL) && (rx_msg->p.desc_req.num < ARRAY_SIZE(control_desc)) ) { memcpy_P(&rsp_msg->p.desc_rsp, &control_desc[rx_msg->p.desc_req.num], sizeof(struct _description_response)); rsp_msg->p.desc_rsp.num = rx_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 ((rx_msg->p.desc_req.type == DESCRIPTION_TYPE_EVENT) && (rx_msg->p.desc_req.num < ARRAY_SIZE(event_desc)) ) { memcpy_P(&rsp_msg->p.desc_rsp, &event_desc[rx_msg->p.desc_req.num], sizeof(struct _description_response)); rsp_msg->p.desc_rsp.num = rx_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) == rx_msg->p.ctrl_req.data_type) && (pgm_read_byte(&control_desc[i].data_num) == rx_msg->p.ctrl_req.data_num) ) { event_queue(rx_msg->p.ctrl_req.data_type, rx_msg->p.ctrl_req.data_num, rx_msg->p.ctrl_req.data_value); rsp_msg->cause = CAUSE_SUCCESS; rsp_msg->p.ctrl_rsp.data_type = rx_msg->p.ctrl_req.data_type; rsp_msg->p.ctrl_rsp.data_num = rx_msg->p.ctrl_req.data_num; rsp_pkt->data_length += sizeof(struct _control_response); break; } } break; case MSG_CMD_REGISTER_REQUEST: rsp_msg->cause = CAUSE_INVALID_PARAMETER; for (i = 0; i < ARRAY_SIZE(event_desc); i++) { if ((pgm_read_byte(&event_desc[i].data_type) == rx_msg->p.reg_req.data_type) && (pgm_read_byte(&event_desc[i].data_num) == rx_msg->p.reg_req.data_num) ) { if (rx_msg->p.reg_req.enable) { event_data[i].state |= EVT_DATA_STATE_REGISTERED; event_data[i].dest_address = rx_pkt->source_address; } else { event_data[i].state |= ~(EVT_DATA_STATE_REGISTERED | EVT_DATA_STATE_UPDATE); } rsp_msg->cause = CAUSE_SUCCESS; rsp_msg->p.reg_rsp.data_type = rx_msg->p.reg_req.data_type; rsp_msg->p.reg_rsp.data_num = rx_msg->p.reg_req.data_num; rsp_msg->p.reg_rsp.data_value = event_data[i].data_value; rsp_pkt->data_length += sizeof(struct _register_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; } } else if ((rx_msg->command & MSG_TYPE_MASK) == MSG_TYPE_CONFIRMATION) { uint8_t i; switch (rx_msg->command) { case MSG_CMD_EVENT_CONFIRMATION: for (i = 0; i < ARRAY_SIZE(event_desc); i++) { if ((pgm_read_byte(&event_desc[i].data_type) == rx_msg->p.evt_cnf.data_type) && (pgm_read_byte(&event_desc[i].data_num) == rx_msg->p.evt_cnf.data_num) ) { if (event_data[i].state & EVT_DATA_STATE_WAIT4CONF) { event_data[i].state &= ~(EVT_DATA_STATE_WAIT4CONF); event_idx++; if (event_idx == ARRAY_SIZE(event_desc)) { event_idx = 0; } } } } break; default: break; } /* RX packet no longer needed */ rfm12_clear_rx(); } } } return 0; } /* main */