Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autoconnect with multiple Static IP clients #122

Closed
simardcl opened this issue Aug 28, 2019 · 5 comments
Closed

Autoconnect with multiple Static IP clients #122

simardcl opened this issue Aug 28, 2019 · 5 comments
Labels
question Further information is requested

Comments

@simardcl
Copy link

Hello Hieromon,

For a project we have multiple ESP8266 boards connecting to the same AP. We want to set ESP8266 boards with static IP, specific Gateway and netmask. On the portal, only the SSID and password of AP could be changed from the web page of the standard Portal. How can we use Autoconnect to be able by the portal to change the static IP settings, saved them to EEprom and reboot with these settings after.

I try some code like without success

AutoConnect portal;
AutoConnectConfig Config;
Config.staip = IPAddress(192,168,1,10);
Config.staGateway = IPAddress(192,168,1,1);
Config.staNetmask = IPAddress(255,255,255,0);
Config.dns1 = IPAddress(192,168,1,1);
portal.config(Config);
portal.begin();

If we change IPadress from 192.168.1.10 to 192.168.1.12 from portal , after save the module should restart with this new IP from now on.

I use Arduino 1.8.7 and AutoConnect 0.9.12

Thank you!

@Hieromon
Copy link
Owner

I would like to consider your theme a little more, so please tell me the following:

  • Are individual modules and static IPs pre-coupled? Or do you choose static IP each time in the portal?
  • If you have an advanced WiFi router, can you set up a DHCP address range on the router side? Or can the MAC address and the IP to be assigned be linked? (This method uses a fixed IP resource for the MAC address.)

@Hieromon Hieromon added the question Further information is requested label Aug 29, 2019
@simardcl
Copy link
Author

Hello Hieromon,

  • Normally individual modules are pre-coupled with there Static IP. But if we have a defective wifi card we want to be able to set back the Static IP by using the web portal on the new unit.
  • Cisco router effectively permit to set static IP address on a MAC address, but again if we have to change the unit, we need to talk to IT people to do modification on Cisco AP to make system work again. We prefer to stick with fix IP address, right now these Satic IP are hard coded, so no flexibility

Thanks for your fast answer

Best regards

@Hieromon
Copy link
Owner

Hieromon commented Sep 3, 2019

Your situation appears to require an external trigger to reconfigure the static IP. The following sketch was implemented in light of your situation. Enable the AutoConnectAux page where you can reconfigure static IP with that trigger.

configip

  1. Equips an external switch for configuring static IP.

    ex_switch

    // Pin assignment for an external configuration switch
    uint8_t ConfigPin = D2;
    uint8_t ActiveLevel = LOW;    // Declare the active signal level of the external switch
  2. Reset the esp8266 module while holding the switch to activate the IP configuration page.

  3. Save the static IP configuration set on that page in EEPROM.

  4. Erase the esp8266 module flash before uploading this sketch (shift the EEPROM area to avoid conflicts with AutoConnect saved credentials)

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <AutoConnect.h>
#include <EEPROM.h>

static const char AUX_CONFIGIP[] PROGMEM = R"(
{
  "title": "Config IP",
  "uri": "/configip",
  "menu": true,
  "element": [
    {
      "name": "caption",
      "type": "ACText",
      "value": "<b>Module IP configuration</b>",
      "style": "color:steelblue"
    },
    {
      "name": "mac",
      "type": "ACText",
      "format": "MAC: %s"
    },
    {
      "name": "staip",
      "type": "ACInput",
      "label": "IP",
      "pattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
    },
    {
      "name": "gateway",
      "type": "ACInput",
      "label": "Gateway",
      "pattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
    },
    {
      "name": "netmask",
      "type": "ACInput",
      "label": "Netmask",
      "pattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
    },
    {
      "name": "dns1",
      "type": "ACInput",
      "label": "DNS",
      "pattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
    },
    {
      "name": "ok",
      "type": "ACSubmit",
      "value": "OK",
      "uri": "/restart"
    },
    {
      "name": "cancel",
      "type": "ACSubmit",
      "value": "Cancel",
      "uri": "/_ac"
    }
  ]
}
)";

