Flash wearing by WiFi.setStaticIP(..)/WiFi.useStaticIP()?


#1

Hi,
I wonder if WiFi.setStaticIP(…) and WiFi.useStaticIP() have a problem with flash wearing, or is it save to call these functions on each startup?

I tried to read WiFi.subnetMask(), WiFi.gatewayIP and similar, to check if the above commands are neccessary, but they always return 0.0.0.0 - so I found no way to check if useStaticIP() and setStaticIP(…) are needed on startup. Reading the docs I got the impression that this is not the intended way. :confused:

(Oh by the way. I’m using a photon 0.6.2)

Thanks for any response
Pixinger


#2

Hi,

Not sure if there is someone interested in this problem. I just wanted to let you know my current status of this.
I received the follwing message from the particle support team:

May 18, 02:37 PDT
Normally you would only call them once because the setting is saved in configuration flash. Then you could later flash normal code that does not set the IP address.

However I believe that they check to see if the value changed before writing to the flash, so they probably won’t cause flash wear.

So at least I have to stay with a “belive” :innocent: that it will have no impact on flash wearing.
To circumvent the problem decided to read/write the current configuration to EEPROM.
Here is the extracted code snippet for the interested ones.

class EEPROMManager
{
private:
  struct Data
  {
    uint32_t IP;
    uint32_t Subnetmask;
    uint32_t Gateway;
    uint32_t Dns;
  };
private:
  int _Address;
public:
  EEPROMManager(int address)
  {
    _Address = address;
  }
public:
  bool Compare(const IPAddress  &ip, const IPAddress &subnetmask, const IPAddress &gateway, const IPAddress &dns) const
  {
    // Read data from EEPROM
    Data data;
    EEPROM.get(_Address, data);
    // convert uint32_t to IPAddress, that will make comparison easier
    IPAddress eepromIP(data.IP);
    IPAddress eepromSubnetmask(data.Subnetmask);
    IPAddress eepromGateway(data.Gateway);
    IPAddress eepromDns(data.Dns);
    // compare
    bool result = (eepromIP == ip) && (eepromSubnetmask == subnetmask) && (eepromGateway == gateway) && (eepromDns == dns);
    return result;
  }
  void Update(const IPAddress &ip, const IPAddress &subnetmask, const IPAddress &gateway, const IPAddress &dns)
  {
    // Prepare data struct, copy the address information to uint32_t. 
    Data data;
    data.IP = ip.raw().ipv4; // I know thats not very elegant. But I found no other way to convert here...
    data.Subnetmask = subnetmask.raw().ipv4;
    data.Gateway = gateway.raw().ipv4;
    data.Dns = dns.raw().ipv4;
    // Write the struct to EEPROM
    EEPROM.put(_Address, data);
  }
};


// -------------------------------------------------------------------------------------------
// put that code where you want to check and set the WiFi address, maybe in the setup() method.
EEPROMManager eepromManager(0); // 0 is the base address of the EEPROM, you can modify it to your liking

// Validate if that address information is up tp date
if (!eepromManager.Compare(_BasisIP, _SubnetMask, _Gateway, _DNS))
{
  //... no it's not. Write the new one to EEPROM and WiFi.
  eepromManager.Update(_BasisIP, _SubnetMask, _Gateway, _DNS);
  WiFi.setStaticIP(_BasisIP, _SubnetMask, _Gateway, _DNS);
  WiFi.useStaticIP();  // now let's use the configured IP
}
else
{
   // no action required here. All should be fine.
}

BTW: I know, the conversion ip.raw().ipv4 looks interesting. But I did not find a more elegant way to convert the IPAddress to a EEPROM compatible format.

Have fun,
Pix


#3

Hi, I’m back again :wink:

The “belive” troubled me a little bit. So I started to roam through the git sources of https://github.com/spark/firmware, searching for an definite answer:

  • Is WiFi.setStaticIP(…) writing each time the settings to the flash, or not?

I think I traced down the correct function and it seems to be, that it is writing the configuration EACH time I call WiFi.setStaticIP(…). As I’m not experienced in photon photon firmware programming, the next question is: Am I right, or do I missed something?

This will be a question to a more experienced person than me.
What I found is in the file firmware\hal\src\photon\wlan_hal.cpp (line 788-800):

// That will be needed by wlan_set_ipaddress(..)
void assign_if_set(dct_ip_address_v4_t& dct_address, const HAL_IPAddress* address)
{
    if (address && is_ipv4(address))
    {
            dct_address = address->ipv4;
    }
}

