045 Project 45: TinyGo I2C Realtime Monitor

Updated

July 30, 2026

045 TinyGo: I2C Device + Realtime Status Monitor

Wire an I2C sensor or peripheral (or a mock if no hardware), read it on a fixed schedule, and publish a compact status line over UART—combining bus IO, timing, and error handling for embedded Go.

Hardware options

Path Hardware Notes
A BME280 / SHT30 / MPU6050 etc. Real I2C; check address + pull-ups
B I2C EEPROM / expander Simpler register IO
C Mock Software sensor if no bus

Many boards: machine.I2C0 with SDA/SCL pins—read your pinout.

Step 1: Bus init sketch

package main

import (
    "fmt"
    "machine"
    "time"
)

func main() {
    machine.Serial.Configure(machine.UARTConfig{BaudRate: 115200})

    i2c := machine.I2C0
    err := i2c.Configure(machine.I2CConfig{
        Frequency: 100_000, // 100 kHz start; 400 kHz if stable
    })
    if err != nil {
        fmt.Fprintf(machine.Serial, "i2c configure: %v\n", err)
        // continue with mock if desired
    }

    const addr uint16 = 0x76 // example — change per device

    ticker := time.NewTicker(200 * time.Millisecond)
    defer ticker.Stop() // if supported; else ignore

    for range ticker.C {
        // Real read example (device-specific registers!):
        // buf := make([]byte, 2)
        // err := i2c.ReadRegister(uint8(addr), 0xD0, buf)
        // Mock:
        v := int(time.Now().UnixNano()%500) / 5
        ok := true
        fmt.Fprintf(machine.Serial, "ok=%v val=%d t=%d\n", ok, v, time.Now().Unix())
        _ = addr
        _ = i2c
    }
}

Replace mock with one real register read for your chip using the datasheet.

Step 2: Error budget (soft RT)

type Monitor struct {
    Period   time.Duration
    Failures int
    LastOK   time.Time
}

func (m *Monitor) Sample(read func() (int, error)) {
    v, err := read()
    if err != nil {
        m.Failures++
        return
    }
    m.LastOK = time.Now()
    // publish v
    _ = v
}

Rules:

  • Cap consecutive failures before SAFE mode (LED pattern / UART alert).
  • Do not block forever on bus errors—timeout if API allows.
  • Keep allocations out of the 200 ms path if RAM is tight (buf reused).

Step 3: Status LED semantics

LED Meaning
Slow blink Healthy samples
Fast blink Bus errors
Solid Fatal / halt (optional)

Step 4: Host-side plot (optional)

Log UART to file and plot with any tool:

picocom -b 115200 /dev/ttyACM0 | tee samples.log

Learning Goals

  • I2C configure + address/register mindset
  • Timed sampling with failure counts
  • Operator-visible status (UART + LED)
  • Datasheet-driven integration

Extensions

  1. Two sensors on one bus.
  2. Change sample rate over UART.
  3. Simple threshold alarm relay pin.

Safety

  • 3.3 V vs 5 V logic levels—do not overvolt pins.
  • Shared GND; pull-ups usually 4.7k on SDA/SCL.

Checkpoint

  • Bus init or explicit mock path
  • Fixed-rate samples with failure counter
  • UART status lines
  • Datasheet note or mock justification