/* * alarmchata.c ... receiver of wireless meteostation data combined with a simple alarm * * Copyright (C) 2010-2013 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 . * * */ /* * alarm chata atmega32, 14.7456MHz * UPLOAD2 flash+eeprom!!! */ //??? Reports from pir sensors via interrupt when disarmed? #undef DEBUG #define printsw print #define printswP printP #define AUTO_ON_PWR #define BAUD 115200 #undef UART_ECHO //always #define ALARM_PULSE_WIDTH 200 #if (XTAL == 14745600L) #define oversampling 4 #else #define oversampling 2 #endif #include "backward.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "alarmchata.h" #include "meteo2_keys.h" #include "meteo2.h" #define useled #define THERMO #define NTHERMO 2 #define TEMPPORT PINC #define TEMPPULLUP PORTC #define TEMPDDR DDRC #define TEMPPIN0 PC0 #define TEMPPIN1 PC1 #define RELAY #define RELAY0 PC3 #define RELAY1 PC4 #define RELAY2 PC5 #define AUTO_ON #define itoa10(N,S) itoa(N,S,10) #define itoa16(N,S) itoa(N,S,16) static int16_t temper[NTHERMO]; static volatile jmp_buf env; /*atmel specific utilities*/ watchdog(uint8_t onoff) { #if(MCU==atmega168) wdt_enable(WDTO_8S); wdt_reset(); return; #else if(onoff) {wdt_enable(WDTO_2S); wdt_reset();} else {wdt_reset();wdt_disable();} #endif } #define UCR UCSRB #define UART_INIT(baud) { \ UBRRH=0; \ UBRRL= (XTAL/baud+15)/16-1; \ UCSRB=(1<5) return; if(on) sbi(PORTC,n); else cbi(PORTC,n); uint8_t all = (PORTC>>3)&7; if(all!=eeprom_read_byte(&relays_all)) eeprom_write_byte(&relays_all,all); char c[32]; sprintf(c,"Ext. relay %d = %d\n",n,on); print(c); } uint8_t relaytoggle(uint8_t n) { if(n<3||n>5) return 0; uint8_t on = (PORTC>>n)&1; on ^= 1; relay(n,on); return on; } void relays(uint8_t all) { uint8_t x = PORTC & (~(7<<3)); all &= 7; x |= (all<<3); PORTC=x; if(all!=eeprom_read_byte(&relays_all)) eeprom_write_byte(&relays_all,all); } /////////////////////////////////////////////////////////////////////////// //receive routines #define MAX(a,b) ((a)<(b)?(b):(a)) #define MESSAGE_BYTES MAX(16,sizeof(METEO2_DATAGRAM)) #define MAXCODELEN (MESSAGE_BYTES+1) volatile uint8_t incode[MESSAGE_BYTES+1]; volatile uint8_t incodelen=0; //0 indicates ready to receive, non-zero indicates ready to read it in main volatile static uint8_t bitcount=0, bytecount=0; //working counters typedef enum {noise,upmark,ready} STATE; //receive routine for the code-hopping car remote - PWM coded void receive_car2(uint16_t time1, uint16_t time0) { static STATE state=noise; static uint8_t headercount=0; //{char c[6]; print(itoa(time1,c,10)); print(":"); print(itoa(time0,c,10)); print("\n"); } switch(state) { case noise: if(abs(time1-time0)<=2*oversampling) headercount++; else headercount=0; if(headercount>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>=MESSAGE_BYTES+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 //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=(PIND&(1<len); if(bitlen==0 || i==0|| i == 0xff) return 1; if(i!=bitlen) return i-bitlen; //last received bit is just the battery-low indicator, skip it! if(i==65) i=64; //we are lazy for this simple alarm to implement here keeloq, just //skip first 4 bytes of floating code, compare only the fixed portion of the code + button codes uint8_t offset = 4; code += 4; i -= 32; i=(i+7)>>3; for(;i>0; --i) if(z=eeprom_read_byte(& eepromp->code[offset++])- *code++) return z; return 0; } uint8_t process_fixedcode(uint8_t xincodelen, uint8_t *xincode) { uint8_t i; for(i=0;i>= 4; return (int16_t) y; } #define NREP_SMT_LOG 5 int16_t temperature_smt(uint8_t mux) //in 0.01C { //check whether the sensor is present at all to prevent lockup in the next measurement { uint8_t temppin=mux?TEMPPIN1:TEMPPIN0; cbi(TEMPDDR,temppin); sbi(TEMPPULLUP,temppin); //prevent random noise on disconnected high-Z input uint8_t i,last,l; watchdog(1); last= bit_is_set(TEMPPORT,temppin); for(i=0; i<100; ++i) { watchdog(1); delay_xs(XTAL/8000); if((l=bit_is_set(TEMPPORT,temppin))^last) goto passed; last=l; } cbi(TEMPPULLUP,temppin); return M2_UNDEFINED_TEMP; passed:; cbi(TEMPPULLUP,temppin); } uint8_t i; int32_t t=0; for(i=0; i<(1<>= 1+NREP_SMT_LOG; return t; } uint8_t voltage(uint8_t mux) //integer, in 0.1 V, max resolution is approx 0.015 { uint32_t v; //enable A/D converter in single conversion mode ADMUX= mux&0x0f; ADCSRA= (1<>13; #else //v*=5.0/1023*183/33*10 v*=1110; return (v+2048)>>12; #endif } void initialize(void) { //switch on radio remote receiver atmega16 or higher required do_receive(1); watchdog(1); //switch on LED #ifdef useled sbi(DDRB,PB1); sbi(DDRB,PB2); #endif } 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); } volatile uint8_t oldincodelen=0; volatile uint8_t oldincode[MESSAGE_BYTES+1]; uint8_t check433(void) { if(!incodelen) return 0; if(incodelen!=oldincodelen || memcmp(oldincode,incode,oldincodelen>>3)) { oldincodelen=incodelen; memcpy(oldincode, incode,(oldincodelen+7)>>3); incodelen=0; //send the packet to linux computer { printP(PSTR("R: ")); char c[4]; bin2octet(c,oldincodelen); c[2]=0; print(c);print(" "); uint8_t i; for(i=0; i<(oldincodelen+7)>>3; ++i) { bin2octet(c,oldincode[i]); c[2]=0; print(c); } print("\n"); } watchdog(1); //try rolling code remotes uint8_t recognized = 0; if(oldincodelen>=0x40 && oldincodelen<=0x42) recognized=process_keeloq1(oldincode); //try recognizing some fixed codes if(!recognized && oldincodelen>=0x40 && oldincodelen<=0x42) recognized=process_fixedcode(oldincodelen,oldincode); //other lengths are processed by the linux master rather than here return 1; } else { incodelen=0; return 0; } } /* main logic of the alarm */ int main(void) { watchdog(1); uint16_t icount=0; uint8_t power_ok=1; uint8_t oldpower_ok=1; //watchdog of rpi cbi(PORTC,PC2); sbi(DDRC,PC2); rpiwatch=0; rpiwatchmax=1800; sbi(SREG,7); //setup UART UART_INIT(BAUD); //allow uart interrupts {char dummy=UDR;} sbi(UCSRB,RXCIE); //setup timer0 centiseconds=0; TCNT0=0; sbi(TIMSK,OCIE0); OCR0 = XTAL/1024/100; TCCR0=(1<0 && inuart[inuartlen-1]== '\n'||inuart[inuartlen-1]=='\r') { switch (inuart[0]) { case 'l': //enquire about status { char c[32]; sprintf(c,"A %d %d %d\n",eeprom_read_byte(&alarm_armed), eeprom_read_byte(&alarm_pending),eeprom_read_byte(&alarm_active)); print(c); } break; case 'p': //power cycle of rpi { if(strncmp(inuart,"powerdown",9)) break; printP(PSTR("Will cycle RPI power\n")); uint8_t i; for(i=0; i<30; ++i) {watchdog(1); delay_ms(1000);} sbi(DDRC,PC2); sbi(PORTC,PC2); for(i=0; i<10; ++i) {watchdog(1); delay_ms(1000);} cbi(PORTC,PC2); rpiwatch=0; rpiwatchmax=900; } break; case 'O': //drive relays individually (counted from 0) { uint8_t n, on; n=inuart[1]-'0'; on= inuart[2]=='1'||inuart[2]=='+'; relay(n+3,on); } break; case 'A': //simulate alarm, arm-disarm, cancel active or pending alarm { switch(inuart[1]) { case 'A': //simulate alarm eeprom_write_byte(&alarm_pending,4); break; case 'R': //reset and arm //no break; case '1': //arm armdisarm(1); break; case '0': //disarm armdisarm(0); break; } } break; case 'W': //watchdog { char *p=strchr(inuart+2,' '); printP(PSTR("rpiwatch ")); rpiwatch=0; if(!p) { printP(PSTR("(missing check) ")); } else { uint16_t rpiwatchmax2 = atoi(p+1); uint16_t rpiwatchmax1 = atoi(inuart+2); if(rpiwatchmax1==rpiwatchmax2) rpiwatchmax = rpiwatchmax1; else printP(PSTR("(mismatch) ")); } {char c[6]; itoa10(rpiwatchmax,c); c[5]=0; print(c);} printP(PSTR("\n")); } break; default: printP(PSTR("Unrecognized command: ")); print(inuart); print("\n"); break; }//switch //allow uart receive again cbi(UCSRB,RXCIE); inuartlen=0; sbi(UCSRB,RXCIE); } if((icount&63)==0) { uint8_t i; for(i=0; i=140; else power_ok= v>=150; if((icount&1023)==0 || power_ok!=oldpower_ok) { char c[16]; sprintf(c,"v= %d %d\n",v,v1); print(c); oldpower_ok=power_ok; } } //delay before LED switch watchdog(1); delay_ms(500); //switch off LED in any case cbi(PORTB,PB1); cbi(PORTB,PB2); uint8_t received = check433(); //check if the remote has sent a message with a recognized code and process it if(received) { //blue led sbi(DDRA,PA7); sbi(PORTA,PA7); watchdog(1);delay_ms(300); cbi(PORTA,PA7); cbi(DDRA,PA7); watchdog(1);delay_ms(300); } //watchdog the linux computer by cycling power if(rpiwatch > rpiwatchmax && rpiwatchmax != 0) { rpiwatch=0; sbi(DDRC,PC2); sbi(PORTC,PC2); uint8_t i; for(i=0; i<6; ++i) {watchdog(1); delay_ms(1000);} cbi(PORTC,PC2); } watchdog(1); delay_ms(500); watchdog(1); delay_ms(500); icount++; }//all done return 0; }