Reverse Engineer Wireless Temperature / Humidity / Rain Sensors — Part 3

Continuing from Part 1 and Part 2, this is the third and last post about how I reverse engineered a few off-the-shelf wireless temperature, humidity, and rain sensors, and used an Arduino to listen to and decode the sensor data. Update: RPi is also supported now! Check the provided programs at the end of this post.

Wireless Rain Sensor

Raw Waveform. There are several different wireless rain sensors on the market. My first target was an Acu-Rite 00875W rain gauge. I bought it two years ago and I don’t know if it’s still available now. This sensor turns out to be a huge pain. The main problem is that I can’t even register a consistent reading at 0 — every time I pop in the battery, I get a different signal that seems to have nothing to do with the previous readings. Also, the 0/1 bit patterns are completely unclear from the captured waveform — there are at least 4 or 5 different wavelengths. Here are two examples of the captured waveforms. They look nothing alike, even though both were captured right after the batteries are popped in and no rain has been detected.


After pulling my hair for a couple of days and finding no clue at all, I decided to give it up and try a different model. This time I bought an Acu-Rite 00899 wireless rain gauge. It’s probably worth explaining at first how the rain gauge works, because it’s quite a clever design. The outside of the transmitter unit looks like a bucket. Underneath the bucket is a plastic seesaw which swings left and right. Basically the rain water drains through the bucket hole onto the seesaw, and creates some motion to be detected.


At the bottom of the assembly is the battery compartment. Remove some tiny screws, the transmitter circuit is finally revealed. At the center is a reed switch, which is normally open but will close if there is a magnet nearby. It is very sensitive to magnetism. So where is the magnet? It’s on the bottom of the seesaw. The way this works is quite neat: the seesaw swings left and right, every time the magnet passes by the reed switch, it triggers a click. By detecting how many clicks there are within a given time, we know how heavy the rain is. Clever!


So let’s see if this rain gauge is easier to tackle. Following the same procedure as before, pop in the battery, power on the RF sniffing circuit, and launch the Audacity recording software, I got a waveform like the one shown in the image below.


Cool, this looks a lot better. I also made sure every time I pop in the battery I get the same waveform. So what patterns do I see here?

  • Each transmission consists of 3 repetitions of the same signal.
  • Every two repetitions are separated by a sync signal defined as 4 squarewaves of roughly 1.2ms wavelength.
  • The bit patterns are: logic 1 is defined by a constant high of 400us followed by a constant low of 200us; and logic 0 is defined by a constant high of 225us followed by a constant low of 400us.

The sync signal is actually very similar to the humidity/temperature sensor I described in the Part 2. The difference is that there is no 2.25ms constant low preceding the 4 squarewaves.

Given the timing data, here is the Arduino program to convert this signal into bits:

Collect and Analyze Data. The transmitter sends a signal every two minutes, so it’s quite annoying that I had to wait two minutes for every reading. To create variations in data, I simply move the seesaw manually. This way I can precisely control how many clicks occurred between two readings. Here is a list of readings I captured. The numbers in the parentheses are the number of clicks since last reading, and the total number of clicks since the program started.

01110010 11111000 11110000 00000000 00000000 00000000 00000000 01011010 (0 / 0 total)
01110010 11111000 11110000 00000000 00000000 00000000 10000001 11011011 (1 / 1 total)
01110010 11111000 11110000 00000000 00000000 00000000 10000010 11011100 (1 / 2 total)
01110010 11111000 11110000 00000000 00000000 00000000 00000011 01011101 (1 / 3 total)
01110010 11111000 11110000 00000000 00000000 00000000 10000100 11011110 (1 / 4 total)
01110010 11111000 11110000 00000000 00000000 00000000 10000111 11100001 (3 / 7 total)
01110010 11111000 11110000 00000000 00000000 00000000 10001101 11100111 (6 / 13 total)

The encoding scheme is quite obvious: the first three bytes are the signature / device ID; the next 4 bytes record the total number of clicks since the transmitter is powered on. The leading bit of each byte is a parity bit (thanks to the lesson I learned from Part 2!) The last byte is for error checking.

This time I looked at the error checking byte more carefully, and noticed some interesting patterns. For example, everything else being the same, the change in its first bit matches the change of the first bit in the second to last byte. This suggest that perhaps the last byte is a parity byte — specifically, bit 0 is the parity of the first bits in all the preceding 7 bytes, and bit 1 is the parity of the second bit in all the preceding 7 bytes and so on. Just eyeballing the numbers, I believe this looks correct.

I felt quite happy that I made the right choice to abandon the first rain gauge which proved to be too difficult to solve. Well, dodging the challenge is not generally recommended, but in this particular case, I have no regret 🙂

Arduino Program and Validation. Here is the Arduino program that listens to the rain gauge and displays the number of clicks onto the serial monitor. The numbers have been validated with the display unit.

