El compilador oficial de Go, conocido como gc, lleva consigo una runtime completa: scheduler M:N, garbage collector generacional, reflection, soporte de red... Todo eso es fantástico cuando tienes gigabytes de RAM, pero en un microcontrolador con 64 KB de memoria ese equipaje no cabe. Y ahí es donde entra TinyGo.
TinyGo reimplementa el compilador de Go sobre LLVM con un único objetivo: generar binarios lo suficientemente pequeños como para caber en hardware con recursos muy limitados. No es un subconjunto de Go ni un lenguaje distinto; es Go, pero con una cadena de compilación diferente y una runtime mucho más ligera.
Soporta la mayor parte de la sintaxis que ya conoces: goroutines, interfaces, defer, punteros, y soporte parcial de generics. Lo que cambia es lo que hay debajo, y eso tiene implicaciones prácticas que verás más adelante.
Los casos de uso principales son dos: microcontroladores (Arduino, Raspberry Pi Pico, ESP32, STM32, nRF52840...) y WebAssembly en el navegador, donde TinyGo genera binarios sensiblemente más pequeños que el compilador oficial.
Hardware soportado
La lista de targets que TinyGo soporta es larga. Puedes verla completa con tinygo targets, pero estos son los más populares:
- Raspberry Pi Pico (RP2040): probablemente el punto de entrada más cómodo. Barato, bien documentado y con una comunidad activa en TinyGo.
- Arduino Uno, Mega y Nano: los clásicos de siempre. El Uno tiene solo 2 KB de RAM, así que aquí la eficiencia de TinyGo importa de verdad.
- ESP32 y ESP8266: WiFi integrado, muy usados en proyectos IoT. TinyGo soporta ambos, aunque el soporte de WiFi en Go todavía tiene limitaciones.
- STM32: familia amplia de microcontroladores de ST Microelectronics, con muchas variantes soportadas.
- nRF52840: el chip de Nordic Semiconductor que se usa en muchos dispositivos Bluetooth Low Energy.
Si tienes un target concreto en mente, consulta la lista oficial de microcontroladores antes de empezar. No todos los periféricos están disponibles en todos los chips.
Instalar TinyGo y el primer programa
La instalación es sencilla. En macOS basta con:
brew install tinygo
En Linux con una distribución basada en Debian:
apt install tinygo
En Windows hay un instalador disponible en tinygo.org.
El "hola mundo" del embebido es hacer parpadear un LED. En TinyGo, para una Raspberry Pi Pico, sería algo así:
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)
}
}
Para compilarlo y mandarlo directamente al microcontrolador:
tinygo flash -target=pico main.go
Ese comando compila el código y lo flashea al dispositivo en un solo paso. No hace falta IDE ni herramienta externa: con Go instalado y TinyGo en el PATH, tienes todo lo que necesitas.
Diferencias con Go estándar
Aquí es donde hay que prestar atención. TinyGo es Go, pero no es todo Go.
Sin terminal, sin fmt.Println
En un microcontrolador no hay una pantalla ni una terminal. Si quieres depurar con mensajes de texto, los mandas por UART y los lees desde tu ordenador con un monitor serie como tinygo monitor. El paquete fmt existe en TinyGo, pero escribir en la salida estándar no tiene sentido en este contexto.
Garbage collector más limitado
TinyGo tiene garbage collector, pero es más básico que el de Go estándar. En la práctica esto significa que debes intentar minimizar las asignaciones en el heap dentro de los bucles principales. No es que no funcione; es que en un chip con 64 KB de RAM cada byte cuenta.
Goroutines cooperativas
Las goroutines funcionan, pero el scheduler de TinyGo es cooperativo, no el scheduler M:N de Go estándar. Eso quiere decir que una goroutine tiene que ceder el control explícitamente (por ejemplo, llamando a time.Sleep o haciendo I/O). No esperes el mismo modelo de concurrencia que en un servidor Go.
Reflection y stdlib parciales
Reflection tiene soporte parcial. Algunos paquetes de la biblioteca estándar no están disponibles o tienen implementaciones incompletas. Antes de depender de un paquete específico, merece la pena comprobar la tabla de compatibilidad de TinyGo.
El resumen práctico: es Go, pero tienes que pensar más en el hardware. Nada que no se aprenda rápido.
Periféricos: GPIO, I2C, SPI y UART
El acceso a los periféricos del hardware se hace a través del paquete machine, que expone una API idiomática de Go sin que tengas que tocar registros en C.
GPIO
Ya lo viste en el ejemplo del LED: configuras un pin como entrada o salida y lo lees o escribes con métodos Go. Leer un botón es igual de directo:
btn := machine.BUTTON
btn.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
if !btn.Get() {
// botón pulsado
}
I2C
El protocolo I2C se usa para comunicar con sensores: temperatura, humedad, acelerómetros, pantallas OLED... TinyGo expone I2C a través de machine.I2C0 y métodos Tx para leer y escribir bytes. Muchos drivers ya están escritos y listos en el paquete tinygo.org/x/drivers.
SPI
Para displays, tarjetas SD y otros periféricos de alta velocidad. La API es similar a I2C pero orientada a transferencias síncronas de bytes.
UART
La comunicación serie con el ordenador. Es tu herramienta de depuración principal cuando no tienes pantalla. Con machine.UART0.Configure y machine.UART0.Write puedes mandar mensajes de texto que ves en el monitor serie.
TinyGo para WebAssembly
Además del hardware embebido, TinyGo tiene otro caso de uso muy distinto: compilar a WebAssembly para el navegador.
El compilador oficial de Go genera binarios WASM que incluyen el runtime completo, lo que se traduce en archivos de varios megabytes. TinyGo compila solo lo que el código realmente usa, y el resultado son binarios de decenas de kilobytes para funciones sencillas.
tinygo build -o app.wasm -target wasm main.go
Para funciones WASM que el JavaScript de la página llama de forma puntual, esa diferencia de tamaño importa bastante en la carga inicial. La contrapartida: sin la stdlib completa y con las mismas limitaciones de reflection que en el hardware.
Si lo que necesitas es ejecutar lógica Go pesada en el navegador con acceso completo a la biblioteca estándar, el compilador oficial de Go sigue siendo la opción. Pero para funciones de procesamiento ligeras que se cargan desde una web, TinyGo tiene ventaja clara.
El paquete de drivers y el proyecto Gobot
Una de las partes más útiles del proyecto TinyGo es tinygo.org/x/drivers, el repositorio oficial de drivers para sensores y displays. Hay decenas de drivers listos: DHT22, BMP280, SSD1306, MPU6050...
Usar el sensor DHT22 de temperatura y humedad es tan sencillo como:
import "tinygo.org/x/drivers/dht"
sensor := dht.New(machine.D2, dht.DHT22)
temperature, humidity, err := sensor.Measurements()
if err == nil {
// usar temperature y humidity
}
Por otro lado, Gobot es un framework para robótica e IoT en Go. Su compatibilidad con TinyGo es parcial, ya que Gobot fue diseñado para sistemas con sistema operativo completo (Linux en una Raspberry Pi, por ejemplo), pero hay integración progresiva con TinyGo para los casos más sencillos.
TinyGo frente a MicroPython y Embassy (Rust)
Si estás eligiendo lenguaje para un proyecto embebido, estas son las alternativas más habituales:
- MicroPython: más simple de aprender, con un ecosistema de librerías más maduro para ciertos sensores, pero más lento y con más consumo de memoria. Es la opción obvia para prototipos rápidos cuando el rendimiento no es crítico.
- Embassy (Rust): sin garbage collector, concurrencia asíncrona real, el mejor rendimiento posible. Es la elección cuando necesitas control total y tienes tiempo para aprender Rust y sus particularidades.
- TinyGo: más rápido que MicroPython, más fácil que Rust, con la sintaxis de Go que ya conoces. No es perfecto en ninguna de las dos dimensiones, pero combina velocidad razonable con una curva de aprendizaje asequible.
Para un desarrollador que ya escribe Go en el backend y quiere explorar el hardware sin aprender C o Rust desde cero, TinyGo es la transición más natural. La diferencia con Go estándar es menor de lo que parece al principio; en una tarde tienes el LED parpadeando y empiezas a entender cómo funciona el hardware desde el código.
Si te interesa Go más allá del hardware, puedes leer sobre Go más allá del backend: nuevos casos de uso o comparar TinyGo vs Embassy de Rust para embebido con más detalle.
Imagen: Pexels / Chengxin Zhao
