Display several tasks on a display using millis()

Hi all

I’ve been doing a program for some days and I can’t get it work properly. I have 5 servos and they must be activated at some time of the day but not all of them at the same time.

I’ve been reading a lot about millis() to make the program do several tasks at the same time. And tried to integrate examples with my program with no luck so far.

The clock stops the servo secuence is finished. In fact, it sounts 1 second after each turn of the servos

I took the clock code from an example in the projecthub and turned it to a 24h clock. I think it works fine so far.

I think the problem is with delays in the servo tasks. I’m pretty sure I need to susbstitute them with millis() code but I’m not sure how to get there.

Can you help me with it?

#include <Servo.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>


bool halfTurn1 = false; //Declaring that the servo is not at 0 degrees possition
bool halfTurn2 = false;
bool halfTurn3 = false;
bool halfTurn4 = false;
bool halfTurn5 = false;


LiquidCrystal_I2C lcd(0x27, 16, 2);

//Servo variables

Servo servo1; 
Servo servo2;
Servo servo3;
Servo servo4;
Servo servo5;

//Multitask variables

unsigned long previousClockMillis = 0;   // will store last time the clock was updated
unsigned long previousServoMillis = 0; // the time when the servo was last moved
unsigned long currentMillis;
const unsigned long period = 1000;


//Timer variables

int h;
int m;
int s;

// Time Set Buttons
int button1;
int button2;
int hs = 11; // pin 8 for Hours Setting
int ms = 12; // pin 9 for Minutes Setting


//============================================================================================

void setup() {

  lcd.init();
  lcd.backlight();

  pinMode(hs, INPUT_PULLUP);
  pinMode(ms, INPUT_PULLUP);

  servo1.attach(5);
  servo2.attach(6);
  servo3.attach(7);
  servo4.attach(8);
  servo5.attach(9);

  servo1.write(0); // Reset position to 0 degrees
  servo2.write(0);
  servo3.write(0);
  servo4.write(0);
  servo5.write(0);

  previousClockMillis = millis();  //initial start time
  previousServoMillis = millis();  //initial start time
}

//============================================================================================

void loop() {

  currentMillis = millis();   // capture the latest value of millis()
  
  timer(); //Begin 24h timer/clock
  if (h == 4) { // When it's 4 o'clock execute action_1
    action_1();
  }
  if (h == 10) { // When it's 10 o'clock execute action_1
    action_2();
  }
  if (h == 15) { // When it's 15 o'clock execute action_1
    action_3();
  }
}

//============================================================================================

void timer() {

  if (currentMillis - previousClockMillis >= period) {
    
    // Update LCD Display
    lcd.setCursor(4, 0);
    if (h < 10)lcd.print("0"); // always 2 digits
    lcd.print(h);
    lcd.print(":");
    if (m < 10)lcd.print("0");
    lcd.print(m);
    lcd.print(":");
    if (s < 10)lcd.print("0");
    lcd.print(s);

    s = s + 1; //increment sec. counting
    



    /*-------Time setting-------*/
    button1 = digitalRead(hs);
    if (button1 == 0) h = h + 1;

    
    button2 = digitalRead(ms);
    if (button1 == 0) m = m + 1;

    /* ---- manage seconds, minutes, hours am/pm overflow ----*/
    if (s == 60) {
      s = 0;
      m = m + 1;
    }
    if (m == 60)
    {
      m = 0;
      h = h + 1;
    }
    if (h == 24)
    {
      h = 0;
    }
    previousClockMillis = currentMillis;
  }
}

//============================================================================================

void action_1() { //Action 1 will only activate servos 1, 3 & 5 in a secuence
  
  act_servo1();
  act_servo3();
  act_servo5();
}

//============================================================================================
void action_2() { //Action 2 will only activate servos 1, 2 & 4 in a secuence
  
  act_servo1();
  act_servo2();
  act_servo4();
}

//============================================================================================
void action_3() { //Action 1 will  activate all servos servos in a secuence
  
  act_servo1();
  act_servo2();
  act_servo3();
  act_servo4();
  act_servo5();
}

//============================================================================================

void act_servo1() {

  if (currentMillis - previousServoMillis >= 1000) {
    if (halfTurn1 == false) { //Check if the position is 0 degrees
    lcd.setCursor(0, 1);
    lcd.print("Servo 1 activated");
    servo1.write(180); // 180º turn
    delay(1000);
    servo1.write(0); // Back to 0º
    delay(1000);
    halfTurn1 = true; // One turn
    previousServoMillis = currentMillis;
    }
  }
}

//============================================================================================

void act_servo2() {

  if (currentMillis - previousServoMillis >= 1000) {
    if (halfTurn2 == false) {
    lcd.setCursor(0, 1);
    lcd.print("Servo 2 activated");
    servo2.write(180);
    delay(1000);
    servo2.write(0);
    delay(1000);
    halfTurn2 = true;
    previousServoMillis = currentMillis;
    }
  }
}

//============================================================================================

