Testing Millis with servos and experiencing odd behavior

I have been modifying and testing A sketch to control servos with Millis and an UNO.
The issue I am having is when I change the MOVING_TIME the servos change speed but also change the angle of the servo. I was thinking that altering the MOVING_TIME that would just change the time they turn.
Basically I would like to be able to control the speed, and angle of several servos independently.

#include <Servo.h>

Servo myServo;
Servo myServo1;

unsigned long MOVING_TIME = 500; // move time in seconds
unsigned long moveStartTime;
unsigned long MOVING_TIME1 = 5000; 
unsigned long moveStartTime1;

int startAngle = 0; // 0°
int stopAngle  = 180; // 180°

void setup() {
  myServo.attach(9);
  myServo1.attach(7);
  moveStartTime = millis(); // start moving
  moveStartTime1 = millis();
  // sensor code
}
void loop() {
  unsigned long progress = millis() - moveStartTime;
  unsigned long progress1 = millis() - moveStartTime1; 
  if (progress <= MOVING_TIME) {
    long angle = map(progress, 0, MOVING_TIME, stopAngle, startAngle);
    myServo.write(angle); 
   
  if (progress <= MOVING_TIME1) { 
  long angle1 = map(progress1, 0, MOVING_TIME1, startAngle, stopAngle);
    myServo1.write(angle1);
  }

  // sensor code
  }
}

Looks close. You got a typo there, pretty sure.

And look at the pattern you are copying or learning from…

nowhere do you update progress or progress1 to be the time at which you, well, made progress.

Also, mind your { braces }. The two basically duplicate code sections that handle the two servos have their braces in different places.

Which version is correct? Again, look at the code you are learning from, again pretty sure one is right and one is wrong.

  if (progressQ <= MOVING_TIME1) { 
    long angle1 = map(progress1, 0, MOVING_TIME1, startAngle, stopAngle);
    myServo1.write(angle1);

    progress1 = millis();
  }

It remains to be seen if what you are timing out is what is need to creep the servos, or whatever you are attempting.

Pro tip: since you are heading for a freely running loop, lotsa people will

    unsigned long  currentMillis = millis();

"look at the clock" just once, at the top of the loop, then use currentMillis in all the places you now call millis().

Either way works here, in some cases we'd like eveyone to be used the same "time" in the tests.

HTH

a7

Should it be " if (progress1 <= MOVING_TIME1) " ?

I am just starting out and dont know much about C / C++.

