First published: Last updated:

Introduction

Originally published in Japanese at https://zenn.dev/ymotongpoo/books/tinygo-otel-esp32/viewer/00-introduction.

This book is the companion material for my talk “Observability with TinyGo? Can OpenTelemetry Run on TinyGo?” at TinyGo Conference 2026 (October 10, 2026). I gave the talk in Japanese.

TinyGo is a compiler that builds Go programs for small environments such as microcontrollers and WebAssembly. OpenTelemetry is an open source project. It defines the format of the data that lets you observe an application from the outside (telemetry), and the mechanisms to collect that data. For server applications, it is already the de facto standard. Telemetry comes in three kinds. Metrics are numeric time series. Logs are records of events. Traces show the flow of processing as a tree of intervals, called spans. OpenTelemetry calls each of these three kinds a signal.

The question of this book is this: can a TinyGo program on a Wi-Fi microcontroller send telemetry over OTLP (OpenTelemetry Protocol)? OTLP is the communication protocol that OpenTelemetry defines. On a server, you can send it by adding the OpenTelemetry Go SDK to your program. For microcontrollers, however, that SDK does not build today. A protocol, on the other hand, is something you can implement yourself.

In this book, an M5Stack CoreS3 sends all three signals: metrics, logs, and traces. The book goes as far as viewing them on a Grafana dashboard. The CoreS3 is a development board with Espressif’s ESP32-S3 microcontroller. The destination is the OpenTelemetry Collector, a server program that receives telemetry, processes it, and forwards it to storage. The device does not use the official SDK. The only thing I borrowed from the official components is the OTLP schema (the structure of the messages and their field numbers). I wrote the encoder, the HTTP client, and the clock synchronization myself. The book shows where the official components stop working and what I wrote in their place, with values that I measured on the real device.

The code in this book comes from tinygo-otel-esp32, which I publish as the reference implementation. When the text says “this book’s implementation” or “this book’s demo”, it means the code in this repository. Besides the device firmware, the repository contains the Collector configuration, the Grafana dashboards, and a Docker Compose setup that starts the Collector and the backends together. The license is Apache License 2.0.

The packages in the repository match the chapters of this book. The hand-written protobuf encoder is in wire (Chapter 5), and the OTLP/JSON encoder is in otlpjson (Chapter 6). The hand-written HTTP client is otlpmini (Chapter 7), and the SNTP client is sntp (Chapter 8). The firmware that combines them is cmd/device. There is also cmd/hostsim, which runs the same export logic on the host. With it, you can try sending to the Collector without the real device.

I checked the code and the measured values in this book in the following environment.

  • TinyGo 0.42.0 (LLVM 22.1.4), Go 1.26.0
  • M5Stack CoreS3 (ESP32-S3 rev v0.2, 16 MB flash), with the TinyGo target esp32s3-generic
  • espradio v0.3.0 (the Wi-Fi package for ESP32 in TinyGo)
  • Field numbers from opentelemetry-proto v1.11.0
  • otel/opentelemetry-collector-contrib 0.160.0, grafana/otel-lgtm 0.32.1

TinyGo and espradio move fast. The unimplemented features and unexported APIs that this book discusses may have changed in newer releases.

How to read this book

The intended reader can write Go but does not know both microcontrollers and OpenTelemetry well. If you have experience with one of the two, you can follow the book. You may have used TinyGo on a microcontroller, or you may have used OpenTelemetry on a server. I explain the terms of the other side as they come up.

Before the main part, Chapter 2 lists the constraints that I worked under. It covers four points: OpenTelemetry does not assume embedded devices, the memory of the microcontroller, the features that TinyGo has not implemented, and the state of the network stack. For each one, it shows the chapter that deals with it. Chapter 3 looks at three earlier attempts at the same goal and how this book differs from them.

Chapters 4 through 8 go through the layers that data passes on its way out over OTLP, in order. Chapter 4 explains why the official Go SDK does not fit. Chapter 5 covers protobuf encoding, and Chapter 6 covers the other encoding, OTLP/JSON. Chapter 7 deals with the HTTP client and failed exports, and Chapter 8 deals with clock synchronization.

Chapter 9 measures the memory, GC, and stacks of the working program. Chapter 10 covers the design of each of the three signals. Chapter 11 covers the Collector setup and the results of a soak run, and Chapter 12 concludes the book. The appendix at the end lists the references.

If you read the constraints in Chapter 2 first, the reasons for the choices in Chapter 4 and later are easier to follow. Still, I wrote each chapter from Chapter 4 on so that you can read it alone, as a discussion of one layer.