Skip to main content

How To Port Arduino Code To The imp

Guidelines To help You Convert C++ Code To Squirrel

Many developers of imp-enabled products want to take advantage of the huge number of libraries of open source software available for the Arduino microcontroller platform. Typically, they do so because they’ve bought a peripheral device to connect to their own product, but the sample code that illustrates how the peripheral works was written for Arduino users.

At the same time, quite a few Arduino users decide to try the Electric Imp Platform. They want to do so using code they have already written for their own microcontroller-based projects. They know their code works and they don’t want to have to develop from scratch a program that does the same job.

Both groups of users have a common need: to understand how to convert Arduino programs into code that will run on an imp. This Developer Guide is intended to help them do just that.

Projects for both platforms are incredibly diverse, so it’s not possible to provide a totally comprehensive framework that will allow you to take a complex Arduino listing and quickly convert it to Squirrel and imp API calls. But it is possible to distil from these many projects a number of principles you can apply to simplify the process of porting code from one platform to other.

Indeed, for many small programs the rules outlined below will be all you need to apply to get Arduino code running on an imp.

The Basics

Arduino programs are written in the C++ language; imps uses Squirrel. Fortunately, both Squirrel and C++ are object-oriented languages that derive from a third language, C. As such there are many similarities between the two, and with some minor changes code can be readily ported from an Arduino project to Squirrel.

Squirrel and C++ use the same program control keywords: switch... case, if... then, do... while..., and so on. They use the same arithmetical, logical (Boolean), bitwise and compound operators. Both support the definition of object classes and use ‘dot syntax’ to drill down through objects’ methods and properties:

hardware.pin1.write(1);      // imp code
keyboard.write(character);   // Arduino code
led.row.column.pixel = 1;    // Code common to both platforms

Arduino code structures, then, can largely be used as is. Squirrel lacks Arduino’s goto keyword, so sequences that make use of this will need to be rewritten, but most code can be copied across and then simply tweaked.

Variable Types

C++ is a typed language, so variables have to initially indicate the type of data they hold. This is not the case with Squirrel, which automatically sets a variable’s type on assignment of a value and, if necessary, changes the type when a new value is assigned to the variable. So while in Arduino programs you will see lines like:

bool keyPressed = false;

In Squirrel, it’s sufficient just to write:

keyPressed = false;

That said, because of the way Squirrel manages variable scope, it is usually necessary to mark the variable as local to its context, so you would actually convert the Arduino line above to:

local keyPressed = false;

Generally, then, converting Arduino variable declarations to Squirrel assignments is simply a matter of removing the variable type and replacing it with local, unless the variable is global in scope or a function parameter. Global variables are initially assigned using Squirrel’s <- operator; to find out why, see the Squirrel Programming Guide.

// Assign a global Squirrel variable
timeToNextEvent <- 20.0;

// Assign a local Squirrel variable
local timeDelta = 4.0;

Squirrel functions require the function keyword. Again, while Arduino function parameters and return values need type declarations, their Squirrel equivalents do not:

Arduino

void lcdClear(int x, int y) {
  for (int index = 0 ; index < (x * y / 8) ; index++) {
    lcdWrite(lcdData, 0x00);
  }
}

imp

function lcdClear(x, y) {
  for (local index = 0 ; index < (x * y / 8) ; index++) {
    lcdWrite(lcdData, 0x00);
  }
}

Variable Sizes and Signs

Though you can remove variable types from code when you are converting from Arduino to imp, don’t ignore the types entirely. Squirrel floats are 32-bit values, ie. they each take up four bytes of memory. Arduino floats may each occupy two, four or more bytes. Squirrel integers are 32-bit signed values, whereas Arduino integers may be signed or unsigned, and run to 8, 16 or 32 bits in length.

The signed 8-bit integer value 0xFF actually represents -127, but drop it into a signed 32-bit integer and Squirrel will treat it as 255. This is because in an 8-bit signed integer (int in Arduino code) uses bit 7 (the rightmost bit is bit 0) as a sign indicator. It uses 1 to indicate a negative value, 0 for a positive number. An unsigned 8-bit value (uint8_t in Arduino code) uses bit 7 as one of the value bits, extending the range of numbers it can represent to 255. A 32-bit Squirrel integer also uses bit 7 as one of the value bits; its sign bit is bit 31.

The rule for converting a signed integer value from m bits to n bits, where m is greater than n is:

x_in_m_bits = (x_in_n_bits << (m - n)) >> (m - n);

This sets the converted number’s sign bit to match that of the original number by moving the original value’s bits leftward a sufficient number of times (equivalent to multiplying by m - n). It then shifts the value bits back to their original location.

Programmers can also use Squirrel’s ‘blob’ — a block of arbitrary bytes — or a string to store incoming values to the correct number of bytes. It’s no coincidence that the imp API’s read and write methods for various IO buses, such as I²C, SPI and UART, operate with strings and blobs rather than Squirrel’s integers and floats. This allows allow the four-byte values Squirrel likes to be easily converted to the single- and two-byte devices connected to these buses expected to send and receive, using the blob.writen() and blob.readn() methods:

// Write 32-bit Squirrel Integer into Blob as an 8-bit unsigned integer
// Note only the lowest eight bits of the source value are read, ie.
// if squirrelIntValue equals 0xFF01, only 0x01 is written to the blob
local squirrelIntValue = 0xFF;
local inBlob = ();
inBlog.writen(squirrelIntValue, 'b');

When sending data, 8-bit values can be represented by strings by prefixing the numeric characters with \x: "\xFF" is not a four-byte string (one byte per character) but a string representation of the single-byte value, 255.

If Arduino code says:

wire.write(arduinoI2CAddress, 0xC0);

we can replace this with:

hardware.i2c12.write(impI2CAddress, "\xC0");

Incidentally, when it comes to I²C, Arduino uses addresses as they are written in datasheets; typically this is a 7-bit number. The imp requires I²C to be in the 8-bit format in which they will actually be used on the bus. To convert from one to the other, just multiply by 2 — equivalent to shifting the 7-bit address’ bits one bit to the left:

impI2CAddress = arduinoI2CAddress << 1;

In the following examples, we’ll assume you’ve made this conversion in your code.

Another way to convert a Squirrel 32-bit integer to an eight-bit value (assuming the original is of that order) is to use the integer.tochar() method. This converts a 32-bit integer into an 8-bit unsigned value. Since 32 bits can hold much larger integers than eight bits can, you need to make sure the value you are converting won’t change in the conversion.

If Arduino code says:

uint8_t data = 0xC0;
wire.write(arduinoI2CAddress, data);

we can replace this on the imp with:

local data = 0xC0;
hardware.i2c12.(impI2CAddress, data.tochar());

Because characters can be added to strings with the + operator, it’s possible to send sequences of numbers very easily:

for (uint8_t i = 0 ; i < 4 ; i++) {
  wire.write(arduinoI2CAddress, data[i]);
}

might then become:

hardware.i2c12.(impI2CAddress, data[0].tochar() + data[1].tochar() + data[2].tochar() + data[3].tochar());

Alternatively, you can use the string.format() function to convert Squirrel integers to 8-bit values and output the result as a string:

local sendString = format("%c", data[0]);
hardware.i2c12.(impI2CAddress, sendString);

Remember, an 8-bit value read into a 32-bit value needs no conversion if it’s unsigned — we don’t care whether the value is positive or negative. If it is signed, we can use the ‘shift-left, shift-right’ method discussed above to preserve the sign.

Software Libraries

Much Arduino code is implemented through libraries that are loaded in when the program is compiled into the machine code that will run on the device. As such, imp programs do not use the #include command used in Arduino programs to tell the compiler to load in the contents of another file. In fact, # is used by Squirrel as an alternative marker for comments in your code. You will need to copy and paste the Arduino library’s code into the application itself, typically as a set of functions to be called by the main body of the imp program.

An exception is Electric Imp’s own code libraries. These are loaded using one or more #require statements at the top of your program. For more details on using these libraries and what is available, see the Libraries page. You may find that the Arduino library you’ve been using already has an Electric Imp equivalent.

Most Arduino libraries simply define classes that you can instantiate as objects and use at runtime, and you can do the same with Squirrel, using its class keyword and a single structure that includes properties and methods (functions) — essentially the .h and .cpp files of Arduino libraries combined into one. Converting an Arduino class to a Squirrel class is generally a matter of removing variable types (replacing them with local as necessary) and adding function to functions.

You may well see Arduino code along these lines:

void Adafruit_SSD1306::begin(uint8_t vccstate, uint8_t i2caddr, bool reset) {
  // Code...
}

This defines a method called begin() which is part of the class Adafruit_SSD1306. You can convert it thus:

class Adafruit_SSD1306 {
  function begin(vccstate, i2caddr, reset) {
    // Code...
  }

  // Further functions...
}

Other methods that are part of the Arduino version of the class can also be dropped into the Squirrel class as functions.

Unlike C++ classes, Squirrel does not define private and public methods and properties, but it does define a specific method, called constructor, which is used to initialize a new instance of the class and is called when you create the instance. The class’ constructor sets whatever parameters the instance requires:

class myClass {
  classString = null;

  constructor(inputString) {
    if (inputString != "") classString = inputString;
  }

  // Class' functions...
}

local myInstance = myClass("Hello World");
server.log(myInstance.classString);
// Displays "Hello World" in the log

Arduino classes often include constructor-type methods, though they are not formally marked as such, as is the case with Squirrel. Look at the code’s comments for guidance as to what values a class’ constructor assigns to its variables. And see the Squirrel Programming Guide to learn about variable scope as applied to Squirrel classes.

Core Program Structure

The Arduino platform defines two core functions which must be implemented in any Arduino program: setup() and loop(). The first of these, setup(), is run when the hardware starts and only then. The second function, loop(), runs next and then runs continually, looping through the function’s lines until the device is powered down or rebooted. All other functions and object methods are called from either of these core functions.

The Electric Imp Platform doesn’t require either of these, though the names may of course be used for code that performs a similar task. Neither will be called automatically, and a loop() will not automatically be re-run when its code completes.

The recommended practice for transferring loop() code to the imp is to use either of the imp API calls imp.wakeup() or imp.onidle(). The first of these takes two parameters: a duration in seconds, and the name of the function which will be called when that period of time has passed, so:

imp.wakeup(0.1, loop);

placed within loop() will set loop() to be executed again in a tenth of a second’s time. Note that the code doesn’t include a call to loop() but rather takes a reference to the function (you can think of a reference as the function’s address in memory, but it’s actually more sophisticated than that).

function loop() {
  // Arduino loop code goes here...

  // Set timer to call loop() again in 0.1 seconds
  imp.wakeup(0.1, loop);
}

If you want the loop to be executed immediately, you can use:

imp.wakeup(0.0, loop);

but it’s better to use:

imp.onidle(loop);

both lines are essentially equivalent, and both will re-run loop() after the imp’s on-board OS has performed any tasks it needs to perform. This is essential for maintaining the imp’s connectivity with its server-based software partner, the agent. It is an aspect of the imp’s fundamental event-driven programming methodology. For more information on this topic, see ‘Event-driven Programming’ and ‘How to Write imp Loop Structures’, which explains why infinite loops, such as while(true), are a Bad Thing on imps and how you deal with this.

There is a subtle difference between the two calls imp.onidle() and imp.wakeup(): the imp can have only one imp.onidle()-registered function, but many imp.wakeup() callbacks. Setting a new imp.onidle() callback clears the existing one; imp.wakeup() callbacks are cleared when the timer fires, or manually using imp.cancelwakeup().

So a typical simple Arduino program transferred to the imp might become:

function setup() {
  // Arduino setup() code goes here...
  loop();
}

function loop() {
  // Arduino loop() code goes here...
  imp.onidle(loop);
}

// To start the program, call setup()
setup();

setup() and loop() become generic Squirrel functions and are called manually when the Squirrel interpreter reaches the last line of the program. However, loop() includes a suitable final line to tell impOS™ to call loop() again just as soon any other OS-managed tasks have been processed.

Device Operation

At this point, it’s important to note some crucial differences between how Arduinos and imps operate. Arduino code is ‘compiled’ from the source code you write into the native machine code of the Atmel microcontroller. Your code runs exclusively. The imp’s Squirrel code is not compiled, but converted to instructions for a virtual machine. The imp’s operating system, impOS, maintains this virtual machine.

The Arduino platform includes the blocking function delay(), which pauses the system (excluding interrupts) for the passed number of milliseconds. The imp equivalent is imp.sleep(), which takes a value in seconds but has microsecond resolution.

For many applications, these differences will have little impact. But while the Electric Imp approach makes it virtually impossible to brick a device — at worse, it will restart and re-download the application code — it does mean you have to play fairly and allow impOS access to processor resources too. Generally, this amounts to little more than avoiding CPU-blocking program loops by using the alternatives the imp API provides, as outlined above.

To learn more about maintaining non-blocking loops in Squirrel, please read the Developer Guide ‘How to Write imp Loop Structures’. Advanced programmers should also read ‘Squirrel Generator Functions’, which discusses an alternative method for implementing this behavior.

Accessing Hardware Features

The standard Arduino pin layout provides a number of GPIO pins which, like those on an imp, can be used for digital and analog IO. Support for certain IO applications are provided by specific libraries, so if an Arduino program imports one or more of these, you’ll know which imp bus object(s) to use:

Arduino Library imp bus class
SPI.h hardware.spi
Wire.h hardware.i2c
Serial.h hardware.uart

 
An Arduino’s GPIO pins are numbered, and you set them using pinMode(), a standard function that takes the pin number and a constant — OUTPUT or INPUT — as parameters. The imp’s equivalent is pin.configure(), a method that is part of a given pin object — one for every pin on the imp — which are, in turn, part of the overarching hardware object. It takes a constant, DIGITIAL_OUT or DIGITAL_IN:

pinMode(1, OUTPUT);                         // Arduino: set pin 1 for output
hardware.pin1.configure(DIGITAL_OUT, 1);    // imp: set pin 1 for output with an initial state of High (1)

Writing to those pins is as follows:

digitalWrite(1, LOW);      // Arduino: set pin 1 to 0V
hardware.pin1.write(0);    // imp: set pin1 to 0V

For analog connections, you would use:

pinMode(1, OUTPUT);                     // Arduino: set up pin 1 for output
hardware.pin1.configure(ANALOG_OUT);    // imp: set up pin 1 for output

and

analogWrite(1, 255);         // Arduino: set pin 1 to max
hardware.pin1.write(1.0);    // imp: set pin1 to max

Note that while this example uses an imp’s pin 1, not all imp pins are available for all possible IO types. Take a look at the imp pin mux listing for full details of which imp pins have what functionality, across all imp types.

Arduino analog values run from 0 to 255. On an imp they run from 0.0 to 1.0, so you’ll need to adjust code that derives data from these values, or generates them, accordingly.

The Arduino functions millis() and micros() yield, respectively, millisecond and microsecond times since the device last booted — as do the imp API’s hardware.millis() and hardware.micros() object methods.

Many of Arduino’s math functions are available using Squirrel’s own math library and included by prefixing them with the namespace math:

Arduino

int a = pow(10, 2);         // Ten squared

imp

local a = math.pow(10, 2);    // Ten squared

Missing Functions

Arduino has a small number of convenience and other functions which have no direct equivalents in the imp API. Usually, however, they can be reconstructed by combining Squirrel structures and imp API calls.

For example, the Arduino function shiftOut() sends timing pulses on the clock pin, and serializes data as a series of bits which are set out on the data pin. The constant determines whether the bits are set in ‘most significant bit first’ order, or ‘least significant bit’. This function can be rendered in Squirrel thus:

function shiftOut(dataPin, clockPin, constant, data) {
  if (constant == MSBFIRST) {
    for (local i = 8 ; i > 0 ; i--) {
      clockPin.write(0);
      dataPin.write(data & 0x80);
      data = data << 1;
      clockPin.write(1);
    }
  } else {
    for (local i = 0 ; i < 8 ; i++) {
      clockPin.write(0);
      dataPin.write(data & 0x01);
      data = data >> 1;
      clockPin.write(1);
    }
  }
}

The input equivalent, shiftIn(), can be converted in much the same way. It’s worth noting that code which makes frequent use of shiftIn() and shiftOut(), or which needs to run particularly quickly, may be better off making use of one of the imp’s SPI bus rather than this solution.

Arduino’s pulseIn() function, which times the duration of a single input pulse, can be reproduced using the analogous pin.configure(PULSE_COUNTER, float) API call. This counts the number of pulses read for a specified period; if the period is unitary, the number of pulses is the inverse of the duration.

Arduino’s various bit and byte functions can be replicated using various standard operators. The function lowByte(), which yields the rightmost eight bits of a larger value can be implemented on the imp thus:

local lowByte = value & 0xFF;

To set a specific bit — roughly equivalent of Arduino’s bitSet() function — use the OR operator, |:

function bitSet(value, bitToSet) {
  return  value | math.pow(2, bitToSet);
}

and add

newValue = bitSet(oldValue, bitToSet);

to the main body of your program code. You can clear the bit thus:

function bitClear(value, bitToClear) {
  return value | !(math.pow(2, bitToSet));
}

Porting Guidelines

In summary, here are ten guidelines for converting Arduino code to Squirrel. They will not cover every possible Arduino program, but they will help you modify much of the code you’ve copied over.

  1. Remove variable type declarations from the code
  2. BUT do take note of the Arduino variable types — it may affect the outcome of the program
  3. Make sure all variables which aren’t global are given the local keyword
  4. Make sure global variables are declared and initially assigned using <- not =
  5. Make sure functions are prefixed with the function keyword
  6. Arduino libraries that provide the same functionality impOS’ built-in classes — typically for IO — can be ignored.
  7. Other Arduino libraries imported using #include need to be added to you Squirrel code manually and modified as above.
  8. Make sure classes are prefixed with the class keyword
  9. Arduino programs run on their hardware exclusively, but imp programs run alongside impOS. So don’t block its runtime in loops: make good use of imp.wakeup() and imp.onidle().
  10. Many Arduino functions without explicit Squirrel equivalents can be reconstructed with simple Squirrel code.