Problem using strcmp_P

Using this code

char * const PROGMEM p1 = "foo";

void setup()
{
  Serial.begin(115200);
  Serial.println(strcmp_P("foo", p1));
}

void loop()
{
}

I expected the two strings to match and for the result of strcmp_P() to be zero instead of which it is 94

The avr-libc documentation says

int strcmp_P	(	const char * 	s1,

const char * s2
)




The strcmp_P() function is similar to strcmp() except that s2 is pointer to a string in program space.

Removing the PROGMEM modifier in the variable declaration and using the standard strcmp() function produces the expected output of zero.

What am I doing wrong ?

This works as expected:

#include "Arduino.h"

const char PROGMEM p1[] = "foo";

void setup() {
 delay(1000);
 Serial.begin(115200);
 Serial.println(strcmp_P("foo", p1));
}

void loop() {
}

I'm gonna guess that your code puts the POINTER in PROGMEM , not the string.

Have you tried withconst char PROGMEM p1[] = "foo";

Edit: GFvalvo was faster

char * const PROGMEM p1 = "foo";
is a pointer in PROGMEM pointing to constant string

const char PROGMEM p1 = "foo";
is an array in PROGMEM, initialized with foo\0

It certainly looks like the pointers are in PROGMEM.

As you may have guessed the situation is more complicated that my original example. It is more like this, only with a much larger array, hence the use of PROGMEM

char * const PROGMEM s[][3] = {{"foo1", "foo2", "foo3"}, {"bar11", "bar12", "bar13"}};

void setup()
{
  Serial.begin(115200);
  Serial.println(strcmp_P("foo1", s[0][0]));
}

void loop()
{
}

As it happens, putting the pointers in PROGMEM and doing the comparison like this

  while (strcmp((char *) pgm_read_word (&sequences[sequenceNumber][commandIndex]), "end") != 0)

works and has saved enough memory for my needs at the moment

I always check this, to make it right
https://www.arduino.cc/reference/en/language/variables/utilities/progmem/

That was one of my first ports of call when investigating the use of PROGMEM, but unless I have missed or misinterpreted it then it does not address my problem

UKHeliBob:
That was one of my first ports of call when investigating the use of PROGMEM, but unless I have missed or misinterpreted it then it does not address my problem

the reference shows in "Arrays of strings", that you should put the 'leaf`single dimension arrays in PROGMEM and then you can eventually store the pointers to them into another PROGMEM array

I tried this

char * const PROGMEM s1[] = {"foo1", "foo2", "foo3"};
char * const PROGMEM s2[] = {"bar11", "bar12", "bar13"};

char * const  PROGMEM sequences[] = {s1, s2};

void setup()
{
  Serial.begin(115200);
  for (int row = 0; row < 3; row++)
  {
    for (int col = 0; col < 3; col++)
    {
      Serial.println(strcmp_P( sequences[row] [col], "bar12"));
    }
  }
}

void loop()
{
}

and got these errors

sketch_apr02a:4: error: cannot convert 'char* const*' to 'char* const' in initialization

 char * const  PROGMEM sequences[] = {s1, s2};

                                            ^

sketch_apr02a:4: error: cannot convert 'char* const*' to 'char* const' in initialization

C:\Users\Bob\AppData\Local\Temp\arduino_modified_sketch_966333\sketch_apr02a.ino: In function 'void setup()':

C:\Users\Bob\AppData\Local\Temp\arduino_modified_sketch_966333\sketch_apr02a.ino:13:51: warning: invalid conversion from 'char' to 'const char*' [-fpermissive]

       Serial.println(strcmp_P( sequences[row] [col], "bar12"));

                                                   ^

In file included from C:\Program Files (x86)\Arduino\1-8-5\hardware\arduino\avr\cores\arduino/Arduino.h:28:0,

                 from C:\Users\Bob\AppData\Local\Temp\arduino_build_7461\sketch\sketch_apr02a.ino.cpp:1:

c:\program files (x86)\arduino\1-8-5\hardware\tools\avr\avr\include\avr\pgmspace.h:1258:12: note: initializing argument 1 of 'int strcmp_P(const char*, const char*)'

 extern int strcmp_P(const char *, const char *) __ATTR_PURE__;

            ^

