SeveralThingsAtTheSameTime Example

From ArduinoInfo
Jump to navigation Jump to search

Example showing how to do SeveralThingsAtTheSameTime


Taken from the example by Robin2 on the Arduino Forum. See:
http://forum.arduino.cc/index.php?topic=223286.0

Also see: The TIME and TimeAlarm libraries here:
http://www.pjrc.com/teensy/td_libs_Time.html
http://www.pjrc.com/teensy/td_libs_TimeAlarms.html


There have been a few occasions recently where newcomers seemed to have difficulty applying the "blink without delay" and "state" concepts to a sketch that is intended to manage a number of concurrent actions.

Its very time consuming to respond with suitable code examples - particularly as every case is a little different.

I have prepared the example sketch (below) in the hope that it will be a suitable model. I have tested it on an Uno and a Mega. I have called it "SeveralThingsAtTheSameTime.ino".

My sketch uses the concept in "blink without delay" to cause three LEDs to blink at different intervals, a fourth LED is controlled by a button and a servo sweeps back and forth at two different speeds. The idea is to demonstrate how different processes can be accommodated in the same general framework.

It also uses the "state machine" concept to manage the various activities and enable the different functions to determine what to do.

I have deliberately designed the sketch as a series of short functions. Short pieces of code are much easier to understand and debug and it will be an easy matter for a user to delete functions that they don't need or to duplicate and modify functions if they need more of the same (for example to flash 5 LEDS at different intervals).

There is a case for saying there is too much in the sketch but I concluded that the larger number of activities is a better demonstration of the capabilities of this approach.


Questions, suggestions: email terry@yourduino.com




// SeveralThingsAtTheSameTimeRev1.ino

// An expansion of the BlinkWithoutDelay concept to illustrate how a script
//  can appear to do several things at the same time

// this sketch does the following
//    it blinks the onboard LED (as in the blinkWithoutDelay sketch)
//    it blinks two external LEDs (LedA and LedB) that are connected to pins 12 and 11.
//    it turns another Led (buttonLed connected to pin 10) on or off whenever a button
//       connected to pin 7 is pressed
//    it sweeps a servo (connected to pin 5) back and forth at different speeds

//  One leg of each LED should be connected to the relevant pin and the other leg should be connected to a
//   resistor of 470 ohms or more and the other end of the resistor to the Arduino GND. 
//   If the LED doesn't light its probably connected the wrong way round.

//  On my Uno and Mega the "button" is just a piece of wire inserted into pin 7. 
//   Touching the end of the wire with a moist finger is sufficient to cause the switching action
//   Of course a proper press-on-release-off button switch could also be used!

//  The Arduino is not capable of supplying enough 5v power to operate a servo
//    The servo should have it's own power supply and the power supply Ground should
//      be connected to the Arduino Ground.

// The sketch is written to illustrate a few different programming features.
//    The use of many functions with short pieces of code. 
//       Short pieces of code are much easier to follow and debug
//    The use of variables to record the state of something (e.g. onBoardLedState) as a means to
//       enable the different functions to determine what to do.
//    The use of millis() to manage the timing of activities
//    The definition of all numbers used by the program at the top of the sketch where 
//       they can easily be found if they need to be changed

//=======

// -----LIBRARIES

#include <Servo.h>

// ----CONSTANTS (won't change)

const int onBoardLedPin =  13;      // the pin numbers for the LEDs
const int led_A_Pin = 12;
const int led_B_Pin = 11;
const int buttonLed_Pin = 10;

const int buttonPin = 7; // the pin number for the button

const int servoPin = 5; // the pin number for the servo signal

const int onBoardLedInterval = 500; // number of millisecs between blinks
const int led_A_Interval = 2500;
const int led_B_Interval = 4500;

const int blinkDuration = 500; // number of millisecs that Led's are on - all three leds use this

const int buttonInterval = 300; // number of millisecs between button readings

const int servoMinDegrees = 20; // the limits to servo movement
const int servoMaxDegrees = 150;


//------- VARIABLES (will change)

byte onBoardLedState = LOW;             // used to record whether the LEDs are on or off
byte led_A_State = LOW;           //   LOW = off
byte led_B_State = LOW;
byte buttonLed_State = LOW;

Servo myservo;  // create servo object to control a servo 

int servoPosition = 90;     // the current angle of the servo - starting at 90.
int servoSlowInterval = 80; // millisecs between servo moves
int servoFastInterval = 10;
int servoInterval = servoSlowInterval; // initial millisecs between servo moves
int servoDegrees = 2;       // amount servo moves at each step 
                           //    will be changed to negative value for movement in the other direction

unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousOnBoardLedMillis = 0;   // will store last time the LED was updated
unsigned long previousLed_A_Millis = 0;
unsigned long previousLed_B_Millis = 0;

unsigned long previousButtonMillis = 0; // time when button press last checked

unsigned long previousServoMillis = 0; // the time when the servo was last moved

//========

