String compareTo() problem

Hello,

I am having issues with the String compareTo method. I am trying to compare the string of the scan from a barcode scanner to another string and then move my servo motor and turn a led on. However, I seem to be having issues with the string comparison part of my code. Can someone lend me a hand? My code is as follows:

Servo servo;
int led1 = D6;

void setup() {
    Serial.begin(9600);
    Serial1.begin(9600);
    servo.attach(A0);
    
    digitalWrite(led1, LOW);
    servo.write(134);
}

void loop() {
    String scan = Serial1.readString();
    String barcode = "X00216DEMN";
    Serial.print(scan);
    
    if(scan.compareTo(barcode) == 0){
        servo.write(32);
        digitalWrite(led1, HIGH);
    }
}

A couple of observations:

  1. Generally better to use c string than String object.
  2. It is unnecessary to redeclare barcode in the loop - do this >1000/second
  3. Your compare is probably not matching because there is an unprinted char at the end of scan. This is where using strncmp() is better in that you only compared the number of barcode chars you need/want.
2 Likes

@armor Thank you for the response. This may be a dumb question, but how do I go about including the c string library? I don’t see it when I search libraries and if I try to declare a string variable I get an error when I verify the code.

There is no library for C strings. C strings are just an array of characters terminated with a zero byte.
And C string manipulation functions are available by default.

1 Like

Okay thank you, then how am I able to read the input I get form my barcode scan? Serial.read returns an int type and Serial.readString returns a String type. Is there a way to convert these to char?

You could use the c_str() method:

const char *scan = Serial1.readString().c_str();

I would however recommend avoiding using the String class when possible:

char scanBuffer[128]; // Hardware Serial1 buffer is 128 bytes on the boron
int counter = 0;
while (Serial1.available() > 0)
{
  // Read into the buffer
  scanBuffer[counter++] = Serial1.read();
}
scanBuffer[counter] = '\0'; // Terminator
1 Like

For the convenience of readString() you could use an alternative provided by the Stream class which is meant for dealing with arrays and hence can also deal with C strings Serial1.readBytes().

  char scanBuffer[128];
  int  count;
  count = Serial1.readBytes(scanBuffer, sizeof(scanBuffer)-1);
  scanBuffer[count] = '\0';

But, this function (as well as readString()) need to timeout in order to move on, while @nrobinson2000 version would move on as soon no more data is to be read.
There are also readStringUntil() and readByteUntil()) which would allow you to terminate as soon a terminator byte was found.

1 Like

The standard c-library is already included with Particle.h. I would suggest a google search for strncmp to see a reference example of how to implement it. It is something like this:

char scan[10];

then your Serial1.read() will be into the scan[] char array but since it reads 1 byte at a time you will need to place inside a while(Serial1.available() && i < 10){scan[i] = Serial1.read(); i++;} and increment the index i after setting it to 0 before the while int i = 0;

if ((strncmp(“X00216DEMN”, scan, 10) == 0)
{
}

Thank you @nrobinson2000 your code implemented worked for me and thank you @ScruffR for giving me an alternative. Also, thanks @armor I tried your code as well and it also worked for me. The only problem I’m running into now is when I scan a barcode, the code is printed on my terminal multiple times and then the application seems to be stalled, i.e if I try to scan the same barcode again then nothing happens.

Does anyone know why this seems to happen?

55%20PM

Your reader is probably reading the same card multiple times and will fill up the input buffer and any subsequent scan will probably not be aligned correctly.

After getting your first scan, you should flush out all data that’s left in the RX buffer to start off with a clean read.

BTW, to help you with your new code, you should probably show it again :wink:

1 Like

@ScruffR You read my mind haha. My code is as follows:

Servo servo;

char scan[12];
int i = 0;
char barcode[] = "X00216DEMN";


void setup() {
    Serial.begin(9600);
    Serial1.begin(9600);
    servo.attach(A0);
    servo.write(134);
}
void loop() {
    while(Serial1.available() && i < 12){
        scan[i] = Serial1.read();
        i++;
    }
    Serial.print(scan);
    if (strncmp(barcode, scan, 12) == 0){
        servo.write(32);
        delay(3000);
        servo.write(134);
    }
}

The barcode scanner I am using operates @5V could that be an issue as to why it’s getting printed multiple times? I have the VCC wire of the scanner wired to the VBUS pin on my Boron device.

If that scanner puts 5V on the RX pin of your Boron that’s no good idea as none of the GPIOs on the Boron are 5V tolerant. But it wouldn’t expain the multiple reads.
Multiple reads are common for NFC readers tho’ - the software should deal with such instances gracefully.

However, once you got the voltage issue sorted, this line would do the flushing of the RX buffer

  while(Serial1.read() >= 0); 

You may want to wrap that also in some sort of timeout routine which also informs the user that the card needs to be removed from the scanner and when a new scan attempt can be made.

BTW, you need to reset i once you have dealt with your scan, otherwise your i < 12 condition will never ever become true again :wink:
Also, (I guess this was an oversight of @armor in his suggested code) when you want to store a 12 (or 10) character string you need an extra byte in the array to also store the terminating '\0' character which you’d probably need to add manually (as our code suggestions above featured :sunglasses:) since the scanner won’t send that (judging by the output you showed above).
Otherwise printing that string would just blurt out some extra bytes up until the first occurance of a '\0' byte in RAM following the end of the array.

@ScruffR I looked at the documentation for the scanner and it does not say anything about putting 5V on any of the pins when in use. I manually added the terminating ‘\0’ like in the suggestions above.

Where would I implement the while(Serial1.read() & = 0) code? Would I swap that out for the Serial.available() part? I originally had done that and when I did a scan this was my output?

12%20PM

This is the code in my loop:

void loop() {

    while(Serial1.read() >= 0 && i < 12){
        scan[i] = Serial1.read();
        i++;
    }
    scan[i] = '\0';
    
    Serial.print(scan);
    
    if (strncmp(barcode, scan, 12) == 0){
        servo.write(32);
        delay(3000);
        servo.write(134);
    }
}

Just for curiosity, why are you going for 12 bytes when your barcode string is only 10 characters long?

Try this

// convenience macro for literal comparison
#define STRNCMP(str, literal) strncpy(str, literal, strlen(literal))

// keeping your constants in one place makes tweaking them easier
const int  scanIndicator = D7;
const int  servoLatency  = 250;
const int  servoClosed   = 134;
const int  servoOpen     =  32; 
const char barcode[]     = "X00216DEMN";

void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);
  
  pinMode(scanIndicator, OUTPUT);                    // for feedback regarding the scan    
  servo.attach(A0);
  servo.write(servoClosed);
  delay(servoLatency); 
  servo.detach();                                    // disengage servo after movement to save power

  while(Serial1.read() >= 0) Particle.process();     // flush the RX buffer of any data already present

  digitalWrite(scanIndicator, HIGH);                 // ready for first scan
}

void loop() {
  // do other stuff independent of your actual scan

  if (Serial1.available() < strlen(barcode)) return; // if not enough data arrived so far bail out
  
  char scan[64];
  digitalWrite(scanIndicator, LOW);                  // indicate a scan is being processed

  Serial1.readBytes(scan, strlen(barcode));          // we know we have enough data, so read
  scan[strlen(barcode)] = '\0';                      // terminate the string  
  Serial.println(scan);                              // print what we got 
    
  if (STRNCMP(scan, barcode) == 0) {
    servo.attach(A0);
    servo.write(servoOpen);
    delay(3000);                                     // I'd rather use a timer instead of blocking delay 
    servo.write(servoClosed);
    delay(servoLatency);
    servo.detach();
  }

  while(Serial1.read() >= 0) Particle.process();     // flush the RX buffer while allowing for more data  
                                                     //   to stream in as long the scanner keeps chatting
  digitalWrite(scanIndicator, HIGH);                 // ready for next scan
}
2 Likes

My bad about the char scan array size - should have been bigger than the 10 chars barcode shown in the compare to value but I was trying to give guidance not an exact solution! Printing out the c string wasn’t in the example!

1 Like