funkstuff/funkstuff.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 */