trying to receive part of a char array without using String

Hello,

this probably is a rather simple questions for the pros, but as I’m a complete newbie when init comes to development for Microcontrollers and (for that matter) plain old standard C, it poses a huge burden on me.

What I’d like to achieve is to get a substring from a char array into another char array without using the String class.

My initial string looks like this: enD: bluemc99 or others might look like so enT: track001.mp3

And I want it to be bluemc99 or of course track001.mp3
where both of which of course would finally end up in different char arrays as one is the directory whereas the other is the first track (mp3 file) within this directory to play.

This is my code, but instead of giving me bluemc99 it ends up with bluemc?⸮?

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

  char plrCurrentFolder[9];
  
  // put your setup code here, to run once:
  // the string we search through
  const char haystack[] = "enD: bluemc99";
  
  // the character we are looking for
  const char needle = 'n';
  
  // this holds the next character after the needle (n)
  char charAtPosition[1];
  
  // this holds a pointer to the rest of the string, after the needle (n) was found
  char *ret;
  
  // get pointer by searching for the needle in the haystack
  ret = strchr(haystack, needle);
  
  // clear memory address of charAtPosition
  memset(charAtPosition, '\0', sizeof(charAtPosition));

  Serial.print(F("String after "));
  Serial.print(needle);
  Serial.print(F(" is "));
  Serial.print(ret[1]);
  Serial.print(F(" in the haystack "));
  Serial.println(ret);

  for (byte c=1; c<sizeof(ret); c++) {
    if (ret[c] == 'D'){
      Serial.print(F(" Found a "));Serial.println(ret[c]);
      for (byte d=1; d<sizeof(ret)-2; d++) {
        plrCurrentFolder[d-1] = ret[d];
      }
      break;
    }
  }

  Serial.print(F(" plrCurentFolder now contains "));
  Serial.println(plrCurrentFolder);
}

void loop() {
  // put your main code here, to run repeatedly:

}

Can someone tell me what I’m doing wrong here?

Kind regards and many thanks,

Chris

Can you please post the actual output of a single run, in code tags?

By the way, it sounds like a job for strtok().

trying to receive part of a char array without using String

I don't see any code in your program that is trying to receive data using Serial. If you really do want to receive data have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract stuff from the received text.

...R

I think I was too unspecific.

The posted code is just a new sketch specific to solve this problem. In the real program, the haystack is read in from an NFC Tag.

For simplicity reasons, I stripped everything away to concentrate on the problem at hand. This problem is simply that I can’t find a way to receive the substring of the haystack “enD: bluemc99” which would be only “bluemc99”

If you want to see all the code, please see the attachment. Be aware though that in the attached sketch I’m still using String-class operations for the problem at hand, which I want to get rid off.

Many thanks,

Christian

main2ndTry.ino (25.7 KB)

siliconchris:
For simplicity reasons, I stripped everything away to concentrate on the problem at hand.

Well, that is actually helpful. But it must be a complete, working program.

Also you never posted the serial output as I requested.

You are also silent on the strtok() idea.

siliconchris:
I can’t find a way to receive the substring of the haystack “enD: bluemc99” which would be only “bluemc99”

Perhaps you mean that you want to extract the substring after you have received the full message.

Please post an example of the full message.

How do you identify the part that you want to extract?

…R

Robin2:
Perhaps you mean that you want to extract the substring after you have received the full message.

Please post an example of the full message.

How do you identify the part that you want to extract?

…R

Yes. Extract is probably the right word. Well the messages I receive are in this form:

Found chip PN532
Firmware ver. 1.6
NFC tag Found!

Tag Type: NFC Forum Type 2
UID: 04 43 E2 22 E7 4C 81
content element 0: enA: Findet Dorie
content element 1: enD: finddori
content element 2: enT: track001.mp3
content element 3: enP: 0

And I would like to extract
finddori for the directory - that is what the enD: prefixes
track001.mp3 for the track - that is what the endT: prefixes
and
0 for the position - that is what the enP: prefixes

I’m very sorry if I made this more complicated than necessary, but I simply am to new to good’ll’c :slight_smile:

Here is the code that I use today, but as said, it makes heavy use of the String-class and I need to get rid of it.

boolean getNfcCardData() {
  boolean usableContent = true;  // in case the tag has everything we need to play an album - in essence there must be a message with the directory - we return this as true
  
  NfcTag tag = nfc.read();
  if (DEBUG) { Serial.print(F("Tag Type: ")); Serial.println(tag.getTagType()); Serial.print(F("UID: ")); Serial.println(tag.getUidString()); }

  if (tag.hasNdefMessage()) {
    NdefMessage message = tag.getNdefMessage();

    // If you have more than 1 Message then it will cycle through them
    for (byte i = 0; i < message.getRecordCount(); i++) {
      NdefRecord record = message.getRecord(i);

      byte payloadLength = record.getPayloadLength();
      byte payload[payloadLength];
      record.getPayload(payload);

      String payloadAsString = ""; // Processes the message as a string vs as a HEX value
      for (byte c = 0; c < payloadLength; c++) {
        payloadAsString += (char)payload[c];
      }
      
      if (DEBUG) { Serial.print(F("content element ")); Serial.print(i); Serial.print(F(": ")); Serial.println(payloadAsString);}

      // now populate the vars for the player
      if (payloadAsString.charAt(payloadAsString.indexOf(':') - 1) == 'D') {  // DIRECTORY
        usableContent = true;
        String contentString = payloadAsString.substring(5);
        contentString.toCharArray(plrCurrentFolder, 9);
        
        if (DEBUG) { Serial.print(F("Directory: ")); Serial.println(contentString); }
        usableContent = true;
      }
      if (payloadAsString.charAt(payloadAsString.indexOf(':') - 1) == 'T') {  // TRACK
        String contentString = payloadAsString.substring(5);
        firstTrackToPlay = contentString.toInt();
        if (DEBUG) { Serial.print(F("Track: ")); Serial.println(contentString); }
      }
      // we do not use the current position as this is not returned by the player
      //if (payloadAsString.charAt(cleanString.indexOf(':') - 1) == 'P') {  // POSITION
      //  String contentString = payloadAsString.substring(5);
      //  plrCurrentPosition = contentString.toInt();
      //  // if (DEBUG) { Serial.print(F("Position: "));Serial.println(contentString); }
      //}
    }
  } else { // does the card have NDEF messages?
    if (DEBUG) { Serial.println(F("THIS CARD DOES NOT HAVE MESSAGES AT ALL - NOT USABLE FOR THE MP3 PLAYER")); }
    usableContent = false;
  }
  
  return (usableContent);
}

aarg:
Can you please post the actual output of a single run, in code tags?

By the way, it sounds like a job for strtok().

strtok() as far as I understood it, is used to get parts of a cstring by specifying a delimiter that separates the different parts. My messages only have one specific identifier and that is the : which comes right after the character that identifies whether it's a directory (D), a track (T) or a position (P) and after that : there is exactly one space and then the rest of the message is the directory or track name or the position in seconds.

Found chip PN532
Firmware ver. 1.6
NFC tag Found!

Tag Type: NFC Forum Type 2
UID: 04 43 E2 22 E7 4C 81
content element 1: enD: finddori
content element 2: enT: track001.mp3
content element 3: enP: 0

I'm not sure how I could make use of stroke() in this situation. Would you have an idea? I'd really appreciate some help here.

Best regards,

Chris

siliconchris:
strtok() as far as I understood it, is used to get parts of a cstring by specifying a delimiter that separates the different parts. My messages only have one specific identifier and that is the : which comes right after the character that identifies whether it's a directory (D), a track (T) or a position (P) and after that : there is exactly one space and then the rest of the message is the directory or track name or the position in seconds.

I think you're confusing what the function strtok() sees as a delimiter and what you think a delimiter must be. strtok() is like honeybadger, he don't care what the delimiter is, *you * get to choose. So, you can specify " " (space) as the delimiter if that's what you want.

AVR string library

Here's an example of strtok() in action.

/* strtok() Arduino example for a string contained in 
   the source code.  

   based on the reference example at: 
   http://www.cplusplus.com/reference/cstring/strtok/

   Zac Staples. July, 2013.
*/

void setup() {
  Serial.begin(9600);
  char str[] = "{label,4,8,9}";
  char* p;
  Serial.println("Example of splitting a string into tokens: ");
  Serial.print("The input string is: '");
  Serial.print(str);
  Serial.println("'");

  p = strtok(str, "{,}"); //2nd argument is a char[] of delimiters
  while (p != '\0') { //not equal to NULL
    Serial.println(p);
    p = strtok('\0', "{,}");  //expects NULL for string on subsequent calls
  }
}

void loop() {
  delay(250);
}

I'm not an expert but I think strtok() is not the appropriate function for this. I think you need to use strstr() which (I believe) finds one string inside another - to find, for example "enA: " which allows you to then get the following characters up to the next space and then search for "enD: " etc etc.

It may be possible to use a combination of strstr() and strtok().

Either way the code is going to be a bit tedious - which is why on a computer with a lot more memory you would the String class :slight_smile:

...R

dougp:
I think you're confusing what the function strtok() sees as a delimiter and what you think a delimiter must be. strtok() is like honeybadger, he don't care what the delimiter is, *you * get to choose. So, you can specify " " (space) as the delimiter if that's what you want.

AVR string library

Here's an example of strtok() in action.

/* strtok() Arduino example for a string contained in 

the source code.

based on the reference example at:
  strtok - C++ Reference

Zac Staples. July, 2013.
*/

void setup() {
  Serial.begin(9600);
  char str = "{label,4,8,9}";
  char* p;
  Serial.println("Example of splitting a string into tokens: ");
  Serial.print("The input string is: '");
  Serial.print(str);
  Serial.println("'");

p = strtok(str, "{,}"); //2nd argument is a char of delimiters
  while (p != '\0') { //not equal to NULL
    Serial.println(p);
    p = strtok('\0', "{,}");  //expects NULL for string on subsequent calls
  }
}

void loop() {
  delay(250);
}

Thank you very very much. You where absolutely right. I completely forgot that I can be the one to define what is the delimiter. I changed the records on my NFC tags and made sure they follow a strict structure. The first one is now always the directory, the second one always the track to start playing, the third is the order in which to play the files. By doing so I was able to eliminate the D, T and so forth and therefore I can now doe a simple strtok() with a space-delimiter as I know that I always have a en prefix before my payload.

Again thank you very much for your valuable help. My last issue now is that I get a warning regarding an invalid conversion from 'byte* {aka unsigned char*}' to 'char*'. I have posted a new question on this specific issue (because I think it best to separate it from this initial question) here:

Excellent!