355 lines
12 KiB
C
355 lines
12 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 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];
|
|
|
|
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_PWM_STATUS:
|
|
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_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 */
|