Go Down

Topic: How to constrict serial input to only 3 numbers? (Read 23918 times) previous topic - next topic

johndimo

Hello everyone, new to the forums and to the arduino platform.

I'm looking to use create some validation code on what is typed into the serial monitor. It needs to be a 3 digit number between 0 and 180. I'm using that number to store to a variable called ServoPos for use with the servo write command.

I've gotten as far as it working at limiting number inputs between 0 and 180, but haven't figured out how not allow other characters such as letters or symbols. If I type anything other than numbers the code will simply return a 0 value to me ServoPos variable.

Here is the code I have so far.
Code: [Select]
#include <Servo.h>

Servo myservo; //Name our Servo
int x = 0;
int ServoPos = 90; //int to hold position info. Set at Midpoint by default
char buffer[3]; //setup array to hold serial data

void setup()
{
 myservo.attach(9); //Setup servo called 'myservo' on Pin 9
 Serial.begin(9600); //Open serial comm port
 Serial.print('\r');
}

void loop()
{
if (Serial.available() > 0){ //checks if anything is in serial buffer
    delay(10); //wait for full message to be received
      x = 0;
      while (Serial.available() > 0){
        buffer[x] = Serial.read(); //Fill the array with up to
        buffer[x+1] = '\0'; //needed to tell atoi() command when array ends
        x++;  
      }
      ServoPos = atoi(buffer); //convert the array into an integer
      if (ServoPos < 0 || ServoPos > 180){ //check to make sure position is within allowable range
        Serial.println("Enter number between 0 and 180"); //print error message
        delay(25);
      } else {
      Serial.print("Position is: ");//Relay back new position info to screen
      Serial.println(ServoPos, DEC);
      myservo.write(ServoPos);//move the servo to commanded position
     
    }}
}


Any help would be greatly appreciated!
http://www.johndimo.com

johndimo

I got the atoi funtion from googling. It is a built in C function that coverts a string into and integer. I guess it works on the arduino platform even though it isn't documented on the reference part of the site.

The way I wrote my code if I type in "123", it'll get converted to and integer value of 123, perfect for setting the position for a servo.

Problems start if I try to send "a123" or anything that contains anything other than a number.

If there is a better way to covert serial strings into integers, I'm all ears.
http://www.johndimo.com

PaulS

Code: [Select]
    delay(10); //wait for full message to be received
This comment is plain wrong. The serial data might all arrive in 10 milliseconds. It might not. You should not be relying on artificial delays to "assure" that all the data arrives.

You should, on the other hand, add some end-of-packet marker, and only process the data when the end-of-packet marker arrives, no matter how long it takes.

Code: [Select]
        buffer[x] = Serial.read(); //Fill the array with up to
        buffer[x+1] = '\0'; //needed to tell atoi() command when array ends
        x++;  

If you don't want to pass non-digit characters to atoi(), don't store them in the array. Keep in mind, though, that - is a digit ("-14" is perfectly legal input to atoi).

Code: [Select]
        Serial.println("Enter number between 0 and 180"); //print error message
        delay(25);

You've told the user he/she's a knucklehead. What are you waiting for?

johndimo

Quote

This comment is plain wrong. The serial data might all arrive in 10 milliseconds. It might not. You should not be relying on artificial delays to "assure" that all the data arrives.

You should, on the other hand, add some end-of-packet marker, and only process the data when the end-of-packet marker arrives, no matter how long it takes.


Not sure how else to do this. I'm a noob and learning via google, here, and what I read on the main arduino site. I'll start googling end-of-packet code examples.


Quote
If you don't want to pass non-digit characters to atoi(), don't store them in the array. Keep in mind, though, that - is a digit ("-14" is perfectly legal input to atoi).


This is the main reason I am posting on the forums. Trying to learn. Some stuff is hard to google if I don't use the right keywords.  If there is a way to receive numbers via serial and store them as integers and ignore everything else, that would be great.

Quote

You've told the user he/she's a knucklehead. What are you waiting for?

That part of the code is just for testing purposes.  A random message if I try to put in a number outside the boundaries of 0 to 180.

I'm just trying to wrap my brain around all this stuff.  There's lots to learn for someone with only limited programming done in basic years ago. I have no prior C experience at all.
http://www.johndimo.com

PaulS

Quote
I'll start googling end-of-packet code examples.

Start by search the forum for "started && ended". I've posted code before that reads serial data bounded by start and end of packet markers.

zoomkat

Quote
I've gotten as far as it working at limiting number inputs between 0 and 180, but haven't figured out how not allow other characters such as letters or symbols. If I type anything other than numbers the code will simply return a 0 value to me ServoPos variable.