static const char AUX_RESTART[] PROGMEM = R"(
{
  "title": "Config IP",
  "uri": "/restart",
  "menu": false,
  "element": [
    {
      "name": "caption",
      "type": "ACText",
      "value": "Settings",
      "style": "font-family:Arial;font-weight:bold;text-align:center;margin-bottom:10px;color:steelblue"
    },
    {
      "name": "staip",
      "type": "ACText",
      "format": "IP: %s"
    },
    {
      "name": "gateway",
      "type": "ACText",
      "format": "Gateway: %s"
    },
    {
      "name": "netmask",
      "type": "ACText",
      "format": "Netmask: %s"
    },
    {
      "name": "dns1",
      "type": "ACText",
      "format": "DNS1: %s"
    },
    {
      "name": "result",
      "type": "ACText",
      "posterior": "par"
    }
  ]
}
)";

AutoConnect portal;
AutoConnectConfig config;
AutoConnectAux    auxIPConfig;
AutoConnectAux    auxRestart;

// Pin assignment for an external configuration switch
uint8_t ConfigPin = D2;
uint8_t ActiveLevel = LOW;

// EEPROM saving structure
typedef union {
  struct {
    uint32_t  ip;
    uint32_t  gateway;
    uint32_t  netmask;
    uint32_t  dns1;
  } ipconfig;
  uint8_t  ipraw[sizeof(uint32_t) * 4];
} IPCONFIG;

// Load IP configuration from EEPROM
void loadConfig(IPCONFIG* ipconfig) {
  EEPROM.begin(sizeof(IPCONFIG));
  int dp = 0;
  for (uint8_t i = 0; i < 4; i++) {
    for (uint8_t c = 0; c < sizeof(uint32_t); c++)
      ipconfig->ipraw[c + i * sizeof(uint32_t)] = EEPROM.read(dp++);
  }
  EEPROM.end();

  // Unset value screening
  if (ipconfig->ipconfig.ip == 0xffffffffL)
    ipconfig->ipconfig.ip = 0U;
  if (ipconfig->ipconfig.gateway == 0xffffffffL)
    ipconfig->ipconfig.gateway = 0U;
  if (ipconfig->ipconfig.netmask == 0xffffffffL)
    ipconfig->ipconfig.netmask = 0U;
  if (ipconfig->ipconfig.dns1 == 0xffffffffL)
    ipconfig->ipconfig.dns1 = 0U;

  Serial.println("IP configuration loaded");
  Serial.printf("Sta IP :0x%08lx\n", ipconfig->ipconfig.ip);
  Serial.printf("Gateway:0x%08lx\n", ipconfig->ipconfig.gateway);
  Serial.printf("Netmask:0x%08lx\n", ipconfig->ipconfig.netmask);
  Serial.printf("DNS1   :0x%08lx\n", ipconfig->ipconfig.dns1);
}

// Save current IP configuration to EEPROM
void saveConfig(const IPCONFIG* ipconfig) {
  EEPROM.begin(128);
  int dp = 0;
  for (uint8_t i = 0; i < 4; i++) {
    for (uint8_t d = 0; d < sizeof(uint32_t); d++)
      EEPROM.write(dp++, ipconfig->ipraw[d + i * sizeof(uint32_t)]);
  }
  EEPROM.end();
  delay(100);
}

// Custom web page handler to set current configuration to the page
String getConfig(AutoConnectAux& aux, PageArgument& args) {
  IPCONFIG  ipconfig;
  loadConfig(&ipconfig);

  // Fetch MAC address
  String  macAddress;
  uint8_t mac[6];
  WiFi.macAddress(mac);
  for (uint8_t i = 0; i < 6; i++) {
    char buf[3];
    sprintf(buf, "%02x", mac[i]);
    macAddress += buf;
    if (i < 5)
      macAddress += ':';
  }
  aux["mac"].value = macAddress;

  // Fetch each IP address configuration from EEPROM
  IPAddress staip = IPAddress(ipconfig.ipconfig.ip);
  IPAddress gateway = IPAddress(ipconfig.ipconfig.gateway);
  IPAddress netmask = IPAddress(ipconfig.ipconfig.netmask);
  IPAddress dns1 = IPAddress(ipconfig.ipconfig.dns1);

  // Echo back the IP settings
  aux["staip"].value = staip.toString();
  aux["gateway"].value = gateway.toString();
  aux["netmask"].value = netmask.toString();
  aux["dns1"].value = dns1.toString();

  return String();
}

