1. 项目概述与核心价值如果你也和我一样喜欢在家里囤点干货、茶叶或者是个电子元件爱好者总担心储物罐里的环境温湿度不合适那这个项目你一定会感兴趣。我最近动手做了一个基于Feather M4和电子墨水屏的温湿度监控器它就像一个不知疲倦的“环境哨兵”能持续监测罐子内部的状况一旦温湿度越界就立刻报警而平时则依靠电子墨水屏的特性在完全断电的情况下依然显示着上一次的读数。这不仅仅是又一个简单的传感器读数项目它融合了低功耗设计、持久化显示和用户可配置交互是一个相当完整的嵌入式系统实践案例。这个监控器的核心目标是解决密封存储环境下的温湿度监控问题。无论是防止中药材受潮发霉还是确保摄影器材干燥甚至是给心爱的雪茄找个安稳的家它都能派上用场。其工作原理并不复杂主控芯片Feather M4 Express定时例如每小时从SI7021传感器读取一次温湿度数据与用户预设的安全范围进行比较然后将结果刷新到电子墨水屏上。如果数据异常它会通过蜂鸣器发出警报直到环境恢复正常或用户手动干预。整个系统的“大脑”运行着用CircuitPython编写的固件这使得开发和调试变得异常简单即使你不是嵌入式开发老手也能轻松上手。我选择这个方案主要是看中了它的实用性和可玩性。实用性在于电子墨水屏eInk的零功耗显示特性让设备在两次测量间隔可以彻底断电仅由一颗小小的定时芯片TPL5111唤醒这使得一颗1200mAh的锂电池可以轻松工作数月。可玩性在于CircuitPython带来了类似桌面Python的开发体验你可以随时通过USB连接电脑像修改文本文件一样更新代码和配置文件调整报警阈值、显示标题甚至自定义报警音效完全摆脱了传统嵌入式开发中编译、烧录的繁琐流程。接下来我就带你从硬件选型到代码实现完整地拆解这个项目的每一个细节。2. 硬件选型与设计思路解析2.1 主控板为什么是Feather M4 Express在早期的版本中项目使用了Trinket M0。这次升级到Feather M4 Express是一个质的飞跃。Trinket M0虽然小巧便宜但其有限的IO引脚和性能制约了功能的扩展比如驱动一个需要多条控制线的电子墨水屏就显得捉襟见肘。Feather M4 Express的核心是ATSAMD51 Cortex-M4处理器运行频率高达120MHz并拥有512KB的Flash和192KB的RAM。这为运行CircuitPython提供了充裕的空间。我选择它主要基于以下几点考量丰富的IO与内置充电管理Feather板载了锂电池充电芯片和JST PH连接器这意味着你可以直接接上一块3.7V的锂聚合物电池并通过USB口为电池充电无需额外的充电模块或复杂的电源管理电路。这对于需要长期离线工作的设备来说是至关重要的便利。CircuitPython的完美支持Adafruit对Feather M4的CircuitPython支持非常成熟驱动库丰富社区资源也多。用Python写嵌入式逻辑调试和迭代的速度远超传统的C/C。引脚布局与扩展性Feather标准的引脚排列和丰富的模拟、数字、PWM、I2C、SPI接口使得连接传感器、显示屏和其他外设变得非常直观。未来如果你想增加更多传感器如光照、气压也有足够的余地。注意市场上也有其他强大的开发板如ESP32系列其自带Wi-Fi/蓝牙更适合需要联网的场景。但本项目核心需求是极致的低功耗和简单可靠无线功能会带来额外的功耗和复杂度因此Feather M4的“纯粹”与“高效”更符合定位。2.2 传感器SI7021的可靠性与精度温湿度传感器选择SI7021是一个经过市场检验的稳妥决定。相比常见的DHT11/DHT22SI7021采用I2C接口通信更稳定精度和响应速度也更高湿度精度±3% RH温度精度±0.4°C。它的接口极其简单只需要连接VCC、GND、SCL、SDA四根线。其内部集成了信号处理电路直接输出校准后的数字值省去了微控制器进行复杂模拟信号处理和补偿的麻烦。在代码中我们只需调用adafruit_si7021库几行代码就能轻松读取数据非常省心。在实际部署时你需要考虑传感器的放置。为了准确测量罐内环境最好用一根杜邦线将传感器模块引到罐子内部而主控板留在外部。确保传感器周围空气流通不要被物品直接包裹同时要做好引线处的密封以维持罐内的密闭环境。2.3 显示模块电子墨水屏的零功耗魅力本项目最大的亮点之一就是这块1.54英寸、152x152像素的三色黑、白、红电子墨水屏。选择它而非传统的LCD或OLED原因只有一个功耗。电子墨水屏的工作原理是通过电场控制带电荷的颜料颗粒移动一旦图像刷新完成即使完全断电图像也能保持数月不变。这意味着我们的设备99%的时间都可以处于深度睡眠状态只有每秒一次唤醒刷新屏幕的那几秒钟需要消耗能量。这对于电池供电的设备而言是革命性的优势。当然它也有缺点刷新速度慢全屏刷新需要2-3秒且有刷新次数寿命。但在每小时才更新一次数据的监控器上这两个缺点几乎可以忽略不计。三色屏的加入让我们可以用红色高亮显示报警信息视觉提示非常醒目。这块屏通过SPI接口与Feather通信除了电源和SPI引脚SCK, MOSI, MISO还需要额外的控制引脚ECS, DC, SRCS, RST, BUSY。Feather M4丰富的IO资源完美满足了这一需求。2.4 电源与定时管理TPL5111的精准守夜人如何实现每小时唤醒一次这里用到了一个精巧的芯片TPL5111低功耗定时器。它的作用就像一个严格的“守夜人”。TPL5111的核心功能是在设定的时间间隔后将其DONE引脚拉高或拉低可配置。我们将Feather M4的EN使能引脚连接到TPL5111的DONE引脚。平时TPL5111输出低电平Feather处于断电状态。当定时时间一到TPL5111将DONE拉高瞬间给Feather上电。Feather启动后第一件事就是通过一个GPIO口本例中是A4将DONE信号主动拉低告诉TPL5111“我醒了计时暂停”。然后Feather执行测量、显示、报警等所有任务。任务完成后Feather再将DONE拉高TPL5111检测到高电平立即切断Feather的电源并开始下一轮的定时。这种设计实现了近乎零的待机功耗只有TPL5111自身的微安级电流。定时精度由TPL5111的外接电阻决定。为了获得更精确、更稳定的定时我建议剪断板载电位器的调节走线改用一颗精密贴片电阻。根据数据手册定时时间T (R * 0.1) 0.5秒其中R是电阻值单位千欧。例如要实现1小时3600秒唤醒可以计算所需电阻R (T - 0.5) / 0.1 (3600 - 0.5) / 0.1 35995 kΩ即约36MΩ。你可以选择一个接近的标准值电阻。一个关键的硬件技巧Feather M4的GPIO在上电初始瞬间是浮空状态可能为高电平。如果TPL5111在Feather还没准备好拉低DONE引脚时就检测到了高电平它会误以为任务已完成立即断电导致系统无法启动。解决方法是在Feather的DONE控制引脚A4和地GND之间并联一个100nF0.1uF的电容。这个电容在上电瞬间会短暂地将该引脚电压拉低为Feather的固件运行争取到几十毫秒的初始化时间等Feather的GPIO配置完成并输出低电平时电容也充电完毕系统便稳定进入工作状态。这个小电容是系统可靠启动的保障必不可少。2.5 其他外围器件蜂鸣器选择一个无源压电蜂鸣器。注意它需要PWM方波驱动才能发声不能直接接电源。我们使用Feather的PWM引脚D5来控制它。按钮一个轻触开关用于消警和进入编辑模式。连接时需启用内部上拉电阻这样平时读到的就是高电平按下时变为低电平。电池一块3.7V、1200mAh的锂聚合物电池通过Feather的JST接口连接即可。这个容量对于本项目来说绰绰有余。3. 电路连接与原型制作要点3.1 完整接线图与原理根据上面的分析我们可以整理出清晰的接线表。请务必在通电前仔细核对。Feather M4 Express 引脚连接至说明VBAT锂电池正极供电输入GND锂电池负极、所有模块GND公共地ENTPL5111DONE引脚电源使能控制A4TPL5111DONE引脚控制TPL5111定时并联100nF电容到GND确保可靠启动3.3VSI7021 VIN, eInk屏 VCC, TPL5111 VDD3.3V电源输出GNDSI7021 GND, eInk屏 GND, TPL5111 GND电源地SCLSI7021 SCLI2C时钟线SDASI7021 SDAI2C数据线SCKeInk屏 SCKSPI时钟线MOSIeInk屏 MOSISPI数据输出线MISOeInk屏 MISOSPI数据输入线本项目未用但需连接D6eInk屏 RST复位引脚D9eInk屏 SRCSSRAM片选本屏内置SRAMD10eInk屏 DC数据/命令选择D11eInk屏 ECS片选引脚D12eInk屏 BUSY忙状态指示D5蜂鸣器正极PWM输出驱动蜂鸣器A5按钮开关一端按钮信号输入按钮开关另一端连接GND接线实操心得电源优先首先连接所有模块的电源3.3V和GND确保供电正常再连接信号线。I2C上拉电阻SI7021模块通常已集成上拉电阻。如果传输距离较长或干扰大可以在SCL和SDA线上各加一个4.7kΩ的上拉电阻到3.3V。SPI线序SPI接口的线序SCK, MOSI, MISO必须一一对应接反了屏幕不会有任何反应。按钮防抖虽然代码中通过延时进行了软件防抖但在硬件上在按钮两端并联一个0.1uF的电容可以更好地滤除触点抖动让检测更稳定。3.2 原型制作与封装建议原项目作者使用了万用板和飞线焊接然后固定在一个3D打印的广口瓶盖子上。这是一个非常实用的原型方案。制作步骤焊接主控板将Feather M4、TPL5111、蜂鸣器、按钮以及必要的电阻电容焊接在一小块万用板上。注意元器件布局要紧凑但也要为连接屏幕和传感器的排针留出空间。处理TPL5111使用美工刀或烙铁小心地割断TPL5111模块背面的“Trim”走线断开板载电位器以及“LED”走线禁用指示灯以省电。然后焊接上你计算好的定时电阻。连接屏幕与传感器使用排针和杜邦线将屏幕和传感器作为可插拔模块连接到主控板。这样方便调试和更换。制作密封盖如果你有3D打印机可以设计或下载一个适配你存储罐的盖子模型并在上面预留安装主控板和屏幕的孔位。没有打印机的话也可以使用厚亚克力板或塑料板手工制作。关键是要处理好传感器线的穿孔可以使用热熔胶或专用的电缆防水接头格兰头进行密封以保证罐内的气密性。整体组装将主控板用螺丝或尼龙柱固定在盖子上方屏幕嵌入开孔中。传感器模块用延长线引入罐内并妥善固定避免直接接触物品。注意事项在最终封装前务必进行完整的系统测试。包括上电能否正常启动、屏幕显示是否正常、按钮功能、传感器读数、报警触发与消警、以及最重要的——TPL5111定时唤醒功能是否工作。你可以先将定时电阻换成一个较小阻值的例如对应10秒快速测试几个睡眠-唤醒周期。4. 软件架构与CircuitPython代码深度解析4.1 工程结构与文件说明将Feather M4通过USB连接到电脑它会显示为一个名为CIRCUITPY的U盘。我们的所有代码和配置文件都将放在这里。CIRCUITPY/ ├── code.py # 主程序系统启动后自动运行 ├── settings.txt # 用户配置文件定义报警阈值等参数 ├── description.txt # 显示在屏幕上的描述文本 ├── font.py # 8x8点阵字体文件 └── lib/ # 库文件目录 ├── adafruit_bus_device/ ├── adafruit_epd/ └── adafruit_si7021.mpycode.py这是CircuitPython的入口文件相当于Arduino的sketch.ino。系统启动后会自动执行这个文件。settings.txt一个文本文件用于配置温度/湿度范围、报警参数等。修改这个文件就能改变设备行为无需重写代码。description.txt最多4行每行最多19个字符的文本会显示在屏幕中央用于标注罐内物品信息。font.py包含了一个8x8像素的ASCII字符点阵数据。由于CircuitPython的显示库可能不包含小字体我们自定义这个字体文件来渲染文本。lib/目录存放项目依赖的第三方库。你需要从Adafruit的CircuitPython库包中将adafruit_bus_device、adafruit_epd和adafruit_si7021这三个库复制到这里。4.2 核心代码流程剖析让我们深入code.py看看这个监控器是如何工作的。4.2.1 生死攸关的第一行代码稳住电源系统上电后CircuitPython运行时环境会先初始化然后才执行我们的code.py。但TPL5111定时器可能比这更快地检测DONE引脚状态。因此代码最开头的任务不是导入库而是立即配置并拉低控制TPL5111的DONE引脚。import digitalio import board done digitalio.DigitalInOut(board.A4) done.direction digitalio.Direction.OUTPUT done.value False # 关键告诉TPL5111“我还在工作别断电”这四行代码是系统稳定运行的“定海神针”。它抢在TPL5111可能误判之前强行将控制线置于安全状态。4.2.2 硬件初始化与默认配置稳住电源后便可以安心初始化其他硬件了。这部分代码顺序清晰导入依赖引入时间、PWM、总线IO、显示屏驱动和传感器驱动等库。初始化SPI与电子墨水屏按照屏幕驱动库的要求配置所有控制引脚并创建显示对象。初始化I2C与SI7021传感器I2C通信是标准的初始化后即可读取数据。配置蜂鸣器与按钮将蜂鸣器引脚设置为PWM输出按钮引脚设置为输入并启用内部上拉电阻。设置默认参数创建一个settings字典存放所有可配置参数的默认值。如果settings.txt文件不存在或配置错误系统将使用这些默认值。# 默认参数示例 settings[temperature_range] (15, 30) # 温度下限和上限单位摄氏度 settings[humidity_range] (60, 70) # 湿度下限和上限单位百分比 settings[title] Jar Minder # 屏幕顶部显示的标题 settings[alarm_frequency] 4000 # 报警蜂鸣器频率单位赫兹 settings[alarm_number_of_beeps] 3 # 每次报警循环的蜂鸣次数 settings[alarm_seconds_beep_on] 0.5 # 单次蜂鸣持续时间 settings[alarm_seconds_between_beeps] 0.5 # 蜂鸣间隔时间 settings[alarm_timeout] 60.0 # 报警持续总时间超时后自动休眠4.2.3 文本渲染引擎自己动手“画”字由于使用的adafruit_epd库可能没有内置小字号字体或者为了极致控制项目选择自己实现一个简单的位图字体渲染器。这就是font.py和render_character、render_string函数的作用。font.py文件定义了一个庞大的列表bitmaps其中每个元素对应一个ASCII字符的8x8点阵数据用8个十六进制数表示。render_character函数根据字符的ASCII码找到对应的点阵数据然后遍历8行8列如果某位是1就在屏幕对应位置画一个黑色或红色的点如果是0就画白色擦除。render_string函数则负责连续调用render_character实现字符串的绘制。centered函数是一个简单的工具根据字符串长度计算使其在152像素宽的屏幕上居中显示的起始X坐标。这种做法的优劣优点完全可控不依赖外部字体文件节省内存渲染速度快。缺点字体固定只有一种大小不支持中文等非ASCII字符。如果你需要显示中文需要寻找包含中文字阵的库或者使用支持TrueType字体渲染的更高性能主板。4.2.4 编辑模式一个巧妙的交互设计设备如何知道用户想要修改配置呢它没有键盘和复杂的菜单。这里用了一个非常巧妙的“上电检测”法。在代码初始化完硬件后、读取配置文件前会立刻检查按钮silence_button是否被按下if not silence_button.value: # 如果按钮在上电时就是按下的 # 1. 发出一声低音蜂鸣提示进入编辑模式 # 2. 在屏幕中央显示“EDIT MODE” # 3. 等待用户松开按钮再等待用户再次按下按钮 # 4. 再次发出低音蜂鸣退出编辑模式继续正常启动流程这个设计的精妙之处在于它利用了一次完整的“按下-松开-再按下”作为触发信号误触概率极低。进入编辑模式后设备会保持上电状态此时用户可以通过USB连接电脑直接修改CIRCUITPY盘里的settings.txt和description.txt文件。修改保存后再次按下按钮设备蜂鸣提示并重启加载新的配置。整个过程无需重写代码交互简单直观。4.2.5 主循环逻辑监测、显示与报警如果不是编辑模式设备就会进入正常的主工作流程读取配置与描述打开settings.txt和description.txt将内容解析后存入变量。清屏并绘制静态内容清除屏幕缓冲区在顶部绘制标题在中间区域绘制描述文本。读取传感器数据从SI7021读取温度和湿度并转换为整数。显示传感器数据在屏幕左右两侧固定位置绘制温度和湿度数值。判断与报警提示将读数与settings中设定的范围比较。如果读数异常则在屏幕下方用红色字体显示“HIGH/LOW TEMPERATURE/HUMIDITY”。同时在屏幕的顶部和底部绘制红色警示条提供强烈的视觉警报。报警处理循环如果数据正常直接跳到第7步。如果数据异常启动一个超时循环时长由alarm_timeout设定。在循环内调用sound_alarm()函数按照设定发出“哔-哔-哔”的报警声。在蜂鸣响起或间隔期间持续检测按钮。如果用户按下按钮则立即静音并跳出报警循环。如果超时时间到也跳出循环。任务完成进入休眠所有工作完成后执行最后也是最重要的一步done.value True。这将DONE引脚拉高TPL5111检测到信号立即切断Feather的电源。设备进入深度睡眠直到下一个定时周期到来。4.3 关键函数详解check_for_push(button, duration): 这是一个非阻塞式的按钮检测函数。它会在指定的duration秒内每0.1秒检查一次按钮状态。一旦检测到按下立即返回True如果超时都未按下则返回False。这保证了在等待蜂鸣或报警间隔时系统仍然能响应用户的消警操作。sound_alarm(): 报警发声函数。它按照配置的频率、次数、时长来驱动蜂鸣器。关键在于每一次蜂鸣和每一次蜂鸣间的间隔都包裹在check_for_push调用中。这意味着用户可以在蜂鸣响起的任何时候按下按钮来立即静音交互响应非常及时。out_of_range(t, h): 简单的逻辑判断函数检查当前读数是否超出预设范围。它是触发整个报警流程的“开关”。5. 调试、优化与扩展方向5.1 常见问题与排查实录在制作和调试过程中我遇到了几个典型问题这里分享给大家问题1设备上电后屏幕闪一下然后立刻关机无法正常工作。排查这是最可能遇到的问题根本原因就是前面提到的“电源竞争”问题。TPL5111过早地检测到了DONE引脚的高电平。解决首先确认代码最开头是否有done.value False这行代码并且done引脚定义正确A4。检查硬件确保在Feather的A4引脚和GND之间正确焊接了一个100nF104的瓷片电容。电容务必贴近引脚焊接。用万用表测量TPL5111的DONE引脚在上电瞬间的电压变化确认电容起到了延时作用。问题2屏幕显示乱码或者完全不显示。排查接线错误这是首要怀疑对象。请严格按照接线表逐根检查SPI总线SCK, MOSI, MISO以及控制线ECS, DC, RST, BUSY, SRCS是否连接正确、接触良好。特别是BUSY线必须连接否则程序无法知道屏幕何时刷新完成。库文件缺失或版本不匹配确认lib文件夹下是否有adafruit_epd及其依赖库adafruit_bus_device。尝试从Adafruit官网下载最新的CircuitPython库包进行替换。电源问题电子墨水屏在刷新瞬间需要较大电流。确保你的电池电量充足或者尝试通过USB供电测试排除电池供电不足的可能。问题3传感器读数不准或为0。排查检查SI7021的I2C接线SDA, SCL。在代码中尝试扫描I2C设备地址确认是否能找到SI7021通常地址是0x40。import board import busio i2c busio.I2C(board.SCL, board.SDA) while not i2c.try_lock(): pass print([hex(x) for x in i2c.scan()]) i2c.unlock()SI7021对焊接温度敏感过热可能损坏。检查传感器模块是否完好。问题4报警不响或按钮无效。排查蜂鸣器是有源还是无源本项目需使用无源蜂鸣器。有源蜂鸣器给电就响无法通过PWM控制音调。检查蜂鸣器正负极是否接反虽然通常影响不大以及是否接在了支持PWM的引脚D5上。按钮接线是否正确一端接A5另一端接GND。代码中启用了内部上拉所以按钮按下时A5应读到低电平False。5.2 功耗优化实测与电池寿命估算低功耗是本项目的核心目标。我们来粗略估算一下电池寿命。睡眠电流Feather M4被完全断电电流为0。TPL5111的静态电流典型值为35nA纳安级几乎可以忽略不计。传感器、屏幕等均已断电。工作电流设备唤醒后主要耗电单元是Feather M4、SI7021和屏幕刷新。Feather M4运行CircuitPython约10-20mA。SI7021测量时约1.5mA。eInk屏全屏刷新峰值电流可达30mA以上但持续时间很短约2秒。单次唤醒耗时假设测量、计算、显示刷新、报警判断等所有操作在5秒内完成。唤醒间隔设为1小时3600秒。计算 假设工作期间平均电流为15mA工作5秒。 每小时耗电量 15mA * (5 / 3600)h ≈ 0.0208 mAh。 1200mAh的电池理论工作时间 1200 / 0.0208 ≈ 57692 小时 ≈ 6.6 年。这只是一个非常理想化的理论值实际中电池自放电、电路漏电、温度影响、报警状态蜂鸣器耗电都会缩短续航。但即便如此持续工作几个月到一年是完全没有问题的。你可以通过增大TPL5111的定时电阻来延长唤醒间隔从而进一步增加续航。5.3 项目扩展与进阶玩法这个项目的基础框架非常扎实留下了很多扩展空间数据记录与历史趋势为Feather M4增加一个微型SD卡模块每次测量后将时间戳和读数写入CSV文件。这样你就能分析罐内环境的历史变化趋势。无线传输与云端监控将主控换成Adafruit Feather M4 Express with AirLift或者Feather ESP32-S2集成Wi-Fi功能。设备唤醒后可以将数据通过MQTT协议发送到Home Assistant、Blynk或自建的服务器上实现手机远程监控和报警推送。多传感器与融合判断增加一个BME280传感器同时获取温度、湿度、气压。气压数据可用于判断罐子是否被打开过气压突变。或者增加一个光照传感器监控是否暴露在光线下。改进人机交互增加更多按钮实现模式切换、翻页查看历史数据等功能。使用旋钮编码器在编辑模式下可以更直观地调整阈值数值而不用连接电脑修改文件。美化显示与UI设计更美观的字体和图形可以寻找或制作更小的像素字体以显示更多信息。或者绘制简单的温度计、水滴图标。添加进度条或图表用简单的条形图显示当前湿度/温度在设定范围内的相对位置。制作专业PCB如果你希望设备更小巧、更可靠可以将目前的万用板电路设计成一块定制PCB。使用KiCad或EasyEDA等工具把Feather M4的芯片、SI7021、TPL5111、蜂鸣器驱动等全部集成在一块板子上只留下屏幕、电池和传感器作为外接模块。这能极大提升项目的完成度和专业性。这个基于Feather M4和电子墨水屏的温湿度监控器从一个具体的需求出发串联了电源管理、传感器应用、低功耗显示和嵌入式Python编程等多个知识点。它不仅仅是一个可以用的工具更是一个绝佳的学习平台。希望这份详细的拆解能帮助你不仅成功复现它更能理解其背后的设计思想并激发出属于自己的创意和改进。