void act_servo3() {

  if (currentMillis - previousServoMillis >= 1000) {
    if (halfTurn3 == false) {
    lcd.setCursor(0, 1);
    lcd.print("Servo 3 activated");
    servo3.write(180);
    delay(1000);
    servo3.write(0);
    delay(1000);
    halfTurn3 = true;
    previousServoMillis = currentMillis;
    }
  }
}

//============================================================================================

void act_servo4() {

  if (currentMillis - previousServoMillis >= 1000) {
    if (halfTurn4 == false) {
    lcd.setCursor(0, 1);
    lcd.print("Servo 4 activated");
    servo4.write(180);
    delay(1000);
    servo4.write(0);
    delay(1000);
    halfTurn4 = true;
    previousServoMillis = currentMillis;
    }
  }
}


void act_servo5() {


  if (currentMillis - previousServoMillis >= 1000) {
    if (halfTurn5 == false) {
    lcd.setCursor(0, 1);
    lcd.print("Servo 5 activated");
    servo5.write(180);
    delay(1000);
    servo5.write(0);
    delay(1000);
    halfTurn5 = true;
    previousServoMillis = currentMillis;
    }
  }
}

Thanks for your help

well, delay blocks your code. So what is you expectation? You "stopped" your code for 1000 milliseconds.

If you want a non blocking movement of the servo start with the "BlinkWithoutDelay" example and adopt it.
Instead of LED blinking to Servo 0 / Servo 180 Movement.

a)
Start with one servo.
Bring it into movement.

b) if it works, tidy up your loop and bring it to a function (like your act_servo)

c) If that works for one servo, see how arrays can help you to avoid code duplicates for the other servos.

or I'm completly wrong and I don't understand what you want to achieve. Than you should draw a time diagram, indicating which movement is done when, in which order or at the same time.

Hello noiasca

Thanks for the input. I'm pretty sure you understood what I'm trying to do. I'm familiar with the BlinkWithoutDelay scketch and other examples. My problem is that all I see are examples with LEDs, and they take LOW and HIGH states to make the changes. I can't figure out how to take it to the servos.

I've tried this... But it didn't work:

void act_servo1() {

  if (currentMillis - previousServoMillis >= 1000) {
    if (halfTurn1 == false) { //Check if the position is 0 degrees
        
      if (servo1.read(0)) { 
      
      servo1.write(180); // 180º turn
      }
      lcd.setCursor(0, 1);
      lcd.print("Servo 1 activated");
      halfTurn1 = true; // One turn
      previousServoMillis = currentMillis;
    }
  }
}

Hello I tried something different:

void act_servo1() {

  if (currentMillis - previousServoMillis >= 1000) {
    if (halfTurn1 == false) { //Check if the position is 0 degrees

      if (servo1.read() == 0) {
        lcd.setCursor(0, 1);
        lcd.print("Servo 1 activated");
        servo1.write(180);  // 180º turn
      }
      halfTurn1 = true; // One turn
      previousServoMillis = currentMillis;
    }
  }
}

Now there is a syncronization between the servos and the clock, but I can't return the servos back to 0 degrees.

Rather than use servo.read() I suggest you have a variable that holds the servo position. Then you can change that variable as required and update the servo position with myServo.write(servoPos);

Have a look at the servo code in Several Things at a Time

...R

Now there is a syncronization between the servos and the clock, but I can't return the servos back to 0 degrees.

So where in the code do you do this? I can’t see anything. When do you want the servo to return to zero?
Immediately after a move?
When another servo is moved?
After a specific time?

My problem is that all I see are examples with LEDs, and they take LOW and HIGH states to make the changes.

This implies you are not writing your own code but simply looking to see if someone has done it before? Not the most efficient way of doing things.

your reply #2 already looked quite ok, but imho it missed half of the logic.

this might swing the servo from 0 to 180 in one second

void act_servo1() {
  if (currentMillis - previousServoMillis >= 1000) {
    if (halfTurn1 == false) { // don't check, just assume... 
      lcd.setCursor(0, 1);
      lcd.print("Servo 1 180");
      servo1.write(180); // 180º turn
    }
    else
    {
      lcd.setCursor(0, 1);
      lcd.print("Servo 1 0");
      servo1.write(0); // 0º turn
    }
    halfTurn1 = !halfTurn1; // inverse
    previousServoMillis = currentMillis;
  }
}

by the way missing: I still miss your timing diagram...

Hi Poleas,

've been doing a program for some days and I can't get it work properly. I have 5 servos and they must be activated at some time of the day but not all of them at the same time.

You haven't written how many hours you spent trying to get it to work.
if it was more than 4 hours this indicates that you did a more or less tinkering around instead of step by step analysis with reading how non-blocking timing with millis() works and writing small testprograms with a lot of serial output to really understand how it works. A very common habit of newbees is to add too much to the code before start to test it.
This results in many places you have to analyse and try different things. In the end this consumes much more time
than adding a small piece then test

  • adding a small piece then test
  • adding a small piece then test ...

And another thing that helps really a lot is adding serial-output to your code to see what your code is really doing (instead of what you think it will do).

There is an old programmer-wisdom: in 98% of all cases the bug has its hands on the keyboard and is staring at the screen. ;-))

best regards Stefan