Skip to main content

Detecting Available Network Interfaces

How To Check Which Interfaces Are Available For Use

A device’s imp may have access to multiple network interfaces. Any application which makes use of these interfaces — for example, for local networking (impOS 42 and up) — will find it useful to be able to determine which of these interfaces are available for use.

The following code provides a means to do this. It uses the imp API method imp.net.info() to get a list of the network interfaces attached to the device via its host imp. For each of these interfaces, the code creates an app-level record and stores in the table variable interfaces. The record includes the interface’s type, a boolean value to indicate whether it is available for use, and the interface object used to access the interface via impOS.

For each known interface, the code calls imp.net.open() to set the interface up for local networking. If this succeeds, and the interface connects to a network, the code notes its availability for such usage and then closes the connection by setting the referrer variable to null.

How do we know the connection succeeded? imp.net.open() registers a callback function to which interface state change notifications are sent when they are detected by impOS. The code within the callback examines the reported state and marks the interface as available upon connection. If the interface subsequently reports a problem, the code marks the interface as unavailable.

It is possible to connect multiple interfaces of the same type to an imp module, so the callback registration uses Squirrel’s bindenv() function to bind the callback to the relevant interface record which thus becomes accessible to the callback code through the context variable this. We do this because the state-change callback of itself has no information to indicate which interface triggered its execution. As an alternative approach, we could have registered a number of interface-specific callbacks, but this increases the length of the code and requires the application to ‘know’ which interfaces will be wired to the imp. The code below does not need this information.

The key code is included in the function getAvailableInterfaces(), but the sample below also includes a function, reportInterfaces(), to log the results. A real-world app would not necessarily include this, or the variables interfaceCount and reportCount, which are used to determine when all the checks have been performed. Application code can use the variables isWifiAvailable, isEthernetAvailable and isCellularAvailable, which are available throughout the app's runtime, to determine whether a given network type is available for use.

Code

// The 'interfaces' variables holds references to available interfaces
local interfaces = {};
// Set flags that the app can check to see if an interface can be used
local availableForLocal = { "wifi" : false,
"ethernet" : false,
"cell" : false };
// These globals are used for reporting
local interfaceCount = 0;
local reportCount = 0;
// Functions
function stateChangeCallback(state) {
// This is the state-change callback function, which we bind to the interface record
// so that we know which interface's state change triggered the callback
// Is the interface available for use? It is if it's connected
this.isAvailable = (state == imp.net.CONNECTED);
// Set the app-level indicators
if (this.isAvailable) {
availableForLocal[this.type] = true;
}
// When we reach a terminal state consider the interface reported
if (state == imp.net.CONNECTED ||
(state >= imp.net.WIFI_STOPPED && state <= imp.net.WIFI_STOPPED_UNHAPPY) ||
(state >= imp.net.ETHERNET_STOPPED && state <= imp.net.ETHERNET_STOPPED_NO_LINK)) {
// Indirectly close the interface, by nulling the variable referring to it
// NOTE You may prefer to comment out this line in order to keep the interface
// open, so that the app's availability information is updated continuously.
// This is useful with, for example, a mobile device that may need to change
// to another interface due to connectivity at its current location.
this.interface = null;
reportCount++;
}
}
function getAvailableInterfaces() {
// Use 'imp.net.info()' to acquire a list of available interfaces
// then check each for their current state
local netInfo = imp.net.info();
interfaceCount = 0;
reportCount = 0;
// Run through the known interfaces to see which may be used: these are
// recorded by impOS in the 'interface' table from imp.net.info()'s results
foreach (interface in netInfo.interface) {
// Prepare a new app-level record for the interface
local interfaceRecord = {};
interfaceRecord.isAvailable <- true;
interfaceRecord.type <- interface.type;
// Now open the interface for local network to test its availability
// When the callback triggers, it sets the availability-by-type flags, etc.
// NOTE Don't action for cellular -- if we're on a cellular link to the impCloud,
// the state-change callback is not triggered
if (interface.type != "cell") {
interfaceCount++;
interfaceRecord.interface <- imp.net.open({"interface": interface.name},
stateChangeCallback.bindenv(interfaceRecord));
}
// Record the interface in the table of interfaces, using the interface name as the slot's key
interfaces[interface.name] <- interfaceRecord;
}
}
function reportInterfaces() {
// Subsidiary output function
function generateReport(type) {
local list = "";
foreach (interfaceName, interfaceRecord in interfaces) {
if (interfaceRecord.type == type) list += interfaceName + ", "
}
local report = "can connect to the impCloud";
if (list.len() > 0) {
if (availableForLocal[type]) {
report += (" and is available for local networking on interface(s) " + list.slice(0, list.len() - 2));
} else {
report += " but not to local networks";
}
return report;
}
return "no interface(s) present";
}
if (reportCount < interfaceCount) {
// Not all interfaces have completed checking, so try again in 0.5s
imp.wakeup(0.5, reportInterfaces);
} else {
// We have completed checks on all possible interfaces, so report the results
server.log(" WiFi : " + generateReport("wifi"));
server.log("Ethernet : " + generateReport("ethernet"));
server.log("Cellular : " + generateReport("cell"));
}
}
// Probe the imp's interfaces
getAvailableInterfaces();
// Begin checking whether we can report back
reportInterfaces();

API Methods Used

Further Reading