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

Value for Checkboxes overwritten - "Advanced" example #1111

Open Lithimlin opened 4 years ago

Lithimlin commented 4 years ago

Basic Infos

Hardware

WiFimanager Branch/Release:

Esp8266/Esp32:

Hardware: ESP-12e, esp01, esp25

ESP Core Version: 2.4.0, staging

Description

I've tried using the checkboxes (as in the "Advanced" example) to enable and disable things on my ESP. While doing that I figured out that once you disable a checkbox and exit the configuration or let the AP timeout, the value saved in the WiFiManagerParameter is lost and the checkbox will not work again.

How to reproduce

  1. Open the "Advanced" example
  2. Enable the checkbox (line 30) and commented the radioboxes (lines 33f)
  3. Upload the sketch
  4. Connect to the AP
  5. In the Params, click savewithout enabling the checkbox
  6. Close the AP or let it timeout
  7. Open the AP again by pressing the button
  8. Try enabling the checkbox and saving

The value of the checkbox will remain empty even if it is checked

Sketch

"Advanced" example:


/**
 * WiFiManager advanced demo, contains advanced configurartion options
 * Implements TRIGGEN_PIN button press, press for ondemand configportal, hold for 3 seconds for reset settings.
 */
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager

#define TRIGGER_PIN 0

WiFiManager wm; // global wm instance
WiFiManagerParameter custom_field; // global param ( for non blocking w params )

void setup() {
  WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP  
  Serial.begin(115200);
  Serial.setDebugOutput(true);  
  delay(3000);
  Serial.println("\n Starting");

  pinMode(TRIGGER_PIN, INPUT);

  // wm.resetSettings(); // wipe settings

  // add a custom input field
  int customFieldLength = 40;

  // new (&custom_field) WiFiManagerParameter("customfieldid", "Custom Field Label", "Custom Field Value", customFieldLength,"placeholder=\"Custom Field Placeholder\"");

  // test custom html input type(checkbox)
  new (&custom_field) WiFiManagerParameter("customfieldid", "Custom Field Label", "Custom Field Value", customFieldLength,"placeholder=\"Custom Field Placeholder\" type=\"checkbox\""); // custom html type

  // test custom html(radio)
  // const char* custom_radio_str = "<br/><label for='customfieldid'>Custom Field Label</label><input type='radio' name='customfieldid' value='1' checked> One<br><input type='radio' name='customfieldid' value='2'> Two<br><input type='radio' name='customfieldid' value='3'> Three";
  // new (&custom_field) WiFiManagerParameter(custom_radio_str); // custom html input

  wm.addParameter(&custom_field);
  wm.setSaveParamsCallback(saveParamCallback);

  // custom menu via array or vector
  // 
  // menu tokens, "wifi","wifinoscan","info","param","close","sep","erase","restart","exit" (sep is seperator) (if param is in menu, params will not show up in wifi page!)
  // const char* menu[] = {"wifi","info","param","sep","restart","exit"}; 
  // wm.setMenu(menu,6);
  std::vector<const char *> menu = {"wifi","info","param","sep","restart","exit"};
  wm.setMenu(menu);

  // set dark theme
  wm.setClass("invert");

  //set static ip
  // wm.setSTAStaticIPConfig(IPAddress(10,0,1,99), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); // set static ip,gw,sn
  // wm.setShowStaticFields(true); // force show static ip fields
  // wm.setShowDnsFields(true);    // force show dns field always

  // wm.setConnectTimeout(20); // how long to try to connect for before continuing
  wm.setConfigPortalTimeout(30); // auto close configportal after n seconds
  // wm.setCaptivePortalEnable(false); // disable captive portal redirection
  // wm.setAPClientCheck(true); // avoid timeout if client connected to softap

  // wifi scan settings
  // wm.setRemoveDuplicateAPs(false); // do not remove duplicate ap names (true)
  // wm.setMinimumSignalQuality(20);  // set min RSSI (percentage) to show in scans, null = 8%
  // wm.setShowInfoErase(false);      // do not show erase button on info page
  // wm.setScanDispPerc(true);       // show RSSI as percentage not graph icons

  // wm.setBreakAfterConfig(true);   // always exit configportal even if wifi save fails

  bool res;
  // res = wm.autoConnect(); // auto generated AP name from chipid
  // res = wm.autoConnect("AutoConnectAP"); // anonymous ap
  res = wm.autoConnect("AutoConnectAP","password"); // password protected ap

  if(!res) {
    Serial.println("Failed to connect or hit timeout");
    // ESP.restart();
  } 
  else {
    //if you get here you have connected to the WiFi    
    Serial.println("connected...yeey :)");
  }
}

