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
}
}
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.
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
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!
// 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
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.
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
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
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()