/* * meteoserver.c ... weater station receiver and simple alarm controller * * Copyright (C) 2006-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 . * * */ //TODO: //@@@ echo ze sms k otestovani spoluprace se smsserverem a i do alarmserveru //@@@ echo ze sms k otestovani spoluprace se smsserverem a i do alarmserveru //@@@ implement processing of SMS, floating code authentication, sms command to force reboot if the server is unreachable due to pppd/modem failure //possibly implement encapsulation of the forwarded packets with hopping counter a AES to avoid the extra layer of openvpn #undef debug //this runs on Raspberry Pi #define RPI #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "meteo2.h" #include "meteo2_keys.h" #define MAILCOMMAND "/usr/bin/mailx" void atmel_reset(void) { system("/usr/local/bin/uisp --rd_fuses -dprog=daparpi > /dev/null"); } typedef char NAME16[16]; const NAME16 alarm_assignment[N_CIRCUITS] = ALARM_ASSIGNMENT; static uint8_t circuits_alarm[N_CIRCUITS]; static uint8_t circuits_status[N_CIRCUITS] = CIRCUITS_STATUS_INITIAL; static time_t last_command_time=0; //forwarding of wireless packets via internet static const int udp_forward=UDP_FORWARD; static int forward_udp_descr=0; //misc static int isday; void xerror(error_text) char error_text[]; { fprintf(stderr,"meteoserver: %s\n",error_text); exit(10); } static struct termios devicetermios0, ttytermios0; static int filehandle; void termioscleanup() { tcsetattr(fileno(stdin),TCSANOW,&ttytermios0); tcsetattr(filehandle,TCSANOW,&devicetermios0); } void handler() { termioscleanup(); exit(0); } void usage() { exit(1); } #define MAXPACKET 255 #define MAXLINE (2*MAXPACKET+2) 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); } #define CIRCUITS_FILE "/usr/local/alarm/circuits" static int savecircuits; void save_circuits() { int f=open(CIRCUITS_FILE,O_WRONLY|O_CREAT,0700); if(f>=0) { int r; r=write(f,circuits_alarm,sizeof(circuits_alarm)); r=write(f,circuits_status,sizeof(circuits_status)); close(f); } } //assuming that initialization took place (if read is not succesfull) void read_circuits() { int f=open(CIRCUITS_FILE,O_RDONLY); if(f>=0) { int r; r=read(f,circuits_alarm,sizeof(circuits_alarm)); r=read(f,circuits_status,sizeof(circuits_status)); } int i; } typedef char EMAIL[64]; static EMAIL emails[N_EMAILS] = EMAILS; typedef char SMS[16]; static SMS smss[N_SMS] = SMSS; static int smsfifodescr; void reset_notify(FILE *logfile,char *timestamp, int email, int sms) { if(email>=0) { char command[256]; sprintf(command,MAILCOMMAND " -s RESET_NOTIFY %s",emails[email]); FILE *p; p=popen(command,"w"); if(!p) fprintf(logfile,"Error sending e-mail %s\n",emails[email]); else { fprintf(p,"%s Alarm RESET! %s\n",LOCATION,timestamp); pclose(p); } } } void sms_notify(FILE *logfile,char *timestamp, int email,char *text) { if(email>=0) { char command[256]; sprintf(command,MAILCOMMAND " -s INCOMING_SMS %s",emails[email]); FILE *p; p=popen(command,"w"); if(!p) fprintf(logfile,"Error sending e-mail %s\n",emails[email]); else { fprintf(p,"%s: %s: INCOMING SMS: %s\n",LOCATION,timestamp,text); pclose(p); } } } void report_alarm(FILE *logfile,int okruh, int stav, char *timestamp, char *otherreason, int email_only) { int i; for(i=0; i=0) { r=read(f,&n,sizeof(int)); if(r==sizeof(int)) { if(YEAR=0) { int r; int n=YEAR; r=write(f,&n,sizeof(int)); r=write(f,rain_cumulative[j],n*sizeof(double)); close(f); } } } #define POLY 0x8408 /* // 16 12 5 // this is the CCITT CRC 16 polynomial X + X + X + 1. // This works out to be 0x1021, but the way the algorithm works // lets us use 0x8408 (the reverse of the bit pattern). The high // bit is always assumed to be set, thus we only use 16 bits to // represent the 17 bit value. */ uint16_t crc16(uint8_t *data_p, uint16_t length) { unsigned char i; unsigned int data; unsigned int crc = 0xffff; if (length == 0) return (~crc); do { for (i=0, data=(uint16_t)0xff & *data_p++; i < 8; i++, data >>= 1) { if ((crc & 0x0001) ^ (data & 0x0001)) crc = (crc >> 1) ^ POLY; else crc >>= 1; } } while (--length); crc = ~crc; data = crc; crc = (crc << 8) | (data >> 8 & 0xff); return (crc); } #undef POLY //second temperature sensor and indoor temperature double temperature2 = M2_UNDEFINED_TEMP*0.01; double temperature_in = M2_UNDEFINED_TEMP*0.01; METEO2_DATAGRAM m2data_old = {0,0,0,0,0,0,0}; int islittleendian; uint16_t process_meteo2(char *source, FILE *logfile, char *timestamp, const uint8_t *code, char *line) { #ifdef testing_meteo2_udp if(strcmp(source,"wireless")==0) return 0; //ignore METEO2 packets received directly to check the UDP transfer #endif METEO2_DATAGRAM m2data; memcpy(&m2data,code,sizeof(METEO2_DATAGRAM)); if(!islittleendian) { fprintf(logfile,"meteo2: byte swap needs to be implemented\n"); exit(1); } if(memcmp(&m2data,&m2data_old,sizeof(METEO2_DATAGRAM))) //ignore repeated messages { memcpy(&m2data_old,&m2data,sizeof(METEO2_DATAGRAM)); fprintf(logfile," meteo2 type %d serial %d report %d counter %d: ",m2data.device_type, m2data.serial, m2data.report_type, #ifdef M2_COUNTER (unsigned int) m2data.counter #else 0 #endif ); //check CRC uint16_t crc=crc16((void *)&m2data,sizeof(METEO2_DATAGRAM)-2); if(crc!=m2data.crc) {fprintf(logfile,"CRC MISMATCH\n"); return 1;} //process the message if(m2data.serial>=N_METEO) {fprintf(logfile,"too high serial number %d\n",m2data.serial); return 2;} int serial = m2data.serial; if(m2data.device_type!= M2_METEOSTATION) {fprintf(logfile,"unrecognized device type %d\n",m2data.device_type); return 3;} switch(m2data.report_type) { case M2_RESET: fprintf(logfile,"reset! (args %d %d)\n",m2data.arg1,m2data.arg2); break; case M2_RAIN: { time_t tr = time(NULL); double mm = 0.55 * m2data.arg1; double hours = (tr-last_rain_time[serial])/3600.; double mmh = mm/hours; if(mmh>50) { fprintf(logfile,"Rain unphysical %.1f raw readout %d collecting interval %.0f s\n",mmh,m2data.arg1,3600.*hours);fflush(logfile); break; } rain_cumulative[serial][0] += mm; if(mm>0) saverain=1; if(mm>0 && mmh < 0.1) mmh=0.1; double mmheff; double Tr=m2data.arg2*0.01; if(mmh>0 || hours>5.) mmheff=mmh; //either something rained, or long since last bucket else mmheff = last_mm[serial]*3600./((double) tr - last2_rain_time[serial]); //average overlast bucket over longer time fprintf(logfile,"Rain = %.1fmm/h raw %.1fmm/h Tr=%.2f (raw readout %d temp %d) collecting interval %.0f s\n",mmheff,mmh,Tr,m2data.arg1,m2data.arg2,3600.*hours); { char name[128]; FILE *f; sprintf(name,"/usr/local/alarm/meteo%d/rain",serial); f=fopen(name,"w"); if(f) { fprintf(f,"%.1f",mmheff); fclose(f); } if(m2data.arg2 != M2_UNDEFINED_TEMP) { sprintf(name,"/usr/local/alarm/meteo%d/temprain",serial); f=fopen(name,"w"); if(f) { fprintf(f,"%.2f",Tr); fclose(f); } } if(mmh>0) { sprintf(name,"/usr/local/alarm/meteo%d/rainlast",serial); f=fopen(name,"w"); if(f) { fprintf(f,"%.1f",mmh); fclose(f); } } sprintf(name,"/usr/local/alarm/meteo%d/raindaily",serial); f=fopen(name,"w"); if(f) { fprintf(f,"%.1f",rain_cumulative[serial][0]); fclose(f); } sprintf(name,"/usr/local/alarm/meteo%d/rainweekly",serial); f=fopen(name,"w"); if(f) { fprintf(f,"%.1f",rain_cumulative[serial][0]+rain_cumulative[serial][1]+rain_cumulative[serial][2]+rain_cumulative[serial][3]+rain_cumulative[serial][4]+rain_cumulative[serial][5]+rain_cumulative[serial][6]); fclose(f); } } if(mm>0) {last2_rain_time[serial] = last_rain_time[serial]; last_rain_time[serial] = tr; last_mm[serial]=mm;} } break; case M2_VOLTAGE: { float v = 0.01*m2data.arg2; fprintf(logfile,"Voltage = %.2fV\n",v); { char name[128]; sprintf(name,"/usr/local/alarm/meteo%d/voltage",serial); FILE *f=fopen(name,"w"); if(f) { fprintf(f,"%.2f",v); fclose(f); } } if(v<11.) power_notify(logfile,timestamp,m2data.arg2/10,"Chata_meteo"); } break; case M2_LIGHT: { float ohm=0.; if(sizeof(uint32_t) == sizeof(float)) memcpy(&ohm,&m2data.arg3,sizeof(float)); double lx = exp(-1.56544*log(ohm)+17.3907693); //calibrated from a fit for a given photoresistor, however it can differ even piece to piece fprintf(logfile,"Illuminance = %.1flx (resistance %.2f)\n",lx,ohm); { char name[128]; sprintf(name,"/usr/local/alarm/meteo%d/light",serial); FILE *f=fopen(name,"w"); if(f) { fprintf(f,"%.1f",lx); fclose(f); } } } break; case M2_PING: fprintf(logfile,"PING %d\n",m2data.arg2); break; case M2_WATER_ALARM: { int onoff = m2data.arg1; float volts = 0.001 * (unsigned int) m2data.arg2; fprintf(logfile,"Water flooding sensor %s (%.3fV)\n",onoff?"ALARM":"OK",volts); report_alarm(logfile,0, onoff, timestamp,(onoff?"BASEMENT FLOOD!":"BASEMENT OK"),1); } break; case M2_WATER_LEVEL: { int wserial = m2data.arg1; float level = 0.001 * (unsigned int) m2data.arg3; //meters float volume = 0.001 * (unsigned int) m2data.arg2; //cubic meters fprintf(logfile,"Water level %d = %.3fm, volume = %.3fm^3\n",wserial,level,volume); { char name[128]; sprintf(name,"/usr/local/alarm/meteo%d/water%d",serial,wserial); FILE *f=fopen(name,"w"); if(f) { fprintf(f,"%.3f %.3f",level,volume); fclose(f); } } } break; case M2_PRESSURE: { float p= m2data.arg3*0.01; float t= m2data.arg2*0.01; const float mgR=0.03416319473631; const float lapse_rate = -0.0065 ; float p2=p * expf(mgR*altitude[serial]/(273.15+t)); float p3=p * powf((273.15+t)/(273.15+t-lapse_rate*altitude[serial]),mgR/lapse_rate); fprintf(logfile,"Pressure = %.2fhPa (at zero altitude %.2fhPa or with lapse rate %.2fhPa)\n",p,p2,p3); { char name[128]; sprintf(name,"/usr/local/alarm/meteo%d/pressure",serial); FILE *f=fopen(name,"w"); if(f) { fprintf(f,"%.2f",p); fclose(f); } sprintf(name,"/usr/local/alarm/meteo%d/pressurecorr",serial); f=fopen(name,"w"); if(f) { fprintf(f,"%.2f",p2); fclose(f); } } } break; case M2_WIND: case M2_WIND_GUSTS: { double dir,speed; if(m2data.arg1) fprintf(logfile,"Anemo error %d new anemo found %d\n",m2data.arg1,m2data.arg2); dir=360./16/256*m2data.arg2; dir+=anemo_offset[serial]; if(dir>360.) dir -= 360.; //!!!correction to offset of the mounting speed=0.1/256*m2data.arg3; fprintf(logfile,"Wind %s direction %.0f degs (%d) speed %.1f m/s (%d)\n",((m2data.report_type)==M2_WIND_GUSTS)?"gusts":"",dir,m2data.arg1,speed,m2data.arg2); if(speed==0.) dir = last_wind_direction[serial]; if(speed<50.) //filter out unphysical results { if(speed>0.) last_wind_direction[serial]=dir; char name[128]; sprintf(name,"/usr/local/alarm/meteo%d/wind%s",serial,m2data.report_type==M2_WIND_GUSTS?"gusts":""); FILE *f=fopen(name,"w"); if(f) { fprintf(f,"%.1f %.2f",dir,speed); fclose(f); } } } break; case M2_WELL_MISC: { uint16_t vphoto = m2data.arg3&0xffff; uint16_t v24 = ((uint32_t)m2data.arg3) >>16; fprintf(logfile,"Well status: magnet status %d magnet voltage %dmV photo voltage %dmV 24V voltage %dmV; ", m2data.arg1, m2data.arg2, vphoto, v24); double ohm = (v24*(270.4+67.73)/vphoto-(10+270.4+67.73)) * 1000.; double lx = exp(-1.56544*log(ohm)+17.3907693); //calibrated from a fit for a given photoresistor, however it can differ even piece to piece fprintf(logfile,"Resistance %g ohms, illuminance %glx\n",ohm,lx); //@@@@@@@ if circuit is armed and light over threshold, make alarm } break; case M2_TEMP_HUMID: { if(m2data.arg3 == M2_UNDEFINED_TEMP || m2data.arg2 == M2_UNDEFINED_HUMID) { fprintf(logfile,"undefined results reported\n"); return 5;} double t= m2data.arg3 * 0.01; double h= m2data.arg2 * 0.1; double dew; double tmp; double m,tn; //dew point calculation by a formula from SHT75 datasheet (it is the Magnus formula, see also http://en.wikipedia.org/wiki/Dew_point for other parameters) if(t>=0) {m=17.62; tn=243.12;} else {m=22.46; tn=272.62;} if(h==0.) dew= -tn; else {tmp=log(h*0.01)+m*t/(tn+t); dew= tn*tmp/(m-dew);} fprintf(logfile,"Native Temperature = %.2fC Humidity = %.1f%% Dew point = %.2fC ",t,h,dew); //dirty hack to circumvent temporarily bad placement of the meteostation which results in a biased temperature during evening - replace the temperature by reading from other sensor and recalculate humidity and modify the original packet to be forwarded if(serial==1 && temperature2!= 0.01*M2_UNDEFINED_TEMP && temperature2 < t) { double hnew = 100.*exp(tmp-m*temperature2/(tn+temperature2)); if(hnew >= 0. && hnew <= 100.) { h=hnew; t=temperature2; fprintf(logfile,"Corrected Temperature = %.2fC Humidity = %.1f%%; ",t,h); //rewrite the original packet m2data.arg2 = 10*h; m2data.arg3 = 100*t; m2data.crc =crc16((void *)&m2data,sizeof(METEO2_DATAGRAM)-2); memcpy(code,&m2data,sizeof(METEO2_DATAGRAM)); int i; for(i=0; i>3; ++i) { if(!line[2*i+6] || !line[2*i+7]) {fprintf(logfile,"truncated line from receiver\n"); return 200;} packet[i] = octet2bin(line + 2*i + 6); } //this is not encrypted - we expect nobody is interested in a sabotage of the meteostation if(len== 8*sizeof(METEO2_DATAGRAM)) return process_meteo2(source,logfile,timestamp,packet,line); //other lengths ... fprintf(logfile," ... unsupported packet\n"); fflush(logfile); return 0; } void power_notify(FILE *logfile,char *timestamp,int onoff, char *location) { { char command[256]; sprintf(command,MAILCOMMAND " -s POWER_NOTIFY %s",emails[0]); FILE *p; p=popen(command,"w"); if(!p) fprintf(logfile,"Error sending e-mail %s\n",emails[0]); else { fprintf(p,"%s AC POWER NOTIFY %s changed to %d\n",location,timestamp,onoff); pclose(p); } } { char command[256]; sprintf(command,"S %s %s AC POWER NOTIFY %s changed to %d\n",smss[0],location,timestamp,onoff); write(smsfifodescr,command,strlen(command)); } } void notify(FILE *logfile,int okruh, int stav, char *timestamp, int email, int sms) { if(email>=0) { char command[256]; sprintf(command,MAILCOMMAND " -s NOTIFY %s",emails[email]); FILE *p; p=popen(command,"w"); if(!p) fprintf(logfile,"Error sending e-mail %s\n",emails[email]); else { fprintf(p,"%s NOTIFY %s : %s %s\n",LOCATION,timestamp,alarm_assignment[okruh],stav?"ARM":"DISARM"); pclose(p); } } if(sms>=0) { char command[256]; sprintf(command,"S %s %s NOTIFY %s : %s %s\n",smss[sms],LOCATION,timestamp,alarm_assignment[okruh],stav?"ARM":"DISARM"); write(smsfifodescr,command,strlen(command)); } } //check whether a given line from an untrusted source has a legal format int checkRline(char *line) { int i; if(strncmp(line,"R: ",3)) return 0; if(!isxdigit(line[3]) || !isxdigit(line[4]) || line[5]!=' ' ) return 0; int len; if(1!=sscanf(line+3,"%x",&len)) return 0; //adjust len from bits to nibbles len += 3; len /= 4; for(i=6; i<6+len; ++i) if(!isxdigit(line[i])) return 0; line[6+32]=0; return 1; } FILE *reopenlogfile(const char *name0, FILE *oldlog) { char name[256]; time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); sprintf(name,"%s_%d.%02d.%02d",name0,tmp->tm_year+1900,tmp->tm_mon+1,tmp->tm_mday); FILE *f=fopen(name,"a"); if(f) { struct passwd *pwd = getpwnam("alarm"); struct group *grp = getgrnam("alarm"); chown(name,pwd->pw_uid,grp->gr_gid); chmod(name,0640); if(oldlog) fclose(oldlog); unlink("/usr/local/alarm/alarm.log"); symlink(name,"/usr/local/alarm/alarm.log"); return f; } else { if(oldlog) return oldlog; xerror("cannot open logfile"); } } void forward_packet(char *line, FILE *log, char *timestamp) { struct sockaddr_in udp_target; bzero(&udp_target,sizeof(udp_target)); udp_target.sin_family=AF_INET; udp_target.sin_addr.s_addr=FORWARD_IP; udp_target.sin_port=htons(FORWARD_PORT); int len0=strlen(line); int r; line[len0]='\n'; line[len0+1]=0; if(forward_udp_descr>0) r=sendto(forward_udp_descr,line,len0+2,MSG_DONTWAIT,&udp_target,sizeof(udp_target)); line[len0]=0; if(r!=len0+2) fprintf(log,"%s forward_packet failed with error: %s\n",timestamp,strerror(errno)); fflush(log); } main(int argc, char **argv) { { union {int i; char z[sizeof(int)];} u; u.i=1; islittleendian= u.z[0]; } char logfilename[128]="/usr/local/alarm/log/alarm.log"; char fifofile[128]="/usr/local/alarm/fifo"; char smsfifofile[128]="/usr/local/alarm/smsfifo_in"; char smsfifofileout[128]="/usr/local/alarm/smsfifo_out"; #ifdef RPI char device[64]="/dev/ttyAMA0"; #endif char baud[16]="115200"; speed_t baudrate=B115200; //check if we are already running { FILE *pf=fopen("/usr/local/alarm/meteoserver.pid","r"); if(pf) { pid_t oldpid; if(1==fscanf(pf,"%d",&oldpid)) { if(kill(oldpid,0)==0) { fprintf(stderr,"It seems that meteoserver is already running.\n Check ps and possibly remove meteoserver.pid\n"); exit(10); } } fclose(pf); } } read_circuits(); read_rain(); while((--argc>0)&&((*++argv)[0]=='-')){ if (argc==0) usage(); switch ((*argv)[1]){ case 'D': ++argv; --argc; strcpy(device,*argv); break; case 'L': ++argv; --argc; strcpy(logfilename,*argv); break; case 'F': ++argv; --argc; strcpy(fifofile,*argv); break; case 'B': strcpy(baud,(*argv)+2); if(!strcmp(baud,"2400")) baudrate=B2400; if(!strcmp(baud,"4800")) baudrate=B4800; if(!strcmp(baud,"9600")) baudrate=B9600; if(!strcmp(baud,"19200")) baudrate=B19200; if(!strcmp(baud,"38400")) baudrate=B38400; if(!strcmp(baud,"57600")) baudrate=B57600; if(!strcmp(baud,"115200")) baudrate=B115200; if(!strcmp(baud,"230400")) baudrate=B230400; if(baudrate==B0) xerror("unsupported baudrate"); break; default : xerror("unknown option,; use option -H for information"); } } if(argc !=0) xerror("wrong command line; use option -H for information"); //open logfile FILE *l; l=reopenlogfile(logfilename,NULL); { time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); char timestamp[64]; strftime(timestamp,64,"%c %Z",tmp); fprintf(l,"\n%s Start meteoserver\n",timestamp); fflush(l); } //atmel_reset(); // do not do it always, it is reliable and has a watchdog of RPI set which would be removed by this reset //change uid to alarm struct passwd *pwd = getpwnam("alarm"); if(chown(device,pwd->pw_uid,pwd->pw_gid)) {perror("cannot chown ttyS"); exit(10);} if(setgid(pwd->pw_gid)) {perror("cannot setgid to alarm"); exit(10);} if(setuid(pwd->pw_uid)) {perror("cannot setuid to alarm"); exit(10);} chdir("/usr/local/alarm"); daemon(1,0); //for debugging { chdir("/usr/local/alarm"); prctl(PR_SET_DUMPABLE,1); struct rlimit rlim; rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_CORE, &rlim); } int f_fifo=open(fifofile, O_RDWR|O_SYNC|O_NOCTTY,0); if(f_fifo<0) {fprintf(l,"cannot open connection to fifo\n"); exit(10); } int f_sms=open(smsfifofile, O_RDWR|O_SYNC|O_NOCTTY); //actually it is RDONLY but mysteriously it makes troubles if(f_sms<0) {fprintf(l,"cannot open connection to smsserver\n"); exit(10); } int f_sms_out=open(smsfifofileout, O_RDWR|O_SYNC|O_NOCTTY,0); //actually is wronly if(f_sms_out<0) {fprintf(l,"cannot open connection 2 to smsserver\n"); exit(10); } smsfifodescr = f_sms_out; //write(f_sms_out,"R\n",2); //reset phone at smsserver //initialize outgoing UDP if(udp_forward) { forward_udp_descr = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(forward_udp_descr<0) {fprintf(l,"cannot open UDP socket\n"); exit(10); } //fcntl(forward_udp_descr,F_SETFL,O_NONBLOCK); } int f=open(device,O_RDWR|O_NONBLOCK|O_NOCTTY|O_EXCL,0); if(f<0) {fprintf(l,"cannot open connection to atmel: %s\n",strerror(errno)); exit(10); } struct termios devicetermios, ttytermios; filehandle=f; if(tcgetattr(f,&devicetermios)) perror("cannot tcgetattr 1"); memcpy(&devicetermios0,&devicetermios,sizeof(struct termios)); cfsetispeed(&devicetermios,baudrate); cfsetospeed(&devicetermios,baudrate); devicetermios.c_iflag |= IGNBRK; devicetermios.c_iflag &= ~BRKINT; devicetermios.c_iflag &= ~ICRNL; devicetermios.c_iflag &= ~IMAXBEL; devicetermios.c_iflag &= ~IXON; devicetermios.c_iflag &= ~IXOFF; devicetermios.c_oflag &= ~OPOST; devicetermios.c_oflag &= ~ONLCR; devicetermios.c_cflag |= CLOCAL; devicetermios.c_cflag &= ~CRTSCTS; devicetermios.c_lflag &= ~ISIG; devicetermios.c_lflag &= ~ICANON; devicetermios.c_lflag &= ~IEXTEN; devicetermios.c_lflag &= ~ECHO; devicetermios.c_lflag &= ~ECHOE; devicetermios.c_lflag &= ~ECHOK; devicetermios.c_lflag &= ~ECHOCTL; devicetermios.c_lflag &= ~ECHOKE; if(tcsetattr(f,TCSANOW,&devicetermios)) perror("cannot tcsetattr 1"); char oldline[MAXLINE]=""; char old2line[MAXLINE]=""; //e-mail and sms notify user 0 about reset { time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); char timestamp[64]; strftime(timestamp,64,"%c %Z",tmp); reset_notify(l,timestamp,0,0); } //create a pidfile { FILE *pf=fopen("/usr/local/alarm/meteoserver.pid","w"); fprintf(pf,"%d\n",getpid()); fclose(pf); } int report_sent=0; int logfile_reopened=1; last_rain_time[0] = last2_rain_time[0] = time(NULL); //read last outer temperature, new one possibly is not measured yet when needed { FILE *ff=fopen("/usr/local/alarm/temperature_OUT","r"); if(ff) { int r = fscanf(ff,"%lf",&temperature2); if(r!=1) temperature2 = M2_UNDEFINED_TEMP*0.01; fclose(ff); } ff=fopen("/usr/local/alarm/temperature_IN","r"); if(ff) { int r = fscanf(ff,"%lf",&temperature_in); if(r!=1) temperature_in = M2_UNDEFINED_TEMP*0.01; fclose(ff); } } //MAIN LOOP int rr=0; int rr_sms=0; int rr_fifo=0; while(1) { savecircuits=0; char line[MAXLINE]; char linework[MAXLINE]; char line_fifo[MAXLINE]; char line_sms[MAXLINE]; //select and read line char *p=line; char *p_fifo=line_fifo; char *p_sms=line_sms; char *x=NULL; char *x_fifo=NULL; char *x_sms=NULL; int alarm_input=0; int fifo_input=0; int sms_input=0; time_t t; struct tm *tmp; char timestamp[64]; //accumulate lines from each input source do{ fd_set readfds; fd_set writefds; fd_set exceptfds; int r,s; FD_ZERO(&writefds); FD_ZERO(&exceptfds); FD_ZERO(&readfds); FD_SET(f,&readfds); FD_SET(f_fifo,&readfds); FD_SET(f_sms,&readfds); int maxf=f; if(f_fifo>f) maxf=f_fifo; if(f_sms>f) maxf=f_sms; if(f_sms_out>f) maxf=f_sms_out; struct timeval timeout; timeout.tv_sec=10; //time resolution for ping and delayed alarm timeout.tv_usec=0; if((s=select(maxf+1,&readfds,&writefds,&exceptfds,&timeout)) == -1) {fprintf(l,"select error\n"); perror("select"); exit(10);} if(FD_ISSET(f,&readfds)) //accomodate for multiple lines in buffer - read only one at a time (atmel generates output fast) { x=NULL; while((p-line) < MAXLINE-1) { r=read(f,p,1); if(r!=1) break; rr+=1; p+=1; if(p[-1] == '\n' || p[-1] == '\r') { x=p-1; break; } } alarm_input= x || rr>=MAXLINE-1; } if(FD_ISSET(f_fifo,&readfds)) //assume single line available at a time { rr_fifo+=r=read(f_fifo,p_fifo,MAXLINE-1-(p_fifo-line_fifo)); p_fifo+=r; x_fifo=strchr(line_fifo,'\n'); if(!x_fifo) x_fifo=strchr(line_fifo,'\r'); fifo_input= x_fifo || rr_fifo>=MAXLINE-1; } if(FD_ISSET(f_sms,&readfds)) //assume single line available at a time { rr_sms+=r=read(f_sms,p_sms,MAXLINE-1-(p_sms-line_sms)); p_sms+=r; x_sms=strchr(line_sms,'\n'); if(!x_sms) x_sms=strchr(line_sms,'\r'); sms_input= x_sms || rr_sms>=MAXLINE-1; } t = time(NULL); tmp = localtime(&t); isday = ( tmp->tm_hour > DAY_HOUR && tmp->tm_hour < NIGHT_HOUR ); strftime(timestamp,64,"%c %Z",tmp); } while(!fifo_input && !alarm_input && !sms_input); int okruh,stav,issensor; //log to a file with a timestamp and make further processing if appropriate if(alarm_input) { rr=0; if(x) x[0]=0; strncpy(linework,line,MAXLINE);linework[MAXLINE-1]=0; switch(linework[0]) { case 'R': //screen from repeated messages if(strcmp(linework,oldline) && strcmp(linework,old2line)) { fprintf(l,"%s %s ",timestamp,linework); fflush(l); strcpy(old2line,oldline); strcpy(oldline,linework); int okruh,stav,dummy; process_packet("wireless",l,timestamp,linework,&okruh,&stav,&dummy); int i; for(i=0; i=N_CIRCUITS) break; savecircuits=1; circuits_alarm[okruh]=stav; if(stav!=0) { report_alarm(l,okruh,2,timestamp,NULL,0); } break; case 'C': //change of circuit status notification triggered by the low level processor { if(2!=sscanf(linework+2,"%d = %d",&okruh,&stav)) {fprintf(l,"ERROR parsing %s\n",linework); fflush(l); break;} fprintf(l,"%s %s %s\n",timestamp,alarm_assignment[okruh],stav?"ARM":"DISARM"); fflush(l); if(okruh<0||okruh>=N_CIRCUITS) break; if(stav) circuits_status[okruh]=stav; else { circuits_alarm[okruh]=0; circuits_status[okruh]=0; } savecircuits=1; notify(l,okruh,stav,timestamp,0,-1); } break; case 'K': //note about registered Keeloq remote button press { int ret,serial,buttons; if(3!=sscanf(linework+2,"%d %d %d",&ret,&serial,&buttons)) {fprintf(l,"ERROR parsing %s\n",linework); fflush(l); break;} fprintf(l,"%s Remote %d button %d pressed\n",timestamp,serial,buttons); } break; case 'v': //voltage { int v,v1; int r; r=sscanf(linework+3,"%d %d",&v,&v1); if(r>0) { fprintf(l,"%s Alarm Input Voltage = %.1fV, battery voltage = %.1fV\n",timestamp,v*.1,v1*.1); fflush(l); if(v<135) power_notify(l,timestamp,v,"Chata_alarm"); } } break; case 't': //local temperature { int t,t2,r; r=sscanf(linework+3,"%d %d",&t,&t2); if(r==2) { if(t != M2_UNDEFINED_TEMP) temperature_in = 0.01*t; if(t2 != M2_UNDEFINED_TEMP) temperature2 = 0.01*t2; fprintf(l,"%s Local Temperature inside %.2f outside %.2f\n",timestamp,t*0.01,t2*0.01); fflush(l); if(temperature_in != M2_UNDEFINED_TEMP*0.01) { FILE *f=fopen("/usr/local/alarm/temperature_IN","w"); if(f) { fprintf(f,"%.2f",temperature_in); fclose(f); } } if(temperature2 != M2_UNDEFINED_TEMP*0.01) { FILE *f=fopen("/usr/local/alarm/temperature_OUT","w"); if(f) { fprintf(f,"%.2f",temperature2); fclose(f); } } } } break; default: fprintf(l,"%s Unprocessed comment: %s\n",timestamp,linework); fflush(l); break; } fflush(l); alarm_input=0; } if(sms_input) //@@@implement some additional primitive authentization, like a password { rr_sms=0; if(x_sms) x_sms[0]=0; int offset; char source[MAXLINE]; sms_notify(l,timestamp,0,line_sms); if(1!=sscanf(line_sms,"%256s %n",source,&offset)) { fprintf(l,"%s MALFORMED INCOMING SMS: %s\n",timestamp,line_sms); } else { //@@@ check password fprintf(l,"%s SMS COMMAND from %s : %s\n",timestamp,source,line_sms+offset); fflush(l); switch(line_sms[offset]) { default: fprintf(l,"%s ILLEGAL SMS COMMAND from %s : %s\n",timestamp,source,line_sms+offset); fflush(l); break; } } sms_input=0; } if(fifo_input) { rr_fifo=0; if(x_fifo) x_fifo[0]=0; fprintf(l,"%s Command from FIFO: %s\n",timestamp,line_fifo); fflush(l); switch(line_fifo[0]) //processing commands from named pipe { case 'a': //reset atmel if(!strncmp(line_fifo,"atmel_reset",11)) atmel_reset(); break; case 'c': //set circuits status { int okruh,status,alarm; if(3 != sscanf(line_fifo,"c %d %d %d",&okruh,&status,&alarm)) break; if(okruh<0||okruh>=N_CIRCUITS) break; circuits_status[okruh] = status; circuits_alarm[okruh] = alarm; savecircuits=1; } break; case 'l': //log the current status of circuits { int i; for(i=0; itm_hour==23) logfile_reopened=0; if(!logfile_reopened && tmp->tm_hour==0) { l=reopenlogfile(logfilename,l); logfile_reopened=1; //shift daily rain accumulation int i,j; for(j=0; j=1; --i) rain_cumulative[j][i] = rain_cumulative[j][i-1]; rain_cumulative[j][0]=0.; } saverain=1; } } //send a weekly sms message on friday noon to check proper functionality if(N_SMS>0) { time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); //clear report_sent on satturday if(tmp->tm_wday == 6) report_sent=0; if(!report_sent && tmp->tm_wday == 5 && tmp->tm_hour >= 12) { char command[256]; sprintf(command,"S %s %s Alarm Health Report at %s\n",smss[0],LOCATION,timestamp); write(smsfifodescr,command,strlen(command)); report_sent=1; } } //test //report_alarm(l,0,0,timestamp,"JUST A TEST",0); exit(0); } //MAIN loop fclose(l); close(f); }