Skip to main content

Knowledgebase

Search knowledgebase

Hints & Tips
How to load an x-bit integer into a 32-bit Squirrel integer and retain the sign

To sign-extend a 16-bit (or other size less than 32) integer in Squirrel, shift it left so that the sign bit is in the 32-bit sign bit (bit 31), then shift it right again by the same amount:

// 16-bit signed to 32-bit signed
local value = 0xC123;
server.log((value << 16) >> 16);
// 8-bit signed to 32-bit signed
local value = 0xFF;
server.log((value << 24) >> 24);
How to determine resources sizes before downloading them

When using an agent to download a very large file, it is helpful to determine the size of the file first so that you can request the file in manageable chunks.

The standard way to do this is to use a HEAD request: call http.request() with "HEAD" as the first argument. The request can then be sent, and the response’s Content-Length header examined. Unfortunately, this operation may fail: the request will timeout.

To avoid this, make a GET request which contains the Range: bytes=0-0 header. The response will contain a Content-Range header which contains the size of the file: eg. 0-1/17616, where 17616 is the size of the file in bytes.

local resp = http.get("https://example.com/images/imp.png", {"Range": "bytes=0-0"}).sendsync();
local parts = split(resp.headers["content-range"], "/");
local filesize = parts[1].tointeger();
How to display the WiFi signal strength where an imp is placed

You can log the strength of the WiFi signal an imp is picking up very easily with the following code:

function reportRSSI() {
    local rssi = imp.rssi();
    local bars = 5;

    if (rssi < -87) {
        bars = 0;
    } else if (rssi < -82) {
        bars = 1;
    } else if (rssi < -77) {
        bars = 2;
    } else if (rssi < -72) {
        bars = 3;
    } else if (rssi < -67) {
        bars = 4;
    }

    if (bars == 1) {
        server.log("Signal Strength: " + rssi + "dBm (1 bar)");
    } else {
        server.log("Signal Strength: " + rssi + "dBm (" + bars + " bars)");
    }
}
How to signal a successful application code update

To make an imp’s LED flash green when a new application firmware version has been successfully installed on the device and run — after you hit the ‘Build and Force Restart’ button in impCentral — add the following lines to the start of your device code:

imp.enableblinkup(true);
imp.wakeup(30.0, function(){
  imp.enableblinkup(false);
});

This will blink the imp’s light for 30 seconds whenever it starts up, including power-cycles and waking from deep sleep. Set this for a decent duration to give you time to re-BlinkUp while the last WiFi network is still ‘visible’.

How to scan an I2C bus for devices

The following code snippet provides a very simple way of determining the addresses of devices connected to any one of an imp’s I²C buses. The code here assumes the bus in use is the imp001’s hardware.i2c89, but can easily be changed to, for example, hardware.i2cAB or hardware.i2cFG on the imp003, or one of the imp004m’s or imp005’s I²C buses:

i2c <- hardware.i2c89;   // Set to desired I2C bus
i2c.configure(CLOCK_SPEED_100_KHZ);

for (local i = 2 ; i < 256 ; i+=2) {
  if (i2c.read(i, "", 1) != null) {
    server.log(format("Device at address: 0x%02X", i));
  }
}
How to identify your device’s type of imp

impOS™ 36 added a new imp API method, imp.info(), which will provide a string indicating the type of imp your code is running on:

local impType = imp.info().type;
server.log(impType);
How to load more than 512KB of data using HTTP

Agents can currently fetch no more than 512KB at any one time using HTTP. This is not a serious limitation in most circumstances, and is more data than the device can sensibly deal with in any case. But for the rare situations in which fetching a larger file is needed, there is a workaround: the file can be streamed, by fetching it in sections using the HTTP ‘Range’ header.

The following code streams the Electric Imp homepage in 1KB lumps. The page is actually much smaller than 512KB, so could have been fetched in one go, but it nonetheless demonstrates the technique well.

function fetch() {
  offset <- 0;
  const LUMP = 1024;
  local URL = "http://electricimp.com";
  local header = {Range = format("bytes = %u - %u", offset, offset + LUMP)};

  do {
    response <- http.get(URL, header).sendsync();

    server.log("Status code: " + response.statuscode);
    server.log("Headers:");

    foreach(key, value in response.headers) {
      server.log(key + " = " + value);
    }

    got <- response.body.len() - 1;
    offset += got;
  }
  while (response.statuscode == 206 && got == LUMP);

  server.log("Got " + offset + " bytes total");
}

To determine the size of the entity you wish to fetch before you fetch it, use the following code:

