Comparing a char array to a String

I have the following edit version of a sketch courtesy of Nick Gammon.
I receive all the data from the SoftwareSerial correctly.
However, I need to compare the received data to a String.
The code compiles, but does not action.
I am at a loss to see where I am going wrong.
Any assistance would be greatly appreciated ;D

// here to process incoming serial data after a terminator received
void process_data (const char * data)
  {
  // for now just display it
  // (but you could compare it to some value, convert to an integer, etc.)

  // Following for debug
  mySerial.println (data);  // data is receiced correctly: either "SW0013A200400A11115" or "SW0013A200400A11114"
  
  //----------------------------------------------
  // I must now compare the incomming data to either "SW0013A200400A11115" or "SW0013A200400A11114" and action relay
            if (data == "SW0013A200400A11115") {
             digitalWrite(relay, HIGH);  // Switch Relay ON
             mySerial.print("SW0013A200400A11115#");
             mySerial.write('\n');
           }  
          else if (data == "SW0013A200400A11114") {
             digitalWrite(relay, LOW);  // Switch Relay ON
             mySerial.print("SW0013A200400A11114#");
             mySerial.write('\n');
          }
  //----------------------------------------------
  }  // end of process_data
  
void processIncomingByte (const byte inByte)
  {
  static char input_line [MAX_INPUT];
  static unsigned int input_pos = 0;

  switch (inByte)
    {

    case '#':   // end of text
      input_line [input_pos] = 0;  // terminating null byte
      
      // terminator reached! process input_line here ...
      process_data (input_line);
      
      // reset buffer for next time
      input_pos = 0;  
      break;

    case '\r':   // discard carriage return
      break;

    default:
      // keep adding if not full ... allow for terminating null byte
      if (input_pos < (MAX_INPUT - 1))
        input_line [input_pos++] = inByte;
      break;

    }  // end of switch
   
  } // end of processIncomingByte  

void loop()
  {
  // if serial data available, process it
  while (mySerial.available () > 0)
    processIncomingByte (mySerial.read ());
    
  // do other stuff here like testing digital input (button presses) ...

  }  // end of loop

I don't see any "String" being defined in that code.

Hi KenF,
The String is the "SW0013A200400A11115" in:

if (data == "SW0013A200400A11115")

Declan:
Hi KenF,
The String is the "SW0013A200400A11115" in:

if (data == "SW0013A200400A11115")

Ah
In that case I think the command you're looking for is

if (strcmp(data,"SW0013A200400A11115")==0)
  {//do stuff here because it was a match

  }

Hi KenF,
Many thanks ..... Got it

Declan:
Hi KenF,
Many thanks ..... Got it

But do you know why ?
You referred to Strings but you are actually using strings, hence the comment earlier that there were no Strings in your code snippet.

See String() - Arduino Reference
and
http://arduino.cc/en/Reference/String

UKHeliBob makes a good point. Allow me to elaborate:

The variable data, passed to the function process_data(), is declared as a pointer to char, which will be used to iterate an array of chars.

An array of chars can, if null terminated, be used as a C string, but it is not a String object. Also, an array of chars can be used to instantiate a String object, like this:

char my_char_array[] = "Jabber jabber!";  // A string constant initializing an array of chars
String my_string = String(my_char_array); // An instantiation of a String object from an array of chars

Since data is being compared to a string constant, such as "SW0013A200400A11115", and since data is a pointer to an array of chars [see Note 1, below], one must use something like the C-library function strcmp() [from string.h] to do the comparison.

char string1[] = "SW0013A200400A11114";  
char string2[] = "SW0013A200400A11115";  
char string3[] = "SW0013A200400A11116";  


void process_data (const char * data)
{
  Serial.print("data:\"");
  Serial.print(data);
  Serial.println("\"");

  if (strcmp(data, "SW0013A200400A11114") == 0)
  {
    Serial.println("-- Is string 1");
  }
  else if (strcmp(data, "SW0013A200400A11115") == 0)
  {
    Serial.println("-- Is string 2");
  }
  else
  {
    Serial.println("-- Is not recognized!");
  }
  
}

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

void loop() {

  process_data(string1);
  process_data(string2);
  process_data(string3);
  
  while(1);
}

/***************************************
 *   Produces the following output:    *
 ***************************************

data:"SW0013A200400A11114"
-- Is string 1
data:"SW0013A200400A11115"
-- Is string 2
data:"SW0013A200400A11116"
-- Is not recognized!


***************************************/

This can also be done with String objects like this:

char string1[] = "SW0013A200400A11114";  // Using a string constant to initialize an array of chars.  Null termination occurs in the string constant.
String String1 = String(string1);                 // Instantiation from existing array of chars
String String2 = String("SW0013A200400A11115");   // Instantiation from string constant
String String3 = String("SW0013A200400A11116"); 


void process_data (String data)
{
  Serial.print("data:\"");
  Serial.print(data);
  Serial.println("\"");

  if (strcmp(data.c_str(), "SW0013A200400A11114") == 0)  // Doing it with a C Library function
  {
    Serial.println("-- Is string 1");
  }
  else if (data.compareTo("SW0013A200400A11115") == 0)  // Doing it with a String method
  {
    Serial.println("-- Is string 2");
  }
  else
  {
    Serial.println("-- Is not recognized!");
  }
  
}

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

