Skip to main content

imp Network Interface Management

Understand impOS’ Mechanism For Network Selection

impOS™ has long been able to support multiple network interfaces. However, impOS contains a new network interface management system to provide Squirrel with better access to and control of these interfaces. It has been developed to provide support for not only multiple types of interface in the same product, such as WiFi and cellular, but also multiple interfaces of the same type: for example, a device with two or more Ethernet ports both under impOS management.

The new mechanism’s default mode of operation is to leave network interface management to impOS, but it also provides tools which allow your application to select the order in which these interfaces are utilized and even to pick and choose which interface is used at any given moment.

This document introduces you to the new network interface management modes, and to the new imp API methods they encompass, to help you understand if it will be beneficial to adopt the new technology in your application and, if that’s the case, how to do so.

Network Management Modes

impOS’ network interface management system provides three application-selectable usage modes: legacy, automatic and manual. These modes determine how network interfaces are chosen for use. There are other modes too, but they are not user-selectable. We discuss these additional modes at the end of this guide.

  • Legacy mode is the default mode: it will step through all of the available network interfaces in a default order until it has successfully connected. It is sufficient for the vast majority of use-cases and is the closest to the management system used by earlier versions of impOS. Indeed, if you make no changes to your code, your application will continue to operate as it did before the release of impOS 42.

  • Automatic mode extends legacy mode by allowing you to replace the default interface connection order with a sequence of your own.

  • Manual mode puts connection management fully in the hands of your code. You choose which interface is used to host the imp’s connection to the impCloud, and when. It is also the responsibility of your code to decide how to deal with connection failures: for example, which interface to attempt to connect to next, which might depend upon the device’s location, whether the device connects over a primary interface and falls back to a secondary one when the first is unavailable for some reason, or some other application-specific factor.

Choosing A Mode

Legacy mode is the default mode and is enabled when an imp first cold-boots. If you are happy to make use of legacy mode, your code need not be altered. If you do not wish to change your code, it will continue to run exactly as before, and impOS’s changes will not affect you.

This is because your code only selects automatic mode when it calls a specific imp API method, imp.net.setserverinterfaces(). Your code only selects manual mode when it calls a specific imp API method, server.connectwith(). Until your code makes either of these calls, which it might never do, the imp remains in legacy mode.

Default Interface Order

The default order is Ethernet, then WiFi and, finally, cellular. Within each of these types, impOS will run through the available interfaces, if any, of that type. imps which lack any of these interfaces will not include the unavailable interfaces in their default sequences. The imp005, for example, has two interface types, Ethernet and WiFi, and one interface of each type.

Single-interface Products

If your product makes use of an imp with a single network interface, it is not necessary to make use of impOS’s network management system: you can continue to use existing impOS methods, such as server.connect(). Even though legacy mode is enabled by default, with only one interface available it behaves exactly like earlier versions of impOS did. Again, you do not need to make any changes to your code.

However, you may make use of the new system if you wish, which you might want to do to future-proof your application firmware, you wish to make use of impOS’s local networking functionality, or your application has to switch between a number of network or proxy configurations.

Let’s now look at each of the new modes in more detail.

Legacy Mode

This mode, which is the default, operates just like earlier versions of impOS but adds some optional extra functionality to support the use of more than one network interface if your product makes use of multiple network interfaces.

Under legacy mode, impOS will attempt to connect to the impCloud one interface at a time. If the interface currently in use cannot be used to connect or reconnect to the impCloud, impOS will move on to the next available interface in a set sequence at the next connection attempt. When it gets to the last interface on the list and cannot connect, it goes back to the first interface on the list, repeating the sequence.

Connection attempts will occur this way whether they are initiated automatically or by your code. For example, whether your reconnection policy (applied by calling server.setsendtimeoutpolicy()) is set to SUSPEND_ON_ERROR, RETURN_ON_ERROR or RETURN_ON_ERROR_NO_DISCONNECT.

If, for example, you are using SUSPEND_ON_ERROR, your imp will follow the existing procedure: a loss of connectivity will cause Squirrel to suspend while impOS attempts to reconnect. If it connects, Squirrel is restarted; otherwise the imp will snooze for nine minutes then attempt to reconnect automatically. This reconnection attempt will use the next available interface.

If you are using RETURN_ON_ERROR and following a disconnection you are attempting to reconnect with server.connect(), impOS will attempt to use the current interface. If it fails to connect, your app will be informed in the usual way, and impOS will set the next interface in sequence as the current one so that when you next try to connect (you call server.connect() again, or call server.log(), server.error() or agent.send()), it will be used. This behavior ensures compatibility with older code, but if your product has multiple interfaces, the use of RETURN_ON_ERROR makes your code a good candidate for conversion to manual mode, though this is not mandatory. However, you will not be able to make use of manual mode unless you also make use of either RETURN_ON_ERROR or RETURN_ON_ERROR_NO_DISCONNECT.

