What No One Tells You About TinyGo: Running Go on an Arduino Changed How I Think About Embedded Programming
When I first heard about TinyGo, I thought: โCool, Go for microcontrollers... I doubt itโs practical.โ Fast forward a month, and Iโm blinking LEDs and handling button inputs with Go on my Arduino Uno โ and loving every minute of it.
This blog post isn't just another overview โ I'm diving deep into what it's actually like to work with TinyGo, what problems it solves (and doesn't), and how it changes the way you can think about embedded systems development. I'll walk you through a complete example with real code, compare the experience to classic Arduino C/C++, and share how switching to Go on microcontrollers made me a better developer both in the embedded world and fullstack web development.
๐ง Why TinyGo Is a Game-Changer
Letโs set the scene.
Traditionally, embedded programming means clunky C code, opaque compilers, memory juggling, and lots of trial and error. Yes, itโs powerful, but the learning curve is steep and debugging is often a form of digital voodoo.
Hereโs why TinyGo flips that on its head:
- โ Type safety and powerful tooling from Go
- โ No garbage collection overhead at runtime (TinyGo uses escape analysis to include the smallest possible runtime)
- โ Works on Arduino, ESP32, BBC micro:bit, and WebAssembly
- โ Compiles blazing fast compared to gcc toolchains
As a developer who is also working in web backend systems using Go, being able to reuse knowledge in the embedded space is cosmic-level productivity alignment.
๐คฏ Real World: Blinking an LED with Button Input in Go
Letโs look at how the same behavior (toggle an LED with a button press) is written in C vs Go.
๐คข The C Way (Traditional Arduino)
const int buttonPin = 2;
const int ledPin = 13;
void setup() {
pinMode(buttonPin, INPUT);
pinMode(ledPin, OUTPUT);
}
void loop() {
int buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
}
๐ The TinyGo Way
package main
import (
"machine"
"time"
)
func main() {
led := machine.LED
button := machine.D2 // pin 2
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
button.Configure(machine.PinConfig{Mode: machine.PinInput})
for {
if button.Get() {
led.High()
} else {
led.Low()
}
time.Sleep(10 * time.Millisecond)
}
}
What do you notice?
- The structure is cleaner, type-checked, and takes advantage of Goโs standard libraries.
- No main() void awkwardness โ the structure feels like writing any regular Go CLI app.
๐ง How to Get Started
Setting up TinyGo is surprisingly smooth.
Step 1: Install TinyGo
brew tap tinygo-org/tools
brew install tinygo
Or grab it from the official downloads.
Step 2: Install AVR tools (for Arduino Uno)
brew install avr-gcc
Step 3: Write Your .go File (like above)
Step 4: Compile and Flash
tinygo flash -target=arduino uno-led-blink.go
Your Arduino will restart and start running your compiled Go program! Yes โ youโre running freaking statically compiled Go code on an 8-bit microcontroller!
๐ TinyGoโs Secret Weapon: Code Size
Compare these code sizes:
- Arduino C++ binary for our LED app: ~3.2 KB
- TinyGo binary for same app: ~2.4 KB
How? TinyGo aggressively compiles only used packages/functions (leveraging Go's linker and escape analysis). And it's not some kind of toy language runtime โ apps scale as you'd expect.
๐ Advanced: Using TinyGo with WebAssembly
That same Go code can be compiled and run in the browser. Donโt believe me?
tinygo build -o main.wasm -target wasm yourfile.go
Then serve it using a web app and call Go functions from your JavaScript glue code. Same syntax, shared logic. Itโs a dream come true for teams building fullstack web apps with heavy IOT integrations.
TinyGo opens the door to fully isomorphic Go codebases, spanning browser apps, backend REST services, and tiny embedded devices.
๐ Watch Out: Language Limitations
TinyGo isnโt a full Go implementation โ it supports a major (and growing) subset of features:
- No reflection
- No interface{} (not yet robust, mostly for simple uses)
- No goroutines on most targets (they compile but run sequentially)
- No map support in many cases
But you still get:
- Great error handling (if err != nil)
- Type-safe APIs
- Structs and modules
- Hardware abstraction for GPIO, I2C, SPI, UART, etc
๐ Why Use TinyGo Over MicroPython or Arduino C?
Let's be honest, MicroPython is great โ but performance is lacking. You donโt want to run real-time sensor loops with tight timing on a MicroPython interpreter.
Arduino C is fast, but unstructured, especially for larger programs.
TinyGo gives you:
Feature | Arduino C | MicroPython | TinyGo |
---|---|---|---|
Type Safe | โ | โ | โ |
Compile Speed | ๐ | โ (none) | โ |
Performance | โ | โ | โ |
WebAssembly | โ | โ | โ |
Modern Syntax | โ | โ | โ |
๐ง Final Verdict: Is TinyGo Worth It?
Absolutely โ if you:
- Already use Golang and want to go to hardware
- Want safer, more maintainable embedded projects
- Hate thinking about pointers and memory management in C
- Want multiple target platforms (microcontrollers, browsers, servers)
TinyGo is not just a developer toy โ itโs a serious rethink in how we approach embedded development. Itโs about time we started seeing microcontrollers as part of the modern dev stack, not some archaic island.
๐ฎ Bonus: Hardware You Can Use with TinyGo
- Arduino Uno
- ESP32/ESP8266
- Raspberry Pi Pico (RP2040)
- BBC micro:bit
- Adafruit Circuit Playground
- STM32 chips
- And moreโฆ
See the full TinyGo supported hardware list here.
๐งช Try it Now
Donโt just read this โ write your first blinking LED in Go! Itโll change how you view the boundary between software and hardware.
Happy hacking!
๐ก If you need help with bringing your idea to life or building embedded and web connected products using Go โ we offer fullstack development.
Top comments (1)
Running Go on an Arduino isnโt just a hack. It forces you to rethink constraints, and thatโs where real engineering clarity comes from.