Skip to content

Guiding Light

Initialize a new project and start to interact with some hardware!

Project creation

The init command will create a new directory with the name provided and scaffold the starting files based on a template or example:

xs-dev init guiding-light

The above command should result in the following output:

Generating Moddable project: guiding-light
Run the project using: cd guiding-light && xs-dev run

The guiding-light directory should contain main.js and package.json files. main.js contains that was run from the Hello Console example:

debugger;
let message = "Hello, world - sample";
console.log(message);

The first line is a debugger statement for setting a breakpoint in xsbug. The third and fourth lines save a string to a variable and log it to the xsbug console using the global console.log function.

The initialized package.json in the guiding-light project should look like this:

{
"name": "guiding-light",
"main": "main.js",
"type": "module",
"description": "A starter project for embedded JS",
"scripts": {
"build": "xs-dev build",
"start": "xs-dev run"
},
"devDependencies": {}
}

The main field points to the generated main.js as the entrypoint for the program.

Executing the start script using the package manager of your choice, i.e. npm start, or xs-dev run will provide the same experience as the Hello Console guide.

Quick tip: check out all the available simulators by using the --list-devices flag with the run command and typing “simulator” to filter the list.

Down to the metal

At this point, we have our chosen hardware in hand and need to set up the dev environment to start running code on the device.

Just like the previous step, the setup command will automate the installation and building of tooling required for the target device. The --list-devices flag will provide an interactive list of supported device platforms:

❯ xs-dev setup --list-devices
? Here are the available target devices: …
esp8266
esp32
nrf52
pico
wasm
mac

You may see different options depending on what operating system or version of xs-dev you are using.

Once this process is done, you should see a success message (where <device> is the selected target device):

Successfully set up <device> platform support for Moddable!
Test out the setup by starting a new terminal session, plugging in your device, and running: xs-dev run --example helloworld --device=<device>
If there is trouble finding the correct port, pass the "--port" flag to the above command with the path to the /dev.cu.* that matches your device.

Running our project on the selected device (which should be connected to the computer somehow, presumable over USB) is the same command as before with the additional --device flag to pass in the target device platform:

xs-dev run --device <device>

This will take some time to compile and send the code over to the device. When it has succeeded, the debugger will open like before but now it is tracing the logs coming from the hardware!

👏 Give yourself a round of applause! You have now run JavaScript on an embedded device! 🎉

Hello blinky

Now that we know we can run code on our device, it is time to shed a little light on hardware control. We will use the ECMA-419 standard APIs to perform this task.

With that default package.json the ECMA-419 APIs are included in the compiled program, so the main.js file can be updated with the following code:

const Digital = device.io.Digital;
const led = new Digital({
pin: device.pin.led,
mode: Digital.Output,
});
led.write(1);
let state = 0;
System.setInterval(() => {
led.write(state);
if (state === 0) {
state = 1;
} else {
state = 0;
}
}, 200);

Using the global device variable provided by the io module, we can access the Digital IO class for controlling the digital output to an LED. In this example, the Digital class is instantiated with the pin property set to the built-in led as defined on the global device and the mode set to the Digital.Output static property found on the class. With that Digital instance variable called led, the write method is called with a value of 1 to send power to the LED.

const Digital = device.io.Digital;
const led = new Digital({
pin: device.pin.led,
mode: Digital.Output,
});
led.write(1);

To make the light blink, the next value to be written is stored as the state variable. The global System class provides the well-known setInterval function that is found in other JavaScript runtimes like the Web and Node.js. Every 200 milliseconds, the state is written to the LED before being updated to the opposite value.

let state = 0;
System.setInterval(() => {
led.write(state);
if (state === 0) {
state = 1;
} else {
state = 0;
}
}, 200);

The project can be run using the same command as before: xs-dev run --device <device>. If it succeeds, you should see a blinking LED somewhere on your device! ✨

Keep exploring!

Tried adding some console.log calls to log the state to the debugger or updating the timer code to send a message in Morse code.

Coming soon: react to digital input by pressing some buttons

In the meantime, check out the many examples available in the Moddable SDK.

Troubleshooting

If you’re working with a device that doesn’t have an on-board LED or encounter an error while trying to use the device.pin.led value, the pin specifier can be set to a custom value based on the device datasheet or pinout diagram, like this one for the Pico. The pin value can match the on-board LED or an external LED connected to a GPIO, most likely by using a breadboard.