Serial Read, String, Convert ATOI

Hey guys, I am not sure if this is a popular subject but I have tried looking through Google as "arduino read string from serial convert to integer" because my issue is having a number entered in the serial by a user and then having that character/string converted into an actual number. My code where I am having problems is shown below

char c = Serial.read(); 
      readString.indexOf(c); //I have also tried using readString.concat(c) as well as append
    int n;
    n = atoi(readString); //convert string to number

The error I get is "error: cannot convert 'String' to 'const char*' for argument '1' to 'int atoi(const char*)'". So, I am unsure of what I am doing wrong. I did also try

char *c = Serial.read(); and arduino did not like that either. Help would be much appreciated.

http://arduino.cc/en/Reference/StringToCharArray ?

AWOL, can you provide an example of how to use the StringToCharArray? I am not quite sure that, that is what I am looking for...

No, sorry, I don't use String. You could search for an example.

char buffer[20];
readString.toCharArray(buffer, 20);
int nRead= atoi(buffer);
long nReadLong= atol(buffer);
unsigned long nReadHex= strtoul(buffer, NULL, 16);

but I think you should not use a String object as intermediate buffer but try to use a char array (buffer in my example) directly. This route requires more knowledge of C and string handling but you need less memory and will not get any heap fragmentation - source of many problems with the limited SRAM we have.

Mareko,

does the length"20" is that the maximum tolerance? If I choose 5, could I have a character length of 5 or less and it still work? Will this read a two or three digit number as a whole entity? I.e. 593 is Five hundred ninety-three or 32 is thirty-two and not 3,2.

From what I understand of your code: The buffer is 20 element array. There is a string length of 20 in the "readString.toCharArray" part you convert the string to integer in the next line.

The last line in the code looks like it has to do with hexidecimals but do I need to use that for reading strings from the serial monitor to convert to integers or floats?

Here’s a sample I had handy that does something similar to what you seem to want - it reads a number (unix time_t value) delimited by a starting ‘T’ and an ending ‘!’:

/*----------------------------------------------------------------------------*/
/* Get a single character from the host                                       */
/*----------------------------------------------------------------------------*/
static int getb(void)
{  while (!(Serial.available() > 0));  /* Wait for data to arrive             */
   return Serial.read();               /* Return next character               */
}                                      /*  end: getb()                        */
/*----------------------------------------------------------------------------*/
/* Get current date/time and set the system reset time                        */
/*----------------------------------------------------------------------------*/
void get_time(void)
{  time_t t;                           /* Current system time                 */
   int c;                              /* Input character                     */
   do                                  /* Until end of message                */
   {  Serial.println("?T");            /* Send time query to host via USB     */
      t = 0;                           /* Initialize time value               */
      while ('T' != getb()) ;          /* Watch for start of time response    */
      while (('0' <= (c = getb())) && (c <= '9')) /* Is this a decimal digit? */
      {  t = 10 * t + (c & 0x0F);      /* If so, build time value             */
      }                                /*  end: building time value           */
   }  while (c != '!');                /* Until a valid time is received      */
   set_time(ss(t));                    /* Calculate and save reset time       */
}                                      /*  end: set_time()                    */
/*----------------------------------------------------------------------------*/

A buffer always has a defined length. I just took 20 bytes without any reason. If you know that your number will be smaller then take an appropriate buffer. Last character of a C string has to be a '\0', so you need one place more for it in the character array (also called string in C). toCharArray() will only copy as many characters to the C string as will fit (19 chars + the trailing 0 in my example, you have to tell the function how long your buffer is). The three conversion lines are only examples of different conversion functions (string to int/long and hex string to unsigned long).

I tried your code, Mareko and I still have some problems. What I am wanting to do is have the program on the serial monitor ask what number I would like to use. After that question has been posed by the program it waits for an input from the user to enter in any positive integer number for a one-digit up to a three digit number. The number that is entered in by the user will then be translated and converted into an actual integer since the input is a string of characters and not a number.

