Pages: [1] 2   Go Down
Author Topic: help with String manipulation  (Read 1054 times)
0 Members and 1 Guest are viewing this topic.
Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Need some help understanding this line of code
Code:
s[0] = cstring[0]; s[1] = '\0'; SlaveID = (unsigned long)atoi(s);

Its purpose is to parse a string input and store the value in SlaveID (in this case the first character from a serial input string)

I want to be able to use 3 characters ie. 255.
the code above only shows the first character

how can i manipulate it so i cand display the first 3 characters of a string
in essence how does the line above work
Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 58
Posts: 4016
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset



s[1] = '\0' ?

same as s[1] = 0; which makes a null terminator right after s[0]



Some time try debugging by printing different variables out, then you'll know what you got -right then-.
Logged

Examples can be found in your IDE.

Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have worked out that this code simply tests the first character and stores it,

so back to my question
how can i filter a string to give me the first 3 characters so that i can store it,
i want to capture a 3 digit value i. 255.
in VB6 i can use the mid or instring command and get the characters i want
how is this acheived with c and the arduino code.

I have looked at the tutorials and string examples but i have not found one that will filter characters out of a string

please help
J.
Logged

East Anglia (UK)
Offline Offline
Faraday Member
**
Karma: 89
Posts: 3466
May all of your blinks be without delay()
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The first question is do you mean a String or a string ?
If you are using Strings then have a look at this page http://arduino.cc/en/Tutorial/StringSubstring

If not then you can certainly do the following which is an extension of the example in your first post.

Code:
char myText1[] = {
  "Hello"};
char myText2[4];        //space for 3 characters plus null in target

void setup()
{
  Serial.begin(9600);
  for (int ch = 0; ch <= 2;ch++)
  {
    myText2[ch]=myText1[ch];
  }
  myText2[3] = '\0';
  Serial.println(myText1);
  Serial.println(myText2);
}

void loop()
{
}

This being C++ I expect that there is a neater way of doing it but this example makes it very obvious what is going on and, of course, you could pick out any characters from the first string and put them in the second.  Note that the null termination of the sub-string is very important.
Logged

Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Temple, Texas
Offline Offline
Sr. Member
****
Karma: 14
Posts: 354
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You mean something like this?
Code:
char *s="123Hello";
int num=atoi(s);   // c-string to integer
char s2[8];
itoa( num, s2, 10);  // integer to c-string base 10
Logged

Atlanta, USA
Offline Offline
Edison Member
*
Karma: 33
Posts: 1424
AKA: Ray Burne
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

