Skip to content

Instantly share code, notes, and snippets.

@piotrmaslanka
Created June 13, 2014 01:05
Show Gist options
  • Save piotrmaslanka/8b23d7b59116b2ebf9c5 to your computer and use it in GitHub Desktop.
Save piotrmaslanka/8b23d7b59116b2ebf9c5 to your computer and use it in GitHub Desktop.
[Atmel AVR] Software for my DIY home alarm controller. Superseded by a ladder-logic PLC solution. Contains a very nice UART/MODBUS library though.
#define F_CPU 1843200
#include "avr/interrupt.h"
#include "avr/wdt.h"
#include "pins.h"
#include "uart.h"
#define SIGNAL_LENGTH 4
#define ALARM_LENGTH 0x04EC
#define VB_ARMED 0x7F
#define VB_UNARMED 0x01
unsigned char bAlarmMask, bAlarmEnabled, bSiren, bDebugStatus = 0, bPresenceSuspend = 0;
void rearm(unsigned char newMask)
{
bAlarmEnabled = 0;
bAlarmMask = (newMask | VB_UNARMED) & VB_ARMED;
}
void boot()
{
rearm(VB_UNARMED);
pins_init();
uart_init();
wdt_enable(WDTO_60MS);
sei();
}
void loop()
{
unsigned char presence = 0; // --------------- do presence
for(int i=0; i<7; i++)
if (baTrigger[i] >= SIGNAL_LENGTH) presence |= 1 << i;
if ((presence & bAlarmMask) > 0) // -------------- check alarm
{
cli(); iTimer = 0; sei();
bAlarmEnabled = 1;
}
// -------------------- alarm disabler
if ((bAlarmEnabled == 1) && (iTimer > ALARM_LENGTH)) bAlarmEnabled = 0;
// -------------- arm-via-casette
if (baTrigger[7] == SIGNAL_LENGTH)
{
cli(); baTrigger[7]++; sei();
if (bAlarmMask == VB_UNARMED) rearm(VB_ARMED);
else rearm(VB_UNARMED);
}
// -------------- arm led blinker
if (bAlarmMask == VB_ARMED)
{
pins_out(PINS_ARMLED, 1);
}
else if (bAlarmMask == VB_UNARMED)
{
pins_out(PINS_ARMLED, 0);
}
else
{
if ((iTimer & 16) == 16) pins_out(PINS_ARMLED, 1);
else pins_out(PINS_ARMLED, 0);
}
// ---------------------- siren
uint8_t siren_temp = 0;
siren_temp |= bSiren;
siren_temp |= bAlarmEnabled;
pins_out(PINS_SIREN, siren_temp);
// ---------------------- suspend stopper
bPresenceSuspend |= presence;
wdt_reset();
}
int main()
{
boot();
for (;;) loop();
}
#define SIGNAL_LENGTH 5
#define ALARM_LENGTH 0x04EC
#define VB_ARMED 0x7F
#define VB_UNARMED 0x01
unsigned char bAlarmMask, bAlarmEnabled, bSiren, bPresenceSuspend, bDebugStatus;
void rearm(unsigned char newMask);
#include "uart.h"
const unsigned int CRC_LOOKUP_TABLE[256] =
{
0x0000,
0xc0c1,
0xc181,
0x140,
0xc301,
0x3c0,
0x280,
0xc241,
0xc601,
0x6c0,
0x780,
0xc741,
0x500,
0xc5c1,
0xc481,
0x440,
0xcc01,
0xcc0,
0xd80,
0xcd41,
0xf00,
0xcfc1,
0xce81,
0xe40,
0xa00,
0xcac1,
0xcb81,
0xb40,
0xc901,
0x9c0,
0x880,
0xc841,
0xd801,
0x18c0,
0x1980,
0xd941,
0x1b00,
0xdbc1,
0xda81,
0x1a40,
0x1e00,
0xdec1,
0xdf81,
0x1f40,
0xdd01,
0x1dc0,
0x1c80,
0xdc41,
0x1400,
0xd4c1,
0xd581,
0x1540,
0xd701,
0x17c0,
0x1680,
0xd641,
0xd201,
0x12c0,
0x1380,
0xd341,
0x1100,
0xd1c1,
0xd081,
0x1040,
0xf001,
0x30c0,
0x3180,
0xf141,
0x3300,
0xf3c1,
0xf281,
0x3240,
0x3600,
0xf6c1,
0xf781,
0x3740,
0xf501,
0x35c0,
0x3480,
0xf441,
0x3c00,
0xfcc1,
0xfd81,
0x3d40,
0xff01,
0x3fc0,
0x3e80,
0xfe41,
0xfa01,
0x3ac0,
0x3b80,
0xfb41,
0x3900,
0xf9c1,
0xf881,
0x3840,
0x2800,
0xe8c1,
0xe981,
0x2940,
0xeb01,
0x2bc0,
0x2a80,
0xea41,
0xee01,
0x2ec0,
0x2f80,
0xef41,
0x2d00,
0xedc1,
0xec81,
0x2c40,
0xe401,
0x24c0,
0x2580,
0xe541,
0x2700,
0xe7c1,
0xe681,
0x2640,
0x2200,
0xe2c1,
0xe381,
0x2340,
0xe101,
0x21c0,
0x2080,
0xe041,
0xa001,
0x60c0,
0x6180,
0xa141,
0x6300,
0xa3c1,
0xa281,
0x6240,
0x6600,
0xa6c1,
0xa781,
0x6740,
0xa501,
0x65c0,
0x6480,
0xa441,
0x6c00,
0xacc1,
0xad81,
0x6d40,
0xaf01,
0x6fc0,
0x6e80,
0xae41,
0xaa01,
0x6ac0,
0x6b80,
0xab41,
0x6900,
0xa9c1,
0xa881,
0x6840,
0x7800,
0xb8c1,
0xb981,
0x7940,
0xbb01,
0x7bc0,
0x7a80,
0xba41,
0xbe01,
0x7ec0,
0x7f80,
0xbf41,
0x7d00,
0xbdc1,
0xbc81,
0x7c40,
0xb401,
0x74c0,
0x7580,
0xb541,
0x7700,
0xb7c1,
0xb681,
0x7640,
0x7200,
0xb2c1,
0xb381,
0x7340,
0xb101,
0x71c0,
0x7080,
0xb041,
0x5000,
0x90c1,
0x9181,
0x5140,
0x9301,
0x53c0,
0x5280,
0x9241,
0x9601,
0x56c0,
0x5780,
0x9741,
0x5500,
0x95c1,
0x9481,
0x5440,
0x9c01,
0x5cc0,
0x5d80,
0x9d41,
0x5f00,
0x9fc1,
0x9e81,
0x5e40,
0x5a00,
0x9ac1,
0x9b81,
0x5b40,
0x9901,
0x59c0,
0x5880,
0x9841,
0x8801,
0x48c0,
0x4980,
0x8941,
0x4b00,
0x8bc1,
0x8a81,
0x4a40,
0x4e00,
0x8ec1,
0x8f81,
0x4f40,
0x8d01,
0x4dc0,
0x4c80,
0x8c41,
0x4400,
0x84c1,
0x8581,
0x4540,
0x8701,
0x47c0,
0x4680,
0x8641,
0x8201,
0x42c0,
0x4380,
0x8341,
0x4100,
0x81c1,
0x8081,
0x4040
};
void crc_calculate()
{
mbCRCH = 0xFF;
mbCRCL = 0xFF;
for(unsigned char i = 0; i < buffer_length; i++)
{
unsigned char value = buffer[i];
unsigned int factor = CRC_LOOKUP_TABLE[value ^ mbCRCL];
mbCRCL = mbCRCH ^ (unsigned char)(factor & 0xFF);
mbCRCH = (unsigned char)(factor >> 8);
}
unsigned char temp = mbCRCH;
mbCRCH = mbCRCL;
mbCRCL = temp;
}
void crc_calculate();
#include "avr/io.h"
#include "avr/interrupt.h"
#include "uart.h"
#define PINS_SIREN 1
#define PINS_ARMLED 2
uint16_t iTimer;
uint8_t baTrigger[8];
uint8_t pins_collect()
{
uint8_t c, d;
c = PINC;
d = PIND;
return (((c & 0xFC) + (d >> 6)) ^ 128);
}
SIGNAL (SIG_OVERFLOW0)
{
iTimer++;
uint8_t pins;
pins = pins_collect();
for (uint8_t i = 0; i < 8; i++)
if (((1 << i) & pins) > 0) baTrigger[i] = baTrigger[i] + 1;
else baTrigger[i] = 0;
if (bStatus == UART_TRANSMITTING) // If the device spends too much time on transmitting...
if ((iTimer - iTransmissionTimer) > 2) // it is invalid - reset transmission, set to listen
uart_switch_receive();
}
void pins_out(uint8_t what, uint8_t status)
{
status = 1 - status; // remember - outputs are negated
// by output board design
if (what == PINS_SIREN)
{
if (status == 0) PORTD &= ~8;
else if (status == 1) PORTD |= 8;
}
else if (what == PINS_ARMLED)
{
if (status == 0) PORTD &= ~4;
else if (status == 1) PORTD |= 4;
}
}
void pins_init()
{
DDRA = 0xFF;
DDRB = 0xFF; // PORTA, PORTB unused - outputs
DDRC = 0x03;
DDRD = 0x3C;
pins_out(PINS_SIREN, 0);
pins_out(PINS_ARMLED, 0);
TCCR0 = (1<<CS02)|(0<<CS01)|(1<<CS00); // Prescaler = 1024
TIMSK = (1 << TOIE0); // Interrupt on overflow
// will happen at 7,03 Hz
for(uint8_t i=0; i<8; i++) baTrigger[i] = 0;
}
#define PINS_SIREN 1
#define PINS_ARMLED 2
extern unsigned char baTrigger[8];
extern unsigned int iTimer;
void pins_out(unsigned char what, unsigned char status);
void pins_init();
#include "Alarm.h"
#include "uart.h"
unsigned int modbus_read_holding(unsigned int port)
{
mbhStatus = MODBUS_HANDLE_OK;
switch (port)
{
case 0x0000:
return (bAlarmEnabled << 8) + bPresenceSuspend;
case 0x0001:
return bAlarmMask;
case 0x0002:
return bSiren;
case 0x0003:
return bDebugStatus;
default:
mbhStatus = MODBUS_HANDLE_ERR;
}
return 0;
}
unsigned int modbus_write_holding(unsigned int port, unsigned int value)
{
mbhStatus = MODBUS_HANDLE_OK;
switch (port)
{
case 0x0000:
bPresenceSuspend = (unsigned char)(value & 0xFF);
return bPresenceSuspend;
case 0x0001:
rearm((unsigned char)(value & 0xFF));
return bAlarmMask;
case 0x0002:
bSiren = (unsigned char)(value & 0xFF);
return bSiren;
case 0x0003:
bDebugStatus = (unsigned char)(value & 0xFF);
return bDebugStatus;
default:
mbhStatus = MODBUS_HANDLE_ERR;
}
return 0;
}
unsigned int modbus_read_holding(unsigned int port);
unsigned int modbus_write_holding(unsigned int port, unsigned int value);
#include "avr/io.h"
#include "avr/interrupt.h"
uint16_t timer_timer;
SIGNAL (SIG_OVERFLOW0)
{
timer_timer++;
}
void timer_init()
{
TCCR0 = (1<<CS02)|(0<<CS01)|(1<<CS00); // equals to 0x05
TIMSK = (1 << TOIE0); // equals to 0x01
}
void timer_init();
#include "avr/io.h"
#include "avr/interrupt.h"
#include "crc.h"
#include "pins.h" // for iTimer
#include "registers.h" // MODBUS Responder
#define ADDRESS 0x0A
#define MAXREGISTER 3
#define SS_ADDRESS 1
#define SS_COMMAND 2
#define SS_REGISTER_H_RW 3
#define SS_REGISTER_L_RW 4
#define SS_VALUE_H_W 5
#define SS_VALUE_L_W 6
#define SS_AMOUNT_H_R 7
#define SS_AMOUNT_L_R 8
#define SS_CRC_H 9
#define SS_CRC_L 10
#define MC_READ 0x03
#define MC_WRITE 0x06
#define MODBUS_HANDLE_OK 0
#define MODBUS_HANDLE_ERR 1
#define UART_TRANSMITTING 0
#define UART_RECEIVING 1
unsigned char bStatus;
unsigned char bScanStatus;
unsigned int iTransmissionTimer;
// ----- modbus values
unsigned char mbCommand;
unsigned char mbRegisterL;
unsigned char mbValueL;
unsigned char mbAmountL;
unsigned char mbCRCH, mbCRCL;
// -------- interfacing
unsigned char mbhStatus;
unsigned char buffer[50];
unsigned char buffer_length;
unsigned char buffer_pointer;
inline void buf_reset() { buffer_length = 0; }
inline void buf_append(unsigned char value) { buffer[buffer_length] = value; buffer_length++; }
void uart_switch_receive() { PORTD &= ~16; bStatus = UART_RECEIVING; }
inline void uart_switch_transmit() { PORTD |= 16; bStatus = UART_TRANSMITTING; iTransmissionTimer = iTimer; }
unsigned char uart_send()
{
if (buffer_pointer == buffer_length) return 0;
UDR = buffer[buffer_pointer];
buffer_pointer++;
return 1;
}
SIGNAL (SIG_USART_TRANS)
{
if (bStatus == UART_RECEIVING) return;
if (uart_send() == 0) uart_switch_receive();
}
void analyze(unsigned char data);
inline void reanalyze(unsigned char data)
{
bScanStatus = SS_ADDRESS;
analyze(data);
}
void uart_process_input()
{
if (mbCommand == MC_READ)
{
buf_reset();
buf_append(ADDRESS);
buf_append(MC_READ);
unsigned char bytesize = mbAmountL * 2;
buf_append(bytesize & 0xFF);
for(unsigned int port=mbRegisterL; port < mbRegisterL+mbAmountL; port++)
{
unsigned int value = modbus_read_holding(port);
if (mbhStatus == MODBUS_HANDLE_ERR) return;
buf_append((unsigned char)(value >> 8));
buf_append((unsigned char)(value & 0xFF));
}
} else
if (mbCommand == MC_WRITE)
{
buf_reset();
buf_append(ADDRESS);
buf_append(MC_WRITE);
buf_append(0);
buf_append(mbRegisterL);
unsigned int value = modbus_write_holding(mbRegisterL, mbValueL);
if (mbhStatus == MODBUS_HANDLE_ERR) return;
buf_append((unsigned char)(value >> 8));
buf_append((unsigned char)(value & 0xFF));
}
crc_calculate();
buf_append(mbCRCH);
buf_append(mbCRCL);
buffer_pointer = 0;
uart_switch_transmit();
uart_send();
}
void analyze(unsigned char data)
{
switch (bScanStatus)
{
case (SS_ADDRESS):
if (data == ADDRESS) bScanStatus = SS_COMMAND;
break;
case (SS_COMMAND):
if ((data == MC_WRITE) || (data == MC_READ))
{
bScanStatus = SS_REGISTER_H_RW;
mbCommand = data;
} else reanalyze(data); break;
case (SS_REGISTER_H_RW):
if (data == 0)
bScanStatus = SS_REGISTER_L_RW;
else reanalyze(data); break;
case (SS_REGISTER_L_RW):
if (data <= MAXREGISTER)
{
mbRegisterL = data;
bScanStatus = ((mbCommand == MC_WRITE) ? SS_VALUE_H_W : SS_AMOUNT_H_R);
}
else reanalyze(data); break;
case (SS_VALUE_H_W):
if (data == 0)
bScanStatus = SS_VALUE_L_W;
else reanalyze(data); break;
case (SS_VALUE_L_W): // this step must calculate CRC
mbValueL = data;
buf_reset();
buf_append(ADDRESS);
buf_append(MC_WRITE);
buf_append(0);
buf_append(mbRegisterL);
buf_append(0);
buf_append(mbValueL);
crc_calculate();
bScanStatus = SS_CRC_H;
break;
case (SS_AMOUNT_H_R):
if (data == 0)
bScanStatus = SS_AMOUNT_L_R;
else reanalyze(data); break;
case (SS_AMOUNT_L_R): // this step must calculate CRC
if ((data <= MAXREGISTER + 1 - mbRegisterL) && (data > 0))
{
mbAmountL = data;
buf_reset();
buf_append(ADDRESS);
buf_append(MC_READ);
buf_append(0);
buf_append(mbRegisterL);
buf_append(0);
buf_append(mbAmountL);
crc_calculate();
bScanStatus = SS_CRC_H;
}
else reanalyze(data); break;
case (SS_CRC_H):
if (data == mbCRCH)
bScanStatus = SS_CRC_L;
else reanalyze(data); break;
case (SS_CRC_L):
if (data == mbCRCL)
{
uart_process_input();
bScanStatus = SS_ADDRESS;
}
else reanalyze(data); break;
}
}
SIGNAL (SIG_USART_RECV)
{
unsigned char data = UDR;
analyze(data);
}
void uart_init()
{
bScanStatus = SS_ADDRESS;
UBRRH = 0;
UBRRL = 11;
UCSRC = (1<<URSEL)|(3<<UCSZ0);
UCSRB |= (1<<TXCIE)|(1<<RXCIE)|(1<<RXEN)|(1<<TXEN);
uart_switch_receive();
}
#define MODBUS_HANDLE_OK 0
#define MODBUS_HANDLE_ERR 1
#define UART_TRANSMITTING 0
#define UART_RECEIVING 1
unsigned char mbhStatus;
unsigned char mbCRCH, mbCRCL;
unsigned char buffer_length;
unsigned char buffer[50];
unsigned char bStatus;
unsigned int iTransmissionTimer;
void uart_switch_receive();
void uart_init();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment