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-lightRun 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: …esp8266esp32nrf52picowasmmac
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.