Same old question. Millis instead of delay

Hello there

Me. Again.. I seem to be lacking a certain... amount of intelligence. I've already asked this as regards another sketch and yet I find myself still unable to replace delay() with millis().

I've read and reread the various references for this also reading 'C for Arduino' in the hope I would be able to get to grips with it. Even compared original sketches with their finally tuned millis() descendants to try and see how it's done.

The current sketch is simple, which makes it even worse that I can't do it. It is based on a 3 x 4 LED matrix. a random number of which are lit at anyone time and overtime the pattern and number of LED's lit changes continually.

There are some delay routines in the setup which can be left I understand because they are not in the loop. The one I am trying to replace is in the void loop() At the moment it is a delay() with a random time frame.

As well as trying to change the delay to millis I also tried simplifying the program as I can see there are numerous areas where the 'long hand' coding could be reduced. Well I couldn't do any of that either. Also attempted to change the sketch format to the IPO method that jurs uses. A nice system that makes converging disparate sketches together a doddle. Well I couldn't even get that right.

Possibly I'm sounding petulant.. Actually I'm, and I am being polite here, browned off to the eye teeth with my total lack of ability, progress or even general understanding of why the hell I can't do the simplest bit of coding in the past week. Any way here is the infernal sketch. I'd be really grateful for any and all assistance. Or even a glass of strychnine :-\

// 12 LED, 6 LED max Random flasher for K9 by Mathew Prentis
// Tested on Arduino UNO V3 Sept 2014
// 12 LED's to pins 1,2,3,4,5,6,7,8,9,10,11,12
// limit LED current with 220R resistor on each LED.
// Pin to LED starting Top Left hand corner
//pin 1, 2, 3, 4 top row of LEDS
//pin 5, 6, 7, 8 middle row of LEDs
//pin 9, 10, 11, 12 bottom row of LEDs
//pin GND common to all LEDs Cathodes
//pins 0 and 13 not connected.

int ranNum1;
int ranNum2;
int ranNum3;
int ranNum4;
int ranNum5;
int ranNum6;
int ranDel;

void setup() {
//randomSeed for Random Number Generator (K9 first TV appearance)
randomSeed(1977);

// Setup 12 output ports for LED's
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
pinMode(14, OUTPUT);
pinMode(15, OUTPUT);
pinMode(16, OUTPUT);
pinMode(17, OUTPUT);

// start up display
// letter K
digitalWrite(6, HIGH);
digitalWrite(10, HIGH);
digitalWrite(14, HIGH);
digitalWrite(11, HIGH);
digitalWrite(8, HIGH);
digitalWrite(16, HIGH);
delay (650);
digitalWrite(6, LOW);
digitalWrite(10, LOW);
digitalWrite(14, LOW);
digitalWrite(11, LOW);
digitalWrite(8, LOW);
digitalWrite(16, LOW);
delay (500);

// number 9
digitalWrite(7, HIGH);
digitalWrite(8, HIGH);
digitalWrite(9, HIGH);
digitalWrite(11, HIGH);
digitalWrite(12, HIGH);
digitalWrite(13, HIGH);
digitalWrite(17, HIGH);
delay (650);
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
digitalWrite(11, LOW);
digitalWrite(12, LOW);
digitalWrite(13, LOW);
digitalWrite(17, LOW);
delay (500);
}

void loop() {
  
//Generate random number between 1 and 20
//more numbers than LEDS so 6 LED's not on all the time
ranNum1=random(3,19);
ranNum2=random(3,19);
ranNum3=random(3,19);
ranNum4=random(3,19);
ranNum5=random(3,19);
ranNum6=random(3,19);

//Trim random number to 12. Pin 0 not used.
if (ranNum1 > 17){
ranNum1 = 0;
}
if (ranNum2 > 17){
ranNum2 = 0;
}
if (ranNum3 > 17){
ranNum3 = 0;
}
if (ranNum4 > 17){
ranNum4 = 0;
}
if (ranNum5 > 17){
ranNum5 = 0;}
if (ranNum6 > 17){
ranNum6 = 0;
}

// Generate random delay time
ranDel=random(200,400);

//Turn on the LED's

digitalWrite(ranNum1,HIGH);
digitalWrite(ranNum2,HIGH);
digitalWrite(ranNum3,HIGH);
digitalWrite(ranNum4,HIGH);
digitalWrite(ranNum5,HIGH);
digitalWrite(ranNum6,HIGH);

delay(ranDel);

//Turn off the LED's
digitalWrite(ranNum1,LOW);
digitalWrite(ranNum2,LOW);
digitalWrite(ranNum3,LOW);
digitalWrite(ranNum4,LOW);
digitalWrite(ranNum5,LOW);
digitalWrite(ranNum6,LOW);
}

