Const char PROGMEM and external

Hello,

I got this in the main sketch:

const static char unitMin[] PROGMEM = "min.";
const static char unitSec[] PROGMEM = "sec.";
const static char unitHour[] PROGMEM = "hours";
const static char unitDay[] PROGMEM = "days";

And I want to use the unitMin in a different tab:

extern const static char unitMin[] PROGMEM;

But this gives an error:

conflicting specifiers in declaration of 'unitMin'

Tried different variants of the extern... but always the same error.

What is the correct way of declaring it external? Using external with all the other non-PROGMEM variables works fine.

What about as a pointer?

(Honestly, never tried it)

Tried:

extern const static char *unitMin PROGMEM;

same shit.

extern and static are incompatible. static means the variable is accessible only in the one compilation unit, which means it can't be used in a separate compilation unit (which is why you would use extern).

However, Arduino is "special" because it concatenates tabs together into a single compilation unit (assuming each file is an .ino file), and so you don't need a declaration in the other tab to use the unitMin variable. You can just use the variable because Arduino will take care of the declaration for you.

The other tab is a .cpp file.

Change the declaration to:

const char unitMin[] PROGMEM = "min.";

and in the .cpp file:

extern const char *unitMin PROGMEM;

Now I get:

variable 'unitMin' must be const in order to be put into read-only section by means of '__attribute__((progmem))'

If you look in pgmspace.h at all the memcpy_P(), etc. functions, you will see that the pointer pasted in has no special designation, just a const char *

I don't think you need the PROGMEM attribute on your external declaration in you .cpp file. You have to know the string is in PROGMEM and call the appropriate function to read it from there.

1 Like

Does not seem to work.
In the .ino file:

const static char unitMin[] PROGMEM = "min.";

and in the .cpp file:

extern const char *unitMin;

give me this error:

C:\Users\dahls\AppData\Local\Temp\ccll7bDA.ltrans1.ltrans.o: In function `loop':

<artificial>:(.text+0x338c): undefined reference to `unitMin'

<artificial>:(.text+0x3390): undefined reference to `unitMin'

collect2.exe: error: ld returned 1 exit status

It work if I copy the line from the .ino file to the .cpp file as-is.

extern const char *unitMin PROGMEM;

This declares a variable unitMin, that contains a pointer to a const char, but does not specify that unitMin itself is const, preventing it from being stored in PROGMEM

extern const char* const unitMin PROGMEM;

This will eliminate the error message, but you will still have a problem because the original declaration of unitMin is as a char array, not a char*.

This should compile OK, have not actually written any working code to test it.

extern const char unitMin[] PROGMEM;

None of them compiles.

Post the complete, updated code that doesn't compiles.

Compiles but untested:
TimeStrings.h:

#ifndef TIME_STRINGS_H
#define TIME_STRINGS_H

#include <Arduino.h>

extern const char unitMin[];
extern const char unitSec[];
extern const char unitHour[];
extern const char unitDay[];

#endif

TimeStrings.cpp:

#include "TimeStrings.h"

const char unitMin[] PROGMEM = "min.";
const char unitSec[] PROGMEM = "sec.";
const char unitHour[] PROGMEM = "hours";
const char unitDay[] PROGMEM = "days";

Main .ino File:

#include "TimeStrings.h"
void anotherFunction();

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println((const __FlashStringHelper *)unitMin);
  Serial.println((const __FlashStringHelper *)unitSec);
  Serial.println((const __FlashStringHelper *)unitHour);
  Serial.println((const __FlashStringHelper *)unitDay);

  anotherFunction();
}

void loop() {
}

AnotherCppFile.cpp:

#include "TimeStrings.h"

void anotherFunction() {
  Serial.println((const __FlashStringHelper *)unitMin);
  Serial.println((const __FlashStringHelper *)unitSec);
  Serial.println((const __FlashStringHelper *)unitHour);
  Serial.println((const __FlashStringHelper *)unitDay);
}

Seems like it work when the string is defined on the .ccp file but not in the .ino file. Interesting.

Anyway - thanks.

If done correctly, it will work with them defined in the .ino. I just chose to use a separate .cpp file.

I think you've nailed the problem. PROGMEM really doesn't do anything for an external declaration. Also static cannot be used in the definition.. So:
const char unitMin[] PROGMEM = "min.";
and:
extern const char *unitMin;

I tried this and it compiles without errors or warnings.

It's a common, industry-wide practice to put the 'extern' declarations in a .h file ("TimeStrings.h" in my example) and #include that file in any .cpp (or .ino) that needs to access the global variables. Likewise, it's also common practice to #include that .h file in .cpp (or .ino) file where the global variables are actually defined. If you follow that paradigm, then the externs can't be:

extern const char *unitMin;

They must be:

extern const char unitMin[];

per my example.

That's true, of course, but I was just trying to solve the OPs problem which was to not use PROGMEM on the extern nor define the array as static. Basically combine the fix given in #4 and #7, which it appears he never tried.

And the issue of using header files is confounded by the Arduino IDE merging "iso" files, rearranging them and automatically adding declarations. I don't think Arduino (the organization) really wants people to use header files!

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.