Go en sistemas embebidos: Escribiendo firmware en un lenguaje que no explota la pila

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Go y el hardware: ¿por qué no es una broma?

\n

Cuando escuchas la frase «desarrollo embebido», normalmente te imaginas C, ensamblador y una depuración dolorosa a través de un LED. Pero el mundo no se detiene. Hoy en día, Go (o Golang) está llamando cada vez con más confianza a la puerta del mundo de los microcontroladores. Y no llama sin razón, sino con gorutinas, recolector de basura e interfaces integradas.

\n

En este artículo, analizaremos cómo se ve el desarrollo embebido en Go, escribiremos código real para un microcontrolador hipotético y entenderemos si vale la pena. Spoiler: sí, si estás cansado de las fugas de memoria y te gusta el código legible.

\n\n

¿Por qué Go y no C? Tres pilares

\n

El embebido clásico es C. Pero Go tiene tres características killer que hacen que los ingenieros reconsideren sus hábitos:

\n
    \n
  • Seguridad de memoria: nada de punteros salvajes que puedan volar al espacio. El compilador de Go es más estricto que el compilador de C.
  • \n
  • Gorutinas: hilos ligeros que se adaptan perfectamente a la arquitectura orientada a eventos de los microcontroladores.
  • \n
  • Multiplataforma: escribes código en Mac y compilas para ARM Cortex-M. Sin bailes con tambores.
  • \n
\n

Por supuesto, hay un detalle: Go nativo (gc) no puede compilar para MCU pequeños. Pero existe el proyecto TinyGo, un compilador que adapta Go para hardware con kilobytes de memoria. En eso nos centraremos.

\n\n

Configuración del entorno: TinyGo y tu placa

\n

Antes de escribir código, preparemos el espacio de trabajo. Para el desarrollo embebido en Go necesitarás:

\n
    \n
  1. Instalar TinyGo: brew install tinygo (o descargarlo del sitio oficial).
  2. \n
  3. Instalar la cadena de herramientas para tu placa (por ejemplo, ARM GCC para STM32).
  4. \n
  5. Conectar la placa por USB.
  6. \n
\n

Verifiquemos que todo funciona:

\n
tinygo version\ntinygo: 0.31.0 (linux/amd64)\n
\n

Ahora puedes compilar código para cualquier placa compatible: tinygo build -target=arduino-nano -o firmware.hex main.go.

\n\n

Primer parpadeo: LED en Go

\n

El clásico del género. Nos conectamos a un pin, movemos la pata. Así se ve la programación de microcontroladores en Go:

\n
package main\n\nimport (\n    "machine"\n    "time"\n)\n\nfunc main() {\n    // El LED integrado suele estar en el pin 13 (o LED_BUILTIN)\n    led := machine.LED\n    led.Configure(machine.PinConfig{Mode: machine.PinOutput})\n\n    for {\n        led.High()\n        time.Sleep(500 * time.Millisecond)\n        led.Low()\n        time.Sleep(500 * time.Millisecond)\n    }\n}\n
\n

Compilamos y cargamos en la placa:

\n
tinygo flash -target=arduino-nano -port=/dev/ttyUSB0 main.go\n
\n

¡Voilà! El LED parpadea. Observa: usamos el paquete machine, que es la abstracción de hardware de TinyGo. Oculta el trabajo con registros, pero proporciona una interfaz Go limpia.

\n\n

Trabajo con GPIO e interrupciones

\n

En proyectos reales, no basta con solo parpadear. Hay que leer botones, sensores, reaccionar a eventos. En Go para sistemas empotrados, las interrupciones se implementan mediante canales y gorutinas. Ejemplo: coloquemos un manejador de pulsación de botón.

\n
package main\n\nimport (\n    "machine"\n    "fmt"\n)\n\nfunc main() {\n    button := machine.D2\n    button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})\n\n    // Esperamos la pulsación (pin == LOW, ya que tiene pull-up a alimentación)\n    for {\n        if !button.Get() {\n            fmt.Println("¡Botón presionado!")\n        }\n    }\n}\n
\n

Pero esto es un bucle ocupado (busy loop). ¿Queremos algo más eficiente? Usamos una interrupción a través de un canal:

\n
func main() {\n    button := machine.D2\n    button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})\n\n    // Canal para eventos\n    ch := make(chan bool, 1)\n    button.SetInterrupt(machine.PinFalling,                

Blogs

Book Recommendations