I'm very new to the wonderful world of ardunio and kicking myself for not coming by them sooner. My background is in environmental monitoring research, which has inspired me to build my own weather station (as a starting system). I've got a strong work history with Campbell Scientific and CRBasic (which I'm starting to think is actually hindering me with coding here).
So far, I've got a smart temperature probe, soil moisture and a NDrive switch (controlling a 12V pump, trigger by the soil moisture probe - for my vegie patch [why not make it work for me, eh?]) all working without a problem. I've also built a wind sensor that looks to be okay as well and should be easy to monitor.
What's killing me is the rain gauge!
Looking around the net, I really liked the look of using a flip flop (which I am about as ignorant of as I am C++). I've bought a flip flop (74HC73N) and search the net, including this forum, for code that I can make sense of and try to convert for my set up.
Please let me know where I'm going wrong! *note it's a 0.7ml per tip bucket and I'm hoping that when it reads a tip, it just reads "0.7" otherwise "0.0" (which I haven't got in the code as yet - is the lack of an 'else' option where I'm going wrong? I was planning to write it in once the first part worked).
Moth_NA:
Please let me know where I'm going wrong! *note it's a 0.7ml per tip bucket and I'm hoping that when it reads a tip, it just reads "0.7" otherwise "0.0" (which I haven't got in the code as yet - is the lack of an 'else' option where I'm going wrong? I was planning to write it in once the first part worked).
This is how rain gauges with a tipping bucket usually work:
On the movable part of the tipping bucket somewhere there is a little magnet connected.
And each time the bucket is tipping over, the magnet will close a "reed contact" for a very short time.
You can use a microcontroller to count how many times the magnet closes the reed contact. With an Arduino UNO you could use the hardware interrupt on pin 2 or 3 to do so.
I've actually gone with a bought rain gauge which I've cut away from the simple logger it came with. It does use the reed / magnet set up.
I've seen code using the interrupt, but heard that it might miss pulses. For instance, I might have it sit on the wind speed sensor for a longer period and shoot through the rest of the code period periodically, to get sensible wind data (I'm in two minds whether I should put the wind sensor on the nano I've just bought instead).
At least with the flip flop I could be sure that I catch every pulse.
But thanks for the link - I'll check it out this evening!
I've been building a weather station and while I had the rain gauge ready first, I just knew it was going to cause me issues and so it's the last one to get working. Initially, I liked the idea of using a flip-flop as RAM, but I was unable to get it working or find answers to result it.
Instead, what I have found is the interrupt option. The method seems easy enough and the principle simple... Alas I couldn't get it to work. So I looked around and tried the code written by others. Only one actually recorded anything, but it didn't make sense. The initial reading is '1' and then with tip, it goes up most often (but not always) by '3'.
The cheap bucket I've got I've calibrated to 0.74ml per tip (the meter it came with stated 0.79ml). I've not adjusted this code to suit the tip measure yet.
Here's the code - thanks for your help. (the loop is just to test it - I've not started compiling all the sensor codes together as yet).
Moth_NA:
Instead, what I have found is the interrupt option. The method seems easy enough and the principle simple... Alas I couldn't get it to work. So I looked around and tried the code written by others. Only one actually recorded anything, but it didn't make sense. The initial reading is '1' and then with tip, it goes up most often (but not always) by '3'.
First, here is a corrected version of your code:
#define INTERRUPT_INPUT 2
volatile int pulse_counter_ISR;
int pulse_counter;
void setup()
{
Serial.begin(9600);
// For noise suppression, enable pullup on interrupt pin
digitalWrite(INTERRUPT_INPUT, HIGH); // activate internal pull-up, so normally input is HIGH
attachInterrupt(INTERRUPT_INPUT - 2, interrupt_handler, FALLING); // count on FALLING edge
}
void loop(){
noInterrupts(); // disable interrupts
pulse_counter=pulse_counter_ISR; // copy volatile variable into normal variable
interrupts(); // enable interrupts
Serial.print(pulse_counter);
Serial.print("\n\r");
delay(1000);
}
void interrupt_handler()
{
pulse_counter = pulse_counter + 1;
}
About interrupt handling:
If you want to access the same variables from an interrupts handler and from normal code, the variable MUST be declared "volatile".
And if you access a 'volatile' variable which is more than 1 byte in size or which has to be changed from normal code (i.e. reset to 0), you would have to disable the interrupts while dealing with volatile variables, then enable interrupts again as quickly as possible.
Try the corrected code.
If the reed contact bounces, which is rare with reed contacts but could happen, the interrupt handling routine must get some additional software debouncing logic.
Edit: Sorry, wrong code! I forgot to fix the interrupt handler.
See corrected version of the interrupt handler function in reply #15.
Thanks for the code fix and background on the code - I am very much a noob with C++ (most CRBasic for Campbell Scientific met stations in my background).
Hi AWOL and Groundfungus,
Thanks for the info on Debouncing. Again, total noob, but I'll research it and see what works best for the rain gauge!
groundfungus:
Put a 0.1uf cap across the switch to hardware debounce it.
I cannot recommend that.
Although reed switches are wearing parts that may need replacement from time to time, i.e. reed switches for "indoor use" that are used in a rain gauge outdoors outside their temperature and humidity specification, I'd avoid doing so, because the additional current spikes on the contacts will wear them out before their time have come normally.
In case of bouncing problems with reed contacts I'd rather use software debouncing and avoid early failure due to more than usual contact wear using capacitor discharge for the reason of debouncing.
Moth_NA:
Thanks for the info on Debouncing.
Do you then see any bouncing using my code?
Rain gauge bucket 10 times tipping, counting more than 10?
What do you find with your testing?
I saw the volatile code in a couple examples I saw around the place but didn't know what it was used for.
However, when I monitored it, it remained on zero after tips. When I commented out the first three lines of the loop (ie. nointerrupts to to interrupts) it responded to tips. This time the jumps where by 4 (mostly, but sometimes 3).
Moth_NA:
However, when I monitored it, it remained on zero after tips. When I commented out the first three lines of the loop (ie. nointerrupts to to interrupts) it responded to tips. This time the jumps where by 4 (mostly, but sometimes 3).
Sorry, my 'corrected' version of the code I posted in reply #5 is wrong.
I forgot to correct the interrupt handler.
This is the corrected interrupt handler you must use with my corrected code version from #5:
Sorry for the delay. I've fixed the code as suggested by Jurs (below). Sorry also to Nick for being slow to respond and to follow your advice.
#define INTERRUPT_INPUT 2
volatile int pulse_counter_ISR;
int pulse_counter;
void setup()
{
Serial.begin(9600);
// For noise suppression, enable pullup on interrupt pin
digitalWrite(INTERRUPT_INPUT, HIGH); // activate internal pull-up, so normally input is HIGH
attachInterrupt(INTERRUPT_INPUT - 2, interrupt_handler, FALLING); // count on FALLING edge
}
void loop(){
noInterrupts(); // disable interrupts
pulse_counter=pulse_counter_ISR; // copy volatile variable into normal variable
interrupts(); // enable interrupts
Serial.print(pulse_counter);
Serial.print("\n\r");
delay(1000);
}
void interrupt_handler()
{
pulse_counter_ISR = pulse_counter_ISR + 1;
}
I still find the same issue with the value jumping primarily by 4 (sometimes 3).
Then I had a thought of using an "if" function to simply click it over to '0.74' whenever there is a tip.
That worked.
#define INTERRUPT_INPUT 2
volatile int pulse_counter_ISR;
int pulse_counter;
unsigned int prevpulse_counter_ISR;
float Rain = 0;
float tip = 0.74; // tip value in ml
void setup()
{
Serial.begin(9600);
// For noise suppression, enable pullup on interrupt pin
digitalWrite(INTERRUPT_INPUT, HIGH); // activate internal pull-up, so normally input is HIGH
attachInterrupt(INTERRUPT_INPUT - 2, interrupt_handler, FALLING); // count on FALLING edge
}
void loop(){
noInterrupts(); // disable interrupts
pulse_counter=pulse_counter_ISR; // copy volatile variable into normal variable
interrupts(); // enable interrupts
Serial.print(Rain);
Serial.print("\n\r");
delay(1000);
}
void interrupt_handler()
{
pulse_counter_ISR = pulse_counter_ISR + 1;
if (prevpulse_counter_ISR < pulse_counter_ISR){
Rain = tip;
}
}
The problem now is either adding it up each tip or returning it to zero in the next value. I've tried using "else" and I tried the last code line as 'Rain = Rain + tip'. Neither worked for me.
Moth_NA:
The problem now is either adding it up each tip or returning it to zero in the next value. I've tried using "else" and I tried the last code line as 'Rain = Rain + tip'. Neither worked for me.
Here is an updated code version for a rain gauge, which includes software debouncing:
#define TENMINUTES (600*1000L) // ten minutes are 600000 milliseconds
#define REEDPIN 2
#define REEDINTERRUPT 0
volatile int pulseCount_ISR;
void reedSwitch_ISR()
{
static unsigned long lastReedSwitchTime;
// debounce for a quarter second = max. 4 counts per second
if (labs(millis()-lastReedSwitchTime)>250)
{
pulseCount_ISR++;
lastReedSwitchTime=millis();
}
}
void setup() {
Serial.begin(9600);
Serial.println();
Serial.println("Total\tCurrent");
pinMode(REEDPIN,INPUT_PULLUP);
attachInterrupt(REEDINTERRUPT,reedSwitch_ISR, FALLING);
}
unsigned long lastSecond,last10Minutes;
long lastPulseCount;
int currentPulseCount;
void loop()
{
// each second read and reset pulseCount_ISR
if (millis()-lastSecond>=1000)
{
lastSecond+=1000;
noInterrupts();
currentPulseCount+=pulseCount_ISR; // add to current counter
pulseCount_ISR=0; // reset ISR counter
interrupts();
Serial.print(lastPulseCount);Serial.print('\t');Serial.print(currentPulseCount);
Serial.println();
}
// each 10 minutes save data to another counter
if (millis()-last10Minutes>=TENMINUTES)
{
last10Minutes+=TENMINUTES; // remember the time
lastPulseCount+=currentPulseCount; // add to last period Counter
currentPulseCount=0;; // reset counter for current period
}
}
I have added debouncing for a quarter second (250ms) to the interrupt handling routine.
Please be aware that this will limit the count rate to max. four counts per second.
And I show how you can count up "total" values for certain periods of time (i.e. 10 minutes), while "current" values will count up until the next period is finished, then the current value will be added to the total value, while the current value starts counting from zero. Of course you could also log data to a file on SD card instead every 10 minutes or as you like.
I’ll test the code later today when I have the opportunity.
Actually, I plan to record the data on an SD card – writing that code is on my to-do list (I’ve got all of my other sensors working so far, it was just the rain gauge and I haven’t even begun to think about building a wind direction sensor yet).
I’ve got the SD shield ready, but have been thinking recently, if it would be just simpler to instead buy a wifi shield and just store it remotely instead. In any case, I’ll probably store 5 min or 10 min avgs and totals which I can then use for 20 min or 30 min avgs and totals depending what standard I want to compare it to. But first things first; getting sensible readings!
It works a treat! The only change I made was to change each pulse to suit the tip value in ml (below).
Plus the output suits to format that I plan to use!
Thanks again for all your help. Hopefully when I get my head around C++ I can return the favour.
#define TENMINUTES (600*1000L) // ten minutes are 600000 milliseconds
#define REEDPIN 2
#define REEDINTERRUPT 0
#define tip 0.74 //value of each tip
volatile int pulseCount_ISR;
void reedSwitch_ISR()
{
static unsigned long lastReedSwitchTime;
// debounce for a quarter second = max. 4 counts per second
if (labs(millis()-lastReedSwitchTime)>250)
{
pulseCount_ISR++;
lastReedSwitchTime=millis();
}
}
void setup() {
Serial.begin(9600);
Serial.println();
Serial.println("Total\tCurrent");
pinMode(REEDPIN,INPUT_PULLUP);
attachInterrupt(REEDINTERRUPT,reedSwitch_ISR, FALLING);
}
unsigned long lastSecond,last10Minutes;
long lastPulseCount;
int currentPulseCount;
void loop()
{
// each second read and reset pulseCount_ISR
if (millis()-lastSecond>=1000)
{
lastSecond+=1000;
noInterrupts();
currentPulseCount+=pulseCount_ISR; // add to current counter
pulseCount_ISR=0; // reset ISR counter
interrupts();
Serial.print(lastPulseCount * tip);Serial.print('\t');Serial.print(currentPulseCount * tip);
Serial.println();
}
// each 10 minutes save data to another counter
if (millis()-last10Minutes>=TENMINUTES)
{
last10Minutes+=TENMINUTES; // remember the time
lastPulseCount+=currentPulseCount; // add to last period Counter
currentPulseCount=0;; // reset counter for current period
}
}