Extracting nummeric data from the serial port and convert them to Integer

Hi,

On 22 March 2014 user "zoomkat"posted an modified example how to extract data from the serial port. The link is http://forum.arduino.cc/index.php?topic=227806.msg1646430#msg1646430 In that example one has to input AZ123.4 EL134.5 AZ90.3 EL2.3 Then in the Serial monitor you see the result as follows: serial delimit test 1.0 AZ123.4 AZ is: 123.4 123

EL134.5 EL is: 134.5 134

AZ90.3 AZ is: 90.3 90

EL2.3 EL is: 2.3 2

So far so good. Sending the same data once more gives a faulty integer value for AZ -> 0 in stead of 123. And a faulty numeric part of the string(Z123.4) See below.

AZ123.4 AZ is: Z123.4 0

EL134.5 EL is: 134.5 134

AZ90.3 AZ is: 90.3 90

EL2.3 EL is: 2.3 2

It can also happen when I resend in stead of AZ123.4 AZ 128.9 that I get this: EL2.3 AZ123.4 AZ is: 2.3 AZ123.4 2

EL is: 2.3 AZ123.4 2

EL134.5 EL is: 134.5 134

AZ90.3 AZ is: 90.3 90

When I restart the serial monitor and sending these data then the Integer for AZ is OK (however only after the first time).

I can not find out what here is going wrong. I have copied the code by using the select button on the webpage of your example.

I am using an Arduino UNO rev.R3, correct baudrate at both ends.

I can use this example because I have also to make 4 variables from the same type of input as used here, which is sent from a visual basic (2107) app which I am programming.

I hope you may help.

I suggest that you search for Robin's "Serial Input Basics" thread on this forum. zoomkat was strongly promoting the use if the String class (capital S), something that is not advisable on Arduinos because it can cause memory fragmentation that result in unexpected behaviour at run time.

// edit got the link: Serial Input Basics - updated

Hi Sterretje,

Thanks for the link. Usefull to read that tutorial of Robin.
I have chosen for his example 2 - Receiving several characters from the Serial Monitor.
The characters are stored in an array I read.
That example works fine.
If I type for example 123 456 789 543 in the Serial Monitor, then I read in the Monitor:

This just in … 123 456 789 543
Now I wish to extract 123 and 456 and 789 and 543 and put them to separate integers, so I get for example:

int A,B,C,D

A=123
B=456
C=789
D=543

I appreciate your help very much.

The function you are looking for now is called atoi.

Cor2: Now I wish to extract 123 and 456 and 789 and 543 and put them to separate integers, so I get for example:

int A,B,C,D

A=123 B=456 C=789 D=543

I appreciate your help very much.

Example 5 shows how you can parse using strtok(). And how to convert the text to a number (e.g. using the above mentioned atoi() function and the atof() function) and store it in a variable.

If you have 4 integers, consider use of an array to store them; your A will be stored in the first element, your B in the second element etc. You can use a while-loop to parse as shown here; instead of printf(), store the parsed and converted data in the array.

With your information I have created a sketch (partly using examples from elsewhere) which shows 4 numbers of a fixed string (instring).
They are extracted and put into array setting and printed in the serial monitor as a test.
Then the 4 array element values are copied into 4 variables A, B, C and D for later use. For this moment also printed.
If I type 4 numbers separated by a “,” and ended with a NewLine then they will printed as Received string → followed with which was typed in.
The numbers may be >32767, so Long is used where needed.
The result on the screen is what was to be expected, because I wrote: char instring = “100000,200000,300000,400000”;
Then I commented this line and activated the next line: char instring = receivedChars; because the received string was found in receivedChars in function showNewData. However setting remains 0 and I hoped to see the 4 numbers which I had typed in.

/*  This sketch is trying to destilate four values out of a string (fixed string for now), 
 *  later used for controlling a device which needs 4 variables.
 *  The values may be more than 32767, so a long is declared where required.
 *  The string will later be sent to the this sketch by a Visual Basic app (VS2017). 
 *  Then I determine the delimiter (for the moment in this sketch a ',').
 *  
 *  When I start the Serial Monitor, it displays the following:
 * <Arduino is ready>
 *   0: 100000
 *   1: 200000
 *   2: 300000
 *   3: 400000
 *   as a result of printing variable "settings".
 *   ...and is repeating (every 2 sec as a test)...
 *   
 *  If a random string of (4) comma separated numbers ended with a newline which is 
 *  typed in the Serial Monitor, these will be shown as Received string -> etc. 
 *  But these numbers are not yet processed (that is the question for now).
 *  Then setting[1],[2],[3,] and [4] are copied to A, B, C and D and Serial printed (test).
 *  In a later stage the four integers values in the array settings[0...3] will be copied 
 *  to the (long)integer variables A, B, C and D.
 * 
 *  
 */
