430 lines
16 KiB
C
430 lines
16 KiB
C
/***************************************************************************
|
|
* 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 <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/wdt.h>
|
|
#include <avr/pgmspace.h>
|
|
|
|
#include <string.h> /* 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<<WDCE) | (1<<WDE);
|
|
WDTCSR = (0<<WDE);
|
|
} /* disable_wdt_timer */
|
|
|
|
|
|
int main(void)
|
|
{
|
|
LED_INIT();
|
|
|
|
timer_init();
|
|
uart_init();
|
|
input_init();
|
|
pwm_init();
|
|
|
|
rfm12_init(RFM12_ADDRESS);
|
|
|
|
sei();
|
|
|
|
uart_putstr_p(PSTR("init\r\n"));
|
|
|
|
while (1)
|
|
{
|
|
if (timer_check(1))
|
|
{
|
|
/* do periodic work (wait for 5 ticks silence before start TX) */
|
|
rfm12_tick(5);
|
|
|
|
led_tick();
|
|
}
|
|
|
|
struct event_entry *event = event_get();
|
|
if (event->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 */
|