void loop() {

  process_data(String1);
  process_data(String2);
  process_data(String3);
  
  while(1);
}

// The output is the same as before!

Note 1: if the array of chars that data is pointing to is not null terminated, one would think there's the possibility of Buffer Overrun -- BUT, The Arduino compiler seems to have some bad a$$ intelligence for catching this, 'cuz I tried to cause buffer overrun, and not only did the compiler issue an "undefined behavior" warning, but the code was actually altered to insure no overrun can happen -- though the code may not behave as intended -- pretty cool! Here's the code I wrote to attempt a Buffer Overrun:

char string1[5];                                  // Notice: only 5 elements allocated
String String1;             
String String2 = String("SW0013A200400A11115");   // Instantiation from string constant
String String3 = String("SW0013A200400A11116"); 


void process_data (String data)
{
  Serial.print("data:\"");
  Serial.print(data);
  Serial.println("\"");

  if (strcmp(data.c_str(), "SW0013A200400A11114") == 0)  // Doing it with a C Library function
  {
    Serial.println("-- Is string 1");
  }
  else if (data.compareTo("SW0013A200400A11115") == 0)  // Doing it with a String method
  {
    Serial.println("-- Is string 2");
  }
  else
  {
    Serial.println("-- Is not recognized!");
  }
  
}

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

  // Try to cause a buffer overrun by assigning more than 5 chars 
  // to string1 (which was declared with room for only 5 chars).
  // Also, I intentionally did NOT null terminate the thing!
  // Buffer overrun is expected here.
  for (int i=0; i<10; ++i)
  {
    string1[i] = 'a';
  }
  String1 = String(string1);  // The buffer overrun could, also, occur here (because string1 was declared to hold only 5 chars, and the 5th char is an 'a' and not a '\0')!  
}

void loop() {

  process_data(String1);
  process_data(String2);
  process_data(String3);
  
  while(1);
}

/***************************************
 *   Produces the following output:   
 * Notice how the string in data has 
 * expanded to contain all 10 'a's
 ***************************************

data:"aaaaaaaaaa"
-- Is not recognized!
data:"SW0013A200400A11115"
-- Is string 2
data:"SW0013A200400A11116"
-- Is not recognized!


***************************************/

Even when I tried it on the C-Lib strcmp() function, it still survived (with warning)!

char string1[5];                                  // Notice: only 5 elements allocated
char string2[] = "SW0013A200400A11115";  
char string3[] = "SW0013A200400A11116";  
//String my_string = String(my_char_array); 


void process_data (const char * data)
{
  Serial.print("data:\"");
  Serial.print(data);
  Serial.println("\"");

  // Buffer Overrun could, also, happen here:
  if (strcmp(data, "SW0013A200400A11114") == 0)
  {
    Serial.println("-- Is string 1");
  }
  else if (strcmp(data, "SW0013A200400A11115") == 0)
  {
    Serial.println("-- Is string 2");
  }
  else
  {
    Serial.println("-- Is not recognized!");
  }
  
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  
  // Try to cause a buffer overrun by assigning more than 5 chars 
  // to string1 (which was declared with room for only 5 chars).
  // Also, I intentionally did NOT null terminate the thing!
  // Buffer overrun is expected here.
  for (int i=0; i<10; ++i)
  {
    string1[i] = 'a';
  }
}

void loop() {

  process_data(string1);
  process_data(string2);
  process_data(string3);
  
  while(1);
}

/***************************************
 *   Produces the following output:    *
 ***************************************

data:"aaaaaaaaaa"
-- Is not recognized!
data:"SW0013A200400A11115"
-- Is string 2
data:"SW0013A200400A11116"
-- Is not recognized!


***************************************/

BTW: The Arduino IDE includes string.h in the background, so no need to add an include statement to your code.
BTW2: instantiate is a fancy word for creating an object from a class. Though, String is a special case, because it's actually a language construct, but, more than likely, behind the scenes, there is an instantiation going on.

Why don't we just use:

if (String(data) == "SW0013A200400A11115") {do this;}

Or is it the same thing as:
if (strcmp(data,"SW0013A200400A11115")==0) {do this;}

1 Like

vettel:
Why don't we just use:

if (String(data) == "SW0013A200400A11115") {do this;}

What you're doing there, is testing an object against a string constant. One would expect a result of false, but I dusted off an Arduino Uno and tried it, and what do you know! It returned true!

So, and don't quote me 'cuz my C++ is rusty, but this suggests that the == operator is overloaded to do a string compare when the String object is one of the operands.

vettel:
Or is it the same thing as:
if (strcmp(data,"SW0013A200400A11115")==0) {do this;}

Hard to say. If it's true that the == operator is overloaded, then it depends how that was implemented.

UPDATE: I did a little Googling, and it turns out the Arduino source does overload the == operator!

And here it is -- from WString.h:

unsigned char operator == (const char *cstr) const {return equals(cstr);}
unsigned char operator == (const String &rhs) const {return equals(rhs);}