local resp = http.get("https://example.com/images/imp.png", {"Range": "bytes=0-0"}).sendsync();
local parts = split(resp.headers["content-range"], "/");
local filesize = parts[1].tointeger();
How to acknowledge receipt of a message

The following code provides a very simple — if inelegant — way of acknowledging receipt of a message. The code here assumes the message has been sent from agent to device, but the same technique can be used for messages travelling in the opposite direction: just flip agent.send() to device.send() and agent.on() to device.on().

agent.on("message.from.agent", function(data) {
  // Send acknowledgement
  agent.send("ack", true);

  // Process message's data payload as normal
  //(code omitted)
});

If you are looking for a more sophisticated approach to managing agent-device communications, please take a look at our Message Manager library. It allows the basic imp messaging API to be extended in a number of interesting and useful ways.

How to Use Regular Expressions to Parse Incoming XML Data

Many web services return data in JSON format, and the imp API has a number of useful tools for converting JSON data into more readily usable forms. However, some web services send data as XML, and this can be more tricky to extract, especially if the order of the XML tags is not constant. Fortunately, impOS™ provides a regular expression object, and this can be used to examine the incoming XML for specific tags.

For example, your agent may receive some XML like this:

<NotifyReturn>
  <ResponseCode>0</ResponseCode>
  <CallAnswered>false</CallAnswered>
  <QueueID>646253102</QueueID>
  <TryCount>0</TryCount> <Duration>13</Duration>
  <CallComplete>false</CallComplete>
</NotifyReturn>

Regular expressions can be used to find specified characters no matter where they appear in the data. In this example, we want to extract the QueueID value when it contains two or more numerical characters. To use regular expressions in Squirrel, we first instantiate a regexp2 object using impOS’ new regexp2() function. You can see this in the code below, in the first non-comment line of the function. We pass the string we’re searching for, including both specific characters (the XML tags we’re interested in) and between them the regular expression code to be matched against the incoming data. Here we use:

([0-9]+)

which says match against any number of characters 0 through 9. The square brackets delimit the range of characters we’re interested in; the plus symbol says ‘match against one or more characters’.

With the regexp2 object instantiated, we call its capture() method, passing the data we want to match the regular expression to. This method returns an array of tables, one table for each match. Each table contains two keys, begin and end, which are the positions within the passed string data of a sub-string matching the regular expression.

We check that we have at least two matches and if so use the second of the two tables in the results array. We use the begin and end values to extract the number embedded in the XML and return it as a string. The first entry in the results array includes the XML tags, so we ignore it.

function getQueueID(xmlString) {
  // Set the regexp object to the string we're searching for
  // <QueueID>xyz</QueueID> where xyz contains arbitrary digits
  local rexp = regexp2(@"<QueueID>([0-9]+)</QueueID>");
  local results = rexp.capture(xmlString);
  local queueID = "";

  if (results && results.len() > 1)  queueID = xmlString.slice(results[1].begin, results[1].end);

  return queueID;
}

If we were interested in the CallAnswered tag, we could match against alphabet characters rather than numbers:

([a-z]+)

and using the example XML above, we’d get false returned. A small piece of Squirrel converts this to Boolean values:

return (queueID == "false" ? false : true);

Regular expressions can be used to detect complex patterns of characters as well as the simple ones used in the example above. Take a look at the the documentation on the regexp2 object for more information on how to assemble regular expressions for string matching.

The regexp2 object makes use of the Google re2 regular expression engine, which is a considerable improvement upon the standard Squirrel regular expression evaluation functionality provided by regexp. We recommended new code uses regexp2 in preference to the standard functionality. However, re2 memory usage issues mean that regexp2 is currently only available in agent code, a regexp2 regular expression is limited to 256 characters, and each agent is only allowed to have up to ten regexp2 objects simultaneously. The standard functionality will continue to be offered for device code and to maintain compatibility with older agent code.

How to make your Squirrel code more compact

Here are some general tips for reducing the size of your device or agent code. For a more in-depth discussion of some of the techniques you can use to boost performance and reduce your code’s memory footprint, please see ‘Writing Efficient Squirrel’.

  • Keep your variable names short — though you should make sure it’s still clear what information they hold.
  • Comment out variables you have added but not used. You can always ‘un-comment’ them later.
  • Do you use all the methods defined by a custom class? If not, comment out the ones you don’t need. Use /* and */ to block out large sections of code. Again, you can always ‘un-comment’ them later.
FAQs
How do I determine the impCloud servers’ IP address range?

We do not expose external IP address ranges for public impCloud™ servers.

