Index

Introduction

Recently (at work) I had to flash a microcontroller to simulate a keyboard. The problem was that I didn’t knew anything about microcontrollers (still don’t) or even how could I flash those.

At the time I was using the Digispark microcontroller since it was cheap and it was user friendly (with an external case, the device looks like a normal usb). Then a co-worker helped me on the flashing process and for that we used Arduino IDE.

This process was a bit overwhelm because I had to run multiple scripts in order to convert keyboard presses into arduino language (because I wanted to run Rubber Ducky scripts), however, the way this can be done is well documented on the internet.

After this, I decided to take a look at other alternatives since Arduino IDE was sometimes buggy and not user friendly at all. And with that I’ve found TinyGo.

TinyGo

Looking at the TinyGo website: TinyGo brings the Go programming language to embedded systems and to the modern web by creating a new compiler based on LLVM. Basically, you can compile and run Go code inside microcontrollers by using Tinygo.

After some hours of digging in the documentation I’ve found that they support the USB HID for some devices and in order to simulate keyboard presses I needed the USB HID.

However, the device I had was a Digispark and according to one of the TinyGo maintainers, this device does not have USB hardware so it needs to be bit-banged.

image

After this, I realised that I needed to choose another microcontroller (until they don’t support Digispark).

Looking at the release notes for TinyGo I’ve found what devices they support for USB HID.

image

samd21 looked promising, so I went out and bought a Seeed XIAO samd21.

XIAO

XIAO SAMD21 is an ultra-small universal development board, supporting Arduino / Micropython / CircuitPython and much more.

It operates up until 48MHz and it has 32KB of SRAM and 256KB of onboard flash memory.

In terms of development interfaces:

  • 11x analog / 11x digital Pins
  • 1x I2C interface
  • 1x UART port
  • 1 SPI port

image

Flashing

XIAO

Looking at this issue we can see that TinyGo flash does not work on Seeed XIAO because as recently as Dec 2022, the Seeed Studio XIAO M0 dev boards shipped with a bootloader from Nov 2019: UF2 Bootloader v3.7.0-33-g90ff611-dirty SFHWRO.

Knowing this, I had to find the new MSD (Mass Storage Device). For that, I plugged the device into my computer, opened the device in my file manager and checked the files present in it. After getting the new MSD name, I’ve created a new target (in /usr/local/lib/tinygo/targets/xiao-new.json) as shown below.

{
    "inherits": ["atsamd21g18a"],
    "build-tags": ["xiao"],
    "serial": "usb",
    "serial-port": ["2886:802f"],
    "flash-1200-bps-reset": "true",
    "flash-method": "msd",
    "msd-volume-name": "Seeed XIAO",
    "msd-firmware-name": "firmware.uf2"
}

After this, the flash command still didn’t worked (at least for me) so in order to fix this, everytime I wanted to upload the new firmware to the microcontroller I had to compile the program and move the .uf2 to the XIAO device

The following shows an example to emulate a keyboard by opening xcalc on my device (using i3wm with dmenu).

package main

import (
	"machine/usb/hid/keyboard"
)

/*
WINDOWS KEY + ENTER (opens dmenu)
WRITE xcalc
ENTER
*/
func Openxcalc() {	
	kb := keyboard.Port()
	runs := 1
	for {
		if runs == 2 {
			break
		}
		runs +=1

		time.Sleep(1000 * time.Millisecond)

		kb.Down(keyboard.KeyModifierGUI)
		kb.Down(keyboard.KeyEnter)
		kb.Release()

		time.Sleep(1000 * time.Millisecond)

		kb.Write([]byte("xcalc"))

		time.Sleep(1000 * time.Millisecond)

		kb.Press(keyboard.KeyEnter)
	}
}
$ tinygo build -o firmware.uf2 -target=xiao-new main.go && sudo mkdir -p /media/xiao && sudo mount /dev/sda /media/xiao && sudo cp firmware.uf2 /media/xiao

Digispark

In order to show how TinyGo works in Digispark, Ill demonstrate a flashing led in the Digispark device.

For that, download the latest release of micronucleus (https://github.com/micronucleus/micronucleus/releases) and perform the following steps:

$ unzip ~/Downloads/micronucleus-cli-master-882e7b4a
$ sudo cp micronucleus-cli-master-882e7b4a/micronucleus /usr/local/bin/micronucleus
$ export PATH=$PATH:/usr/local/bin/micronucleus
$ tinygo flash -target=digispark digispark/digispark.go
INSERT USB STICK

The following code is the flashing led provided by TinyGo.

package main


import (
	"machine"
)

// Blinking LEDs
func BlinkingLEDs() {
	led := machine.LED
	led.Configure(machine.PinConfig{Mode: machine.PinOutput})
	for {
		led.Low()
	    time.Sleep(1000 * time.Millisecond)

		led.High()
	    time.Sleep(1000 * time.Millisecond)
	}
}

Reset

There are two simple techniques to reset the devices, after flashing them.

The first works in XIAO and the goal is to double tap the RST pins like is shown here.

To reset the Digispark board, we can use the same flash command but now with an empty for loop.

Resources