The main commands I know I need are Serial.begin, Serial.read, and atoi. How I get the Serial.read to atoi is a huge challenge I am having. I have had some experience in beginner programming. Right now I am in a class using Matlab and this is not my first time using Arduino but for the life of me, I am not sure why coding and programming with Arduino is not as comprehensive as c-language in Matlab.

This is still just C with some C++ additions. No magic. First important information: serial transmission of data is slow. Between two characters the Arduino runs in circles doing ‘nothing’. When you type something into the serial monitor your characters will be transmitted one by one and the return you pressed is transmitted, too. So if you type 413 and read this at the Arduino you’ll get “413\r\n” - 5 characters. Usually you read a character from Serial (Serial.read()) if one is available (Serial.isavailable()) and store it in a string until a ‘\n’ character is received (ignoring and not storing the ‘\r’ character). This ‘\n’ char is the signal to start conversion to an int (do not store the ‘\n’). If you use C strings and not the String class it is also the signal to add a ‘\0’ to the received and stored characters to create a valid C string (the String class makes this all the time). Does this help? There are plenty of threads about reading from Serial.

Mareko, thanks. I learned something today about how the serial reads the user input. I understand that the Arduino will go through a loop and take each character as its own entity in a string from a user-input. Like 187 is "1", "8" and "7". My main concern is figuring out how to convert a character or string to an integer or a float value from the Serial read value. I know that somehow I can change each character into a number and then do a simple program so that 187 is 1*100 + 8*10 + 7*1 = 187 but that would be after the program has converted the characters into numerical values.

Thank you, though. I did not know about the \r and \n read until after you informed me.

@Morris: If OP can't use a simple library function to extract a character array from a String class, and can't understand a 5 line example that is presented, how do you expect him/her to understand that code?

If you are going to contribute to the thread, it HAS to be at a level that the OP can understand.

"Just use this" doesn't help.

@Paul...

We agree that the OP is in over his/her head from the start.

My preference was to provide an example of some working code that didn't compound the lack of understanding by introducing unnecessary discussion of String memory fragmentation, buffer arrays, and not readily-visible library functions.

I may not have it right - it may, indeed, be somehow more effective to learn to do things badly before learning to do them well - but that seems like an incredible waste of human resources to me. Obviously, everyone's mileage varies on this. :grin:

My preference was to provide an example of some working code that didn't compound the lack of understanding by introducing unnecessary discussion of String memory fragmentation, buffer arrays, and not readily-visible library functions.

An admirable goal, but I think you missed the mark.

While there are disadvantages to using functions like atoi(), the advantages are that the code was developed by professionals, the code has been fully tested, over decades, and is known to work.

Explaining how to capture data in an array, that can be printed out to validate that it was received correctly, and then how to use proven library methods like strtok() and atoi() to newbies seems a more useful technique than showing, but not explaining, code that uses pointers, bit operations, magic numbers, etc.

I guess that we will just have to agree to disagree. I think pointing users to atoi() to convert a string to a value is perfectly acceptable. Unless the user is so tight on RAM that a small buffer to read the whole value is not available. Even when that is the case, and code like yours is necessary, I think that explaining how the code works is necessary.

An admirable goal, but I think you missed the mark.

I may have.

While there are disadvantages to using functions like atoi(), the advantages are that the code was developed by professionals, the code has been fully tested, over decades, and is known to work.

I find less comfort in that mantra. For more than a half century, I made a living by cleaning up ghastly messes developed by "seasoned" professionals. Along the way, I found (and corrected) errors in not only their code, but in a more than reasonable number of standard library functions.

Explaining how to capture data in an array, that can be printed out to validate that it was received correctly, and then how to use proven library methods like strtok() and atoi() to newbies seems a more useful technique than showing, but not explaining, code that uses pointers, bit operations, magic numbers, etc.

I agree - and that's why I selected a snippet with line-by-line comments explaining what that line contributed to the process. When/if that's not adequate, the reader at least has an opportunity to point and say: "That right there, I don't understand what's happening," and ask for a more detailed explanation. Where we seem to disagree appears (to me) to have more to do with "easy" than with building software problem-solving skills.

