Check my code

Hi guys,

This is my first arduino project, so Im hoping someone can just have a look over it for me and tell me if I can make any improvements etc. Im still learning the code so I want to pick off any mistakes early before I develop bad habits. Functionally the code is working.

Its a simple pump timer. You have a momentary push button and a rotary switch. You dial the rotary switch to a set time (each position of the rotary switch is connected to a digital input of pin 3 to pin 11) and push the push button. The pump then starts and runs for that corresponding time. You can also turn the pump off during use by pushing the push button again. It runs off solar power, which can provide more than enough power, but I dont like wasting the power unnecessarily and so I have popped a sleep command in there. I have also used software debounce to debounce the push button. I did this just to cut down on the number of external components required.

Everything seems to work - can you have a look over it and just tell me if there is anything that I should/shouldnt be doing.

I have three questions about it, only minor, nothing that Im too worried about but still interested to know about.

1 - The debounce circuit. This is done in an interrupt, I had some problems and so this code I got from the web. It works well, but it is not reliable for long button presses. Probably not a problem but it would be great if I could sort that one out.

2 - The millis and roll-over. I got the impression reading around that there is a potential problem when the millis counter rolls over. If the pump was to run when the millis counter rolled over, Im concerned it may cause the pump to get stuck running (after something like 55 days from memory). I used subtraction as one person suggested, but am not 100% sure.

3 - Sleep mode. When you turn the pump on, it runs through its set time and the pump is then turned off. I then put it to sleep - that works fine. If, however, I turn the pump off while it is running, by pushing the pushbutton again, the interrupt stops the pump but I cant put it to sleep. Trying to put the sleep code into the interrupt caused the arduino to go to sleep and get stuck with the output either stuck on or stuck off.

So here it is, appreciate any feedback at all:

#include <avr/sleep.h>                                    // include the sleep library so we can put the audrino to sleep when its not in use

int pumpPin = 13;                                         // output pin for the pump and solenoid (goes to the relay)
int buttonPin = 2;                                        // input pin (for a pushbutton switch)
int rotaryPin [] = {3,4,5,6,7,8,9,10,11};                 // input pins that the rotary switch is connected to
unsigned long delayTime [] = {3,4,5,6,7,8,9,10,11};       // Setup for delay times
unsigned long startMillis = 0;                            // Used as a starting point for the timer
volatile int flag = HIGH;                                  // Setup to allow the pump to be turned off during a cycle.  When triggered and interrupt will set a flag that will stop the timer.


void setup()
{
pinMode(pumpPin, OUTPUT);                               // declare pump output pin as an output
pinMode(buttonPin, INPUT);                              // declare switch as input
  for (int counter=0; counter<9; counter++) 
     {
     pinMode(rotaryPin[counter], INPUT);                 // initialize the rotary pins as inputs 
     digitalWrite(rotaryPin[counter],HIGH);              // Set the rotary input pins as logic high using the microprocessors inbuilt 20k pullup resistors.  
     }                                                   // Otherwise we would need a 10k to ground pulldown resistor for each pin.
  
delayTime[0] = 2;                               // used to set rotary switch position 1 run time in minutes
delayTime[1] = 5;                               // used to set rotary switch position 2 run time in minutes
delayTime[2] = 10;                              // used to set rotary switch position 3 run time in minutes
delayTime[3] = 15;                              // used to set rotary switch position 4 run time in minutes
delayTime[4] = 20;                              // used to set rotary switch position 5 run time in minutes
delayTime[5] = 30;                              // used to set rotary switch position 6 run time in minutes
delayTime[6] = 45;                              // used to set rotary switch position 7 run time in minutes
delayTime[7] = 60;                              // used to set rotary switch position 8 run time in minutes
delayTime[8] = 120;                             // used to set rotary switch position 9 run time in minutes

attachInterrupt (0, setFlag, RISING);           // Interrupt to turn the pump off if the push button is pressed when the pump is turned on.  
                                                // 0 is the interrupt pin used (digital pin 2). 
                                                // setFlag is the function called. 
                                                // Rising is the signal to detect from the pushbutton.
                                                
set_sleep_mode(SLEEP_MODE_PWR_DOWN);            // Chooses which power saving mode we would like to use.  In this case we are powering down the circuit.
}


