dumb question about a timer and looping

Hi guys.
I'm building a tester for auto lamps, the hand controller has a button for each lamp and a potentiometer to select duration to have the selected lamp lit up for.
all buttons and potentiometer are working fine but I'm having difficulty with the countdown time.
basically I can turn the pot to pic duration, 30s 1m 2m 3m 5m and always on.
Upon a button push I'm reading the position of the pot to determine how long to light the light, all of this is working fine but I'm having trouble implimenting a countdown to lamp off.
the main questions,

  1. Is there a way of running the timer outside the main loop () without loosing monitoring of other buttons
  2. Is using delay(1000) a horrible way of coding a countdown timer?

If I click button1 with pot on 30s I want to run that lamp for 30s but if another button is pushed during that time I want to switch lamp1 off and activate the other lamp instead but all timers I've written keep going until complete and ignore all other button pushes whilst timer is running

post your code so we can see how far you have got and what direction you are heading

Using delay(...) is usually to be avoided. delay(1000) means that for 1000 milliseconds (one second, a l-o-n-g time for an Arduino) very little else happens: for example, no monitoring of other buttons.

There are better ways. For example, record the current time. Every time through loop(), subtract the recorded current time from the current time NOW. When this difference becomes greater than the time you wanted to wait, do something. PLEASE note that all variables that contain a time should be unsigned long, and that only subtraction (NOT addition!) should be used in the comparisons.

delay(...) or anything that duplicates what delay(...) does is handy for simple examples but in general is evil! Avoid it!

It is usual in this forum to post your code (autoformat first, then post in code tags) and to provide a wiring diagram and/or a schematic.

Thanks guys for taking time out to reply.
I'll have to upload code tomorrow from the laptop as I'm on my phone now.

I think from the look of it I've gone about this whole timer wrong, when I detect a button push I'm calling a corresponding function to deal with the button push (send a command to the 433tx chip, change the display on the lcd and commence the countdown using delay 1000) this in turn is not doing anything in the main loop until the timer has elapsed.
I'll post up the code tomorrow, is it customary in this forum to post the whole sketch even if it's quite long or just the relevant portions?

NDagger:
I'll post up the code tomorrow, is it customary in this forum to post the whole sketch even if it's quite long or just the relevant portions?

The whole thing. You can attach it if it is so long that the forum won't let you post it.

Post the whole sketch if you can. Often the snippet of code thought to be relevant isn't where the problem really is. Variable declarations, libraries used, code in the setup function, etc. might be important to trouble shooting.

I like to use classes for this sort of thing.

const int potPin = 0; // pot on analog 0

class Lamp {
  public:
    const int buttonPin;
    const int lampPin;

    boolean buttonDown = false;
    boolean lampOn = false;

    unsigned long onAtMs;
    unsigned long durationMs;

    Lamp(int buttonPin, int lampPin) :
      buttonPin(buttonPin),
      lampPin(lampPin)
    {
      // don't need to do anything here
    }

    void setup() {
      pinMode(buttonPin, INPUT_PULLUP);
      pinMode(lampPin, OUTPUT);
    }

    void loop() {
      int buttonNow = digitalRead(buttonPin);

      if (buttonNow && !buttonDown) {
        onAtMs = millis();
        lampOn = true;
        durationMs = analogRead(potPin); // you might want to scale this
        digitalWrite(lampPin, HIGH);
      }
      else if (lampOn && millis() - onAtMs >= durationMs) {
        lampOn = false;
        digitalWrite(lampPin, LOW);
      }
      else {
        // do nothing
      }

      buttonDown = buttonNow;
    }
};

So now we have a thing that we can attach to a button and a lamp. It has it's own loop method which can control the behaviour of a lamp. NOtice that we have to declare potPin before the class and outside it.

If you have many lamps, it makes sense to use an array. Here is an array of four lamps. Buttons are on pins 2-5, and lamps are on pins 9-12.

Lamp lamps[] = {
  Lamp(2,9),
  Lamp(3,10),
  Lamp(4,11),
  Lamp(5,12)
};

Ok! In our setup, we need to call set up for each of our lamps, and in the loop we call the loop for each of the lamps. Notice the expression in the for() statement - "take the size of the entire lamp array, and divide it by the size of a single element of the array". This gives the number of items.

void setup() {
  for(int i = 0; i<sizeof(lamps)/sizeof(*lamps); i++) {
    lamps[i].setup();
  }  
}

void loop() {
  for(int i = 0; i<sizeof(lamps)/sizeof(*lamps); i++) {
    lamps[i].loop();
  }  
}

And - well, that's it.

PaulMurrayCbr:
I like to use classes for this sort of thing.

const int potPin = 0; // pot on analog 0

class Lamp {
  public:
    const int buttonPin;
    const int lampPin;

boolean buttonDown = false;
    boolean lampOn = false;

unsigned long onAtMs;
    unsigned long durationMs;

Lamp(int buttonPin, int lampPin) :
      buttonPin(buttonPin),
      lampPin(lampPin)
    {
      // don't need to do anything here
    }

void setup() {
      pinMode(buttonPin, INPUT_PULLUP);
      pinMode(lampPin, OUTPUT);
    }

void loop() {
      int buttonNow = digitalRead(buttonPin);

if (buttonNow && !buttonDown) {
        onAtMs = millis();
        lampOn = true;
        durationMs = analogRead(potPin); // you might want to scale this
        digitalWrite(lampPin, HIGH);
      }
      else if (lampOn && millis() - onAtMs >= durationMs) {
        lampOn = false;
        digitalWrite(lampPin, LOW);
      }
      else {
        // do nothing
      }

buttonDown = buttonNow;
    }
};




So now we have a thing that we can attach to a button and a lamp. It has it's own loop method which can control the behaviour of a lamp. NOtice that we have to declare potPin **before** the class and outside it.

If you have many lamps, it makes sense to use an array. Here is an array of four lamps. Buttons are on pins 2-5, and lamps are on pins 9-12. 



Lamp lamps[] = {
  Lamp(2,9),
  Lamp(3,10),
  Lamp(4,11),
  Lamp(5,12)
};




Ok! In our setup, we need to call set up for each of our lamps, and in the loop we call the loop for each of the lamps. Notice the expression in the for() statement - "take the size of the entire lamp array, and divide it by the size of a single element of the array". This gives the number of items.




void setup() {
  for(int i = 0; i<sizeof(lamps)/sizeof(*lamps); i++) {
    lamps[i].setup();
  } 
}

void loop() {
  for(int i = 0; i<sizeof(lamps)/sizeof(*lamps); i++) {
    lamps[i].loop();
  } 
}




And - well, that's it.

this is beautifully efficient and something I will have to go through and impliment, at the moment I've just written a lot of unnecessary code to get the whole thing working as I want. I'll upload the sketch when I'm back from work but as you'll see in the main loop I'm checking for each button state and calling it's associated function, eg
if (buttonstatebrake == HIGH){
Brakebuttonpush ();
}
If (buttonstatefog == HIGH){
fogbuttonpush();
}
Etcetc
where in the brakebuttonpush () function I'm trying to do the stuff.

As I say, the buttons, pots and lcd are behaving as I want but these are just visual, it's not actually doing anything yet but you'll understand more when I put the sketch up this afternoon

here is my sketch.
at the moment it isnt actually doing anything, ive been working at getting all of the buttons and pots to work as they should.
when it's functional i will be going through to optimise it and take out as much redundancy and trimming as much fat as i can, but for now its bloated but functioning.
project background -
its the code for one half of my project, it is the hand controller for a test box to plug into a leisure vehicle to simulate signals from the car - ie, brake lights, fog lights, car power present (12v), fridge power on, indicators etc. This handcontroller has a button for each test, 2 potentiometers (1 for how long to illuminate selected light, 1 for the speed to cycle through a test of all lights when you have pressed the full test button) a lcd to display what is currently being tested and remaining time of test.

this will be talking to a seperate unit plugged into the front of the leisure vehicle which will switch relays to provide 12v power to whichever light or function has been selected by the hand controller, and switch them off when timer has expired.

so in short, you can stand at the back of the leisure vehical and test the road lights without wires or walking from front to back to switch on each lamp individually like i do currently.

haven't drawn a schematic of it yet but all hardware is functioning as expected

RangerHandControl.ino (12.8 KB)

that's a lot of sketch to read. the way your code is written doesn't play well with timers.

After following the logic (using pen and paper) it looks like a full rewrite is in order based on a solid plan

the plan would be something like

whats the pot settings

have the pot settings changed

if a button is pressed what ever the pot is set at that is the timer you are using

do your thing and monitor the time

also watch the buttons

if button pressed change test

display how long timer has to run

after timer finishes go back to start screen

if all test is selected run a pattern (that means you need a pattern in the code that can be used later)

sometimes the best way to start is to forget all the lcd text as that's just eye candy

i will include a sketch that's a idea of how the code might flow. Its rough as its a test not a example

i don't have a mega or lcd spare so i changed pin numbers etc for testing using a uno and serial monitor

be aware the code may have problems as its not fully tested (also pots have been disabled and test code added that will need to be removed)

// include the library code:
//#include <LiquidCrystal.h>
//#include <RH_ASK.h>
//#include <SPI.h> // Not actually used but needed to compile

//RH_ASK driver;
// initialize the library with the numbers of the interface pins
//LiquidCrystal lcd(7, 8, 9, 10, 11, 12);


//define digital pins
const int brakebutton = 2;
const int fogbutton =  3;
const int revbutton = 4;
const int lhindbutton = 5;
const int rhindbutton = 6;
const int lhsidebutton = 7;
const int rhsidebutton = 8;
const int fridgebutton = 9;
const int carbutton = 10;
const int testbutton = 11;

//define analogue pins
//const int durationpot = A1;
//const int speedpot = A8;


boolean initialised = 0;
boolean waiting = 0;
int duration;
int lastduration = 0;
int globalduration;
int cycle;
int lastcycle = 0;
int globalcycle;
unsigned long previousMillis = 0;
unsigned long interval = 1000;
unsigned long previousMillis1 = 0;
unsigned long interval1 = 1000;
unsigned long  fulltestinterval = 1000;
unsigned long fulltestMillis = 0;
byte test = 0;
byte fullTest = 0;
byte constantOn = 0;
byte prevduration = 0;
byte prevcycle = 0;
byte prevTest = 0;
long timercount = 0;


void setup() {
  // put your setup code here, to run once:
  // set up the LCD's number of columns and rows:
  // lcd.begin(16, 2);
  // initialise serial comms
  Serial.begin(9600);
  // initialize the button pins as an INPUT_PULLUP:
  pinMode(brakebutton, INPUT_PULLUP);
  pinMode(fogbutton, INPUT_PULLUP);
  pinMode(revbutton, INPUT_PULLUP);
  pinMode(lhindbutton, INPUT_PULLUP);
  pinMode(rhindbutton, INPUT_PULLUP);
  pinMode(lhsidebutton, INPUT_PULLUP);
  pinMode(rhsidebutton, INPUT_PULLUP);
  pinMode(fridgebutton, INPUT_PULLUP);
  pinMode(carbutton, INPUT_PULLUP);
  pinMode(testbutton, INPUT_PULLUP);

}

