My first step was to figure out the transmitter's frequency. Using a spectrum analyzer, I saw that it sent a signal at 433.92 MHz once every 12 seconds, using what appeared to be on-off keying. I also searched FCC filings using the Maverick's FCC ID of N9ZMAV221 (http://transition.fcc.gov/oet/ea/fccid/, or just http://www.fcc.io/N9ZMAV221) which turned out to be extremely useful. In addition to verifying what I saw on the spectrum analyzer, I was able to download schematics and all sorts of other helpful information about the transmitter.
Now that I knew the frequency, I needed to capture some data. I had some spare 433.92 MHz transceiver modules made by Radiometrix at the office, so I hooked one up and captured the data coming out the module's /CD pin (Carrier Detect, low-true) with my Saleae logic analyzer.
The first thing I noticed is that the same message repeats 4 times, so it's got some redundancy built into the protocol.
Zooming in a bit, I saw that each message looks like 13 bytes of Manchester coded data at 2kbps, with an eight-pulse preamble (each pulse is about 230uS wide, with 5ms between pulses). This seemed like it would be reasonable to decode, so I got to work collecting some controlled data.
The Maverick thermometer has two probes and two buttons.One button controls whether the display is in Celsius or Fahrenheit, and the other is labeled RESYNC. To get all the data I needed, I would need to take some samples with each probe at a different temperature, some with each at the same temperature, and some with one or the other probe (or both) disconnected. I also would want to collect data with the display in both Fahrenheit and Celsius modes to see if the units are transmitted with the temperature. I assumed that the existence of a RESYNC button meant that there must be some kind of synchronization command at startup, so I also wanted to collect the first message sent upon startup and the first after pressing RESYNC.
After collecting all of this data and discarding the redundant messages, I converted the Manchester coded data into hexadecimal bytes (MSB first, falling edge is binary 1) and put all of it into a table.
Now that all the data was aggregated, I could start looking for patterns. The first thing I noticed was that the only digits (hex nibbles) used in any of the transmissions were 0x5, 0x6, 0x9, and 0xA.Interestingly, 0x5 and 0xA are complements (0101 and 1010), as are 0x6 and 0x9 (0110 and 1001). This gave me the idea that the information might be coded in nibbles rather than bytes, so I rearranged the table:
Next, I tried to figure out how the data was organized. The first 8 nibbles are common to all transmissions, with the exception of the data captured immediately at startup which has different values for #6 and #7. I assumed that the first 6 nibbles are a fixed header and that the next two have something to do with signaling a startup to the receiving unit.
To find the actual temperature data, I first examined the rows where the probes had identical temperature values (82F/82F and NC/NC) and looked for a repeating pattern. I found it in nibbles 8-12 and 13-17. To figure out which probe was which, I looked to see which set of nibbles matched across the two rows where Probe 1 was at 90F. Nibbles 8-12 were the same when the Probe 1 temperatures were the same, so I deduced that Probe 1 is represented in nibbles 8-12 and Probe 2 is represented in 13-17.I wasn't immediately able to see any patterns in the remaining data, so at this point I got stuck. However, I had found all the data that I needed for my project, so I decided to just ignore the last 8 nibbles for the time being. Better to be done than perfect. I assume that there is probably a checksum in there at the end, and maybe something related to the RESYNC button. There might be some kind of counter which increments with each transmission so the receiving unit can tell if it has missed data. In the future, I may revisit this by taking a long capture and seeing if there are any patterns that become clear when I compare sequential transmissions.
Anyway, now that I knew where the data was located, I had to figure out how to decode it. The first thing I checked was whether the state of the Celsius/Fahrenheit button makes any difference. I saw that the data was the same for Probe 1 at 84F and Probe 2 at 28C, so I concluded that the thermometer does not differentiate between Celsius and Fahrenheit when sending data. I also checked to see how the thermometer behaves when a probe is disconnected, and saw that in these cases the sequence transmitted is always 0x55555.
Next, I needed to decode the actual temperature values. To start, I copied the probe 2 data into a new table and sorted it by temperature, being sure to list temperature in both C and F in case one was more helpful than the other.
Two things jumped out at me right away. First, it appears that the transmitted data is natively in Celsius, and that the Fahrenheit value is just a conversion. This is clear because the data for 82F and 84F are exactly the same, and they both convert to 28C. Second, it appears that #13 is the most significant nibble and #17 is the least significant nibble.
Now, remember that each nibble is one of four hex values: 0x5, 0x6, 0x9, or 0xA. This means that data is being sent using a base-4 (quaternary) number system, and that the 5 digits can represent 45 = 1024 possible temperature values. The next step I took was to figure out which hex values correspond to which quaternary digits. To do this, I started by comparing the values for 30C and 31C, which are sequential.Going from 30C to 31C, nibble #17 changes from 9 to A and nibble #16 is unchanged – therefore A must follow 9 sequentially. I unfortunately didn't collect any data for 29C or 31C, so my next step was to compare 28C to 30C. Moving from 28C to 30C, nibble #17 changes from 5 to 9 and again nibble #16 is unchanged. Therefore, 9 must follow whichever number comes after 5 – and the only remaining possibility for that number is 6. I now had my conversion from hex to quaternary.I rewrote the probe 2 temperature table with quaternary digits, and then converted them from base-4 to base-10 (decimal).It appears that the decimal values have a fixed offset of 532 from the Celsius temperature, so I just subtracted 532 from the decimal value to complete the conversion. If the base station receives a temperature of -532C, it means that the probe is disconnected.
To verify this, I manipulated the Probe 1 data in the same manner, and got valid results for that as well.
So, to summarize:
- The thermometer transmits 4 identical messages every 12 seconds at 433.92 MHz, using on-off keying and 2000bps Manchester encoding, with each message preceded by 8 carrier pulses 230uS wide and 5ms apart.
- Each message consists of 26 nibbles (104 bits total), which can each only have the value of 0x5, 0x6, 0x9, or 0xA.
- Assuming MSB first and falling edge = 1.
- The message is organized as follows:
- Nibbles 0-5: Fixed header 0xAA9995
- Nibbles 6-7: 0x6A upon startup, 0x59 otherwise
- Nibbles 8-12: Probe 1 temperature
- Nibbles 13-17: Probe 2 temperature
- Nibbles 18-25: Unknown right now, TBD
- Temperature data is sent as quaternary-encoded Celsius with an offset of +532.
- Hex 5 = Quaternary 0
- Hex 6 = Quaternary 1
- Hex 9 = Quaternary 2
- Hex A = Quaternary 3
- To convert to Celsius, first convert the 5 hex nibbles into the 5 digits of a quaternary number. Then, convert the quaternary number to decimal and subtract 532.
- If a probe is disconnected, the probe temperature is sent as 0x55555.