Getting the imp to switch network on disconnect

I took half of this idea from another post on this forum, the first part is how it can scan and show the networks it can pick up.

The second part is my attempt to store multiple SSID and the keys needed to log on those, the idea was then to use server.isconnected() to determine if it is connected or not, and scan the networks and find a match if it can, then use that to log on to that SSID instead.

When calling the agent url it will also return a list of the networks it found, marking the one it is currently connected to with bold, Italic means it is a SSID we also got the code for, and are able to switch to if the one we are connected to goes down, normal means it was just found, but we have no info on it.

Agent:
`wifi <- “”;

http.onrequest(function(request,res)
{
if (request.body == “”)
{
local tempString = split(wifi, “|”);

  local output = "
";
      
      if (wifi.len() < 1)
      {
        output = "Waiting for the Impee to send some data to the Agent...";
      }
      else
      {
        foreach(sub in tempString)
        {
          local subString = split(sub, ",");
        
          if (subString[0] == "1")
          {
            output += "SSID: <b>" + subString[1] + "</b>";
          }
          else if (subString[0] == "2")
          {
            output += "SSID: <i>" + subString[1] + "</i>";
          }
          else
          {
            output += "SSID: " + subString[1];
          }
        
          if (subString[1].len() > 4 && subString[1].len() <= 9)
          {
            output += "\\t";
          }
          else if (subString[1].len() <= 4)
          {
            output += "\\t\\t";
          }
        
          output += "\\tChannel: " + subString[2];
          output += "\\tdB: " + subString[3];
          output += "\\t\\tOpen: " + subString[4] + "\\r\
";
        }
      }
      output += "

";

  res.send(200, "<!DOCTYPE html><html><head><meta http-equiv=\"refresh\" content=\"2\"></head><body>" + output + "</body></html>");
}

});

device.on(“sendwifi”, function(data) { wifi = data; });
`

Device:
`connections <- [
{ ssid = “wifi1”, pw = “key1” },
{ ssid = “wifi2”, pw = “key2” },
{ ssid = “wifi3”, pw = “key3” }
];

function scanwifi()
{
local scanResults = imp.scanwifinetworks();

local wifi = “”;

foreach (idx,val in scanResults)
{
local connStatus = “0”;

if (imp.getssid() == scanResults[idx].ssid)
{
  connStatus = "1";
}
else
{
  foreach (idy,val in connections)
  {
    if (scanResults[idx].ssid == connections[idy].ssid)
    {
      connStatus = "2";
    }
  }
}
wifi += connStatus + "," + scanResults[idx].ssid +"," + scanResults[idx].channel + "," + scanResults[idx].rssi + "," + scanResults[idx].open + "|";

}

imp.wakeup(2, scanwifi); //Do this again in 1 minute

agent.send(“sendwifi”, wifi);
}

scanwifi();

server.log(“run2ning”);

server.setsendtimeoutpolicy(RETURN_ON_ERROR, WAIT_TIL_SENT, 30);

currentConnection <- 0;

// disconnects from current network (if required)
// tries connecting with the supplied ssid / pw
// executes the callback
function ChangeWifi(ssid, pw, callback) {
// if we’re connected
if (server.isconnected()) {
// flush wifi and disconnect
server.flush(30);
server.disconnect();
}

imp.setwificonfiguration(ssid, pw);
server.connect(callback, 30);

}

// This function doesn’t use the result parameter,
// but it’s required since it’s the callback from
// a server.connect()
function ConnectOrFailToNextConnection(result = null) {
// if we’re connected, do nothing
if (!server.isconnected()) {
// if we’ve already tried all the connections
if (currentConnection > connections.len()) {
// go to sleep for 5 minutes
currentConnection = 0;
imp.deepsleepfor(5*60);
} else {
// if there are still connections to try
// grab current ssid and pw, and increment currentConnection
// for the next attempt (if connection fails)
local ssid = connections[currentConnection].ssid;
local pw = connections[currentConnection].pw;
currentConnection++;
// try connecting
ChangeWifi(ssid, pw, ConnectOrFailToNextConnection)
}
}
}

// On boot, make sure we’re connected
// or try connecting
ConnectOrFailToNextConnection();

// called after imp tries to reconnect to current
// server for 1 minute and fails
server.onunexpecteddisconnect(function(reason) {
// Loop through connections until we connect
currentConnection = 0;
ConnectOrFailToNextConnection();
});`

The web display of SSID info is working! I learned a lot from comparing your code to my original code, thank you! I haven’t tested the auto-connect feature yet.

I updated the first post with some new code, I tested it last night and it works! :slight_smile: Also added a bit extra for displaying what it found, so it shows the ones it can switch to if needed.

