Hacking the WH2 Wireless Weather Station Outdoor Sensor – Part 2: Protocol Specification

In Part 1 of this series of posts, I described the process of reverse engineering the WH2 outdoor weather sensor from Fine Offset Electronics. Here’s what the sensor looks like:

The WH2 wireless outdoor sensor transmits on 433.92MHz - sometimes known as the 433MHz or 434MHz band - using On-Off Keying. In normal operation (after a start up period during which the receiving indoor unit attempts to discover the remote sensor(s)) the WH2 transmits a data packet every 48 seconds, as depicted below:

From now on, I’ll refer to these data bursts as packets.

Within each packet of data, binary 1s are encoded as 500 microsecond wide positive pulses; binary 0s are encoded as 1.5 millisecond wide positive pulses. Between each positive going pulse is 1 millisecond of “dead time” during which the transmission is idle. A natural consequence of the pulse-width coding approach taken is that packets can vary in duration for a given number of bits encoded. The pulse-width coding approach is summarised visually below:

Each packet consists of 6 bytes of data - 1 byte of preamble, 4 bytes of payload, and a 1 byte CRC. The packet format is as follows:

In more detail:

  • The preamble is 8-bits long. It consists of all 1s - i.e. 8 x 500us pulses with 1ms dead time between each pulse. The preamble bits allow the receiver to “wake-up” and adjust its automatic gain control (AGC) appropriately before the payload is sent.

  • The first 12 bits of the payload contains the device-type and device-id. These 12 bits differ from one WH2 sensor to the next. They change for a given WH2 sensor when its batteries are changed. This allows the receiver unit to discriminate between multiple WH2 remote sensors operating within its vicinity. I’m guessing that some portion of the 12 bits is dedicated to identifying the device-type (i.e distinguishing WH2 sensors from other sensors using the same protocol) and therefore doesn’t change between WH2 sensors. But I haven’t captured enough packets to determine which bits indicate the device-type and which indicate the device-id. The most-significant bit (MSB) of the 12 bits is always a 0. This allows the receiver to determine the end of the preamble (all 1s) and the start of the payload.

  • The next 12 bits of the payload contain the measured temperature. The temperature is given in tenth’s of a degree Celsius. For example, 25 degrees Celsius would be encoded as the binary representation of 250 decimal. The MSB is a sign bit. A 1 for this bit indicates a negative temperature (eg. < zero degrees Celsius).

  • The next 8 bits of the payload contain the measured humidity. This is encoded as a binary representation of % relative humidity. For example, 65% relative humidity would be encoded as the binary representation of 65 decimal.

  • The final 8 bits of the packet are a CRC-8 over the payload - i.e. over the four preceding bytes. (The preamble is not included in the CRC). The CRC-8 polynomial used is x8 + x5 + x4 + 1.

That’s it for this instalment. In the planned third part of this series of posts, I will explain how to use an Arduino to receive and decode packets from the WH2.