With Arduino 1.0 forward (I'm using 1.0.3), you can use the Streams class to manage input from terminals or PC hosted applications.  A new set of commands have been included to make life easier: Serial.parseFloat() and Serial.parseInt()

The easiest way to understand the new commands is to run a simple program, the example below will run on probably all Arduinos, but I've only tested with UNO and Nano328.  When running the example, use the Arduino Serial Monitor first.  You can put in integers or floating point for the triangle sides "a" and "b".  Then try the stream by inputting a set of variables, comma separated.  Now try a set of variables separated by a space!  IN ALL CASES, you must use a leading zero for any number less than 0: for example, ".5" will be taken as 5.0 but 0.5 will be taken correctly.  IF you only want integers, then replace the .parseFloat() with .parseInt() and change the variables from float to int.

Now use an external terminal program such as ttermpro (Tera Term) and connect to the Arduino.  Because the terminal is a character-by-character transfer, each keystroke is transmitted up until the newline.  So, the behavior is a bit different than that of the Arduino Serial Monitor.

Code:
/* Right Triangle - User Interactive via Serial Terminal
   M. Ray Burnette 20130125
   Arduino Nano 328P target processor
   Binary sketch size: 5,384 bytes (of a 30,720 byte maximum)
*/

#include "math.h"

#define BAUD 9600
#define timeoutPeriod 2147483647    // Long var... about 25 days

float a;
float b;
float h;
// long timeoutPeriod = 2147483647;

void setup()
{
  Serial.begin(BAUD);
  Serial.setTimeout(timeoutPeriod); // default is 1 second

  Serial.println("Let's calculate a hypotenuse, h");
  Serial.println("               /|");
  Serial.println("             /  |");
  Serial.println("           /    |");
  Serial.println("      h  /      |");
  Serial.println("       /       b|");
  Serial.println("     /          |");
  Serial.println("   /            |");
  Serial.println(" /________a_____|");
  Serial.println("");
  Serial.println("");
  
}

void loop()
{
  Serial.println("Enter value for leg 'a', Press ENTER");
  if (Serial.read() == '\n') ;  // Wait here until input buffer has a newline
  {
      //Side 1
    a = Serial.parseFloat();        // new command in 1.0 forward
    Serial.print("a = "); Serial.println(a, 6);
  }

  Serial.println("Enter value for leg 'b', Press ENTER");
  if (Serial.read() == '\n') ;  // Wait here until input buffer has a newline
  {
       //Side 2
    b = Serial.parseFloat();
    Serial.print("b = "); Serial.println(b, 6);
  }

  h = sqrt ( a*a + b*b );

  Serial.print("hypotenuse = ");
  Serial.println(h, 6); Serial.println();

}


Parse 2 integers from stream... any non-numeric delimiter can be used, i.e., "O" or "|"
Code:

#define BAUD 9600
#define timeoutPeriod 2147483647

int a;
int b;

void setup()
{
  Serial.begin(BAUD);
  Serial.setTimeout(timeoutPeriod); // default is 1 second
}

void loop()
{
  Serial.println("Enter first integer: ");
  {
    a = Serial.parseInt();
    Serial.print("a = "); Serial.println(a);
  }

  Serial.println("Enter second integer: ");
  {
    b = Serial.parseInt();
    Serial.print("b = "); Serial.println(b);
  }
}


Output example:


* StreamParseINT.jpg (27.72 KB, 640x430 - viewed 8 times.)
« Last Edit: January 27, 2013, 03:48:36 pm by mrburnette » Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 58
Posts: 4016
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

255 would be '2' '5' '5' '0'

The zero marks the end of the string. If you are parsing a longer string then after 255 there would be a ' ' (space) that you would look for to maybe replace with 0 but if you want to parse the rest then  save that location to a pointer so you can go back and do that.

If you don't know what pointers are then you need to search on C pointers and do some learning. Nothing I could post quickly here will just give it to you right away, learning this properly means doing some homework with sketches.

You can turn '2' into 2 through val = '2' - '0' but check that you have '0' to '9' -- always expect that an error will happen, serial data has no guarantees.

Normally I would pull each character in, check (if error then report it and either stop the program or ask for input again), multiply any old value by 10 (initialized to 0, 0 * 10 = 0) and add the new value until I hit the terminating zero.

But there's at least 3 other ways including using atoi() which can return error as well.
Even parsing, you can use strtok() which is very nice but actually more complicated than processing a string as an array which is funny because so many people use it "because it's easier". Yeah sure, -after- you learn the ins and outs there's less typing so that makes it easier. Or don't learn the ins and outs and spend even more time puzzling out bugs.

You have a few things to learn no matter what. Like how C strings work, what ASCII code is, and pointers. Then have a look through C string commands which once you the first stuff should make easy sense.
After that, plan your code.

Here's another clue; if you don't understand everything about example code then find out before you go modifying it or using pieces of it in something else.

Logged

Examples can be found in your IDE.

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 238
Posts: 24322
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
255 would be '2' '5' '5' '0'
No, "255" would be '2' '5' '5' '\0'
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

East Anglia (UK)
Offline Offline
Faraday Member
**
Karma: 89
Posts: 3466
May all of your blinks be without delay()
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That's neater (less code) but hardly easily understood.  How would you use the method to get sub-strings ?
Also, it only works if the string starts with an integer as far as I can see
Logged

Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

0
Offline Offline
Tesla Member
***
Karma: 114
Posts: 8919
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
so back to my question
how can i filter a string to give me the first 3 characters so that i can store it,
i want to capture a 3 digit value i. 255.

The below is for servo control. It captures a string number sent using a comma as a delimiter to signal the end of transmission. You should be able to do something similar to get your number.

Code:
//zoomkat 3-5-12 simple delimited ',' string parce
//from serial port input (via serial monitor)
//and print result out serial port
// CR/LF could also be a delimiter

String readString;
#include <Servo.h>
Servo myservo;  // create servo object to control a servo

void setup() {
  Serial.begin(9600);

  myservo.writeMicroseconds(1500); //set initial servo position if desired
  myservo.attach(7);  //the pin for the servo control
  Serial.println("servo-delomit-test-22-dual-input"); // so I can keep track of what is loaded
}

void loop() {

  //expect a string like 700, or 1500, or 2000,
  //or like 30, or 90, or 180,

  if (Serial.available())  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == ',') {
      if (readString.length() >1) {
        Serial.println(readString); //prints string to serial port out

        int n = readString.toInt();  //convert readString into a number

        // auto select appropriate value, copied from someone elses code.
        if(n >= 500)
        {
          Serial.print("writing Microseconds: ");
          Serial.println(n);
          myservo.writeMicroseconds(n);
        }
        else
        {  
          Serial.print("writing Angle: ");
          Serial.println(n);
          myservo.write(n);
        }

        //do stuff with the captured readString
        readString=""; //clears variable for new input
      }
    }  
    else {    
      readString += c; //makes the string readString
    }
  }
}