exit status 1
cannot convert 'char* const*' to 'char* const' in initialization

For the moment I think that I will just be grateful that I have got the pointers im PROGMEM

t

UKHeliBob:
I tried this

char * const PROGMEM s1[] = {"foo1", "foo2", "foo3"};

char * const PROGMEM s2 = {“bar11”, “bar12”, “bar13”};

char * const  PROGMEM sequences = {s1, s2};

void setup()
{
  Serial.begin(115200);
  for (int row = 0; row < 3; row++)
  {
    for (int col = 0; col < 3; col++)
    {
      Serial.println(strcmp_P( sequences[row] [col], “bar12”));
    }
  }
}

void loop()
{
}



and got these errors


sketch_apr02a:4: error: cannot convert ‘char* const*’ to ‘char* const’ in initialization

char * const  PROGMEM sequences = {s1, s2};

^

sketch_apr02a:4: error: cannot convert ‘char* const*’ to ‘char* const’ in initialization

C:\Users\Bob\AppData\Local\Temp\arduino_modified_sketch_966333\sketch_apr02a.ino: In function ‘void setup()’:

C:\Users\Bob\AppData\Local\Temp\arduino_modified_sketch_966333\sketch_apr02a.ino:13:51: warning: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]

Serial.println(strcmp_P( sequences[row] [col], “bar12”));

^

In file included from C:\Program Files (x86)\Arduino\1-8-5\hardware\arduino\avr\cores\arduino/Arduino.h:28:0,

from C:\Users\Bob\AppData\Local\Temp\arduino_build_7461\sketch\sketch_apr02a.ino.cpp:1:

c:\program files (x86)\arduino\1-8-5\hardware\tools\avr\avr\include\avr\pgmspace.h:1258:12: note: initializing argument 1 of ‘int strcmp_P(const char*, const char*)’

extern int strcmp_P(const char *, const char *) ATTR_PURE;

^

exit status 1
cannot convert ‘char* const*’ to ‘char* const’ in initialization




For the moment I think that I will just be grateful that I have got the pointers im PROGMEM

the strings are the one-dimensional ‘leaf’ arrays. you should put every single “abcd” to PROGMEM. see the "“Arrays of strings” example on the reference page

you should put every single "abcd" to PROGMEM.

Someone is having a laugh if I must do that !

As you may have guessed from the name of the containing array the strings at each level are a sequence of commands to be executed by 4 servos. The largest sequence so far has 72 steps, and there will be more. Whilst I could assign the values individually to each array element, maintenance would be a nightmare if a command is added or removed.

I think that I will stick with what I have got but thanks for the suggestions

UKHeliBob:
Someone is having a laugh if I must do that !

As you may have guessed from the name of the containing array the strings at each level are a sequence of commands to be executed by 4 servos. The largest sequence so far has 72 steps, and there will be more. Whilst I could assign the values individually to each array element, maintenance would be a nightmare if a command is added or removed.

I think that I will stick with what I have got but thanks for the suggestions

yes, it is inconvenient. I didn't use an array of PROGMEM strings yet, but I would put it in a .cpp and access in sketch only the top array over 'extern' declaration.
in the cpp file I would in case of short strings name the strings by the content (ABC = "abc").
I didn't work with servos yet, so I don't know how the strings are the commands, but aren't the individual commands used more then once in sequence?

You could possibly #define the base sequence and let the preprocessor create a long sequence as a huge string for you
That would be at the cost of duplicating steps in memory thus more flash used but may be easier to maintain / type

I have been experimenting using a different method of storing the sequence of servo movements like this

const char s1[100] PROGMEM = "foo1,foo2,foo3,";
const char s2[200] PROGMEM = "bar11,bar12,bar13,";

const char * const sequences[] PROGMEM = {s1, s2};

This is the method suggested in the PROGMEM reference page

My intention was to use strtok_P to parse the string using the commas as delimiters to extract the commands like this

#include <avr/pgmspace.h>

const char s1[] PROGMEM = "foo1,foo2,foo3";
const char s2[] PROGMEM = "bar11,bar12,bar13,bar14";
const char * const sequences[] PROGMEM = {s1, s2};