Update: the code is adapted to RPi as well, using wiringPi. The code below uses wiringPi GPIO 2 (P1.13) for data pin.


This concludes the three-part series. If you have comments / questions / suggestions, please feel free to leave them below. Thanks!

New: continue to Part 4, wireless soil moisture sensor.

22 thoughts on “Reverse Engineer Wireless Temperature / Humidity / Rain Sensors — Part 3

  • April 20, 2014 at 5:59 am

    Hi Ray,

    I really enjoyed these articles, thanks!
    One day when I return home, I’m going to implement your OpenSprinkler system, but alas am still travelling.
    One other thing I noticed whilst looking through Kickstarter the other day is that there are a couple of similar sprinkler projects out there, but this one mentions how it’ll redefine sprinkler systems with it’s mobile interface and how nothing existed like it 3 years ago, but it’s currently an open kickstarter, but more interestingly how a 12 zone wireless sprinkler controller is “patent pending”.

    Keep up the good work 🙂

  • April 21, 2014 at 4:45 pm

    Tuning the super-regenerative module I bought made a big difference.

    Many of the super-heterodyne receivers seem to be combined 315/433Mhz receivers using a PLL to multiply a lower frequency crystal output. A single-frequency super-heterodyne receiver should be better than the super-regen ones, but I’m not so sure of the multiple-frequency receivers. Can you post the details on the receiver you’re using?

  • April 22, 2014 at 9:21 pm

    Nice article. I appreciate the thoroughness as it’s an excellent exercise in how to go about reverse engineering a protocol.

    Once again, thank you for publishing what you learned. I learned a lot from your posts.

  • July 10, 2015 at 12:25 am

    You rock sir! Thank you for taking the time to put this together. I bought the Acu-Rite rain gauge & RF sensor from Sparkfun and along with your instructions & Sketches, had it talking right away.

  • October 25, 2015 at 1:09 pm

    Hi Ray,

    I am wondering how easy it would be to add an RF433 receiver to Opensprinkler itself. Is it as simple as connecting it to suitable pins/rails and using the RF library (already used for transmission) to read the signals, as in your decoding examples?

    I did note you intended to use this with Opensprinkler so I’m wondering if there was a firmware update in the pipeline to supports this?

    My main interest at the moment for receiving RF433 signals directly on Opensprinkler is to control my central heating. I already use Opensprinkler with a couple of RF433 switches to control the heating and hot water schedules, but what I really want to do is create a demand from each room, and zone the house accordingly.

    After much research I am thinking the simplest and cheapest way to do this is to place an RF433 enabled temp sensor in each room (DIY or off the shelf) add an RF433 receiver to my Opensprinkler DC, use a cheap electrothermic actuator on each station to control the radiators, with the master station as boiler interlock.

    There are of course plenty of RF/WIFI radiator valves around. These typically have an embedded temp sensor with motorised valve, and allow set points to be programmed and temp/status to be read etc. Some run in isolation, have learning/fuzzy control logic, and open window detection etc. However they are generally quite expensive, use batteries, are noisy, and mostly employ 866mhz with closed proprietary protocols. To be honest they seem unnecessarily complex for the average home, and I suspect in practice I could equal their control given my temp sensor would not be right next to the heat source.

    By complete coincidence I happen to have an old weather station with 433mhz temp sensor which I saved from the bin when a friend was emigrating so I can have a play with this.

    One issue I have is driving the electrothermic actuators. They are almost all 240vac or 24vac, the latter being ideal for use with Opensprinkler AC. However in my wisdom I purchased Opensprinkler DC, which upon reflection may have been a mistake (given everything I am driving seems to be 24vac or RF power switches). After much searching I have only managed to find one 12v actuator, and that’s two or three times the price of a typical 24v.

    However these actuators generally use a PTC thermistor to heat an encapsulated wax plug which takes a few minutes to expand and open the valve. Now having read up on PTC thermistors here:

    It seems they can be very tolerant on voltage, for example a thermistor can be chosen which heats up to pretty much the same temperature on 240v or 120v, and being a resistive load I’m thinking DC might also work (one stockist does state 24vdc can be used but that might be a mistake).

    So I’m wondering if a typical 24vac actuator might work with 12vdc. If the thermistors threshold can indeed be reached with 12vdc (the boost feature should help here), then it might work. On the other hand it might just fall back after the boost, or it might hold, but then take twice as long to open the valve, or perhaps only open half way. What are your thoughts?

    The following link is to the spec for a common Honeywell MT4 valve actuator.

    The particular model we might use on a radiator would be an MT4-024LC-NC (4mm travel, 24vac, low current, normally closed). This has an inrush of about 200ma for 500ms then drops to 50ma and takes about 4mins to open fully. How these characteristics might change if it could be driven from Opensprinkler DC is something I think you would be best to speculate upon.

    Of course I could use relays with a separate 24vac power supply, or use 240vac actuators with an RF power switch, or just purchase an Opensprinkler AC, but driving them direct from Opensprinkler DC is my first choice.

    I did wonder if it was possible (with a little modification) to drive an Opensprinkler AC expansion board (with a separate 24vac supply) from a DC controller.

    Anyway in summary I have two questions:

    1) How easy is it to add an RF433 receiver to Opensprinkler DC to monitor temp sensors, and is this in the pipeline?

    2) What are your thoughts on using Opensprinkler DC to drive these 24vac electrothermic actuators?

  • November 1, 2015 at 8:52 pm

    Hi Brett, I read your comments out of order, so if I understand correctly, OpenSprinkler DC is working fine with your actuators. Regarding 433 receiver, it’s pretty straightforward: just connect the three pins of the receiver to VIN (+5V), GND, and one of the hardware interrupt pins (I think INT2 is available in the pin out area). The reason to use a hardware interrupt pin is that it gets high priority (otherwise you can also just use any pin and set up pin change interrupt, but pin change interrupts are often slower). Then the rest is software change.

  • December 5, 2015 at 3:16 pm

    Thank you for your job and for sharing it!. I am looking for a solution for cheap and ready to go temperature sensors.
    My concern is what happens when more than one sensor transmit at the same time or their transmission overlaps.

    • December 7, 2015 at 8:48 pm

      If they transmit at exactly the same time, you will have a RF ‘collision’ issue. However, given that each signal is fairly short, the chance of collision is very small.

  • December 5, 2015 at 3:27 pm

    The temperature sensors I have only allows up to three channels.
    How many channels allow the one you chose?

    • December 7, 2015 at 8:48 pm

      I think it’s probably three too. But how does this matter? You can change the channel code in software.

  • December 8, 2015 at 8:12 am

    If the sensor has only three channel options then if you have more than three sensors, at least there will be two with the same channel identification. And you will receive the information coming from two sensors with the same sensor identification and I do not see how to identify from which sensor is coming from.

    Is there any other identification information in the message other than the channel number?

    • December 17, 2015 at 3:34 pm

      I am not sure if there are other identification information other than channel number. You may have to get two, set them to the same channel, and see if there is anything you can use to tell them apart.

  • June 8, 2016 at 7:22 pm

    Hey there, got a quick question for you. I have my serial monitor showing the binary code for the number of clicks. But, instead of decoding it, and displaying the number of clicks, I am getting “ssss” after my last bit. Any ideas?

  • June 8, 2016 at 7:30 pm

    After taking a harder look at the code I realized the “s” is relating to the sync square waves portion of the code. Do you possibly have a snippet of code that decodes and displays the number of clicks in the monitor?

  • November 27, 2016 at 1:05 am

    Thanks Ray. This really helped get my curiosity fired up and started learning how to read RF and hack my rain gauge.

  • January 15, 2017 at 10:11 pm

    Is it possible to take both sets of code from Part 2 and Part 3 combine them two rest the 2 sensors. Either with 2 receivers on two interrupts or one receiver with code to know what is coming back. Put it on a arduino with a wifi shield so that I can send mqtt messages to the mqtt server for both temp, humidity and rain accumulation updates.

    I use Home Assistant and this would work perfect but I don’t want tie up two arduino.

    • January 18, 2017 at 3:01 pm

      Should be possible. I’ve never tried it myself, but I don’t see why they can’t be combined.

  • February 17, 2017 at 12:40 am

    Hi Ray,
    I have just purchased an Acurite 899 sensor. I would like my Pi 3 to receive and decode the 433 mhz signals so that
    I can email myself the rain amounts and adjust my sprinklers remotely. I am trying to compile your supplied code
    on my raspberry but am having little luck. My programming experience is with PLC’s and this is my first crack at
    c++ code. I have installed wiringPi on my raspberry and I tried to compile your code as follows:

    g++ rain_display.cpp -o rain_display
    This generates the following error:
    /tmp/cct4pZlv.o: In function `handler()’:
    rain_display.cpp:(.text+0x100): undefined reference to `micros’
    /tmp/cct4pZlv.o: In function `loop()’:
    rain_display.cpp:(.text+0x4dc): undefined reference to `delay’
    rain_display.cpp:(.text+0x510): undefined reference to `wiringPiISR’
    /tmp/cct4pZlv.o: In function `main’:
    rain_display.cpp:(.text+0x554): undefined reference to `wiringPiSetup’
    rain_display.cpp:(.text+0x590): undefined reference to `wiringPiISR’
    collect2: error: ld returned 1 exit status

    Can you please help me to get the code compiled properly?


  • February 17, 2017 at 11:14 am


    I added -lwiringPi to the compile command and am good to go. Thanks for providing the code.


  • June 4, 2017 at 3:47 am

    Hi, thanks for the code. Also works on an ESP8266 with suitable pin changes defined for the interrupt source. Note for those using millimeters instead of inches, one ‘click’ is equivalent to 0.254mm. (although the display unit rounds this off)

Leave a Reply