timing without using delay

I wrote the code for a piece of art I just built in a big hurry (i.e. just hours before the gallery doors opened). The easiest way for me to create the timings I wanted between events was with delay statements. Trouble is I really need to implement interrupts in the code, and I can't use delay in my interrupt handling functions. So what I am looking for is general advice in the ways one could create the timing they desire without the use of the delay command.

Thanks in advance for any advice.


I can point here http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

You may get better results if you post your code.

yea, but I'm looking more for how to fish than people re-writing my fish. How's that for a mixed metaphor? :-D

It's more a generic question that's been on my mind, i.e. learning all the possible ways to create delays without using delay(), than just this one case on this one piece.

Oh and in that example you link to - it uses millis...Can't use millis with interrupts either :-(

The arduino atmega chip has a mind bending number of permutations for using timers and interrupts to create delays. The following tutorial may help you see some of the possibilities: http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106

Knowing a little more about the kind of application you want would help pointing you in the right direction. If you say a little more about the functionality you require, it would be easier to provide relevant guidance.

Can't use millis with interrupts either

Why do you think that?

When you're an interrupt handler (i.e. one you registered with attachInterrupt()) other interrupts are disabled (since the core doesn't enable nested interrupts) so the timer 0 overflow interrupt that increments the value returned by millis() stops. You can still call millis(), but it will always give the same answer as long as you're in the interrupt handler. I think. :)

Because well behaved interrupt handler code should only be active for a relatively short period of time, the other interrupts will still function. In the case cited, as long as other interrupt handlers use less then a millisecond, the millis() handler will be ready to do its stuff when the timer0 reaches one millisecond.

Typically, interrupt handlers used for timing will do little more then checking and perhaps changing a few clock registers and updating a variable or two that is shared with the non interrupt code. Think of the interrupt as little more then a trigger with most of the time intensive work done outside the handler.

If the OP wants to do a lot timed processing that overlaps, then he may want to look at using something like a state machine that is sequenced by timer events.

Interrupt service routines (ISRs) should be very short chunks of code, to avoid problems like those being discussed now.

For example, I use interrupts in my DTMF decoder sketch. The entire ISR is

void dtmf_isr()
  tone_detected = 1;

In my loop() function, I check this variable each time through and act on it if needed. This way, other interrupts will still function correctly.


You all make some good points, so here is my code (up until now). Before you read it let me describe what is going on - the viewer approaches the piece (a "genie") and places their beverage into its hand. The weight of the drink activates a lever switch and the Arduino runs the main routine. Because void loop is running in a loop the piece doesn't react immediately - that is where I want to use the first interrupt. Once the "main show" function is running, I want the removal of the drink to send execution back to void loop. The code you see here is what I wrote in the gallery with little time to spare before the show opening - and having been awake for 28 hours to boot. The eventual program will have more action - but I want to try to incorporate those interrupts before I head down that path. Thanks for looking!

Pix to help explain the thing - http://www.flickr.com/photos/shifzr/2055471345/ - http://www.flickr.com/photos/hackaday/2055635032/

int inPin = 7; // lever switch connected to pin 7 int val = 0; // switch read value

/* Lynxmotion channels: 2 - fortune card servo 7 - eyes servo 11 - head servo 15 - jaw servo 16 - chamber LED 17 - status check LED 19 - status check LED 22 - panel LED 24 - panel LED 26 - panel LED 28 - hand LED green 29 - hand LED red serial Rx

Arduino channels: 1 - serial Tx to Lynxmotion 7 - arm switch */