void setup()
{
  Serial.begin(115200);
  Serial.println("start");
  char * cmd;
  cmd = strtok_P(sequences[0], "," );
  while (cmd != NULL)
  {
    cmd = strtok_P(NULL,",");
    Serial.print(">");
    Serial.print(cmd);
    Serial.println("<");
  }
  Serial.println("end");
}

void loop()
{
}

This fails to print any of the commands in the array so I am stuck (again)

Removing the PROGMEM specifier and using the ordinary strtok() function works as expected and lists the commands

Ideas anyone ?

UKHeliBob:
I have been experimenting using a different method of storing the sequence of servo movements like this

const char s1[100] PROGMEM = "foo1,foo2,foo3,";

const char s2[200] PROGMEM = “bar11,bar12,bar13,”;

const char * const sequences PROGMEM = {s1, s2};



This is the method suggested in the PROGMEM reference page

My intention was to use strtok_P to parse the string using the commas as delimiters to extract the commands like this



#include <avr/pgmspace.h>

const char s1 PROGMEM = “foo1,foo2,foo3”;
const char s2 PROGMEM = “bar11,bar12,bar13,bar14”;
const char * const sequences PROGMEM = {s1, s2};

void setup()
{
  Serial.begin(115200);
  Serial.println(“start”);
  char * cmd;
  cmd = strtok_P(sequences[0], “,” );
  while (cmd != NULL)
  {
    cmd = strtok_P(NULL,",");
    Serial.print(">");
    Serial.print(cmd);
    Serial.println("<");
  }
  Serial.println(“end”);
}

void loop()
{
}




This fails to print any of the commands in the array so I am stuck (again)

Removing the PROGMEM specifier and using the ordinary strtok() function works as expected and lists the commands

Ideas anyone ?

strtok_P failed because the sequences (array of pointers) is in PROGMEM too. you should read it with pgm_read_word

the “Arrays of strings” example shows it
strcpy_P(buffer, (char )pgm_read_word(&(string_table)));*

You need to first define all the strings that are to be stored in PROGMEM. Then you create the array of pointers, using references to those pre-existing strings. You cannot create the strings and the array of pointers in a single statement as is done with the RAM-based equivalent.

Nick Gammons site has a very clear PROGMEM tutorial with an example of precisely how to do this, and WHY it is such a round-about process.

Regards,
Ray L.

RayLivingston:
You need to first define all the strings that are to be stored in PROGMEM. Then you create the array of pointers, using references to those pre-existing strings. You cannot create the strings and the array of pointers in a single statement as is done with the RAM-based equivalent.

Nick Gammons site has a very clear PROGMEM tutorial with an example of precisely how to do this, and WHY it is such a round-about process.

Regards,
Ray L.

You want to consider whether saving 4 bytes of SRAM by putting sequences in PROGMEM is worth the extra level of complexity in accessing the data. Personally, I'd be looking somewhere else to trim 4 bytes of SRAM.

You want to consider whether saving 4 bytes of SRAM by putting sequences in PROGMEM is worth the extra level of complexity in accessing the data. Personally, I'd be looking somewhere else to trim 4 bytes of SRAM.

Having got the pointers in PROGMEM I now have plenty of SRAM, so I agree with you and I will stop trying, for now at least.

I did try the method described in Nick Gammon's page and it worked but it still irked me that I could not get the strtok_P() version working as it looks made for the job

Thanks for the pointers ( :slight_smile: ) and suggestions.

UNTESTED! Assuming you’re still in the Arduino environment (even if you’re not you can just remake the F() macro)

#define cF(x) ((const char *)F(x))

const char * array[] PROGMEM = {cF("Hello"), cF("World")};

The F() macro applies the PROGMEM attribute to the enclosed string and casts it to a FlashStringHelper* pointer (so the print functions can differentiate a PROGMEM string from a normal RAM stirng). My cF() macro applies the F() macro to the string and casts the result back to a const char* pointer.

Or you could just make the array of type FlashStringHelper* and use only the F() macro for each string.

Note that the F() macro can only be used within a function. It is very unhappy hanging out in global-land because it uses the PSTR macro that causes the error: “statement-expressions are not allowed outside functions nor in template-argument lists” when used outside of a function.