You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

368 lines
13 KiB

/***************************************************************************
* 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];
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<<WDCE) | (1<<WDE);
WDTCSR = (0<<WDE);
} /* disable_wdt_timer */
int main(void)
{
LED_INIT();
/* timer0, FCPU/64, overflow interrupt */
TCCR0B = (1<<CS01) | (1<<CS00);
TIMSK0 = (1<<TOIE0);
uart_init();
input_init();
pwm_init();
rfm12_init(RFM12_ADDRESS);
sei();
uart_putstr_p(PSTR("init\r\n"));
while (1)
{
if (clock_tick)
{
clock_tick = 0;
/* do periodic work (wait for 5 ticks silence before start TX) */
rfm12_tick(5);
led_tick();
input_tick();
timer_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,
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 */