Time Servo without delay

So I'm planning to build a bluetooth tank, I'm done doing the movement portion I'm now working on the turret. I then had the idea of using a continuos rotation servo to pull an elastic band. I want to do it in a click. When I click a button I want the servo to rotate automatically for a number of seconds to avoid snapping the elastic band. Using the delay function won't do since it will interrup the movement program. So I am humbly asking for your advices and suggestions. Thanks

See Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

as an everyday example with easy to follow numbers
delay() is blocking. As long as the delay is "delaying" nothing else of the code can be executed.
Now there is a technique of non-blocking timing.
The basic principle of non-blocking timing is fundamental different from using delay()

You have to understand the difference first and then look into the code.

otherwise you might try to "see" a "delay-analog-thing" in the millis()-code which it really isn't
Trying to see a "delay-analog-thing" in millis() makes it hard to understand millis()
Having understood the basic principle of non-blocking timing based on millis() makes it easy to understand.

imagine baking a frosted pizza
the cover says for preparation heat up oven to 200°C
then put pizza in.
Baking time 10 minutes

You are estimating heating up needs 3 minutes
You take a look onto your watch it is 13:02 (snapshot of time)
You start reading the newspaper and from time to time looking onto your watch
watch shows 13:02. 13:02 - 13:02 = 0 minutes passed by not yet time
watch shows 13:03. 13:03 - 13:02 = 1 minute passed by not yet time
watch shows 13:04. 13:04 - 13:02 = 2 minutes passed by not yet time

watch shows 13:05 when did I start 13:02? OK 13:05 - 13:02 = 3 minutes time to put pizza into the oven

