/* Program by Christopher Nafis Written October 2004 Copyright 2004 Demonstrate posting weather data from the AAG 1-wire weather instrument (http://www.aagelectronica.com/aag/) to the WeatherUnderground (http://www.wunderground.com/) and CWOP (http://wxqa.com/) on the Internet. The rabbit communicates with a THE LINK (http://www.ibuttonlink.com/) over a RS232 connection. Uses the ImagineTools Ethernet Start Kit (http://www.imaginetools.com/) A male DB-9 connector is wired to pins on the J15 connector. DB-9 pin 2 => J15 R1 DB-9 pin 3 => J15 T1 DB-9 pin 4 & 8 => J15 VCC DB-9 pin 5 => J15 GND */ #class auto #define CINBUFSIZE 127 #define COUTBUFSIZE 127 #define C_BAUDRATE 9600L #define TCPCONFIG 3 // DHCP //#define PROXY "proxyserver.com" #define DEST "weatherstation.wunderground.com" #define DEST2 "arizona.aprs2.net" #define PORT 80 #define PORT2 23 // ibutton viewer lists addresses in reverse byte order #define TAI8515_DS1820 "10B80F1400080072" #define TAI8515_DS2450 "208DD00000000011" #define TAI8515_DS2423 "1D484A01000000CC" // Wind Direction Offset #define WINDOFFSET 337.5 // Weather Underground Account info #define WUID "xxxxxx" #define WUPASSWORD "xxxxxx" // CWOP info #define CWOPCALLSIGN "CWxxxxx" #define CWOPLAT "4249.91N" #define CWOPLONG "07352.78W" #use "dcrtcp.lib" #use "HOBBYIST.LIB" // This is clumsy, but I needed a non-blocking method // for talking with THE LINK rs232 1-wire interface while using // co-states. // #define SEND1WIRECOMMAND wfd cof_serCputs("r"); \ waitfor(DelayMs(50)); \ if (serCpeek() == -1) abort; \ wfd getOK = cof_serCgets(s,CINBUFSIZE-1,1000); \ if (getOK == 0) abort; \ if (s[0] != 'P')abort; \ if (serCpeek() == -1) abort; \ wfd getOK = cof_serCgets(junk,CINBUFSIZE-1,1000); \ if (getOK == 0) abort; \ if (strlen(linkreq)>0) \ { \ wfd cof_serCputs(linkreq); \ waitfor(DelayMs(100)); \ if (serCpeek() == -1) abort; \ wfd getOK = cof_serCgets(s,CINBUFSIZE-1,1000); \ if (getOK == 0) abort; \ if (serCpeek() == -1) abort; \ wfd getOK = cof_serCgets(junk,CINBUFSIZE-1,1000); \ if (getOK == 0) abort; \ } unsigned int hexchar2byte(); unsigned char checkcrc(); int getWindDirection(); const unsigned char crc_table[256] = { 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65, 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220, 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98, 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255, 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7, 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154, 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36, 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185, 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205, 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80, 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238, 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115, 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139, 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22, 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168, 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53}; void main() { // network variables char buffer[300]; int bytes_read; longword destIP; tcp_Socket socket; // serial port variables int getOK; char s[CINBUFSIZE]; char junk[CINBUFSIZE]; char linkreq[COUTBUFSIZE]; // HTTP/TELNET request variables char request[300]; char request2[300]; // time related variables struct tm rtc; unsigned long TAI8515windspeedtimestamp; unsigned long lastTAI8515windspeedtimestamp; int beenhereforwind; // weather sensor data float TAI8515tempF; float TAI8515tempC; float TAI8515winddir; float TAI8515windspeedMPH; float TAI8515windspeedgust; float TAI8515averagewindspeed; // sensor data validity int validTAI8515temp; int validTAI8515winddir; int validTAI8515windspeedcounter; int validTAI8515windspeed; int validTAI8515windspeedgust; int validconsensuswinddirection; int validTAI8515averagewindspeed; // low level sensor data unsigned int DS2450channelAMSB; unsigned int DS2450channelBMSB; unsigned int DS2450channelCMSB; unsigned int DS2450channelDMSB; int TAI8515windposition; int winddirbins[20]; unsigned long TAI8515windspeedcounter; unsigned long lastTAI8515windspeedcounter; // LCD variables char lcdtext[80]; // Temporary variables unsigned char LSB; unsigned char MSB; int countremain; int countperc; int i; int hungcheck; int TAI8515windspeedcount; float TAI8515windspeedsum; float consensuswinddirection; int consensussum; int consensusindex; /* Initialize LCD */ HBlcdInit(); HBlcdClear(); HBlcdSetCursor(0, 0); HBlcdWrite("Rabbit Weather"); HBlcdSetCursor(0, 1); HBlcdWrite("V1.0"); /* Initialize Network Connection */ sock_init(); while (ifpending(IF_DEFAULT) <= IF_COMING_UP) { tcp_tick(NULL); } /* Initialize rate calculations */ beenhereforwind = FALSE; TAI8515windspeedgust = 0; TAI8515windspeedsum = 0; TAI8515windspeedcount = 0; for (i=0;i<20;i++) winddirbins[i] = 0; /* Assume sensors don't exist until read correctly */ validTAI8515winddir = FALSE; validTAI8515temp = FALSE; validTAI8515windspeedcounter = FALSE; validTAI8515windspeed = FALSE; validTAI8515windspeedgust = FALSE; validconsensuswinddirection = FALSE; validTAI8515averagewindspeed = FALSE; /* Gather data, calculate rates, and post data using co-states */ loopinit(); while (1) { loophead(); /*============================================================================= Serial Communications with LINK Costate =============================================================================*/ costate { // force serial port to reset in case it is hung serCclose(); serCopen(C_BAUDRATE); /****************************************************************************** TA8515 Temperature http://www.aagelectronica.com/aag/index.html?target=p_1.html&lang=en-us http://pdfserv.maxim-ic.com/en/ds/DS18S20.pdf ******************************************************************************/ sprintf(linkreq,"p55%s44\r",TAI8515_DS1820); SEND1WIRECOMMAND HBpinHigh(HB_LED1); waitfor(DelayMs(900)); sprintf(linkreq,"p55%sBEFFFFFFFFFFFFFFFFFF\r",TAI8515_DS1820); SEND1WIRECOMMAND if ((strlen(s) == 38) && (strcmp(&s[20],"FFFFFFFFFFFFFFFFFF") != 0)) { if (checkcrc(&s[20]) == 0) { LSB = hexchar2byte(s[20],s[21]); MSB = hexchar2byte(s[22],s[23]); countremain = hexchar2byte(s[32],s[33]); countperc = hexchar2byte(s[34],s[35]); if (MSB == 0) TAI8515tempC = (LSB >> 1) - 0.25 + (countperc - countremain) / (float)countperc; else { LSB = ~(LSB >> 1) & 0x7F; TAI8515tempC = -(LSB+1.0); TAI8515tempC = TAI8515tempC - 0.25 + (countperc - countremain) / (float)countperc; } if (TAI8515tempC != 85) // possible power reset { TAI8515tempF = TAI8515tempC* 9.0 / 5.0 + 32.0; printf("TAI8515 temp = %.2fF\n",TAI8515tempF); validTAI8515temp = TRUE; HBpinLow(HB_LED1); } } } /****************************************************************************** TAI8515 Wind Direction http://www.aagelectronica.com/aag/index.html?target=p_1.html&lang=en-us http://pdfserv.maxim-ic.com/en/ds/DS2450.pdf ******************************************************************************/ // assume 5v is supplied sprintf(linkreq,"p55%s551C0040ffffff\r",TAI8515_DS2450); SEND1WIRECOMMAND // set to 8bit, 5.12V sprintf(linkreq,"p55%s55080008ffffff01ffffff08ffffff01ffffff08ffffff01ffffff08ffffff01ffffff\r",TAI8515_DS2450); SEND1WIRECOMMAND // Convert A/D (convert 3C, input select mask 0F, and zero readout 55) sprintf(linkreq,"p55%s3c0F55ffffff\r",TAI8515_DS2450); SEND1WIRECOMMAND sprintf(linkreq,""); SEND1WIRECOMMAND waitfor(DelayMs(5)); // get A,B,C,D channels sprintf(linkreq,"p55%sAA0000FFFFFFFFFFFFFFFFFFFFFFFF\r",TAI8515_DS2450); SEND1WIRECOMMAND if ((strlen(s) == 48) && (strcmp(&s[24],"FFFFFFFFFFFFFFFFFFFFFFFF") != 0)) { DS2450channelAMSB = hexchar2byte(s[26],s[27]); DS2450channelBMSB = hexchar2byte(s[30],s[31]); DS2450channelCMSB = hexchar2byte(s[34],s[35]); DS2450channelDMSB = hexchar2byte(s[38],s[39]); TAI8515windposition = getWindDirection( DS2450channelAMSB * 0.02,DS2450channelBMSB * 0.02, DS2450channelCMSB * 0.02,DS2450channelDMSB * 0.02); if (TAI8515windposition > 0) { winddirbins[TAI8515windposition - 1]++; TAI8515winddir = TAI8515windposition * 22.5; TAI8515winddir = TAI8515winddir + WINDOFFSET; if (TAI8515winddir >= 360) TAI8515winddir = TAI8515winddir - 360; printf("Wind from %g degrees\n",TAI8515winddir); validTAI8515winddir = TRUE; } } /****************************************************************************** TA8515 Wind Speed Counter http://www.aagelectronica.com/aag/index.html?target=p_1.html&lang=en-us http://pdfserv.maxim-ic.com/en/ds/DS2423.pdf ******************************************************************************/ sprintf(linkreq,"p55%sa5df01ffffffffffffffffffff\r",TAI8515_DS2423); SEND1WIRECOMMAND if ((strlen(s) == 44) && (strcmp(&s[24],"FFFFFFFFFFFFFFFFFFFF") != 0)) { MSB = hexchar2byte(s[28],s[29]); LSB = hexchar2byte(s[26],s[27]); TAI8515windspeedcounter = (MSB << 8) | LSB; TAI8515windspeedtimestamp = SEC_TIMER; printf("Windspeed counter = %ld at %ld\n", TAI8515windspeedcounter, TAI8515windspeedtimestamp); validTAI8515windspeedcounter = TRUE; } serCclose(); } /*============================================================================= Calculate Windspeed and Gusts =============================================================================*/ costate { waitfor(DelaySec(5)); if (validTAI8515windspeedcounter) { if (beenhereforwind) { if ((TAI8515windspeedtimestamp - lastTAI8515windspeedtimestamp) > 0) { TAI8515windspeedMPH = (TAI8515windspeedcounter-lastTAI8515windspeedcounter) * 2.453 / (TAI8515windspeedtimestamp - lastTAI8515windspeedtimestamp); printf("Windspeed = %.2f mph\n", TAI8515windspeedMPH); validTAI8515windspeed = TRUE; if (TAI8515windspeedMPH > TAI8515windspeedgust) TAI8515windspeedgust = TAI8515windspeedMPH; validTAI8515windspeedgust = TRUE; TAI8515windspeedsum = TAI8515windspeedsum + TAI8515windspeedMPH; TAI8515windspeedcount++; } } lastTAI8515windspeedcounter = TAI8515windspeedcounter; lastTAI8515windspeedtimestamp = TAI8515windspeedtimestamp; beenhereforwind = TRUE; } } /*============================================================================= Calculate average Windspeed (5 minute average) =============================================================================*/ costate { waitfor(DelaySec(300)); if (validTAI8515windspeedcounter) { if (TAI8515windspeedcount > 0) TAI8515averagewindspeed = TAI8515windspeedsum / TAI8515windspeedcount; else TAI8515averagewindspeed = 0; TAI8515windspeedsum = 0; TAI8515windspeedcount = 0; validTAI8515averagewindspeed = TRUE; } } /*============================================================================= Calculate average Wind direction (5 minute average) =============================================================================*/ costate { waitfor(DelaySec(300)); winddirbins[16] = winddirbins[0]; winddirbins[17] = winddirbins[1]; winddirbins[18] = winddirbins[2]; winddirbins[19] = winddirbins[3]; consensussum = 0; consensusindex = 0; for (i=0;i<16;i++) { if ((winddirbins[i]+winddirbins[i+1]+winddirbins[i+2]+winddirbins[i+3]+winddirbins[i+4]) > consensussum) { consensussum = winddirbins[i]+winddirbins[i+1]+winddirbins[i+2]+winddirbins[i+3]+winddirbins[i+4]; consensusindex = i; } } consensuswinddirection = (float) (consensusindex * winddirbins[consensusindex] + (consensusindex+1) * winddirbins[consensusindex+1] + (consensusindex+2) * winddirbins[consensusindex+2] + (consensusindex+3) * winddirbins[consensusindex+3] + (consensusindex+4) * winddirbins[consensusindex+4]) / (float) consensussum * 22.5; consensuswinddirection = consensuswinddirection + WINDOFFSET; if (consensuswinddirection >= 360) consensuswinddirection = consensuswinddirection - 360; printf("consensus wind direction = %g\n",consensuswinddirection); validconsensuswinddirection = TRUE; for (i=0;i<16;i++) winddirbins[i] = 0; } /*============================================================================= Update LCD Display =============================================================================*/ costate { tm_rd(&rtc); if (validTAI8515temp) { waitfor(DelaySec(2)); HBlcdClear(); sprintf(lcdtext,"%02d/%02d %02d:%02d UTC", rtc.tm_mon, rtc.tm_mday, rtc.tm_hour, rtc.tm_min); HBlcdWrite(lcdtext); HBlcdSetCursor(0, 1); sprintf(lcdtext,"Temp: %.1fF",TAI8515tempF); HBlcdWrite(lcdtext); } if (validTAI8515winddir) { waitfor(DelaySec(2)); HBlcdClear(); sprintf(lcdtext,"%02d/%02d %02d:%02d UTC", rtc.tm_mon, rtc.tm_mday, rtc.tm_hour, rtc.tm_min); HBlcdWrite(lcdtext); HBlcdSetCursor(0, 1); sprintf(lcdtext,"Wind from: %.1f",TAI8515winddir); HBlcdWrite(lcdtext); } if (validTAI8515windspeed) { waitfor(DelaySec(2)); HBlcdClear(); sprintf(lcdtext,"%02d/%02d %02d:%02d UTC", rtc.tm_mon, rtc.tm_mday, rtc.tm_hour, rtc.tm_min); HBlcdWrite(lcdtext); HBlcdSetCursor(0, 1); sprintf(lcdtext,"Wind: %.1f mph",TAI8515windspeedMPH); HBlcdWrite(lcdtext); } if (validTAI8515windspeedgust) { waitfor(DelaySec(2)); HBlcdClear(); sprintf(lcdtext,"%02d/%02d %02d:%02d UTC", rtc.tm_mon, rtc.tm_mday, rtc.tm_hour, rtc.tm_min); HBlcdWrite(lcdtext); HBlcdSetCursor(0, 1); sprintf(lcdtext,"Gust: %.1f mph",TAI8515windspeedgust); HBlcdWrite(lcdtext); } } /*============================================================================= Network Communications Costate =============================================================================*/ costate { waitfor(DelaySec(300)); HBpinLow(HB_LED2); #ifdef WUID tm_rd(&rtc); /****************************************************************************** Post Data to the WeatherUnderground web site ******************************************************************************/ #ifdef PROXY if( 0L == (destIP = resolve(PROXY)) ) { printf( "ERROR: Cannot resolve \"%s\" into an IP address\n", PROXY ); abort; } #else if( 0L == (destIP = resolve(DEST)) ) { printf( "ERROR: Cannot resolve \"%s\" into an IP address\n", DEST ); abort; } #endif if (!tcp_open(&socket,0,destIP,PORT,NULL)) { printf("failed to open\n"); abort; } waitfor(DelaySec(1)); if (!sock_established(&socket)) { printf("problem with socket connection\n"); sock_close(&socket); abort; } #ifdef PROXY sprintf(request,"GET http://%s/weatherstation/",DEST); #else sprintf(request,"GET /weatherstation/"); #endif sprintf(request2,"updateweatherstation.php?action=updateraw&ID=%s&PASSWORD=%s",WUID,WUPASSWORD); strcat(request,request2); sprintf(request2,"&dateutc=%04d-%02d-%02d%%20%02d:%02d:%02d",1900+rtc.tm_year,rtc.tm_mon,rtc.tm_mday,rtc.tm_hour,rtc.tm_min,rtc.tm_sec); strcat(request,request2); if (validTAI8515temp) { sprintf(request2,"&tempf=%0.2f",TAI8515tempF); strcat(request,request2); } if (validconsensuswinddirection) { sprintf(request2,"&winddir=%g",consensuswinddirection); strcat(request,request2); } if (validTAI8515averagewindspeed) { sprintf(request2,"&windspeedmph=%g",TAI8515averagewindspeed); strcat(request,request2); } if (validTAI8515windspeedgust) { sprintf(request2,"&windgustmph=%g",TAI8515windspeedgust); strcat(request,request2); } sprintf(request2,"&softwaretype=rabbit1wire"); strcat(request,request2); strcat(request," HTTP/1.0\r\n\r\n"); if (sock_write(&socket,request,strlen(request))!= strlen(request)) { printf("socket write failed\n"); sock_close(&socket); abort; } hungcheck = 0; while(tcp_tick(&socket)) { bytes_read=sock_fastread(&socket,buffer,sizeof(buffer)-1); if (bytes_read == -1) { printf("socket read error\n"); sock_close(&socket); abort; } if (bytes_read > 0) { buffer[bytes_read] = '\0'; if (strstr(buffer,"success") != NULL) { printf("data successfully posted to WeatherUnderground\n"); HBpinHigh(HB_LED2); break; } } /* we could be hung */ if (bytes_read == 0) { waitfor(DelaySec(5)); hungcheck++; if (hungcheck > 10) { printf("never got an HTTP response\n"); sock_close(&socket); abort; } } yield; } sock_close(&socket); while (tcp_tick(&socket)) { yield; } #endif /****************************************************************************** Post data to CWOP ******************************************************************************/ #ifdef CWOPCALLSIGN if( 0L == (destIP = resolve(DEST2)) ) { printf( "ERROR: Cannot resolve \"%s\" into an IP address\n", DEST2 ); abort; } printf("finished resolve\n"); if (!tcp_open(&socket,0,destIP,PORT2,NULL)) { printf("failed to open\n"); abort; } waitfor(DelaySec(10)); if (!sock_established(&socket)) { printf("problem with socket connection\n"); sock_close(&socket); abort; } if (validconsensuswinddirection && validTAI8515averagewindspeed && TAI8515windspeedgust && validTAI8515temp) { sprintf(request,"user %s pass -1 vers rabbitweather 1.00\r\n%s>APRS,TCPXX*:@",CWOPCALLSIGN,CWOPCALLSIGN); sprintf(request2,"%02d%02d%02dz%s/%s",rtc.tm_hour,rtc.tm_min,rtc.tm_sec,CWOPLAT,CWOPLONG); strcat(request,request2); sprintf(request2,"_%03d",(int)consensuswinddirection); strcat(request,request2); if ((TAI8515averagewindspeed >= 0) && (TAI8515averagewindspeed <= 999)) sprintf(request2,"/%03d",(int)TAI8515averagewindspeed); else { sprintf(request2,"/000"); printf("strange average wind %03d (%g)\n",(int)TAI8515averagewindspeed,TAI8515averagewindspeed); } strcat(request,request2); sprintf(request2,"g%03d",(int)TAI8515windspeedgust); strcat(request,request2); sprintf(request2,"t%03d",(int)TAI8515tempF); strcat(request,request2); sprintf(request2,"\r\n"); strcat(request,request2); if (sock_write(&socket,request,strlen(request))!= strlen(request)) { printf("socket write failed\n"); sock_close(&socket); abort; } hungcheck = 0; while(tcp_tick(&socket)) { bytes_read=sock_fastread(&socket,buffer,sizeof(buffer)-1); if (bytes_read == -1) { printf("socket read error\n"); sock_close(&socket); abort; } if (bytes_read > 0) { buffer[bytes_read] = '\0'; printf("CWOP: %s\n",buffer); if (strstr(buffer,CWOPCALLSIGN) != NULL) { printf("data successfully posted to CWOP\n"); break; } } /* we could be hung */ if (bytes_read == 0) { waitfor(DelaySec(5)); hungcheck++; if (hungcheck > 10) { printf("never got an CWOP response\n"); sock_close(&socket); abort; } } yield; } } sock_close(&socket); while (tcp_tick(&socket)) { yield; } #endif TAI8515windspeedgust = TAI8515windspeedMPH; } /*============================================================================= Process TCP/IP network stack work =============================================================================*/ costate { tcp_tick(NULL); } } } /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Convert two hex ASCII characters into a decimal number !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ unsigned int hexchar2byte(char MSnibble ,char LSnibble) { int ln; int mn; if (MSnibble >= 'A') mn = MSnibble - 'A' + 10; else mn = MSnibble - '0'; if (LSnibble >= 'A') ln = LSnibble - 'A' + 10; else ln = LSnibble - '0'; return (mn * 16 + ln); } /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Do the Dallas Semiconductor One Wire CRC8 on a string returned from THE LINK !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ unsigned char checkcrc(char* line) { int i; unsigned char crcsum; crcsum = 0; for (i=1;i