Lake District, UK
Offline
Jr. Member
Karma: 0
Posts: 62
Electronics needs smoke to work, if it escapes its broken
|
 |
« on: January 27, 2013, 08:25:27 am » |
Need some help understanding this line of 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
Faraday Member
Karma: 31
Posts: 2931
I only know some basic electricity....
|
 |
« Reply #1 on: January 27, 2013, 08:43:07 am » |
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 at Learning in the Main Site and at the Playground
|
|
|
|
Lake District, UK
Offline
Jr. Member
Karma: 0
Posts: 62
Electronics needs smoke to work, if it escapes its broken
|
 |
« Reply #2 on: January 27, 2013, 09:51:01 am » |
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
Edison Member
Karma: 48
Posts: 1414
May all of your blinks be without delay
|
 |
« Reply #3 on: January 27, 2013, 10:23:59 am » |
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/StringSubstringIf 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.
|
|
|
|
|
Logged
|
|
|
|
|
Temple, Texas
Offline
Sr. Member
Karma: 14
Posts: 354
|
 |
« Reply #4 on: January 27, 2013, 10:29:38 am » |
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
|
|
|
|
|
Logged
|
|
|
|
|
Atlanta, USA
Offline
Jr. Member
Karma: 4
Posts: 64
AKA: Ray Burne
|
 |
« Reply #5 on: January 27, 2013, 10:35:39 am » |
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:
|
|
|
|
« Last Edit: January 27, 2013, 03:48:36 pm by mrburnette »
|
Logged
|
|
|
|
|
Pittsburgh, PA, USA
Offline
Faraday Member
Karma: 31
Posts: 2931
I only know some basic electricity....
|
 |
« Reply #6 on: January 27, 2013, 10:36:02 am » |
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 at Learning in the Main Site and at the Playground
|
|
|
|
Global Moderator
UK
Offline
Brattain Member
Karma: 138
Posts: 19067
I don't think you connected the grounds, Dave.
|
 |
« Reply #7 on: January 27, 2013, 10:37:54 am » |
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.
|
|
|
|
East Anglia (UK)
Offline
Edison Member
Karma: 48
Posts: 1414
May all of your blinks be without delay
|
 |
« Reply #8 on: January 27, 2013, 10:48:31 am » |
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
|
|
|
|
|
0
Offline
Tesla Member
Karma: 50
Posts: 6549
Arduino rocks
|
 |
« Reply #9 on: January 27, 2013, 10:49:45 am » |
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 } } }
|
|
|
|
|
Logged
|
|
|
|
|
Pittsburgh, PA, USA
Offline
Faraday Member
Karma: 31
Posts: 2931
I only know some basic electricity....
|
 |
« Reply #10 on: January 27, 2013, 10:51:52 am » |
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 at Learning in the Main Site and at the Playground
|
|
|
|
Lake District, UK
Offline
Jr. Member
Karma: 0
Posts: 62
Electronics needs smoke to work, if it escapes its broken
|
 |
« Reply #11 on: January 27, 2013, 11:58:04 am » |
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
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Offline
Brattain Member
Karma: 314
Posts: 35518
Seattle, WA USA
|
 |
« Reply #12 on: January 27, 2013, 12:02:49 pm » |
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
Jr. Member
Karma: 0
Posts: 62
Electronics needs smoke to work, if it escapes its broken
|
 |
« Reply #13 on: January 27, 2013, 12:22:03 pm » |
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
Brattain Member
Karma: 314
Posts: 35518
Seattle, WA USA
|
 |
« Reply #14 on: January 27, 2013, 12:24:43 pm » |
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
|
|
|
|
|
|