Preventing delay() from blocking programs - Arduino.

Thread Starter

SalceyForest

Joined Nov 6, 2015
11
I've got a confession, I don't have an Arduino and have never programmed one but http://www.allaboutcircuits.com/projects/build-a-desktop-industrial-automation-trainer/ has caught my eye, as have many examples in here. This post uses an Arduino to sequence through a hard-coded output list, with each transition delayed by a second. Unless interrupts are involved, the controller is just stuck there during that second, doing nothing and responding to less. All this is a general problem relating to the difference between electronics and the real world. In real life, things move sloooowly!

What I've done here is replace the delay() block with a simple test. If the time isn't up yet, the processor can go and take can of other business. As part of a full solution, this can make the difference between a sulky, sluggish unit and a lively, responsive one. Imagine looking for a button press during the delayed part of the process.

While I was at it, I used an array of timers, allowing a different time for each step. What I haven't done is actually try it, I wonder if someone could give it a go and point out any glaring errors?

Code:
/*  3 INPUT LOGIC GATE SIMULATOR
*  APRIL 8, 2016
*  BY DON WILCHER
* modified May 13th by Steve Root
*
* This code will provide digital inputs to the  Velocio ACE PLC (Programmable
* Logic Controller) using the following Truth Table Inputs.
* The output of the Logic Gate will be visible by the integrated
* LED of the ACE PLC.
*
*           3 Input Logic Gate Simulator
*                 INPUTS
*                A |  B | C
*               ___|____|____
*                0 | 0 | 0
*                0 | 0 | 1
*                0 | 1 | 0
*                0 | 1 | 1
*                1 | 0 | 0
*                1 | 0 | 1
*                1 | 1 | 0
*                1 | 1 | 1
*
*/


// Arduino Pin assignments
int A = 13;
int B = 12;
int C = 11;

// Timer declaration
bool SeqTimer(int, bool *);

// Timer definitions
#define NUMTIMERS 8
unsigned long TimeDelays[NUMTIMERS}={1000000, 500000, 200000, 100000, 100000, 200000, 500000, 1000000);
int tIndex = 0;
bool tReset = true;


