Sitemap

Bare Metal Go on micro:bit V2 Go

5 min readJan 18, 2026

--

Step-by-step tutorial for Embedded Go with TinyGo on the micro:bit V2 Go.

Press enter or click to view image in full size

Prerequisites

Follow these steps if it’s your first time working with embedded TinyGo. Otherwise, skip to the Building section.

Versions

  • TinyGo v0.40.1 or newer
  • Go v1.25.5 or newer

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/arm64

TinyGo

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.

Source: https://microbit.org/

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.

Press enter or click to view image in full size
Source: https://tech.microbit.org/

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 off

By 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.go

Flashing

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.go

Debugging

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.gov

You’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 openocd

Start a debug session. Run this instead of tinygo flash, since it also flashes the program:

$ tinygo gdb -target=microbit-v2 app.go

TinyGo 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) continue

The program stops before the LED turns on. Now execute just that one line:

(gdb) next

The LED lights up and stays on, because the chip is paused. Type continue again and it stops at the same spot one loop later.

Press enter or click to view image in full size

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-output to 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 machine package
  • 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.

Press enter or click to view image in full size

You can now call yourself an Embedded GoLang Developer and a real Gopher — congrats!

--

--

Paul Pietzko
Paul Pietzko

Sharing solutions to problems I faced... Creator of www.logier.dev