Replacing String objects with C strings

Hi All,

I'm working on a program that controls a stepper board. The board issues an '' character each time it finished a command. So, I have some code that waits for the * and then issues a stepper location. The board also issues feedback on location and such. Right now I use a String object to load in the response form the board and print it out to the serial monitor. After feedback on this forum, I'm trying to replace this String object with a character array so that I don't run into memory problems. However, I'm running into compiling problems and I get a lot of "invalid conversion from 'char' to 'const char'" errors doing what I'm doing. I think there is some char* vs char stuff going on that I don't quite understand.

Here is the code that compiles with my String object. The comparison between the readString object and the "L,16392" isn't working, but that's kinda why I'm trying to change these Objects to C strings...in hopes that I can make that comparison work out. Right now my output looks like it is the same, but the IF statement always says that the strings are not equal.

/*
CNC Bed Sketch 

This program drives a Peter Norburg SD4DEU Stepper Driver board running SD4DNCRouter firmware connecting at 9600 Baud.
 
 
*/

//******************Constants***********
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX

char homePosition[5] = "0XYG";



//******************Variables***********

String readString;
int commandNumber = 0;
int numCommands = 0;


//****************Function Prototypes******
long convertToSteps(int inches);

//***************Setup******************************
void setup() {
  delay(1000);
  
  Serial.begin(9600);    //Begin serial communication at 9600
  mySerial.begin(9600);  //Set software serial baud rate
  
  randomSeed(analogRead(0)); 
  
  // Initialize Stepperboard Values
  mySerial.write("!");      //reset Stepper Board
  delay(50);
  mySerial.write("1500R");  //Set Run Rate
  delay(50);
  mySerial.write("800P");  //Set Ramp Rate
  delay(1000);
}

//*************Main Program Loop************************
void loop() {
 
 
 char* stepCommand[] = {    //Load up CNC Patterns into string array
 "30000XG", 
 "-30000XG"
  }; 
 
 int numCommands = (sizeof(stepCommand)/sizeof(char*)); //Calculate array size

 while(mySerial.available()){
   char c = mySerial.read();   //get one byte from buffer
   readString += c;
   if(c== '*'){
     mySerial.write(stepCommand[commandNumber]);
     Serial.print(stepCommand[commandNumber]);
     Serial.println(" --> IF LOOP");
     commandNumber++;
     if(commandNumber == numCommands){    //if the max number of commands is reached...
        commandNumber = 0;                //reset command number back to zero
     }  
     break;
   } 
 }

 mySerial.write("L");
  
 if(readString.length() > 0){
   Serial.println(readString + " --> ReadString"); 
   String response = "L,16392";
   if(readString == response){
     Serial.println("Write Q Here");
   }
   else{
     Serial.println("Strings not Egual");
   }
   readString = "";  //clears variable for new input
   }
}
//*********************FUNCTIONS***************************
long convertToSteps(int inches){
  long steps = 130435 * inches;  //there are 130435 steps in 1-inch
  return(steps);
}
//**************************************************************************

And here is my attempt at converting the readString into a C character array. I made an array 20 characters long to hold the myserial.read() data. I'm trying to use the strcat() concatenate function to load charachters into the array, but I get the "invalid conversion from 'char' to 'const char*'" errors. I can get around the errors by pointing to the address of c like:

strcat(readString, &c); but the output is all garbled up.

/*
CNC Bed Sketch 

This program drives a Peter Norburg SD4DEU Stepper Driver board running SD4DNCRouter firmware connecting at 9600 Baud.
 
 
*/

//******************Constants***********
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX

char homePosition[5] = "0XYG";



//******************Variables***********

char readString[20];
int commandNumber = 0;
int numCommands = 0;
int i = 0;

//****************Function Prototypes******
long convertToSteps(int inches);

//***************Setup******************************
void setup() {
  delay(1000);
  
  Serial.begin(9600);    //Begin serial communication at 9600
  mySerial.begin(9600);  //Set software serial baud rate
  
  randomSeed(analogRead(0)); 
  
  // Initialize Stepperboard Values
  mySerial.write("!");      //reset Stepper Board
  delay(50);
  mySerial.write("1500R");  //Set Run Rate
  delay(50);
  mySerial.write("800P");  //Set Ramp Rate
  delay(1000);
}

//*************Main Program Loop************************
void loop() {
 
 
 char* stepCommand[] = {    //Load up CNC Patterns into string array
 "30000XG", 
 "-30000XG"
  }; 
 
 int numCommands = (sizeof(stepCommand)/sizeof(char*)); //Calculate array size

 while(mySerial.available()){
   char c = mySerial.read();   //get one byte from buffer
   strcat(readString, c);
   if(c== '*'){
     mySerial.write(stepCommand[commandNumber]);
     Serial.print(stepCommand[commandNumber]);
     Serial.println(" --> IF LOOP");
     commandNumber++;
     if(commandNumber == numCommands){    //if the max number of commands is reached...
        commandNumber = 0;                //reset command number back to zero
     }  
     break;
   } 
 }

 mySerial.write("L");
  
 if(strlen(readString) > 0){
   Serial.print(readString);
   Serial.println(" --> Read String");
   char response[10] = "L,16392";
   if(strncmp(readString, response, 7) == 0){
     Serial.println("Write Q Here");
   }
   else{
     Serial.println("Strings not Equal");
   }
   for(int j = 0; j <= 20; j++){
   readString[j] = ' ';  //clears variable for new input
   }
 }  
}
//*********************FUNCTIONS***************************
long convertToSteps(int inches){
  long steps = 130435 * inches;  //there are 130435 steps in 1-inch
  return(steps);
}
//**************************************************************************

Thanks for any guidance here.

strcat() expects a null terminated string, not a char.

