In freudiger Erwartung der GopherConEU 2023 wollte ich ein Board mit RP2040 Chipsatz bestellen. Da tinygo von Haus aus den Raspberry Pi Pico unterstützt, war das auch gleich das Gerät der Wahl.
Während der Holder nett und insgesamt irrelevant ist, hat mich am Ende das Wifi fast wahnsinnig werden lassen, doch dazu später mehr.

Das Setup

Der Code

Ein simples “Hallo, Welt” des Embedded Computing: Blinking LED
Zur Vollständigkeit hier einmal eingefügt gibt es den genauen Ablauf und weiterführende Details dazu in obiger Dokumentation.

package main

import (
    "machine"
    "time"
)

func main() {
    led := machine.LED
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})
    for {
        led.Low()
        time.Sleep(time.Millisecond * 500)

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

Mit Definition des TinyGo Target wird das machine Package mit Leben gefüllt und wir können wie gehabt entwickeln.
Auch dazu gibt es eine IDE Integration Documentation, die aber natürlich wieder nicht auf meine esoterischen Eigenarten zugeschnitten ist.

Immerhin gibt es neben einer sehr guten vim-go Integration auch ein tinygo.vim Package, dass es auszuprobieren gilt: Selbst ist der Gopher!

Die Herausforderungen

Spoiler: Am Ende habe ich das Sample dann doch tatsächlich lauffähig bekommen.
Der Weg dort hin ist hier aufgelistet.

vim Integration

Obwohl ich mich vim-go echt gute Erfahrungen gemacht hat, blieben diese bei tinygo.vim bislang aus.
Der Umfang ist zwar gering, doch führt ein beherztes :TinygoTarget pico unerwarteterweise zu einem Fehler:

Fehler beim Ausführen von “function tinygo#TinygoTarget[7]..tinygo#ChangeTinygoTargetTo”:
Zeile 61:
E492: Kein Editorbefehl: LspStop

Diesen Punkt habe ich übersprungen, das war nicht mein Kaninchenbau.

NixOS Integration

Der Pico unterstützt den uf2 Bootloader, das heißt, sobald eine .uf2 kompatible Binärdatei in den USB Mountpoint übertragen wird, rennt dass Gerät mit eben dieser Software los.

Leider generiert tinygo für den Pico aber eine .elf Datei.

$ tinygo build --target=pico
$ ls
go.mod  hello.elf   main.go

Das bewegt den Pico zu rein überhaupt gar nichts.

Nun kann tinygo angeblich direkt auf das Gerät flashen, aber auch das ist nur angeblich:

$ tinygo flash --target=pico
error: failed to flash /tmp/tinygo2877794396/main.uf2: unable to locate device: RPI-RP2

Aber: Wir haben eine ordentliche main.uf2 Datei! Warum auch immer die nicht beim build gebaut wird…
Dennoch können wir damit arbeiten, indem wir mittels -work mitteilen, dass das Arbeitsverzeichnis erhalten bleiben soll.

$ tinygo flash -work -target=pico
WORK=/tmp/tinygo2877794396
error: failed to flash /tmp/tinygo2877794396/main.uf2: unable to locate device: RPI-RP2

$ sudo cp /tmp/tinygo2877794396/main.uf2 ~/usb

Daraufhin teilt uns dmesg mit, dass der Pico erfolgreich neu gestartet ist.
Aber es blinkt keine LED.

PicoW != Pico

Beim Pico W handelt es sich um die Wifi Variante.
Akuell wird Wifi von TinyGo noch nicht unterstützt, aber das ist nicht das Problem.

Das Problem ist, dass der Wifi Chip irgendwo angeschlossen sein muss.
Dieses Irgendwo scheint zufällig GP25 zu sein, der im handelsüblichen Pico auf die LED fällt.

Entsprechend mappt TinyGo auch machine.LED für --target=pico auf GP25.
Leider heißt die LED auf dem Pico W aber WL_GPIO0 und ist entsprechend I/O Pin 0 des Infineon Wireless Chipsatzes.
Zusätzlich findet sich im gesamten Pin Plan kein ``GP25`, an den sich einfach eine externe LED anschließen ließe.

Die Raspberry Pico Dokumentation hält besagte Pin Pläne für beide Varianten bereit.

Also hilft nur ein beherzter Griff in die Elektronikkiste, eine kleine Codeanpassung und die Erfahrung im Upload der fertigen .uf2 Datei, und schon läuft es:

package main

import (
	"machine"
	"time"
)

func main() {
	led := machine.GP22
	led.Configure(machine.PinConfig{Mode: machine.PinOutput})

	for {
		led.Low()
		time.Sleep(500 * time.Millisecond)

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

Am Ende blinkt eine LED. Geschafft!

Hallo, Welt: Embedded Edition