The public impCloud has many IP ranges, and all addresses change at every deploy. As such, the device cannot ‘expect’ to connect to a specific IP address or range of IP addresses. In order to maintain the impCloud’s high levels of reliability and scalability, devices must be able to move from server to server for load balancing, or because of a hardware failure. A given devices may therefore connect to different servers with very different addresses at different times in its lifecycle. Again, fixed IP addresses would severely hinder this mobility.

If you require server IP addresses for the purposes of whitelisting devices at a network firewall, we offer a proxy service which routes the public impCloud servers via a proxy server at a fixed IP address. For more information about this additional service, please contact Electric Imp Sales.

How do I determine an agent’s IP address?

The impCloud™ servers on which agents are hosted are not maintained at fixed IP addresses. In addition, any given server may be host to a great many individual agents. As such, we do not provide details of agents’ IP addresses, only their URLs, which can be accessed via the agent-side imp API method http.agenturl().

Alternatively, you can use the impCentral™ API to access a device‘s record to learn its agent URL, via the device data attributes object’s agent_id key. The agent URL is currently of the form https://agent.electricimp.com/{agent_id}. You will need to log in to retrieve an access token, which you then use to authorize a request for the device record:

curl -v 'https://api.electricimp.com/v5/devices/{device_ID}' 
    -H 'Authorization: Bearer {ACCESS_TOKEN}'

Finally, an agent’s URL is also displayed in the impCentral code editor.

How do I determine an imp’s IP address?

Call imp.net.info() in your device code. This will return a table of network-specific information. You need the ipv4 key — its value is also a table and this contains the key address which will be the imp’s current IP address as a string, eg. "192.168.0.3". This value can be written to the device log, or relayed via UART.

Alternatively, you can use the impCentral™ API to access devices’ records to learn their most recent IP address, via the device data attributes object’s ip_address key. You will need to log in to retrieve an access token, which you then use to authorize a request for the device record:

curl -v 'https://api.electricimp.com/v5/devices/{device_ID}' 
    -H 'Authorization: Bearer {ACCESS_TOKEN}'
How large can my Squirrel code get?

The Electric Imp IDE, and the impCentral API, put a limit of 4MB on your Squirrel agent code. The same limit is also applied to your device code. Neither agent nor device code can be larger than 4MB.

What happens if I use the BlinkUp API key from a different account in my app?

The BlinkUp API key you use to make calls to the BlinkUp SDK’s methods must be the one associated with your account. If this is not the case — for example, it is used with devices blessed to application firmware that is associated with a different account than yours — then your end-users will not be able to perform BlinkUp with your app successfully and they will find their devices are unresponsive.

How do I customize the text in a BlinkUp interface?

Almost all of the text in the BlinkUp process can be customized for your application. The Android BlinkUp SDK allows you to set these programmatically; the iOS BlinkUp SDK includes a file of strings which can be edited. Both SDKs also include default BlinkUp strings.

What is the difference between standard and custom BlinkUp?

Standard BlinkUp presents a user interface generated by the BlinkUp SDK. It allows the user to select which network they wish to connect to, and then perform a BlinkUp. You and your app developer can customize much of the text presented throughout the process.

If you feel you want more control over the look and feel of the BlinkUp process, you can do a custom BlinkUp. This integrates BlinkUp into your own network-selection UI. More information on this process can be found in the documentation accompanying the BlinkUp SDK.

Can I have the same agent URL for a device no matter what?

This is not recommended and should be avoided. Each user has their own plan ID to ensure proper security. Please spend some time architecting your use and management of agent URLs in your app and on your server. Develop a proper plan for the agent URL before launch.

Why does a device’s agent URL keep changing?

The agent URL is derived from both the device ID and the plan ID. If either of these tokens change, a new URL will be generated and the previous agent URL associated with the device ID will be invalidated.

The device ID never changes. The plan ID can be changed — by requesting a new one from the Electric Imp impCloud™ — but this should be avoided in order to prevent the loss of a user’s agent and device settings. Using a new plan ID, even for the same device, will always generate a new agent.

A pre-existing plan ID must be passed to a BlinkUpController object before the BlinkUp process is initiated, otherwise a new plan ID will be requested by the BlinkUp SDK automatically. If the same device is set up with the same plan ID, the agent URL returned will be the same as before.

The plan ID should be unique per user. If you have a single user — ‘George’, for example — you can use the plan ID generated for George each time he uses BlinkUp on a device. It is your job to keep track of the association between plan ID and users. If you need to keep track of unique devices, the device ID should be used.

Why does it sometimes take so long to get an agent URL?

