我的第一个 TinyGo 项目:ESP32-C3 + DHT11 + SSD1306

我的第一个 TinyGo 项目:ESP32-C3 + DHT11 + SSD1306

前言

最近开始学习 TinyGo,这是我的第一个 TinyGo 嵌入式项目。作为一个 Go 语言爱好者,能用熟悉的语法来编写嵌入式程序,这种感觉非常奇妙。项目已开源在 Gitee 上:gitee.com/magic_drago...

为什么选择 TinyGo?

TinyGo 是 Go 语言的一个编译器子集,专门针对微控制器和 WebAssembly 等资源受限环境。相比 C/C++,它有这些吸引我的地方:

  • 语法熟悉 --- 如果你已经会 Go,上手几乎零成本
  • 垃圾回收 --- 不用手动管理内存,嵌入式开发也能享受 GC 的便利
  • Goroutine --- 虽然在 MCU 上用得不多,但并发模型依然可用
  • 丰富的驱动库 --- tinygo.org/x/drivers 提供了大量常见传感器的驱动

项目简介

这个项目使用 ESP32-C3 开发板,搭配 DHT11 温湿度传感器和 SSD1306 OLED 显示屏,实现了一个简单的环境监测小装置:

  • 每 2 秒读取一次 DHT11 的温度和湿度数据
  • 将温湿度及 LED 状态实时显示在 OLED 屏幕上
  • 通过按钮中断控制 LED 开关,带软件消抖

硬件连接

外设 ESP32-C3 引脚 说明
LED GPIO2 通过 330Ω 电阻接 LED 正极
按钮 GPIO3 一端接 GPIO3,另一端接 GND
DHT11 DAT GPIO4 DHT11 模块的数据引脚
OLED SDA GPIO5 I2C 数据线
OLED SCL GPIO6 I2C 时钟线

核心代码解读

1. I2C 总线与 OLED 初始化

go 复制代码
i2c.Configure(machine.I2CConfig{
    SCL:       machine.GPIO6,
    SDA:       machine.GPIO5,
    Frequency: 400_000,
})

disp = ssd1306.NewI2C(i2c)
disp.Configure(ssd1306.Config{
    Width:   128,
    Height:  64,
    Address: 0x3C,
})

SSD1306 通过 I2C 总线通信,配置为 400kHz 快速模式。128x64 分辨率的屏幕使用 0x3C 地址。

2. DHT11 传感器读取

go 复制代码
dhtSensor = dht.New(machine.GPIO4, dht.DHT11)
dhtSensor.Configure(dht.UpdatePolicy{
    UpdateTime:          2 * time.Second,
    UpdateAutomatically: true,
})

DHT11 数据手册要求最小读取间隔为 2 秒,设置 UpdateAutomatically 后调用 TemperatureFloat / HumidityFloat 会自动触发读取,非常方便。

3. 按钮中断与消抖

go 复制代码
button.SetInterrupt(machine.PinFalling, func(p machine.Pin) {
    if time.Since(lastPress) < 50*time.Millisecond {
        return
    }
    lastPress = time.Now()
    led.Set(!led.Get())
})

机械按钮按下时触点会弹跳,产生多次快速电平变化。通过记录上次触发时间,忽略 50ms 内的重复触发,实现软件消抖。

4. 超级循环模式

go 复制代码
for {
    err := dhtSensor.ReadMeasurements()
    if err != nil {
        show("DHT11 Error", "", "")
    } else {
        temp, _ := dhtSensor.TemperatureFloat(dht.C)
        hum, _ := dhtSensor.HumidityFloat()
        show(
            fmt.Sprintf("T:%.1fC", temp),
            fmt.Sprintf("H:%.1f%%", hum),
            fmt.Sprintf("LED:%v", led.Get()),
        )
    }
    time.Sleep(2 * time.Second)
}

嵌入式程序的核心结构:无限循环中不断读取传感器、处理数据、刷新显示。注意 main 函数不能退出,否则设备会重启。

踩坑记录

  1. DHT11 读取间隔 --- 刚开始频繁读取导致数据全是错误,后来才注意到数据手册要求至少间隔 2 秒
  2. OLED 地址 --- 128x64 的屏幕地址是 0x3C,128x32 是 0x3D,搞混了屏幕不会亮
  3. 按钮消抖 --- 不加消抖逻辑的话,按一次按钮 LED 会翻转多次,50ms 的消抖阈值实测效果不错
  4. main 不能退出 --- 嵌入式程序的 main 函数必须保持运行,退出后芯片会自动重启

编译与烧录

bash 复制代码
# 编译并烧录到 ESP32-C3
tinygo flash -target=esp32c3 .

# 仅编译生成固件
tinygo build -target=esp32c3 -o firmware.bin .

总结

作为第一个 TinyGo 项目,虽然功能简单,但覆盖了嵌入式开发的几个核心知识点:GPIO 控制、I2C 通信、传感器驱动、中断处理。TinyGo 的开发体验确实不错,Go 的简洁语法让嵌入式编程也变得优雅起来。

接下来打算尝试:

  • 接入 WiFi,把温湿度数据上报到服务器
  • 尝试 MQTT 协议
  • 用 Goroutine 实现更复杂的并发逻辑

如果你也对 TinyGo 感兴趣,欢迎交流!

项目地址gitee.com/magic_drago...

相关推荐
老梁agent7 小时前
工业 Agent 的边缘部署:Ollama + LangChain4j 本地推理方案
物联网·边缘计算·agent
比老马还六1 天前
Bipes-Blockly项目二次开发/Coze智能体(十)
前端·嵌入式
老梁agent2 天前
MCP 协议实战:用标准化方式让 Agent 调用工业工具
物联网·agent·mcp
ForTime3 天前
HAL库大雷预警!STM32 HAL库CAN启动超时解决办法
嵌入式
大辉狼_音频架构3 天前
Vol. NXP SOF Arch
嵌入式
用户805533698035 天前
主线 U-Boot 上 RK3506:和闭源 rkbin 拔河的三个隐性契约
linux·嵌入式
老梁agent5 天前
一个 Agent 不够用?工业场景下的多 Agent 路由模式实战
物联网·agent
老梁agent6 天前
从 0 到 22 篇:工业 Agent 的六大设计原则
物联网·agent
荣--8 天前
在 strip 二进制 + 基址随机化的栈里做崩溃去重 —— 三阶段算法与一行 Crash Flag
嵌入式·崩溃分析·栈指纹·去重算法