cheers paul (currently behind the village idiot!)

Look at the example in the arduino IDE, '2. Digital/Blink without delay'.

Really understand what delay() does and what is millis(). They are not the same.

hello there

That is one of the primary references I've been staring at. Delay literally stops the loop of the sketch for the duration of the delay(). Millis() is referenced continually as the loop loops until the conditions of the millis() are met.

As for the blink without delay - I can't seem to 'translate' it into this sketch. I can see what I want to do isn't that complicated but I haven't been able to do it any which way I've tried.

cheers paul

Not tested but try this

int ranNum1;
int ranNum2;
int ranNum3;
int ranNum4;
int ranNum5;
int ranNum6;
int ranDel;
boolean waiting = false;
unsigned long startTime;


void setup()
{
  //randomSeed for Random Number Generator (K9 first TV appearance)
  randomSeed(1977);

  // Setup 12 output ports for LED's
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(14, OUTPUT);
  pinMode(15, OUTPUT);
  pinMode(16, OUTPUT);
  pinMode(17, OUTPUT);

  // start up display
  // letter K
  digitalWrite(6, HIGH);
  digitalWrite(10, HIGH);
  digitalWrite(14, HIGH);
  digitalWrite(11, HIGH);
  digitalWrite(8, HIGH);
  digitalWrite(16, HIGH);
  delay (650);
  digitalWrite(6, LOW);
  digitalWrite(10, LOW);
  digitalWrite(14, LOW);
  digitalWrite(11, LOW);
  digitalWrite(8, LOW);
  digitalWrite(16, LOW);
  delay (500);

  // number 9
  digitalWrite(7, HIGH);
  digitalWrite(8, HIGH);
  digitalWrite(9, HIGH);
  digitalWrite(11, HIGH);
  digitalWrite(12, HIGH);
  digitalWrite(13, HIGH);
  digitalWrite(17, HIGH);
  delay (650);
  digitalWrite(7, LOW);
  digitalWrite(8, LOW);
  digitalWrite(9, LOW);
  digitalWrite(11, LOW);
  digitalWrite(12, LOW);
  digitalWrite(13, LOW);
  digitalWrite(17, LOW);
  delay (500);
}

void loop()
{
  if (!waiting)
  {
    //Generate random number between 1 and 20
    //more numbers than LEDS so 6 LED's not on all the time
    ranNum1 = random(3, 19);
    ranNum2 = random(3, 19);
    ranNum3 = random(3, 19);
    ranNum4 = random(3, 19);
    ranNum5 = random(3, 19);
    ranNum6 = random(3, 19);

    //Trim random number to 12. Pin 0 not used.
    if (ranNum1 > 17) {
      ranNum1 = 0;
    }
    if (ranNum2 > 17) {
      ranNum2 = 0;
    }
    if (ranNum3 > 17) {
      ranNum3 = 0;
    }
    if (ranNum4 > 17) {
      ranNum4 = 0;
    }
    if (ranNum5 > 17) {
      ranNum5 = 0;
    }
    if (ranNum6 > 17) {
      ranNum6 = 0;
    }

    // Generate random delay time
    ranDel = random(200, 400);

    //Turn on the LED's

    digitalWrite(ranNum1, HIGH);
    digitalWrite(ranNum2, HIGH);
    digitalWrite(ranNum3, HIGH);
    digitalWrite(ranNum4, HIGH);
    digitalWrite(ranNum5, HIGH);
    digitalWrite(ranNum6, HIGH);
    startTime = millis();
    waiting = true;
  }

  if (waiting && millis() - startTime >= ranDel)
  {
    //Turn off the LED's
    digitalWrite(ranNum1, LOW);
    digitalWrite(ranNum2, LOW);
    digitalWrite(ranNum3, LOW);
    digitalWrite(ranNum4, LOW);
    digitalWrite(ranNum5, LOW);
    digitalWrite(ranNum6, LOW);
    waiting = false;
  }
}