void setFlag()                                  // This is the function called by the interrupt to stop the pump
{

                                 
  static unsigned long last_interrupt_time = 0;      // Setup for switch debounce
  unsigned long interrupt_time = millis();           // Setup for switch debounce
  if (interrupt_time - last_interrupt_time > 250)    // Timer for the switch debounce - can be fine tuned if required
  {
    flag = !flag;                                    // The flag is set which will stop the timer.
    last_interrupt_time = interrupt_time;            // Update for the switch debounce code  
  }

}

  
void loop()
{
  for (int count=0; count<9; count++)                                                         // Starts a counter which is used to scan the rotary pin inputs
    {
     if (digitalRead(buttonPin) == HIGH && digitalRead(rotaryPin[count]) == LOW)             // If the button is pressed the corresponding rotary switch is checked
        {
          startMillis = millis();                                                             // Checks the current time so that we can later work out when to stop the pump
              while (flag == LOW && (millis() - startMillis) < (delayTime[count] * 60000))     // Checks if the stop flag has been set and if the timer has finished.  
                                                                                              // Multiplied by 60000 to convert the delay time to minutes
              {
                digitalWrite(pumpPin, HIGH);                                                  // If the timer hasnt finished and the flag isnt set, the pump is turned on
              }
        
        digitalWrite(pumpPin, LOW);                                                           // Once we are finished, either because the timer is finished or the flag is set, the pump is turned off.  Also sets the pump off when the microprocessor is powered up.
        if (flag == LOW)                                                                      // If the timer completes, the flag needs to be reset
        {                                                                                     // If the flag isnt reset, the next time the button is pressed the flag is set and the pump wont turn on.
         flag = HIGH;                                                                         // This would mean pressing the pushbutton twice if the cycle finished without being stopped.
         sleep_enable();                                                                      // If the pump finishes we enable sleep mode (enabled but not actually turned on)
         sleep_mode();                                                                        // Controller is put to sleep.  Reduces the standby power from 21mA to 14mA.
      }
      }
    }
}

kopper:
appreciate any feedback at all:

unsigned long delayTime [] = {3,4,5,6,7,8,9,10,11};       // Setup for delay times

:
    :
delayTime[0] = 2;                               // used to set rotary switch position 1 run time in minutes
delayTime[1] = 5;                               // used to set rotary switch position 2 run time in minutes
delayTime[2] = 10;                              // used to set rotary switch position 3 run time in minutes

Why have both a static initializer and then go and assign values in the setup? The static initializer is easier to read and more logical than the subsequent assignments. I would keep to the former.

void setFlag()

{
   flag = !flag;                                    // The flag is set which will stop the timer.
}

:
  :

