// *** RPi3 WS - Raspberry Pi 3, Weather Station V1.2 *** // 04.02.2018. Author: YT2FSG - Goran Stankovic dipl.ing.el. // http://www.qsl.net/yt2fsg/ (goranstank@gmail.com) // // gcc ws3.c -o ws3 -lm // ./ws3 #include #include //Used for UART #include //Used for UART #include //Used for UART #include //pow, atan2 #include int hex_val(unsigned char s1, unsigned char s2); int hex_val2(unsigned char s1, unsigned char s2, unsigned char s3, unsigned char s4); unsigned int atan_fast(signed int y, signed x); unsigned int CalculateLux(unsigned int iGain, unsigned int tInt, unsigned int ch0, unsigned int ch1); int bmp_GetPressure(int msb, int lsb, int xsb); int bmp_b5_Get(int msb, int lsb); long signed int t_fine; long signed int compensateTemperature(int msb, int lsb, int xsb); long unsigned int compensatePressure(int msb, int lsb, int xsb); long unsigned int compensateHumidity(int msb, int lsb); //**************************************************************************** // Calculate values - SOLAR - Sunce //**************************************************************************** #define solar_const 126.58 // ??? 1lux = 0.0079W/m2 => 1/0.0079=126.58 #define uv_const 747 // Rset=270K; IT=4T => 747 //**************************************************************************** // Calibration values - RAIN - Kisa //**************************************************************************** #define rain_const 5.8 // count: 1l => 58 impuls (P=0.1m2, 1l=1mm) // 58/10 = 5.8 //**************************************************************************** // Calibration values - WIND - Vetar //**************************************************************************** #define wind_const 201 // 1 impuls/min = 0.29845m/s (2 x magnet) // 60/0.29845 = 201s/m in min #define wdeg_offset 0 // wind direction offset -180'...+180' //**************************************************************************** // Calibration values - BME280 //**************************************************************************** // These are stored in the BME280 // MSB, LSB unsigned short int dig_T1 = 0x6E64; //0x89,0x88 signed short int dig_T2 = 0x66FD; //0x8B,0x8A signed short int dig_T3 = 0x0032; //0x8D,0x8C unsigned short int dig_P1 = 0x900A; //0x8F,0x8E signed short int dig_P2 = 0xD5A7; //0x91,0x90 signed short int dig_P3 = 0x0BD0; //0x93,0x92 signed short int dig_P4 = 0x1C5C; //0x95,0x94 signed short int dig_P5 = 0xFFAB; //0x97,0x96 signed short int dig_P6 = 0xFFF9; //0x99,0x98 signed short int dig_P7 = 0x26AC; //0x9B,0x9A signed short int dig_P8 = 0xD80A; //0x9D,0x9C signed short int dig_P9 = 0x10BD; //0x9F,0x9E unsigned char dig_H1 = 0x4B; // 0xA1 signed short int dig_H2 = 0x0162; //0xE2,0xE1 unsigned char dig_H3 = 0x00; // 0xE3 signed short int dig_H4 = 0x0152; //0xE4,0xE5[3:0] => dig_H4[11:4],[3:0] signed short int dig_H5 = 0x0000; //0xE6,0xE5[7:4] => dig_H5[11:4],[3:0] signed char dig_H6 = 0x1E; // 0xE7 //**************************************************************************** // Calibration values - TSL2561 //**************************************************************************** #define LUX_SCALE 14 // scale by 2^14 #define RATIO_SCALE 9 // scale ratio by 2^9 //--------------------------------------------------- // Integration time scaling factors //--------------------------------------------------- #define CH_SCALE 10 // scale channel values by 2^10 #define CHSCALE_TINT0 0x7517 // 322/11 * 2^CH_SCALE #define CHSCALE_TINT1 0x0fe7 // 322/81 * 2^CH_SCALE //--------------------------------------------------- // T, FN, and CL Package coefficients //--------------------------------------------------- #define K1T 0x0040 // 0.125 * 2^RATIO_SCALE #define B1T 0x01f2 // 0.0304 * 2^LUX_SCALE #define M1T 0x01be // 0.0272 * 2^LUX_SCALE #define K2T 0x0080 // 0.250 * 2^RATIO_SCALE #define B2T 0x0214 // 0.0325 * 2^LUX_SCALE #define M2T 0x02d1 // 0.0440 * 2^LUX_SCALE #define K3T 0x00c0 // 0.375 * 2^RATIO_SCALE #define B3T 0x023f // 0.0351 * 2^LUX_SCALE #define M3T 0x037b // 0.0544 * 2^LUX_SCALE #define K4T 0x0100 // 0.50 * 2^RATIO_SCALE #define B4T 0x0270 // 0.0381 * 2^LUX_SCALE #define M4T 0x03fe // 0.0624 * 2^LUX_SCALE #define K5T 0x0138 // 0.61 * 2^RATIO_SCALE #define B5T 0x016f // 0.0224 * 2^LUX_SCALE #define M5T 0x01fc // 0.0310 * 2^LUX_SCALE #define K6T 0x019a // 0.80 * 2^RATIO_SCALE #define B6T 0x00d2 // 0.0128 * 2^LUX_SCALE #define M6T 0x00fb // 0.0153 * 2^LUX_SCALE #define K7T 0x029a // 1.3 * 2^RATIO_SCALE #define B7T 0x0018 // 0.00146 * 2^LUX_SCALE #define M7T 0x0012 // 0.00112 * 2^LUX_SCALE #define K8T 0x029a // 1.3 * 2^RATIO_SCALE #define B8T 0x0000 // 0.000 * 2^LUX_SCALE #define M8T 0x0000 // 0.000 * 2^LUX_SCALE //**************************************************************************** //**************************************************************************** //**************************************************************************** int main(int argc, char **argv) { FILE * wdat; //------------------------- //----- SETUP USART 0 ----- //------------------------- //At bootup, pins 8 and 10 are already set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively int uart0_filestream = -1; unsigned char rx_buffer[256]; unsigned char buf[256]; puts(buf); //izbrisi buf int rx_length, i, j, rx_ok=0; float rh, t, p, t2, rh2, r, rs, r1, r2, w, wx1, wy1, wdeg1, sw; signed short int wdeg2, wx, wy; unsigned int wdeg, s, s0, s1, suv, uvi, min, sec; int xmin=0, xmax=0, ymin=0, ymax=0, xoff, yoff, xd, yd, xo, yo, xgain, ygain; int msb,lsb,xsb; int sec1=0, min1=0, hour1=0, zsec1=0; time_t tt1; struct tm *tt2; char filename_a[40]="/var/www/data/w20170101.csv"; tt1=time(NULL); tt2=localtime(&tt1); strftime(filename_a,40,"/var/www/data/w%Y%m%d.csv",tt2); //OPEN THE UART //The flags (defined in fcntl.h): // Access modes (use 1 of these): // O_RDONLY - Open for reading only. // O_RDWR - Open for reading and writing. // O_WRONLY - Open for writing only. // // O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status // if there is no input immediately available (instead of blocking). Likewise, write requests can also return // immediately with a failure status if the output can't be written immediately. // // O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process. uart0_filestream = open("/dev/serial0", O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode if (uart0_filestream == -1) { //ERROR - CAN'T OPEN SERIAL PORT printf("ERROR - Unable to open UART. Ensure it is not in use by another application\n"); } //CONFIGURE THE UART //The flags (defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html): // Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, // B230400, B460800, B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000 // CSIZE:- CS5, CS6, CS7, CS8 // CLOCAL - Ignore modem status lines // CREAD - Enable receiver // IGNPAR = Ignore characters with parity errors // ICRNL - Map CR to NL on input (Use for ASCII comms where you want to auto correct end of line characters - don't use for bianry comms!) // PARENB - Parity enable // PARODD - Odd parity (else even) struct termios options; tcgetattr(uart0_filestream, &options); options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; // 64) j=0; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // HEX Data on RS232: // 00000000001111111111222222222233333333334444444444555555555566 // 01234567890123456789012345678901234567890123456789012345678901 // 0011222233334444445555556666777788889999AAAABBBBCCCCDDDDEEEEDA // m s RH T P T2 RH2 S0 S1 Suv R1h R W WX WY nr // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if ( rx_ok == 1) { rx_ok=0; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - min = hex_val(buf[0],buf[1]); printf("%02d:",min); sec = hex_val(buf[2],buf[3]); printf("%02d = ",sec); // ----- Temperatura vazduha (C) msb = hex_val(buf[18],buf[19]); lsb = hex_val(buf[20],buf[21]); xsb = hex_val(buf[22],buf[23]); t = (float)compensateTemperature(msb,lsb,xsb)/100; printf("%2.1f'C ",t); // ----- Pritisak vazduha (mbar) msb = hex_val(buf[12],buf[13]); lsb = hex_val(buf[14],buf[15]); xsb = hex_val(buf[16],buf[17]); p = (float)compensatePressure(msb,lsb,xsb)/100; printf("%5.0fmbar ",p); // ----- Vlaznost vazduha (%) msb = hex_val(buf[24],buf[25]); lsb = hex_val(buf[26],buf[27]); rh = (float)compensateHumidity(msb,lsb)/1000; printf("%2.1f%% ",rh); // ----- IR&V Suncevo zracenje (lux, W/m2) s0 = hex_val2(buf[28],buf[29],buf[30],buf[31]); s1 = hex_val2(buf[32],buf[33],buf[34],buf[35]); s = CalculateLux(1,1,s0,s1); // (lux) sw = s / solar_const; // (W/m2) printf("%dlux %4.1fW/m2 ", s, sw); // ----- UV Suncevo zracenje (index) suv = hex_val2(buf[36],buf[37],buf[38],buf[39]); uvi = suv/uv_const; printf("UV%d=%d", suv, uvi); // ----- Padavine - Kisa (mm/m2) r1 = (hex_val2(buf[40],buf[41],buf[42],buf[43])); r = (hex_val2(buf[44],buf[45],buf[46],buf[47])); r1 = r1 / rain_const; // 5.8 r = r / rain_const; // 5.8 printf("%5.1fmm/h %5.1fmm ", r1, r); // ----- Brzina vetra (m/s) w = (hex_val2(buf[48],buf[49],buf[50],buf[51])); w = w / wind_const; // 201 printf("%3.1fm/s ",w); // ----- Smer vetra (deg) wx = hex_val2(buf[52],buf[53],buf[54],buf[55]); wy = hex_val2(buf[56],buf[57],buf[58],buf[59]); printf("(X=%d Y=%d) ",wx,wy); // Offset racun if(wx > xmax) { xmax = wx; } if(wx < xmin) { xmin = wx; } if(wy > ymax) { ymax = wy; } if(wy < ymin) { ymin = wy; } xoff = (xmax + xmin)/2; yoff = (ymax + ymin)/2; xo = wx - xoff; yo = wy - yoff; // Gain racun xd = (xmax - xmin); yd = (ymax - ymin); if(xd > yd) { xgain = 1000; ygain = (xd * 1000) / yd ; } else { xgain = (yd *1000) / xd ; ygain =1000; } xo = (xo * xgain) / 1000; yo = (yo * ygain) / 1000; wdeg = atan_fast(xo,yo); // X osa pokazuje SEVER // Deg offset racun wdeg = wdeg + wdeg_offset; if(wdeg >= 360) wdeg = wdeg -360; if(wdeg < 0) wdeg = wdeg + 360; printf("Wd=%3d' ",wdeg); printf("\n"); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - time(&tt1); tt2=localtime(&tt1); sec1=tt2->tm_sec; if (sec1 != zsec1) // ? sec { zsec1=sec1; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Upis podataka u FILE za razmenu sa Apache serverom if((wdat = fopen("/var/tmp1/wdata.txt","w")) == NULL) { printf("File /var/tmp1/wdata.txt not open!\n"); } else { fprintf(wdat,"%3.1f %5.0f %3.1f %5d %4.1f %2d %5.0f %5.0f %3.1f %3d\n",t,p,rh,s,sw,uvi,r1,r,w,wdeg); } fclose(wdat); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (sec1 == 0) // ? hh:mm:00 pocetak minuta { min1=tt2->tm_min; hour1=tt2->tm_hour; if (min1==0) // ? hh:00:00 pocetak sata { // Na pocetak sata posalji sinhro impuls, potreban za merenje padavina //----- TX BYTES ----- p_tx_buffer = &tx_buffer[0]; *p_tx_buffer++ = '0'; *p_tx_buffer++ = '\n'; *p_tx_buffer++ = '\r'; if (uart0_filestream != -1) { int count = write(uart0_filestream, &tx_buffer[0], (p_tx_buffer - &tx_buffer[0])); //Filestream, bytes to write, number of bytes to write if (count < 0) { printf("UART TX error\n"); } } printf ("= SYNC = %02d:%02d:%02d\n",hour1,min1,sec1); if (hour1==0) // ? 00:00:00 pocetak dana { // Promeni filename u ponoc tt1=time(NULL); tt2=localtime(&tt1); strftime(filename_a,40,"/var/www/data/w%Y%m%d.csv",tt2); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //Upisi podatke u FILE 'aYYYYmmdd.csv' - akvizicija analognih podataka na 1 minut if ((wdat = fopen(filename_a,"a")) == NULL) { printf("File %s not open! \n",filename_a); } else { fprintf(wdat,"%02d:%02d:%02d; %3.1f; %5.0f; %3.1f; %5d; %4.1f; %2d; %5.0f; %5.0f; %3.1f; %3d\n", hour1,min1,sec1,t,p,rh,s,sw,uvi,r1,r,w,wdeg); } fclose(wdat); } } // ? sec // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } } // Loop } //----- CLOSE THE UART ----- close(uart0_filestream); return 0; } //---------------------------------------------------------------------- //---------------------------------------------------------------------- int hex_val(unsigned char s1, unsigned char s2) { if ((s1>=48) && (s1<=57)){s1=s1-48;} else if ((s1>=65)&& (s1<=70)){s1=s1-55;} else {s1=0;} if ((s2>=48) && (s2<=57)){s2=s2-48;} else if ((s2>=65)&& (s2<=70)){s2=s2-55;} else {s2=0;} return (s1*16+s2); } //-------------------------------------------------------------------- int hex_val2(unsigned char s1, unsigned char s2, unsigned char s3, unsigned char s4) { if ((s1>=48) && (s1<=57)){s1=s1-48;} else if ((s1>=65)&& (s1<=70)){s1=s1-55;} else {s1=0;} if ((s2>=48) && (s2<=57)){s2=s2-48;} else if ((s2>=65)&& (s2<=70)){s2=s2-55;} else {s2=0;} if ((s3>=48) && (s3<=57)){s3=s3-48;} else if ((s3>=65)&& (s3<=70)){s3=s3-55;} else {s3=0;} if ((s4>=48) && (s4<=57)){s4=s4-48;} else if ((s4>=65)&& (s4<=70)){s4=s4-55;} else {s4=0;} return (s1*4096+s2*256+s3*16+s4); } //---------------------------------------------------------------------- //---------------------------------------------------------------------- unsigned int atan_fast(signed int y, signed int x) { unsigned char negflag, tempdegree, comp; unsigned int degree, ux, uy; negflag=0; if (x<0) { negflag+=0x01; x=(0-x); } ux=x; if (y<0) { negflag+=0x02; y=(0-y); } uy=y; if (ux>uy) { degree=(uy*45)/ux; negflag+=0x10; } else { degree=(ux*45)/uy; } comp=0; tempdegree=degree; if(tempdegree > 22) { if(tempdegree <= 44) comp++; if(tempdegree <= 41) comp++; if(tempdegree <= 37) comp++; if(tempdegree <= 32) comp++; } else { if(tempdegree >= 2) comp++; if(tempdegree >= 6) comp++; if(tempdegree >= 10) comp++; if(tempdegree >= 15) comp++; } degree += comp; if(negflag & 0x10) degree = (90-degree); if(negflag & 0x02) { if(negflag & 0x01) degree = (180 + degree); else degree = (180 - degree); } else { if(negflag & 0x01) degree = (360 - degree); } return (degree); } //---------------------------------------------------------------------- //---------------------------------------------------------------------- unsigned int CalculateLux(unsigned int iGain, unsigned int tInt, unsigned int ch0, unsigned int ch1) { //------------------------------------------------------------------------ // first, scale the channel values depending on the gain and integration time // 16x, 402mS is nominal. // scale if integration time is NOT 402 msec unsigned long chScale; unsigned long channel1; unsigned long channel0; // chScale = CHSCALE_TINT1; switch (tInt) { case 0: // 13.7 msec chScale = CHSCALE_TINT0; break; case 1: // 101 msec chScale = CHSCALE_TINT1; break; default: // assume no scaling chScale = (1 << CH_SCALE); break; } // scale if gain is NOT 16X if (!iGain) chScale = chScale << 4; // scale 1X to 16X // scale the channel values channel0 = (ch0 * chScale) >> CH_SCALE; channel1 = (ch1 * chScale) >> CH_SCALE; //------------------------------------------------------------------------ // find the ratio of the channel values (Channel1/Channel0) // protect against divide by zero unsigned long ratio1 = 0; if (channel0 != 0) ratio1 = (channel1 << (RATIO_SCALE+1)) / channel0; // round the ratio value unsigned long ratio = (ratio1 + 1) >> 1; // is ratio <= eachBreak ? unsigned int b, m; // T, FN and CL package if ((ratio >= 0) && (ratio <= K1T)) {b=B1T; m=M1T;} else if (ratio <= K2T) {b=B2T; m=M2T;} else if (ratio <= K3T) {b=B3T; m=M3T;} else if (ratio <= K4T) {b=B4T; m=M4T;} else if (ratio <= K5T) {b=B5T; m=M5T;} else if (ratio <= K6T) {b=B6T; m=M6T;} else if (ratio <= K7T) {b=B7T; m=M7T;} else if (ratio > K8T) {b=B8T; m=M8T;} else; unsigned long temp; temp = ((channel0 * b) - (channel1 * m)); // do not allow negative lux value if (temp < 0) temp = 0; // round lsb (2^(LUX_SCALE-1)) temp += (1 << (LUX_SCALE-1)); // strip off fractional portion unsigned long lux = temp >> LUX_SCALE; return(lux); } //---------------------------------------------------------------------- double bmp_altitude(double p){ //return 145437.86*(1- pow((p/1013.25),0.190294496)); //return feet return 44330*(1- pow((p/1013.25),0.190294496)); //return meters } //-------------------------------------------------------------------- // Calculate calibrated temperature BME280 // resolution 1/100 long signed int compensateTemperature(int msb, int lsb, int xsb) { long signed int var1, var2, T; long signed int adc_T; adc_T = (((unsigned int) msb << 16) | ((unsigned int) lsb << 8) | (unsigned int) xsb) >> 4; var1 = ((((adc_T>>3) - ((long signed int)dig_T1 <<1))) * ((long signed int)dig_T2)) >> 11; var2 = (((((adc_T>>4) - ((long signed int)dig_T1)) * ((adc_T>>4) - ((long signed int)dig_T1))) >> 12) * ((long signed int)dig_T3)) >> 14; t_fine = var1 + var2; T = (t_fine * 5 + 128) >> 8; return T; } //-------------------------------------------------------------------- // Calculate calibrated pressure BME280 // resolution 1/256 long unsigned int compensatePressure(int msb, int lsb, int xsb) { long long signed int var1, var2, p; long signed int adc_P; adc_P = (((unsigned int) msb << 16) | ((unsigned int) lsb << 8) | (unsigned int) xsb) >> 4; var1 = ((long long signed int)t_fine) - 128000; var2 = var1 * var1 * (long long signed int)dig_P6; var2 = var2 + ((var1*(long long signed int)dig_P5)<<17); var2 = var2 + (((long long signed int)dig_P4)<<35); var1 = ((var1 * var1 * (long long signed int)dig_P3)>>8) + ((var1 * (long long signed int)dig_P2)<<12); var1 = (((((long long signed int)1)<<47)+var1))*((long long signed int)dig_P1)>>33; if (var1 == 0) { return 0; // avoid exception caused by division by zero } p = 1048576 - adc_P; p = (((p<<31) - var2)*3125) / var1; var1 = (((long long signed int)dig_P9) * (p>>13) * (p>>13)) >> 25; var2 = (((long long signed int)dig_P8) * p) >> 19; p = ((p + var1 + var2) >> 8) + (((long long signed int)dig_P7)<<4); return (long unsigned int)p/256; } //-------------------------------------------------------------------- // Calculate calibrated humidity BME280 // resolution 1/1024 long unsigned int compensateHumidity(int msb, int lsb) { long signed int v_x1_u32r; long signed int adc_H; adc_H = ((unsigned int) msb << 8) | (unsigned int) lsb; v_x1_u32r = (t_fine - ((long signed int)76800)); v_x1_u32r = (((((adc_H << 14) - (((long signed int)dig_H4) << 20) - (((long signed int)dig_H5) * v_x1_u32r)) + ((long signed int)16384)) >> 15) * (((((((v_x1_u32r * ((long signed int)dig_H6)) >> 10) * (((v_x1_u32r * ((long signed int)dig_H3)) >> 11) + ((long signed int)32768))) >> 10) + ((long signed int)2097152)) * ((long signed int)dig_H2) + 8192) >> 14)); v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((long signed int)dig_H1)) >> 4)); v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; return (long unsigned int)(v_x1_u32r>>12); } //--------------------------------------------------------------------