/* * rpialarm.c ... firmware for the open hardware RPi+AVR alarm switchboard * * Copyright (C) 2010-2016 Jiri Pittner * * 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, either version 3 of the License, or * (at your option) any later version. * * 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, see . * * Hardware: cf. rpialarm.pro kicad schematics; ATmega128, Q14.7456 This is a successor of bohdalec2.c for the newly developed PCB which combines RPi and AVR and alarm circuits */ #undef debug /* TODO: rx433 po prijeti kratsiho kodu poplete prvni dlouhy kod -proc - opravit@@@ PIN ASSIGNMENT PA0-2 = loop address 0-2 PA3 = sirena PA4 = sirenka PA5 = LED red PA6 = LED green PA7 = LED blue PF0=ADC0 ... READ loops 0-7 PF1=ADC1 ... READ loops 8-15 PF2=ADC2 ... READ loops 16-23 PF3=ADC3 ... READ loops 24-31 PF4=ADC4 ... VINP PF5-PF7 ... external ADC connector PB0 ... RFM_CS PB1 ... SCK (shared for ISP, RFM, etc.) PB2 ... MOSI PB3 ... MISO PB4 ... RX_MOD PB5 ... TX_MOD PB6 ... TX_EN PB7 ... RFM_MOD PC0-PC7 power control bus or alternatively LCD OBSOLETE!!! present implementation inherited from bohdalec2.c: PC7 = bist relay IN-PS ->canon9:1 PC6 = bist relay IN-ACCU ->canon9:2 PC5 = relay 13.8 - 1 (DSL modem)->canon9:3 PC4 = relay 13.8 - 2 (switch) ->canon9:4 PC3 = relay 5 foxboard ->canon9:5 PC2 = relay 7and30 (ISDN) ->canon9:6 PC1 = INPUT PS_ON ->canon9:7 PC0 = relay AMD power reset ->canon9:8 GND ->canon9:9 alternatively PC0=LCD_DB4 PC1=LCD_DB5 PC2=LCD_DB6 PC3=LCD_DB7 PC4=LCD_E PC5=LCD_RW PC6=LCD_RS PC7=LCD_LIGHT PD0 = INT0 = ROT_L version 1.3 (IRQ2 in version 1.4) PD1 = INT1 = ROT_R version 1.3 (RFM_IRQ in version 1.4) PD2 = RXD PD3 = TXD PD4 = rpi_power PD5 = AVR_CS PD6 = unused PD7 = AVR_CS2 PE0 = ISP_MOSI PE1 = ISP_MISO PE2 = thermometer SMT160-30 PE3 = PWM1 PE4 = PWM2 PE5 = IRQ2 version 1.3 (ROT_P in version 1.4) PE6 = ROT_P version 1.3 (ROT_L in version 1.4) PE7 = RFM_IRQ version 1.3 (ROT_R in version 1.4) PG0 = RFM_CLK PG1 = RFM_GPIO0 PG2 = RFM_SDN PG3 = RFM_TX_ANT PG4 = RFM_RX_ANT */ #include "backward.h" #include #include #include #include #include #include #include #include #include #define ATmega #ifdef ATmega #define USR UCSRA #endif #define MAXREMOTES 16 #define BAUD 115200 #define assembler #define oversampling 4 #define itoa10(N,S) itoa(N,S,10) #define itoa16(N,S) itoa(N,S,16) //alarm-byt: asi 0.6ms nahore 0.6 dole, auto 1.2ms perioda 800:400 ci 400:800 //alarm-byt: 9ms nahore 4.5ms dole pak data; auto: 12 pulsu 400:400us, 3.5ms dole, pwm data // receive oversamplingem 4: 0.1 ms sampling rate //for transmit 400,600,800 us intervals are needed void printP (PGM_P string){ char c; c=pgm_read_byte(string); while (c) { loop_until_bit_is_set(UCSR1A, UDRE); UDR1 = c; c=pgm_read_byte(++string); } return; } void print (char *string){ while (*string) { loop_until_bit_is_set(UCSR1A, UDRE); UDR1 = *string++; } return; } //UART initialize #define UART_INIT(baud) { \ UBRR1H=0; \ UBRR1L= (XTAL/baud+15)/16-1; \ UCSR1B=(1<6) state=upmark; break; case upmark: if(time1>=3*oversampling && time1<=6*oversampling && time0>=30*oversampling&& time0<=60*oversampling) {state=ready; incode[0]=bitcount=bytecount=0;break;} if(time1>=3*oversampling && time1<=6*oversampling && time0>=3*oversampling && time0<=6*oversampling) headercount++; else {state=noise; headercount=bitcount=bytecount=0;} break; case ready: //{char c[6]; print(itoa(time1,c,10)); print(":"); print(itoa(time0,c,10)); print("\n"); } if(time1==time0||time1<2*oversampling || time1 >12*oversampling || time0<2*oversampling) {state=noise; incode[0]=incodelen=bitcount=bytecount=0; break;} //unexpected abort incode[bytecount]|= (time1>time0?0:1)<60*oversampling || bytecount>=MAXCODELEN+1) {state=noise; incodelen=bitcount+(bytecount<<3)-1; } //legal end of transmition break; } } void receive_car(uint8_t level,uint16_t time) { static uint16_t time1=0; //{char c[6]; print(level?"1:":"0:"); print(itoa(time,c,10)); print("\n");} if(level) time1=time; else receive_car2(time1,time); } static uint16_t count[2]={0,0}; static uint8_t laststate=0; //this generates a run length encoded stream of data from the receiver and passes control to specialized decoders //maybe it could be replaced by an edge-interrupt with timer capture? //but it seems to work reasonably like this INTERRUPT(SIG_OUTPUT_COMPARE1B) { uint8_t state; TCNT1=0; //with B it cannot run in CTC mode state=(PINB&(1<outcodelen+1) { //data finished cbi(PORTB,PB5); cbi(TIMSK,OCIE1A); ostate=trailer; ocount=13; OCR1A=0; } else { if(n) sbi(PORTB,PB5); //avoid spurious first peak if(n>outcodelen) { OCR1A=((uint16_t)(XTAL*12./10000+.5)-1)*1/3; ++obitcount; } else { OCR1A=((uint16_t)(XTAL*12./10000+.5)-1)* ((outcode[obytecount]& (1<>= 5; } return t; return t; } uint8_t letter2bin (char c) { return c>'9' ? c+10-(c>='a'?'a':'A') : c-'0'; } uint8_t octet2bin(char* octet) { return (letter2bin(octet[0])<<4) | letter2bin(octet[1]); } void bin2letter(char *c, uint8_t b) { *c = b<10? '0'+b : 'A'+b-10; } void bin2octet(char *octet, uint8_t bin) { bin2letter(octet,bin>>4); bin2letter(octet+1,bin&0x0f); } static uint8_t blueon=0; static uint8_t greenon=0; static uint8_t redon=0; static int16_t sirenka=0; static int16_t sirena=0; static int8_t dsl_reset=0; static int8_t amd_reset=0; static int8_t isdn_reset=0; static int8_t switch_reset=0; static int8_t fox_reset=0; static int8_t reset_delay=0; static volatile uint16_t foxwatch; static volatile uint8_t centiseconds; static uint16_t foxwatchmax; INTERRUPT(SIG_OUTPUT_COMPARE0) { if(++centiseconds == 100) { centiseconds = 0; ++foxwatch; } } uint8_t incode2[MAXCODELEN+1]; uint8_t incodelen2=0; #define MAXBANK 1 int main(void) { char statusold[8*MAXBANK]="xxxxxxxxxxxxxxxx"; uint8_t voltage_old= 0; uint16_t ii=0; foxwatch=0; foxwatchmax=0; watchdog(1); UART_INIT(BAUD); delay_ms(10); //setup timer0 centiseconds=0; TCNT0=0; sbi(TIMSK,OCIE0); OCR0 = XTAL/1024/100; TCCR0=(1<500 )) //about every 256 seconds or fire { char c[6]; itoa10(t,c); printP(PSTR("t= ")); c[5]=0; print(c); printP(PSTR("\n")); } } //check whether transmitter can be set off if(!receiving && !txcount && !outcodelen) { delay_ms(5); do_transmit(0); delay_ms(5); do_receive(1); } //read UART watchdog(1); if(uartline) { //execute the command switch(inuart[0]) { case 'p': switch(inuart[1]) { case 'd': dsl_reset=5; break; case 'i': isdn_reset=5; break; case 'f': reset_delay=60; fox_reset=5; break; //leave enough time for a shutdown case 's': switch_reset=5; break; case 'a': reset_delay=100; amd_reset=5; break; //leave enough time for a shutdown case 'A': watchdog(1); while(1); break; } break; case 'W': //watchdog { char *p=strchr(inuart+2,' '); printP(PSTR("Foxwatch ")); foxwatch=0; if(!p) { printP(PSTR("(missing check) ")); } else { uint16_t foxwatchmax2 = atoi(p+1); uint16_t foxwatchmax1 = atoi(inuart+2); if(foxwatchmax1==foxwatchmax2) foxwatchmax = foxwatchmax1; else printP(PSTR("(mismatch) ")); } {char c[6]; itoa10(foxwatchmax,c); c[5]=0; print(c);} printP(PSTR("\n")); } break; case 'g': greenon=inuart[1]-'0'; break; case 'r': redon=inuart[1]-'0'; break; case 'b': blueon=inuart[1]-'0'; break; case 's': //sirenka number of seconds (<0 = infinity) sirenka=atoi(inuart+2); break; case 'S': //sirena sirena=atoi(inuart+2); break; case 't': //teplomer { int16_t t=temperature(); char c[6]; itoa10(t,c); printP(PSTR("t= ")); c[5]=0; print(c); printP(PSTR("\n")); } break; case 'V': //voltage do_voltage=1; break; case 'w': printP(PSTR("transmit CW 10 seconds\n")); txcw(); break; case 'T': //tx433 "Tn xx hex" { uint8_t i,len; printP(PSTR("\nT: ")); print(inuart); if(inuartlen<7) {printP(PSTR(" TX error1\n")); break;} len= octet2bin(inuart+3); if(inuartlen<6+2*((len+7)/8)) {printP(PSTR(" TX error2\n")); break;} while(txcount || outcodelen) {printP(PSTR("waiting for TX\n"));delay_ms(200);} for(i=0; i<(len+7)/8; ++i) outcode[i] = octet2bin(inuart+6+2*i); //start transmission do_receive(0); //would break also the transmit do_transmit(1); txcount = inuart[1]-'0'; outcodelen=len; } break; default: //unrecognized command printP(PSTR("Error: "));; print(inuart); //should be terminated by newline printP(PSTR("\n")); } //allow uart receive again cbi(UCSR1B,RXCIE1); inuartlen=0; uartline=0; sbi(UCSR1B,RXCIE1); } //fox watchdog if(foxwatch > foxwatchmax && foxwatchmax != 0) { foxwatch=0; printP(PSTR("Note: watchdog reset of RPI\n")); sbi(DDRD,PD4); sbi(PORTD,PD4); uint8_t i; for(i=0; i<20; ++i) {watchdog(1); delay_ms(500);} cbi(PORTD,PD4); printP(PSTR("Note: Rpi power back on\n")); } //set sirenes and relays if(sirenka) sbi(PORTA,PA4); else cbi(PORTA,PA4); if(sirena) sbi(PORTA,PA3); else cbi(PORTA,PA3); if(reset_delay==0 && fox_reset) sbi(PORTD,PD4); else cbi(PORTD,PD4); if((0xff & ii) == 0) //overflow of uint8_t about every 1 second { if(dsl_reset>0) --dsl_reset; if(isdn_reset>0) --isdn_reset; if(switch_reset>0) --switch_reset; if(sirenka>0) --sirenka; if(sirena>0) --sirena; if(reset_delay==0) { if(fox_reset>0) --fox_reset; if(amd_reset>0) --amd_reset; } else --reset_delay; } watchdog(1); //ADC conversion uint16_t u,v,v0; ADMUX=4|(1<>1; //check for AC power and battery voltage { uint32_t voltage10= 10412L * v0; voltage10 >>= 16; if( do_voltage || abs(voltage10 - voltage_old) > 1 && !txcount && !outcodelen //voltage unstable during transmit ) { char c[4]; voltage_old=voltage10; printP(PSTR("P: V = ")); itoa10(voltage_old,c); print(c); printP(PSTR("\n")); do_voltage=0; } } char status[8*MAXBANK]; uint8_t i; char c[8*MAXBANK],z; for(i=0; i<8; ++i) { PORTA=i | (PORTA&0xf8); //address for analog mux uint8_t bank; for(bank=0; bank>1; z='0'; if(v>(v0>>2)) ++z; if(v>(v0>>1)) ++z; if(v>(v0>>1)+(v0>>2)) ++z; status[bank+i*MAXBANK] = z; #ifdef debug char p[30]; printP(PSTR("CIRC ")); itoa10(bank+i*MAXBANK,p); print(p); printP(PSTR(" = ")); putcharx(status[bank+i*MAXBANK]); printP(PSTR(" ")); itoa10(v,p); print(p); printP(PSTR(" ")); itoa10(v0,p); print(p); printP(PSTR("\n")); #endif } } //receive watchdog(1); if(incodelen) { uint8_t i; char c[4]; if(incodelen==incodelen2) { if(!memcmp(incode2,incode,(incodelen2+7)>>3)) goto same; } memcpy(incode2,incode,(incodelen2+7)>>3); incodelen2=incodelen; incodelen=0; printP(PSTR("\nR: ")); bin2octet(c,incodelen2); c[2]=0; print(c); printP(PSTR(" ")); for(i=0; i<(incodelen2+7)>>3; ++i) { bin2octet(c,incode2[i]); c[2]=0; print(c); } printP(PSTR("\n")); same: incodelen=0; } watchdog(1); //check for change of status for(i=0; i<8*MAXBANK; ++i) { //notice that status has changed if(status[i]!=statusold[i]) { char num[4]; printP(PSTR("A: ")); itoa10(i,num); print(num); printP(PSTR(" = ")); putcharx(status[i]); printP(PSTR("\n")); statusold[i]=status[i]; } } } }