Silicon Labs Gecko support in Moddable SDK

The Moddable SDK helps developers build applications for low cost, constrained microcontrollers. Last spring, we embarked on a project to use a processor from Silicon Labs to prototype a low cost, low power IoT device that includes proprietary radio communication. The Silicon Labs line of Energy-Friendly MCUs are very efficient in their use of power, and combined with the Moddable SDK make it possible to build energy efficient JavaScript applications that run on battery powered devices.

The Moddable SDK on Gecko is a full implementation of the XS engine, capable of working with the same sensors and displays as the Espressif ESP8266 and ESP32 ports. The Gecko port covers several Silicon Labs device families: Giant Gecko, Mighty Gecko, Blue Gecko, and Thunderboard Sense 2.

This post describes the features of the Gecko port and two features that were added to the Moddable SDK during its development. It concludes with information about how to get started the Moddable SDK with Gecko devices.

Features of the Gecko port

The Gecko port includes support for the following standard hardware connections:

  • Digital GPIO pins, including Digital.Monitor for callbacks on input changes
  • Analog
  • I2C
  • SPI
  • Displays (ILI9451, DESTM32S, LS013B4DN04)

In addition, there are two new modules to support unique capabilities of the Gecko hardware:

Sleep

The key motivation for working with the Gecko silicon is to bring the Moddable SDK to very low power devices. Many IoT applications run a repetitive cycle of reading sensors and reporting the results or triggering actions. Much of the time spent in that cycle is idle time between periodic sensor readings. To save power, the CPU can be put into a low-power state during the idle periods. Depending on the demands of the application and frequency of sensor measurement, power use can be slashed drastically by taking this step.

For applications that sleep for long periods, power consumption is further reduced by putting the device into a deep sleep mode. The Gecko deep sleep (EM4) mode shuts down most of the device, including RAM, peripheral clocks, oscillators, and peripherals. A low-power timer or GPIO pin are left on to wake the device, and a small amount of persistent memory can be configured to allow the application to save state for when it resumes operation after restart.

The following example saves a value to persistent memory, deep sleeps for 20 seconds, and increments the value when woken from deep sleep.

import Sleep from "sleep";

let storedValue = 0;

if (Sleep.getWakeupCause() & Sleep.EM4WakeupReset) {
    // woke from deep sleep, fetch value that persists across EM4
    storedValue = Sleep.getPersistentValue(0);
    trace("Woke from deep sleep. Value is ${storedValue}\n");
}
else {
    trace("First start\n");
}

// update stored value
Sleep.setPersistentValue(0, storedValue + 1);

// deep sleep 20 seconds
Sleep.doSleepEM4(20000);

You can read more about the low-energy modes of Silicon Labs Gecko hardware here.

Radio

Another motivation for supporting the Gecko silicon is its unusually flexible radio support, which makes it possible to build extremely efficient custom communication protocols. The Mighty Gecko processor powering the Mighty Gecko, Blue Gecko, and Thunderboard modules has a built in radio.

The Moddable SDK adds a Radio module for simple, low-power communication between nodes. The protocol is completely configurable allowing the packet size to be minimized to reduce power consumption.

To send a message, simply assemble a packet into an ArrayBuffer and post it:

import Radio from "radio";

let radio = new Radio;

let packet = new Uint32Array(8);
let sequenceNumber = 0;

// fetch sensor readings
let humidTemp = new SI7021;
let values = {};
humidTemp.read(values);

// set up packet
packet[0] = radio.getUnique();
packet[1] = kModdableTag;
packet[2] = sequenceNumber++;
packet[3] = values.humidity;
packet[4] = values.celsius;

// send data
radio.postMessage(packet.buffer);

The radio's onMessage function receives packets, which can then be processed:

radio.onMessage = function(msg) {
    let packet = new Uint32Array(msg);

    let requesterID = packet[0];
    let tag = packet[1];
    let sequenceNumber = packet[2];
    let humidity = packet[3];
    let celsius = packet[4];

    if (tag === kModdableTag) {
        let f = Math.round(celsius * 1.8 + 32);
        trace(`Received from: ${requesterID}, sequenceNumber: ${sequenceNumber}`);
        trace(` ${f} degrees Fahrenheit, ${humidity}% humidity\n`);
    }
}

New features of the Moddable SDK

Porting the Moddable SDK to Gecko raised new challenges, which led to two new features that impact more than just the Gecko hardware family.

Sub-platform identifiers

The Moddable SDK uses distinct platform identifiers for the Espressif ESP8266 and ESP32 (esp and esp32 respectively) because the devices are so different. Each member of in the Gecko device family is different enough -- in pin assigments, available interfaces (e.g number of SPI ports), memory capacity -- that it requires a different platform identifier. Yet, they are all similar enough to share significant code and configuration.

Instead of creating completely separate platform identifiers with repetitive configurations, we've added a sub-platform feature to the manifest mechanism so that instead of mightygecko and bluegecko we have gecko, gecko/mighty, and gecko/blue. The sub-platforms (gecko/mighty and gecko/blue) inherit the configuration from the gecko platform, overriding that configuration as appropriate.

In a manifest file, settings that apply to all of the Gecko devices are specified with a platform of gecko, while the settings for a particular device family are specified using a sub-platform, gecko/mighty for example.

"gecko": {
    "modules": {
        "*": [
            "$(MODULES)/base/timer/*",
        ]
    },
},
"gecko/giant": {
    "defines": {
        "sleep": {
            "wakeup": { "pin": 2, "port": "gpioPortF",
                    "level": 0, "register": "GPIO_EM4WUEN_EM4WUEN_F2" },
        },
    },
},
"gecko/mighty": {
    "defines": {
        "sleep": {
            "wakeup": { "pin": 7, "port": "gpioPortF",
                "level": 0, "register": "GPIO_EXTILEVEL_EM4WU1" },
        },
     },
  },

Dead-stripping JavaScript built-ins

The limited ROM on the original Thunderboard Sense, just 256 KB total, led to the implementation of the “strip” feature of the XS linker to automatically remove unused built-in objects and functions of of the JavaScript language during the build process. The final C link can then dead-strip the unused code, resulting in a smaller and more efficient binary.

When we built the Sensor Hub demo on the Thunderboard Sense in the spring of 2017, we didn't have the "strip" feature in the XS linker. We remove unused functions manually. This was time consuming and required knowledge of the XS JavaScript engine's internals. Once we implemented strip in the XS linker, the linker removed even more code than we had manually and eliminated the need for any expertise with XS internals.

The "strip" feature of XS now is applied by default for all device targets today, resulting in smaller binaries and reduced RAM use on all devices.

For more information on how to use the strip feature in your applications, see the strip section of the manifest documentation.

Getting started

Building the Moddable SDK for Silicon Labs Gecko devices is a complex process. If you are already familiar with building for Gecko, it will be straightforward. If not, follow the instructions carefully. The complete Gecko build documentation describes the Gecko port in detail and includes instructions on how to get started with the Moddable SDK on the Gecko platform. This section contains links to resources to help you get started.

Building a Moddable application for a Gecko device requires a two-step build. First, the Moddable runtime, XS engine and assets are built into an archive. Second, the native Silicon Labs developer tool, Simplicity Studio, is used to build, install, and debug the application onto the device.

There are many Gecko parts and modules. The EFR32 Mighty Gecko Starter Kit contains three boards with radio modules and is suggested for Moddable SDK development. The Thunderboard™ Sense 2 IoT Development Kit has a small form factor and many built-in sensors.

Small monochrome memory display connected to Thunderboard Sense

xsbug is supported to debug the application’s JavaScript code. You will need a serial-to-USB adapter like this FTDI board to connect to xsbug.

Many of the examples in the Moddable SDK are compatible with the Gecko devices. Here are some examples to get you started:

  • balls is the classic sample application for a device with an ili9341 screen.
  • si7021 demonstrates how to read the onboard I2C Temp/Humidity sensor.
  • radiotest is a simple radio application that shows how to send and receive messages.
  • sleep demonstrates the use of sleep modes.

Good luck!