Arduino: Using PSTR and PROGMEM string with template class causes section type conflict

Created on 23 Jun 2017  路  4Comments  路  Source: esp8266/Arduino

Using latest git and arduino IDE.

I use PROGMEM strings and a debug define that places debug messages into progmem, however this does not seem to work when combined with templated classes.

The error is the same as this issue: https://github.com/esp8266/Arduino/issues/2078

i define my debug as

#define DEBUG(_1, ...) { TEST.printf_P( PSTR(_1) ,##__VA_ARGS__); }

and a const char as

const char testString[] PROGMEM = "Test String"; 

Individually they both compile. i.e. if the debug define is enabled but i do not use testString. it works, and vice versa. but if i use both

In file included from /Users/amelvin/Documents/Arduino/templatesketch/templatesketch.ino:2:0:
test.h:7: error: testString causes a section type conflict with __c
 const char testString[] PROGMEM = "Test String"; 
            ^
In file included from /Applications/Arduino.app/Contents/Java/hardware/esp8266com/esp8266/cores/esp8266/Arduino.h:240:0,
                 from /var/folders/nv/8j3v5v0s18v7p4249kv8qbz80000gq/T/arduino_build_216642/sketch/templatesketch.ino.cpp:1:
/Applications/Arduino.app/Contents/Java/hardware/esp8266com/esp8266/cores/esp8266/pgmspace.h:21:51: note: '__c' was declared here
 #define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
                                                   ^
/var/folders/nv/8j3v5v0s18v7p4249kv8qbz80000gq/T/arduino_build_216642/sketch/test.h:11:41: note: in expansion of macro 'PSTR'
 #define DEBUG(_1, ...) { TEST.printf_P( PSTR(_1) ,##__VA_ARGS__); }
                                         ^
/var/folders/nv/8j3v5v0s18v7p4249kv8qbz80000gq/T/arduino_build_216642/sketch/test.hpp:5:3: note: in expansion of macro 'DEBUG'
   DEBUG("abc");
   ^
exit status 1
testString causes a section type conflict with __c

A full working example can be found here
https://gist.github.com/sticilface/9a6410978d7235a469c1e154c1c4c396

Most helpful comment

I have recently encountered this problem and I may have a solution. 馃槃

Background

The background of my issue is that, I have been using custom debug logging macros, something like:

DEBUG_LOG("Blah blah %d ...", ValueX);

I have so many of them and the string literals starts to eat up the RAM, obviously transit to PROGMEM is the solution.
And I want to leverage on my use of logging macro to reduce the effort, by converting:

#define DEBUG_LOG(...) Serial.printf(__VA_ARGS__)

to

#define DEBUG_LOG(fmt, ...) \
{ static const char pfmt[] PROGMEM = fmt; Serial.printf_P(pfmt, ## __VA_ARGS__); }

And I encountered the infamous error "pfmt causes a section type conflict with pfmt", because I have debug logs in both "regular" functions and template functions.

The solution of extracting each and every log string into a global static variable is not acceptable to me - not only too much transitional effort, but also increase the maintenance overhead and reduce the usability. (Imagine the scenario you see a debug log line, and wants to look at the relevant code. With inline string you just do a single search. With extracted variable, you first find the variable, and you have to search the variable name again to locate the source...)

Reasoning

The post here gave me an idea of the root cause, and then I dug out "eagle.app.v6.common.ld" with an interesting line:

.irom0.text : ALIGN(4)
{
...
    *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text .irom.text.*)
...
}

The .irom.text.* gave me an idea:

  • According to this document, PROGMEM translates to ICACHE_RODATA_ATTR translates to __attribute__((section(".irom.text")))
  • Of course, it is a conflict to merge into a single section from template code with GROUP attribute and non-template code without GROUP attribute
  • What if I define a new section, say .irom.text.template to hold all flash strings from template code?

    • First, there should not be any conflict

    • Second, this section will be put into flash because of the .irom.text.* specification

Solution

So I tried the following:

#define PROGMEM_T __attribute__((section(".irom.text.template")))
#define DEBUG_LOG_T(fmt, ...) \
{ static const char pfmt[] PROGMEM_T = fmt; Serial.printf_P(pfmt, ## __VA_ARGS__); }

And for all debug log states in template method I switched from:

DEBUG_LOG("Blah blah %d ...", ValueX);

to

DEBUG_LOG_T("Blah blah %d ...", ValueX);

And the code compiles and runs perfectly fine! 馃槅
It is still a little more work than just changing the macro, but with a reasonable cost, at least it allows to use inline flash string in log statements.

All 4 comments

2078

3351

I found the first one, but I'm not sure i follow. As long as i don't use the debug define it works. so it can link even when the progmem is used inside a templated class. or is that where the second one enters the picture and you can't use PROGMEM and FPSTR inside same file?

Ok got it to work. Need to move the implementation of the PROGMEM string from the .h file to a .cpp file, and extern it in the header. Then it compiles fine.

in .h:

namespace ESPmanagerinternals {
extern const char key_networks[]; 
}

in .cpp

const char ESPmanagerinternals::key_networks[] PROGMEM = "networks";

I have recently encountered this problem and I may have a solution. 馃槃

Background

The background of my issue is that, I have been using custom debug logging macros, something like:

DEBUG_LOG("Blah blah %d ...", ValueX);

I have so many of them and the string literals starts to eat up the RAM, obviously transit to PROGMEM is the solution.
And I want to leverage on my use of logging macro to reduce the effort, by converting:

#define DEBUG_LOG(...) Serial.printf(__VA_ARGS__)

to

#define DEBUG_LOG(fmt, ...) \
{ static const char pfmt[] PROGMEM = fmt; Serial.printf_P(pfmt, ## __VA_ARGS__); }

And I encountered the infamous error "pfmt causes a section type conflict with pfmt", because I have debug logs in both "regular" functions and template functions.

The solution of extracting each and every log string into a global static variable is not acceptable to me - not only too much transitional effort, but also increase the maintenance overhead and reduce the usability. (Imagine the scenario you see a debug log line, and wants to look at the relevant code. With inline string you just do a single search. With extracted variable, you first find the variable, and you have to search the variable name again to locate the source...)

Reasoning

The post here gave me an idea of the root cause, and then I dug out "eagle.app.v6.common.ld" with an interesting line:

.irom0.text : ALIGN(4)
{
...
    *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text .irom.text.*)
...
}

The .irom.text.* gave me an idea:

  • According to this document, PROGMEM translates to ICACHE_RODATA_ATTR translates to __attribute__((section(".irom.text")))
  • Of course, it is a conflict to merge into a single section from template code with GROUP attribute and non-template code without GROUP attribute
  • What if I define a new section, say .irom.text.template to hold all flash strings from template code?

    • First, there should not be any conflict

    • Second, this section will be put into flash because of the .irom.text.* specification

Solution

So I tried the following:

#define PROGMEM_T __attribute__((section(".irom.text.template")))
#define DEBUG_LOG_T(fmt, ...) \
{ static const char pfmt[] PROGMEM_T = fmt; Serial.printf_P(pfmt, ## __VA_ARGS__); }

And for all debug log states in template method I switched from:

DEBUG_LOG("Blah blah %d ...", ValueX);

to

DEBUG_LOG_T("Blah blah %d ...", ValueX);

And the code compiles and runs perfectly fine! 馃槅
It is still a little more work than just changing the macro, but with a reasonable cost, at least it allows to use inline flash string in log statements.

Was this page helpful?
0 / 5 - 0 ratings