My Project: Rotating a servo back and forth ~60 degrees at 20 Hz, at the press of a button. The speed of the servo is controlled in my code, below, via the variable "interval", which is the time (in microseconds) that the arduino waits before telling the servo to move to the next position (as shown in the ServoPos array). With an interval of 10,000, the servo oscillates at 1 Hz.
Problem: I had a program that successfully oscillated my servo at 20 Hz, and then my hard drive crashed and I lost nearly everything (whoops). I have tried to rebuild my program, but I am not achieving the speeds that I had before. My servo now is peaking at 1 Hz, and if I decrease my interval variable below 10,000, there is no noticeable increase in speed. My wiring is the same as it was, and my battery is freshly recharged.
I have posted my code below. If you see any glaring problems, or if you have any recommendations for things to test for, please advise.
//Including Libraries
#include <Servo.h> //Necessary for Servo Control
#include <avr/pgmspace.h> //Necessary for storing and retreiving data into/from Programmable Memory (PROGMEM)
//Objects
Servo myservo; // create servo object to control a servo
//Variables
//in PROGMEM (Arduino Forum: this array below is representative of its original 200-element length)
const int ServoPos[] PROGMEM = {1500 , 1516 , 1531 , 1547 , 1423 , 1438 , 1453 , 1469 , 1484};
//in RAM
int buttonState = 0; // variable for reading the pushbutton status
int oscCounter = 3;
unsigned long lastUpdate = 0; // will store the time that the Servo Position was last updated
byte Counter = 1;
int pos = 1500; //introduce local variable "pos" and set it equal to the home position of the servo
// constants won't change :
const long interval = 10000; // interval at which to update Servo Position (microseconds), 1/freq
const int buttonPin = 2; // pushbutton pin
void setup() {
myservo.attach(9); // attaches the servo on pin 9 to the servo object
pinMode(buttonPin, INPUT);
digitalWrite (buttonPin, LOW);
// open the serial port at 9600 bps:
Serial.begin(9600);
}
void loop() {
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
if (oscCounter <= 2){
if (micros() - lastUpdate >= interval) { //if the difference between the time at the current position and the time at the previous position is greater than our assigned interval
lastUpdate = micros();
Counter = Counter + 1; //Increase counter
if (Counter == 200) {
Counter = 1; //Reset Counter once it reaches the end of the Servo Position Array
oscCounter = oscCounter +1;
}
//Update the servo position
pos = pgm_read_word_near(ServoPos + Counter); //retrieve the "i"th value from the Servo Position Array in PROGMEM, and save that value as "pos"
myservo.writeMicroseconds(pos); //move servo to the new "pos" position
Serial.print(pos);
Serial.print("\t"); // prints a tab
Serial.print(Counter);
Serial.print("\t"); // prints a tab
}
}
else if (buttonState == HIGH){
oscCounter = 0;
}
}
UKHeliBob:
Can the servo actually move that fast ?
I don't think so, but check my math.
The 60 degree move time is pretty much the given speed standard, so it is easy to find.
Typical hobby servos move 60 degrees in .10-.20 seconds. This would support a sweep at 5Hz or less.
Higher speed applications like tail servos for helicopters are faster. 60 degrees in .04 seconds.
My math says that the best speed this will give is 16.5Hz.
UKHeliBob:
So the servo has to move from say 90 to 150 and back 20 times per second.
Is that right ?
Can the servo actually move that fast ?
The usual servo project question : How is the servo powered ?
The servo has a speed of 0.066 s/60 degrees. These questions jogged my memory, and reminded me that my previous working program had the servo oscillating a ~30 degree sweep at maybe 15 Hz, which may be good enough for my project. The servo is powered by a 8.4 V, 3000 mAh NiMH battery pack.
slipstick:
You could try running Serial faster than 9600 (e.g. 115200) or, better yet, take those time-wasting serial prints out completely.
Steve
Thank you, Steve! You were right on the money! I did both of those things, and it is running much faster. I've set up some tests to see how fast it will actually go, and will update this post with my results and also maybe with some more questions. My next step is optimizing my code for fastest possible results.
UKHeliBob:
I am intrigued as to what the servo is moving at this rate
It is part of a prototype for my Master's thesis in biomedical engineering. It oscillates a rigid vane, set inside a housing, to produce pressurized air waves. The airwaves go into a persons mouth (and into the lungs), to stimulate the respiratory system. The response of the system (how much your lungs "jiggle") can be used to diagnose various lung diseases.
//Including Libraries
#include <Servo.h> //Necessary for Servo Control
#include <avr/pgmspace.h> //Necessary for storing and retreiving data into/from Programmable Memory (PROGMEM)
//Objects
Servo myservo; // create servo object to control a servo
//Variables
//in PROGMEM (Arduino Forum: this array below is representative of its original 200-element length)
const int ServoPos[] PROGMEM = {1500 , 1516 , 1531 , 1547 , 1423 , 1438 , 1453 , 1469 , 1484};
//in RAM
int buttonState = 0; // variable for reading the pushbutton status
int oscCounter = 3;
unsigned long lastUpdate = 0; // will store the time that the Servo Position was last updated
byte Counter = 1;
int pos = 1500; //introduce local variable "pos" and set it equal to the home position of the servo
// constants won't change :
const long interval = 10000; // interval at which to update Servo Position (microseconds), 1/freq
const int buttonPin = 2; // pushbutton pin
void setup() {
myservo.attach(9); // attaches the servo on pin 9 to the servo object
pinMode(buttonPin, INPUT);
digitalWrite (buttonPin, LOW);
// open the serial port at 9600 bps:
Serial.begin(115200);
}
void loop() {
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
if (oscCounter <= 2){
if (micros() - lastUpdate >= interval) { //if the difference between the time at the current position and the time at the previous position is greater than our assigned interval
lastUpdate = micros();
Counter = Counter + 1; //Increase counter
if (Counter == 200) {
Counter = 1; //Reset Counter once it reaches the end of the Servo Position Array
oscCounter = oscCounter +1;
}
//Update the servo position
pos = pgm_read_word_near(ServoPos + Counter); //retrieve the "i"th value from the Servo Position Array in PROGMEM, and save that value as "pos"
myservo.writeMicroseconds(pos); //move servo to the new "pos" position
}
}
else if (buttonState == HIGH){
oscCounter = 0;
}
}
I am wondering now if anyone could provide me insight into how much time the operations in my code would theoretically take. Is there a minimum time that it take an Uno to retrieve data from PROGMEM? Or a minimum time the myservo.writeMicroseconds operation takes? Or how I can figure this out on my own?
I am oscillating my servo at maximum frequencies of around 12 Hz for a 50 degree sweep, and I am wondering if that is due to the limitations of my servo, or limitations of my arduino code/hardware.