// Convert IP address from AutoConnectInput string value
bool getIPAddress(AutoConnectInput& input, uint32_t* ip) {
  IPAddress ipAddress;

  if (input.value.length()) {
    if (!input.isValid())
      return false;
    ipAddress.fromString(input.value);
  }
  *ip = (uint32_t)ipAddress;
  return true;
}

// Custom web page handler to save the configuration to AutoConnectConfig
String setConfig(AutoConnectAux& aux, PageArgument& args) {
  IPCONFIG  ipconfig;
  bool valid = true;

  // Retrieve each IP address from AutoConnectInput field
  AutoConnectInput& staipInput = auxIPConfig["staip"].as<AutoConnectInput>();
  AutoConnectInput& gatewayInput = auxIPConfig["gateway"].as<AutoConnectInput>();
  AutoConnectInput& netmaskInput = auxIPConfig["netmask"].as<AutoConnectInput>();
  AutoConnectInput& dns1Input = auxIPConfig["dns1"].as<AutoConnectInput>();

  // Echo back the ip configurations to result page
  aux["staip"].value = staipInput.value;
  aux["gateway"].value = gatewayInput.value;
  aux["netmask"].value = netmaskInput.value;
  aux["dns1"].value = dns1Input.value;

  // Validation entered IP addresses
  valid &= getIPAddress(staipInput, &ipconfig.ipconfig.ip);
  valid &= getIPAddress(gatewayInput, &ipconfig.ipconfig.gateway);
  valid &= getIPAddress(netmaskInput, &ipconfig.ipconfig.netmask);
  valid &= getIPAddress(dns1Input, &ipconfig.ipconfig.dns1);

  // Make a result message
  if (valid) {
    saveConfig(&ipconfig);
    aux["result"].value = "Reset by AutoConnect menu will restart with the above.";
  }
  else
    aux["result"].value = "Invalid IP address specified.";
  return String();
}

// Sense the external switch to enter the configuraton mode
bool senseConfig(const uint8_t pin, const uint8_t activeLevel) {
  bool  sw = digitalRead(pin) == activeLevel;
  if (sw) {
    // Cut-off the chattering noise
    while (digitalRead(pin) == activeLevel)
      delay(10);
  }
  return sw;
}

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

  // Shift the credentials storage to reserve saving IPCONFIG
  config.boundaryOffset = sizeof(IPCONFIG);

  // Load current IP configuration
  IPCONFIG  ipconfig;
  loadConfig(&ipconfig);

  // Configure pre-loaded static IPs
  config.staip = IPAddress(ipconfig.ipconfig.ip);
  config.staGateway = IPAddress(ipconfig.ipconfig.gateway);
  config.staNetmask = IPAddress(ipconfig.ipconfig.netmask);
  config.dns1 = IPAddress(ipconfig.ipconfig.dns1);
  portal.config(config);

  // Sense the configuration button (external switch)
  pinMode(ConfigPin, INPUT);
  if (senseConfig(ConfigPin, ActiveLevel)) {
    Serial.println("IP configuration enable");
    auxIPConfig.load(AUX_CONFIGIP);
    auxIPConfig.on(getConfig);
    auxRestart.load(AUX_RESTART);
    auxRestart.on(setConfig);
    portal.join({ auxIPConfig, auxRestart });
  }

  portal.begin();
}

void loop() {
  // User sketch process is here.

  portal.handleClient();
}

EEPROM.begin(128); statement in the saveConfig function should be carefully considered. The argument specifies usage size for the EEPROM. It must include the size of the credentials area for AutoConnect saving.

I hope the above sketch will match your requirements.

@simardcl
Copy link
Author

simardcl commented Sep 3, 2019

Good day Hieromon,

Thank you for your very complete answer , we will try this approach , using EEprom location to save credentials.

Thanks again for your great support.

Best regards

@Hieromon Hieromon mentioned this issue Sep 6, 2019
Hieromon added a commit that referenced this issue Sep 7, 2019
@Hieromon
Copy link
Owner

Hieromon commented Sep 9, 2019

AutoConnect v1.0.0 includes a sample sketch named Config.ino that implements the requirements of this topic.
The issue closed once. If something issue appears, please post a new one.

@Hieromon Hieromon closed this as completed Sep 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants