news 2026/9/13 13:08:31

WLED 实战:为 Wemos D1 mini / Wemos32 mini 扩展板添加 OLED 状态屏与温度传感器(usermod 详解)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WLED 实战:为 Wemos D1 mini / Wemos32 mini 扩展板添加 OLED 状态屏与温度传感器(usermod 详解)

WLED 实战:为 Wemos D1 mini / Wemos32 mini 扩展板添加 OLED 状态屏与温度传感器(usermod 详解)

【免费下载链接】WLEDControl WS2812B and many more types of digital RGB LEDs with an ESP32 over WiFi!项目地址: https://gitcode.com/GitHub_Trending/wl/WLED

WLED 提供了 v1 usermod 机制(userSetup/userConnected/userLoop三个钩子),允许在不改动主代码的前提下给固件附加自定义功能。本文基于仓库中 usermods/Wemos_D1_mini+Wemos32_mini_shield/readme.md 及其配套源码 usermod.cpp、usermod_bme280.cpp,完整讲解如何把 SSD1306 OLED 状态屏和 DS18B20 / BME280 温度传感器接入 Wemos D1 mini(ESP8266)或 Wemos32 mini(ESP32)扩展板:包括安装方法、PlatformIO 依赖配置、引脚定义、显示刷新与自动休眠逻辑、以及每 60 秒一次的 MQTT 温湿度/气压上报实现。读完后,你可以复现整套带屏幕状态指示与传感器遥测的 WLED 构建方案。

这个 usermod 解决什么问题

WLED 默认没有本地显示屏,设备挂在墙上的灯带控制器上时,你无法直接看到它的 IP 地址、连接的 WiFi、当前灯效和调色板——这些正是配网后最常被需要的信息。这个 usermod 利用扩展板(Wemos shield)上的 I2C OLED 把以下信息常驻显示:

  • SSID(第一行)与本机 IP(第二行),AP 模式且亮度为 0 时第二行改显示 AP 密码
  • 当前灯效(mode)名称与调色板(palette)名称(第三、四行)
  • WiFi、Home、太阳/月亮(亮/灭状态)三个图标

同时它还实现了 readme 中列出的完整功能清单:

功能实现依据
SSD1306 128x32 或 128x64 I2C OLEDU8g2 库的U8X8_SSD1306_128X32_UNIVISION_HW_I2C/U8X8_SSD1306_128X64_NONAME_HW_I2C构造函数
屏幕上显示 IP、SSID 与控制器状态(开关、当前灯效)userLoop()中的重绘逻辑
自动熄屏以延长 OLED 寿命3 分钟无变化后调用u8x8.setPowerSave(1)
Dallas 温度传感器(DS18B20)Dallas()快速读数函数 + OneWire 库
温度上报 MQTT brokermqttDeviceTopic/temperature为主题定时发布
继电器省电扩展板自带继电器;从源码看用户固件本身未直接操作继电器引脚,推断由 WLED 主程序的RLYPIN机制(亮度为 0 时断电)承担

文件清单与替换规则(readme 的原始安装说明):

  • 默认(Dallas 传感器)版本:把 usermod.cpp 复制并替换WLED 源码树wled00目录下的usermod.cpp
  • BME280 版本:改用 usermod_bme280.cpp,复制到wled00重命名为usermod.cpp
  • 第三控制器选项:Heltec WiFi-Kit-8,同样带 OLED(纯 DIY 硬件,见下文引脚说明)。

WLED 的 v1 usermod 入口骨架见 wled00/usermod.cpp(userSetup启动时调用一次、userConnected每次 WiFi(重)连时调用、userLoop每轮主循环调用)。这个 usermod 正是填充这三个钩子:userSetup()初始化 OLED 并触发首次传感器读数,userLoop()负责 MQTT 发布与屏幕刷新,userConnected()留空。usermod 的目录组织规范(每个 usermod 一个文件夹、附 readme 说明所需改动)可在 usermods/readme.md 中查看。

引脚定义:ESP8266 与 ESP32 的差异

两个源文件用编译期宏区分两种主控板,这是复现硬件连接的关键:

