WifiManager custom parameters not working on ES32-S3

Hi, I was hoping someone could help me. I'm having problems using the custom parameters in WifiManager and getting them to save. I can sometimes get it to save once, but as soon as the ESP32 is restarted, the parameters revert back to their default values. For the record, I'm using an ESP32-S3 board made by YEJMKJ.

Could anyone tell me what I'm doing wrong, please? I'm very very frustrated :frowning:

Here is my code:

#include <WiFiManager.h>          
#include <String.h>               
#include <ArduinoJson.h>          
  #ifdef ESP32  
    #include <SPIFFS.h>
  #endif




// Global Variables
  WiFiClient client;
  char servername[]="api.openweathermap.org";              
  String result;



// WifiManager webpage settings default variables
  char latitude[12] = "44.5902";
  char longitude[12] = "-104.7152";
  String apiKey = "xxxxx";



// Flag for saving data 
  bool shouldSaveConfig = false;




// Callback notifying us of the need to save config 
void saveConfigCallback () {
  Serial.println("Should save config");
  shouldSaveConfig = true;
}





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

  Serial.println("Hi there!  Starting up...");


  //  clean FS, for testing
    //  SPIFFS.format();



  //read configuration from FS json
  Serial.println("mounting FS...");

  if (SPIFFS.begin()) {
    Serial.println("mounted file system");
    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading

      Serial.println("reading config file");
      File configFile = SPIFFS.open("/config.json", "r");
      if (configFile) {
        Serial.println("opened config file");
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);

        #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
          DynamicJsonDocument json(1024);
          auto deserializeError = deserializeJson(json, buf.get());
          serializeJson(json, Serial);
          if ( ! deserializeError ) {
        #else
          DynamicJsonBuffer jsonBuffer;
          JsonObject& json = jsonBuffer.parseObject(buf.get());
          json.printTo(Serial);
          if (json.success()) {
        #endif
          Serial.println("\nparsed json");
          strcpy(latitude, json["latitude"]);
          strcpy(longitude, json["longitude"]);  
          Serial.println("Read variables");
        } else {
          Serial.println("failed to load json config");
        }
        configFile.close();
      }
    }
  } else {
    Serial.println("failed to mount FS");
  }
  //end read

  // The extra parameters to be configured (can be either global or just in the setup)
  // After connecting, parameter.getValue() will get you the configured value
  // id/name placeholder/prompt default length
  WiFiManagerParameter custom_latitude_token("latitude", "Latitude", latitude, 10);
  WiFiManagerParameter custom_longitude_token("longitude", "Longitude", longitude, 10);
  WiFiManagerParameter custom_key("apikey", "API Key", apiKey.c_str(), 32);

  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;

 

  //add all your parameters here
  wifiManager.addParameter(&custom_latitude_token);
  wifiManager.addParameter(&custom_longitude_token);
  wifiManager.addParameter(&custom_key);

  //reset settings - for testing
  //wifiManager.resetSettings();

  //set minimu quality of signal so it ignores AP's under that quality
  //defaults to 8
  //wifiManager.setMinimumSignalQuality();

  //sets timeout until configuration portal gets turned off
  //useful to make it all retry or go to sleep
  //in seconds
  //wifiManager.setTimeout(120);

  //fetches ssid and pass and tries to connect
  //if it does not connect it starts an access point with the specified name
  //here  "AutoConnectAP"
  //and goes into a blocking loop awaiting configuration
  if (!wifiManager.autoConnect("WeatherDisplay", "")) {
    Serial.println("failed to connect and hit timeout");
    delay(3000);
    //reset and try again, or maybe put it to deep sleep
    ESP.restart();
    delay(5000);
  }

  //if you get here you have connected to the WiFi
  Serial.println("connected...yeey :)");

  //read updated parameters
  strcpy(latitude, custom_latitude_token.getValue());
  strcpy(longitude, custom_longitude_token.getValue());
  apiKey = custom_key.getValue();
  //strcpy(apiKey, custom_key.getValue());
  Serial.println("The values in the file are: ");
  Serial.println("\tLatitude : " + String(custom_latitude_token.getValue()));
  Serial.println("\tLongitude : " + String(custom_longitude_token.getValue()));
  Serial.println("\API Key : " + String(custom_key.getValue()));

  Serial.println("The new variables are: ");
  Serial.println("\tLatitude : " + String(latitude));
  Serial.println("\tLongitude : " + String(longitude));
  Serial.println("\API Key : " + String(apiKey));


  //save the custom parameters to FS
  if (shouldSaveConfig) {
    Serial.println("saving config");
 #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
    DynamicJsonDocument json(1024);
#else
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
#endif

    json["latitude"] = latitude;
    json["longitude"] = longitude;
    json["apiKey"] = apiKey;

    File configFile = SPIFFS.open("/config.json", "w");
    if (!configFile) {
      Serial.println("failed to open config file for writing");
    }

#if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
    serializeJson(json, Serial);
    serializeJson(json, configFile);
#else
    json.printTo(Serial);
    json.printTo(configFile);
#endif
    configFile.close();
    //end save
  }

  Serial.println("local ip");
  Serial.println(WiFi.localIP());  


   //set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);
}