bool SeqTimer(int tIndex, bool *tRst)
{
    static unsigned long TargetList[NUMTIMERS];

    if(*tRst== true)
    {
        TargetList[tIndex] = micros() + TimeDelays[tIndex];
        *tRst= false;
    }
    else
    {
        if(micros() > TargetList[tIndex])
        {
            return true;
        }
    return false;
    }

void setup()
{
  // initialize digital pins 1, 2, and 3 as outputs
  pinMode(A, OUTPUT);
  pinMode(B, OUTPUT);
  pinMode(C, OUTPUT);
}

void loop()
{
    if (SeqTimer(tIndex, &tReset) == true)
    {
        tIndex++; // Next timer in list
        if(tIndex >= NUMTIMERS)
        {
            tIndex = 0;
        }
        // write the outputs
        if(tIndex & 0x1) DigitalWrite(C, HIGH);
        else DigitalWrite(C, LOW);
        if(tIndex & 0x2) DigitalWrite(B, HIGH);
        else DigitalWrite(B, LOW);
        if(tIndex & 0x4) DigitalWrite(A, HIGH);
        else DigitalWrite(A, LOW);
        // Timer will reset on next scan
        tReset = true;
    }
// else do other stuff!
}
 
Last edited:

Don Wilcher

Joined Jun 22, 2015
44
Hi SalceyForest,

Thank you for your input. The idea behind the project was to introduce an introductory lesson on Programmable Logic Controllers to the novice. As a Technical Educator, my goal is to teach basic concepts in electrical-electronics with application emphasis on robotics and industrial automation principles. As an Electrical Engineer, I am acutely aware of advanced software programming techniques used in embedded controllers. The overall goal of the project was to allow the reader to build a low cost and uncomplicated programmable Industrial Automation Trainer. Basic programming concepts and techniques were the secondary educational goals for the project.

You have identified a potential problem (depending on the application) which warrants testing. Though you do not have the Arduino, you can use the Arduino compiler (website: Arduino.cc) to ensure that your programming instructions are compatible with the Arduino library functions. If there are problems, the compiler will flag the errors. As a recommendation, please provide solutions that have correct validations rather than offering only criticism of a fully functional and tested application. Suggestions have no value if you have not properly tested but only speculated. I've worked on multi-million dollar industrial, consumer, and automotive embedded systems. If I had ever presented such a solution without proper validation results, my engineering qualifications and knowledge would have been scrutinized after that. Validation is an essential habit for the professional or the novice practitioner.

I appreciate your readership and the offer of a more advanced programming technique. Bear in mind; however, that the difference in time is not critical and removes visibility of the function for the novice programmer. I think your proposal is interesting, and I’d like to offer a challenge to other readers to test your code and provide additional feedback. I am accepting my own challenge and will report on your coding, soon.
 

dannyf

Joined Sep 13, 2015
2,197
you could use the millie() function to do it.

for example:

Code:
uint8_t isTime0(unsigned time eTime) {
  static unsigned long pTime=0; //previous time
  unsigned long tmp=millis(); //take current time
  
  if (tmp >= pTime + eTime) {  //enough time has elapsed
    pTime=tmp; //save the current time
    return 1;  //time's up
  } else return 0;
}
You can copy the same routines over a few times for addition "timers".

Alternatively, use a struct to hold the values and one timer routine will work.
 

Thread Starter

SalceyForest

Joined Nov 6, 2015
11
Hi Don, thanks for your reply and the slap on the wrist!

Please appreciate that the post was not a criticism of your article per se, which was after all on a mostly unrelated topic, but actually of the very provision of a function in the library (almost all libraries) which is specifically designed to stop the hardware dead in its tracks. The work-round is small enough to be trivial and from my own experience (PICs, not Arduinos, and not at home) can vastly improve overall functionality. Sorry about the lack of validation, but I thought the principle was important. Thanks for offering the advice about Arduino.cc which I will take up tomorrow.

As a ps, I'm aware of a small style error which has given a SeqTimer input parameter the same name as a global variable. Poor style but it will still work.
 

Thread Starter

SalceyForest

Joined Nov 6, 2015
11
you could use the millie() function to do it.

for example:

Code:
uint8_t isTime0(unsigned time eTime) {
  static unsigned long pTime=0; //previous time
  unsigned long tmp=millis(); //take current time
 
  if (tmp >= pTime + eTime) {  //enough time has elapsed
    pTime=tmp; //save the current time
    return 1;  //time's up
  } else return 0;
}
You can copy the same routines over a few times for addition "timers".

Alternatively, use a struct to hold the values and one timer routine will work.
I used micros because why not, basically! I don't see what advantage can be had in copying routines over simply #defining a different number?
 

dannyf

Joined Sep 13, 2015
2,197
Because your implementation isn't terribly efficient.

Adapting my routine to your application would be something like this:

Code:
  if isTime0(TimeDelays[index]) {//time is up
    //do something
    index=(index>=NUMTIMERS)?0:(index+1); //advance index
  } else {  
    //time is not yet up
    //do someting else
  }
 

Don Wilcher

Joined Jun 22, 2015
44
Hi Don, thanks for your reply and the slap on the wrist!

Please appreciate that the post was not a criticism of your article per se, which was after all on a mostly unrelated topic, but actually of the very provision of a function in the library (almost all libraries) which is specifically designed to stop the hardware dead in its tracks. The work-round is small enough to be trivial and from my own experience (PICs, not Arduinos, and not at home) can vastly improve overall functionality. Sorry about the lack of validation, but I thought the principle was important. Thanks for offering the advice about Arduino.cc which I will take up tomorrow.

As a ps, I'm aware of a small style error which has given a SeqTimer input parameter the same name as a global variable. Poor style but it will still work.
Hi Don, thanks for your reply and the slap on the wrist!


Please appreciate that the post was not a criticism of your article per se, which was after all on a mostly unrelated topic, but actually of the very provision of a function in the library (almost all libraries) which is specifically designed to stop the hardware dead in its tracks. The work-round is small enough to be trivial and from my own experience (PICs, not Arduinos, and not at home) can vastly improve overall functionality. Sorry about the lack of validation, but I thought the principle was important. Thanks for offering the advice about Arduino.cc which I will take up tomorrow.

As a ps, I'm aware of a small style error which has given a SeqTimer input parameter the same name as a global variable. Poor style but it will still work.
SalceyForest:
Thanks for your reply. I'm definitely interested in validating your code alternative. I do appreciate you posting a comment. When time permits, I will do the validation and let you know of the results. Please keep commenting!!!!
 

Don Wilcher

Joined Jun 22, 2015
44
SalceyForest:
Thanks for your reply. I'm definitely interested in validating your code alternative. I do appreciate you posting a comment. When time permits, I will do the validation and let you know of the results. Please keep commenting!!!!
 

Don Wilcher

Joined Jun 22, 2015
44
Hi dannyf

Thanks for providing an alternative code solution. Once the validated, I let you know the results. Thanks and keep commenting!!!
 
Top