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
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.
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);
}
}
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.
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
}
}
}
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
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.
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.
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 ....
GoForSmoke:
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().