Introduction
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.
Discussion
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 { bufferFilled=false; } inSync = false; ringIndex = -1; } } else { if (duration > SYNC - 100 && duration < SYNC + 100) { syncSig++; if (syncSig > 7) { //printf("Received %d consecutive square waves\n", syncSig); ringIndex = -1; inSync = true; syncSig = 0; } } else { syncSig = 0; } } return; }
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.
Timestamp
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); fclose(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], (int)timings[i+1]); 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); j++; } //rcat(sTmp, "\n"); sprintf(dbuff,"%s\n", sTmp); fwrite(dbuff, strlen(dbuff), 1, fileDbug); sprintf(dbuff,"<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>\n"); fwrite(dbuff, strlen(dbuff), 1, fileDbug); } fclose(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); fclose(fileRain); } } lastMod = mod;
Next Steps
- Make the data accessible via internet
- Rewrite in Python
Why?
- 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.