The servo library is pretty robust as to what gets sent to a servo. Do some actual testing with a servo to see what causes a problem and what doesn't. Try adding extra characters, characters mixed in with the numbers and such. I think no matter what is sent, the position control signal actually sent to the servo can only be 0-180. The "started && ended" is probably a dead end for your project, what ever it is. If the three character position value can be incorrectly sent, so can the start and end delimiters. The delimiters do nothing to ensure what is inside them isn't junk. What you need really code wise depends on what is sending the position characters and what are the types of errors that need to be defended against. Why spend $600 for a fluke multimeter when a harbor freight $4 multimeter will perform acceptably well for the intended use.
Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

spambake

If example code helps, you might want to look at the code for the Torrentmeter (google that).  It accepts serial input and turns it into PWM to drive a meter.  It's set up to work with LCD Smartie but accepts input from Serial Monitor just fine.

The program uses receipt of an upper case letter for the end marker for the packet and the letter is used to route the info to different meters.  He made his own isNumber() and isUpper() equivelents but they are available in the IDE without that.

I butchered up his code and Frankensteined it with some stepper code to run some steppers to various positions.

Graynomad

You should at least reject any non-numeric characters as already stated, something like

Code: [Select]
c = Serial.read();
if (isNumber(c)) {  // note, made-up function, either write or get code from somewhere
  buffer[x++] = c;
  buffer[x] = '\0';
}


______
Rob

Rob Gray aka the GRAYnomad www.robgray.com

PaulS

Quote
The servo library is pretty robust as to what gets sent to a servo. Do some actual testing with a servo to see what causes a problem and what doesn't.

And the relevance of this statement to atoi processing strings to ints is?

Quote
I think no matter what is sent, the position control signal actually sent to the servo can only be 0-180.

Well, the compiler will certainly complain if you pass anything but an int to servo.write().

Quote
The "started && ended" is probably a dead end for your project, what ever it is. If the three character position value can be incorrectly sent, so can the start and end delimiters.

But, the use of delimiters means that you don't have to send three characters to set the position to "90" or "5". You send only as many characters as are needed to represent the value to send the servo to, plus the delimiters.

The delimiters also offer an opportunity to do some error checking. A serial stream composed of "100110120" might be 100, 110, 120. But suppose one of the characters got lost, and the stream consisted of "10110120". There is never a way to get back in sync. If the serial data consisted of "<100><110><120>, the actual values are easy to pick out. If a digit gets lost, and the serial data instead contained "<10><110><120>", one missing character won't result in the expected value, and the servo will go to  the wrong place, but then it will go back where it is supposed to go.

Aslo, with start and end of packet markers, additional data, like string length up front, and CRC on the end is easy to add. Without delimiters, it is much more difficult.

Quote
Why spend $600 for a fluke multimeter when a harbor freight $4 multimeter will perform acceptably well for the intended use.

Neither of us knows whether OP needs the fluke or the junk. However, the increased cost of doing the code right is not quite as high as your multimeter example would infer.

zoomkat

Seeing as the string would be received as characters, I'd do a test on the characters to see if they are appropriate. Assuming that the string will always have three characters (an 8 deg position would be sent as 008), then evaluate each character in the string like so (assuming the characters have numeric values for comparison). Combinations in the second and third characters that would result in a position greater than 180 are handled in the servo library by limiting them to being 180.

fist character >= '0' and <= '1'
second character >= '0' and <= '9'
third character >= '0' and <= '9'

Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

zoomkat

Quote
And the relevance of this statement to atoi processing strings to ints is?


snip...

Your concern over atoi and compiling issues seems to be somewhere out in left field in reguards to solving the three character issue. Fix the character issue up front so the proper characters are in play and your atoi and compiling concerns are non events.
Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

PaulS

And, if you feed proper data into atoi, all your crap about the robustness of the servo library is irrelevant.

zoomkat

#12
Dec 24, 2010, 05:16 am Last Edit: Dec 24, 2010, 06:18 am by zoomkat Reason: 1
Quote
And, if you feed proper data into atoi, all your crap about the robustness of the servo library is irrelevant.


Actually the robustness of the servo library makes getting the appropriate characters to feed to atoi easier.
Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

retrolefty

Quote
It needs to be a 3 digit number between 0 and 180.


But 0 is a one digit number and 42 is a two digit number.  ;)

Lefty

zoomkat

#14
Dec 24, 2010, 06:09 am Last Edit: Dec 24, 2010, 06:17 am by zoomkat Reason: 1
Quote
But 0 is a one digit number and 42 is a two digit number.


Perhaps 000 and 042 would be sent. Just the character/number thing.
Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

Go Up