#ifdef ARDUINO_ARCH_ESP32 // Wemos32 mini uint8_t SCL_PIN = 22; uint8_t SDA_PIN = 21; uint8_t DALLAS_PIN = 23; #else // ESP8266 Wemos D1 mini uint8_t SCL_PIN = 5; uint8_t SDA_PIN = 4; uint8_t DALLAS_PIN = 13; // uint8_t RST_PIN = 16; // Heltec WiFi-Kit-8 时取消注释 #endif

即:

信号Wemos D1 mini(ESP8266)Wemos32 mini(ESP32)
I2C SCLGPIO5GPIO22
I2C SDAGPIO4GPIO21
DS18B20(Dallas)数据脚GPIO13GPIO23
OLED RESET(仅 Heltec WiFi-Kit-8 需要)GPIO16

Heltec WiFi-Kit-8 的 OLED 带独立 RESET 线,此时需要:取消RST_PIN = 16U8X8_PIN_RESET的注释,并把 OLED 对象构造从U8X8_PIN_NONE改为U8X8_PIN_RESET(源码中以“第三选项”注释形式给出)。readme 中提到的“第三个控制器 Heltec WiFi-Kit-8”即由此实现。

OLED 驱动对象的选择(默认启用第一行):

// 第一选项:廉价 128x32 0.91" I2C OLED U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_NONE, U8X8_PIN_SCL, U8X8_PIN_SDA); // 第二选项:128x64 0.96" 或 1.3" //U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(...); // 第三选项:Heltec WiFi-Kit-8(使用 RESET 脚) //U8X8_SSD1306_128X32_UNIVISION_HW_I2C u8x8(U8X8_PIN_RESET, U8X8_PIN_SCL, U8X8_PIN_SDA);

构造函数参数依次为 Reset、SCL、SDA;硬件 I2C(_HW_I2C)意味着 SDA/SCL 必须与上表匹配,否则显示不出来或显示错乱(源码注释建议对照 u8g2 的 u8x8setupcpp 参考页核对)。

安装与 PlatformIO 依赖配置

步骤一:替换 usermod 文件

将对应版本的.cpp放入wled00并命名/替换为usermod.cpp(WLED 只编译wled00/usermod.cpp这一个 v1 入口,这也是 readme 中“copy and replace file in wled00 directory”的原因)。

步骤二:声明外部库依赖

readme 给出的platformio.ini配置片段如下——Dallas 传感器版本:

# platformio.ini ... [platformio] ... ; default_envs = esp07 default_envs = d1_mini ... [common] ... lib_deps_external = ... #For use SSD1306 OLED display uncomment following U8g2@~2.27.3 #For Dallas sensor uncomment following 2 lines DallasTemperature@~3.8.0 OneWire@~2.3.5 ...

BME280 传感器版本:

# platformio.ini ... [platformio] ... ; default_envs = esp07 default_envs = d1_mini ... [common] ... lib_deps_external = ... #For use SSD1306 OLED display uncomment following U8g2@~2.27.3 #For BME280 sensor uncomment following BME280@~3.0.0 ...

即依赖组合为:OLED 必选U8g2@~2.27.3;Dallas 版追加DallasTemperature@~3.8.0OneWire@~2.3.5;BME280 版追加BME280@~3.0.0

适用前提提醒(基于当前仓库实际情况):上述片段中的default_envs = d1_miniesp07以及lib_deps_external段落来自原始 WLED-wemos-shield 项目(readme 中列出的外部项目仓库/Wiki/预编译固件)的platformio.ini。而在当前 WLED 主仓库的 platformio.ini 中,并没有d1_mini/esp07构建环境,也没有lib_deps_external这一键名。若你要在当前仓库的源码树上构建该 usermod,正确做法是利用extra_configs机制——platformio.ini 中声明了extra_configs = platformio_override.ini platformio_release.ini——即按 platformio_override.sample.ini 的样例新建一份platformio_override.ini,在其中自定义d1_mini构建环境(board = d1_mini,ESP8266 平台)并把U8g2OneWireDallasTemperature(或BME280)写入该环境的lib_deps。readme 中的; default_envs = esp07注释也暗示原项目同时支持 ESP8266 的 esp07 环境,可按同样思路扩展。

步骤三:编译烧录