You need make no changes to your Squirrel application code to take advantage of legacy mode. However, you may do so if you wish to discover which is the current interface. Calling the new imp imp.net.getcurrentconfig() will return an object representing the current interface configuration. We’ll look at this method in more detail shortly. Calling imp.net.getserverinterfaces() will return a list of available interfaces. Again, we’ll discuss this method in more detail below.

Automatic Mode

If impOS’ default interface order is not be optimal for your application, you can set a preferred order using the new imp API method imp.net.setserverinterfaces(). This method takes as an argument an array of Network Interface Identifiers (NIIs) in the order you require.

Network Interface Identifiers

NIIs are strings which can be used to specify one or more network interfaces. These include "wl0", "eth0" and "cell0". You may also see the generic NIIs "wifi", "ethernet" or "cell".

All of these identifiers can be referenced even if your application lacks any of them. Under automatic mode, any references your code makes to unavailable interfaces when it calls imp.net.setserverinterfaces() are ignored, as are repeated references.

If you don’t include an available interface in your list, impOS will not make use of it if other interfaces fail to connect. However, this does not mean that the omitted network interface is disabled — it is simply not used as a failover and may continue to draw power if it has previously been enabled. Interfaces which have never been enabled will not draw power, but it is possible that they may be enabled by impOS itself (see Override Mode, below).

Setting The Current Interface List

The following examples show how you can apply imp.net.setserverinterfaces() using NIIs.

// Attempt to connect to WiFi first, or failover to cellular
imp.net.setserverinterfaces(["wifi", "cell"]);

You can apply a new list at any time, but unless you manually disconnect and reconnect, the current interface will not change until the imp disconnect and then (immediately or later) reconnects. However impOS reconnects, it will start working with the first interface in the current sequence.

Interface List Persistence

Any list you apply to enable automatic mode, or to change the sequence while the imp is already in automatic mode, will be maintained across subsequent device restarts, including power-cycles. The sequence is written to non-volatile memory and so will also persist after Squirrel changes such as updated code or a change of Device Group. To return to legacy mode, you must first explicitly re-enable the default network interface list.

The list is limited 12 items; an exception will be thrown if a longer list is provided. Additionally, duplicate interface types will be removed. Specific interface names will be changed to generic ones, eg. "wl0" will become "wifi". The list is persisted only after conversion and de-duplication.

Getting The Current Interface List

To retrieve the current network interface failover sequence, whether impOS' default or one set by you, call imp.net.getserverinterfaces(). It returns an array of NII strings. Again, if you subsequently alter the array you receive, the change will not be applied until you call imp.net.setserverinterfaces().

Resetting The Interface List To The Default

If you wish to clear a sequence of interfaces that you have provided, and revert to impOS’ default list, call imp.net.setserverinterfaces() with an empty array as its argument:

// Go back to default interface list
imp.net.setserverinterfaces([]);

This moves the imp from automatic mode to legacy mode.

Manual Mode

Manual mode allows your code to specify which of the available network interfaces is used to connect, and when. New imp API methods are available to your code for this purpose. These methods make use of a new data structure called a Network Interface Configuration (NIC).

To make use of manual mode, you must first set the imp’s reconnection policy to either RETURN_ON_ERROR or RETURN_ON_ERROR_NO_DISCONNECT using server.setsendtimeoutpolicy(). Attempting to use manual mode under the SUSPEND_ON_ERROR policy will cause an exception to be thrown.

Important Manual mode allows you to bring up multiple interfaces and have them operate simultaneously: for example, cellular for connection to the impCloud server and Ethernet for connections to local devices. You should consider the power implications of having multiple interfaces operating at the same time, as this may have a significant impact on your product’s power consumption.

What Is A NIC?

A NIC is a Squirrel table containing one or more of the following keys. Only interface is mandatory; the other keys are optional and should only be used if the network you are connecting to requires them.

NIC Key Data Type Required? Relevant Interfaces Description
interface String Yes Ethernet, WiFi, cellular The interface through which to attempt the connection: "wl0", "eth0" or "cell0"
staticconfig Table No Ethernet, WiFi Optional static network configuration. For a list of accepted keys, see below
proxy Table No Ethernet, WiFi Optional proxy server configuration. For a list of accepted keys, see below
wificonfig Table No WiFi Optional WiFi configuration. For a list of accepted keys, see below

Static Network Configurations

Key Data Type Required Description
ip String Yes The desired static IPv4 address, eg. "192.168.0.128"
netmask String Yes The network’s IPv4 address mask, eg. "255.255.255.0"
gateway String Yes The network gateway’s IPv4 address, eg. "192.168.0.0"
dns Array of strings Yes An array of 1-2 strings, each the IPv4 address of a DNS server

