I am trying to create a servo controller that accepts input via serial...
Here is the complete code I have so far. None of the servo code is written yet, but once I figure out how to convert a String to an int I can easily add that. Any code improvements would be appreciated also.
//receive 5 numbers via serial. First 2 numbers are the servo number (01,02,etc...), Second 3 numbers are the servo position(001,030,180,etc...)
char inData[4]; // Allocate space for incoming serial data (2 digit servo number, 3 digit position)
char inByte; // Incoming serial character
byte index = 0; // Index into array; where to store the character
int8_t servo_num;
int8_t servo_position;
void setup() {
Serial.begin(9600);
}
void loop() {
while (Serial.available() > 0){
inByte = Serial.read (); //read serial input one byte at a time
inData[index] = inByte; //add serial input to array
index++; //increment index (length/bytes of serial data received)
if (index > 4 || inByte == 10 || inByte == 13){ //serial data is 5 bytes long or received a charage return or line feed
inData[index] = '\0'; //Null terminate the string
index = 0;
//Is there a better way to do the next 3 lines?
String serialData = inData; //convert character array to String
String servoNum = String(String(aton(serialData.charAt(0)))+String(aton(serialData.charAt(1))));
String servoPosition = String(String(aton(serialData.charAt(2)))+String(aton(serialData.charAt(3)))+String(aton(serialData.charAt(4))));
//****THIS IS THE PART I AM STRUGGLING WITH****
//need to convert servoNum and servoPosition to integers servo_num and servo_position...
//****THIS IS THE PART I AM STRUGGLING WITH****
Serial.println(servo_num);
Serial.println(servo_position);
}
}
}
int8_t aton(int num){
//convert ASCII code to number
int aton_i;
if (num == 48){
aton_i = 0;
} else if (num == 49){
aton_i = 1;
} else if (num == 50){
aton_i = 2;
} else if (num == 51){
aton_i = 3;
} else if (num == 52){
aton_i = 4;
} else if (num == 53){
aton_i = 5;
} else if (num == 54){
aton_i = 6;
} else if (num == 55){
aton_i = 7;
} else if (num == 56){
aton_i = 8;
} else if (num == 57){
aton_i = 9;
}
return(aton_i);
}
Update: Thanks to help from Arrch the code is working, and I no longer need to worry about converting String to int. See updated code below.
// zoomkat 10-22-11 serial servo test
// type servo position 0 to 180 in serial monitor
// or for writeMicroseconds, use a value like 1500
// for IDE 0022 and later
// Powering a servo from the arduino usually *DOES NOT WORK*.
String readString;
#include <Servo.h>
Servo myservo; // create servo object to control a servo
void setup() {
Serial.begin(9600);
myservo.writeMicroseconds(1500); //set initial servo position if desired
myservo.attach(7); //the pin for the servo control
Serial.println("servo-test-22-dual-input"); // so I can keep track of what is loaded
}
void loop() {
while (Serial.available()) {
char c = Serial.read(); //gets one byte from serial buffer
readString += c; //makes the string readString
delay(2); //slow looping to allow buffer to fill with next character
}
if (readString.length() >0) {
Serial.println(readString); //so you can see the captured string
int n = readString.toInt(); //convert readString into a number
// auto select appropriate value, copied from someone elses code.
if(n >= 500)
{
Serial.print("writing Microseconds: ");
Serial.println(n);
myservo.writeMicroseconds(n);
}
else
{
Serial.print("writing Angle: ");
Serial.println(n);
myservo.write(n);
}
readString=""; //empty for next input
}
}
I have been looking to figure out a way to do the same on some longer strings with various types of values.
I have been looking into two functions not commonly used on the arduino stuff.
// zoomkat 10-4-10 serial servo test
// type servo position 0 to 180 in serial monitor
// for writeMicroseconds, use a value like 1500
// for IDE 0019 and later
// Powering a servo from the arduino usually DOES NOT WORK.
String readString;
#include <Servo.h>
Servo myservo; // create servo object to control a servo
void setup() {
Serial.begin(9600);
myservo.writeMicroseconds(2000); //set initial servo position if desired
myservo.attach(7); //the pin for the servo control
Serial.println("servo-test-21"); // so I can keep track of what is loaded
}
void loop() {
while (Serial.available()) {
delay(1);
if (Serial.available() >0) {
char c = Serial.read(); //gets one byte from serial buffer
readString += c; //makes the string readString
}
}
if (readString.length() >0) {
Serial.println(readString); //so you can see the captured string
int n;
char carray[6]; //converting string to number
readString.toCharArray(carray, sizeof(carray));
n = atoi(carray);
myservo.writeMicroseconds(n); // for microseconds
//myservo.write(n); //for degees 0-180
readString="";
}
}
I ended using Arrch's approach,with a bit of tweaking (less code and achieves exactly what I am looking for).
Here is the updated code:
//receive 5 numbers via serial. First 2 numbers are the servo number, Second 3 numbers are the servo position
char inData[5]; // Allocate space for incoming serial data (2 digit servo number, 3 digit position)
char inByte; // Incoming serial character
byte index = 0; // Index into array; where to store the character
int8_t servo_num;
uint8_t servo_position;
char pos[4];
void setup() {
Serial.begin(9600);
}
void loop() {
while (Serial.available() > 0){
inByte = Serial.read (); //read serial input one byte at a time
inData[index] = inByte; //add serial input to array
index++; //increment index (length/bytes of serial data received)
if (index > 4 || inByte == 10 || inByte == 13){ //serial data is 5 bytes longs or received a charage return or line feed
inData[index] = '\0'; //Null terminate the string
index = 0;
servo_num = getServoNum();
servo_position = getServoPos();
Serial.println(servo_num);
Serial.println(servo_position);
}
}
}
int getServoNum(){
char num[3];
strncpy(num, inData, 2);
num[2] = '\0';
return(atoi(num));
}
int getServoPos(){
char pos[4];
strcpy(pos, inData+2);
pos[3] = '\0';
uint8_t t_pos = atoi(pos);
if (t_pos > 180){
t_pos = 180;
}
return(t_pos);
}
The only reason we copied the server number into it's own array was because it was null-terminated; you don't have that same concern in the server position.
Thinking about it further, you could just do something like this too, and save yourself a couple steps:
unsigned long tmp = atoi(inData);
int servo_num = tmp / 1000;
int servo_pos = tmp % 1000;
So say for example, inData was { '0', '2', '1', '2', '3', '\0' }.
tmp would be 2123.
tmp / 1000 would be 2
tmp % 1000 would be 123
I made the changes and it works great! Thanks for all the help.
I will play around with the tmp math...
//receive 5 numbers via serial. First 2 numbers are the servo number, Second 3 numbers are the servo position
char inData[6]; // Allocate space for incoming serial data (2 digit servo number, 3 digit position)
char inByte; // Incoming serial character
byte index = 0; // Index into array; where to store the character
int8_t servo_num;
uint8_t servo_position;
void setup() {
Serial.begin(9600);
}
void loop() {
while (Serial.available() > 0){
inByte = Serial.read (); //read serial input one byte at a time
inData[index] = inByte; //add serial input to array
index++; //increment index (length/bytes of serial data received)
if (index > 4 || inByte == 10 || inByte == 13){ //serial data is 5 bytes longs or received a charage return or line feed
inData[index] = '\0'; //Null terminate the string
index = 0;
servo_num = getServoNum();
servo_position = getServoPos();
Serial.println(servo_num);
Serial.println(servo_position);
}
}
}
int getServoNum(){
char num[3];
strncpy(num, inData, 2);
num[2] = '\0';
return(atoi(num));
}
int getServoPos(){
uint8_t t_pos = atoi(inData+2);
if (t_pos > 180){
t_pos = 180;
}
return(t_pos);
}
I have another issue now... I am trying to add some error checking to the if statement in the loop() function.
The if statement tests for index to be greater than 4 or inByte to be either a charage return or line feed.
I need to clear inData and break the while loop if index is less than 5, but a charage return/line feed was received.
I have tried several things, but none seem to achieve the intended results. Any ideas?
void loop() {
while (Serial.available() > 0){
inByte = Serial.read (); //read serial input one byte at a time
inData[index] = inByte; //add serial input to array
index++; //increment index (length/bytes of serial data received)
if (index > 4 || inByte == 10 || inByte == 13){ //serial data is 5 bytes longs or received a charage return or line feed
if (index < 5){
//clear inData...
break;
}
inData[index] = '\0'; //Null terminate the string
index = 0; //reset index
unsigned long tmp = atoi(inData);
servo_num = tmp / 1000;
servo_pos = tmp % 1000;
if (servo_pos > 180){
servo_pos = 180;
}
Serial.println(servo_num);
Serial.println(servo_pos);
}
}
}