if (flag == LOW)                                                                      // If the timer completes, the flag needs to be reset
       {                                                                                     // If the flag isnt reset, the next time the button is pressed the flag is set and the pump wont turn on.
        flag = HIGH;                                                                         // This would mean pressing the pushbutton twice if the cycle finished without being stopped.
        sleep_enable();                                                                      // If the pump finishes we enable sleep mode (enabled but not actually turned on)
        sleep_mode();                                                                        // Controller is put to sleep.  Reduces the standby power from 21mA to 14mA.

I don't follow why the ISR is flipping the flag, not just setting to true. In any event, in the main code where you test and set the flag, that should be bracketed by a cli() sei() to avoid having an interrupt occur during this logic. See Arduino Playground - AVR

It's not clear what you're up to with sleep mode exactly. There are some forum threads with usable examples.

Thanks gardener for your time.

Re - static initializer. I see what youre saying. I think what I was trying to do initally was assign each a pin, but they are just values, so I dont need to do that. I have the table there assigning the values because this is probably the one thing I would edit in the future, so I wanted to be easy to change the times, even if Im a bit rusty on the code. Valid point though, I will omit one and adjust accordingly. Nice pickup.

Setting the flag as a true/false boolean now. Just out of interest, is that good practice or is there another reason?

In regards to disabling the interrupts, thats a good idea. Is that the same as saying detachInterrupt and attachInterrupt? Is this correct use of the cli/sei code?

*I just tried disabling the interrupt around the while loop - it didnt like that. I used the following code but it doesnt like that either. The pump will turn on, it will turn off but it then gets stuck off and I cant turn it back on. Am I missing something?

        cli();
        if (flag == false)                                                                      // If the timer completes, the flag needs to be reset
        {                                                                                     // If the flag isnt reset, the next time the button is pressed the flag is set and the pump wont turn on.
         flag = true;                                                                         // This would mean pressing the pushbutton twice if the cycle finished without being stopped.
         sei();

Ill keep reading about the sleep mode. It is working in the main code. The arduino goes to sleep and drops from 21mA to 14mA. When I take the atmega chip out and put it in my own circuit that value should drop a bit more again.

Thanks again

kopper:

        cli();

if (flag == false)                                                                      // If the timer completes, the flag needs to be reset
       {                                                                                     // If the flag isnt reset, the next time the button is pressed the flag is set and the pump wont turn on.
        flag = true;                                                                         // This would mean pressing the pushbutton twice if the cycle finished without being stopped.
        sei();

You'll need to re-enable interrupts even if flag != false -- ie: after the if. Likely you don't need the sei() inside the if.

detachInterrupt and attachInterrupt change the interrupt handler -- ie: hook it up to the code that will run. cli() and sei() simply block (delay) an interrupt from happening. cli() and sei() translate into single instructions and are very cheap. To protect a critical section, cli() and sei() is what you want.

        cli();
        if (flag == false)                                                                      // If the timer completes, the flag needs to be reset
        {                                                                                     // If the flag isnt reset, the next time the button is pressed the flag is set and the pump wont turn on.
         flag = true;                                                                         // This would mean pressing the pushbutton twice if the cycle finished without being stopped.
         sei();                                                                         // This would mean pressing the pushbutton twice if the cycle finished without being stopped.
         sleep_enable();                                                                      // If the pump finishes we enable sleep mode (enabled but not actually turned on)
         sleep_mode();                                                                        // Controller is put to sleep.  Reduces the standby power from 21mA to 14mA.
      }
      sei();

Regarding the sleeping logic, have a look at these threads:

http://arduino.cc/forum/index.php/topic,54950.0.html
http://arduino.cc/forum/index.php/topic,60554.0.html

Thanks for that. So the working code to disable the interrupt now looks like this:

        if (flag == false)                                                                    // If the timer completes, the flag needs to be reset
        {                                                                                     // If the flag isnt reset, the next time the button is pressed the flag is set and the pump wont turn on.
         cli();                                                                               // Disables the interrupt while we check the flag
         flag = true;                                                                         // This would mean pressing the pushbutton twice if the cycle finished without being stopped.
         sei();                                                                               //Re-enables the interrupt

I checked out those threads regarding putting the arduino to sleep, no help though. Maybe the sleep function is relying on the millis timer, which isnt running during the interrupt? Maybe I could try running the interrupt code but having a separate sleep function to put the arduino to sleep and pointing the interrupt code from the sleep function.

ACTUALLY, now that I think about it like that, it may just be the placement of my sleep code. At the moment, the sleep code is only run if the arduino gets to that end code and if the flag == false. If I was to place it a bit further up in the code, it might work....

OK, I found a thread that talks about the problem that Im having:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1286455596

Having a bit of trouble following it, but Ill keep working on it.

Also found this one http://arduino.cc/forum/index.php/topic,60334

I ran the working code that was given by Nick Gammon, it did funny things. Could it be my Arduino or maybe the version 1 editor?

I played around for a couple of hours but couldnt get anything reliable.

I managed to get it to go to sleep every second press of the button, but not every press.

I managed to get it to work, but it would lock up if the timer was allowed to run, the manually stopped the next time.

I dont know.

Otherwise Ill just stick with my previous version.

kopper:
Maybe the sleep function is relying on the millis timer, which isnt running during the interrupt?

No, timers aren't running when it is asleep. In fact interrupts don't work except LOW ones. Can you post your current code?

Reduces the standby power from 21mA to 14mA.

You should be able to get a lot less than that. What board is this?

kopper:
I ran the working code that was given by Nick Gammon, it did funny things.

It was working but it did funny things? Can you be more specific? What code? I make lots of code. What funny things exactly?

Hi Nick,

Current code is this:

*/

#include <avr/sleep.h>                                    // include the sleep library so we can put the audrino to sleep when its not in use

int pumpPin = 13;                                         // output pin for the pump and solenoid (goes to the relay)
int buttonPin = 2;                                        // input pin (for a pushbutton switch)
int rotaryPin [] = {3,4,5,6,7,8,9,10,11};                 // input pins that the rotary switch is connected to
unsigned long delayTime [] = {3,4,5,6,7,8,9,10,11};       // Setup for delay times
unsigned long startMillis = 0;                            // Used as a starting point for the timer
volatile boolean flag = true;                             // Setup to allow the pump to be turned off during a cycle.  When triggered and interrupt will set a flag that will stop the timer.


void setup()
{
pinMode(pumpPin, OUTPUT);                               // declare pump output pin as an output
pinMode(buttonPin, INPUT);                              // declare switch as input
  for (int counter=0; counter<9; counter++) 
     {
     pinMode(rotaryPin[counter], INPUT);                 // initialize the rotary pins as inputs 
     digitalWrite(rotaryPin[counter],HIGH);              // Set the rotary input pins as logic high using the microprocessors inbuilt 20k pullup resistors.  
     }                                                   // Otherwise we would need a 10k to ground pulldown resistor for each pin.
  
delayTime[0] = 2;                               // used to set rotary switch position 1 run time in minutes
delayTime[1] = 5;                               // used to set rotary switch position 2 run time in minutes
delayTime[2] = 10;                              // used to set rotary switch position 3 run time in minutes
delayTime[3] = 15;                              // used to set rotary switch position 4 run time in minutes
delayTime[4] = 20;                              // used to set rotary switch position 5 run time in minutes
delayTime[5] = 30;                              // used to set rotary switch position 6 run time in minutes
delayTime[6] = 45;                              // used to set rotary switch position 7 run time in minutes
delayTime[7] = 60;                              // used to set rotary switch position 8 run time in minutes
delayTime[8] = 120;                             // used to set rotary switch position 9 run time in minutes

attachInterrupt (0, setFlag, RISING);           // Interrupt to turn the pump off if the push button is pressed when the pump is turned on.  
                                                // 0 is the interrupt pin used (digital pin 2). 
                                                // setFlag is the function called. 
                                                // Rising is the signal to detect from the pushbutton.
                                                
set_sleep_mode(SLEEP_MODE_PWR_DOWN);            // Chooses which power saving mode we would like to use.  In this case we are powering down the circuit.
}


void setFlag()                                  // This is the function called by the interrupt to stop the pump
{

                                 
  static unsigned long last_interrupt_time = 0;      // Setup for switch debounce
  unsigned long interrupt_time = millis();           // Setup for switch debounce
  if (interrupt_time - last_interrupt_time > 250)    // Timer for the switch debounce - can be fine tuned if required
  {
    flag = !flag;                                    // The flag is set which will stop the timer.
    last_interrupt_time = interrupt_time;            // Update for the switch debounce code  
  }

}

  
void loop()
{
  for (int count=0; count<9; count++)                                                         // Starts a counter which is used to scan the rotary pin inputs
    {
     if (digitalRead(buttonPin) == HIGH && digitalRead(rotaryPin[count]) == LOW)             // If the button is pressed the corresponding rotary switch is checked
        {
          startMillis = millis();                                                             // Checks the current time so that we can later work out when to stop the pump
           while (flag == false && (millis() - startMillis) < (delayTime[count] * 1000))     // Checks if the stop flag has been set and if the timer has finished.  
                                                                                              // Multiplied by 60000 to convert the delay time to minutes
              {
                digitalWrite(pumpPin, HIGH);                                                  // If the timer hasnt finished and the flag isnt set, the pump is turned on
              }
        
        digitalWrite(pumpPin, LOW);                                                           // Once we are finished, either because the timer is finished or the flag is set, the pump is turned off.  Also sets the pump off when the microprocessor is powered up.
        if (flag == false)                                                                    // If the timer completes, the flag needs to be reset
        {                                                                                     // If the flag isnt reset, the next time the button is pressed the flag is set and the pump wont turn on.
         cli();                                                                               // Disables the interrupt while we check the flag
         flag = true;                                                                         // This would mean pressing the pushbutton twice if the cycle finished without being stopped.
         sei();                                                                               //Re-enables the interrupt
         sleep_enable();                                                                      // If the pump finishes we enable sleep mode (enabled but not actually turned on)
         sleep_mode();                                                                        // Controller is put to sleep.  Reduces the standby power from 21mA to 14mA.
         
        }
      }
    }

}

*Note that I have changed it from minutes to seconds for testing purposes (x60000 changed to x1000, so rather than turning on for two minutes it is turning on for two seconds).
I am running on a uno board with the atmega328. To test it out I have pin 3 connected to ground and pin 2 connected to a momentary pushbutton (with a resistor tied to ground because its active high). At the moment, with the above code, the arduino will go to sleep when the timer is finished but not if you stop it by pushing the pushbutton again.

I played around with the code for a couple of hours - didnt really get any further and kept getting these 'funny things' depending on my code -

ie - quiet often the arduino would go to sleep and stay asleep, I couldnt wake it up

  • if you ran the timer repeatedly it would sleep, if you turned it off manually it would sleep, but if you turned the timer on once then turned it off manually it would go to sleep and stay asleep
  • The arduino would only sleep on every second cycle

Lots of combinations but no luck. Im still not sure what the best way to debug is. At the moment im working through my logic with pen and paper, but that doesnt tell me everything thats going on in the arduino.

Cheers

kopper:
To test it out I have pin 3 connected to ground and pin 2 connected to a momentary pushbutton (with a resistor tied to ground because its active high).

Like I said, in sleep mode it detects LOW interrupts, and you deciding you want RISING ones won't change that.

You need to have a pull-up, not a pull-down, and detect a LOW. Not FALLING, LOW. That's the one that will wake it up.

To detect change (ie. rise, fall, change) it needs to be awake to know something has changed. However it is designed to notice a low interrupt even when asleep.

Ill give it a try. I did try it but didnt have any luck. It does work at the moment though with the rising state. Ill change it to low and keep playing

Well I must admit, that took me a good hour or two to get working. :slight_smile:

Just shows how fiddly interrupts can be, eh?

This is my code, tested with LED on pin 13, and a simulated button on pin 2. Plus a wire connecting pin 4 to ground to simulate a time selection.

#include <avr/sleep.h>                  

const int pumpPin = 13;       // output pin for the pump and solenoid (goes to the relay)
const int buttonPin = 2;      // input pin (for a pushbutton switch)
const int firstSwitch = 3;    // first switch pin for time rotary button
const int lastSwitch = 11;    // last switch pin
const int debounceTime = 20;  // debounce in milliseconds
const unsigned long delayTime [] = { 2, 5, 10, 15, 20, 30, 45, 46, 120};   // Pump run times in minutes
unsigned long startPumpTime = 0;           // when pump started
unsigned long pumpTime;                    // how long to run pump
volatile boolean buttonPressed;    // set when button pressed                       

// interrupt service routine in sleep mode
void wake ()
{
  sleep_disable ();         // first thing after waking from sleep:
}  // end of wake

// interrupt service routine when awake and button pressed
void buttonDown ()                            
{
  buttonPressed = true;
}  // end of buttonDown

void sleepNow ()
{
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);   
  sleep_enable ();          // enables the sleep bit in the mcucr register
  attachInterrupt (0, wake, LOW); 
  sleep_mode ();            // here the device is actually put to sleep!!
  detachInterrupt (0);     // stop LOW interrupt
}  // end of sleepNow

void deBounce ()
{
  unsigned long now = millis ();
  do
  {
    // on bounce, reset time-out
    if (digitalRead (buttonPin) == LOW)
      now = millis ();
  } 
  while (digitalRead (buttonPin) == LOW ||
    (millis () - now) <= debounceTime);

}  // end of deBounce

void setup()
{
  pinMode(pumpPin, OUTPUT);               
  digitalWrite (buttonPin, HIGH);    // pull-up on button
  for (int i = firstSwitch; i <= lastSwitch; i++) 
    digitalWrite (i, HIGH);           // pull-up on switch
} // end of setup

void loop ()
{

  // if pump is running, see if time to turn it off
  if (digitalRead (pumpPin) == HIGH)
  {
    if ((millis () - startPumpTime) >= pumpTime || buttonPressed)
    {
      digitalWrite (pumpPin, LOW);  
      deBounce ();
      buttonPressed = false;
    }
    return;  // not time to sleep yet
  }  // end of pump running

  // ------ here if pump not running -----

  // pump not running? sleep then
  sleepNow ();

  // pump is not running and we woke up, work out how long to run it

  deBounce ();
  pumpTime = 0;

  EIFR = 1;      // cancel any existing falling interrupt (interrupt 0)
  attachInterrupt (0, buttonDown, FALLING);   // ready for button press

  for (int i = firstSwitch; i <= lastSwitch; i++) 
    if (digitalRead (i) == LOW)
      pumpTime = delayTime [i - firstSwitch];

  if (pumpTime == 0)
    return;  // no time selected

  // start pump
  startPumpTime = millis ();
  digitalWrite (pumpPin, HIGH);    

  pumpTime *= 60000UL;  // convert minutes to milliseconds

  // pumpTime = 5000;  // FOR TESTING - 5 seconds

}  // end of loop

There were a few tricky bits. I have two interrupt handlers now, one for when asleep (that cancels sleep) and one when awake (that sets a flag). The debouncing needed quite a bit of work to get right, partly probably because I was just rubbing two wires together. This seems to work reliably now. :slight_smile:

Wow :astonished:. What can I say... it works perfectly.

I must admit I had given up. I spent probably close to 24 hours on that code - I am learning though so there was a bit of reading, trial and error etc in amongst that. It was at a point where it wasnt perfect, but it did the job it was meant to do closely enough.

I hope you dont mind if I use your code. I will study it and work out exactly how youve done it, it will be a good learning tool. I almost didnt believe it when I watched the arduino sleep everytime after watching it for hours and hours not go to sleep.

Id be happy to some $ your way for your time, Ill shoot you a PM. Just out of interest though, how do you debug and get past the problems. Do you just print the status of the flags etc with Serial.print to know whats going on?

Im now keen to get my sleep current right down. On the uno board Im using about 21mA when on, 14mA when off. Hopefully when I pull the chip off and put it on my own circuit I can knock that number right down. I see in some of your posts you were getting down in the 0.2mA range. Love it.

Just put the atmega chip into my own circuit. Very happy with the results.

The circuit is going to powered up 24x7, but for most of that time, probably 23.5 hours of the day, its going to be doing nothing.

Arduino without sleep - 20mA when doing nothing. 20mA isnt much, but when its drawing that 23.5 hours a day, it starts to add up. Thats a good half hour of sun on my small solar panels just to keep the arduino powered each day.

Arduino with sleep in my circuit - 0.5mA. Thats 40x less power. Thats with a 5V regulator and a protection diode. This means about 1 minute of sun on my solar panels will be enough to power the arduino for another 24 hours.

When its running, the pump, solenoid, relay etc will probably draw about 10 amps but its only for a relatively short amount of time.

Originally I was going to build the circuit out of discrete components - 555 timer etc, but decided to learn the arduino and use it. Its taken a lot longer to do it with the arduino, but theres a lot more flexibility there, a lot less soldering, future projects will be faster and I could duplicate the timer quite easily.

Im building it for my Dad, who has a remote shed. He put a water tank off it to water a small vege garden next to the shed. I put the solar panel setup on it with a 12V pump so he could water it. He would sometimes turn the pump on, do something else and forget about it, come back the next day to find the poor little pump running dry. The batteries are big enough to keep running it, but it eventually killed a pump. I purchased a valve and wanted to make this timer up, so all he has to do is turn it on, it will do its thing then turn off. I didnt want anything fancy with an LCD that you had to setup to run etc, just simple switch to turn it on and another switch to choose the time. Anything too fancy and I know it wouldnt be used.

Thanks to gardner and Nick Gammon for your help. Its greatly appreciated. All the best to you and your family.

kopper:
I hope you dont mind if I use your code.

Of course, use it! Enjoy.

kopper:
Just out of interest though, how do you debug and get past the problems. Do you just print the status of the flags etc with Serial.print to know whats going on?

Actually I did slip in some Serial.println here and there. I wasn't sure if it was sleeping and waking, or not sleeping, or not waking. Then I stared at those and went "huh?" for a while.

The thing is, the chip works. The compiler works. When the end result doesn't work, there is always a reason. Sometimes it can be obscure like a "race condition", or bouncing contacts, or interrupts not being enabled when they should be.

I'm starting a write-up on interrupts, to summarize some of the "gotchas" that can crop up.

kopper:
Arduino with sleep in my circuit - 0.5mA. Thats 40x less power. Thats with a 5V regulator and a protection diode.

You should be able to get it a bit lower than that. I did a write-up on interrupts here:

I measured 6 uA (0.006 mA) in sleep mode, with most of the internal devices turned off. Maybe the voltage regulator is gobbling a bit up.

Also there is a very interesting video by Afrotechmods where he explains how to use a MOSFET as reverse polarity protection rather than a diode. His method has much lower power consumption ...

Thanks for the info on the interrupts. I did a heap of searching around, there is information out there describing what they are but when it comes to coding them for the arduino things were a little less clear. Your post is definitely the most comprehensive.

6uA is pretty impressive. Actually thats very impressive. Im probably not too concerned for this project but I have some others in mind and getting power usage down that low will come in handy. Ive never seen the P channel mosfet connected like that before but its such a good idea. So simple, no external components required. Will use that one for sure from now on.

Re the pump timer. I connected it up this afternoon but had some problems. Everything worked fine on the bench. Went and wired it all up - initially appeared to work but then had problems. The pump was set for 2 minutes but ran for 45 seconds then turned off. A couple of times it missed the button presses, usually after finishing too early. The time was random, ran for a bit then turned off, regardless of what time was set (tried a few different). Sometimes ran without a problem. I know the switch is good, as it was working on the setup that I removed (without a timer). So I think it might have something to do with the switch and debounce (might need some adjustment with this switch) or maybe a capacitive effect. Not 100% sure but ran out of time. Will keep testing.

Well, there will be an explanation. :slight_smile:

If it runs for a lesser time than you expect I would be measuring voltages, perhaps the pump is drawing more than you can provide. Also if you are using a relay a reverse protection diode across the coil might help, although I don't see how that would account for it shutting off early.

The fact that it works on the bench, but not in the field, pretty clearly points to an electrical problem of some sort. You could look into a MOSFET transistor to control the pump rather than a relay. A relay will have its own current requirements. Or a solid-state relay (which will probably be a MOSFET packaged up for you).

The other possibility is that the pump is generating spikes of some sort that are causing the processor to reset. Try some decoupling capacitors (eg. 0.1 uF on the +5V line).