Rain Gauge Project – Raspberry Pi


There are several reasons to move the project code to the RPi:

  • The capability to timestamp the log entries
  • File I/O
  • Internet connectivity

The first step in this phase is to convert the working Arduino code, into code for the Pi, which can be quite straightforward with C|C++ and the WiringPi library. The code can be found here, but the following discusses the changes from the Arduino code.


The first thing I discovered is that the GPIO interrupt is not as responsive as the Arduino because the processor is busy sharing other computer tasks.  I opened up the timing range checks in the interrupt handler function to accommodate:

// global declarations
#define SYNC  600	
void handler() {
    static unsigned long duration = 0;
    static unsigned long lastTime = 0;
    static unsigned int syncSig = 0;
    // ignore if we haven't processed the previous full buffer
    if (bufferFilled == true) return;

    // calculating timing since last change
    long time = micros();
    duration = time - lastTime;
    lastTime = time;

    if (inSync) {
	if (ringIndex < 129) {
	    // store data in ring buffer
	    ringIndex = (ringIndex + 1) % RING_BUFFER_SIZE;
	    timings[ringIndex] = duration;
	    //printf("ringIndex=%d	%d\n", ringIndex, duration);
	    bufferFilled = false;
	    } else {
		// verify end of message
		if (timings[112] < 200 && timings[113] > 1000) {
		    bufferFilled = true;
		} else {
		inSync = false;
		ringIndex = -1;
        } else {
	    if (duration > SYNC - 100 && duration < SYNC + 100) { 
                if (syncSig > 7) {
	            //printf("Received %d consecutive square waves\n", syncSig);
	            ringIndex = -1;
	            inSync = true;
	            syncSig = 0;
	    } else {
	        syncSig = 0;

During test and debug, it became apparent that disabling the interrupt is ineffective.  This seems to be a minor shortfall within the super-helpful WiringPi library, There is a suggested work-around that helps, although not completely; placing printf() statements within the interrupt handler will demonstrate that.  Hopefully a future Python rewrite will overcome this annoyance.


Knowing the time a reading was taken is at the heart of tracking the data.  The Pi reports this quite well.

// global declarations
time_t now;		// current time for timestamp
char timeStamp[40];	// formatted time

    // get time stamp for report once the buffer is filled
    now = time(0);
    strftime (timeStamp, 100, "%Y-%m-%d %H:%M", localtime(&now));

File I/O

The code saves the data into a CSV file as follows:

// global declaration
FILE* fileRain = NULL;			// rain log

    // save this value to the data log file once the rain measurement is properly extracted
    fileRain = fopen("rainlog.txt", "a+");
    if (!fileRain) { 
	printf("File open failure!");
    } else {
	sprintf(outbuff, "%s, %6.2f\n", timeStamp, (d/100));
	fwrite(outbuff, strlen(outbuff), 1, fileRain);

Also include a debug file for unexpected exceptions

// global declaration
FILE* fileDbug = NULL;		// debug log

// track errors for future analysis
void sayDebug(char when[]) {	// ‘when[]’ is the timestamp string

    char dbuff[100]; 
    // save this value to the data log file
    fileDbug = fopen("debug-rainlog.txt", "a+");
    if (!fileDbug) { 
	printf("Debug File open failure!");
    } else {
	sprintf(dbuff,"%s\n", when);
	fwrite(dbuff, strlen(dbuff), 1, fileDbug);
	// report timing information for debugging
	for (int i = 0; i < 113; i+=2){
	    sprintf(dbuff,"%d	%d	%d\n", i, (int)timings[i], 
	    fwrite(dbuff, strlen(dbuff), 1, fileDbug);
	// report all data formated as bytes
	int j = 0;
	char sTmp[100] = "";
	outbuff[0] = '\0';
	for (int i = 0; i < 111; i+=2) {
	    if (j % 8 == 0) {
		strcat(sTmp," ");
	    sprintf(outbuff,"%d", t2b( timings[i], 
                timings[i+1] ) );
	    strcat(sTmp, outbuff);
	//rcat(sTmp, "\n");
	sprintf(dbuff,"%s\n", sTmp);
	fwrite(dbuff, strlen(dbuff), 1, fileDbug);
	fwrite(dbuff, strlen(dbuff), 1, fileDbug);

Redundant Log Entries

The rain gauge transmits about every 15-20 seconds, resulting in plentiful data, to say the least.  The following code limits logging to one entry every 5 minutes.

	// report the results every 5 minutes
	strftime(outbuff, 40, "%M",localtime(&now));
	mod = atoi(outbuff) % 5;
	//printf( "*** Minutes: %s Remainder: %d ***\n", outbuff,
            mod );
	if (mod % 5 < lastMod) {
	    //printf("5 minutes elapsed\n");
	    printf("%s, %6.2f\n", timeStamp, (d/100));
	    // save this value to the data log file
	    fileRain = fopen("rainlog.txt", "a+");
	    if (!fileRain) { 
		printf("File open failure!");
	    } else {
		sprintf(outbuff, "%s, %6.2f\n", timeStamp, (d/100));
		fwrite(outbuff, strlen(outbuff), 1, fileRain);
	lastMod = mod;

Next Steps

  1. Make the data accessible via internet
  2. Rewrite in Python


  • To make use of the GPIO routines offered by the RPi library that may disable interrupts during analysis.
  • There are plentiful examples within the Raspberry Pi community teaching internet connectivity, cloud database updates, etc.


Rain Gauge Project – Arduino


This is a project begun of curiosity and weather-geekiness.  I have been trying to find a reason to get more familiar with the Arduino (actually learn something about it, period), gain more long-term understanding, and hopefully more engagement with the Internet of Things.

I have an Acu-Rite 00614-TX rain gauge that measures the rain well, but doesn’t allow me to track rainfall with the granularity I might like.   That is not a big deal, but gave me enough incentive to learn quite a number of new things.

Major Steps

  1. Learn how to receive and decode the RF transmissions from the gauge
  2. Store results in a database in the cloud
  3. Write a web software application to show full range of statistics from the gauge
  4. Convert the web software to a mobile device app.

The biggest hurdle came immediately – I knew nothing about RF.  I have many, diverse talents, but not this one – yet.

  • Define the problem

I need to learn as much as I need to know about RF, how to read and parse the data, and determine the equipment that I will need.

  • Research possible solutions
    • The local colleges and universities have classes that will be helpful
    • Case studies from the Web
  • Evaluate alternatives and make a plan
    • I want to take classes, yet this project is not really worthy of that time. It might be a excuse to enroll however.
    • Study the web for ideas in the meantime before classes start.
  • Implement and follow up
    • I am a results-oriented guy, needing intermediate feedback to keep my enthusiasm. This means that each project needs to be designed for short term and long term successes.  Or it needs to be a project that can be completed in a weekend.  learning what I want to know is a bigger project.
    • Phase One will be to study the vast information available on the web to learn how to read and decode the RF information using an Arduino. This will be helpful to work out technical details, but may not give good date and time stamp updates needed to extract useful information from the data. Also an external file system will be needed. These requirements may be better served by a Raspberry Pi.
    • Phase Two will be to transfer what I have learned to a Raspberry PI implementation for the database, and internet capability.
    • Phase Three will be the web and mobile software application development. (This may be broken into two deliverables)

Stand on the Shoulders of Giants

It didn’t take much research to discover the great work done by Ray Wang at rayshobby.net.  He has done much of the work that I needed, and his blog was very instructive.  I freely admit to being a “Frankenstein-er”, and by this I mean learning from, and incorporating, pieces of previous work to make a new result.    I repeated Ray’s work, as best I could, to learn the details. Purchasing an RFToy from his website which was instrumental to getting the project underway, recording the RFToy audio out.

Introducing RFToy 1.0

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


Waveform of the transmission

The sequence begins with a series of 4 square waves for synchronizing: HI 570us, LO 650us.  A digital ‘1’ is a HI pulse of 340us and a LO of 270us; digital ‘0’ is a HI of 160us and LO of 450us. There is some fluctuation length of the pulses, so a tolerance of ±75us helps to capture them effectively.  (Notes)

1 340 µs 270 µs
0 160 µs 450 µs
Sync 570 µs 650 µs

Arduino Code

The Arduino code found here (pdf version) will output the full set of data to collect the data to determine the encryption.  My results were similar to Ray Wang’s except the records are only 7 bytes. Furthermore, this rain gauge counts 2 for each click of the seesaw, so the data reported is actually in .02 inches of rain per tip. I was expecting data to count tips, so this is much nicer, though a bit difficult to reconcile when investigating.

This code has a few deviations from the original.  I discovered that if the sync signal is not found in the first 134 elements of the array, decoding will exceed the array boundary, so I limit the search.    I added more printf statements to aid my understanding.  Arduino doesn’t have the nice step-by-step debugging that I have become used to with Visual Studio, etc.  However, having lots of printf() statements in the ISR seemed to collide with, confound, or delay, other interrupts.


The timings listed in the table above were determined by having the Arduino code report lists of the timings, then pasting them into Excel for further study.   Fourteen data sets were used to get average, median, and mode values for proper binary digitizing.  See the worksheet.

Encoding / Decryption

Gathering a large data set is helpful, though there is much redundancy.  Fortunately the data for this rain gauge follows the pattern described in Ray Wang’s blog posts mentioned earlier. The relevant data is reported in bytes 4, 5, and 6. The high-order bits parity) are ignored when parsing for count.

Unintended Lessons Learned

  • How and why to use Arduino interrupts
  • When frustrated, read the manual again
  • A good RF receiver is important – superheterodyne really reduced the noise and cleaned up the signal
  • Although it was disappointing when the imported code didn’t magically work for me, walking through the code, line by line, is a really good way to gain deeper understanding

The Next Step

Convert the Arduino code to the Raspberry Pi.  The Pi will easily allow me to provide date stamping of the log entries, which is REALLY important for me.  Many of the other RPi abilities will come in handy, too.  I’m looking forward to this…