If an issue occurs during the BlinkUp process, the agent URL may never be returned. In this case, a delegate method (iOS) or callback method (Android) will be called with an error after one minute. The BlinkUp may fail for a number of reasons, such as incorrect WiFi settings, obstructions between the phone’s screen and the device’s photosensor, and the device’s on-board imp being unable to connect to the Electric Imp impCloud™. It is possible to change the default timeout.

Technotes
Endian-ness in Squirrel code

Please see this Developer Guide: ‘Endian-ness in Squirrel Code’.

How to temporarily prevent impOS and Squirrel updates from being installed

impOS™ now provides ‘polite deployment’ functionality. This allows application code to register to be informed when there is an impOS and/or Squirrel code update available to be deployed, and to take action to defer the installation of such updates.

To learn how to apply this functionality, please see the following documentation:

Calling functions vs referencing functions

Many methods in the imp API allow you to register a function that will be automatically called when at some time in the future an event takes place. These functions are called ‘callback functions’ or ‘callbacks’.

Callbacks are registered by providing the imp API method with a reference to the function. This essentially tells the method which function it should call. To indicate a reference, use its name — for example:

device.on("message.from.agent", responseHandler);

Passing a reference to a function is not the same action as calling that function. This often confuses even experienced programmers who are nonetheless new to Squirrel and the imp API. For example, they write:

imp.wakeup(1.0, aCallbackFunction());

When Squirrel encounters this, it parses the line and sees that it must call aCallbackFunction() first in order to determine what value should be passed into imp.wakeup()’s second parameter. Of course, aCallbackFunction() may return a function reference, but it it does not, its return value will still be used by Squirrel as the reference to the function to be called when the timer fires. The result is unpredictable, but will most likely cause a runtime error.

The rule of thumb, then, is: to execute a function now, append brackets to the function’s name; to register the same function for execution later, omit the brackets.

Agents have a 30-day lifespan when their device is offline

When a device goes offline, its agent will stay online and accessible via its unique URL. However, if the device remains offline for an extended period of time — currently 30 days — the agent will be shut down automatically to conserve server resources. The agent is stopped; it is not deleted.

If a mobile application, web site or third-party server attempts to contact the agent after this point, they will receive the standard ‘resource missing’ HTTP status code, 404, indicating that the agent is offline.

Be aware, however, that a 404 status code is not a guarantee that an agent has been stopped. An agent can itself issue a 404 HTTP status code; it does so when it receives an HTTP request but has no HTTP request handler function registered using http.onrequest().

As and when a device comes back online, the disabled agent will automatically be restarted. Any data previously preserved using server.save() will still be accessible. The rebooted agent will continue to be contactable at the same URL as before.

How to Manage Agent and Device Code Version Mismatches

Please see ‘Polite Squirrel Deployment’ for the latest information.

An imp won’t reconnect after a WiFi password has been cleared

If you configure an imp-enabled device to connect to your password-protected WiFi network but subsequently downgrade the security of the network by removing the password, the on-board imp will no longer connect to the network. To re-connect, the device must be reconfigured with a blank password.

This is by design. It is intended to prevent your device (and other devices on the network) being ‘captured’ by a rogue WiFi access point masquerading as your network router. It can do this by transmitting the same SSID as your network but at a higher signal strength, but without the password, which the assailant does not know.

Known Issues
Using imp-enabled Devices with UniFi AP-AC-Pro Access Points

UniFi AC-Pro AP access points may cause imp004m- and imp005m-based devices to disconnect if the access point’s ‘Enable connectivity monitor and wireless uplink’ option is set. In this case, we recommend you disable this feature in the access point’s settings under the ‘Services’ section:
Unifi access point control
Please note, however, that this may prevent any access points which connect solely via wireless from connecting to your network.

Using imp-enabled Devices in the UK with BT HomeHub Routers

BT’s HomeHub routers, available in the UK, include a feature called Smart Setup which uses a captive portal wizard to connect new devices for the first time. Captive portals are not supported by imp-enabled devices and therefore Smart Setup will prevent them from connecting to the Internet.

If an imp-enabled device is not connecting via a BT HomeHub 4, 5 or later ,disable Smart Setup](http://bt.custhelp.com/app/answers/detail/a_id/44328/%7E/what-is-smart-setup-on-the-bt-hub%3F-how-can-i-turn-it-on-and-off%3F). Typically, the imp will connect to the HomeHb’s WiFi network but not to Internet services, including the Electric Imp impCloud™.

Once a device has connected via the HomeHub, it will no longer be presented with the captive portal, so it is safe to re-enable Smart Setup if you wish. The imp will now connect whenever it needs to. You may need to temporarily disable Smart Setup again if you ever perform a factory reset on the HomeHub.