Use indexing instead, that way you know just where to put the '\0'.
Also make your array buffer 21 characters to leave room for a '\0'.

   char c = mySerial.read();   //get one byte from buffer
   readString += c;
   if(c== '*'){

So, the '*' is appended to the String instance, too.

   String response = "L,16392";
   if(readString == response){

Of course they don't match.

In the char array version, the readString array is NOT NULL terminated. Life will be a lot more pleasant when you NULL terminate it.

   for(int j = 0; j <= 20; j++){
   readString[j] = ' ';  //clears variable for new input
   }

Wrong.

readString[0] = '\0';

is all that is needed.

When you concatenate a character onto the end of your array, the strcat() function looks for the NULL, and replaces it with the new character(s), and then puts the NULL in the next empty spot. No NULL means that strcat() is going to write outside the bounds of your array.

You must compare strings with strcmp(). If you use ==, you just compare the pointer vaiues which are almost guaranteed not to be equal.

I'm getting closer. I'm a bit confused on how to make sure readString is null terminated..I just chose the last place in the array and put a '\0' there. I made c a character array and null terminated the second spot. So, now the strcmp() function is working. Looking at the serial monitor output, it makes sense that readString is loading up. I've noticed that if I increase the size of the serialRead array, I get more of my code going through. If I don't make it big enough to accept the initialization/firmware strings from the stepper board, it never gets to the " * " and my motors won't start. If I make the readString array around [40] my code will go load for a while and stop. If I make it [100], the code runs for a long time, and doesn't seem to stop. Any clue as to what going on here?

The string compare function that compares my limit code "L,16392" to readString still doesn't work, even if I put an * on the end. So, there is still something fishy there, too.

/*
CNC Bed Sketch 

This program drives a Peter Norburg SD4DEU Stepper Driver board running SD4DNCRouter firmware connecting at 9600 Baud.
 
 
*/

//******************Constants***********
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX

char homePosition[5] = "0XYG";



//******************Variables***********

char readString[100];
int commandNumber = 0;
int numCommands = 0;

//****************Function Prototypes******
long convertToSteps(int inches);

//***************Setup******************************
void setup() {
  delay(1000);
  
  readString[99] = '\0';    //Add a null termination to readString
  
  Serial.begin(9600);    //Begin serial communication at 9600
  mySerial.begin(9600);  //Set software serial baud rate
  
  randomSeed(analogRead(0)); 
  
  // Initialize Stepperboard Values
  mySerial.write("!");      //reset Stepper Board
  delay(50);
  mySerial.write("1500R");  //Set Run Rate
  delay(50);
  mySerial.write("800P");  //Set Ramp Rate
  delay(1000);
}

//*************Main Program Loop************************
void loop() {
 
 
 char* stepCommand[] = {    //Load up CNC Patterns into string array
 "30000XG", 
 "-30000XG"
  }; 
 
 int numCommands = (sizeof(stepCommand)/sizeof(char*)); //Calculate array size

 while(mySerial.available()){
   char c[2] = {mySerial.read(), '\0'} ;   //get one byte from buffer 
   strcat(readString, c);
   //Serial.print(c);
   //Serial.println(" --> c charachter byte");
   if(strcmp(c, "*") == 0){
     mySerial.write(stepCommand[commandNumber]);
     Serial.print(stepCommand[commandNumber]);
     Serial.println(" --> IF LOOP");
     commandNumber++;
     if(commandNumber == numCommands){    //if the max number of commands is reached...
        commandNumber = 0;                //reset command number back to zero
     }  
     break;
   } 
 }

 mySerial.write("L");
  
 if(strlen(readString) > 0){
   Serial.print(readString);
   Serial.println(" --> Read String");
   char response[10] = "L,16392";
   response[9] = '\0';
   if(strncmp(readString, response, 7) == 0){
     Serial.println("Write Q Here");
   }
   else{
     Serial.println("Strings not Equal");
   }
  readString[0] = '\0';      //clear the array
 }  
}
//*********************FUNCTIONS***************************
long convertToSteps(int inches){
  long steps = 130435 * inches;  //there are 130435 steps in 1-inch
  return(steps);
}
//**************************************************************************

SERIAL MONITOR OUTPUT BELOW:

M1,-12
SD4DNCRouter 4.3 Oct 3, 2011
o?

  • --> Read String
    Strings not Equal
    -30000XG --> IF LOOP
    ?
  • --> Read String
    Strings not Equal
    30000XG --> IF LOOP
    ?
  • --> Read String
    Strings not Equal
    -30000XG --> IF LOOP
    ?
    G3* --> Read String
    Strings not Equal
    30000XG --> IF LOOP
    ?
    L,256
  • --> Read String
    Strings not Equal
    -30000XG --> IF LOOP
    ?
  • --> Read String
    Strings not Equal
    30000XG --> IF LOOP
    ?
    G2* --> Read String
    Strings not Equal
    -30000XG --> IF LOOP
    ?
    L,0
  • --> Read String
    Strings not Equal
    30000XG --> IF LOOP

(REPEATING)

I'm a bit confused on how to make sure readString is null terminated..I just chose the last place in the array and put a '\0' there.

You should quit trying to use strcat() to append the character to the string. The problem with that is that it expects the string being appended to to be NULL terminated. And, yours isn't.

NULL terminated means that the NULL goes at the end of the valid data NOT at the end of the array.

Start with the NULL in the 0th position.

Ditch using strcat(). Create an index, and increment that every time a character is added (after adding the character), and put the NULL at the location defined by index.

byte index;
char array[100];

array[index++] = c;
array[index] = '\0';

Of course, use your own names, and the code does not go together like this snippet shows. index and array are global (in your case) and the other code goes inside the while statement, where you are currently using strcat().

Let's see how much better this makes things for you, as the next step. Then, if there are still issues, we'll work on them.

Thanks, Paul. I've indexed the array and its working better. I've tried to get an idea of what is in the readSting array by using a FOR loop to print each character and where it is in the array. Just the initialization strings sent from the control board after power up eats up 43 array spots. I copied a portion of the Serial Monitor output showing where I hit the limit switch. The "L,16392" in the readString array seems to be chopped into spaces and some other garbage characters. So, I either need to filter the characters coming into the array so I don't get the garbage. Or, I have to somehow modify my strcmp() function to start looking at the "L" character and then read the array from there. Maybe strbrk() would do this? Any recommendations?

SERIAL MONITOR OUTPUT:

? ---> Character 0

---> Character 1
? ---> Character 2

---> Character 3
? ---> Character 4

---> Character 5
L ---> Character 6
, ---> Character 7
0 ---> Character 8

---> Character 9

---> Character 10

  • ---> Character 11
    ?
    ?
    ?
    L,0
  • --> Read String
    Strings not Equal
    -30000XG --> IF LOOP
    30000XG --> IF LOOP
    -30000XG --> IF LOOP
    30000XG --> IF LOOP
    -30000XG --> IF LOOP

---> Character 0
? ---> Character 1

---> Character 2
L ---> Character 3
, ---> Character 4
1 ---> Character 5
6 ---> Character 6
3 ---> Character 7
9 ---> Character 8
2 ---> Character 9

---> Character 10

---> Character 11

  • ---> Character 12

?
L,16392

  • --> Read String
    Strings not Equal
    30000XG --> IF LOOP
    -30000XG --> IF LOOP
    30000XG --> IF LOOP
    -30000XG --> IF LOOP
/*
CNC Bed Sketch 

This program drives a Peter Norburg SD4DEU Stepper Driver board running SD4DNCRouter firmware connecting at 9600 Baud.
 
 
*/

//******************Constants***********
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX

char homePosition[5] = "0XYG";



//******************Variables***********

char readString[70];
char c;
int i = 0;
int commandNumber = 0;
int numCommands = 0;

//****************Function Prototypes******
long convertToSteps(int inches);

//***************Setup******************************
void setup() {
  delay(1000);
  
  //readString[0] = '\0';    //Add a null termination to readString
  
  Serial.begin(9600);    //Begin serial communication at 9600
  mySerial.begin(9600);  //Set software serial baud rate
  
  randomSeed(analogRead(0)); 
  
  // Initialize Stepperboard Values
  mySerial.write("!");      //reset Stepper Board
  delay(50);
  mySerial.write("1500R");  //Set Run Rate
  delay(50);
  mySerial.write("800P");  //Set Ramp Rate
  delay(1000);
}

//*************Main Program Loop************************
void loop() {
 
 
 char* stepCommand[] = {    //Load up CNC Patterns into string array
 "30000XG", 
 "-30000XG"
  }; 
 
 int numCommands = (sizeof(stepCommand)/sizeof(char*)); //Calculate array size

 while(mySerial.available()){
   c = mySerial.read();   //get one byte from buffer 
   readString[i++] = c;    //increment array (original array index is returned)
   readString[i] = '\0';    //Add NULL to next index
   //i=i+1;
   //Serial.print(c);
   //Serial.println(" --> c charachter byte");
   if(readString[i-1]== '*'){
     mySerial.write(stepCommand[commandNumber]);
     Serial.print(stepCommand[commandNumber]);
     Serial.println(" --> IF LOOP");
     commandNumber++;
     if(commandNumber == numCommands){    //if the max number of commands is reached...
        commandNumber = 0;                //reset command number back to zero
     }  
     break;
   } 
 }

 mySerial.write("L");
  
 if(strlen(readString) > 0){
   for(int j=0; j < strlen(readString); j++){
     Serial.print(readString[j]);
     Serial.print(" ---> Character ");
     Serial.println(j);
   }
   Serial.print(readString);
   Serial.println(" --> Read String");
   char response[] = {'L', ',','1', '6', '3', '9', '2',' ','\0'};
   if(strcmp(readString, response) == 0){
     Serial.println("Write Q Here");
   }
   else{
     Serial.println("Strings not Equal");
   }
   readString[0] = '\0';      //clear the array
 }  
}
//*********************FUNCTIONS***************************
long convertToSteps(int inches){
  long steps = 130435 * inches;  //there are 130435 steps in 1-inch
  return(steps);
}
//**************************************************************************

Try this change:
Serial.print(readString[j], HEX);

Also, add a header and footer:

if(strlen(readString) > 0)
{
Serial.println("Characters read:");
for(int j=0; j < strlen(readString); j++)
{
Serial.print(readString[j], HEX);
Serial.print(" ---> Character ");
Serial.println(j);
}
Serial.println("============");

   readString[0] = '\0';      //clear the array

Good and bad. The problem here is that you are not also resetting i to 0. The name i is terrible. One letter global variables generally are. The name index is a much better choice.

All right. So I changed the println command in include HEX and the spaces disappear. I also set index back to zero right after I clear the array. The Hex shows all the crap in there. Thats a good trick! Now, I think I can use the strstr() function (or something) to pluck out useful string. Or, is there an easier way to do that?

LIMIT SWITCH PORTION OF SERIAL MONITOR OUTPUT:

30000XG --> IF LOOP
Character Read:
FFFFFF86 ---> Character 0
A ---> Character 1
FFFFFF86 ---> Character 2
A ---> Character 3
FFFFFF86 ---> Character 4
A ---> Character 5
4C ---> Character 6
2C ---> Character 7
31 ---> Character 8
36 ---> Character 9
33 ---> Character 10
39 ---> Character 11
32 ---> Character 12
D ---> Character 13
A ---> Character 14
2A ---> Character 15

?
?
?
L,16392

  • --> Read String
    Strings not Equal
/*
CNC Bed Sketch 

This program drives a Peter Norburg SD4DEU Stepper Driver board running SD4DNCRouter firmware connecting at 9600 Baud.
 
 
*/

//******************Constants***********
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX

char homePosition[5] = "0XYG";



//******************Variables***********

char readString[70];
char c;
int index = 0;
int commandNumber = 0;
int numCommands = 0;

//****************Function Prototypes******
long convertToSteps(int inches);

//***************Setup******************************
void setup() {
  delay(1000);
  
  //readString[0] = '\0';    //Add a null termination to readString
  
  Serial.begin(9600);    //Begin serial communication at 9600
  mySerial.begin(9600);  //Set software serial baud rate
  
  randomSeed(analogRead(0)); 
  
  // Initialize Stepperboard Values
  mySerial.write("!");      //reset Stepper Board
  delay(50);
  mySerial.write("1500R");  //Set Run Rate
  delay(50);
  mySerial.write("800P");  //Set Ramp Rate
  delay(1000);
}

//*************Main Program Loop************************
void loop() {
 
 
 char* stepCommand[] = {    //Load up CNC Patterns into string array
 "30000XG", 
 "-30000XG"
  }; 
 
 int numCommands = (sizeof(stepCommand)/sizeof(char*)); //Calculate array size

 while(mySerial.available()){
   c = mySerial.read();   //get one byte from buffer 
   readString[index++] = c;    //increment array (original array index is returned)
   readString[index] = '\0';    //Add NULL to next index
   //i=i+1;
   //Serial.print(c);
   //Serial.println(" --> c charachter byte");
   if(readString[index-1]== '*'){
     mySerial.write(stepCommand[commandNumber]);
     Serial.print(stepCommand[commandNumber]);
     Serial.println(" --> IF LOOP");
     commandNumber++;
     if(commandNumber == numCommands){    //if the max number of commands is reached...
        commandNumber = 0;                //reset command number back to zero
     }  
     break;
   } 
 }

 mySerial.write("L");
  
 if(strlen(readString) > 0){
   Serial.println("Character Read: ");
   for(int j=0; j < strlen(readString); j++){
     Serial.print(readString[j], HEX);
     Serial.print(" ---> Character ");
     Serial.println(j);
   }
   Serial.println("======================");
   Serial.print(readString);
   Serial.println(" --> Read String");
   char response[] = {'L', ',','1', '6', '3', '9', '2','\0'};
   if(strcmp(readString, response) == 0){
     Serial.println("Write Q Here");
     Serial.println("");
   }
   else{
     Serial.println("Strings not Equal");
     Serial.println("");
   }
   readString[0] = '\0';      //clear the array
   index = 0;
 }  
}
//*********************FUNCTIONS***************************
long convertToSteps(int inches){
  long steps = 130435 * inches;  //there are 130435 steps in 1-inch
  return(steps);
}
//**************************************************************************

Or, is there an easier way to do that?

Maybe. The values starting with FF are clearly negative values. So, it appears that the device you are talking to is sending a mix of binary and ASCII data. You could store only those characters that are meaningful to humans ('0' to '9', 'A' to 'Z', and 'a' to 'z'). Stop storing when the '*' arrives. This will exclude the ',', but that might be OK. You could elect to use a different range (or ranges) for the characters to store (maybe ' ' to 'z'), which would then include the ','.

I used the strstr() function and it seems to be working. I finally got the code to recognize the output. So, a minor victory! Thanks a ton.

Now, I use this information to set the home position for my CNC, or to let me know if my bed over-traveled in the +X, -X, +Y or -Y directions. Each limit has it's own L,XXXX code. In the grand scheme of things, I'd like to be able to call a function like "triangle" or "square" that would fill my stepCommand array with the desired string commands. This involves loading my char *stepCommand array with passed from functions, which I'm guessing is going to be a pain. But, I'll research that and see what I can do.

Serial Monitor output at limit switch activation:

-30000XG --> IF LOOP
Character Read:
FFFFFF86 ---> Character 0
A ---> Character 1
4C ---> Character 2
2C ---> Character 3
31 ---> Character 4
36 ---> Character 5
33 ---> Character 6
39 ---> Character 7
32 ---> Character 8
D ---> Character 9
A ---> Character 10
2A ---> Character 11

?
L,16392

  • --> Read String
    Write Q Here
/*
CNC Bed Sketch 

This program drives a Peter Norburg SD4DEU Stepper Driver board running SD4DNCRouter firmware connecting at 9600 Baud.
 
 
*/

//******************Constants***********
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX

char homePosition[5] = "0XYG";



//******************Variables***********

char readString[70];
char c;
int index = 0;
int commandNumber = 0;
int numCommands = 0;

//****************Function Prototypes******
long convertToSteps(int inches);

//***************Setup******************************
void setup() {
  delay(1000);
  
  //readString[0] = '\0';    //Add a null termination to readString
  
  Serial.begin(9600);    //Begin serial communication at 9600
  mySerial.begin(9600);  //Set software serial baud rate
  
  randomSeed(analogRead(0)); 
  
  // Initialize Stepperboard Values
  mySerial.write("!");      //reset Stepper Board
  delay(50);
  mySerial.write("1500R");  //Set Run Rate
  delay(50);
  mySerial.write("800P");  //Set Ramp Rate
  delay(1000);
}

//*************Main Program Loop************************
void loop() {
 
 
 char* stepCommand[] = {    //Load up CNC Patterns into string array
 "30000XG", 
 "-30000XG"
  }; 
 
 int numCommands = (sizeof(stepCommand)/sizeof(char*)); //Calculate array size

 while(mySerial.available()){
   c = mySerial.read();   //get one byte from buffer 
   readString[index++] = c;    //increment array (original array index is returned)
   readString[index] = '\0';    //Add NULL to next index
   //i=i+1;
   //Serial.print(c);
   //Serial.println(" --> c charachter byte");
   if(readString[index-1]== '*'){
     mySerial.write(stepCommand[commandNumber]);
     Serial.print(stepCommand[commandNumber]);
     Serial.println(" --> IF LOOP");
     commandNumber++;
     if(commandNumber == numCommands){    //if the max number of commands is reached...
        commandNumber = 0;                //reset command number back to zero
     }  
     break;
   } 
 }

 mySerial.write("L");
  
 if(strlen(readString) > 0){
   Serial.println("Character Read: ");
   for(int j=0; j < strlen(readString); j++){
     Serial.print(readString[j], HEX);
     Serial.print(" ---> Character ");
     Serial.println(j);
   }
   Serial.println("======================");
   Serial.print(readString);
   Serial.println(" --> Read String");
   char response[10];
   response[0] = '\0';
   
   
   
   if((strstr(readString, "L,16392")) != 0){
     Serial.println("Write Q Here");
     Serial.println("");
   }
   else{
     Serial.println("Strings not Equal");
     Serial.println("");
   }
   readString[0] = '\0';      //clear the array
   index = 0;
 }  
}
//*********************FUNCTIONS***************************
long convertToSteps(int inches){
  long steps = 130435 * inches;  //there are 130435 steps in 1-inch
  return(steps);
}
//**************************************************************************