Logged

Consider the daffodil. And while you're doing that, I'll be over here, looking through your stuff.   smiley-cool

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 58
Posts: 4016
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
255 would be '2' '5' '5' '0'
No, "255" would be '2' '5' '5' '\0'

Oh right, I should have wrote '2' '5' '5' 0
Logged

Examples can be found in your IDE.

Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok
I have attached my code, so hopefully you can see what im trying to acheive.

I have been working on getting an RS485 modbus link set up and this code will be used by the Host PC to communicate with the master module

Input string should arrive in the format of 255O255|
the first 3 characters are the Slave device address
Capital Letter "O" designates an output function
the remainder is the digital value to be transmitted over an RS485 Link
followed by a termination character "|"
used to generate a message of ID, Function, value

The code im using was plagerised from a stepper motor program, I liked the way in which it analyses the serial input.
I just want to increase the number of Slave IDs

The code is working

Code:
#define USB_SERIAL
#define ON 1
#define OFF 0


#include <stdio.h> // for function sprintf


// Conditional start
int Startup = 0;
int Run = 0;
int SlaveID = 0;
byte DigitalVal =0;

#if defined(USB_SERIAL)
 #include <ctype.h>     //needed for function 'toupper'
 #include <string.h>    //needed for 'strcmp' and 'strcat'
#endif

int test = ON; // E for Echo                 //test facility which can be enabled/diabled via serial port command
                                  //presently this only echoes commands back to serial port  --  possibly useful during development of PC based driver programs
int hundreds,tens,ones;

 #if defined(USB_SERIAL)
 int analyse_command_string(char cstring[]);
 char *stripallspaces( char *s );

 //set up program information
 char progname[60] = "Winder Control v";  //this info will be echoed to the serial port on startup or reboot of the Arduino
 char progversion[] ="1";
 char commandstring[130] = "";     //serial input string buffer
#endif



void setup()

  #if defined(USB_SERIAL)

  //set up serial port
  Serial.begin(9600); 
#endif 
}


void loop()
{
  int c, i;
   int static n = 0; // NOTE: THIS VARIABLE MUST BE DECLARED AS STATIC
   
// ----------------------------- READ SERIAL INPUT STRING -------------------------------------------------
#if defined(USB_SERIAL)

     //SET FOR CONTROL VIA USB_SERIAL PORT
     //
     if (Serial.available() > 0)  //ie charater(s) in input buffer
       {
       commandstring[n] = Serial.read(); n++;   
       if (commandstring[n-1] == '|') //ie character is '|' (end of command marker)
          {
          commandstring[n]='\0';  //terminate string and reset counter ready for next string
          n = 0;
       
          //if test mode enabled, echo string back to serial port -- could be useful for development of driver programs
          if (test == ON) {Serial.println(commandstring); Serial.print("\n");}
             
          //analyse command string and place info into motor1 or motor2 'struct's
          if(analyse_command_string(commandstring) == -1) n = 0;
       } 
      } //END OF USB_SERIAL PORT SECTION
#endif

}
// -------------------------- SERIAL COMMANDS ------------------------------------------