I am basically taking sketch`s and changing thme around using deduction to try to reverse

engineer them but that only goes so far, LOL.

I have a couple books, programming arduino and arduino workshop and they have helped.

I dabbled with basic and Pbasic when I used the basic stamp about 20 years ago but this is

quite different.

I am just trying to independently control several (12) servos to make a four legged walker

move around.

there are several libraries that can control slower movement of servos.

one of them I had used a longer time ago is named
ControlledServo

best regards Stefan

Oh, this sketch that I am using was titled "how to control speed of servo" seems to not do that, it seems to alter the time the servo is on, not the speed.
Does that sound correct? By modifying the MOVING_TIME, that does just that, not the speed?

Moving the servo in tiny increments with tiny delays between movements towards its destination is how a hobby type servo's speed is controlled.

Lets say the value of delay is 5000mS and the time to rotate the servo's to position is 5 seconds, which would be 1 unit of servo rotation for 1 unit of time. Then I'd take the (current servo position)-(desired servo position) to get the total servo increments or decrements. Then it becomes a simple matter of ServoRotationIncrements/ServoTimeIncrements to get how much to torque the servo for each time unit.

By changing MOVING_TIME and MOVING_TIME1 to different times, (say one is set for 3

seconds and another for 10 seconds) I would have expected the servos to move at different

speeds but start and stop at the same angles?

Is that not correct?

#include <Servo.h>

/* PIN LAYOUT

   LEG            ELBOW PIN       SHOULDER PIN

   -------------------------------------------

   1              30              31

   2              32              33

   3              34              35

   4               8               9

   5              10              11

   6              12              13

*/

Servo shoulders[6];

Servo elbows[6];



int shoulder_pos[6][3];



int even_legs[3];

int odd_legs[3];



const int forward=0; 

const int neutral=1;

const int rear=2;



const int up=30;

const int down=70;

const int in=90;

const int out=20;





void register_servos() {

  // Hook up all the legs to their appropriate pins and set movement ranges.

  even_legs[0] = 0;

  even_legs[1] = 2;

  even_legs[2] = 4;

  

  odd_legs[0] = 1;

  odd_legs[1] = 3;

  odd_legs[2] = 5;

  

  elbows[0].attach(30);

  shoulders[0].attach(31);

  shoulder_pos[0][forward] = 115;

  shoulder_pos[0][neutral] = 130;

  shoulder_pos[0][rear] = 145;



  elbows[1].attach(32);

  shoulders[1].attach(33);

  shoulder_pos[1][forward] = 75;

  shoulder_pos[1][neutral] = 90;

  shoulder_pos[1][rear] = 105;



  elbows[2].attach(34);

  shoulders[2].attach(35);

  shoulder_pos[2][forward] = 45;

  shoulder_pos[2][neutral] = 60;

  shoulder_pos[2][rear] = 75;



  elbows[3].attach(8);

  shoulders[3].attach(9);

  shoulder_pos[3][forward] = 145;

  shoulder_pos[3][neutral] = 130;

  shoulder_pos[3][rear] = 115;



  elbows[4].attach(10);

  shoulders[4].attach(11);

  shoulder_pos[4][forward] = 105;

  shoulder_pos[4][neutral] = 90;

  shoulder_pos[4][rear] = 75;



  elbows[5].attach(12);

  shoulders[5].attach(13);

  shoulder_pos[5][forward] = 75;

  shoulder_pos[5][neutral] = 60;

  shoulder_pos[5][rear] = 45;

}



void move_legs_forward(int legs[]){

  for (int i=0; i<3; i+=1){

   shoulders[legs[i]].write(shoulder_pos[legs[i]][forward]);

  }

  delay(75);

}



void move_legs_up(int legs[]){

  for (int i=0; i<3; i+=1){

   elbows[legs[i]].write(up); 

  }

  delay(75);

}



void move_legs_down(int legs[]){

  for (int i=0; i<3; i+=1){

   elbows[legs[i]].write(down); 

  }

  delay(75);

}



void move_legs_backward(int legs[]){

  for (int i=0; i<3; i+=1){

   shoulders[legs[i]].write(shoulder_pos[legs[i]][rear]);

  }

  delay(75);

}



void move_legs_in(int legs[]){

  for (int i=0; i<3; i+=1){

    elbows[legs[i]].write(in);

  } 

  delay(200);

}



void move_legs_out(int legs[]){

  for (int i=0; i<3; i+=1){

    elbows[legs[i]].write(in);

  }

  delay(200); 

}



void leg_move_neutral(int leg){

  shoulders[leg].write(shoulder_pos[leg][neutral]);

  delay(75);

    

  elbows[leg].write(down);

  delay(75);

}



void move_legs_neutral(int legs[]){

  for (int i=0; i<3; i+=1){ 

   leg_move_neutral(legs[i]);

  } 

}



// Walk forward

void walk_forward() {

  move_legs_up(odd_legs);

  move_legs_forward(odd_legs);

  move_legs_down(odd_legs);

  

  move_legs_up(even_legs);

  move_legs_backward(odd_legs);

  move_legs_forward(even_legs);

  

  move_legs_down(even_legs);

  move_legs_up(odd_legs);

  move_legs_backward(even_legs);

}



void init_position() {

  // Elbows should be moved to position 55

  // Shoulders should be moved to position 90

  move_legs_neutral(even_legs);

  move_legs_neutral(odd_legs);

}



void setup() {

  // Put setup code here. This only runs once.

  register_servos();

  init_position();

}



void loop() {

  // Put main code here; this loops continuously.

  walk_forward();

}

Base code I used that I found on the internet to move my hexapod around.

consider

#include <Servo.h>

#define Nservo  2
Servo myServo [Nservo]; 

const int AngleStart = 0;
const int AngleStop  = 180;

float servoPos [Nservo] = { AngleStart, AngleStart };

const unsigned long MsecPeriod = 20;
unsigned long msecLst;

float steps [Nservo] = {
     (AngleStop - AngleStart) / ( 500.0 / MsecPeriod),
     (AngleStop - AngleStart) / (5000.0 / MsecPeriod),
};

char s [80];

// -----------------------------------------------------------------------------
void setup() {
    Serial.begin (9600);

    myServo [0].attach  (9);
    myServo [1].attach (7);

    for (int n = 0; n < Nservo; n++)
        myServo [n].write (servoPos [n]);
}

void loop ()
{
    unsigned long msec = millis ();
    if (msec - msecLst >= MsecPeriod)  {
        msecLst = msec;

        sprintf (s, " %8d ", msec);
        Serial.print (s);

        for (int n = 0; n < Nservo; n++)  {
            if (AngleStop <= servoPos [n])
                continue;

            servoPos [n] += steps [n];
            myServo [n].write (servoPos [n]);
            sprintf (s, " %d %4d", n, (int)servoPos [n]);
            Serial.print (s);
        }
        Serial.println ();
    }
}

Thank you idahowalker for the code. I was looking for a way using Millis so I can add sensors and such later, but I will check out that code.
Thanks.

there are probably a sequence of positions each servo needs to be at at the end of each sequence. there are probably a series of intermediate sub-positions between each sequence so that each servo reaches the end position at the same time. the number of intermediate positions is such that all the servos can reach the intermediate position within some small tolerance of time

some higher level processing would determine the sequence positions and number of intermediate position to achieve some acceptable amount of jitteriness

once a correct sequence is determined, o course it can be repeated

Thanks JGCR, I didnt see your post until I refreshed the page. I will check out the code you posted.
Thanks.

First, I mislead you by trying to do two things at once, one of which was not compatible with the other, the other sadly being thinking straight. For that I apolize.

Below is your code stripped down and printing.

It shows you have indeed managed to make something go for MOVING_TIME1 milliseconds.

Try this simplified version:

unsigned long MOVING_TIME1 = 2000; // move time in seconds
unsigned long moveStartTime1;


void setup() {
  Serial.begin(115200);
}

void loop() {
  
  unsigned long progress1 = millis() - moveStartTime1;

  if (progress1 <= MOVING_TIME1) {

    Serial.println(progress1);

  }

}

The body of the if statement will be executed thousands of times, occasionally progress1 will increase. And occasionally, a servo would move a bit.

The map function makes the servo1 go from 0 to 180. Perfect.

Since servos don't care if you tell them to go to where they already are, it matters not that in between real moves, the servo is getting dozens of commands identical to the last command.

Just a waste of processor time, which you might regret one day if you have other things to spend that resource on.

Then nothing more. Until something you do not yet have code for sets moveStartTime1 to the current millis(). If you did that, you 'd get another thousands of executions of the body, and over the MOVING_TIME1 progress1 would again go from 0 to MOVING_TIME1.

So it works, if a bit not what I expected (and thought, maybe, hard to remember so long ago).

Here's one servo fully coded like you were aiming for, with serial printing. BTW, adding serial print statements liberally strewn about your code can help you see the values of the variables and verify the flow in the program that they inform. I don't know how to enjoy flying blind!


run it in the simulator here


// https://forum.arduino.cc/t/testing-millis-with-servos-and-experiencing-odd-behavior/1092322
// https://wokwi.com/projects/357135312156712961

# include <Servo.h>

Servo myServo;

// I changed this for the moment:

unsigned long MOVING_TIME = 2500;
unsigned long moveStartTime;

int startAngle = 0;
int stopAngle  = 180;

void setup() {
  Serial.begin(115200);
  Serial.println("Servo World!\n");

  myServo.attach(9);

  myServo.write(stopAngle);
  delay(1000);

  moveStartTime = millis(); // start moving
}

void loop() {
  unsigned long currentMillis = millis();

  unsigned long progress = currentMillis - moveStartTime;

  if (progress <= MOVING_TIME) {
    long angle = map(progress, 0, MOVING_TIME, stopAngle, startAngle);
    myServo.write(angle); 

// reporting section
    static unsigned long timeOfLastReport;
    static int lastReportedAngle = -1;

    if (lastReportedAngle != angle) {   // just print if the new issued angle is different
      Serial.print(currentMillis - timeOfLastReport);
      Serial.print(" ms later told servo to ");
      Serial.println(angle);

      timeOfLastReport = currentMillis;
      lastReportedAngle = angle;
    }
//  end reporting section - remove it when you dare

  }
}

So that could work in your larger sketch, but as has been pointed out, this is not the usual way to dole out little steps to a servo without blocking. The idea of updating servo positions regularly is demonstrated in @gcjr's code above, who evidently did not go to the beach today and, well never mind.

One timed if block can loop over all your servos and move them a bit closer to their end goals.

Doing it that way, you have to figure how far to move with each timed step. You figured out a way by mapping passage of time into angle position, so I bet you could figure that out in what is essentially reverse: what fraction of the angle should be added or subtracted with each increment of time.

Etiher way, there will be complications, naturally, when it comes to 12 servos walking. But you have made a good start by thinking about not blocking like a simple servo example might.

You'll probably confuse me, and I dare say others, if you come to grips with and adopt the more conventioanl method in #9

a7

Thank you alto777, I will check out the code you posted.

This millis seems very complicated, I may just go ahead with my project using delay then

check out millis later.

Thanks.

That of course is up to you.

I am compelled to advise that you spend just a little time, or even a bit more than a little time, and attain an understanding and ability to use the basic timing technique that is in the code you posted and also in the code others have posted on this thread.

I never did make a dodecapod, or anything with twelve servos, but I think it would be way easier to write, get working, modify and enhance if you stay away from a reliance on delay() to do basic timing.

If anything I wrote (code) doesn't make sense, feel free to ask questions.

I'll assume you are familiar with the things that turn up when you google

 arduino blink without delay

and

 arduino two things at once

It's not all that hard, but does involve, I have noticed, an Aha! moment. Then you'll wonder why you did things the other way.

In any case, we here to help. L8R.

a7

The reason why you find milis() complicated is
because almost nobody takes the time to explain the fundamental

difference

between delay and non-blocking timing based on millis()

As long as you try to see a delay-similar-thing in non-blocking timing there is a conflict between your imagination how it works and what the code is really doing.

It is like your imagination is:

"it must by a triangle "

while if you look at it:
it IS a circle

totally different and a triangle can

never match

a circle no matter how hard you try to do it!

delay() creates a

freezing = stops code-execution

of the microcontroller

non-blocking timing does the opposite
non-blocking timing keeps your

looping code running at high speed

And you do a hundred-thousand times repeated compairing of two points in time
1st point in time a reference-point = start-point
2nd point in time is actual time

this is explained in this tutorial with an everyday example of baking a frosted pizza

best regards Stefan

Thanks for the words of encouragement alto777. I have not completely given up on Millis,

just for now. I have another project where using millis would be beneficial as well.

Yes, I did find both the blink without delay and two things at once links while looking up

Millis. The walker thing I am creating is 4 legs but has 12 servos, lower legs, upper legs and

hips.

I accidentally stripped one of the servos while testing, turns out these are only 120 degrees

not 180... Oops. LOL. Ive got more on order.

Thanks again.

I just started with arduino programming and it all seems complicated.

Am I understaning this correctly. The Millis function (if thats what it is called) tracks the time that A program starts running on the arduino and you read different time frames to initiate and stop things from happening during the running time.

Yes.
All "commands in c++ are called functions.
The function millis() starts counting up milliseconds as soon as you connect power to the microcontroller and then counts up until
2^32= 4.294.967.295 then the counter "rolls over" to zero and starts counting up again.
This rollover is no problem if you do unsigned integer-math.
calculating a time-difference even across a roll-over.

the function millis() can be used for non-blocking timing
which essentially is compairing a reference-point in time with actual time

best regards Stefan

what your attempting to do, kinematics is not trivial

i would separate determining the servo positions from the actually driving the servos. determining the positions could be computed a priori on a laptop and those positions implemented as a table in an Arduino program.

at run time, the arduino could apply the same position sequence to different legs but indexed at different offsets within the sequence for each leg. i'll guess that driving outside vs inside legs at different rates could affect turning

millis() simply returns the time in milliseconds since the processor started running. it can be used for many things

one common use is to capture the time, T0 at some starting point, check if some amount of time has passed since T0 and take some action, including possibly capturing the current time to repeat the process. this "check" can be one of many things done within loop()