Weather Station Project

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:

Arduino Implementation


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:

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

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
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.

The code can be viewed here:

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:

I also calculate the dewpoint using a python script from, 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 ASC

Latest 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


RIT to Unveil Major Commitment to Hockey Arena Campaign Tonight – RIT News


RIT to Unveil Major Commitment to Hockey Arena Campaign Tonight – RIT News. 

Bill Destler, president of Rochester Institute of Technology, will take to the ice of Frank Ritter Arena tonight to announce details of the latest and most significant gift to Tiger Power Play—the Campaign for RIT Hockey. His message will include an update on the overall status of fundraising activities for the new facility.

Help Them See It

YouTube Preview Image
(Embedded video:

I love From the Earth to the Moon, especially this episode (Spider).  This scenario demonstrates something I run into with software development all the time.  Frequently, the engineers or developers working on a product come up with novel or unorthodox ideas that project leads are reluctant to go along with.  In those situations, sometimes it’s best to just hammer out a prototype and demo the idea.

Just as happens in the video, sometimes work on an “unauthorized” prototype should be done outside of normal paid time, if for no other reason then as protection in case it doesn’t fly.  It’s also important to understand that the lead must also sell the idea to his or her superiors, so even if you convince the lead, it may not go anywhere.  I would imagine that the manager here had a hard time selling this to NASA.

I’m not saying, “It’s easier to ask forgiveness than to ask for permission”, but if done correctly, helping them see it can produce better software and make a developer a hero in the process.

Image Embedding in QR Codes

A neat feature of QR Codes is that they have error correction built in, so even if a fairly large portion of the code has been obscured or destroyed, most readers can still read it.  Some QR Code creators out there will even let you tweak the error correction level (less error correction means smaller codes).

Since this correction exists, you can add an image to a code and the code will remain completely functional.  I just found a page which will do this all in one shot for you; check it out: Sure, you can just create a code and paste an image yourself, but this is a bit easier.

The site is in German, but it’s still easy to figure out what’s going on.  The code to the left is a shortened URL which will redirect to the ShareMyApps market page.  ( ->  The larger code created by the full link works fine, but less data in a QR code is usually better.  The shortened URL could also be used to track scan-through rates (I may have made up that term).

As QR Codes are popping up everywhere, this is one of the many ways to increase scan-through rates for codes; it could also be used to hint at the purpose or content of the code.

First Android App

I finished my first android app; Ryan mentioned that he wanted a simple app to email a list of his installed apps to friends.  I got something basic up and running pretty quick, but then tinkered with it to add checkboxes so you can pick and choose apps to send around.

I’m still not happy with the actual app list that comes back; it’s still showing a few system apps but I wanted to get it out there.  I’m also very bad at graphic design, so for now it just has a cheezy icon.  If anyone wants to make me a nicer icon, let me know!  The app is called ShareMyApps; you can search for it or scan the enclosed QR code.

Update (v 0.8): I’ve already released an update; I found the magic flag I was looking for to filter out any apps that came on the original system image, so now it should only display apps that you have installed yourself.

Market QR Code

Facebook, Twitter, and finger

I was reading about stack buffer overflows, then the Morris Worm, which got me reading about the *nix finger command;

Very quick background for non unix nerds- finger is a command to find out who is logged into a machine, to get their contact info, and to read their ‘Plan’, which is like a user’s status.  It was handy when working in the CS labs during college to see who was in the labs.

Anyway, I found a newsgroup post from the creator of finger talking about how and why he created it;  it’s an iteresting article in general, but what stuck out for me was the following:

Some people asked for the Plan file feature so that they could explain their absence or how they could be
reached at odd times, so I added it.  I found it interesting that this feature evolved into a forum for social commentary and amusing observations.

So, it turns out people were updating their status way back in the ’70s, and we’re now seeing the same sort of evolution (or devolution depending on who you ask) of away messages, statuses, and tweets.  Of course, status updates and tweets today are severely lacking in ascii art.

     _    ___  ____ ____
 ___ |\/\ |  \ |_ _\|_ _\
|___\|   \| . \  ||   ||
     |/v\/|/\_/  |/   |/
 ___________________          _-_
 \==============_=_/ ____.---'---`---.____
             \_ \    \----._________.----/
               \ \   /  /    `-_-'
          /____          ||

Rochester roulette

Rochester roulette:

The decision making process used when deciding whether to dress for warm or cool weather during Spring and Autumn in Rochester, New York due to the swings in temperature during those seasons.

Usage Example:

Person 1: “Why are you wearing shorts and a t-shirt?  It’s only 45° outiside?”
Person 2: “I played Rochester roulette this morning and lost.”

GPS Experiments

I bought a GPS data logger this week; it also communicates with my phone, so I can use some location based services.  The datalogging function is neat; I haven’t tried to tag any photos yet, but I’ve been leaving it on and tracking where I’ve been.  Here’s my day:

View Larger Map

If you zoom in, you’ll see that the times when I was indoors for long periods of time it gets really jumpy; all things considered, I think it’s working pretty well.  Maybe in the future I’ll post more interesting/useful maps 🙂

Charity Shenanigans

I saw the following on Facebook this morning:

Starbucks goes (RED).

Join us for World AIDS Day. On Dec 1, buy any hand-crafted beverage and we’ll give 5¢ to the Global Fund to help save lives in Africa.

5¢ is better than nothing; but really, this campaign will help Starbucks more than it will help AIDS research-  maybe a better campaign would be the following:

On Dec 1, skip Starbucks, and give the $5 you would spend there to the Global Fund to help save lives in Africa

Here’s the link where you can donate: