Skip to main content


Search knowledgebase

Hints & Tips
How to determine resources sizes before downloading them

When an agent is being used to download a very large file, it is very helpful to be able to determine the size of the file before it is retrieved. This allows you to request the file in manageable chunks.

The standard way to do this is to use a HEAD request: in Squirrel, you would call http.request() and pass "HEAD" into the first parameter. The request can then be sent synchronously or asynchronously, and the response’s Content-Length header examined.

Unfortunately, this operation may fail: the request will timeout.

To avoid this, please apply the following workaround: make an initial 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. The following code shows how this can be used:

local resp = http.get("", {"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 Firmware 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 Run’ button in the IDE — add the following lines to the start of your device code:

imp.wakeup(30.0, function(){

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

for (local i = 2 ; i < 256 ; i+=2) {
    if (, "", 1) != null) server.log(format("Device at address: 0x%02X", i));
How to Identify Your Device’s Type of imp

impOS™ 36 will add a new imp API method,, which will provide a string indicating the type of imp your code is running on. When impOS 36 is released to production, this page will be updated to make use of that method.

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 = "";
    local header = {Range = format("bytes = %u - %u", offset, offset + LUMP)};

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

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

        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 http.request() and make an HTTP/1.1 HEAD request:

local httpreq = http.request("HEAD", URL, "", "");
local resp = httpreq.sendsync();
local resource_size = resp.headers.rawget("content-length");
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 Bullwinkle 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:

    <TryCount>0</TryCount> <Duration>13</Duration>

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:


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:


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

if (queueID == "false") {
    return false;
} else {
    return 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 mare Squirrel run more efficiently to boost performance and reduce the code’s memory footprint, see the Developer Guide ‘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.
How to Convert an Object’s Name to a Reference

The following function takes a string containing an object’s name and parentage — using standard dot notation — and returns a reference to the object in memory.

For example, if the string passed to the function is "myclass.mytable.myelement", the function will return a reference to the object myelement, provided it is a valid object.

function resolveReference(pathString) {
    local pathArray = split(pathString, ".");
    local object = this;

    foreach (element in pathArray)  {
        if (element in object) {
            object = object[element];
        } else {
            return null;

    return object;
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.

What is an agent URL?

The agent URL is the Internet address of the unique agent assigned to the device being configured by BlinkUp. When the BlinkUp process completes successfully, the agent URL is returned to the app performing the operation. It should be recorded: the app will need to use it to interact with the agent in future, for instance to issue commands to the device or to request information from the device.

What is a device ID?

The device ID — previously termed the impee ID and still referred to by the SDK as such — is an imp’s authentication ID. It is used to identify which devices are tied to your developer account and which production devices should get the application firmware they were assigned. You can view a developer device’s ID in the IDE, at the top of the device code pane. A device’s ID never changes.

What is a plan ID?

The plan ID is a token that is used to identify a device’s user. New users should be given a new plan ID, which your app should store and use in future BlinkUp operations by setting the planID property of the BlinkUpController object instanced in your app code. A user’s plan ID should be retained and used this way to allow them to re-configure the device’s network access without the loss of agent and device settings. Using a new plan ID, even for the same device, will always generate a new agent.

Troubleshooting Device-to-Agent Data Transfers

When you send data from an imp-enabled device to its agent using agent.send(), impOS™ encrypts the data and puts it into a TCP packet ‘first in, first out’ (FIFO) send buffer which empties into the WiFi chip’s internal buffer. The WiFi chip schedules the packet transmissions depending on radio conditions, and will retransmit if no acknowledgement (ACK) is received from the access point. This process continues until all the data in the TCP FIFO has been sent.


When the Buffer Fills

If you send a large volume of data or make many agent.send() calls in rapid succession, it is possible to fill impOS’ send buffer. This will prevent subsequent agent.send() calls from adding data to the buffer, and this is also the outcome if WiFi has disconnected: the impOS buffer can’t be emptied because the WiFi sub-system can’t empty its own buffer.

This situation can cause seemingly strange connection and program execution behaviour. For instance, by default agent.send() blocks until its data is written into the buffer (another possibility is discussed below). If the send buffer is full, agent.send() may block until send timeout is reached (30 seconds by default), which can cause the device to appear unresponsive.

Fortunately, this and many other parts of the device-to-agent data transfer process can be configured to minimize the impact of this behavior.

Increase the TCP Send Buffer Size

If you need to send particularly large amounts of data, consider increasing the TCP send buffer’s size using imp.setsendbuffersize(). The default size is 3456 bytes (six packets of 576 bytes each), but this can be increased to 30KB, free memory permitting. It is only possible to increase the buffer’s size; it can only be reduced by restarting the imp, which sets it to the default.

function increaseBuffer(dataToSend) {
  local oldSize = 0;
  local newSize = dataToSend.len();
  if (newSize > 3456) oldSize = imp.setsendbuffersize(newSize);
  server.log("Buffer increased to " + newSize + " bytes from " + oldSize + " bytes");

Wait for Buffer Additions or Server Acknowledgements

You can control whether agent.send() returns as soon as the data has been placed in the impOS send buffer or waits until the data has been passed to WiFi, sent and its receipt acknowledged by the imp server. The first of these is recommended for most applications and so is the default behavior, though it can be selected explicitly by passing the constant WAIT_TIL_SENT onto the method server.setsendtimeoutpolicy()’s second parameter.

However, once the packet leaves impOS’ FIFO, impOS can’t tell whether the packet has been sent successfully, so a packet leaving the FIFO does not guarantee delivery: it could be stuck within the WiFi chip, within the WiFi access point, or in an intermediate router between the WiFi network and the imp server. To force the imp to wait until it has received acknowledgement from the server of receipt of the data, call server.setsendtimeoutpolicy() at the start of your device code and pass the constant WAIT_FOR_ACK as its second parameter. WAIT_FOR_ACK allows you to call agent.send() again safe in the knowledge that the impOS buffer is now clear, provided no disconnection has taken place of course (see below).

Note that WAIT_FOR_ACK can decrease throughput noticeably, and it does not guarantee the agent will get the message, only that the server has received it. If the agent is overloaded with messages or in the process of restarting, for example, the data could still be dropped. The only way to be sure is to code the agent to respond to the successful arrival of a message with a message of its own.

server.settimeoutpolicy(SUSPEND_ON_ERROR, WAIT_FOR_ACK, 10);

// Program does some work...

foreach (count, blob in buffer) {
  // Every send will not return until the server has ACK’d
  // Assume no timeout for this example
  local outcome = agent.send(“”, blob);
  server.log(“Blob %u sent to server”, count + 1);

Alter the Timeout

Whichever of these approaches you choose, you can specify how long agent.send() waits for the data to be placed in the send buffer or for the server to acknowledge receipt of the data at protocol level. This is done by setting server.setsendtimeoutpolicy()’s third parameter, which is a timeout value in seconds; the default is 30 seconds. agent.send() will block until it returns; setting a shorter timeout ensures the call returns more quickly if the operation times out: it is unable to add the data to the buffer (the buffer is full) or the agent is taking longer than expected to acknowledge (it may have disconnected).

Test for Send Success or Failure

agent.send() always returns a value to inform you the result of its operation: a 0 if it was successful, or a Send Error Code to indicate the cause of failure:

Code Constant Description
SEND_ERROR_NOT_CONNECTED There is no connection to the server
SEND_ERROR_TIMEOUT The timeout expired before all the data was sent to the server
SEND_ERROR_DISCONNECTED The connection was disconnected before all data was sent to the server

Reading this return value allows your code to decide how to proceed. For example, non-critical data may be discarded and important data stored for re-transmission once the connection with the server is back up.

If your code is working to the default disconnection policy, SUSPEND_ON_ERROR, on anything other than a success, the imp will halt Squirrel execution immediately while impOS attempts to reconnect to the server. However, if you have chosen the RETURN_ON_ERROR policy — by passing this constant as server.setsendtimeoutpolicy()’s first parameter — you can adapt your code’s response to an agent.send() failure by examining the call’s return value: again, storing or rejecting data in the case of a disconnection, or attempting to resend the data after a suitable pause in the case of a timeout:

server.settimeoutpolicy(RETURN_ON_ERROR, WAIT_TIL_SENT, 10);

dataToSend <- null;
retryTime <- 600;  // Ten minutes

// Code at some point will place the data it needs to send into dataToSend and then call
// transmitter(), a wrapper for agent.send() and related calls, to send it.
function transmitter() {
  // Make sure we have data to send
  if (dataToSend == null) return;
  if (server.isconnected()) {
    // At this point we have WiFi connected so try to add dataToSend to TCP send buffer
    local outcome = agent.send(“message”, dataToSend);
    if (outcome == 0) {
      // Record successful addition of data to impOS buffer by zero-ing dataToSend
      // This can be used to indicate dataToSend is free for more data
      dataToSend = null;
    } else {
      // Couldn’t add dataToSend to the buffer OR
      // WiFi has been lost OR some other failure
      // RETURN_ON_ERROR in force so we need to re-connect before
      // attempting to add dataToSend to the buffer again

      // Reschedule transmitter()
      imp.wakeup(retryTime, transmitter);
  } else {
    // WiFi is disconnected, so attempt to reconnect as per RETURN_ON_ERROR
    // Whatever the outcome, we come back to transmitter().
    // We can't schedule transmitter() directly as server.connect()'s
    // callback expects a single parameter to take outcome indicator integer
    server.connect(function(reason) { 


This chart summarizes the possible outcomes of agent.send():

agent.send() timeline

  1. Squirrel code calls agent.send().
  2. The policy is WAIT_TIL_SENT.
    impOS writes the data to the TCP Send Buffer.
    agent.send() returns the value 0.
  3. The policy is WAIT_TIL_SENT.
    The attempt to write the data to the buffer times out.
    If SUSPEND_ON_ERROR is in force, Squirrel halts.
    If RETURN_ON_ERROR is in force, agent.send() returns SEND_ERROR_TIMEOUT.
  4. The policy is WAIT_FOR_ACK.
    The server ACK to WiFi is relayed to impOS.
    impOS notifies agent.send() which returns the value 0.
  5. The policy is WAIT_FOR_ACK.
    The wait for the server ACK times out.
    If SUSPEND_ON_ERROR is in force, Squirrel halts.
    If RETURN_ON_ERROR is in force, agent.send() returns SEND_ERROR_TIMEOUT.
Calling Functions vs Registering 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. The function being referred to can be declared inline within the method call, or separately:

// Inline callback declaration
device.on("message.from.agent", function(data) {
  server.log("Message received from agent");
  if (data) server.log("Contains " + data.len() + " bytes of data");
// Separate callback declaration
function cb(data) {
  server.log("Message received from agent");
  if (data) server.log("Contains " + data.len() + " bytes of data");

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

The use of a separate declaration is particularly useful as it allows a function to call itself:

loopCount <- 0;

function loop() {
  server.log("Cycle count: " + loopCount);
  imp.wakeup(1.0, loop);

In both examples of the use of a separate callback declaration, the callback is registered by passing its name not by calling it. 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() immediately in order to determine what value should be passed into imp.wakeup()’s second parameter. It does so, and then processes the remainder of the line, which sets up a timer to fire in 1.0 seconds, when it will call the function named in the second parameter. Unfortunately, because it has actually called aCallbackFunction() already and received a return value (or null), this return value is used as the reference to the function that should be called when the timer fires. The result is unpredictable, but will most likely cause a runtime error.

The one time where this syntax will work is when aCallbackFunction() returns a function reference. For example:

function aCallbackFunction() {
  return function() {
    server.log("Callback called");

Now, when the imp.wakeup() line is parsed and aCallbackFunction() executed, the timer is cued to execute the inline function. So in one second’s time, the line "Callback Called" will appear in the log.

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

Agents Have a 30-Day Device-less Lifespan

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. End-users may report such errors if, for example, they attempt to configure a device using its mobile app when that device has been offline for more than a month. Whether the app reports this failure to access the agent as a 404 error or some other message will depend on how your app has been written, and whether the app communicates directly with the agent or routes such messages via your server.

End-users experiencing such communications errors should be instructed to ensure their devices are online. If necessary, they should power-cycle the device and confirm visually that it is connecting. This verification can be made by observing the device’s LED for the status codes it presents.

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 and this takes place when the agent is online, receives an HTTP request but has no HTTP request handler function as registered using http.onrequest() to deal with it (see ‘HTTP Request Response Codes’).

As and when the device comes back online, the disabled agent will automatically be restarted. Any data previously preserved using will still be accessible. The rebooted agent will continue to be contactable at its prior URL — it does not gain a new URL.

How to Manage Agent-device Code Mismatches

When new application code is deployed, there may be a significant gap between the new code beginning to run on agent and device.

Agents that are running a given deployment will be updated with new code one after the other. There is a delay of 100ms between one agent restarting and the next one doing so. Devices are also restarted at 100ms intervals. This is intentional: the delay prevents customers’ backend servers from becoming overloaded as they would be if agents were restarted en masse and all attempted to contact the customer’s backend simultaneously.

The order of the agent and device restarts is not currently pre-defined and so a device may restart with the new code before its agent does, and vice versa. Consequently, there may be an arbitrarily long period when a device might be communicating with an agent running an older or newer version of the assigned application firmware.

This period can become more extensive as the number of agents and devices that are part of a given product increases. For example, if 15,000 agents are running certain application firmware, deploying updated agent code at a rate of ten agents every second (ie. one every 100ms) means that the deployment will take at least 25 minutes to complete. It is therefore possible that a device might be running different code from its agent for a period of up to 25 minutes.

Looking ahead, ‘polite deployment’ provision for application firmware is on the Electric Imp roadmap and when this is implemented, each agent-device pair will be updated simultaneously, followed by the 100ms delay to prevent customers’ backend servers being overloaded. Until that time, we recommend customers implement the following workaround:

  1. Customers should internally version their application firmware and enter that version in both agent and device code.
  2. At start-up, the agent should request the device’s application version number and compare it to its own. If there is a mismatch, the agent can take appropriate action:
    • If the agent is ahead of the device, it can instruct the device to initiate a manual disconnect-reconnect cycle, which should cause the device to acquire the updated code.
    • If the agent is lagging behind the device, it can call server.restart() to trigger a reboot, this time resulting in the agent receiving the latest version of the application code.
    • If the two versions match, take no action.

This is demonstrated by the following agent code:

const AGENT_VERSION = "3.4.5";

function checkVersion() {
  // Register a function to check the incoming device code version
  device.on("send.devicecode.version", function(deviceVersion) {
    // 'deviceVersion' is a string of the form x.y.z
    local dv = deviceVersion.split(".");
    local av = AGENT_VERSION.split(".");

    // Compare version number components left to right
    for (local i = 0 ; i < 3 ; i++) {
      if (av[i].tointeger() > dv[i].tointeger()) {
        // Agent version component is higher than device's
        // so device should restart to update
        device.send("action.device.restart", true);
        imp.wakeup(10, checkVersion);

      if (av[i].tointeger() < dv[i].tointeger()) {
        // Agent version component is lower than device's
        // so agent should restart to update

  // Ask the device for its code version
  device.send("request.devicecode.version", true);


And the following device code:

const DEVICE_VERSION = "3.4.5";

// Register a function to handle device code version requests
agent.on("request.devicecode.version", function() {
  // Immediately return the device code version to the agent
  agent.send("send.devicecode.version", DEVICE_VERSION);

// Register a function to initiate a restart
agent.on("action.device.restart", function (dummy) {
  imp.onidle(function() {
    // When the imp goes idle, restart after five seconds' deep sleep
Squirrel Error Messages

Squirrel has many runtime error types pre-programmed. They are largely self-explanatory. However, not all of the errors defined by the standard version of Squirrel are included in the imp version of the language: errors relating to files, for instance, are absent because the Electric Imp platform doesn’t support files.

Here is a list of error messages you may encounter:

  • A closure with free valiables bound cannot be serialized
  • Arith op [operator] on between [variable name] and [variable name]
  • Assertion failed
  • Attempt to call [entity]
  • Attempt to delete a slot from a [variable type]
  • Attempt to negate a [variable type]
  • Attempt to perform a bitwise op on a [variable type]
  • Bitwise op between [variable name] and [variable name]
  • Call failed
  • Cannot apply instanceof between a [entity] and a [entity]
  • Cannot clone blob
  • Cannot convert the string
  • Cannot create blob
  • Cannot create blob with negative size
  • Cannot delete a slot from [table]
  • Cannot deserialize a [variable type] (0x%x)
  • Cannot iterate a generator
  • Cannot iterate [name]
  • Cannot resize stack while in a metamethod
  • Cannot serialize a [variable type]
  • Cannot set property of object of type [variable type]
  • Cannot suspend through native calls/metamethods
  • Cannot use non-cloneable type for default parameter
  • Class instances do not support the new slot operator
  • Clear only works on table and array
  • Cloning a [type]
  • _cmp must return an integer
  • Compare function failed
  • Comparison between [variable type] and [variable type]
  • CRT API failure
  • Delegate cycle
  • Division by zero
  • Division overflow
  • Empty array
  • Empty separators string
  • Float expected for the specified format
  • Format too long
  • Idx out of range
  • Inconsistent compare function
  • Indexing [variable name] with [variable name]
  • Index out of range
  • Integer expected for the specified format
  • Internal error (_nexti) wrong argument type
  • Invalid base type
  • Invalid environment
  • Invalid format
  • Invalid free var index
  • Invalid index type for an array
  • Invalid object type
  • Invalid or corrupted closure stream
  • Invalid origin
  • Invalid param / Invalid parameter
  • Invalid param type
  • Invalid typemask
  • Invalid type tag
  • Invalid type / Inalid type, expected table
  • Modulo by zero
  • Modulo overflow
  • Native closure expected
  • Native stack overflow
  • Negative size
  • New member only works with classes
  • _nexti failed
  • _nexti returned an invalid idx
  • No closure in the calls stack
  • No data left to read
  • Not enough parameters for the given format string
  • Not enough params in the stack
  • Null cannot be used as index
  • Null is not a valid key / Null key
  • Numeric value expected as return value of the compare function
  • Object must be a class
  • Only generators can be resumed
  • Out of memory
  • Parameter [name] has an invalid type [variable type]
  • Precision format too long
  • rawget works only on array/table/instance and class
  • rawset works only on array/table/class and instance
  • remove() failed
  • rename() failed
  • resize failed
  • Resuming active generator
  • Resuming dead generator
  • Size must be a number
  • Slice out of range
  • Stack overflow
  • Stack overflow, cannot resize stack while in a metamethod
  • String expected for the specified format
  • Table expected
  • The blob is invalid
  • The class is locked
  • The index doesn’t exist / The index [name] does not exist
  • The object is not a class
  • The object is not a class instance
  • The object is not a closure / The object is not a native closure
  • The object must be a weakref
  • The property [name] already exists
  • The target is not a closure
  • The type doesn’t have a default delegate
  • This object cannot be cloned
  • top() on a empty array
  • Trying to inherit from a [variable type]
  • Trying to modify a class that has already been instantiated
  • Trying to resume a [entity], only genenerator can be resumed
  • Trying to yield a [entity], only genenerator can be yielded
  • Unknown error
  • Unrecognized encoding
  • Width format too long
  • Wrong argument type
  • Wrong index / Wrong indexes
  • Wrong number of parameters
  • Wrong param / Wrong parameter
  • Wrong type / Wrong type (expected class or instance)
How to Fix ‘no handler for device.send()’-type Errors

When agent code calls device.send() and there is no equivalent agent.on() in the device code, the device will log a error like this:

2015-07-07 19:54:17 UTC-5  [server.error]  ERROR: no handler for device.send()

This may seem odd, since the device can’t call device.send(), but it’s simply a sign that the device has received a message transmitted by a device.send() from the agent.

The usual way to debug this is to compare all the instances of device.send() in the agent code to ensure they are accompanied by an agent.on(). The first parameter of both calls, a string containing an identifier for the message being sent, must match across the two paired calls. A common source of error is a mis-typed message name string in either of the two calls.

What if you have eliminated such issues as causes of the error? In this case, it may be that you are experiencing a race condition. If, for instance, your agent code calls device.send() as soon as it boots, but the paired agent.on() is near the bottom of your device code, the message can be sent and received before the device’s Squirrel interpreter has been able to register the handler function you assign in the agent.on() call. Because the code hasn’t yet performed this registration when the message arrives, it will throw the above error.

If this is the case, you can apply any of the following techniques:

  • Register the device’s message handlers as early as possible in the code sequence.
  • Delay the device.send() call on the agent side to allow the device time to complete its setup. For example:
imp.wakeup(1, function() {
  // Wait 1 second for the device to boot before
  // sending it the first message
  device.send("my.message", data);
  • Have the device signal its readiness to the agent and ensure that no messages are sent from agent to device until this signal has been received. For example:
device.on("ready", function() {
  // Device has signalled its readiness - send over saved settings
  device.send("clock.set.preferences", settingsTable);

An equivalent error involves a missing handler for agent.send() posted by the agent. Again, this is caused by either a message from the device arriving before its intended handler function has been registered, or the other causes described above.

An imp Won’t Reconnect After a WiFi Network 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. 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.