tzapu / WiFiManager

ESP8266 WiFi Connection manager with web captive portal
http://tzapu.com/esp8266-wifi-connection-manager-library-arduino-ide/
MIT License
6.64k stars 1.98k forks source link

Using PROGMEM for string arrays #1587

Open dalbert2 opened 1 year ago

dalbert2 commented 1 year ago

Basic Infos

string arrays in wm_consts_en.h aren't being placed in PROGMEM properly

Hardware

WiFimanager Branch/Release: Master

Esp8266/Esp32: ESP8266

Hardware: ESP-12e

Core Version: 3.0.5

Description

Moving strings into PROGMEM is essential for ESP8266 where IRAM is extremely tight.

This code is incorrect (see wm_consts_en.h):

const uint8_t _nummenutokens = 11;
const char * const _menutokens[_nummenutokens] PROGMEM = {
    "wifi",
    "wifinoscan",
    "info",
    "param",
    "close",
    "restart",
    "exit",
    "erase",
    "update",
    "sep",
    "custom"
};

The above code places the pointers to the string constants in PROGMEM, but the string constants themselves land in RAM. The below code shows how to put both the pointers and the strings themselves in PROGMEM:

static const char _wifi_token[]       PROGMEM = "wifi";
static const char _wifinoscan_token[] PROGMEM = "wifinoscan";
static const char _info_token[]       PROGMEM = "info";
static const char _param_token[]      PROGMEM = "param";
static const char _close_token[]      PROGMEM = "close";
static const char _restart_token[]    PROGMEM = "restart";
static const char _exit_token[]       PROGMEM = "exit";
static const char _erase_token[]      PROGMEM = "erase";
static const char _update_token[]     PROGMEM = "update";
static const char _sep_token[]        PROGMEM = "sep";
static const char _custom_token[]     PROGMEM = "custom";
static PGM_P _menutokens[] PROGMEM = {
    _wifi_token,
    _wifinoscan_token,
    _info_token,
    _param_token,
    _close_token,
    _restart_token,
    _exit_token,
    _erase_token,
    _update_token,
    _sep_token,
    _custom_token
};
const uint8_t _nummenutokens = (sizeof(_menutokens) / sizeof(PGM_P));

I know this is ugly, but it works; if someone knows a prettier solution, please share it. At present, WiFiManager uses 1344 of IRAM even if you don't instantiate an instance of the class which is a problem for many applications on the ESP8266.

Settings in IDE

Sketch

Debug Messages

dalbert2 commented 1 year ago

Even worse, the string constants are all declared in header files which are included by WiFiManager.h so any class or module that includes WiFiManager.h creates a copy of all of the string constants. Why are these not defined in a compilation unit (.c or .cpp)?

tablatronix commented 1 year ago

What am I missing here? I am not seeing this memory waste

RAM:   [====      ]  40.0% (used 32732 bytes from 81920 bytes)
Flash: [====      ]  36.9% (used 385916 bytes from 1044464 bytes)
.pio/build/nodemcuv2/firmware.elf  :
section            size         addr
.data              1612   1073643520
.noinit              60   1073645132
.text               463   1074790400
.irom0.text      351480   1075843088
.text1            27993   1074790864
.rodata            4368   1073645200
.bss              26752   1073649568
.comment           5066            0
.xtensa.info         56            0                                                                                                                                                                              56            0

total 1366237

------------------------------------
BEFORE

RAM:   [====      ]  40.0% (used 32732 bytes from 81920 bytes)
Flash: [====      ]  36.9% (used 385836 bytes from 1044464 bytes)
.pio/build/nodemcuv2/firmware.elf  :
section            size         addr
.data              1612   1073643520
.noinit              60   1073645132
.text               463   1074790400
.irom0.text      351400   1075843088
.text1            27993   1074790864
.rodata            4368   1073645200
.bss              26752   1073649568
.comment           5066            0
.xtensa.info         56            0

total 1366157
dalbert2 commented 1 year ago

@tablatronix please see: https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html#how-do-i-declare-arrays-of-strings-in-progmem-and-retrieve-an-element-from-it

If you do this with the string arrays, you will see free heap increase.

I also think the strings need to be moved into a separate compilation unit (e.g. wm_strings.cpp). You can still use the pre-processor to select a language and a header file to define extern references for strings needed by other compilation units. Example:

strings.h

#pragma once
extern const char *_str_table[];

strings.c

#include "strings.h"

#ifdef LANG_ES
    static const char _hello[] = "hola";
    static const char _world[] = "mundo";
#else
    static const char _hello[] = "hello";
    static const char _world[] = "world";
#endif

const char *_str_table[] = { _hello, _world };

main.c:

#include <stdio.h>

#include "strings.h"

int main(int argc, char **argv) {
    printf("%s %s\n", _str_table[0], _str_table[1]);
    return 0;
}

Compile for english:gcc strings.c main.c Compile for spanish: gcc strings.c main.c -DLANG_ES