New basetime 13:05 (the snapshot of time)
watch 13:06 not yet time
watch 13:07 not yet time
watch 13:08 not yet time (13:08 - 13:05 = 3 minutes is less than 10 minutes
watch 13:09 not yet time
watch 13:10 not yet time
watch 13:11 not yet time
watch 13:12 not yet time
watch 13:13 not yet time
watch 13:14 not yet time (13:14 - 13:05 = 9 minutes is less than 10 minutes
watch 13:15 when did I start 13:05 OK 13:15 - 13:05 = 10 minutes time to eat pizza (yum yum)

You did a repeated comparing how much time has passed by
This is what non-blocking timing does

In the code looking at "How much time has passed by" is done

currentTime - startTime >= bakingTime

bakingTime is 10 minutes

13:06 - 13:05 = 1 minute >= bakingTime is false
13:07 - 13:05 = 2 minutes >= bakingTime is false
...
13:14 - 13:05 = 9 minutes >= bakingTime is false
13:15 - 13:05 = 10 minutes >= bakingTime is TRUE time for timed action!!

So your loop() is doing

void loop()
// doing all kinds of stuff like reading the newspaper

if (currentTime - previousTime >= period) {
previousTime = currentTime; // first thing to do is updating the snapshot of time
// time for timed action
}

it has to be coded exactly this way because in this way it manages the rollover from Max back to zero of the function millis() automatically

best regards Stefan

1 Like

Here's an example of the aforementioned techniques for sweeping a servo over time while blinking an LED at the same time. The delay() function is used only in setup() to give the servo time to "home" after boot. After that, it uses micros() and millis() timing.

#include <Servo.h>

Servo myservo;

// Define the servo and LED pins
const uint8_t pinServo = 9;
const uint8_t pinLED = LED_BUILTIN;

void setup() 
{    
    myservo.attach(pinServo);           //attach servo to the pin
    myservo.writeMicroseconds(500);     //set its initial position
    pinMode( pinLED, OUTPUT );          //set the LED pin as output

    delay(1000);                        //give the servo a moment to "home"
    
}//setup

void loop() 
{
    SweepServo();     //go handle the servo
    BlinkLED();       //blink the LED
    
}//loop

void SweepServo( void )
{
    //static means these variables are "remembered" when function exits
    static uint16_t       
        servoValue = 500;     //tracks the current position of the servo
    static bool
        bDirection = true;    //increasing (true) or decreasing (false)
    static uint32_t
        timeServoStep = 0ul;  //used to time each update of servoValue
    uint32_t
        timeNow = micros();   //grab the current microsecond timer count
        
    //we want to move from 500mS to 2500mS (2000 increments) in 2-seconds
    //this means we need to update the servo time once every 1000uS
    //has 1000uS elapsed since we last udpated the servo?
    if( (timeNow - timeServoStep) >= 1000ul )
    {
      //yes, save the microsecond count now so we can time the next update
        timeServoStep = timeNow;
        
        //are we increasing or decreasing?
        switch( bDirection )
        {
            case    true:
                //increasing; bump the servoValue by one
                servoValue++;
                //if we've reach the maximum value...
                if( servoValue == 2500 )
                    bDirection = false;   //change direction
                
            break;

            case    false:
                //decreasing; subtract one from the current servo value
                servoValue--;
                //if it reaches the min value, reverse direction again
                if( servoValue == 500 )
                    bDirection = true;
            break;
            
        }//switch

        //update the servo position with the new servoValue
        myservo.writeMicroseconds( servoValue );
        
    }//if
    
}//SweepServo

void BlinkLED( void )
{
    static uint32_t
        timeBlink = 0ul;      //used to time when the LED toggles
    uint32_t
        timeNow = millis();   //grab the current milliseconds count

    //if 250mS has passed since the last update...
    if( (timeNow - timeBlink) >= 250ul )
    {
        //save the millis count now to time the next LED toggle
        timeBlink = timeNow;
        //toggle the LED pin by reading its value and using XOR with '1'
        digitalWrite( pinLED, digitalRead( pinLED ) ^ HIGH );
        
    }//if

}//BlinkLED

Hello
Take a view to this sketch, checkout and make mods to your project. You have to make changes wrt to the servo control.

/* BLOCK COMMENT
  ATTENTION: This Sketch contains elements of C++.
  https://www.learncpp.com/cpp-tutorial/
  https://forum.arduino.cc/t/time-servo-without-delay/933588
*/
#define ProjectName "Time Servo without delay"
// HARDWARE AND TIMER SETTINGS
// YOU MAY NEED TO CHANGE THESE CONSTANTS TO YOUR HARDWARE AND NEEDS
// VARIABLE DECLARATION ANF INITIALZATION 
struct ROTATIONTIME {
  byte button_;             // portPin o---|button|---GND
  byte relay;               // portPin o---|relay|---GND
  unsigned long duration;   // interval time
  unsigned long stamp;      // actual time
  bool control_;            // control for start/stop
} rotationTimes [] {
  {A0, 3, 3000, 0, false},
  {A1, 5, 2000, 0, false},
  {A2, 6, 5000, 0, false},
};
// -------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  Serial.println(F("."));
  Serial.print(F("File   : ")), Serial.println(__FILE__);
  Serial.print(F("Date   : ")), Serial.println(__DATE__);
  Serial.print(F("Project: ")), Serial.println(ProjectName);
  pinMode (LED_BUILTIN, OUTPUT);  // used as heartbeat indicator
  //  https://www.learncpp.com/cpp-tutorial/for-each-loops/
  for (auto rotationTime : rotationTimes) pinMode(rotationTime.button_, INPUT_PULLUP);
  for (auto rotationTime : rotationTimes) pinMode(rotationTime.relay, OUTPUT);
}
void loop () {
  unsigned long currentTime = millis();
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  for (auto &rotationTime : rotationTimes) {
    if (!rotationTime.control_ && !digitalRead(rotationTime.button_)) {
      rotationTime.control_ = true;
      rotationTime.stamp = currentTime;
      digitalWrite(rotationTime.relay, HIGH);
    }
    if (currentTime - rotationTime.stamp >= rotationTime.duration && rotationTime.control_) {
      rotationTime.control_ = false;
      digitalWrite(rotationTime.relay, LOW);
    }
  }
}

Have a nice day and enjoy coding in C++.

Wow! Thanks a lot for those replies. But to be honest I'm just in the "Advanced" level of arduino programming so I'll try to simplify the things that youve said.

For @StefanL38 example I was able to understand it thanks to the analogy but as each second passes won't it update the previoustime and make the mathematical difference of currenttime-previoustime all just the same?

Also which part can I place the Servo.write(180) and make it stop in a specific allotted time? And last question, Right I can put all of this inside an if statement?