void checkButton(){
  // check for button press
  if ( digitalRead(TRIGGER_PIN) == LOW ) {
    // poor mans debounce/press-hold, code not ideal for production
    delay(50);
    if( digitalRead(TRIGGER_PIN) == LOW ){
      Serial.println("Button Pressed");
      // still holding button for 3000 ms, reset settings, code not ideaa for production
      delay(3000); // reset delay hold
      if( digitalRead(TRIGGER_PIN) == LOW ){
        Serial.println("Button Held");
        Serial.println("Erasing Config, restarting");
        wm.resetSettings();
        ESP.restart();
      }

      // start portal w delay
      Serial.println("Starting config portal");
      wm.setConfigPortalTimeout(120);

      if (!wm.startConfigPortal("OnDemandAP","password")) {
        Serial.println("failed to connect or hit timeout");
        delay(3000);
        // ESP.restart();
      } else {
        //if you get here you have connected to the WiFi
        Serial.println("connected...yeey :)");
      }
    }
  }
}

String getParam(String name){
  //read parameter from server, for customhmtl input
  String value;
  if(wm.server->hasArg(name)) {
    value = wm.server->arg(name);
  }
  return value;
}

void saveParamCallback(){
  Serial.println("[CALLBACK] saveParamCallback fired");
  Serial.println("PARAM customfieldid = " + getParam("customfieldid"));
}

void loop() {
  checkButton();
  // put your main code here, to run repeatedly:
}

Debug Messages

Starting
*WM: [3] allocating params bytes: 20
*WM: [2] Added Parameter: customfieldid
*WM: [1] AutoConnect 
*WM: [1] No Credentials are Saved, skipping connect 
*WM: [2] Starting Config Portal 
*WM: [2] AccessPoint set password is VALID 
*WM: [1] password 
*WM: [3] WiFi station disconnect 
*WM: [3] WiFi_enableSTA enable
*WM: [2] Disabling STA 
*WM: [2] Enabling AP 
*WM: [1] StartAP with SSID:  AutoConnectAP
*WM: [1] SoftAP Configuration 
*WM: [1] -------------------- 
*WM: [1] ssid:             AutoConnectAP
*WM: [1] password:         password
*WM: [1] ssid_len:         13
*WM: [1] channel:          1
*WM: [1] authmode:         3
*WM: [1] ssid_hidden:     
*WM: [1] max_connection:   4
*WM: [1] country:          CN
*WM: [1] beacon_interval:  100(ms)
*WM: [1] -------------------- 
*WM: [1] AP IP address: 192.168.4.1
*WM: [3] setupConfigPortal 
*WM: [1] Starting Web Portal 
*WM: [3] dns server started with ip:  192.168.4.1
*WM: [2] HTTP server started 
*WM: [2] WiFi Scan completed in 2905 ms
*WM: [2] Config Portal Running, blocking, waiting for clients... 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [2] <- HTTP Root 
*WM: [3] -> 192.168.4.1 
*WM: [3] lastconxresulttmp: WL_IDLE_STATUS
*WM: [3] lastconxresult: WL_DISCONNECTED
*WM: [2] Scan is cached 7147 ms ago
*WM: [3] -> 192.168.4.1 
*WM: [2] <- HTTP Param 
*WM: [3] lastconxresulttmp: WL_IDLE_STATUS
*WM: [3] lastconxresult: WL_DISCONNECTED
*WM: [3] Sent param page 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [2] <- HTTP WiFi save  
*WM: [3] Method: POST
*WM: [2] Parameters 
*WM: [2] -------------------- 
*WM: [2] customfieldid: 
*WM: [2] -------------------- 
[CALLBACK] saveParamCallback fired
PARAM customfieldid = 
*WM: [3] Sent param save page 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [2] <- HTTP Exit 
*WM: [3] configportal abort 
*WM: [2] disconnect configportal 
*WM: [2] restoring usermode STA
*WM: [2] wifi status: WL_DISCONNECTED
*WM: [2] wifi mode: STA
*WM: [1] config portal exiting 
Failed to connect or hit timeout
Button Pressed
Starting config portal
*WM: [2] Starting Config Portal 
*WM: [2] AccessPoint set password is VALID 
*WM: [1] password 
*WM: [3] WiFi station disconnect 
*WM: [3] WiFi_enableSTA enable
*WM: [2] Disabling STA 
*WM: [2] Enabling AP 
*WM: [1] StartAP with SSID:  OnDemandAP
*WM: [1] SoftAP Configuration 
*WM: [1] -------------------- 
*WM: [1] ssid:             OnDemandAP
*WM: [1] password:         password
*WM: [1] ssid_len:         10
*WM: [1] channel:          1
*WM: [1] authmode:         3
*WM: [1] ssid_hidden:     
*WM: [1] max_connection:   4
*WM: [1] country:          CN
*WM: [1] beacon_interval:  100(ms)
*WM: [1] -------------------- 
*WM: [1] AP IP address: 192.168.4.1
*WM: [3] setupConfigPortal 
*WM: [1] Starting Web Portal 
*WM: [3] dns server started with ip:  192.168.4.1
*WM: [2] HTTP server started 
*WM: [2] WiFi Scan completed in 3106 ms
*WM: [2] Config Portal Running, blocking, waiting for clients... 
*WM: [2] Portal Timeout In 115 seconds
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [2] <- HTTP Root 
*WM: [3] -> 192.168.4.1 
*WM: [3] lastconxresulttmp: WL_IDLE_STATUS
*WM: [3] lastconxresult: WL_DISCONNECTED
*WM: [2] Scan is cached 8867 ms ago
*WM: [3] -> 192.168.4.1 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [2] <- HTTP Param 
*WM: [3] lastconxresulttmp: WL_IDLE_STATUS
*WM: [3] lastconxresult: WL_DISCONNECTED
*WM: [3] Sent param page 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [2] <- HTTP WiFi save  
*WM: [3] Method: POST
*WM: [2] Parameters 
*WM: [2] -------------------- 
*WM: [2] customfieldid: 
*WM: [2] -------------------- 
[CALLBACK] saveParamCallback fired
PARAM customfieldid = 
*WM: [3] Sent param save page 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [3] -> connectivitycheck.platform.hicloud.com 
*WM: [2] <- Request redirected to captive portal 
*WM: [2] <- HTTP WiFi save  
*WM: [3] Method: POST
*WM: [2] Parameters 
*WM: [2] -------------------- 
*WM: [2] customfieldid: 
*WM: [2] -------------------- 
[CALLBACK] saveParamCallback fired
PARAM customfieldid = 
*WM: [3] Sent param save page 
Lithimlin commented 4 years ago

