Building a Web Weather Station
Reports that come down the serial line look like this:
17:05 08/09/97 WSW 00MPH 460F 069F 057F 085% 23.42F 00.00"D 01.39"M 00.00"R
The first two fields are time and date. The current time and date are maintained on the WRL-25 display unit. I have not noticed any great drift in the time setting.
The second two fields are wind direction (WSW—West by South West) and speed (00 MPH—miles per hour).
Fields 5 through 7 are temperature readings in degrees Fahrenheit. Field 5 is not connected to any sensor, so it should be ignored. Field 6 is indoor temperature and field 7 is outdoor temperature.
Field 8 is relative humidity, which in this example is 85%. Field 9 is atmospheric pressure in inches of mercury accompanied by a single character for falling (F), rising (R) or steady (S).
The last three fields are daily rainfall and monthly rainfall, both in inches, and rainfall rate in inches per hour.
At the end of each day, two lines of daily minimum and maximum readings are reported:
Max 08/08/97 WSW 24MPH 460F 074F 081F 100% 23.42" 00.00"D 01.39"M 00.00"R Min 08/08/97 SW 00MPH 460F 068F 044F 021% 23.28" 00.00"D 01.39"M 00.00"R
These lines are in the same format as the other reports, except for the first field which marks these records as Max/Min reports. The readings for daily min/max are independent. In the above example, the high temperature for the day was 81 degrees F and the highest humidity reading was 100%. These readings did not occur at the same time and are unrelated, except that both are the maximum for that particular statistic.
My first pass at a data collection script was a simple cat command:
cat /dev/ttyS19 >> data1
It worked—mainly by accident. The next time my machine was rebooted, it didn't work at all. When I fired it up, the weather station console started spitting out all sorts of long reports. After a little head scratching, it became obvious that regular character echoing was feeding back command characters—not at all what I had in mind.
My current script is called weatherd.pl and is shown in Listing 1 with blank lines removed. Line 8 sets the variable $TTY to be the first command-line argument. Line 10 resets the terminal to the appropriate speed, parity and non-echoing. I used the stty command to get the terminal settings the way I wanted them, then saved the setup to a file. This stty command reads from that file and sets the terminal to the saved configuration.
Data is kept in a file, with the name of the file being the current date. Each day's data goes into one file (lines 14-16). The print statement on line 16 helps me feel confident things are working right. Beginning with line 27, for each data line that comes in from the tty, we check to see if it is a minimum or maximum and that it is still today's data. Minimum and maximum data go into separate files.
To start weatherd.pl, I added the following single line to my /etc/inittab file:
This line starts up weatherd.pl and respawns it if it should die for any reason.
Now I had the data coming in from the weather station, getting picked up by weatherd.pl and thrown into a file using the date as its name.
The next step was to format the data into an HTML file for display over the Web. I wrote a Perl script (Listing 2) that takes the last line from the current data file, in combination with a template HTML file, and fills in the weather information. It also calculates the corrected atmospheric pressure and the dew point. (The dew point calculation was given to me by John Kleist, Colorado Climate Center, email@example.com.) Last of all, it looks in yesterday's Max/Min files and puts those values in the output HTML.
I also wrote a script, plotdays.pl (Listing 3), that plots some of the interesting statistics for the last few days.
Finally, I have a master program called loop.pl, which I wrote to reliably connect to my ISP, set up my PPP connection, transfer e-mail, set the system time and upload the weather data.
First, we have to look at how to automate an FTP connection. FTP is usually used for interactive network file transfer. To use regular FTP, you need to have an account on the remote machine. In this case, I use my ISP shell account with Front Range Internet (frii.net). A user can set up an automated FTP session by using a file called .netrc in their $HOME directory. I added to my system a user called “weather” dedicated to owning the weather data files and scripts. In the /home/weather/.netrc file, I have the following lines:
machine ftp.frii.net login: password: macdef init cd public_html put /home/weather/WWW/wscurrent.html wscurrent.html put /home/weather/WWW/wsplot.html wsplot.html put /home/weather/WWW/temp.gif temp.gif put /home/weather/WWW/pressure.gif pressure.gif put /home/weather/WWW/raind.gif raind.gif put /home/weather/WWW/winds.gif winds.gif quit
The statements in this file define and execute a macro called init. All I have to do is start FTP and this script attempts to run the macro, uploading my HTML and GIF files to the appropriate place on my ISP account.
Both Listing 1 and Listing 3 are called from a short script called doup, Listing 4, which also does the actual FTP call.
Last but not least, loop.pl (Listing 5), is executed continuously by the root user. The only tricky part of loop.pl is the necessity to recover if a command gets stuck due to a loss of connection with the ISP. I wrote a replacement for the regular Perl “system” function which allows me to specify a timeout for each command. If that command exceeds the time alloted, it and all of its children are killed. Loop.pl also has other jobs to do, like downloading and uploading my e-mail and setting the system time using ntpdate. While those jobs are happening, I have a child process simultaneously running the doup script. If the ancillary jobs finish first, they give doup a little more time to complete. With a good connection, there is no problem getting everything done in less than one minute.
I have two different dial-up scripts, because there are two possible telephone numbers for the computer to try when dialing my ISP. If the primary number fails, it tries the second number. If both fail, it sleeps for 60 seconds and tries again. The goal of the sleep statements in loop.pl is to have the connection occur approximately every 15 minutes. Otherwise, the comments in the code pretty much cover all of the details.
Practical books for the most technical people on the planet. Newly available books include:
- Agile Product Development by Ted Schmidt
- Improve Business Processes with an Enterprise Job Scheduler by Mike Diehl
- Finding Your Way: Mapping Your Network to Improve Manageability by Bill Childers
- DIY Commerce Site by Reven Lerner
Plus many more.