// That is called by WiFi.setStaticIP(..) and seems to be responsible for writing to the flash.
void wlan_set_ipaddress(const HAL_IPAddress* host, const HAL_IPAddress* netmask,
                        const HAL_IPAddress* gateway, const HAL_IPAddress* dns1,
                        const HAL_IPAddress* dns2, void* reserved)
{
    const static_ip_config_t* pconfig = wlan_fetch_saved_ip_config();
    static_ip_config_t config;
    memcpy(&config, pconfig, sizeof(config));
    assign_if_set(config.host, host);
    assign_if_set(config.netmask, netmask);
    assign_if_set(config.gateway, gateway);
    assign_if_set(config.dns1, dns1);
    assign_if_set(config.dns2, dns2);
    dct_write_app_data(&config, DCT_IP_CONFIG_OFFSET, sizeof(config));
}

If I’m right, this is the function that gets finally called by WiFi.setStaticIP(…). Below you find the same function with my thoughts…

void wlan_set_ipaddress(const HAL_IPAddress* host, const HAL_IPAddress* netmask,
                        const HAL_IPAddress* gateway, const HAL_IPAddress* dns1,
                        const HAL_IPAddress* dns2, void* reserved)
{
  // We read the current IP setting, that is very nice. It will set us in a position, where
  // we can finally decide if we realy need to write the new configuratino to flash, or not.
  const static_ip_config_t* pconfig = wlan_fetch_saved_ip_config();
	
  // Create a new config object. Right now it's empty.
  static_ip_config_t config;
	
  // copy over the previously readed config settings.
  memcpy(&config, pconfig, sizeof(config));
	
  // assign_if_set(..) is defined in the same file, above this function.
  // It will basically check if both parameters are vaild. If so, it 
  // will assign the HAL_IPAddress to the appropriate config object (as uint32_t).
  assign_if_set(config.host, host);
  assign_if_set(config.netmask, netmask);
  assign_if_set(config.gateway, gateway);
  assign_if_set(config.dns1, dns1);
  assign_if_set(config.dns2, dns2);
	
  // This function will directly write the config object to the flash.
  dct_write_app_data(&config, DCT_IP_CONFIG_OFFSET, sizeof(config));
}

Okay, so far I see no situation where the config will not be written to the flash.
From my point of view, it’s a pity, because we already loaded the existing configuration from flash.
A quick comparison would not hurt in this case, or am I wrong? Maybe I miss something?

My approach would be something like this:


...
  assign_if_set(config.dns2, dns2);
   
  if ((config.host == pconfig->host) && (config.netmask == pconfig->netmask) && (config.gateway == pconfig->gateway) && (config.dns1 == pconfig->dns1) && (config.dns2 == pconfig->dns2))
    return;

  dct_write_app_data(&config, DCT_IP_CONFIG_OFFSET, sizeof(config));
...

Maybe someone more experienced in the community, can post some suggestions.

Best regards
Pix


#4

Perhaps @rickkas7 can help


#5

I’m the source of the “believe it’s safe” advice, so I’m not sure how helpful I’ll be. LOL

I finally found the post that I base that advice on. Mdma said it’s safe to set every time, so that’s what I based my belief on.

Pix’s analysis looks sound, but there is still one other possibility: that dct_write_app_data is where the change detection occurs.


#6

Hi

First of all, thank you, to both of you for your response. Especially to rickkas7 for jumping in the second time :+1:.
I have have to admit, that I did not trace further than my mentioned methods. I just had a quick look in “dct_write_app_data” and decided to stop drilling down :innocent:. From the logic point of view, I thought that the “wlan_set_ipaddress” method would be the best place to compare, as the previous setting is already readed here and available for comparison. If the comparison takes place in “dct_write_app_data” it would mean, that we have to read the flash a second time - to collect the data to compare to.
But you are right, I can not be sure of that. There meight be reasons to compare later.
I will see if I can follow the code a little deeper. Maybe it would be good if I setup a build environment to do some tests (not sure if I am capable of this).

One question. For me this is a fundamental problem. But my impression is, that I’m more or less alone with that feeling. The rest of the users seem to be absolutely fine with the current solution. So the question is: Does it make sense to solve this “problem/feature” and maybe extend the firmware, or is there simply no demand for it?
I would love to support this open source project - if needed. But in the end, I am happy with my workaround.

Have a nice weekend :sunglasses:
Pix