void loop() {
  unsigned long currentMillis = millis();
  byte buttonstatebrake = digitalRead(brakebutton);
  byte  buttonstatefog = digitalRead(fogbutton);
  byte buttonstaterev = digitalRead(revbutton);
  byte  buttonstatelhind = digitalRead(lhindbutton);
  byte  buttonstaterhind = digitalRead(rhindbutton);
  byte  buttonstatelhside = digitalRead(lhsidebutton);
  byte  buttonstaterhside = digitalRead(rhsidebutton);
  byte  buttonstatecar = digitalRead(carbutton);
  byte  buttonstatetest = digitalRead(testbutton);
  byte  buttonstatefridge = digitalRead(fridgebutton);
 // duration = analogRead(durationpot);
  //cycle = analogRead(speedpot);

 duration = 865;//test code
  cycle = 525;//test code
  if (test == 0) {
    //30s 1m 2m 3m 5m
    if ((prevduration > duration + 3) || (prevduration < duration - 3)) {
      if ((duration <= 1023) && (duration > 850)) {
        interval = 1000 * 30;
        constantOn = 0;
      }
      else if ((duration <= 850) && (duration > 700)) {
        interval = 1000 * 60;
        constantOn = 0;
      }
      else if ((duration <= 700) && (duration > 500)) {
        interval = 1000 * 60 * 2;
        constantOn = 0;
      }
      else if ((duration <= 500) && (duration > 300)) {
        interval = 1000 * 60 * 3;
        constantOn = 0;
      }
      else if ((duration <= 300) && (duration > 150)) {
        interval = 1000 * 60 * 5;
        constantOn = 0;
      }
      else if ((duration <= 150) && (duration >= 0)) {
        constantOn = 1;
        interval = 10;
      }
    }
    prevduration = duration;

    if ((prevcycle > cycle + 3) || (prevcycle < cycle - 3)) {

      if ((cycle <= 1023) && (cycle > 900)) {
        fulltestinterval = 3000;
      }
      else if ((cycle <= 900) && (cycle > 700)) {
        fulltestinterval = 6000;
      }
      else if ((cycle <= 700) && (cycle > 500)) {
        fulltestinterval = 9000;
      }
      else if ((cycle <= 500) && (cycle > 300)) {
        fulltestinterval = 12000;
      }
      else if ((cycle <= 300) && (cycle >= 0)) {
        fulltestinterval = 15000;
      }
    }
    prevcycle = cycle;
    timercount = interval / 1000;
  }

  if (buttonstatebrake == LOW) {
    test = 1;
    fullTest = 0;
  }
  if (buttonstatefog == LOW) {
    test = 2;
    fullTest = 0;
  }
  if (buttonstaterev == LOW) {
    test = 3;
    fullTest = 0;
  }
  if (buttonstaterhind == LOW) {
    test = 4;
    fullTest = 0;
  }
  if (buttonstatelhind == LOW) {
    test = 5;
    fullTest = 0;
  }
  if (buttonstaterhside == LOW) {
    test = 6;
    fullTest = 0;
  }
  if (buttonstatelhside == LOW) {
    test = 7;
    fullTest = 0;
  }
  if (buttonstatetest == LOW) {
    test = 10;
  }
  if (buttonstatecar == LOW) {
    test = 9;
    fullTest = 0;
  }
  if (buttonstatefridge == LOW) {
    test = 8;
    fullTest = 0;
  }
  if (test != prevTest) {
    previousMillis = currentMillis;
  }
  if  (constantOn == 0) {
    if (currentMillis - previousMillis >= interval) {
      test = 0;
    }
  }
  switch (test) {
    case 0:
      Serial.println("start screen press button");
      //lcd text select a button
      break;
    case 1://buttonstatebrake
      Serial.println("brake button pressed");
      //lcd text
      //set output
      break;
    case 2://buttonstatefog
      Serial.println("fog button pressed");
      //lcd text
      //set output
      break;
    case 3://buttonstaterev
      //lcd text
      //set output
      break;
    case 4://buttonstaterhind
      //lcd text
      //set output
      break;
    case 5://buttonstatelhind
      //lcd text
      //set output
      break;
    case 6://buttonstaterhside
      //lcd text
      //set output
      break;
    case 7://buttonstatelhside
      //lcd text
      //set output
      break;
    case 8://buttonstatetest
      //lcd text
      //set output
      break;
    case 9://buttonstatecar
      //lcd text
      //set output
      break;
    case 10://buttonstatefridge
      fullTest = 1;
      test = 0;
      break;
  }
  if (test != 0) {
    if (currentMillis - previousMillis1 >= interval1) {
      timercount--;
      Serial.println(timercount);
      previousMillis1 = currentMillis;
    }
  }

  prevTest = test;
  if (fullTest == 1) {
    if (currentMillis - fulltestMillis >= fulltestinterval) {
      test++;
      if (test >= 9) {
        fullTest = 0;
        test = 0;
      }
      fulltestMillis = currentMillis;
    }
  }
}

Sorry for not getting back sooner.
wow, gpop1 big thanks for taking time to look through it for me.
I was getting the feeling a rewrite was in order, this is the first time I've really messed with arduino and haven't touched code since back in my college days, I've been treating it similar to vb where you write functions for things which can run simultaneously rather than focusing on the main loop, so yeah like you say, there was never really a plan when I went into it.
I think now I've got the hardware laid out I'll start a fresh with a proper structure like you posted.
thanks again gpop1

I have wondered before if what the arguino programming environment really needs is a whole event driven library - even a new IDE where you link together a state machine using visual blocks.