Oregon WMR 300 USB Protocol

Since no open-source software or other information about the Oregon WMR 300 USB protocol was around, I had to test and decode every single packet. Find out the detailed USB protocol here.

In case you missed the first step of this series, go back to the first article Oregon WMR 300 on a Raspberry Pi.

Once you set up the low-level C++ communication with the base station, we are ready to decode the USB protocol data!

Overall structure of the WMR 300 USB protocol

As mentioned in the previous article, each line retrieved via USB consists out of 64 hex-encoded bytes. Please note that we are using zero-based indices in order to be able to implement it easier later on!

The general set-up of the data packet can be specified as following:

Bytes
012345678 to length-1lengthlength+1 to 63
package typedata lengthUTC datechannel ID[varying according to data packages, see below, depending on length. first live data, then day and month high/lows for each field are sent.]checksumpadding with 00

First of all, it turns out, the WMR 300 sends different data package types via USB which is specified by byte 0, namely

  • 0xD3 packages contain temperature, humidity and dewpoint data, with a length of 0x3D
  • 0xD4 packages contain wind data, with a length of 0x36
  • 0xD5 packages contain rain data, with a length of 0x28
  • 0xD6 packages contain air pressure data, with a length of 0x2E

Byte 1 contains the overall data package length, starting from 0 to the checksum field. That means, a length of 0x3D (61) means that the checksum will be on byte 60, and all other following bytes will be 0x00.

Byte 2 through 6 contain the base stations UTC timestamp, encoded as followed:
Bytes
...23456...
...years since 2000, e.g. 0x0F for 2015monthdayhourminute...

Byte 7 contains the channel, that is a number from 0x00 to 0x09, because the WMR 300 supports up to 8 sensor stations connected plus one base station. This is why the channel 0x00 announces data coming from the base station (that is, inside temperature, humidity and dewpoint as well as air pressure).

The following bytes start with the current weather data, followed by historical daily and monthly high/lows together with their UTC dates. Since this is mostly irrelevant for sending live data, this blog entry won't explain how to decode them. You can, however, find information about the extended WMR 300 USB Protocol Information here.

Representation of numbers

Numbers are stored over 1 to three bytes in big endian byte order. Negative numbers are stored via the Two's complement, that is if the first byte starts with F it is a negative number. Let's suppose we got a hex-notation style line from our C++ program developed in the earlier article. The following PHP snippet demonstrates on how the data can be accessed.

[code lang="php"] $line = 'D3 12 34 56 78 ... 00 00'; $bytes = explode(' ',$line); function getBytes($bytes,$start,$len) { // invalid parameters specified- out of range if(($start + $len) < count($bytes)) return 0; // concat the relevant bytes $s = ''; for($i = $start;$i<$start+$len;$i++) { $s = $s.$bytes[$i]; } // handles when invalid data is sent. this happens when the sensor is disconnected! $failed = '7f'.str_repeat('ff',$len-1); if(strpos($s,$failed) !== false) { return false; } // handle negative numbers $sub = 0; if($s[0] === 'f') { $sub = pow(2,8*$len); } return hexdec($s) - $sub; } var_dump(getBytes($bytes,0,1)); // 211 [/code]

This means we can extract a date with the following function:

[code lang="php"] function getDate($bytes,$start) { $y = '20'.str_pad(getBytes($bytes,$start++,1),2,'0',STR_PAD_LEFT); $m = str_pad(getBytes($bytes,$start++,1),2,'0',STR_PAD_LEFT); $d = str_pad(getBytes($bytes,$start++,1),2,'0',STR_PAD_LEFT); $h = str_pad(getBytes($bytes,$start++,1),2,'0',STR_PAD_LEFT); $mi = str_pad(getBytes($bytes,$start,1),2,'0',STR_PAD_LEFT); //var_dump($y,$m,$d,$h,$mi); return strtotime($y.'-'.$m.'-'.$d.' '.$h.':'.$mi); } [/code]

D3 package: temperature, humidity and dewpoint

Bytes
012 to 6789101112
D33DY-m-d H:i0 =in, 1 = outtemperature*10 [°C]humidity [%]dewpoint*10 [°C]

D4 package: wind average, gusts and directions

Bytes
012 to 6789101112131415
D436Y-m-d H:i0 =in, 1 = outgust speed*10 [m/s]gust dir [°]avg speed*10 [m/s]avg dir[°]

D5 package: rain rate, sums and total rain

Bytes
012 to 6789101112131415161718
D528Y-m-d H:i0 =in, 1 = out??rain 1h*100 [in]??rain 24h*100 [in]??rain since reset*100 [in]rain rate*100 [in/h]

D6 package: air pressure and height

Bytes
012 to 67891011
D62EY-m-d H:i0 =in, 1 = outpressure*10 [mb]pressure at sea level*10 [mb]

For further development, I decided to share the source code that I am using to upload the data from my Raspberry Pi to the SQL database. You can access the folder here.

Creative Commons License

WMR 300 PHP Decoder by Zahlii is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Leave a Reply