Proxy Configurations

Key Data Type Required Description
proxyType Constant Yes Currently must be PROXY_TYPE_HTTP
address String Yes The proxy server address in dotted quad or a resolvable form, eg. "192.168.0.128" or "proxy.electricimp.com". Up to 64 characters in length
port Integer Yes Port number between 0 and 65535
username String No An optional server login username. Up to 32 characters in length
password String See Description The server login password. This is required if a username is included in the configuration. Up to 32 characters in length

WiFi Configurations

Key Data Type Required? Description
ssid String Yes The name of the wireless network
key String or Blob No The key for the wireless network (if any). Plain text keys are passed as strings; encrypted keys are blobs

Manual Mode Methods

Manual mode introduces two new imp API methods:

These methods both rely on NICs to communicate interface settings with your application. For example, to initiate a connection to a specific interface, create a new NIC (or retrieve an existing one), and pass it into server.connectwith() as the method’s first argument. The method also takes a callback function which will be executed whether the connection attempt succeeds, fails or times out.

As such, it operates like the existing method server.connect() but allows you to specify which interface is used. You will typically use server.connectwith() where you might previously have used server.connect() (you can still use both, if you wish) and so manual mode is solely intended to be used on imps whose connection policy has been set to RETURN_ON_ERROR or RETURN_ON_ERROR_NO_DISCONNECT.

If the connection attempt succeeded, then the interface described by the NIC is said to be active and is now the current interface. Your code can use imp.net.getcurrentconfig() to retrieve a NIC which describes the current interface.

Note The use of imp.net.getcurrentconfig() is also available in any mode. Even if the imp connects automatically under impOS control, or connection is mediated by the existing imp API method such as server.connect() or agent.send(), it will have a current interface which is described in the NIC returned by imp.net.getcurrentconfig().

Encrypted Attributes

When generated in Squirrel, certain NIC attributes, such as a static network configuration’s password and a WiFi network’s passphrase, will be provided in plain text. Once a NIC has been successfully used to connect to the impCloud, it can be retrieved using imp.net.getcurrentconfig(). The NIC returned by this method will not contain passwords in plain text but in encrypted form. Passwords are encrypted using a per-device key and cannot be recovered from a NIC, but they can be saved for future re-use on the device, as can the NIC itself.

However, please bear in mind that if the network’s SSID, password and/or security type (eg. WEP, WPA2) change, then the stored NIC can no longer be used to connect to that network.

Network Configuration Persistence

impOS preserves network configuration information in persistent storage, but it only does so in certain circumstances: when BlinkUp™ takes place, and when code calls the imp API methods imp.setwificonfiguration(), imp.setstaticnetworkconfiguration() or imp.setproxy().

If you provide extended configuration information in a call to server.connectwith(), ie. you include any of the ...config keys described above in the NIC you pass in, impOS will not retain this information in persistent storage. If the connection attempt fails, or if the attempt to connect is successful but the connection is subsequently lost for any reason at all, including Squirrel virtual machine restarts, device power-cycles, and manual and unexpected disconnections, then the supplied configuration will be discarded and will not be used by impOS in any further connection attempts.

This is also the case if you call server.connect() while the imp is connected after making a call to server.connectwith() that included an extended NIC.

It’s important to be aware that under the RETURN_ON_ERROR policy, the imp does not reconnect automatically upon waking from sleep or a server.restart() call until your code calls server.connectwith(), server.connect() or any other imp API method that communicates with the server, such as server.log() or agent.send(). You should consider how your code might reconnect in such circumstances. If reconnection is not initiated by a call to server.connectwith() that includes an extended NIC, it will connect using a stored network configuration, if there is one.

The only way to re-use an extended NIC in any connection attempt is to pass it in to a server.connectwith() call.

If, however, you call server.connectwith() and simply identify an interface in your NIC, eg. {"interface" : "wl0"}, then impOS will use that interface for all future connections, or until an alternative interface is specified. Interfaces that required extra information to connect — for example, WiFi connections may require an SSID and password to be supplied — will make use of persisted configuration data in this instance. If there is no relevant configuration data available, the connection attempt will fail.

Note The interface sequence set by calling imp.net.setserverinterfaces() is written to non-volatile memory and will persist even after a cold boot and after Squirrel changes such as a change of Device Group.

Adding Network Persistence

If you want the imp to reconnect to the network defined in the NIC passed into server.connectwith(), you must either check the network in use after the reboot and, if necessary, switch to a NIC that you have persisted manually, or call imp.setwificonfiguration() when you first use server.connectwith() in order to record the change. For example:

local nic = { "interface"  : "wl0",
              "wificonfig" : { "ssid" : "fintlewoodlewix",
                               "key"  : "verybadpassw0rd!" }};
server.connectwith(nic, function(result, interfaceName) {
    if (result == SERVER_CONNECTED) {
        // Persist the WiFi config for the next reboot
        imp.setwificonfiguration(nic.wificonfig.ssid, nic.wificonfig.key);
    }
});

You can clear the persisted WiFi settings with imp.clearconfiguration() but beware that, until you apply new persistent WiFi settings, this will cause the imp to fail to connect upon restart.

The Persisted Network As A Back-up

This process enables some new use-cases. For example, you might choose to activate your production devices in the factory to a default network. This is the persisted network. As part of end-user set-up, you capture the end-user's network and apply it not by BlinkUp but by embedding the local network details in a NIC (which can be saved in Squirrel-accessible SPI flash) and passing that NIC into server.connectwith(). This way, the factory-set network is always present as a back-up — perhaps field engineers use a cellular-enabled WiFi hotspot set to host this default network.

When you consider such applications, you should bear in mind that, a power-cycled imp will always try to connect to the network to determine if it needs to download fresh Squirrel. If it cannot contact the default network, this will be signalled on the status LED. However, after ten seconds it will start its current Squirrel, which will presumably load a NIC and connect using it early on.

Single-interface imps

As noted above, on imps equipped with a single network interface, such as the imp001 through imp004m, it is not necessary to use NICs or the manual mode imp API methods; server.connect() and other existing imp API methods, such as imp.setwificonfiguration(), are sufficient.

However, you may use the new interface management system if you wish. For example, if your product is based on the imp003 and uses only the impOS-managed WiFi sub-system, the following code blocks, though different (the first uses existing calls; the second the new calls), are equivalent in all but one respect: the first overwrites the persistent configuration (set by BlinkUp; see Network Configuration Persistence) while the second does not.

Old Mechanism

// Assume we have an array of accessible WiFi networks...
local wlans = [];
local currentwlan = 0;

function doSwitch() {
    // Switch to the next WLAN on the list...
    switchwifi();

    // ...and attempt to connect to it
    reconnect();
}

function switchwifi() {
    // Switch to the next WLAN on the list
    currentwlan++;
    if (currentwlan >= wlans.len()) currentwlan = 0;
    local wlan = wlans[currentwlan];
    imp.setwificonfiguration(wlan.ssid, wlan.psk);
}

function reconnect() {
    // Make sure we've sent all we're currently trying to send
    server.flush(30);

    // Disconnect from the current network
    server.disconnect();

    // Connect to the new proposed current network
    server.connect(function(outcome) {
        local wlan = wlans[currentwlan];
        if (outcome == SERVER_CONNECTED) {
            server.log("Connected via " + wlan.ssid);
        } else {
            server.error("Could not connect to " + wlan.ssid);

            // Try the next WLAN on the list
            doSwitch();
        }
    });
}

// Wait for a message from the agent (via control app or web UI)
// to trigger a network configuration change
agent.on("change.wifi", function(ignored) {
   doSwitch();
});

New Mechanism

// Assume we have an array of accessible WiFi networks...
local wlans = [];
local currentwlan = 0;

function doSwitch() {
    // Switch to the next WLAN on the list...
    local nic = switchwifi();

    // ...and attempt to connect to it
    reconnect(nic);
}

function switchwifi() {
    // Switch to the next WLAN on the list
    currentwlan++;
    if (currentwlan >= wlans.len()) currentwlan = 0;
    local wlan = wlans[currentwlan];
    return { "interface"  : "wl0",
             "wificonfig" : { "ssid" : wlan.ssid,
                              "key"  : wlan.psk }
    };
}

function reconnect(nic) {
    // Make sure we've sent all we're currently trying to send
    server.flush(30);

    // Disconnect from the current network
    server.disconnect();

    // Connect to the new proposed current network
    server.connectwith(nic, function(outcome, ifname) {
        local wlan = wlans[currentwlan];
        if (outcome == SERVER_CONNECTED) {
            server.log("Connected via " + wlan.ssid);
        } else {
            server.error("Could not connect to " + wlan.ssid);

            // Try the next WLAN on the list
            doSwitch();
        }
    });
}

// Wait for a message from the agent (via control app or web UI)
// to trigger a network configuration change
agent.on("change.wifi", function(ignored) {
    doSwitch();
});

Override Mode

There is a fourth network management mode, but this is one which is not application-selectable. Automatic mode may be temporarily overridden by impOS if it needs to do so in order to complete an operating system upgrade or perform a Squirrel recovery. Once the override mechanism activates, the imp goes back to legacy mode until imp.net.setserverinterfaces() is called again. It can also be overridden in server.connect() calls if all of the interfaces from the applied interface list fail to connect.