I’ve had a simple weather station for a while; it’s an Oregon Scientific system with a base unit and wireless outside thermometer hygrometer. Â I was pretty happy with it, so I got an extra sensor for the basement, and was later given a third sensor to put in the baby’s room. Â The base unit also measures temperature, humidity, and pressure. Â The unit shows high and low temperatures for the last 24 hours, but I’ve always wished I could log the data to a PC so I could see graphs of temperature and humidity changes throughout the house.
While doing research for a different project, I found some articles about receiving data from the same wireless sensors I already had, so I decided to dive in.  My plan was to receive the data using an arduino, and send it to the PC over a USB cable.  A wifi receiver would be better, but I already had the arduino lying around so I thought it would do the job.  I bought a 433MHz receiver module and set to work experimenting with the arduino.  I had some trouble getting data out at first, and then spent a lot of time trying to figure out the data formats coming from the devices.  The end result is this site, which displays the records from the station: http://mattandcalin.com/weather/
Arduino Implementation
Parts:
I used an arduino diecimila, and a 433MHz receiver module- I picked one of the better modules on the recommendation of some of the experiences I read about.  I used the sketch provided here to receive the Oregon Scientific signals.  All I modified in that script was how the strings were dumped to serial.  The byte arrays are sent in hex format, which makes them easy to deal with.  The output from the arduino then looks like this:
[ookDecoder]
OSV2 1A2D10EC622010D74A0B
OSV2 1A2D404D9222306441FC
The first token in the string is the protocol type; OSV2 is Oregon Scientific Version 2. Â Interestingly, I also saw some “CRES” strings, but it appears to be just noise.
I had some trouble figuring out which pin to hook the receiver into; it’s analog pin 1.
Packet Format
The most frustrating thing about the project was figuring out how to decode the packets. Â There are several documents out there which describe how to decode these, but unfortunately none of them had it 100% right for my devices. Â The most extensive resource was this document. Â Oregon Scientific has several devices out there, so it may just be that my devices happen to be different. Â While these documents helped tremendously, I ended up reverse engineering the formats through trial and error. Â Many values are in Binary-coded decimal, or BCD.
Decoding  Oregon Scientific THGR122NX Temperature and Humidity sensor:
Sample String:Â 1A 2D 40 4D 92 22 30 64 41 FC
Indexes below are bytes, which are two hex digits each:
Byte | Code Sample | Example | Description |
0..1 | bytes[0], bytes[1] | 1A2D | Device Model ID |
2 (upper 4 bits) | bytes[2]>>4 | 4 | Channel (4) note: channels were 1,2,4- 4 is channel 3. |
2 (lower) | bytes[2]&0x0F | 0 | Battery Code |
3 | bytes[3] | 4D | Rolling Code? Reported to change on battery replacement. |
4 (upper) | bytes[4]>>4 | 9 | Tenths place of temp in degrees celsius BCD format |
4 (lower) | bytes[4]&0x0F | 2 | Unknown |
5 (upper) | bytes[5]>>4 | 2 | Tens place of temp in degrees celsius BCD format |
5 (lower) | bytes[5]&0x0F | 2 | Ones place of temp in degrees celsius BCD format |
6 (upper) | bytes[6]>>4 | 3 | Humidity ones place BCD format |
6 (lower) | bytes[6]&0x0F | 0 | Sign of degrees Non zero indicates negative |
7 (upper) | bytes[7]>>4 | 6 | Unknown |
7 (lower) | bytes[7]&0x0F | 4 | Humidity tems place BCD format |
8 | bytes[8] | 41 | Checksum |
9 | bytes[9] | FC | Unknown |
I later bought another sensor which has some slight format differences:The checksum is calculated by adding all nibbles for bytes 0-7, subtracting 10, and then taking the bottom 8 bits. Â The result should match the checksum at byte 8. Â Many thanks to this page for giving me the hint to subtract 10; to figure out how the checksum worked I just spent hours with a calculator adding bytes and nibbles to try to get something meaningful out.
Checksum Code: (In part from http://www.nerdworld.org/weather/localweather/weather.py)
CheckSumCalculated = 0
for ElementCounter in range(len(bytes) – 2):
CheckSumCalculated = CheckSumCalculated + (bytes[ElementCounter] >> 4)
CheckSumCalculated = CheckSumCalculated + (bytes[ElementCounter] & 0x0F)CheckSumCalculated = CheckSumCalculated – 10
CheckSumCalculated = (CheckSumCalculated & 0xFF) # Bottom 8 bits (not 7 as stated in docs)if bytes[len(bytes)-2] == CheckSumCalculated:
self.valid = 1
else:
self.valid = 0
Decoding Oregon Scientific BTHR968 Wireless Baro-Thermo-Hygrometer:
Sample String:Â 5A 6D 00 7A 10 23 30 83 86 31
Indexes below are bytes, which are two hex digits each:
Byte | Code Sample | Example | Description |
0..1 | bytes[0], bytes[1] | 5A6D | Device Model ID |
2 (upper 4 bits) | bytes[2]>>4 | 0 | Unknown |
2 (lower) | bytes[2]&0x0F | 0 | Battery Code |
3 | bytes[3] | 7A | Rolling Code? Reported to change on battery replacement. |
4 (upper) | bytes[4]>>4 | 1 | Tenths place of temp in degrees celsius BCD format |
4 (lower) | bytes[4]&0x0F | 0 | Unknown |
5 (upper) | bytes[5]>>4 | 2 | Tens place of temp in degrees celsius BCD format |
5 (lower) | bytes[5]&0x0F | 3 | Ones place of temp in degrees celsius BCD format |
6 (upper) | bytes[6]>>4 | 3 | Humidity ones place BCD format |
6 (lower) | bytes[6]&0x0F | 0 | Sign of degrees Non zero indicates negative |
7 (upper) | bytes[7]>>4 | 8 | Unknown |
7 (lower) | bytes[7]&0x0F | 3 | Humidity tems place BCD format |
8 | bytes[8] | 86 | Barometric Pressure binary quantity. Add 856 to get mb of pressure |
9 | bytes[9] | 31 | Unknown |
This device does not appear to have a checksum. Â It’s also worth noting that it seemed to interfere with the other three sensors, but verifying the checksum on those devices seemed to help.
Client Software:
A python 3 script reads the hex strings using pySerial. Â The strings are then decoded as specified in the previous section, values are adjusted, and the data is sent off to my mysql server and weather underground. The code may be posted at a later time, but I’d like to clean it up first.
Update:
The code can be viewed here:Â https://bitbucket.org/mattlary/weatherlogger
Before storing the barometric pressure readings, they must be converted for sea level equivalent values.  The altitude of the station is about 500, indicating an offset of 18.2mb or .54 inHg according to this document: http://www.novalynx.com/manuals/bp-elevation-correction-tables.pdf
I also calculate the dewpoint using a python script from http://joeellsworth.com/, which has unfortunately since been removed.
Server Software:
On the server side I just created a mysql database with a simple table to store the readings for each channel. Â I then dump that data into jpgraph to create the graphs. Â I had some issues with the page failing because of memory issues, so I had to trim down how many rows I was throwing at it. Â I found a neat trick to round the times to 5 minute intervals, taking the average of all of the readings for those 5 minutes. Â So far that’s working great. Â Here are some sample queries I use to feed the data to jpgraph:
7 Days of records at 5 minute intervals:
SELECT CHANNEL, avg(TEMPF) tempf, avg(HUMIDITY) humidity, ROUND(unix_timestamp(recordtime) / (60*5)) * 60 * 5 as rounded_time FROM templog where recordtime between date_sub(now(), interval 7 day) and now() group by channel, rounded_time ORDER BY rounded_time ASCLatest readings for all channels:
SELECT t1.CHANNEL, t1.TEMPF, t1.HUMIDITY, t1.pressureinhg, t1.recordtime FROM templog t1 left join templog t2 on (t1.key=t2.key and t1.recordtime > t2.recordtime) where t2.key is null