void setup() {

 Serial.begin(9600);
 Serial.println("Starting SeveralThingsAtTheSameTimeRev1.ino");  // so we know what sketch is running

     // set the Led pins as output:
 pinMode(onBoardLedPin, OUTPUT);
 pinMode(led_A_Pin, OUTPUT);
 pinMode(led_B_Pin, OUTPUT);
 pinMode(buttonLed_Pin, OUTPUT);

     // set the button pin as input with a pullup resistor to ensure it defaults to HIGH
 pinMode(buttonPin, INPUT_PULLUP);

 myservo.write(servoPosition); // sets the initial position
 myservo.attach(servoPin);

}

//=======

void loop() {

     // Notice that none of the action happens in loop() apart from reading millis()
     //   it just calls the functions that have the action code

 currentMillis = millis();   // capture the latest value of millis()
                             //   this is equivalent to noting the time from a clock
                             //   use the same time for all LED flashes to keep them synchronized

 readButton();               // call the functions that do the work
 updateOnBoardLedState();
 updateLed_A_State();
 updateLed_B_State();
 switchLeds();
 servoSweep();

}

//========

void updateOnBoardLedState() {

 if (onBoardLedState == LOW) {
         // if the Led is off, we must wait for the interval to expire before turning it on
   if (currentMillis - previousOnBoardLedMillis >= onBoardLedInterval) {
         // time is up, so change the state to HIGH
      onBoardLedState = HIGH;
         // and save the time when we made the change
      previousOnBoardLedMillis += onBoardLedInterval;
         // NOTE: The previous line could alternatively be
         //              previousOnBoardLedMillis = currentMillis
         //        which is the style used in the BlinkWithoutDelay example sketch
         //        Adding on the interval is a better way to ensure that succesive periods are identical

   }
 }
 else {  // i.e. if onBoardLedState is HIGH

         // if the Led is on, we must wait for the duration to expire before turning it off
   if (currentMillis - previousOnBoardLedMillis >= blinkDuration) {
         // time is up, so change the state to LOW
      onBoardLedState = LOW;
         // and save the time when we made the change
      previousOnBoardLedMillis += blinkDuration;
   }
 }
}

//=======

void updateLed_A_State() {

 if (led_A_State == LOW) {
   if (currentMillis - previousLed_A_Millis >= led_A_Interval) {
      led_A_State = HIGH;
      previousLed_A_Millis += led_A_Interval;
   }
 }
 else {
   if (currentMillis - previousLed_A_Millis >= blinkDuration) {
      led_A_State = LOW;
      previousLed_A_Millis += blinkDuration;
   }
 }
}

//=======

void updateLed_B_State() {

 if (led_B_State == LOW) {
   if (currentMillis - previousLed_B_Millis >= led_B_Interval) {
      led_B_State = HIGH;
      previousLed_B_Millis += led_B_Interval;
   }
 }
 else {
   if (currentMillis - previousLed_B_Millis >= blinkDuration) {
      led_B_State = LOW;
      previousLed_B_Millis += blinkDuration;
   }
 }
}

//========

void switchLeds() {
     // this is the code that actually switches the LEDs on and off

 digitalWrite(onBoardLedPin, onBoardLedState);
 digitalWrite(led_A_Pin, led_A_State);
 digitalWrite(led_B_Pin, led_B_State);
 digitalWrite(buttonLed_Pin, buttonLed_State);
}

//=======

void readButton() {

     // this only reads the button state after the button interval has elapsed
     //  this avoids multiple flashes if the button bounces
     // every time the button is pressed it changes buttonLed_State causing the Led to go on or off
     // Notice that there is no need to synchronize this use of millis() with the flashing Leds

 if (millis() - previousButtonMillis >= buttonInterval) {

   if (digitalRead(buttonPin) == LOW) {
     buttonLed_State = ! buttonLed_State; // this changes it to LOW if it was HIGH 
                                          //   and to HIGH if it was LOW
     previousButtonMillis += buttonInterval;
   }
 }

}

//========

void servoSweep() {

     // this is similar to the servo sweep example except that it uses millis() rather than delay()

     // nothing happens unless the interval has expired
     // the value of currentMillis was set in loop()

 if (currentMillis - previousServoMillis >= servoInterval) {
       // its time for another move
   previousServoMillis += servoInterval;

   servoPosition = servoPosition + servoDegrees; // servoDegrees might be negative

   if (servoPosition <= servoMinDegrees) {
         // when the servo gets to its minimum position change the interval to change the speed
      if (servoInterval == servoSlowInterval) {
        servoInterval = servoFastInterval;
      }
      else {
       servoInterval = servoSlowInterval;
      }
   }
   if ((servoPosition >= servoMaxDegrees) || (servoPosition <= servoMinDegrees))  {
         // if the servo is at either extreme change the sign of the degrees to make it move the other way
     servoDegrees = - servoDegrees; // reverse direction
         // and update the position to ensure it is within range
     servoPosition = servoPosition + servoDegrees;
   }
       // make the servo move to the next position
   myservo.write(servoPosition);
       // and record the time when the move happened
 }
}

//=====END