I guess that we will just have to agree to disagree. I think pointing users to atoi() to convert a string to a value is perfectly acceptable. Unless the user is so tight on RAM that a small buffer to read the whole value is not available. Even when that is the case, and code like yours is necessary, I think that explaining how the code works is necessary.

I have no problems with strings or atoi(). I don't mind that we might have philosophical differences and/or differing opinions about what's worth learning and at what stage in programmer development different things can/should be learned - and I confess to a strong bias in favor of compact, fast, and correct code, and a near complete indifference toward ease.

I feel like I’m in the middle of an arguement. In any case, I am still having problems from getting from point A to point B-my code worked two years ago with Arduino but there have been a lot of updates since then. I was going to use my old class programs as reference for a project I am contributing to as part of a club.

PaulS:
Explaining how to capture data in an array, that can be printed out to validate that it was received correctly, and then how to use proven library methods like strtok() and atoi() to newbies seems a more useful technique than showing, but not explaining, code that uses pointers, bit operations, magic numbers, etc.

I take it strtok() is conversion of string to integer?

This is the whole code of what I was trying to use bits of for referencing which now this code is obsolete. I am sure it is changing a little bit in the code but I tried searching the forums and google about this alternate version and cannot find what I am looking for. It may be because I read somewhere that Arduino now has a string library with the software so I do not need to unzip and drag a WString library to the arduino library. If that is the case, I know .append does not work but this is to show the original code. .concat or .indexOf I believe is the substitute but even then the code does not work.

I must be doing some very simple programming in my Matlab class if I cannot understand my own code in Arduino and why now it does not work anymore on top of the error message not making any sense to me either.

//Servo control #2 by [color=yellow]Anonymous[/color]
//The way this program works is by when a pushbutton is pressed once, it will prompt the
//user to enter in a angle from 0 to 180 degrees. The angular change is approx. 45 deg/s
//Once the angle is reached, the screen should prompt user to enter in a new angle value
//If angle value is outside of 0 to 180 it displays an error message
//If angle value is 999 it deactivates the system
//The system can also be deactivated by pushing the button a 2nd time

#include <Servo.h>
#include <WString.h> //Make sure to download this if your arduino did not come with it at http://arduino.cc/en/uploads/Tutorial/String.zip
String readString = String(100);

const int  buttonPin = 2;    
const int motorPin = 3;      


int buttonPushCounter = 0;   
int buttonState = 0;         
int lastButtonState = 0;     

short timer= 20;
short timer2 = 100;

Servo myservo;
void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(motorPin, OUTPUT);

  Serial.begin(9600);
  myservo.attach(3);
}
void loop() {
  buttonState = digitalRead(buttonPin);
  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      buttonPushCounter++;
    } 
    else {
    }
    lastButtonState = buttonState;
  }
  
if(buttonPushCounter % 2 == 1){
  if(buttonState == HIGH){
      Serial.println("System ACTIVE");
      myservo.write(90);
      delay(timer2*10);
      Serial.print("Enter in a value between 0 and 180 degrees: ");
      delay(timer2);
  }
  else{

	  while (Serial.available()) {
	  delay(10);  
	    if (Serial.available() >0) {
	  char c = Serial.read();  //gets one byte from serial buffer
	  readString.append(c); } //makes the string readString
	  }
	 
        if(readString.length() >0) {
	
	int n;
	n = atoi(readString); //convert string to number
        if (n != 999){
	Serial.println(readString);
              if((n > myservo.read()) && (n<= 180)){
          for (int i= myservo.read(); i<= n; i+=1){
          myservo.write(i);
          delay(timer);
          if (i == n) {Serial.print("Enter in a value between 0 and 180 degrees: "); break;   
          }
          }
              }
              else if((n < myservo.read()) && (n>=0)){
          for (int i= myservo.read(); i>= n; i-=1){
          myservo.write(i);
          delay(timer);
          if (i == n) {Serial.print("Enter in a value between 0 and 180 degrees: "); break;   
          }
          }
              }
              else if((n>180 || n <0) && n!=999){
          Serial.print("Error\nEnter in a value between 0 and 180 degrees: ");
          delay(timer2);
              }
              else if(n == myservo.read()){
              Serial.print("The servo is already set at this angle.\nEnter in a value between 0 and 180 degrees: ");
              delay(timer2);
              }
        }
        if (n == 999){ //if 999 is entered, it turns off and centers servo
        myservo.write(90);
        Serial.println("\n\n\n");
        Serial.println("System DEACTIVATED");
        Serial.println("\n\n\n\n\n\n\n\n\n\n\n\n\n");
        buttonPushCounter++;
        delay(timer2);
        }
	
	readString="";
	}      
  }
}
if (buttonPushCounter % 2 == 0){//turns off and centers servo
    if(buttonState == HIGH){
        myservo.write(90);
        Serial.println("\n\n\n");
        Serial.println("System deactivated");
        Serial.println("\n\n\n\n\n\n\n\n\n\n\n\n\n");
      delay(timer2);      
}
}


}