void setup() { Serial.begin(9600); pinMode(inPin, INPUT); // sets the digital pin 7 as input }

void loop() //loop contains default resting behavior { if (!digitalRead(inPin)) // read the arm switch, if not down run default { Serial.println("#17H #19L #24L #22L #25H #26L #28H #29L"); //initialize LEDs Serial.println("#2 P1900 S350"); //slowly return fortune servo Serial.println("#7 P1500 #15 P1500 #16L"); //eyes slow, mouth closed Serial.println("#11 P600 S200 #17H #19L"); //head slow delay (2000); Serial.println("#11 P1600 S150"); //head slow delay (2000); } else //if arm is down run the following function { run_main_show(); } }

void run_main_show() { Serial.println("#7 P1520 #15 P600 #11 P600 #17L #19H #28L #29H"); //eyes stop, head back, mouth closed delay (3000); Serial.println("#7 P1400"); //move eyes fast Serial.println("#11 P700 S300"); //head faster //Serial.println("#15 P600"); //mouth open delay (2000); Serial.println("#11 P1300 S300"); //head faster Serial.println("#15 P1500"); //mouth closed delay (2000); Serial.println("#7 P1520 #15 P1500 #11 P600 #24H"); //eyes stop, head back, mouth closed delay (4000); Serial.println("#15 P600"); //mouth open quick delay (150); Serial.println("#15 P1500"); //mouth closed quick delay (150); Serial.println("#15 P600"); //mouth open quick delay (150); Serial.println("#15 P1500"); //mouth closed quick delay (150); Serial.println("#15 P600"); //mouth stays open 2 seconds delay (2000); Serial.println("#16H #24L"); //chamber lights on, panel light off Serial.println("#15 P1500"); //mouth closed delay (6000); Serial.println("#2 P600 S500 #22H #16L"); //fortune kicks first time, fortune panel light on, chamber light off delay (1500); Serial.println("#2 P1500"); //slowly return fortune servo part way back delay (1500); Serial.println("#2 P600 S500 #22H"); //fortune kicks second time, fortune panel light on delay (2000); Serial.println("#2 P1900 S350"); //slowly return card servo Serial.println("#26H #22L"); //remove drink light on, fortune light off delay (10000);


You could replace the calls to delay() with something like:

start = milliis();
while (millis() - start < 2000) {
if (digitalRead(drinkPin) == LOW) {

which you could #define to something like:


What you really want, though, is to define the actions in data, not code, e.g.:

char *main_show_actions = {
#7 P1520 #15 P600 #11 P600 #17L #19H #28L #29H”,
#7 P1400”,
// …

int main_show_delays = {
// …

char *loop_actions = {
// …

int loop_times = {
// …

And then have a pointer:

char ***current_actions = &loop_actions;
int **current_times = &loop_times;
int index = 0;

Then, your loop is something like:

void loop() {
start = millis();
while (millis() - start < (*current_times)[index]) {
drinkPinReading = digitalRead(drinkPin);
if (drinkPinReading != previousDrinkPinReading) {
previousDrinkPinReading = drinkPinReading;
index = -1;
if (drinkPinReading == LOW) {
current_actions = &loop_actions;
current_times = &loop_times;
} else {
current_actions = &main_show_actions;
current_times = & main_show_times;
if ((*current_actions)[index] == NULL) {
index = 0;

wow....well that there is a WHOLE lot of Arduino for me to chew on, that's for sure.

Good thing it's nearly the weekend :-)

As I read through some of the other responses I get the feeling that I was misinterpreting how attachInterrupt and delay and millis may or may not interact...I will check those assumptions this weekend.

btw - I'm starting to have flashbacks to 20 years ago when I decided to be a sculptor instead of a programmer - and now they are merging...merging like an egg and the sidewalk from 10 stories up ;-)

Okay, after reading thru I get how (and why) to keep my various actions and such in arrays.

What do these lines do and what is the significance of the splats and ampersands?

char ***current_actions = &loop_actions; int **current_times = &loop_times;

char ***current_actions” is a pointer to the currently used array of actions. Each action is a string (char *), and an array is pointer to strings (char **), so the pointer to the array is a triple-removed pointer (char ***) current_actions points to an array, array members point to string, strings point to chars. This is about the level of indirection where I give up an start defining more complex data structures (action_table *current_actions), but that’ll add an additional level of coding complexity if you’re not fully C-literate.

Similarly , current_times is a point to an array of ints.

I think the amperstands are redundant (perhaps not in C++); they ensure that you pass the address of the arrays rather than something else.

Okay - first off this is great stuff, and thanks for all the advice.

Secondly, though, I have to mention that my niche in teaching microcontrollers is teaching beginners how to program them. While some of my students could be shown arrays, and how they work, I don;t have the majority of my students for a long enough stretch of time to go there. Any chance any of you feel like having a crack at this one from other angles? And not necessarily the tersest or most efficient ways, but some that are easier for the relative newcomer to get. I'll keep all the examples together to show them of course...options are important.

Thanks again for the help btw!

Yea, I think westfw is right and I added one level of indirection that didn’t need to be there. Still, this would probably still look confusing. There’s a String library that I’ve been hoping to add to the distribution that might simplify things a bit.