Timon van Spronsen

Introduction to Hardware Programming with DeviceScript

Hardware programming used to require you to learn C/C++. If you’re already familiar with JavaScript or TypeScript, you will be delighted to learn about DeviceScript, a TypeScript programming environment for microcontrollers. In this blogpost you’ll learn the basics of electronics and DeviceScript. We will create the “hello world” of programming: making an LED blink!

Hackerspace Pixelbar

This blogpost originates from a workshop organized at Hackerspace Pixelbar in Rotterdam, which was generously sponsored by No Fluff Jobs. A hackerspace is a physical space where people interested in science, technology or digital art (and many other areas) can meet and exchange ideas. Want to visit? We’re open on Wednesday evenings and Saturday afternoons. Feel free to connect with us on Discord or refer to our website for current opening hours. Additionally, we regularly organize workshops about JavaScript and web-related topics. Join our Meetup group to get notified of the next event!

Safety instructions

Keep these safety instructions in mind while working with electronics:

The setup

We’re going to need a few components to get going:

Aside from hardware, here are the software requirements:

Before we start, it’s essential to learn a bit about the components we’re going to be using.

The breadboard

A breadboard is a way to build electronic circuits. The image below shows the front and the back of a breadboard with the adhesive backing removed. The top side is where we plug in our components and jumper wires. The backside demonstrates how everything is connected. The rows are connected horizontally, we call them terminal strips, they are not connected across the divider gap in the middle. The columns on the side (with the + and - above them) are called power rails, and are connected vertically.

The microcontroller

A microcontroller is a tiny computer on a single integrated circuit, common examples include Arduino and the ESP32 series. Microcontrollers usually contain several general purpose input/output pins (GPIO). These input/output pins allow us to connect components like LEDs. The pinout diagram below shows the pins present on the Seeed Studio XIAO ESP32C3, you can reference this diagram when you wire up the components.

Pinout schema of Seeed Studio XIAO ESP32-C3

The LED and resistor

An LED has two legs, we call them the anode (+) and the cathode (-). The anode leg is slightly longer. We use a resistor to limit the current that flows through the LED, if we were to omit a resistor, the LED could burn out. In my setup I’m using 2V 20mA LEDs, the specifications of your LED may differ slightly, not to worry, the specified resistor will likely be enough to keep your LED from burning out. The anode should be connected to the positive side of the circuit, while the cathode should be connected to the ground, the other way around won’t hurt the LED, but it won’t do anything either.

Wiring up the hardware

Let’s connect the components to the breadboard, you can follow the schema below. The bended leg of the LED in the breadboard schema below is the anode (the longer leg).

Schematic of an electronic circuit. The ESP32C3 is connected at the top (row 1) on columns D and H. There is a jumper cable that connects I2 with I10. The cathode of the LED is plugged into G10, the anode is plugged into E10. The resistor is plugged into B6 and B10.

Let’s examine what we did. We connected the GND pin (ground) with a jumper wire to row 10, this connects the ground to the terminal strip on row 10 on the top side of the gap, meaning that the cathode of the LED is connected to the ground. The anode of the LED is connected with the resistor, which in turn is connected with pin D6.

Programming the hardware

Now onto the most fun part! First create a new folder to contain your project (e.g., blinky), open this folder in VS Code (File → Open Folder…). If you have the DeviceScript extension installed you should see a DeviceScript icon in the activity bar, click on it, then press “Create New Project” and follow the wizard.

Go back to the file explorer, open src/main.ts and replace the contents with the code below.

import { startLightBulb } from "@devicescript/servers";
import { pins } from "@dsboard/seeed_xiao_esp32c3";

const led = startLightBulb({ pin: pins.SCL_D5 });

setInterval(async () => {
  await led.toggle();
}, 1000);

A couple of things to note here. It’s important to import the correct pin definitions, as every board has different pins, or a different layout. I once made the mistake of importing the pin definitions for esp32c3_bare instead, wondering for an hour why my code didn’t work 😅. startLightBulb starts a client to interact with the LED that we connected, we have to give it the pin that’s driving the LED (D5). await led.toggle() will toggle the LED on and off, this is encapsulated in a setInterval which is triggered every 1000 ms (= 1 second).

Flashing the microcontroller

Before we can load our code on the microcontroller, we have to flash the DeviceScript firmware onto the microcontroller.

  1. Connect the microcontroller to your computer with a USB cable.
  2. Open the command palette in VS Code (Command+Shift+P on Mac, Ctrl+Shift+P on Windows and Linux), search for “flash”.
  3. Select DeviceScript: Flash Firmware… and choose the appropriate device.

A terminal will open where you can track the progress, it might take a few seconds, once finished it should say “flash OK”. You can now connect the device.

Loading code onto the microcontroller

Once again, open the DeviceScript panel, but now click on “Connect Device”, then select serial, your device should now appear in the sidebar (1). Then click on the little chevron besides the debug button in the top-right corner (2) and select “DeviceScript: Run” from the context menu. The LED on your breadboard should now start blinking!

The result

Completed circuit with blinking LED

Dimming an LED

Let’s make things slightly more exciting, we’ll make the LED fade. To achieve this we must add dimmable: true to startLightBulb, this will allow us to change the intensity of the LED with led.intensity.write(intensity), where intensity is some number between 0 and 1.

import { sleep } from "@devicescript/core";
import { startLightBulb } from "@devicescript/servers";
import { pins } from "@dsboard/seeed_xiao_esp32c3";

const led = startLightBulb({
  pin: pins.SCL_D5,
  dimmable: true,

const fadeInLevels = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1];
const fadeOutLevels = [1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0];

async function fade() {
  // Fade in
  for (const level of fadeInLevels) {
    await led.intensity.write(level);
    await sleep(50);
  } // Stay at peak brightness for a bit
  await sleep(500); // Fade out
  for (const level of fadeOutLevels) {
    await led.intensity.write(level);
    await sleep(50);

while (true) {
  await fade();
  await sleep(200);

Next steps

This concludes the tutorial. Next up you could try wiring up a button to a digital pin and use it to toggle the LED, I’ll leave you with the schema below. If you have any questions, don’t hesitate to drop by Pixelbar hackerspace in Rotterdam. We also have a Discord community that you can join to ask questions.

Schematic of electronic circuit, it adds a button to the circuit discussed earlier. There is a jumper wire between J2 and J22, and another between J3 and J20. The button is connected between the gap at G20 and E20.