Where we seem to disagree appears (to me) to have more to do with "easy" than with building software problem-solving skills.

Once can never get to the point of being able to create robust code like you do without a lot of experience. Some of that experience comes from producing working code. When one gets comfortable doing that, then one can explore what the libraries/functions that are taken for granted are doing. I prefer to help people get something going. You prefer to offer alternatives to the standard library routines. Wile I have no problem with that, showing a Ferrari to a kid who keeps falling off a tricycle is probably not going to turn them into a competent vehicle operator. Learning to ride the tricycle first, then the bicycle with training wheels, then without the training wheels, etc. is necessary, in my opinion.

I feel like I'm in the middle of an arguement.

There's no yelling, name calling, etc. going on. It's just a discussion about the best way to help you.

I take it strtok() is conversion of string to integer?

No. The strtok() function is a string parsing function. The atoi() function converts a string to an integer value.

It may be because I read somewhere that Arduino now has a string library with the software so I do not need to unzip and drag a WString library to the arduino library.

Case is important. The Arduino has a String library. While it is a good idea, the implementation leaves a lot to be desired on such a memory-constrained platform. Even if it didn't have a String library, unzipping and dragging a WString library to the Arduino would probably not be a good idea.

String readString = String(100);

One of the changes between the old WString class and the new String class is what the constructor does. Now, the value in the parentheses is converted to a string, and wrapped in the String object. The value no longer reserves space.

      readString.append(c); } //makes the string readString

The += operator is much easier to use and understand, in my opinion.

readString += c; // Append c onto the end...
    n = atoi(readString); //convert string to number

atoi() doesn't know anything about Strings, which are not the same as strings. There are three or more ways to deal with the issue. One, the best, is to ditch the String class altogether. Storing data in a NULL terminated char array is simple. That string can then be passed to atoi(). The second way is to use the String::toInt() method, and let it make the call to atoi() on the string is wraps. The third way would be to use String::toCharArray() to extract the wrapped string, and pass that to atoi().

There's Morris' method, too. It's a great approach, if you understand it.

Without knowing exactly what issues you are having with the code, I'll leave it at this, for now.

The issue I was having was during compilation:

error: cannot convert 'String' to 'const char*' for argument '1' to 'int atoi(const char*)

It sounds like what you are explaining relates directly to the error I get when compiling.

Can you provide an example of the second and third method you mentioned? The first method sounds like a basic talor series function I can use with an array. The only problem I may have is knowing which number would be placed in array first. Let's say I enter in 142 degrees. I am guessing that "1" would be entered into the first, "4" into the 2nd and "2" in the 3rd. So then I could do something similar to in a loop...

numFromPort = numFromPort + (data[i]) * pow(10, 6-i);

Since you're pointed in this direction, in your loop you might want

num = 10 * num + digit_value;

In order to make that work, num will need to be set to zero before looping and each digit character will need to be converted to that digits binary value.

A way to convert an ASCII digit character to its corresponding binary value is to subtract an ASCII zero from it so that

'0' - '0' -> 0 '1' - '0' -> 1 '2' - '0' -> 2 ... '9' - '0' -> 9

If you incorporate this into the assignment in the loop, it comes out:

num = 10 * num + digit_char - '0';

Does this make sense to you?