Rtl_433 is a great program. I have it running on an RPi with an RTL-SDR logging the temperature readings from the sensors for a couple of AcuRite thermometers I have, plus whatever random sensors I pick up from my neighbors, plus a tipping rain gauge I have.

It makes it really easy for DIY projects like the aforementioned raid gauge. To illustrate this I'll give some details about my rain gauge project.

This is the rain gauge I'm using [1]. It's got a reed switch that gets momentarily closed by a magnet on the tipping part whenever it tips. I hooked that up to an GPIO on a cheap microcontroller (an ATTiny 85 [2]) which counts the tips. Also connected to the microcontroller is a 434 MHz transmitter [3]. This is all powered by 3 NiMH rechargeable batteries in series.

That transmitter is remarkably simple. It just contains an oscillator at ~434 MHz that can be turned on and off by the microcontroller.

My send message function takes a byte string and sends it by turning the transmitter one for 1.5 ms, then off for 1.5 ms, and then for each bit off the message sending it as follows: for a 1 it turns on the transmitter for 0.4 ms then off for 0.6 ms, and for a 0 it turns on the transmitter for 0.6 ms then off for 0.4 ms. There is no gap between bits.

The data sent is the string "TZS\4Count:" and then a two byte count.

Here's a config file for rtl_433 that tells it how to recognize those messages and display the count:

decoder {
name = TZS Rain Gauge,
modulation = OOK_PWM,
short = 416,
long = 612,
reset = 1452,
gap = 0,
tolerance = 0,
sync = 1484,
bits = 96,
match = {24}0x545a53,
get = Tips:@80:{16},
}

I really should have included some kind of checksum, but for now I'm relying on the fact that the count should never go down (except when I change batteries) and should not go up too fast. With those constraints I should be able to look at a series of messages from the rain gauge and figure out which ones were corrupted by interference.

I got information on how fast the count might go up here (I'm in Washington) from the Washington State Department of Transportation's Hydraulics Manual [4]. It gives a formula for calculating at various places what would be an N year amount of rain over an M minute interval. E.g., how much rain over 5 minutes would be a once in a 100 year kind of event? (This is of interest to the DoT for things like calculating drainage requirements for roads).

Here's the Arduino code for the transmitting to show how straightforward it is:

#define CYCLE 1000
#define W_0 600
#define W_1 400

#define INTRO_1 1500
#define INTRO_0 1500

#define OUTRO_1 1500

void report(int pin, unsigned long count)
{
char msg[16] = {'T', 'Z', 'S', 0x04, 'C', 'o', 'u', 'n', 't', ':'};
msg[10] = (count >> 8) & 0xff;
msg[11] = count & 0xff;
send_msg(pin, msg, 12);
}

void send_msg(int pin, char * bp, int n)
{
intro(pin);
send_bytes(pin, bp, n);
//outro(pin);
}

void send_bytes(int pin, char * bp, int n)
{
while (n-- > 0) {
int b = *bp++ & 0xff;
int m = 0x80;
while (m != 0) {
int w = (b & m) ? W_1 : W_0;
digitalWrite(pin, HIGH);
delayMicroseconds(w);
digitalWrite(pin, LOW);
delayMicroseconds(CYCLE-w);
m >>= 1;
}
}
}

void intro(int pin)
{
digitalWrite(pin, HIGH);
delayMicroseconds(INTRO_1);
digitalWrite(pin, LOW);
delayMicroseconds(INTRO_0);
}

void outro(int pin)
{
digitalWrite(pin, HIGH);
delayMicroseconds(OUTRO_1);
digitalWrite(pin, LOW);
}

For completeness, here is the rest of the code:

#include <avr/interrupt.h>
#include <Adafruit_SleepyDog.h>

/* RST |1 8| Vcc
SERIAL 3 |2 7| 2 LED (SCK)
RADIO 4 |3 6| 1 TIP (MISO)
GND |4 5| 0 (MOSI)
*/

#define TIP 1 // physical 6, PCINT1
#define RADIO_TX 4 // physical 3
#define SERIAL_TX 3 // physical 2
#define LED 2 // physical 7

volatile unsigned long int_count = 0;
unsigned long last_interrupt_time = 0;

void setup() {
pinMode(TIP, INPUT_PULLUP);
pinMode(LED, OUTPUT);
pinMode(RADIO_TX, OUTPUT);
pinMode(SERIAL_TX, INPUT_PULLUP);

cli();
GIMSK |= 0b00100000; /* enabled pin change interrupt */
PCMSK |= 0b00000010; /* PCINT0 */
sei();
}

ISR(PCINT0_vect) {
++int_count;
}

void loop() {
int led = 1;
unsigned long tip_count = 0;
unsigned long tip_int_count = 0;
unsigned long old_int_count = 0;
int silence_time = 0;

while (1) {
int slept = Watchdog.sleep(1000);
int changed = 0;
if (int_count != old_int_count) {
old_int_count = int_count;
} else {
if (old_int_count != tip_int_count) {
++tip_count;
++changed;
tip_int_count = old_int_count;
}
}
digitalWrite(LED, 0);
led = 1 - led;
if (changed || ++silence_time >= 30) {
report(RADIO_TX, tip_count);
silence_time = 0;
}
}
}

(The code mentions an LED, but that was just used during prototyping. I did not include the LED on the deployed rain gauge).

Battery life was 285 days on the first set of batteries, and 219 on the second set. I haven't yet went over my records to see if it was more rainy during the second set's deployment.

[1] https://www.argentdata.com/catalog/product_info.php?products...

[2] https://www.digikey.com/en/products/detail/microchip-technol...

[3] https://www.sparkfun.com/products/10534

[4] https://wsdot.wa.gov/engineering-standards/all-manuals-and-s...