help with String manipulation

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-.

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.

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.

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.

You mean something like this?

char *s="123Hello";
int num=atoi(s);   // c-string to integer
char s2[8];
itoa( num, s2, 10);  // integer to c-string base 10

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.

/* 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 "|"

#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

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.

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

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

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

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.

//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
    }
  }
}

AWOL:

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

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

#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

Input string should arrive in the format of 255O255|

Why? 255,O,255| would be a lot simpler to parse.

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.

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.

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

There is the below.

Hi Zoomkat,
i saw the information you pointed out,
unfortunately i couldnt find an example which would simply strip characters from a string between linits.
could you provide me with a direct link to the example
cheers
Jason

JT007:
Hi Zoomkat,
i saw the information you pointed out,
unfortunately i couldnt find an example which would simply strip characters from a string between linits.
could you provide me with a direct link to the example
cheers
Jason

The below shows how to find the position of a delimiting character in a string, then using an offset, find the location of the next delimiting character in the string. Once you know the positions of the two characters, you can use those locations to capture the substring between the two like in the bottom example.

Yes i understand what you are saying but unfortunately i dont have delimiting characters
the format is 255O255|the part of the code which is handling the parsing does not see the end character "|" which has already been stripped.
i need to be able to use the character position instead.
the examples dont offer any suggestion on how to do this.

as mentioned above i could in VB.

I am currently going back over the code and seeing if i can adopt Pauls suggestion.

thanks for the help
J.

with
cmd="255O255|";

preconditions
strlen(cmd)==8
&& cmd[3]=='O'
&& cmd[7]=='|' )

int slave=atoi( cmd); // gives you the address
int value=atoi( cmd+4); // gives you the value

Doesn't that "get you there from here"?

Also are you sure about the 3 digit thing?
i.e. you will be getting "023O004|" not "23O4|"

John

JT007:
Yes i understand what you are saying but unfortunately i dont have delimiting characters
the format is 255O255|the part of the code which is handling the parsing does not see the end character "|" which has already been stripped.
i need to be able to use the character position instead.
the examples dont offer any suggestion on how to do this.

Using a 4 char array string you can put the first 3 serial chars in array[0], [1], [2], and make [3] = 0.
Then you have a C string suitable for atoi().

i need to be able to use the character position instead.
the examples dont offer any suggestion on how to do this.

Make a variable to hold the final number.
1 Multiple final number variable by 10.
2 Read the 1st serial char, check that it is >= '0' and <= '9' and if so,
subtract '0' and add to the final number variable.
Repeat 1 and 2 two more times.
If no errors then you have read 3 digits and your converted value is in the final number variable.

Otherwise rely on black box function until you learn how simple the basis for the above is.

C++ container classes on an UNO is like Formula 1 cars on a half acre dirt track.
Possible but ....