The program would be much better if it used arrays but get the timing stuff working first

Hello there

The untested code is working fine. Thank you. Looking over it I can appreciate it but just don't see how I would ever have got there. There seems to be a subtle change to the overall structure but just by adding small pieces of code at specific points.

I tried a couple of days ago to set up some arrays but had some issues, no surprise there, about the only progress I am making is being able to see that an array could be used and avoid using delay() :cold_sweat:

cheers paul

Fenrisulfr: I'd be really grateful for any and all assistance.

It's always the same programming logic with timed actions in regular intervals:

#define INTERVAL 5000L  // timing interval in milliseconds

void createNewPattern()
{
  // insert code for pattern creation here 
}

void setup() {
  createNewPattern(); // create initial pattern
}

unsigned long lastAction;
void loop() {
  if (millis()-lastAction>=INTERVAL) // INTERVAL time has passed
  {
    lastAction+= INTERVAL; 
    createNewPattern();
  }  
}

Fenrisulfr: Millis() is referenced continually as the loop loops until the conditions of the millis() are met.

Millis() definition

Returns the number of milliseconds since the Arduino board began running the current program

Also have CLEAR objective what you want to happen. I still don't know what you want with millis(). The hard part to programming is clearly know what you want to do. The other stuff, you can look up.

In my own version of the holy-crap-long-variable-name-programming-form see below.

Don't know if it will help or not. But its worth a shot since the existing stuff isn't making it clear enough for whatever reason.

unsigned long VariableToStoreTheLastTimeSomethingHappened; // You want to store the time each time your thing happens
long AmountOfTimeYouWantToWaitForSomethingToHappen = 4000; // want to wait four seconds for things to happen again

loop(){
    if (millis() - VariableToStoreTheLastTimeSomethingHappened >= AmountOfTimeYouWantToWaitForSomethingToHappen) { 
    /* The current value of the millis counter - the last time something happened was farther away than the the intended delay period?*/

        //Doing Stuff.....
        //Something Important Happens.....

        VariableToStoreTheLastTimeSomethingHappened = millis(); 
        /* set the value of this variable to the current time because now that something has happened again it needs to be updated*/
    }
}

I tried a couple of days ago to set up some arrays but had some issues

Here is a version using arrays to hold pin numbers and random numbers. This allows the pin numbers to be accessed using for loops which tidies up the program. It also uses calculated constants for the number of levels in the pin arrays whcih means that if you change the array the for loops still work.

byte ranNums[6];
const byte NUM_RANDS = sizeof(ranNums) / sizeof(ranNums[0]);
int ranDel;
const byte allOutputs[] = {6, 7, 8 , 9, 10, 11, 12, 13, 14, 15, 16, 17};
const byte NUM_OUTPUTS = sizeof(allOutputs) / sizeof(allOutputs[0]);
const byte kPins[] = {6, 10, 14, 11, 8, 16};
const byte NUM_K = sizeof(kPins) / sizeof(kPins[0]);
const byte ninePins[] = {7, 8, 9, 11, 12, 13, 17};
const byte NUM_NINE = sizeof(ninePins) / sizeof(ninePins[0]);
boolean waiting = false;
unsigned long startTime;

void setup()
{
  //randomSeed for Random Number Generator (K9 first TV appearance)
  randomSeed(1977);

  // Setup 12 output ports for LED's
  for (int p = 0; p < NUM_OUTPUTS; p++)
  {
    pinMode(allOutputs[p], OUTPUT);
  }

  // start up display
  for (int p = 0; p < NUM_K; p++)  // letter K
  {
    digitalWrite(kPins[p], HIGH);
  }
  delay (650);
  for (int p = 0; p < NUM_K; p++)
  {
    digitalWrite(kPins[p], LOW);
  }
  delay (500);
  for (int p = 0; p < NUM_NINE; p++)  // number 9
  {
    digitalWrite(ninePins[p], HIGH);
  }
  delay (650);
  for (int p = 0; p < NUM_NINE; p++)
  {
    digitalWrite(ninePins[p], LOW);
  }
  delay (500);
}

void loop()
{
  if (!waiting)
  {
    //Generate random number between 1 and 20
    //more numbers than LEDS so 6 LED's not on all the time
    for (int r = 0; r < NUM_RANDS; r++)
    {
      ranNums[r] = random(3, 19);
    }
    
    //Trim random number to 12. Pin 0 not used.  <<<< Not sure what this is doing ?
    for (int p = 0; p < NUM_RANDS; p++)
    {
      if (ranNums[p] > 17)
    {
      ranNums[p] = 0;
      }
    }

    // Generate random delay time
    ranDel = random(200, 400);

    //Turn on the LED's

    for (int r = 0; r < 6; r++)
    {
      digitalWrite(ranNums[r], HIGH);
    }
    startTime = millis();
    waiting = true;
  }

  if (waiting && millis() - startTime >= ranDel)
  {
    //Turn off the LED's
    for (int r = 0; r < 6; r++)
    {
      digitalWrite(ranNums[r], LOW);
    }
    waiting = false;
  }
}

As before, untested but it compiles. There are further improvements that could be made including using functions to do some of the work and passing parameters to them so that the same function could be used for different purposes.

http://www.gammon.com.au/blink

@Goofballtech that LongVariableNameHelpsExplainThings example was awesome! I should use that in some of my code!

Thanks!

If you are trying to learn something do NOT use random numbers because they are, by definition, unpredictable. Learn how to get the process to work with fixed values that are hard coded into your program. Upload another program when you want to try different values.

If you are trying to learn something do NOT use 12 leds. Just use 2.

When you know how to make the simple system work then you can add more LEDs and add in random values. Even then, do it it step by step so you can immediately identify what causes a problem.

This is not "baby" advice for a beginner. This is how experienced programmers approach new concepts.

...R

So, let's start by saying there is nothing at all wrong with using delay(). If there was, why would the function exist?

For your particular application as I understand it, delay() is a perfectly satisfactory solution.

So, the question then becomes: What is your sketch not doing that makes you want to switch to millis()?

If your sketch is working to your satisfaction, you're done!

Now, for your particular sketch as written, changing it to millis() is a bit of a hassle. Conceptually, the hardest thing is to realise that loop() gets called immediately after you have exited loop. This makes little difference to using delay(), but it makes a huge difference to using millis(). Essentially, every line that occurs inside the loop has to be protected by a "if millis()..." type statement, or it will get executed. Additionally, each time the loop gets executed, your local variables are re-declared so you get different random numbers. In other words, the program would need to be changed quite a bit (if you really want to do it, you need to store the random numbers in static variables, and only replace them when the "if millis" is triggered). It is not as simple as "replace delay() with millis()". You would get quite strange results if you did that.

There are lots of things wrong with using delay() but not in this context

const int noLeds = 6;
byte ranNum[noLeds];
unsigned long mark;

void setup(){
  for(int i = 6; i<=17; i++) pinMode(i, OUTPUT);
  randomSeed(1977);//randomSeed for Random Number Generator (K9 first TV appearance)
  // start up display
  memcpy(ranNum, (byte[]){6, 10, 14, 11, 8, 16}, 6);// letter K
  showLeds();
  delay (650);//no reason to avoid delay in setup()
  ledsOff();
  delay(500);
  memcpy(ranNum, (byte[]){7, 8, 9, 11, 12, 13}, 6);//digit 9
  digitalWrite(17, 1);//last led in digit 9
  showLeds();
  delay(650);
  ledsOff();
  delay(500);
  mark = millis();//set timestamp to now
}//setup()

void loop(){
  if(mark>=millis()){//timestamp reached
    mark += random(200, 400);//new timestamp
    ledsOff();
    generate();
    showLeds();
  }//if(timestamp reached)
}//loop()

void showLeds(){
  for(int i = 0; i<noLeds; i++) if(ranNum[i]) digitalWrite(ranNum[i], 1);
}//showLeds()

void ledsOff(){
 for(int i = 3; i<=17; i++) digitalWrite(i, 0);
}//ledsoff()

void generate(){
  for(int i = 0; i<noLeds; i++){
    byte n = random(3, 19);
    if(n>17) n = 0;
    ranNum[i] = n;
  }//for(i)
}//generate()

arduinodlb: So, let's start by saying there is nothing at all wrong with using delay(). If there was, why would the function exist?

For your particular application as I understand it, delay() is a perfectly satisfactory solution.

So, the question then becomes: What is your sketch not doing that makes you want to switch to millis()?

Fenrisulfr is one of the forums users who always tells just a small part of the whole story.

Most likely he wants to make half a dozen of appliances work with one Arduino board at the same time.

But when he starts a new thread, he only tells about one appliance connected to the Arduino. Then, after having half a dozen or so sketches that work with just one appliance, he wants to copy all the stuff together into one file ("combine sketches") and run them in multitasking mode.

Hello there

misterGreen - I agree completely. A clear idea is always good. Unfortunately despite my knowing what I want to do and with all the reference material out there I still just come to a grinding halt when I try implementing the idea. There is a possibility that I'm clinically thick :grin:

I want to use Millis() to avoid the 'blocking' effect of a delay(). Although the code is just free running it is going to be added to an existing sketch that operates to servos in a distinct way when a radio signal is applied to the relevant pins for each servo. So with millis() on all parts of the sketch all 3 parts, 2 servos, 1 LED matrix, can rub along nicely on a single arduino.

Goofballtech - I see what your saying. I think a lot of my problem with 'getting it', apart from the above diagnosis, is it's all so abstract. At least that's the best way I can describe it.

Nick Gammon - That's one of the clearer sketch I've seen on use of millis() over delay(). I just don't seem to be able to transpose what I'm seeing to make the changes required. It's not for want of trying either. I am quite literally turning into a monster when I try to do it, just ask the wife, I find it infuriating that I am unable to do the simplest change. So much so that I have to walk away from my laptop before I do something rash to it. Probably not the best idea but constantly chewing over the idea trying again and again and I still can't do it days later is 'doing my box in'. On the flip side I can design and make my own PCB's with my eyes closed and add all the components for a working circuit that will use the arduino and sketches.

UKHeliBob - Never any doubt here. That untested also works. The comment you make about the 0 not being connect in the comment of the original sketch is where the chap wanted any random numbers over 12 assigned to pin 0 which was connected to nothing. This was to keep the maximum number of LEDs lit at any one time at 6 tops. I have changed which pins are actually used to keep pins 2,3,4,5 clear for the servo I/0.

cheers paul

nilton61: There are lots of things wrong with using delay() but not in this context

What other context were we talking about? There's lots of things wrong with using anything in the wrong context. The problem is religious zealots who put out the "ohhh, never use a delay" ill-informed mantra to people who do not have sufficient understanding to debate the issue. Delays() are fine. As are millis(). As are everything else, used appropriately.

That being said, nilton61's sketch should give the OP a good idea of how to use millis() for his application.

The problem is religious zealots who put out the "ohhh, never use a delay" ill-informed mantra to people who do not have sufficient understanding to debate the issue.

That is, of course, true but this thread started with the express question as to how millis() could be used instead of delay(). Although not stated initially, the OP seems to have realised that this was a requirement if/when extra functionality was added to the program which he plans to do.

As far as possible I try to avoid the knee jerk "look at BWD" response to questions or problems caused by using delay() inappropriately because it often does not help in the context of the problem. The main dilemma I have is deciding whether to make suggestions about possible changes to code, which can lead to painful exchanges until the OP gets the point or gets fed up, or to do the coding myself and post it here as a solution.

UKHeliBob: That is, of course, true but this thread started with the express question as to how millis() could be used instead of delay(). Although not stated initially, the OP seems to have realised that this was a requirement if/when extra functionality was added to the program which he plans to do.

I agree. The problem is I have seen so many people say "don't use delay() for anything" that in the case of the OP, it wasn't clear why he wanted to use millis(), so I had to be sure it wasn't simply because he had been told not to use delay() in loops, without understanding when to choose millis(), and when to choose delay().

Things that are wrong with using delay():

  • It is a bad habit
  • It is blocking code
  • It creates more problems than it solves
  • It promotes QD solutions instead of planning

I could go on with this forever. So, no, delay() is not fine. The same reasoning was applied to DDT and asbetos once but nobody serioulsly wants to defend that now.