Bare Metal Go on micro:bit V2 Go
Step-by-step tutorial for Embedded Go with TinyGo on the micro:bit V2 Go.
Prerequisites
Follow these steps if it’s your first time working with embedded TinyGo. Otherwise, skip to the Building section.
Versions
GO
Install Go by following the instructions at https://go.dev/dl/. If you already have Go installed check your version with go version.
$ go version
go version go1.25.5 darwin/arm64TinyGo
Install TinyGo by following the instructions at https://tinygo.org/getting-started/install/macos/. If you already have TinyGo installed check your version with tinygo version.
$ tinygo version
tinygo version 0.40.1 darwin/arm64 (using go version go1.25.5 and LLVM version 20.1.1)Hardware
The micro:bit has many integrated sensors and gadgets. In this tutorial, we will focus on the Microcontroller (MCU) and the 5×5 LED matrix.
See all Features.
For more details on the hardware visit this website. There you will also find more information about the processor model (nRF52833) and the micro:bit’s architecture.
Configuring the Project
Create a new project with $ mkdir hello-world. Then go into the projects root $ cd hello-world.
This is optional: Create a Go Module with $ go mod init example/hello-world. This module allows us to use dependencies in the future.
Create an app.go file with $ touch app.go. In this file we will create our program for the microcontroller. Paste the following lines into your file, for a basic setup of Go projects.
package main
import "fmt"
func main() {
fmt.Println("Hello world!")
}In a next step we can get rid of the fmt package, and add the machine package from TinyGo and the time package from Go
// blink program for the BBC micro:bit
package main
import (
"machine"
"time"
)
func main() {
}Now we can add the core logic in the main() function.
// blink program for the BBC micro:bit
package main
import (
"machine"
"time"
)
// The LED matrix in the micro:bit is a multiplexed display: https://en.wikipedia.org/wiki/Multiplexed_display
// Driver for easier control: https://github.com/tinygo-org/drivers/tree/master/microbitmatrix
func main() {
ledrow := machine.LED_ROW_1
ledrow.Configure(machine.PinConfig{Mode: machine.PinOutput})
ledcol := machine.LED_COL_1
ledcol.Configure(machine.PinConfig{Mode: machine.PinOutput})
ledcol.Low()
for {
ledrow.Low()
time.Sleep(time.Millisecond * 500)
ledrow.High()
time.Sleep(time.Millisecond * 500)
}
}This program directly controls one LED of the 5×5 LED matrix.
First, we select one row and one column pin of the matrix using constants provided by TinyGo (machine.LED_ROW_1 and machine.LED_COL_1).
Both pins are configured as digital outputs.
The LED matrix is multiplexed, meaning an LED lights up only when its row and column form a current path.
On the micro:bit, columns are active-low and rows are active-high.
ledcol.Low() // enable column
ledrow.Low() // turn LED on
ledrow.High() // turn LED offBy toggling the row pin and adding delays with time.Sleep, we create a blinking effect.
Building the Project
To run our project on the micro:bit we need to first build it as a binary. Because the microcontroller has a different architecture than your computer, it needs to be cross compiled.
Get Paul Pietzko’s stories in your inbox
Join Medium for free to get updates from this writer.
TinyGo handles cross-compilation automatically for us. To build the program, run:
$ tinygo build -target=microbit-v2 app.goFlashing
This is the process of transferring our program in binary form into the microcontroller’s persistent memory. After flashing, the microcontroller will run the program automatically each time it powers on.
$ tinygo flash -target=microbit-v2 app.goDebugging
There are two ways to see what your program is doing: print messages over USB (quick and easy), or pause the chip with a real debugger and step through the code line by line.
The micro:bit V2 already has a debug probe built in, so you don’t need any extra hardware for either. The second chip on the board handles USB and also acts as a CMSIS-DAP debug probe.
Option 1: Print messages to your computer
Add a println call to the loop:
for {
ledrow.Low()
println("LED on")
time.Sleep(time.Millisecond * 500)
ledrow.High()
println("LED off")
time.Sleep(time.Millisecond * 500)
}Flash it and open the serial monitor in one step:
$ tinygo flash -target=microbit-v2 -monitor app.govYou’ll see LED on / LED off appear in your terminal every half second. Press Ctrl+C to quit. If you only want to reconnect without flashing again, run tinygo monitor.
Option 2: Step through the code with GDB
Install the tools
# macOS
$ brew install open-ocd arm-none-eabi-gdb
# Debian / Ubuntu
$ sudo apt-get install gdb-multiarch openocdStart a debug session. Run this instead of tinygo flash, since it also flashes the program:
$ tinygo gdb -target=microbit-v2 app.goTinyGo starts OpenOCD in the background, connects GDB to the board, loads your program, and stops at the very start. You’ll land at a (gdb) prompt.
Try it out: set a breakpoint on the line with ledrow.Low() (line 18 in the code above), then run to it:
(gdb) break app.go:18
(gdb) continueThe program stops before the LED turns on. Now execute just that one line:
(gdb) nextThe LED lights up and stays on, because the chip is paused. Type continue again and it stops at the same spot one loop later.
If something goes wrong:
unable to find a matching CMSIS-DAP device: the micro:bit isn’t detected. Check that the cable carries data (some only charge), then replug the board.- Variables show as
<optimized out>: rebuild with less optimization, e.g.tinygo gdb -target=microbit-v2 -opt=1 app.go. - To see what’s happening under the hood: add
-ocd-outputto show OpenOCD’s messages.
More details are in the TinyGo debugging guide.
Summary
TinyGo makes bare-metal programming on the micro:bit very accessible.
- Uses standard Go syntax with a small runtime
- Hardware access is straightforward via the
machinepackage - Ideal for quick experiments, education, and simple embedded projects
TinyGo hides much of the low-level complexity while still producing efficient native code, making it a great entry point into embedded development with GoLang on the micro:bit V2.
You can now call yourself an Embedded GoLang Developer and a real Gopher — congrats!