//------- Declaration recvWithEndMarker() -------------------------------
    const byte numChars = 32;
    char receivedChars[numChars]; // an array to store the received data
    boolean newData = false;
 
//------- Declaration Setting Array (= final wanted 4 values) ----------- 
    
    char instring[numChars];
    long setting[] = {0, 0, 0, 0};
    long A;
    long B;
    long C;
    long D;
    
void setup() { //-------------------------------------------------------

    Serial.begin(9600);  
    Serial.println("<Arduino is ready>");
}

void loop() { //--------------------------------------------------------

    recvWithEndMarker();
    showNewData();
    StringtoInt();
   
    A=setting[0];
    B=setting[1];
    C=setting[2];
    D=setting[3];
    Serial.print("A= ");
    Serial.println(A);
    Serial.print("B= ");
    Serial.println(B);
    Serial.print("C= ");
    Serial.println(C);
    Serial.print("D= ");
    Serial.println(D);
}

void recvWithEndMarker() { //--- receiving a string --------------------

    static byte ndx = 0;
    char endMarker = '\n';
    char rc;

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

       if  (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;               
            }
       }
       else {
                receivedChars[ndx] = '\0'; // terminate the string
                ndx = 0;
                newData = true;           
       }
    }
}

void showNewData() { //--- show received string ------------------------

       if (newData == true) {
           Serial.print("Received string ->  ");
           Serial.println(receivedChars);
           newData = false;       
    }
}

void StringtoInt() { //-------------------------------------------------

//  char instring[] = "100000,200000,300000,400000";
    char instring = receivedChars;

    char delimiters[] = ",";
    char* valPosition;
  
    valPosition = strtok(instring, delimiters);

  for(int i = 0; i < 4; i++){
     setting[i] = atol(valPosition);
     Serial.print(i);Serial.print(": ");Serial.println(setting[i]);
     valPosition = strtok(NULL, delimiters);
  }
  delay(2000);
}

After a lot of other trials in this sketch with a bunch of compiler errors I have no idea how to resolve the next step.
Your suggestion is appreciated very much.

Kees

char instring = receivedChars;

You can't copy an array in one line like that. You have to use memcpy or a for loop to copy the contents of an array.

Or instring could be a char* and could be set to point to receivedChars.

Cor2:
Thanks for the link. Usefull to read that tutorial of Robin.
I have chosen for his example 2 - Receiving several characters from the Serial Monitor.
The characters are stored in an array I read.
That example works fine.

Have a look at the parse example. In the Tutorial it assumes the use of the recvWithStartEndMarkers() function but the parsing works just the same if you receive the data with recvWithEndMarker()

The parse example also includes copying the array.

…R

Delta_G: char instring = receivedChars;

You can't copy an array in one line like that. You have to use memcpy or a for loop to copy the contents of an array.

Or instring could be a char* and could be set to point to receivedChars.

Thank you. Incremented your karma. Adding the * helped in char* instring = receivedChars; Now instring[0], [1], [2] and [3] showed these numbers after typing the 4 number string. During the next loop the numbers are overwritten with a zero except the first one instring[0]. So the data in instring[1], [2] and [3] is lost. I have to sort that out.

Cor2: Adding the * helped in char* instring = receivedChars; Now instring[0], [1], [2] and [3] showed these numbers after typing the 4 number string. During the next loop the numbers are overwritten with a zero except the first one instring[0]. So the data in instring[1], [2] and [3] is lost. I have to sort that out.

Show your new code; I suspect that you have a misunderstanding.

char* instring = receivedChars;

does not make a copy.

It creates a pointer to the memory location where receivedChars lives. Working on that pointer will still destroy the original receivedChars in the same way as if you were working on the receivedChars. Robin's code makes an actual copy.

You only need to make a copy of receivedChars if you want to keep it in its original state. Often, after processing, you don't care so you can work directly on receivedChars.

To explain what strtok() does, below shows receivedChars (rc) in memory; it contains "123,45,678". I've used a thumbsuck valye for the address where recievedChars is stored.

addr | rc   |  after strtok
-----+------+---------------    
 345 | '1'  |  '1'
 346 | '2'  |  '2'
 347 | '3'  |  '3'
 348 | ','  |  '\0'
 349 | '4'  |  '4'
 350 | '5'  |  '5'
 351 | ','  |  '\0'
 352 | '6'  |  '6'
 353 | '7'  |  '7'
 354 | '8'  |  '8'
 355 | '\0' |  '\0'

After a first call to strtok(), the value at address 348 is changed to the null terminator; after the second call, the value at address 351 is changed.

If you print receivedChars after the first parse, it will only print "123" as that's now the nul terminated string.