作者在电子制作 DIY 方面仅为爱好者水平,文中如有错误,欢迎指正交流。
前言
刷淘宝的时候看到有像素时钟,预览图很戳我的喜好,但是一看价格要 90+,感觉还是有点贵了。合计了下,手上正好有一块 ESP32,遂决定自己做一个。
制作思路
首先确定基本需求,时钟最重要的还是显示时间,而且得走得准。
本来想着加一个单独的时钟模块,后来想起手上还有一块之前凑单买的 ESP32-C3 Mini,带 Wi-Fi 和 BLE。既然可以联网,那就借助 NTP 自动校时,腾讯云、阿里云都有公共的 NTP 服务器,也就先不加独立时钟模块了。现在程序默认每小时校时一次,也可以在 App 里修改间隔。
不过这里有一个前提:上电后得先拿到正确的时间。断网期间可以靠 ESP32 的系统时钟继续走,但断电之后就不知道停了多久,重新上电还得联网,或者用手机手动设时。这一点和带后备电池的独立时钟模块还是有区别的。
除了时间,也想顺便显示一些常用信息,温湿度挺合适,所以又加了一个 DHT22 模块。
还有就是晚上不能太晃眼睛,因此加了光敏电阻模块,根据环境光自动调整亮度。程序里也保留了固定亮度和睡眠亮度两种模式,后面可以按实际使用情况调整。
电路部分直接用洞洞板焊起来,再配一个 3D 打印的外壳,做出一个能放在桌上用的版本。
成本汇总
| 模块 | 价格(元) |
|---|---|
| ESP32-C3 Mini 最小系统板 | 10.39 |
| WS2812B 16×16 像素矩阵屏 | 24.48 |
| 四路 3.3V / 5V 双向电平转换模块(这次只用到一路) | 1.87 |
| DHT22 模块 | 5.28 |
| 洞洞板 | 2.20 |
| 磨砂亚克力板 200×200×2 mm | 5.20 |
| 光敏电阻模块 | 1.65 |
| 焊锡丝、3D 打印耗材等暂不计入 | - |
| 合计 | 51.07 |
上面算的是这些材料的钱,电源、线材和制作时间也没有算进去,比成品便宜一些,虽说如果真要把折腾的时间也算上,就不好说了。不过折腾的过程也正是乐趣所在嘛 :P
制作流程
首先是像素屏,从淘宝买了一块 WS2812B 16×16 矩阵,总共 256 颗 LED。可以把它理解成一串按蛇形排列的可编程灯珠,每一颗的颜色都能单独控制,排成矩阵后就可以当作一个低分辨率的屏幕。

这块屏幕用 5V 供电。一开始还担心普通充电器带不动,到货后试了一下,手上的充电器和充电宝都能点亮。不过这只是当时的显示内容和亮度下的结果,不能因此认为全屏满亮也没问题。平时显示几个数字,和 256 颗灯同时亮白色,耗电差别还是很大的。
程序里默认也做了 1000 mA 的估算限流,超出预算就降低输出亮度。这里没有测量真实电流,所以电源和接线还是要按实际负载来选,不能把这个设置当成硬件保护。
然后是在面包板上确定接线、调试程序。我之前没接触过 ESP32,也没做过相关的 BLE 和 Wi-Fi 开发,想在国庆假期里速成,时间还是太紧了,况且也不能保证 7 天都满时间投入。
后面想了想,干脆借助 AI,把需求整理出来,拆成一步一步的小任务,再把外围器件、接线和 datasheet 告诉它,让它逐步实现。我用的模型是 GPT-6.1 sol 和 GPT-6 Luna,生成的效果还蛮可以的。
目前嵌入式代码和手机客户端代码都是 AI 生成的,我一行代码都没写。不过程序生成出来之后,烧录、接线、看日志和排查硬件问题这些事情还是得自己来,后面 Wi-Fi 连不上就折腾了很久。
嵌入式部分用的是 ESP-IDF,客户端用 Flutter,通过 BLE 给时钟配置 Wi-Fi、亮度、显示颜色和轮播内容。设置保存在时钟里,不需要一直开着手机 App,配好之后就可以独立运行。
后面还加了画板、贪吃蛇和麦克风频谱这些小功能。频谱用的是手机端的麦克风,由 App 算好画面再通过 BLE 发到屏幕上,时钟本身没有接麦克风。
也不得不感叹,在 AI 时代,单纯把代码写出来的门槛确实越来越低了。这次花掉的时间里,有不少都在想清楚需求,以及排查东西接上去之后为什么不能正常工作。
程序和接线确认后,再焊到洞洞板上。焊好之后大概是这样。



接下来就是做外壳。我用 Blender 建模,导出 STL,切片后交给拓竹 A1 打印。为了减少相邻灯珠之间的串光,还做了一层网格,让每颗 LED 尽量待在自己的格子里。


立起来大概是这样,上面显示时间,下面可以轮播日期、温度和湿度。


