Go Down

Topic: ascii char to int conversion (Read 4823 times) previous topic - next topic

blokk_m

Hello,

I am sending an ASCII string through the serial port to my arduino board, which looks like this: X000Y000
where 'X' and 'Y' are used as string delimiters.

I'm saving the three characters that comes after the delimiter in an array, like this:

Code: [Select]

char incomingByte = 0;
char b_value[3];
char t_value[3];

void loop() {
   
      incomingByte = Serial.read(); 
     
      if(incomingByte=='X') {
       
        //read the next 3 bytes, and save them in an array 
        for(int i=0; i < 2; i++) {
          b_value[i] = Serial.read();
          }
         
          } else if(incomingByte=='Y') { 
   
            for(int i=0; i < 2; i++) {   
              t_value[i] = Serial.read();         
              }
     
      } else {
     
      incomingByte = Serial.read();   
     
      };


what I would like to do next is to put the values in the array together into an integer number.
For an example, if "b_value" consists of [0, 2, 1], I simply want 21. How could this be achieved?

thanks,

Grumpy_Mike

#1
Jun 12, 2011, 11:26 pm Last Edit: Jun 12, 2011, 11:27 pm by Grumpy_Mike Reason: 1
Simply multiply them up:-

Code: [Select]
b_value = t_value[0]*100 + t_value[1]*10 + t_value[2];

or if they are ASCII values then:-
Code: [Select]
b_value = (t_value[0] & 0xf) *100 + (t_value[1] & 0xf) *10 + (t_value[2] & 0xf);

blokk_m

Hi, and thanks for answering.

It almost works as expected now.. although when i try to send "X011"
in the serial monitor, i receive the number 10 instead of 11.
i'm guessing something is going on with the last digit in my array..

By the way, i'm unfamiliar with this bit of code in your answer (the "& 0xf" part..), could you/someone elaborate on this?
Code: [Select]
(b_value[0] & 0xf)

here's a modified version of my code if anyone want's to check it out..
Code: [Select]

char incomingByte = 0;
char b_value[3];
char t_value[3];

void setup() {

  Serial.begin(115200);
};

void loop() {
   
      incomingByte = Serial.read(); 
     
      if(incomingByte=='X') {
        //collect the next three bytes in an array   
        for(int i=0; i < 2; i++) {
          b_value[i] = Serial.read();
          }
         
          } else if(incomingByte=='Y') { 
   
            for(int i=0; i < 2; i++) {   
              t_value[i] = Serial.read();         
              }
     
      } else {
     
      incomingByte = Serial.read();   
     
      };
 
    //int b_val = b_value[0]*100 + b_value[1]*10 + b_value[2];
    //int t_val = t_value[0]*100 + t_value[1]*10 + t_value[2];
    int b_val = (b_value[0] & 0xf) * 100 + (b_value[1] & 0xf) * 10 + (b_value[2] & 0xf);
    int t_val = (t_value[0] & 0xf) * 100 + (t_value[1] & 0xf) * 10 + (t_value[2] & 0xf);

    Serial.println(b_val);
    delay(500);

};




PaulS

Code: [Select]
        for(int i=0; i < 2; i++) {
          b_value[i] = Serial.read();

With no Serial.available() calls, and no tests for valid values returned by Serial.read(), you are going to get a lot of (mostly) garbage values, this way.

Repeat after me: Serial data transmission is SSSSLLLLOOOOWWWW, compared to the speed of loop.
The art of getting good answers lies in asking good questions.

Grumpy_Mike

Quote
the "& 0xf" part

This is a logical AND it zeros all the bits with a zero in the number and leaves untouched all the bits with a one in the number. The number or mask, in this case is 0xf or in binary 0000 1111 so it converts from ASCII into a number.
If you are getting the wrong number back check the numbers are going into the array in the order you think they are.

blokk_m

@PaulS: Okey, I now have this at the beginning of the loop:
Code: [Select]
while(!Serial.available());

@Grumpy_Mike: Thanks for making that clear!

Anyways, i'm trying to follow your advices and i came up with this:
Code: [Select]

const int led0 = 9;
const int led1 = 11;

char incomingByte = 0;
char checkByte = 0;
char b_value[3];
char t_value[3];

void setup() {

  Serial.begin(115200);
 
  pinMode(led0, OUTPUT);
  pinMode(led1, OUTPUT);

};

void loop() {
 
  while(!Serial.available());     
   
      incomingByte = Serial.read(); 
     
      if(incomingByte=='X') {
          //collect the next three bytes in an array
          for(int i=0; i < 2; i++) {

            checkByte = Serial.read();

            //check that only values 0-9 are indexed
            if(checkByte >= '0' && checkByte <= '9') {

              b_value[i] = checkByte;

              //Serial.println(b_value); //debug
              } else {
             
              break;
              } 
          }
         
        } else if(incomingByte=='Y') {
         
          for(int i=0; i < 2; i++) {
         
            checkByte = Serial.read();

            //check that only values 0-9 are indexed
            if(checkByte >= '0' && checkByte <= '9') {
         
              t_value[i] = checkByte;
             
              //Serial.println(t_value); //debug
              } else {
             
              break;
              }
          }
     
      } else {
     
      incomingByte = Serial.read();   
     
    };
   
    //int b_val = b_value[0]*100 + b_value[1]*10 + b_value[2];
    //int t_val = t_value[0]*100 + t_value[1]*10 + t_value[2];
    int b_val = (b_value[0] & 0xf) * 100 + (b_value[1] & 0xf) * 10 + (b_value[2] & 0xf);
    int t_val = (t_value[0] & 0xf) * 100 + (t_value[1] & 0xf) * 10 + (t_value[2] & 0xf);
   
    //analogWrite(led0, b_val);
    //delay(30);
    //analogWrite(led1, t_val); 
    //delay(30);
   
    Serial.println(b_val); //debug
   
};


The idea was to have "if(checkByte >= '0' && checkByte <= '9') { .. }" make sure that only the numbers I want are indexed into the array.

But it doesn't seem to work as expected.
I get only 0 in return, when I input e.g. "X120Y000" in the serial monitor.
And I get nothing when I try Serial.println from the for loop(s).. so I guess the boolean in my if statement isn't working as I think it should?
I'm also not sure how to check that the order is correct when collecting the values from "checkByte".. 

Last, if anyone has a better suggestion for making this work, i'll be happy to hear it!

thanks,

Grumpy_Mike

I would try replacing:-
"if(checkByte >= '0' && checkByte <= '9')
with
"if(checkByte >= 0x30 && checkByte <= 0x39)
As those are the actual numbers, it could be the type casting is not happening like you want.

PaulS

Code: [Select]
  while(!Serial.available());     
   
      incomingByte = Serial.read(); 
     
      if(incomingByte=='X') {
          //collect the next three bytes in an array
          for(int i=0; i < 2; i++) {

            checkByte = Serial.read();

As soon as the first byte arrives, the while loop ends, and you then read the byte that arrived. If that byte is an 'X', you then read the next two bytes, which may not have arrived yet.

Since checkByte is not a digit, nothing is stored in the b_value array.

You really need to define an end of packet marker, and simply accumulate data until that end of packet marker arrives. Then, parse the stored data.

Either that or wait until 3 bytes have arrived before reading anything.

Keep in mind, though, that serial data transmission does not follow the UPS model, where they  guarantee to deliver the package. It follows the USPS model, where they guarantee to try to deliver the package.
The art of getting good answers lies in asking good questions.

draythomp

This is a very different way to do this.  Suppose you don't want to hang around waiting for a character to arrive because you have other things the processor can be doing.  This little bit of code checks for a character and uses a minimal state machine to tell where you are in the input.  This is not meant to discount other methods, just to show you a different way.

Using this you can type X123 wait an hour, type Y456 and the values get assigned while the processor could be blinking lights and stuff.  Of course you'll have to modify it to make it work for your purposes.  It's just an illustration, think of it as a toy and play around.

Code: [Select]
#include <ctype.h>

void setup() {
  Serial.begin(9600);
  Serial.println("just testing");
}

#define waitforX 0
#define waitforY 1
#define waitforYdata 2

char data[50];
int xValue = 0;
int yValue = 0;
int state = waitforX;

void loop(){
  char c;
  /* just another way of doing it, this method doesn't block and
  is controlled by a switch statement.  You can do other stuff while
  you wait for the data to come in.  To see what happens uncomment
  the line below and watch the scrolling window while you type.  */
 
  //Serial.println("Loop running");
 
  // to get more of a feel for it, change to "no line ending" in the
  // terminal window (menu at the bottom) then send one character
  // at a time.
 
  if (Serial.available() > 0){ // is there a character in the buffer?
    c = toupper(Serial.read()); // I just got tired of pressing the shift key
    Serial.print("Incoming character is ");
    Serial.println(c);         //just shows the incoming character
    switch (state) {           //decide what need to be done with it
      case waitforX:           // haven't got the X yet
        if (c == 'X'){         // there it is
          Serial.println("got the X");
          xValue = 0;  //start the X value off with zero
          state = waitforY;    //gather data until the Y shows up
        }
        break;
      case waitforY:
        if (c == 'Y'){         // the Y came in, gather Y data
          Serial.println("got the Y");
          yValue = 0;          // start the Y off at zero
          state = waitforYdata;
        }
        if (isdigit(c)){       // ignore non numbers
          xValue = (xValue * 10) + (c & 0x0f); //gather incoming numbers
          Serial.print("current Value of X is ");
          Serial.println(xValue,DEC);
        }
        break;
      case waitforYdata:       // same kind of thing with the Y data
        if (isdigit(c)){
          yValue = (yValue * 10) + (c & 0x0f);
          Serial.print("current Value of Y is ");
          Serial.println(yValue,DEC);
        }
        if (c == 'X'){  // this just starts over again
          Serial.println("got a beginning X");
          state = waitforY;
        }
        break;
            default: //ignore anything else
        break;
    }
  }
}

PaulS

No waitForXdata state? Or case?
The art of getting good answers lies in asking good questions.

draythomp

Naw, when you're waiting for Y, you're also collecting X data.  I could have bumped around on the X, but it was already getting too complicated for a simple illustration.  Sorta took the easy way out.

blokk_m

@draythomp: Thank you for your code! It works great and seems more efficient than the way I was doing it. I just had to add "xValue = 0;" in the last if statement, and now it's working as expected.
Code: [Select]

        if (c == 'X'){  // this just starts over again
          Serial.println("got a beginning X");
          xValue = 0;
          state = waitforY;
        }
        break;
            default: //ignore anything else
        break;
    }
  }
}

However, I just got to figure out a way to save the last number and the jump from that number straight to the next..because now it obviously starts from 0 every time I supply a new number.

@PaulS: I'll try out the "end-of-packet marker" approach too!


Thank you all for your answers!

draythomp

You're certainly welcome.  Remember though, there's about as many ways to do this as there are people on this forum.  My particular method has some advantages and some disadvantages; notably seeming waaay to complicated. The main purpose is to have some fun and accomplish something interesting.  If you implement Pauls's suggestion of an ending character, then the loop will get even simpler.  Just have a new case with that value, save your integers and go do something with them.

zoomkat

Below is another way to convert ascii to integer using strings. Capture the sent string, parse out the desired data, and convert the ascii number into an intger for use with servos.

Code: [Select]

// zoomkat 11-22-10 serial servo (2) test
// for writeMicroseconds, use a value like 1500
// for IDE 0019 and later
// Powering a servo from the arduino usually DOES NOT WORK.
// two servo setup with two servo commands
// send eight character string like 15001500 or 14501550

#include <Servo.h>
String readString, servo1, servo2;
Servo myservo1;  // create servo object to control a servo
Servo myservo2;

void setup() {
  Serial.begin(9600);
  myservo1.attach(6);  //the pin for the servo control
  myservo2.attach(7);
  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); //see what was received
     
      // expect a string like 07002100 containing the two servo positions     
      servo1 = readString.substring(0, 4); //get the first four characters
      servo2 = readString.substring(4, 8); //get the next four characters
     
      Serial.println(servo1);  //print to serial monitor to see results
      Serial.println(servo2);
     
      int n1; //declare as number 
      int n2;
     
      char carray1[6]; //magic needed to convert string to a number
      servo1.toCharArray(carray1, sizeof(carray1));
      n1 = atoi(carray1);
     
      char carray2[6];
      servo2.toCharArray(carray2, sizeof(carray2));
      n2 = atoi(carray2);
     
      myservo1.writeMicroseconds(n1); //set servo position
      myservo2.writeMicroseconds(n2);
    readString="";
  }
}

Google forum search: Use Google Search box in upper right side of this page.
Why I like my 2005 Rio Yellow Honda S2000  https://www.youtube.com/watch?v=pWjMvrkUqX0

Go Up