Move now, don’t delay(); the metro library

The classic “hello world” of the Arduino world includes something like:

void loop()
{
  digitalWrite(ledPin, HIGH);   // sets the LED on
  delay(1000);                  // waits for a second
  digitalWrite(ledPin, LOW);    // sets the LED off
  delay(1000);                  // waits for a second
}

Simple sample code for robotic vehicles may include something similar, such as

//Move Forward for 10 seconds
advance (255,255);
delay(10000);

where the advance function is previously defined. This approach, using delay(), works fine until you want your robot to do something else while moving, like check sensors for obstacles, or see if a button was pushed to turn it off, or updating its position based on sensor input. The problem of course, is that while the delay is running, the cpu does nothing (although it will process interrupts). So clearly using delay() for this purpose is not a good idea.

Rather than having your code sit idle at a point in the loop function, spinning its figurative wheels, you want the software to continue looping through the main loop, but stopping when a set time is reached. One approach is to use a timer interrupt. Have your code continue through the loop, but set an interrupt service routine that sets a flag, signifying the time has elapsed and it’s time to stop doing what you were doing (e.g., moving forward) and do something else. An introduction to timers and interrupts can be found on the Let’s Make Robots site, and you should also check the Arudino reference page on interrupts .  If you’d like to keep things simpler, there are Arduino libraries for using timer interrupts as well.

An alternative approach is to use the metro library.  The metro library provides an alternative to using timer interrupts.  Because it does not use interrupts, the code in your loop will need to check to see if the timer has expired or not.  The sample code below works on my robot that uses an Arduino-compatible Romeo controller and RP5 chassis.  It was based on the example provided by DFRobot but adds a) using switch 1 on the Romeo board as a start/stop switch and b) instead of using delay(), it uses the Metro library.

The key section of the code is the nextMovement function. It’s called every time the main loop executes. A timer called movementMetro was already initialized as part of the setup. This nextMovement function checks if the timer has gone off, and if it has, changes the movement case to the next in line and resets the value for the appropriate time. One tricky part is that the timer value is for the NEXT movement, not the one in the movement just completed.

void nextMovement (int next,unsigned long wait) // Set case and delay for next movement
  {
    if (movementMetro.check() == 1) {
      movement = next;
      movementMetro.interval(wait);
    }
  }

Here’s the full, working code. When run, you need to push button 1 once the code is loaded. The robot then moves forward, turns, move forward, reverses, turns, and reverses again, with pauses between each movement. You can stop and restart the cycle by using switch 1.

#include <Metro.h>

// Setup movement timer using the Metro library
Metro movementMetro = Metro(4000);

// Buttons vars

const int key_S1_5 = 7;

int state = false; // Off if 0, On if 1

int buttons_check(){
 // This function checks button 1 and returns
 // 0 if no button pressed, 1 if pressed
 // Priorities: S1, S2, S3, S4, S5
 int w = analogRead(key_S1_5);

#define vS2 130
 if ( w < vS2/2 ){
  return 1;
}
 return 0;
}//End buttons_check()

 // Set up for running motors
int E1 = 5;  // digital output pin sending PWM power to the motor
int E2 = 6;  // digital output pin sending PWM power to the motor
int M1 = 4;  // controls is motor moves forward or backwards
int M2 = 7;  // controls is motor moves forward or backwards

void forward(byte a,byte b)		//Move forward
        {
          analogWrite(E1,a);
          digitalWrite(M1,HIGH);
          analogWrite(E2,b);
          digitalWrite(M2,HIGH);
        }

void stop(void)                    	//Stop
        {
          digitalWrite(E1,LOW);
          digitalWrite(E2,LOW);
        }

void backward (byte a,byte b)		//Move backward
        {
          analogWrite (E1,a);
          digitalWrite(M1,LOW);
          analogWrite (E2,b);
          digitalWrite(M2,LOW);
}

void Left (byte a,byte b)		//Turn left
        {
          analogWrite (E1,a);
          digitalWrite(M1,LOW);
          analogWrite (E2,b);
          digitalWrite(M2,HIGH);
        }
void Right (byte a,byte b)		//Turn right
        {
          analogWrite (E1,a);
          digitalWrite(M1,HIGH);
          analogWrite (E2,b);
          digitalWrite(M2,LOW);
        }
int movement = 1; // movement controls which movement (forward, turn, etc.)

void nextMovement (int next,unsigned long wait) // Set case and delay for next movement
  {
    if (movementMetro.check() == 1) {
      movement = next;
      movementMetro.interval(wait);
    }
  }

void setup(){
    for(int i=4;i<=7;i++)
    pinMode(i, OUTPUT);
    Serial.begin(9600);
}// End setup
void loop(){

// This section checks button 1, which functions as stop/start switch, initially stopped
 int button = buttons_check();
 if ( button == 1 ) {
 // Button 1 has been pressed
  state = !state;
 }
 Serial.println(state);

 // Whenstopped after running, reset to original start mode
 if (state == false) {
   stop(); //Stop
   movement = 1;
   movementMetro.interval(4000);
 }
 else {
   Serial.println(movement);

 // Each case sets movement command, signals next movement, and resets timer for duration OF NEXT MOVEMENT
   switch (movement) {
     case 1:
       forward(255,255); //Forward at full speed
       nextMovement(2, 5000);
       break;
     case 2:
       stop(); //Stop
       nextMovement(3, 650);
       break;
     case 3:
       Right(255,255);  //Right at full speed
       nextMovement(4, 5000);
       break;
     case 4:
       stop(); //Stop
       nextMovement(5, 4000);
       break;
     case 5:
       forward(255,255); //Forward at full speed
       nextMovement(6, 5000);
       break;
     case 6:
       stop(); //Stop
       nextMovement(7, 4000);
       break;
     case 7:
       backward(255,255); //backward at full speed
       nextMovement(8, 5000);
       break;
     case 8:
       stop(); //Stop
       nextMovement(9, 650);
       break;
     case 9:
       Left(255,255);  //Left at full speed
       nextMovement(10, 5000);
       break;
     case 10:
       stop(); //Stop
       nextMovement(11, 4000);
       break;
     case 11:
       backward(255,255); //backward at full speed
       nextMovement(12, 5000);
       break;
     case 12:
       stop(); //Stop
       nextMovement(1, 4000);
       break;
   }
 }
}// End loop

Nothing fancy, but it was my start on getting my project underway and moving beyond using delay().

One thought on “Move now, don’t delay(); the metro library

  1. Look like a good approach! I used something much less sophisticated for my AVC robot last year; just read a millisecond timer, checked the modulus and ran one piece of code every 20ms and another piece every 100ms. 🙂 Coincidentally, uchobby just posted an article on this very topic, a different take on it, I guess. http://www.uchobby.com/index.php/2012/01/21/replacing-delay-in-arduino-sketches-istime-to-the-rescue/ Take it easy –Michael

Leave a Reply

Your email address will not be published. Required fields are marked *

The maximum upload file size: 512 MB. You can upload: image, audio, video, document, spreadsheet, text. Links to YouTube, Facebook, Twitter and other services inserted in the comment text will be automatically embedded. Drop files here