_PLEASE NOTE: this is preliminary, changes will surely be made. If you have information to contribute with, please contact me!_
This thread on a forum for GraphWeather was very helpful. I managed to read it all in spite of my lousy french. Thank's guys!
And big thanks to Kustaa Nyholm, George Warner and Fernando Urbina for your help!
There are a number of weather stations from Oregon Scientific that uses this protocol. I know that WMR100N, WMRS200, RMS300 and RMS600 share the same protocol and work with my software. All of them are low speed USB 1.1 Human Interface Devices, but with a completely closed, vendor-specific application level protocol. I've tried to contact Oregon Scientific about the possibility to get access to the protocol but they haven't even answered my letters.
The nice thing about them being a HID is that it makes it quite easy to communicate with it, and using the HID Manager that came with Mac OS X 10.5 enabled me to get a very simple and robust way of speaking with them. At first I wasn't using the proper callback function so occasionally I got data losses. But with excellent help from Fernano Urbina and George Warner at Apple I understood what was going on, and with George's example code I finally got it stable. Setting up the USB handling and register callbacks for data and device plugin/remove is not even 10 lines of code, excluding error control. But then to actually understand the data from the station requires much more of coding.
###Initialization
To kick-start the station the following initialization string (hex code) must be sent once, at least for WMR100N:
20 00 08 01 00 00 00 00
It is only needed after a reset (or power failure) of the station.
###Report layout
The station sends 8 bytes long USB reports. Of the 8-byte reports, only a few bytes are of interest, and to put together a complete weather measurement, data must be extracted from several 8 byte long USB reports.
A typical 8-byte report looks like this
02 00 47 30 01 0c 01 00
The first byte tell us how many of the directly following bytes are part of a measurement. In the example above there are 2 bytes, so 00 47
is valid parts of a weather measurement. Keep on assembling USB reports like this and you will create a stream of bytes containing weather measurements. Most USB reports contain only one or two relevant bytes, but I have recently found occasional, rare reports with up to 7 bytes of real data, so make sure that you don't assume only one or two bytes.
All measurements are separated by 0xffff
. Earlier, I thought that there was a singe 0xff
between measurements, but that was wrong. I was lead to believe that by my USB handling bug that caused some data to go lost now and then.
Now, the type of a measurement (like rain or wind) is determined in the second byte of the measurement. And the last two bytes of a measurement is a checksum, it should be the same as the sum of all preceding bytes in the measurement. The checksum comes low byte first, then high byte.
An example:
If you have this byte sequence filtered out from the USB reports
... 05 ca 00 ff ff 00 47 08 4f 00 ff ff 00 42 ...
then you can find the 5 byte long measurement 00 47 08 4f 00
in it, where the second byte (47
) indicates an UV Index measurement, third byte is the actual UV index measurement of 8 (wear sunscreen because this is very high!), and fourth and fifth byte is the checksum 004f
in reverse order. Add the first three and check for yourself. And in the first byte, the high nibble contains information about the battery level in the UV device.
###Sensor readings
Following are description of the known data in the different measurements. The content of some bytes are unknown, and there might be misinterpretations as well. It's difficult to be 100% sure when the vendor has decided to be mum. But what I have written here gives readings that matches perfectly with what WMR100N says on its own screen, or what the PC software for WMRS200 reports.
####Date and time
This "measurement" is sent once a minute. Most other readings are sent within one minute but some, like the UV Index, is reported every 73 second, so if you want to be sure you have all data before reporting it or store in a db, wait at least two minute cycles.
Length | 12 | Example: 00 60 00 00 14 09 1c 04 09 01 a7 00 |
---|---|---|
Byte | Data | Comment |
0 | 00 | Flags, see below |
1 | 60 | Identifier |
2-3 | 00 00 | Unknown |
4 | 14 | Minutes: 20 |
5 | 09 | Hour: 09 |
6 | 1c | Day: 28 |
7 | 04 | Month: 04, April |
8 | 09 | Year: 2009 (add 2000) |
9 | 01 | Time Zone: GMT +1 (highest bit 1 if negative) |
10-11 | a7 00 | Checksum: 0xa7 |
Flags in byte 0 | Power and RF signal |
---|---|
Bit 7 (MSB) | 1: power unplugged, 0: power attached |
Bit 6 | 1: low battery level, 0: good battery level |
Bit 5 | 1: RF sync active, 0: RF sync inactive |
Bit 4 | 1: RF signal strong, 0: RF signal weak |
Bit 3 | Unused |
Bit 2 | Unused |
Bit 1 | Unused |
Bit 0 (LSB) | Unused |
####Temperature and humidity
This measurement is sent for each device connected. At least there's the outdoor device and the indoor device build into station itself, plus any extra sensors for temperature and humidity that you have added. Dew point is only reported from the outdoor unit and in whole degrees C only.
Heat index is reported if temperature climbs over 80 F, 26.7 C.
Length | 12 | Example: 20 42 d1 91 00 48 64 00 00 20 90 02 |
---|---|---|
Byte | Data | Comment |
0 | 20 | Flags, see below |
1 | 42 | Identifier |
2 | d1 | Low nibble is device channel number, high nibble: see below |
3-4 | 91 00 | Temperature: (256 * byte 4 + byte 3) / 10 = 14,5 degrees |
5 | 48 | Humidity: 72% |
6-7 | 64 00 | Dew point: (256 * byte 7 + byte 6) / 10 = 10 degrees |
8-9 | 00 20 | Heat index or wind chill, see below |
10-11 | 90 02 | Checksum: 0x290 |
Flags in byte 0 | Battery and temp trend |
---|---|
Bit 7 (MSB) | Unused |
Bit 6 | 1: low battery level, 0: good battery level |
Bit 5 | Unused |
Bit 4 | Unused |
Bit 3 | Unused |
Bit 2 | Unused |
Bit 1-0 (LSB) | Temp trend. 0: stable, 1: rising, 2: falling |
Flags in byte 2 | Smileys and humidity trends |
---|---|
Bit 7-6 (MSB) | Smiley. 0: no smiley, 1: :-), 2: :-(, 3: :-| |
Bit 5-4 | Humidity trend. 0: stable, 1: rising, 2: falling |
Byte 8-9 | Heat Index |
---|---|
Byte 9, high nibble | ? |
Byte 8 + 9 | Valid result in Fahrenheit if > 0: ((256*low nibble(byte 9) + byte 8) / 10 |
####Wind
Wind is measured in m/s, direction in degrees (0-360). Wind chill is only reported for lower degrees.
To do: figure out byte 3
Length | 11 | Example: 00 48 0a 0c 16 e0 02 00 20 76 01 |
---|---|---|
Byte | Data | Comment |
0 | 00 | Battery level in high nibble |
1 | 48 | Identifier |
2 | 0a | Wind direction in low nibble, 10 * 360 / 16 = 225 degrees |
3 | 0c | Unknown |
4-5 | 16 e0 | Wind gust, (low nibble of byte 5 * 256 + byte 4) / 10 |
5-6 | e0 02 | Wind average, (high nibble of byte 5 + byte 6 * 16) / 10 |
7 | 00 | ? |
8 | 20 | ? |
9-10 | 76 01 | Checksum: 0x176 |
Byte 7-8 | Wind Chill |
---|---|
Byte 7, high nibble | 0: ?, 1: windchill, 2: None |
Byte 7 + 8 | NB! Result in Fahrenheit: ((256*low nibble(byte 8) + byte 7) / 10 |
####Pressure
Barometer reading is reported both as absolute and relative. Reading also includes a forecast indicator.
Forecast indicator is a number were 0 is Partly cloudy, 1 is Rainy, 2 is Cloudy, 3 is Sunny and 5 is Snowy.
To do: verify all forecast indicators. Does 4 ever occur?
Length | 8 | Example: 00 46 ed 03 ed 33 56 02 |
---|---|---|
Byte | Data | Comment |
0 | 00 | Unused? |
1 | 46 | Identifier |
2-3 | ed 03 | Absolute pressure, low nibble of byte 3 * 256 + byte 2 |
3 | 03 | High nibble is forecast indicator |
4-5 | ed 03 | Relative pressure, low nibble of byte 5 * 256 + byte 4 |
5 | 03 | High nibble unknown |
6-7 | 56 02 | Checksum: 0x256 |
####Rain
Rain is reported in inches * 10, rain rate in inches/hour * 10. Divide by 10 to get proper values. Or, even better, multiply with 0.254 to get millimeters. :-)
Length | 17 | Example: 00 41 ff 02 0c 00 00 00 25 00 00 0c 01 01 06 87 01 |
---|---|---|
Byte | Data | Comment |
0 | 00 | Battery level in high nibble |
1 | 41 | Identifier |
2-3 | ff 02 | Rain rate: (byte 3 * 256 + byte 2) / 10, in inches/hour |
4-5 | 0c 00 | Rain last hour: (byte 5 * 256 + byte 4) / 10, in inches |
6-7 | 00 00 | Rain last 24 hours: (byte 7 * 256 + byte 6) / 10, in inches |
8-9 | 00 25 | Total rain since reset date: (byte 9 * 256 + byte 8) / 10, in inches |
10 | 00 | Minute of reset date |
11 | 0c | Hour of reset date |
12 | 01 | Day of reset date |
13 | 01 | Month of reset date |
14 | 06 | Year + 2000 of reset date |
15-16 | 87 01 | Checksum: 0x187 |
####UV Radiation
UV Index is reported from the UVN800 sensor. It's an dimensionless value (whole number) from 0 and upwards.
Length | 6 | Example: 00 47 00 05 4c 00 |
---|---|---|
Byte | Data | Comment |
0 | 00 | Battery level in high nibble |
1 | 47 | Identifier |
2 | 00 | Unknown |
3 | 05 | UV Index 5 |
4-5 | 4c 00 | Checksum: 0x4c |