Any idea what could be causing this? I'm trying to understand the library atm to see if I find the issue and am possibly able to fix it.

Lithimlin commented 4 years ago

So the issue seems to lie with this method:

void WiFiManager::doParamSave(){
   // @todo use new callback for before paramsaves, is this really needed?
  if ( _presavecallback != NULL) {
    _presavecallback();
  }

  //parameters
  if(_paramsCount > 0){
    DEBUG_WM(DEBUG_VERBOSE,F("Parameters"));
    DEBUG_WM(DEBUG_VERBOSE,FPSTR(D_HR));

    for (int i = 0; i < _paramsCount; i++) {
      if (_params[i] == NULL || _params[i]->_length == 0) {
        DEBUG_WM(DEBUG_ERROR,"[ERROR] WiFiManagerParameter is out of scope");
        break; // @todo might not be needed anymore
      }
      //read parameter from server
      String name = (String)FPSTR(S_parampre)+(String)i;
      String value;
      if(server->hasArg(name)) {
        value = server->arg(name);
      } else {
        value = server->arg(_params[i]->getID());
      }

      //store it in params array
      value.toCharArray(_params[i]->_value, _params[i]->_length+1); // length+1 null terminated
      DEBUG_WM(DEBUG_VERBOSE,(String)_params[i]->getID() + ":",value);
    }
    DEBUG_WM(DEBUG_VERBOSE,FPSTR(D_HR));
  }

   if ( _saveparamscallback != NULL) {
    _saveparamscallback();
  }

}

and the saveParamCallback in the example. I added two lines to reset the value afterwards in the example:

void saveParamCallback(){
  Serial.println("[CALLBACK] saveParamCallback fired");
  Serial.println("PARAM customfieldid = " + getParam("customfieldid"));
  String value = "Custom Field Value";
  custom_field.setValue(value.c_str(), value.length());
}
lchamiso commented 3 years ago

I have the same problem. Someone managed to solve?

tablatronix commented 3 years ago

is it an issue with ids? There was a similar issue reported

Lithimlin commented 3 years ago

iirc, this was indeed an issue with the IDs. I think #1130 fixed it. Though it's been a while so no guarantees.

iPhilBln commented 1 year ago

