Hello all, I am trying to control a servo using the serial monitor. I want to input a rotational velocity (deg/s) and the motor will hit that within the positional constraints I set. Simple enough. And I have got it to do it once and stay at that velocity oscillating as I want. But I can't get the thing to change its speed again.
I want to be able to type various velocities into the serial monitor and have it change when there is a new input. I'm relatively new to code and very new to Arduino, I know I could achieve this with certain loops in certain places but for the life of me I can't work it out.
Here is my code so far:
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 0; // variable to store the servo position
long val = 0; // New Speed
int input =0;
char incomingByte;
void setup() {
Serial.begin(9600);
Serial.println("Enter A New Velocity (deg/s)"); //Write instructions
myservo.attach(9); //Setting servo pin
}
void loop() {
if(Serial.available()){ //Checks that there is data in the buffer?
input = 0;
while (1) {
incomingByte = Serial.read();
if (incomingByte == '\n') break; // exit the while(1), we're done receiving
if (incomingByte == -1) continue; // if no characters are in the buffer read() returns -1
input *=10; //shift left 1 decimal place
input = ((incomingByte - 48)+input); //Converting ASCII to integer, add, and shift, left one decimal place
}
Serial.print("New Speed:");
Serial.print(input);
Serial.println("(deg/s)");
}
while(input = input){
for (pos = 0; pos <= 80; pos += input) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for (pos = 80; pos >= 0; pos -= input) { // goes from 180 degrees to 0 degrees
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
}
}
I know the connection works as running basic servo code runs everything fine. I am using an Arduino Uno and the standard Tower Pro micro servo motor.
The moment you have a Serial.available() you're going to get stuck in the while(1) loop until you receive a \n character. That's a pretty bad idea.
But after clearing that hurdle you're ending up stuck in the while(input = input) loop forever (no idea what you're really trying to there in the first place as the statement doesn't make sense).
I understand not to run servos on the Arduino but this is the starter kit servo and it's managed before so it should be fine but I will look into a better power supply later.
I think I am setting the velocity at the bottom where 'input' is the for loop condition. Once it gets there the other for loop takes it back and they go back and fourth, oscillating the servo.
Okay, I'll clear that hurdle and keep trying.
The while input = input loop was an attempt at having the loop run constantly but change when a new input is sent. I realise that probably isn't the best way to do it and it in fact does not do it but I can't think of another way to do it.
Dead easy connections, servo is connected to 5V port, the ground port and the 9 attachment port on the Arduino uno which is connected via USB to my laptop. No way to measure voltages on me I dont think, what are yout thinking I could do with that?
To check for a change you need to store the old value in a separate variable, comparing something to itself is always correct.
Then you're not comparing, you're assigning. A = is assignment; a == is boolean comparison.
There's a "serial input basics" tutorial around here, look it up.
Don't wait for Serial input - grab characters as they come in, store them in a char array, then when you see your terminating \n you process that string (look at atoi() for easy conversion to int).
So for you the Serial input should look more like this (untested; may have typos):
char input[5]; // store values 0-9999.
byte index;
int speed;
void loop() {
if (Serial.available()) {
char c = Serial.read();
if (c == '\n') {
input[index] = 0; // null termination
speed = atoi(input);
index = 0;
}
else {
input[index] = c;
index++;
}
}
// Servo code here - without while loops, loop() is doing that for you.
}
Some things left for you to add: input validation and making sure your buffer doesn't overrun.
I still can't get it to work even trying to use your adaption. Below is the code that I am trying to use now except now it isn't even oscillating for any, even the first, input. I have no idea where it's going wrong.
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 0; // variable to store the servo position
long val = 0; // New Speed
byte index;
char input [3];
char incomingByte;
void setup() {
Serial.begin(9600);
Serial.println("Enter A New Velocity (deg/s)"); //Write instructions
myservo.attach(9); //Setting servo pin
}
void loop() {
if(Serial.available()){ //Checks that there is data in the buffer?
incomingByte = Serial.read();
if (incomingByte == '\n');{ // If we hit enter
val = atoi(input);
input[index] = 0;}
}else {
input[index] = incomingByte;
index++;}
Serial.print("Input Speed:");
Serial.print(val);
Serial.println(); //Giving Space
for (pos = 0; pos <= 80; pos += val) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for (pos = 80; pos >= 0; pos -= val) { // goes from 180 degrees to 0 degrees
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
}
index never gets set back to zero after recognizing the \n.
You set the last byte in the input to the null character AFTER trying to read the input number. Swap the order of those two lines. Otherwise if you entered "12" and the next entry is "3" then the array contains "32".
You never check if you exceed the bounds of the input[3] array. If you entered "123" then you will be writing to a memory location you don't own.
The prompt to enter a speed prints on every oscillation instead of just printing once each time a new number is entered.
Okay, thank you, that made a lot of sense but I have changed those things around and I'm still not getting an output. And it doesn't like the nestled if statement within the other if statement. Gives an error that '}' is expected before 'else'.
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 0; // variable to store the servo position
long val = 0; // New Speed
byte index;
char input [3];
char incomingByte;
void setup() {
Serial.begin(9600);
Serial.println("Enter A New Velocity (deg/s)"); //Write instructions
myservo.attach(9); //Setting servo pin
}
void loop() {
if(Serial.available()){ //Checks that there is data in the buffer?
incomingByte = Serial.read();
if (incomingByte == '\n');{ // If we hit enter
Serial.print("Input Speed:");
Serial.println(); //Giving Space
input[index] = 0;
val = atoi(incomingByte);
Serial.print(val);
} else {
input[index] = incomingByte;
index++;
}
for (pos = 0; pos <= 80; pos += val) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for (pos = 80; pos >= 0; pos -= val) { // goes from 180 degrees to 0 degrees
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
}
}
The error still holds. The only thing I can think is that I'm missing some kind of protocol when putting an if … else statement within another if statement...
TolpuddleSartre: if (incomingByte == '\n');Whoops
No amount of ctrl-T is going to find that one.
Bloody hell :') Thanks for the catch there. I fixed that and still no output mind, hopefully there's another stupid thing like that maybe?
Okay, that's all sorted, yet it still won't work correctly. I've put Serial prints to tell me where the code is getting to and the problem is that at no point does it think the condition "if (incomingByte == '\n')" is true. So it runs the else and then gets stuck in the first servo for loop without going to the second.
In output this results in the servo receiving one input, hitting that speed to the desired position and then getting stuck at that position because it can't operate the second for loop to oscillate back.
Why this is happening is beyond me. The {} are all in the correct places, I have cross checked them witht he various codes I've pulled from, including Arduino's own tutorials.
Can anyone help? The code with the catch messages is here:
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 0; // variable to store the servo position
long val = 0; // New Speed
byte index;
char input [5];
void setup() {
Serial.begin(9600);
Serial.println("Enter A New Velocity (deg/s)"); //Write instructions
myservo.attach(9); //Setting servo pin
}
void loop() {
if (Serial.available()) { //Checks that there is data in the buffer?
char incomingByte = Serial.read();
if (incomingByte == '\n') { // If we hit enter
input[index] = 0;
Serial.print("Input Speed:");
val = atoi(input);
Serial.print(val);
Serial.println(); //Giving Space
index = 0;
Serial.println("First here");
} else {
index++;
Serial.println("Now here");
input[index] = incomingByte;
}
for (pos = 0; pos <= 60; pos += val) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15);// waits 15ms for the servo to reach the position
Serial.println("finally here");
if (val == 60) break;
}
for (pos = 80; pos >= 0; pos -= val) { // goes from 180 degrees to 0 degrees
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
}
}
When it starts, val is zero. So it gets stuck in those for loops. Enforce a minimum value for val just before entering that section.
if(val<MinVal) val=MinVal;
You spend a gigantic amount of time in those for loops. Your code will only process one character for each cycle. You will have to change to millis-based timing in the future but just for now, change if(Serial.available()) to while(Serial.available())