Go
Go for Embedded/IoT
This closing chapter is the honest answer to a question the whole domain has been building toward: where does Go actually fit next to Embedded C and embedded C++? The short version: rarely on a tiny microcontroller running standard Go, sometimes on one running TinyGo, and genuinely often on the Linux-class device or cloud gateway sitting one layer up from it.
- What it is
- Standard Go's runtime requirements vs. TinyGo, a separate compiler targeting microcontrollers directly, plus where Go fits in a typical IoT architecture regardless of which compiler is used.
- Mental model
- Standard Go assumes an operating system underneath it; TinyGo strips that assumption away for bare metal, at the cost of a smaller standard library and a still-present (if simpler) garbage collector.
- Key numbers
- Standard Go's runtime needs several MB minimum — far beyond a typical microcontroller's RAM; TinyGo brings that down dramatically, into the range of small MCUs with tens of KB.
- Reach for it when
- An IoT gateway, an edge server aggregating sensor data, or a Linux-class embedded board (an SBC) — not, typically, the tiny sensor-node MCU itself.
Why standard Go doesn't fit tiny microcontrollers
Every chapter in this domain has been standard Go, which assumes real resources most microcontrollers don't have:
- A garbage-collected runtime — Go's automatic memory management (see Introduction to Go's philosophy table) needs meaningful RAM and CPU overhead to run at all — a real cost Embedded C's Dynamic Memory chapter explains the underlying reasons to avoid even in C, magnified further by GC specifically.
- Goroutines and channels' scheduler — the concurrency machinery genuinely central to Go needs its own runtime scheduler running underneath every program.
- An assumed OS — standard Go's compiler targets an operating system's process model by default, not bare metal directly.
None of this is a Go design flaw — it's the same tradeoff Introduction to Go's comparison table named from the start: Go optimized for programmer productivity on networked systems, not for the tightest possible resource footprint.
TinyGo
TinyGo is a separate compiler (not a fork of the language) that compiles a meaningful subset of Go directly for microcontrollers and WebAssembly, built on LLVM — the same compiler infrastructure much of the C/C++ embedded toolchain world already uses:
// blink.go — runs on an actual microcontroller via TinyGo
package main
import (
"machine"
"time"
)
func main() {
led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
for {
led.High()
time.Sleep(500 * time.Millisecond)
led.Low()
time.Sleep(500 * time.Millisecond)
}
}
tinygo flash -target=arduino blink.go # compiles AND flashes to real hardware
This still has a (simpler, smaller) garbage collector and still uses goroutines — TinyGo doesn't remove Go's core design, it re-targets the compiler backend and trims the standard library to fit. It's a real, working option for microcontroller-class Go, not a toy — but it's a genuinely different, narrower tool than the standard Go compiler used throughout the rest of this domain.
Where Go actually fits in IoT
The honest, practical answer is one layer up from the sensor node itself:
- Sensor/actuator node
- Almost always C, occasionally TinyGo, rarely standard Go — tightest resource constraints, closest to the hardware.
- IoT gateway
- A genuinely strong fit for standard Go — aggregating data from many sensor nodes, running on real Linux-class hardware (an SBC or small server) with megabytes of RAM to spare.
- Edge/cloud backend
- Go's strongest, most common home — the networked-services use case Go was actually designed for, per Introduction to Go.
A typical device-to-cloud stack, with the language layer named
Real-world applications
- IoT gateway software — Go's goroutines make handling many simultaneous sensor-node connections genuinely natural, exactly the concurrency case the language was built around.
- Edge servers on Linux-class embedded boards — a Raspberry Pi–class device running Go easily has the RAM and OS support standard Go assumes.
- TinyGo for genuinely simple MCU firmware — real, if narrower, production use exists for straightforward sensor-reading or LED/GPIO-control firmware where TinyGo's supported hardware and library subset are sufficient.
Gotchas
- TinyGo supports a real but genuinely smaller subset of the standard library and language than desktop Go — always verify a specific package or language feature is actually supported on TinyGo's target before assuming parity with what this domain's earlier chapters covered.
- TinyGo's garbage collector, while much lighter than standard Go's, still introduces GC pause behavior that embedded C++'s avoidance of heavy dynamic allocation exists specifically to sidestep — hard real-time deadlines are a genuine risk area, worth testing explicitly rather than assuming away.
- "Go doesn't fit tiny microcontrollers" is a generalization, not an absolute — TinyGo's supported-hardware list keeps growing, and the right call for any specific project depends on its actual real-time and memory constraints, not a blanket rule.
- This is the final chapter of the Go domain, and of this notebook's core language coverage — C, Embedded C, C++, and Go each cover the same underlying computer science from a different, deliberately contrasting angle; re-reading any one after finishing another is often when the differences click hardest.