Read in string from serial and parse in C

BLUF: Arduino is sending in values of 9 potentiometers over serial to prop. I need to parse them and do math against the values. All in C, no SPIN.

Really have no idea where's left to go on this one.. I have an arduino program printing as ASCII potentiometer values from its serial port to the prop. The values vary from 0 to 299 and the output format is: 0,5,89,4,54,202,3,25,180
On the prop side, I've gotten it to see that values are coming in but I can't parse them, or read them as whole numbers (89 comes in as two separate characters). This is all the more infuriating since I've gotten it to work with C# code in Unity, but the prop is a non starter.
I really don't have the time to learn SPIN to make this all work, and really feel it can be done in C, I'm just at the end of my rope.. Any suggestions?

ARDUINO CODE
void loop()
{

//Read analog inputs on IO1
pots[0] = map(muxShield.analogReadMS(1,0), 0, 1023, 0, 300); //IO1, pin 0
pots[1] = map(muxShield.analogReadMS(1,1), 0, 1023, 0, 300); //IO1, pin 1
pots[2] = map(muxShield.analogReadMS(1,2), 0, 1023, 0, 300); //IO1, pin 2
pots[3] = map(muxShield.analogReadMS(1,3), 0, 1023, 0, 300); //IO1, pin 3
pots[4] = map(muxShield.analogReadMS(1,4), 0, 1023, 0, 300); //IO1, pin 4
pots[5] = map(muxShield.analogReadMS(1,5), 0, 1023, 0, 300); //IO1, pin 5
pots[6] = map(muxShield.analogReadMS(1,6), 0, 1023, 0, 300); //IO1, pin 6
pots[7] = map(muxShield.analogReadMS(1,7), 0, 1023, 0, 300); //IO1, pin 7
pots[8] = map(muxShield.analogReadMS(1,8), 0, 1023, 0, 300); //IO1, pin 8

//Print the results
Serial.print(pots[0]); Serial.print(",");
Serial.print(pots[1]); Serial.print(",");
Serial.print(pots[2]); Serial.print(",");
Serial.print(pots[3]); Serial.print(",");
Serial.print(pots[4]); Serial.print(",");
Serial.print(pots[5]); Serial.print(",");
Serial.print(pots[6]); Serial.print(",");
Serial.print(pots[7]); Serial.print(",");
Serial.println(pots[8]);

// Give Unity time to read lines
delay(27);
}

PROP CODE
#include "simpletools.h" // Include simple tools
#include "fdserial.h"

fdserial *unity;
fdserial *disp;

int main()
{
// (RX, TX, 0, Baud)
unity = fdserial_open(1, 0, 0, 57600);
disp = fdserial_open(31,30,0,57600);
char seps[] = (" ,\t\n");
char store[8];

writeChar(disp, CLS);
dprint(disp, "Click this terminal, \n");

dprint(disp, "and type on keyboard...\n\n");

char c;

while(1)
{
c = fdserial_rxChar(unity);
//Cycle through string
for(int i = 0; i <8; i++){
//Test for start of input--- Result: Confirmed good
if(c = "!"){
i = 0;
//dprint(disp,"Start.\n");
}
store = c;
//Test for end of input--- Result: Confirmed good
if(c = "#"){
// Reached the end, so print the results of storage
for(int j = 0; j < 8; j++){
dprint(disp,store[j]);
}
dprint(disp," END\n");
i = 8;
break;
}
}
}
}

Comments

  • 13 Comments sorted by Date Added Votes
  • cosmosfood wrote: »
    BLUF: Arduino is sending in values of 9 potentiometers over serial to prop. I need to parse them and do math against the values. All in C, no SPIN.

    Really have no idea where's left to go on this one.. I have an arduino program printing as ASCII potentiometer values from its serial port to the prop. The values vary from 0 to 299 and the output format is: 0,5,89,4,54,202,3,25,180
    On the prop side, I've gotten it to see that values are coming in but I can't parse them, or read them as whole numbers (89 comes in as two separate characters). This is all the more infuriating since I've gotten it to work with C# code in Unity, but the prop is a non starter.
    I really don't have the time to learn SPIN to make this all work, and really feel it can be done in C, I'm just at the end of my rope.. Any suggestions?

    ARDUINO CODE
    void loop()
    {

    //Read analog inputs on IO1
    pots[0] = map(muxShield.analogReadMS(1,0), 0, 1023, 0, 300); //IO1, pin 0
    pots[1] = map(muxShield.analogReadMS(1,1), 0, 1023, 0, 300); //IO1, pin 1
    pots[2] = map(muxShield.analogReadMS(1,2), 0, 1023, 0, 300); //IO1, pin 2
    pots[3] = map(muxShield.analogReadMS(1,3), 0, 1023, 0, 300); //IO1, pin 3
    pots[4] = map(muxShield.analogReadMS(1,4), 0, 1023, 0, 300); //IO1, pin 4
    pots[5] = map(muxShield.analogReadMS(1,5), 0, 1023, 0, 300); //IO1, pin 5
    pots[6] = map(muxShield.analogReadMS(1,6), 0, 1023, 0, 300); //IO1, pin 6
    pots[7] = map(muxShield.analogReadMS(1,7), 0, 1023, 0, 300); //IO1, pin 7
    pots[8] = map(muxShield.analogReadMS(1,8), 0, 1023, 0, 300); //IO1, pin 8

    //Print the results
    Serial.print(pots[0]); Serial.print(",");
    Serial.print(pots[1]); Serial.print(",");
    Serial.print(pots[2]); Serial.print(",");
    Serial.print(pots[3]); Serial.print(",");
    Serial.print(pots[4]); Serial.print(",");
    Serial.print(pots[5]); Serial.print(",");
    Serial.print(pots[6]); Serial.print(",");
    Serial.print(pots[7]); Serial.print(",");
    Serial.println(pots[8]);

    // Give Unity time to read lines
    delay(27);
    }

    PROP CODE
    #include "simpletools.h" // Include simple tools
    #include "fdserial.h"

    fdserial *unity;
    fdserial *disp;

    int main()
    {
    // (RX, TX, 0, Baud)
    unity = fdserial_open(1, 0, 0, 57600);
    disp = fdserial_open(31,30,0,57600);
    char seps[] = (" ,\t\n");
    char store[8];

    writeChar(disp, CLS);
    dprint(disp, "Click this terminal, \n");

    dprint(disp, "and type on keyboard...\n\n");

    char c;

    while(1)
    {
    c = fdserial_rxChar(unity);
    //Cycle through string
    for(int i = 0; i <8; i++){
    //Test for start of input--- Result: Confirmed good
    if(c = "!"){
    i = 0;
    //dprint(disp,"Start.\n");
    }
    store = c;
    //Test for end of input--- Result: Confirmed good
    if(c = "#"){
    // Reached the end, so print the results of storage
    for(int j = 0; j < 8; j++){
    dprint(disp,store[j]);
    }
    dprint(disp," END\n");
    i = 8;
    break;
    }
    }
    }
    }

    It can be done on the prop in C. The ascii characters the arduino is sending need to be converted to integers or floats, and the result used for the math.
    In science there is no authority. There is only experiment.
    Life is unpredictable. Eat dessert first.
  • dgatelydgately Posts: 904
    edited December 3 Vote Up1Vote Down

    You can try this parser (quickly put together, but tested as working)... i.e. may not be the best 'C' code but provides a working example. You may need to test with real data input...

    Note: The C tab on this website's input field provides proper 'code' formatting.
    #include "simpletools.h" // Include simple tools
    #include "fdserial.h"
    
    fdserial *unity;
    fdserial *disp;
    
    void parseValue(char*);
    
    int main()
    {
      char seps[] = (" ,\t\n");        // ASCII chars are unsigned 8 bit values
      char store[8];                   // store 7 ASCII chars and a 'C' end-of-string zero
      char c;
    
      // (RX, TX, 0, Baud)
      unity = fdserial_open(1, 0, 0, 57600);
      disp = fdserial_open(31,30,0,57600);
          
      writeChar(disp, CLS);
      dprint(disp,"Click this terminal, \n");
      dprint(disp,"and type on keyboard...\n\n");
    
      // TEST code (uncomment to execute with an example string)
      // TEST - int i = 0;
      // strcpy(store, "!12,13,14,15#");
      // dprint(disp,store);
      // dprint(disp,"\n");
    
      int index = 0;                            // store character index
      
      while(1)
      {
        // Input example given: !12,13,14,15#
        // TEST - c = store[i++];
    
        if ((c = fdserial_rxChar(unity)) != -1)
        {
          if (c == '!')                         // start of input
          {
            // read the string into store until a '#' is received
            // TEST - while ((c = store[i++]) != '#')
            while ((c = fdserial_rxChar(unity)) != '#')
            {
              if (c == ',' || c == '#') {
                store[index++] = 0;         // replace commas and pound sign with 'C' end-of-string!
                parseValue(store);          // parse and print the integer values from the string
                index = 0;                  // reset index for next integer
              }
              else
              {
                store[index++] = c;         // add the next char to the storage buffer
              }          
            }        
          }      
        }    
      }    
    }
    
    void parseValue(char* inputStr)
    {
      int val;
      
       val = atoi(inputStr);            // convert a 'C' string to an integer
       dprint(disp,"String value = %s, Int value = %d\n", inputStr, val);
      
    }    
    

    dgately
    Livermore, CA (50 miles SE of San Francisco)
  • tonyp12tonyp12 Posts: 1,870
    edited December 3 Vote Up1Vote Down
    Does Prop C have string library with token?

    char *strtok(char *str, const char *delim)
    Parameters
    str − The contents of this string are modified and broken into smaller strings (tokens).
    delim − This is the C string containing the delimiters. These may vary from one call to another.
    https://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm

    You would still need to turn it in to integer.
    a while loop maybe x=x*10 +ascII byte -48;
  • I did not use strtok because of stackoverflow.com warnings that:

    The strtok function modifies its first argument
    The strtok function cannot be used on constant strings
    The identity of the delimiting character is lost
    The strtok() function uses a static buffer while parsing, so it's not thread safe

    The solution I chose seemed to give a more "insteructable" path for a new C user...

    But, if you know what you are doing with strtok, and test for possible problems, by all means, use it!

    dgately
    Livermore, CA (50 miles SE of San Francisco)
  • tonyp12tonyp12 Posts: 1,870
    edited December 3 Vote Up1Vote Down
    strtok does trash the string that need to be in ram, It is embedded C after all and not run-time allocation C++
    It will change the token you looking for to a zero, so the returned pointer will now point to a smaller chunk of text that is zero terminated.

    This could work after strtok inserted the zero:

    pnt = strtok(NULL, ",");
    while (*pnt) x=x*10+*pnt++ -48;
  • cosmosfoodcosmosfood Posts: 2
    edited December 5 Vote Up0Vote Down
    Thank you for your inputs! Thank you especially for your code @dgately. I was certain my 11th hour change from Arduino to Prop was going to sink me, but it looks like I'll make it through. Thank you!
  • Isn't the 'C' end of string either NULL or '\0'?
    Thus,
    store[index++] = 0;         // replace commas and pound sign with 'C' end-of-string!
    

    should be:
    store[index++] = NULL;         // replace commas and pound sign with 'C' end-of-string!
    

    or:
    store[index++] = '\0';         // replace commas and pound sign with 'C' end-of-string!
    

    Although, I do agree with Tony regarding the use of strtok if you are looking to add the 'string.h' lib to the code.
    However, I would do something like:

    // Declare a delimiter
    const char s[4] = ",";

    Then you could read in the first token as
    char* token;
    token = strtok(str,s);

    if token is not 0, then read in the rest of the string as:
    token = strtok(0, s);

    You would only need to compare for the "#" to get the end of the input:
    int ret;
    ret = strcmp(token, "#");
  • Looking at a Bluetooth program I have that parses the serial input from the Bluetooth device, I tend to like adding a flush after reading or sending data using the fdserial tool.

    Ex:
    fdserial_rxFlush(ble);

    Also, I like to check whether or not there is anything in the buffer before reading in the data:

    Ex:
    fdserial_rxPeek(ble)

    This will not remove a character from the buffer like fdserial_rxChar does.
  • JonM wrote: »
    Isn't the 'C' end of string either NULL or '\0'?
    Well, the character '\0' is basically an 8-bit zero (0x00), as is NULL... I tend to think of the 'C' end-of-string in a boolean manner. i.e. a non-zero or a zero. (what can I say, it works!)
    // some loop:
      for (int i = 1; i < someBigInteger; i++) {
        if ( ! store[n] ) {        // looking for the end of the string
          return;
        }
        else
          // do something with the character at i
      }
    

    dgately
    Livermore, CA (50 miles SE of San Francisco)
  • I like to use: while with pointers, example

    while (*s++ = *t++); // string copy until zero
  • tonyp12 wrote: »
    I like to use: while with pointers, example

    while (*s++ = *t++); // string copy until zero

    me too!

    Livermore, CA (50 miles SE of San Francisco)
  • dgately wrote: »
    Well, the character '\0' is basically an 8-bit zero (0x00), as is NULL... I tend to think of the 'C' end-of-string in a boolean manner. i.e. a non-zero or a zero. (what can I say, it works!)
    // some loop:
      for (int i = 1; i < someBigInteger; i++) {
        if ( ! store[n] ) {        // looking for the end of the string
          return;
        }
        else
          // do something with the character at i
      }
    

    dgately

    Understood. It's just a matter of style. However, I have to correct myself since 'NULL' typically in C is defined as "((void *)0)" or 0 if '__cplusplus' is defined and is used with pointers. 'NUL' with a single 'L' would be the equivalent to '\0'.

    In your example you are looking for the end of a string not the end of the array.
    If the array string had something like a '\0' in the middle of the string, the loop would stop before the end of the array. Ex: "This is \0 an array" will produce

    "This is ".

    To be on the safe side you should get the sizeof of the array and used that to parse through the array.
    EX:
          int ret = 0;
          ret = sizeof(store) / sizeof(store[0]);
    
    Although this would give the size of the entire array.

    Also, I believe you meant :
    if ( ! store[i] )
    



  • JonM wrote: »
    In your example you are looking for the end of a string not the end of the array.
    Yes, in the case that I'm just looking for a single integer, I'm assuming that there are no errant zero bytes. And, since the code above was controlling when a zero byte gets inserted, I was fairly certain that was the case. If this was a more generic function, I would definitely use more reliable means. It is just an example to get the OP's question answered and not an example of the best coding practices :-)
    JonM wrote: »
    Also, I believe you meant :
    if ( ! store[i] )
    
    Yes, thanks for noticing that!

    dgately


    Livermore, CA (50 miles SE of San Francisco)
Sign In or Register to comment.