void loop()
{
  
}           

The immediate problem is that the notify callback is set way too late, at the bottom of the setup function. wifiManager is active only during the call to autoConnect, acting as a captive portal and displaying its UI. As the comment before that line says

//and goes into a blocking loop awaiting configuration

So you never get the call to save, and flip the shouldSaveConfig flag. Move the setSaveConfigCallback before the wifiManager.addParameter block.

When it says what's happening with the parameters

  //read updated parameters
  strcpy(latitude, custom_latitude_token.getValue());
  strcpy(longitude, custom_longitude_token.getValue());
  apiKey = custom_key.getValue();
  //strcpy(apiKey, custom_key.getValue());
  Serial.println("The values in the file are: ");
  Serial.println("\tLatitude : " + String(custom_latitude_token.getValue()));
  Serial.println("\tLongitude : " + String(custom_longitude_token.getValue()));
  Serial.println("\API Key : " + String(custom_key.getValue()));

  Serial.println("The new variables are: ");
  Serial.println("\tLatitude : " + String(latitude));
  Serial.println("\tLongitude : " + String(longitude));
  Serial.println("\API Key : " + String(apiKey));

The three blocks here are in the wrong order. It

  1. Overwrites the variables read from the file (if any) with the parameters
  2. Prints the same parameters
  3. Prints the variables, which have the same values

It's also missing the t for a tab with the "API Key". It should be more like

  Serial.println("The values in the file were: ");
  Serial.println("\tLatitude : " + String(latitude));
  Serial.println("\tLongitude : " + String(longitude));
  Serial.println("\tAPI Key : " + String(apiKey));

  //read updated parameters
  strcpy(latitude, custom_latitude_token.getValue());
  strcpy(longitude, custom_longitude_token.getValue());
  apiKey = custom_key.getValue();
  //strcpy(apiKey, custom_key.getValue());

  Serial.println("The new variables are: ");
  Serial.println("\tLatitude : " + String(latitude));
  Serial.println("\tLongitude : " + String(longitude));
  Serial.println("\tAPI Key : " + String(apiKey));

The lat/log are char[], but the API key is -- at least temporarily -- a String. You should be able to use all char[]. And in that case, use printf with %s and \n to print them.

Make sure the size of the char[] matches the length passed to WiFiManagerParameter constructor. If the length of the parameters for the lat/long is 10, then you need one more for the NUL terminator: char[11]. You can avoid manually synchronizing those by passing sizeof(latitude) - 1 as the length instead.

Are you separately formatting the SPIFFS volume? If not then you need to pass true here

  if (SPIFFS.begin(true)) {

When parsing the JSON, the apiKey is not copied. For these, you should use strlcpy, because it's possible there is some change -- on purpose or by accident -- and the values in the JSON file are too long.

  strlcpy(latitude,  json["latitude"],  sizeof(latitude));
  strlcpy(longitude, json["longitude"], sizeof(longitude));
  strlcpy(apiKey,    json["apiKey"],    sizeof(apiKey));

strcpy is OK to use when copying from the parameters, since those are bounded by the lengths that were passed. (And don't use strncpy because that does the wrong thing when the source string is too long.)

WiFiManager uses ESP32's WiFi config to store the SSID and Password. To test your save code, you can clear it intentionally at the beginning of setup so that the captive portal opens automatically.

  wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
  esp_wifi_init(&wifi_init_config);
  wifi_config_t config;
  esp_wifi_get_config(WIFI_IF_STA, &config);
  Serial.printf("SSID before clearing: %s\n", config.sta.ssid);
  config.sta.ssid[0] = 0;
  config.sta.password[0] = 0;
  esp_wifi_set_config(WIFI_IF_STA, &config);

Those functions need

#include <esp_wifi.h>

and both can be inside #ifdef ESP32 blocks. BTW, what is

#include <String.h>

for? Arduino's String is included automatically, and it's in WString.h.

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