不过直接看灯珠还是有点晃眼睛,所以最前面又加了一块磨砂亚克力板。一方面让光散开,减轻直视 LED 的刺眼感,另一方面也能让每一格看起来更均匀,不至于只有灯珠所在的位置特别亮。

最后把亚克力板裁到合适大小,盖到前面。建模的时候忘了考虑面板怎么固定,现在只能用热熔胶粘起来,比较丑,后面有机会再改。

另外,我买到的这块像素屏正面并不是完全平的,网格底部没有避开凸起的地方,没法完全贴合。因此底部还是留了一些空隙,有漏光,相邻格子也被照亮了一点。

外观还有这些小问题,不过最后的效果基本符合预期。写这篇文章的时候已经连续运行了好几天,暂时没遇到明显异常,显示时间也没有发现明显偏差。
踩坑记录
过程中也踩了一些坑,这里留个记录。
WS2812B 的供电和信号电平是两回事
这块屏幕是 5V 供电,但 ESP32-C3 的 GPIO 输出是 3.3V。我这次直接接上去没能正常驱动,最后加了电平转换模块才解决,当时不知道这一点,花了一个下午排查 orz
不过也不能直接说“所有 WS2812B 都认不了 3.3V”。不同版本灯珠的输入门槛有区别,厂家后续版本也调整过控制信号电平,实际要看买到的型号和规格书。厂家升级说明里就提到了这件事。
另外,屏幕和 ESP32 要共地,数据线接的是 DIN,别接到 DOUT。电平转换模块也得能跟上这里 800 kHz 的数据时序,不能只看商品名写了“3.3V 转 5V”就认为都一样。
灯珠亮了,显示方向也可能不对
16×16 看起来是一个二维平面,但发送数据时还是一颗接一颗。蛇形的起点在哪、按行还是按列排列、外壳里怎么安装,都会影响最后的显示方向。
现在程序里留了旋转、镜像、行列布线和蛇形开关,App 里也能调整。测试时可以先显示四角不同颜色,再逐颗点亮确认顺序,比一开始就显示时间更容易看出问题。仓库里也留了单独的矩阵测试程序,不用把 Wi-Fi、蓝牙和传感器全启动就能测屏幕。
Wi-Fi 扫描不到和连接不上,要分开看
通过 BLE 把 Wi-Fi 信息发给 MCU 之后,一直连不上家里的路由器,但手机热点又可以。前后遇到过两种情况:
- 扫描不到路由器的 Wi-Fi。
- 能找到路由器,但连接一直失败。
一开始路由器用的是信道 12,改成信道 1 之后就能找到了。这里不能简单理解成 ESP32-C3 不支持信道 12,扫描范围还和国家或地区配置有关。ESP-IDF 文档里说明,默认的 world safe mode 配置范围是信道 1~11。改信道是这次有效的处理办法,具体配置还得结合实际情况检查。
后面扫描到了,还是连不上,又折腾了一晚上。其中一个有效的调整是降低 Wi-Fi 的最大发射功率。回头看代码,最后保留的是 13 dBm 的上限,对应 esp_wifi_set_max_tx_power(52);这个接口的参数单位是 0.25 dBm,并不是直接填 dBm 数值。接口说明里也有实际档位的映射。
调试时还关闭过 Wi-Fi 省电模式,调整过 PMF 等连接设置,所以现在只能说这套设置下连上了,具体是不是供电、射频或者路由器兼容性的问题,还没有单独验证清楚。不能把“调低功率就好了”当成所有 ESP32-C3 连不上 Wi-Fi 的通用解法。
后面还专门拆了一个只连接 Wi-Fi 的测试程序,不启动 BLE、屏幕和传感器,单独看扫描、关联、断开原因和获取 IP 的日志。排查时也要分清楚卡在哪一步:没关联上路由器、关联了但没拿到 IP、拿到 IP 之后 NTP 请求失败,处理方向都不一样。
DHT22 的读取时序问题
最初的代码靠软件轮询测量高低电平的持续时间,为了不被打断,接收整帧数据期间会屏蔽中断,持续大约几毫秒。把 Wi-Fi 和 BLE 一起跑起来之后,这种做法有影响通信的风险。
后面改成用 ESP32-C3 的 RMT 外设接收脉冲,再交给程序解码,避免在整段接收期间屏蔽中断。目前温湿度每 2 秒读取一次。
保存时间,不代表断电后还能准确走时
现在程序每 10 分钟保存一次时间检查点,重新启动时可以从上次保存的时间继续走。不过断电期间过去了多久,ESP32 并不知道,检查点本身也可能是断电前几分钟保存的。
所以这个功能只能做一个近似恢复,最终还是要靠联网校时,或者手机设时。以后如果想做断网、断电之后仍然能保持准确时间的版本,还是得把带后备电池的独立时钟模块考虑进去。不过在目前的使用场景下,已经可以覆盖几乎所有的场景了,问题不大。