Very helpfull, thanks.
But the code works better when you change
connections { ssid = "wifi1", pw = "alsowifikey" }, { ssid = "wifi2", pw = "wifikey" } ];
in
local connections = [ { ssid = "wifi1", pw = "alsowifikey" }, { ssid = "wifi2", pw = "wifikey" } ];

Thanks, not sure how that happened on the copy/paste, but fixed now :slight_smile:

using the built-in webserver, sometimes with long or short SSIDs the columns were not outlined right.
So I changed some lines like this:
if (subString[1].len() > 4 && subString[1].len() <= 9) { output += "\\t"; } else if (subString[1].len() <= 4) { output += "\\t\\t"; }

for this:
output +=" "; output = output.slice(0, output.len() - subString[1].len());

I just tried that, but it seems to do something wrong

SSID: OpenWrt Channel: 1 dB: -61 Open: false SSID: Søblik Channel: 5 dB: -88 Open: false SSID: nygade 26 Channel: 6 dB: -86 Open: false SSID: Mikey Channel: 9 dB: -58 Open: false

The one called Søblik is missing a space.

Len: 7 > SSID: OpenWrt Channel: 1 dB: -59 Open: false Len: 7 > SSID: Søblik Channel: 5 dB: -83 Open: false Len: 5 > SSID: Mikey Channel: 9 dB: -64 Open: false
For some reason it says Søblik is 7 long, but it is only 6…

Problem found, it seems to be a bug in the system, going to make a report on it.

Added a bit more to the agent side of it

It now sorts the scan results according to strength
`wifi <- “”;

http.onrequest(function(request,res)
{
if (request.body == “”)
{
local tempString = split(wifi, “|”);

  local output = "";
  
  if (wifi.len() < 1)
  {
    output = "Waiting for the Impee to send some data to the Agent...";
  }
  else
  {
    local lastRSSI = 0;
    foreach(sub in tempString)
    {
      local subString = split(sub, ",");
      local result = "";
      if (subString[0] == "1")
      {
        result += "SSID: <b>" + subString[1] + "</b>";
      }
      else if (subString[0] == "2")
      {
        result += "SSID: <i>" + subString[1] + "</i>";
      }
      else
      {
        result += "SSID: " + subString[1];
      }
    
      result +="                  ";
      result = result.slice(0, result.len() - subString[1].len());
    
      result += "Channel: " + subString[2];
      result += "\\tdB: " + subString[3];
      result += "\\t\\tOpen: " + subString[4] + "\\r\

";

      if (lastRSSI > subString[3].tointeger())
      {
        output += result;
      }
      else
      {
        output = result + output;
      }
      
      lastRSSI = subString[3].tointeger();
    }
  }
  
  output = "
" + output + "

";

  res.send(200, "<!DOCTYPE html><html><head><meta http-equiv=\"refresh\" content=\"2\"></head><body>" + output + "</body></html>");
}

});

device.on(“sendwifi”, function(data) { wifi = data; });`

@MikeyDK, thanks for your thoughts here. I’ve been considering a similar approach for an application which would require at least 2 SSIDs, perhaps 3. Switching to an alternate SSID (in the event of connection failure OR disappearance of the original SSID) is an extra safeguard to minimise service disruptions. Since the Imp has no local NVM available to store a table of SSIDs and passwords, I’ll have to consider EEPROM on the design (would love for a small amount of the ATSHA EEPROM to be made available for user use [-O< ). The imp code would maintain a table of SSIDs within the EEPROM. I’m very comfortable with managing the write-cycle limit of the EEPROM myself, but I have concerns about the use of imp.setwificonfiguration(ssid,pw). Calling it not only overrides the existing SSID but probably forces a write to NVM on the Atmel ID chip. In the event of password changes or security policy changes, the supported SSIDs could refuse connection with the Imp. This could lead to the device endlessly cycling through the SSIDs without making a successful connection. The max write-cycles of 100,000 could get burned up in the process, even if the device backs-off for a fixed period.

@Imp Team, would it be possible to extend imp.setwificonfig… to include a boolean field that determines whether the config change is committed to NVM or not? Another option would be to add an extra function imp.wificommit() which would write the current SSID to NVM. Not invoking the commit, would make the SSID change temporary (until next reset).

Setwificonfig writes to an internal flash page on the imp; tagged blocks are written here and we only erase when an entire page is filled. The wifi config is a small entry, so I wouldn’t worry about endurance (ie you have likely hundreds of thousands of cycles writing the wifi config, more likely millions at room temperature)…

You should still probably give up, or at least slow down, after a while though!