Shop OBEX P1 Docs P2 Docs Learn Events
Read in string from serial and parse in C — Parallax Forums

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

  • kwinnkwinn Posts: 8,697
    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.
  • dgatelydgately Posts: 1,621
    edited 2017-12-03 19:17

    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
  • tonyp12tonyp12 Posts: 1,950
    edited 2017-12-03 20:04
    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
  • tonyp12tonyp12 Posts: 1,950
    edited 2017-12-03 23:18
    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 2017-12-05 01:51
    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
  • 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!

  • 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


  • 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 from the Unity tutorial, 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?

    If it works with C# then why not just use that to run your code in the Unity Engine?
    Apologies if this question is stupid, I am just starting out with Unity and am just a little curious to know about the game engine.
  • JonnyMacJonnyMac Posts: 8,912
    edited 2018-05-17 05:09
    I'm confused. Does the steam come in like this:

    0,5,89,4,54,202,3,25,180

    ... or like this:

    !0,5,89,4,54,202,3,25,180#

    The latter, of course, is easier to deal with.

    Since I find I can write a Spin program faster than the C compiler can compile anything I bash together in C -- and I've been working on a parser today so that the Propeller can connect to a show-control program called VenueMagic -- I'm submitting a Spin example, anyway. Being a C programmer, you should be able to convert this pretty easily as it doesn't rely on any special libraries or conversion functions. I used RealTerm to test because it allows me to build a string and then send it.

  • JonnyMacJonnyMac Posts: 8,912
    edited 2018-05-17 07:48
    Well, I tried to do the conversion to C on my own. This code compiles but doesn't work (like the Spin version did). Perhaps you can fix it.

    It took two hours and a double-shot of Jameson's whiskey, but I got it to work. I'm sure many of the C gurus will point out my ignorant flaws.
    // =======================================
    //
    //   File....... jm_unity_parser.c
    //   Purpose....
    //   Author..... Jon "JonnyMac" McPhalen
    //   E-mail..... jon@jonmcphalen.com
    //   Started....
    //   Updated.... 16 MAY 2018
    //
    // =======================================
    
    
    #define RX1 31
    #define TX1 30
    
    
    #include "simpletools.h" 
    #include "fdserial.h"
    
    
    fdserial *unity;
    
    char stream[40];
    int  pot[9];
    
    
    
    void setup()
    {
      simpleterm_close();
      unity = fdserial_open(RX1, TX1, 0, 57600);   
    }
    
    
    void capture_stream()
    {
      for (uint8_t idx = 0; idx < 40; idx++) {
        stream[idx] = NULL;
      }  
      
      uint8_t idx = 0;
      
      while (idx < 39) {
        char c = fdserial_rxTime(unity, 10);
        if ((c < 0) || (c == "#")) {
          return;
        }    
        else {
          stream[idx++] = c;
        }    
      }          
    }
    
    
    void parse_pots()
    {
      for (uint8_t idx = 0; idx < 9; idx++) {
        pot[idx] = 0;
      } 
      
      uint8_t sidx = 0;
      uint8_t pidx = 0;
      
      while (sidx < 40) {
        char c = stream[sidx++];
        if ((c >= '0') && (c <= '9')) {
          pot[pidx] *= 10;
          pot[pidx] += c - 48;
        }
        else {
          if (++pidx == 9) { 
            return;
          }        
        }               
      }    
    }
    
    
    void show_pots()
    {
      dprint(unity, "\n");
      
      for (uint8_t idx = 0; idx < 9; idx++) {
        dprint(unity, "pot[%d] = %d\n", idx, pot[idx]);
      }     
    }
    
    
    void main()
    {
      setup();
      
      dprint(unity, "Unity Parser Test\n");
     
      while(1)
      {
        char c = fdserial_rxChar(unity);
    
        if (c == '!') {
          dprint(unity, "-- stream detected\n");
          capture_stream();
          parse_pots();
          show_pots();  
        }
      }  
    }
    
Sign In or Register to comment.