用 PlatformIO 编译你选择的d1_mini(ESP8266)或对应 ESP32 mini 环境并烧录即可。readme 列出的实测验证环境为:Wemos D1 mini 原版 v3.1 及克隆板、Wemos32 mini、PlatformIO 构建流程、SSD1306 128x32 I2C 屏、DS18B20 温度传感器、BME280 温湿度气压传感器、N.O. 瞬动按钮(扩展板上的按钮由 WLED 主程序的BTNPIN机制处理)。

源码走读:userSetup 初始化

Dallas 版 usermod.cpp 的userSetup()逻辑:

void userSetup() { Dallas(DALLAS_PIN, 1); // 触发一次 DS18B20 转换并延时 1s,保证首次读数是有效值 u8x8.begin(); u8x8.setPowerSave(0); // 退出省电模式,点亮屏幕 u8x8.setFlipMode(1); // 翻转画面(按屏的贴装方向调整) u8x8.setContrast(10); // 低对比度以延长 OLED 寿命,需要更亮可调到 255 u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.drawString(0, 0, "Loading..."); }

两个值得注意的细节:

  1. 启动即预热传感器Dallas(DALLAS_PIN, 1)的第二个参数start控制“转换—延时—重试”的循环次数。DS18B20 的0x44转换指令需要时间才能算出数据,启动时先跑一轮并delay(1000),可避免后续读到上一次残留值。
  2. 对比度只有 10:OLED 的对比度寄存器越小画面越暗,这里刻意压暗以降低有机像素的老化速度,与 readme 中“auto display shutoff for extending display lifetime”的寿命策略一脉相承。

Dallas 快速读数函数本身(usermod.cpp)是 OneWire 协议的“免库存”写法:reset()总线复位后发跳过 ROM 命令0xCC和读 Scratchpad 命令0xBE,取 2 字节温度低 12 位;result>>=4后按符号位扩展(data[1]&128时或上61440),并处理了 DS18B20 的 +0.5 LSB 舍入位(data[0]&8)。返回的是 1/2 摄氏度(0.5 °C 精度)的整数。

BME280 版 usermod_bme280.cpp 的userSetup()则额外初始化 I2C 总线并做传感器探测重试

Wire.begin(SDA_PIN, SCL_PIN); while(!bme.begin()) { Serial.println("Could not find BME280I2C sensor!"); delay(1000); } switch(bme.chipModel()) { case BME280::ChipModel_BME280: Serial.println("Found BME280 sensor! Success."); break; case BME280::ChipModel_BMP280: Serial.println("Found BMP280 sensor! No Humidity available."); break; default: Serial.println("Found UNKNOWN sensor! Error!"); }

while(!bme.begin())是死循环重试——I2C 传感器上电慢时能自恢复;chipModel()区分 BME280 与 BMP280,后者没有湿度通道(后续发布的/humidity值会是 NaN)。传感器对象BME280I2C bme默认参数为强制模式、1000 ms 待机、气压/温度/湿度均 1 次过采样、无滤波(源码注释已写明)。

userLoop 上半段:每 60 秒一次 MQTT 遥测

两个版本的userLoop()都以“传感器发布”开头。Dallas 版(usermod.cpp):

temptimer = millis(); if (temptimer - lastMeasure > 60000) { // 60 秒周期 lastMeasure = temptimer; #ifndef WLED_DISABLE_MQTT if (mqtt != nullptr) { // 未连接 MQTT 时跳过,防止 8266 崩溃 #ifdef Celsius int16_t board_temperature = Dallas(DALLAS_PIN, 0); // start=0:只读,不再触发新转换 #else int16_t board_temperature = (Dallas(DALLAS_PIN, 0) * 1.8 + 32); #endif String t = String(mqttDeviceTopic); t += "/temperature"; mqtt->publish(t.c_str(), 0, true, String(board_temperature).c_str()); } #endif }

要点:

  • 主题由 WLED UI 决定mqttDeviceTopic是 WLED 主程序全局变量(在 Web UI 的 MQTT 设置里配置的设备主题),usermod 追加/temperature子主题发布,因此下游 Home Assistant 等系统订阅<你的设备主题>/temperature即可。
  • WLED_DISABLE_MQTT条件编译:如果构建时用 build flag 禁用了 MQTT,这段代码不编译;运行期还要检查mqtt != nullptr——源码注释特别提到不检查会让 ESP8266 崩溃。
  • Celsius宏决定温标:定义时(#define Celsius)以 0.5 °C 步进的整数摄氏度发布;取消定义则按×1.8 + 32换算为华氏度。BME280 版对应地在UpdateBME280Data()里切换BME280::TempUnit_CelsiusTempUnit_Fahrenheit,压力单位固定为 Pa。
  • start=0的含义:循环里每次发布前再调一次Dallas(pin, 0),只读 Scratchpad 并重新发起转换但不阻塞等待,实现“读的同时预热下一次”。

BME280 版发布的是三个主题(usermod_bme280.cpp),同一 60 秒节拍内连续发布:

UpdateBME280Data(); // 读取并缓存 temp / hum / pres(单位:所选温标、%、Pa) mqtt->publish(t.c_str(), 0, true, String(SensorTemperature).c_str()); // <deviceTopic>/temperature mqtt->publish(p.c_str(), 0, true, String(SensorPressure).c_str()); // <deviceTopic>/pressure mqtt->publish(h.c_str(), 0, true, String(SensorHumidity).c_str()); // <deviceTopic>/humidity

UpdateBME280Data()调用bme.read(pres, temp, hum, tempUnit, presUnit)后把结果写入三个全局 float(初值均为NAN,BMP280 的湿度将保持 NaN)。

userLoop 下半段:屏幕刷新与自动熄屏

显示部分是一个“5 秒一拍、按需重绘”的节流状态机(usermod.cpp):

  1. 节拍控制#define USER_LOOP_REFRESH_RATE_MS 5000,距上次检查不足 5 秒直接return
  2. 自动熄屏lastRedraw之后 3 分钟(3*60*1000ms)没有任何重绘,就u8x8.setPowerSave(1)关机屏并置displayTurnedOff;下次需要重绘时先setPowerSave(0)唤醒。
  3. 变更检测:与“上次已知值”(knownSsid/knownIp/knownBrightness/knownMode/knownPalette)逐一比对,任一项变化才置needRedraw = true
if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) { needRedraw = true; } else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) { needRedraw = true; } else if (knownBrightness != bri) { ... } else if (knownMode != strip.getMainSegment().mode) { ... } else if (knownPalette != strip.getMainSegment().palette) { ... }

注意两个细节:AP 模式(apActive)下 IP 被固定显示为占位值4.3.2.1(ESP8266 版的 SSID 也改用WiFi.softAPSSID()),真实 IP 只在 STA 模式下取WiFi.localIP();效果/调色板取自strip.getMainSegment()的主段,即 UI 上你正在操作的那个灯段。

  1. 四行两图标布局(重绘时):
内容说明
第 1 行SSID超过列宽截断,并在末尾补~表示被截断;左侧配 WiFi 图标(open_iconic 80 号字形)
第 2 行IP,或 AP 模式且bri == 0时的 AP 密码左侧 Home 图标(68 号字形);这是“无屏配网”的实用设计——忘记密码时把灯关掉就能看到
第 3 行灯效名通过extractModeName(knownMode, JSON_mode_names, ...)取 16 字符以内名称,左侧太阳/月亮图标表示亮/灭(bri > 0时切到亮月字形 69,否则 66)
第 4 行调色板名extractModeName(knownPalette, JSON_palette_names, ...)

灯效名/调色板名的提取由 WLED 主程序提供:extractModeName()定义于 wled00/util.cpp(声明见 wled00/fcn_declare.h),它从strip.getModeData(mode)拷贝到遇到\0@(SR 扩展分隔符)为止,并保证写入长度不超过maxLen。usermod 传入 16 字节缓冲、上限 15,正好适配 128x32 屏每行 32 列的版式。这也体现了 v1 usermod 的典型模式:usermod 只做外设交互,状态数据(SSID、IP、亮度bri、灯段 mode/palette)全部复用 WLED 全局变量与工具函数

两套版本怎么选、有哪些限制

  • Dallas(DS18B20)版:只需温度,占一个 GPIO(D1 mini 的 13 / Wemos32 mini 的 23),依赖OneWire+DallasTemperature;温度精度 0.5 °C(12 位读取),发布为整数。
  • BME280 版:温度 + 湿度 + 气压三合一,走 I2C 与 OLED 共享 SDA/SCL(WLED 的 I2C 默认引脚与此处Wire.begin(SDA_PIN, SCL_PIN)一致,D1 mini 为 4/5),依赖BME280@~3.0.0;注意若实际芯片是 BMP280,湿度无意义。
  • 显示刷新是事件驱动 + 5 秒节拍的,任何一项(SSID/IP/亮度/灯效/调色板)变化最坏 5 秒内上屏;静态场景 3 分钟熄屏。
  • 该 usermod 属 v1 API(直接替换wled00/usermod.cpp),同一构建只能启用一个 v1 usermod 文件;若需要多个 usermod 共存或更多 API,仓库推荐转向 v2 usermod API(可参考 usermods/readme.md 中提到的EXAMPLE_v2Temperature示例)。
  • 由于它直接引用mqttmqttDeviceTopicbriapActiveapPassstrip等 WLED 内部全局符号,随 WLED 大版本演进存在兼容风险,这与 usermods/readme.md 中“usermods 可能随 WLED 更新而损坏、维护责任在作者”的声明一致。

小结

这个 Wemos 扩展板 usermod 用约 200 行代码演示了 v1 usermod 的完整套路:在userSetup中初始化 I2C 外设(OLED 用 U8g2 的硬件 I2C 构造,BME280 用带重试的begin()),在userLoop中做“定时遥测 + 节流失效重绘 + 低功耗熄屏”三件事,并全部复用 WLED 的全局状态与工具函数(extractModeNamemqttDeviceTopic、主灯段 mode/palette)。配合 readme 给出的 PlatformIO 依赖清单(U8g2OneWire/DallasTemperatureBME280),再按当前仓库的platformio_override.ini机制补齐d1_mini构建环境,即可得到一台带状态屏、能自我熄屏、并把环境数据持续推给 MQTT 的 WLED 控制器。

【免费下载链接】WLEDControl WS2812B and many more types of digital RGB LEDs with an ESP32 over WiFi!项目地址: https://gitcode.com/GitHub_Trending/wl/WLED

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 13:04:48

明牌功放:参数透明化如何重塑音频设备信任体系

1. 项目概述&#xff1a;当“明牌”从扑克桌杀进功放圈&#xff0c;我们到底在聊什么&#xff1f;这功放都做到明牌了&#xff1f;跟吗&#xff1f;——看到这个标题&#xff0c;我第一反应不是去翻电路图&#xff0c;而是下意识摸了摸手机屏幕&#xff0c;确认自己没点进错直播…

作者头像 李华
网站建设 2026/9/13 13:04:24

用Django+MySQL构建图书管理系统:从模型设计到部署优化全指南

简介&#xff1a;基于Django与MySQL的图书管理系统源码包&#xff0c;面向需要课程设计、毕业设计或实际项目参考的Web开发者&#xff0c;提供了从数据库设计到前端交互的完整实现方案。系统涵盖图书增删改查、批量入库、多条件排序、借阅续借及归还、用户注册登录等核心功能&a…

作者头像 李华
网站建设 2026/9/13 13:04:19

台达CANopen伺服CAN总线调试实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 13:03:20

Argo CD 核心配置 argocd-cm.yaml 全参数详解与实战指南

Argo CD 核心配置 argocd-cm.yaml 全参数详解与实战指南 【免费下载链接】argo-cd Declarative Continuous Deployment for Kubernetes 项目地址: https://gitcode.com/GitHub_Trending/ar/argo-cd Argo CD 的绝大部分系统级行为——从外部访问地址、SSO 登录、资源差异…

作者头像 李华
网站建设 2026/9/13 13:02:43

Archon 变量替换完全指南:Workflow 与命令中的占位符系统详解

Archon 变量替换完全指南&#xff1a;Workflow 与命令中的占位符系统详解 【免费下载链接】Archon The first open-source harness builder for AI coding. Make AI coding deterministic and repeatable. 项目地址: https://gitcode.com/GitHub_Trending/archon3/Archon …

作者头像 李华