I know this thread is old but i run into the same issue. I have some checkboxes with const hidden ip address but if the user don't enable the check box the value of this uncheck checkbox will be overwritten with an empty string. So I don't thing this issue is fixed.

Maybe its not important but all parameters are global.

main.cpp

    const char* title_str = "<br/><label style='font-weight: bold; font-size: 25px;'>NTP Server Auswahl</label><br/>";
    const char* lineBreak_str = "<br/><br />";
    const char* tu_berlin_ip    = "130.149.7.7";
    const char* tu_dresden_ip   = "141.76.32.160";
    const char* t_online_ip     = "194.25.134.196";
    const char* custom_0_ip     = "000.000.000.000";

    new (&title) WiFiManagerParameter (title_str);
    new (&tu_berlin_ntp_server) WiFiManagerParameter("tu_berlin_ntp_server", "TU Berlin", tu_berlin_ip, strlen(tu_berlin_ip), checkbox_unchecked, WFM_LABEL_AFTER);      // custom html input for tu berlin ntp server
    new (&tu_dresden_ntp_server) WiFiManagerParameter("tu_dresden_ntp_server", "TU Dresden", tu_dresden_ip, strlen(tu_dresden_ip), checkbox_unchecked, WFM_LABEL_AFTER);    // custom html input for tu dresden ntp server
    new (&t_online_ntp_server) WiFiManagerParameter("t_online_ntp_server", "T-Online", t_online_ip, strlen(t_online_ip),checkbox_checked, WFM_LABEL_AFTER);        // custom html input for t-online ntp server
    new (&lineBreak) WiFiManagerParameter (lineBreak_str);
    new (&custom_0_ntp_server) WiFiManagerParameter("custom_0_ntp_server", "NTP Server IP:", NULL, strlen(custom_0_ip), "placeholder=\"000.000.000.000\"");

    wm.addParameter(&title);
    wm.addParameter(&tu_berlin_ntp_server);    
    wm.addParameter(&tu_dresden_ntp_server);
    wm.addParameter(&t_online_ntp_server);
    wm.addParameter(&lineBreak);
    wm.addParameter(&custom_0_ntp_server);

    wm.setPreSaveParamsCallback(saveParamCallback);
    wm.setSaveParamsCallback(saveParamCallback);

callback function

const char* getParam(const char* name){
    //read parameter from server, for customhmtl input
    if (wm.server->hasArg(name)) {
        String value = wm.server->arg(name);
        return value.length() > 0 ? value.c_str() : nullptr;
    } 
    else {
        return nullptr;
    }
}

void saveParamCallback(void) {

    static char*   lost_ips[3];
    static int     lost_len[3];
    uint8_t        lost_cnt = 0;

    static bool preSave = true;
    preSave ? Serial.println("[CALLBACK] savePreParamCallback fired") : Serial.println("[CALLBACK] saveParamCallback fired");
    preSave = ! preSave;

    for (uint8_t i = 0; i < wm.getParametersCount(); i++) {
        const char* name = wm.getParameters()[i]->getID();
        if (name) {
            const char* param = getParam(name);

            if (param) {
                Serial.println(String(i) + " was checked");
                Serial.print("PARAM[" + String(name) + "]: ");
                Serial.println(param);

                // hier dann die ntp client objecte erstellen
            }
            else {
                const char* value = wm.getParameters()[i]->getValue();
                int         length = wm.getParameters()[i]->getValueLength();
                Serial.println(String(i) + " was nullptr");
                Serial.println("val: " + String(value)); 
                Serial.println("len: " + String(length));
                //wm.getParameters()[i]->setValue(value,length);
            }
        }
        else {
            Serial.println(String(i) + " has no id");
        }
        Serial.println();
    }
}

LOG output

// const hidden ip addresses 
130.149.7.7
141.76.32.160
194.25.134.196

// before the data are saved from webserver
[CALLBACK] savePreParamCallback fired
0 has no id

1 was nullptr
val: 130.149.7.7
len: 11

2 was nullptr
val: 141.76.32.160
len: 13

3 was checked
PARAM[t_online_ntp_server]: PARAM[t_online_ntp_server]: 

4 has no id

5 was nullptr
val: 
len: 15

// after the data are saved from webserver
[CALLBACK] saveParamCallback fired
0 has no id

1 was nullptr
val: 
len: 11

2 was nullptr
val: 
len: 13

3 was checked
PARAM[t_online_ntp_server]: PARAM[t_online_ntp_server]: 

4 has no id

5 was nullptr
val: 
len: 15