#if defined(USB_SERIAL)
//CODE BELOW HERE NEEDED ONLY FOR CONTROL VIA USB_SERIAL PORT
int analyse_command_string(char cstring[])
{
 
   
  int x = 0;//

   char s[20];
   
   //Strip all spaces that might be in the command string
   stripallspaces(cstring);
   
    //Look for Start winder enable/disable
   if (toupper(cstring[0]) == 'P')
      {
      s[0] = cstring[1]; s[1] = '\0';
      if(strcmp(s,"1") == 0) Startup = 1;
      if(strcmp(s,"0") == 0) Startup = 0;
     // Serial.println("CF1!");
      return 0;
      }
   
    //Look for winder run/stop command
   if (toupper(cstring[0]) == 'R')
      {
      s[0] = cstring[1]; s[1] = '\0';
      if(strcmp(s,"1") == 0) Run = 1;
      if(strcmp(s,"0") == 0) Run = 0;
      Serial.println("OK!");
      return 0;
      }
     

   //Look for test enable/disable
   
   if (toupper(cstring[0]) == 'E') //E for Echo was T for Test - all test statements have been scripted out
      {
     s[0] = cstring[1]; s[1] = '\0';
     if(strcmp(s,"+") == 0) test = ON;
      if(strcmp(s,"0") == 0) test = OFF;
    return 0;
     }
     
//======================================================================================================================
//Input string should arrive in the format of 255O255|
// the first 3 characters are the Slave device address
// Capital Letter "O" designates an output function
// the remainder is the digital value to be transmitted over an RS485 Link
// followed by a termination character "|"
// used to generate a message of ID, Function, value



 //All other commands must begin with a SlaveID number/character
   //Get SlaveID
   s[0] = cstring[0]; s[1] = '\0'; SlaveID = atoi(s);
   
  //The second character defines the parameter that is to be set
  //** i want this to be the fourth as i want 255 addresses and not just 9.
 
   //Ensure that this is in upper case
   cstring[1] = toupper(cstring[1]);  //this is the function character
       
   //Now search string for the function parameters
 
 if (cstring[1] == 'O') //Send DigitalOutput value
      {
      x = 0; while(cstring[2+x] !='|'){s[x] = cstring[2+x]; x++;}
      s[x] = '\0';
      DigitalVal = (unsigned long)atol(s);  //note use of 'long int' here
      //if (motorID == 1) motor1.interval = interval; else motor2.interval = interval;
     Serial.print(SlaveID);
     Serial.print(" ");
     Serial.print(DigitalVal);
      return 0;
      }
   
 
//=====================================================================================================================
     
   //Should not get to here so print error
   Serial.print("ERROR: Unrecognised command\n"); return -1;
}


char *stripallspaces( char *s )
{
   //Strips ALL spaces from string 's'
   char s1[256];
   int c0 = 0;
   int c1 = 0;
   while ( s[c0] != '\0' )
       {
       while( s[c0] == ' ' ) c0++;
           s1[c1] = s[c0];
           c0++;
           c1++;
       }
   s1[c1] = '\0';
   strcpy( s, s1 );
   return &s[0];
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
#endif    // END OF CODE NEEDED FOR CONTROL VIA USB_SERIAL PORT
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 548
Posts: 46042
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Input string should arrive in the format of 255O255|
Why? 255,O,255| would be a lot simpler to parse.
Logged

Lake District, UK
Offline Offline
Jr. Member
**
Karma: 0
Posts: 69
Electronics needs smoke to work, if it escapes its broken
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Paul,
You are absolutely right.
i was going down the road of manipulating the code i had already.
comma delimited does make a lot of sense.

It looks like im going back to rewriting a hole heap of code.

I was hoping that there would be a straight forward solution like SlaveID=mid(cstring,0,3) - good old VB

ahh well back to the drawing board
cheers
J.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 548
Posts: 46042
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I was hoping that there would be a straight forward solution like SlaveID=mid(cstring,0,3) - good old VB
If the slave ID will always be three characters, then strncpy() will copy three characters to another array. You can then append a NULL and use atoi() to get a numeric value.
Logged

Pages: [1] 2   Go Up
Jump to: