基于ESP32-S2与CircuitPython的宠物喂食提醒时钟制作指南
1. 项目概述你是否也经历过这样的场景早上匆匆忙忙出门晚上回家面对喵星人幽怨的眼神心里直打鼓——“我早上到底喂猫了没”或者家里不止一个人负责喂食结果谁也没记清上一次投喂是什么时候要么让宠物饿着要么一不小心就喂多了。对于室内宠物来说食物不仅是生存所需更是它们一天中最重要的娱乐和期待一个清晰的喂食记录对维持它们的健康体重和你的家庭和谐都至关重要。今天我们就来动手制作一个能彻底解决这个问题的智能小工具基于ESP32-S2与CircuitPython的宠物喂食提醒时钟。它不仅仅是一个时钟更是一个贴在冰箱上或放在猫粮旁的智能记录板。核心功能很简单一块低功耗的E-Ink屏幕常年显示当前时间当你喂完宠物后只需按一下按钮它就会把当前的日期和时间比如“mon 08:30a”记录在屏幕上直到下一次记录被更新。这样无论是你本人还是家人看一眼就知道“哦猫今天早上8点半已经吃过了”。这个项目的核心硬件是Adafruit MagTag它集成了乐鑫ESP32-S2 Wi-Fi芯片和一块2.9英寸的灰度电子墨水屏。选择ESP32-S2是因为它提供了可靠的Wi-Fi连接和足够的处理能力而CircuitPython则让嵌入式开发变得像写Python脚本一样简单直观极大地降低了开发门槛。E-Ink屏幕的特性是仅在刷新时耗电显示静态内容时零功耗这使得设备可以常年插电显示无需担心功耗和屏幕老化问题。整个项目的价值在于它用一个非常具体、实用的场景串联起了物联网开发的几个核心环节硬件驱动、网络连接、云服务API调用和用户交互是一个绝佳的嵌入式物联网入门与实践项目。2. 核心硬件与工具选型解析2.1 为什么是Adafruit MagTag在开始动手前我们得先搞清楚手头的“兵器”。Adafruit MagTag是一个高度集成的开发套件它为我们这个项目省去了大量硬件连线、电源管理和屏幕驱动的麻烦。主控芯片ESP32-S2这是乐鑫ESP32系列中专注于物联网应用的型号。相比于经典的ESP32S2版本移除了蓝牙功能但强化了USB OTG和支持更丰富的外设接口。对于本项目我们最看重的是其稳定的Wi-Fi连接能力和低功耗特性。它内置了丰富的外设如GPIO、I2C、SPI等足以驱动E-Ink屏幕和读取按钮状态。更重要的是它有充足的Flash存储空间来存放CircuitPython解释器、我们的程序代码以及必要的字体库。2.9英寸灰度E-Ink显示屏这是项目的“脸面”。电子墨水屏的原理是通过电场控制黑白颗粒的排列来显示图像一旦画面刷新完成即使断电图像也能永久保持。这带来了两大优势一是极低的功耗设备绝大部分时间处于“显示但不耗电”的状态只有每分钟刷新时间或按按钮记录时才消耗少量电能二是视觉体验舒适无反光、广视角像真正的纸张一样适合长时间观看。这块屏幕的分辨率是296x128像素足够清晰地显示大号时间数字和一行记录信息。集成设计与外围电路MagTag板载了4个贴片按键我们主要使用最左边的A键、一个RGB LED指示灯、一个蜂鸣器以及一个锂电池管理电路。它采用USB-C接口供电和编程背部有钕磁铁安装槽可以轻松吸附在冰箱等金属表面。这种开箱即用的设计让我们可以跳过原理图设计、PCB打样、焊接调试等一系列硬件开发步骤直接聚焦在软件逻辑和功能实现上。2.2 物料清单与备选方案原项目给出了明确的物料清单但对于国内的开发者我们完全可以寻找替代方案核心是抓住功能要点。必需核心部件Adafruit MagTag开发板这是项目基石。如果购买原版有困难可以寻找集成ESP32-S2和同类E-Ink屏幕如GDEY029T94的开发板。关键是要确认该板是否有完善的CircuitPython库支持。USB-C数据线必须是数据线而非仅能充电的线。很多便宜的USB-C线只有电源引脚无法进行数据传输这会导致电脑无法识别设备是新手最常见的坑。准备一条你确认过可以传输数据的USB-C线。5V/1A USB电源适配器由于屏幕每分钟刷新一次设备需要长期供电。一个普通的手机充电头5V/1A或更高电流完全足够。注意不要使用快充头的高电压档位如9V/12V确保输出是标准的5V。可选/增强部件磁吸脚垫如果你想把它贴在冰箱上磁吸脚垫是最优雅的方案。也可以使用3M双面胶或者干脆放在桌面上。外壳MagTag的PCB是裸露的长期使用可以考虑3D打印一个简单的外壳既能保护电路也能让外观更美观。工具准备电脑Windows macOS 或 Linux 均可。文本编辑器用于编写和修改Python代码及配置文件。推荐使用专为CircuitPython设计的Mu Editor它集成了串行监视器非常方便。当然VS Code、Sublime Text甚至系统自带的记事本需注意编码保存为UTF-8也完全可以。网络环境设备需要连接2.4GHz Wi-Fi网络以同步时间。请确保你的路由器开启了2.4GHz频段并准备好Wi-Fi名称SSID和密码。3. CircuitPython环境搭建与网络配置3.1 为MagTag安装CircuitPythonCircuitPython是Adafruit主导开发的一款基于MicroPython的嵌入式编程语言其最大特点是将开发板模拟成一个U盘CIRCUITPY你可以直接像编辑文本文件一样修改代码保存后自动运行极大地简化了开发流程。第一步获取固件访问CircuitPython官方网站找到MagTag的下载页面。这里有一个关键细节Adafruit在2025年更新了MagTag硬件版本。如果你的MagTag正面是黑色PCB那么它是2025版必须下载10.x.x或更高版本的CircuitPython固件.bin和.uf2文件。如果是早期的白色PCB版本则可以兼容9.x.x版本。保险起见建议都下载最新稳定版。第二步安装固件推荐UF2方式对于大多数用户特别是2025版MagTag使用UF2引导程序是最简单的方法。用USB-C数据线连接MagTag和电脑。快速双击MagTag板上的Reset按钮位于USB-C口旁边。如果成功电脑上会出现一个名为MAGTAGBOOT的U盘。将下载好的.uf2文件直接拖入MAGTAGBOOT盘。复制完成后设备会自动重启。重启后电脑上会出现一个新的U盘名为CIRCUITPY。这表明CircuitPython已成功安装。注意在Windows系统下复制UF2文件时可能会弹出“错误 0x800701B1指定不存在的设备”的警告。这通常是UF2引导程序在烧录完成后立即断开连接而Windows还没反应过来导致的。你可以直接忽略这个错误只要CIRCUITPY盘成功出现就说明安装无误。如果觉得烦人可以按照Adafruit指南更新一下板子的TinyUF2引导程序。备用方案使用esptool或Web烧录工具如果双击Reset键无法进入UF2模式多见于早期版本则需要使用esptool这个命令行工具通过ESP32-S2自带的ROM引导程序来烧录.bin文件。这需要你在电脑上安装Python和esptool。或者更简单的方法是使用Chrome浏览器的Web Serial ESPTool这是一个网页版的烧录工具无需安装任何软件通过浏览器即可完成固件烧录非常适合环境配置有困难的情况。3.2 配置Wi-Fi连接与网络测试CircuitPython连接网络需要一个配置文件来存放你的Wi-Fi凭证而不是把密码硬编码在代码里。这是一个好习惯既能保护隐私也方便分享代码。创建settings.toml文件 在CIRCUITPY盘的根目录下用文本编辑器创建一个新文件命名为settings.toml注意全小写扩展名是.toml。然后输入以下内容CIRCUITPY_WIFI_SSID 你的Wi-Fi名称 CIRCUITPY_WIFI_PASSWORD 你的Wi-Fi密码进行网络连接测试 为了验证网络是否通畅我们需要运行一个测试脚本。你可以从Adafruit的教程页面下载“项目包”其中包含测试代码和必要的库文件。解压后将lib文件夹里面是CircuitPython库和code.py文件复制到CIRCUITPY盘的根目录覆盖原有文件。 这个测试代码code.py会执行以下操作扫描并列出周围的Wi-Fi网络。使用settings.toml中的凭证连接到你指定的网络。获取并打印本机的IP地址。尝试Ping谷歌的DNS服务器8.8.8.8来测试网络连通性。分别从几个测试网址获取文本和JSON数据验证HTTP/HTTPS请求功能。打开Mu Editor或其他串口监视工具如screen或putty连接到MagTag的串行端口波特率通常为115200你就能看到详细的连接日志。如果看到“Connected to XXX!”和你的IP地址并且成功获取了网页内容恭喜你网络层已就绪。实操心得网络测试失败最常见的原因有三个一是settings.toml文件格式错误如用了中文引号、丢了等号务必确保是纯英文标点二是Wi-Fi密码错误三是路由器设置了MAC地址过滤或仅允许5GHz频段。请逐一排查。4. 获取网络时间与Adafruit IO服务集成一个能用的时钟核心是准确的时间。让嵌入式设备自己维护实时时钟RTC精度不够且无法处理时区和夏令时。因此我们需要从网络获取时间。4.1 为什么选择Adafruit IO Time理论上我们可以让设备连接任何公共NTP网络时间协议服务器。但这里有几个现实问题第一许多公共NTP服务器有访问频率限制不适合大量设备频繁请求第二从NTP时间戳UTC转换到本地时间需要考虑复杂的时区规则和夏令时这在单片机上实现起来比较繁琐且容易出错。Adafruit IO Time服务完美地解决了这些问题。它作为一个中间层你只需要向其发送一个带有你时区信息的HTTP请求它就会返回已经格式化好的本地时间字符串。你无需在设备端进行任何时区计算。这项服务对拥有免费Adafruit账户的用户开放其请求限制对个人项目来说完全足够。配置Adafruit IO凭证访问io.adafruit.com用你的Adafruit账户登录没有的话需免费注册。登录后点击页面右上角的“My Key”。在弹出的窗口中你会看到你的Username和Active Key。这个Key就是访问API的令牌。再次编辑CIRCUITPY盘根目录下的settings.toml文件添加以下三行CIRCUITPY_WIFI_SSID 你的Wi-Fi名称 CIRCUITPY_WIFI_PASSWORD 你的Wi-Fi密码 ADAFRUIT_AIO_USERNAME 你的Adafruit IO用户名 ADAFRUIT_AIO_KEY 你的Active Key TIMEZONE Asia/Shanghai # 例如上海时区时区字符串必须遵循IANA时区数据库的格式。你可以去worldtimeapi.org/timezones查找你所在城市或区域的正确字符串例如America/New_York,Europe/London,Asia/Tokyo等。4.2 时间同步代码解析配置好后我们可以写一段简单的测试代码来验证时间服务。以下代码展示了如何从Adafruit IO获取时间import os import wifi import socketpool import adafruit_requests import ssl # 从 settings.toml 读取配置 ssid os.getenv(CIRCUITPY_WIFI_SSID) password os.getenv(CIRCUITPY_WIFI_PASSWORD) aio_username os.getenv(ADAFRUIT_AIO_USERNAME) aio_key os.getenv(ADAFRUIT_AIO_KEY) timezone os.getenv(TIMEZONE, America/New_York) # 提供默认值 # 构建请求URL。关键参数x-aio-key用于认证tz指定时区fmt定义返回时间格式 TIME_URL fhttps://io.adafruit.com/api/v2/{aio_username}/integrations/time/strftime TIME_URL f?x-aio-key{aio_key}tz{timezone} TIME_URL fmt%25Y-%25m-%25d%25H%3A%25M%3A%25S.%25L%25j%25u%25z%25Z # 连接Wi-Fi wifi.radio.connect(ssid, password) # 创建网络会话 pool socketpool.SocketPool(wifi.radio) requests adafruit_requests.Session(pool, ssl.create_default_context()) # 发送请求并打印结果 print(Fetching time from Adafruit IO...) response requests.get(TIME_URL) print(Time received:, response.text)这段代码会返回一个类似2025-12-18 14:30:15.123 352 4 0800 CST的字符串包含了年月日、时分秒毫秒、一年中的第几天、星期几、时区偏移和时区名称。我们的宠物喂食时钟项目正是利用了这项服务来保证时间的准确性。注意事项settings.toml文件包含了你的Wi-Fi密码和Adafruit IO密钥切勿将此文件上传到GitHub等公开代码仓库。在分享项目时应该提供一个settings.toml.example文件作为模板让他人填入自己的信息。5. 宠物喂食提醒时钟代码实现详解现在我们将网络时间、E-Ink显示和按钮交互组合起来构建完整的应用程序。5.1 项目文件结构与库依赖首先我们需要准备好项目运行所需的所有文件。从Adafruit的项目页面下载“Project Bundle”解压后你会看到类似如下的文件结构Magtag_Cat_Fed_Clock/ ├── code.py # 主程序 ├── background.bmp # E-Ink屏幕背景图猫和餐具图标 ├── Lato-Regular-74.bdf # 显示主要时间的大字体 ├── BebasNeueRegular-41.bdf # 显示记录时间的小字体 └── lib/ # 依赖库目录 ├── adafruit_magtag/ ├── adafruit_display_text/ ├── adafruit_bitmap_font/ └── ... (其他必要库)你需要将整个lib文件夹和code.py、background.bmp以及两个.bdf字体文件全部复制到CIRCUITPY盘的根目录。lib文件夹里的库提供了驱动MagTag硬件、显示文本和图形所需的所有功能。5.2 主程序代码深度剖析让我们逐段分析code.py理解其工作原理。初始化与全局变量import time from adafruit_magtag.magtag import MagTag USE_AMPM_TIME True # True为12小时制上午/下午False为24小时制 weekdays (mon, tue, wed, thur, fri, sat, sun) last_sync None # 记录上一次网络同步时间的时间戳 last_minute None # 记录上一次更新显示时的“分钟”数 magtag MagTag() # 初始化MagTag对象这是控制所有硬件功能的核心USE_AMPM_TIME变量让你轻松切换12/24小时制。weekdays元组对应周一至周日。last_sync和last_minute是两个重要的状态标志用于控制同步和更新的频率避免不必要的网络请求和屏幕刷新E-Ink刷新较慢且耗电略高。图形界面设置magtag.graphics.set_background(/background.bmp) mid_x magtag.graphics.display.width // 2 - 1 # 创建主时间显示文本区域大字体 magtag.add_text( text_fontLato-Regular-74.bdf, text_position(mid_x, 10), text_anchor_point(0.5, 0), # 锚点在文本水平中点、顶部 is_dataFalse, ) magtag.set_text(00:00a, auto_refreshFalse) # 设置初始文本不自动刷新屏幕 # 创建喂食记录显示文本区域小字体 magtag.add_text( text_font/BebasNeueRegular-41.bdf, text_position(126, 86), text_anchor_point(0, 0), # 锚点在文本左上角 is_dataFalse, ) magtag.set_text(DAY 00:00a, index1, auto_refreshFalse)这部分代码构建了显示界面。set_background加载背景图片。add_text创建了两个文本显示区域“标签”第一个索引0用于显示当前时间居中靠上字体很大第二个索引1用于显示喂食记录位于屏幕下方偏左。auto_refreshFalse意味着设置文字时不会立刻刷新屏幕我们将集中控制刷新时机以优化性能。时间格式化函数def hh_mm(time_struct, twelve_hourTrue): 将time.struct_time格式化为HH:MMa/p或HH:MM字符串 postfix if twelve_hour: if time_struct.tm_hour 12: hour_string str(time_struct.tm_hour - 12) # 13-23 - 1-11 (下午) postfix p elif time_struct.tm_hour 0: hour_string str(time_struct.tm_hour) # 1-12 postfix a else: # time_struct.tm_hour 0 hour_string 12 # 0点 - 12点 (上午) postfix a else: # 24小时制 hour_string {hh:02d}.format(hhtime_struct.tm_hour) return hour_string :{mm:02d}.format(mmtime_struct.tm_min) postfix这个函数是显示逻辑的核心。它接收一个time.localtime()返回的结构体然后根据twelve_hour参数将其格式化为像“8:30a”或“20:30”这样的字符串。注意处理0点午夜和12点中午的特殊情况这在12小时制下容易出错。主循环逻辑 主循环while True是程序的心脏它持续运行负责三件事同步时间、更新时钟显示、检测按钮按下。while True: # 1. 时间同步启动时或每隔3600秒1小时同步一次 if not last_sync or (time.monotonic() - last_sync) 3600: magtag.network.get_local_time() # 调用Adafruit IO服务同步时间 last_sync time.monotonic() # 更新同步时间戳 # 2. 获取当前时间并检查分钟是否变化 now time.localtime() # 获取已同步的本地时间 if not last_minute or (last_minute ! now.tm_min): # 分钟数变了更新主时钟显示 magtag.set_text(hh_mm(now, USE_AMPM_TIME), index0) last_minute now.tm_min # 记录当前分钟数 # 3. 检测A按钮是否被按下喂食记录 if magtag.peripherals.button_a_pressed: # 组合字符串例如”mon 08:30a“ out weekdays[now.tm_wday] hh_mm(now, USE_AMPM_TIME) magtag.set_text(out, index1) # 更新第二个文本标签喂食记录 while magtag.peripherals.button_a_pressed: # 等待按钮释放防止误触 pass逻辑精要低频同步网络同步每小时进行一次3600秒既保证了时间的长期准确性又避免了频繁网络请求带来的功耗和服务器压力。分钟级更新只有检测到分钟数发生变化now.tm_min时才更新主时间显示。这意味着屏幕每分钟只刷新一次完美契合E-Ink的低刷新率特性也节省了电量。事件驱动记录喂食记录完全由物理按钮A触发。按下时程序获取当前的星期和时刻格式化后更新到屏幕下方。while循环等待按钮释放这是一个简单的“消抖”和防重复触发处理。6. 高级优化、问题排查与扩展思路项目基本功能完成后我们可以思考如何让它更稳定、更省电或者增加新功能。6.1 功耗优化与电源管理虽然MagTag插电使用但了解其功耗特性对设计电池版本或其他物联网项目很有帮助。E-Ink刷新功耗屏幕刷新特别是全刷是耗电高峰。当前代码每分钟刷新一次是合理的。切勿在循环中无延迟地频繁刷新。Wi-Fi功耗magtag.network.get_local_time()内部会开启Wi-Fi并连接网络完成HTTP请求。每小时一次平均功耗很低。如果你想极致省电可以考虑在同步完成后调用wifi.radio.stop_station()断开Wi-Fi连接下次同步前再连接。但要注意重连Wi-Fi可能需要1-3秒会增加同步过程的耗时和不确定性。深度睡眠Deep Sleep对于电池供电场景可以使用ESP32-S2的深度睡眠模式。设定一个定时器例如每55分钟唤醒一次唤醒后连接Wi-Fi同步时间、更新屏幕然后立刻再次进入深度睡眠。这样功耗可以降到微安级别使设备依靠小容量电池运行数周甚至数月。这需要修改代码结构将主循环改为单次执行并在末尾调用microcontroller.deepsleep()。6.2 常见问题与排查指南在制作过程中你可能会遇到以下问题问题现象可能原因排查步骤与解决方案电脑无法识别CIRCUITPY盘1. USB线是充电线无数据功能。2. CircuitPython固件未正确安装。3. 驱动问题Windows。1.更换数据线这是最常见原因。2. 重新执行UF2烧录步骤确认.uf2文件被复制到MAGTAGBOOT盘。3. 在设备管理器中查看是否有未识别的设备尝试重新插拔或安装CP210x/CH340等USB转串口驱动。Wi-Fi连接失败1.settings.toml配置错误。2. Wi-Fi密码错误或网络不可用。3. 路由器设置了MAC过滤或仅5GHz。1. 检查settings.toml文件格式确保无中文字符变量名正确。2. 用手机确认Wi-Fi可连接密码正确。3. 查看串口输出确认扫描到的网络列表中有你的SSID信号强度RSSI是否过弱。时间同步失败1. Adafruit IO凭证错误。2. 网络连接不稳定。3. 时区字符串格式错误。1. 核对settings.toml中的ADAFRUIT_AIO_USERNAME和ADAFRUIT_AIO_KEY。2. 运行网络测试代码确认能访问外网。3. 检查TIMEZONE值确保是有效的IANA时区字符串。屏幕无显示或显示乱码1. 字体文件.bdf缺失或路径错误。2. 背景图片路径错误。3. 屏幕排线接触不良。1. 确认Lato-Regular-74.bdf和BebasNeueRegular-41.bdf文件在根目录。2. 确认background.bmp在根目录且代码中路径为/background.bmp。3. 轻轻按压MagTag主板与屏幕的连接器。按钮按下无反应1. 代码中按钮检测逻辑有误。2. 按钮物理损坏。1. 在串口监视器中添加print语句输出magtag.peripherals.button_a_pressed的值确认是否检测到按下。2. 尝试按下其他按钮B、C、D看代码是否能有不同响应需修改代码测试。6.3 功能扩展与创意改造这个项目是一个完美的起点你可以基于它进行各种改造多宠物/多事件记录目前只记录一个时间。你可以利用另外三个按钮B、C、D分别记录“喂狗”、“换水”、“吃药”等不同事件并在屏幕上分区域显示。历史记录与统计当前设计只显示最后一次记录。你可以利用MagTag的有限内存或外接SPI Flash将每次按按钮的时间戳保存下来。然后通过长按某个按钮在屏幕上循环显示最近几次的记录甚至计算平均喂食间隔。云端同步与远程查看将喂食记录通过Wi-Fi上传到Adafruit IO、Blynk或自建的MQTT服务器。这样你可以在手机App或网页上远程查看喂食历史甚至设置提醒。自动化集成结合智能插座当你按下喂食记录按钮时MagTag可以通过网络触发智能插座自动打开喂食器或饮水机泵实现“记录执行”的联动。美化与个性化使用图形库如adafruit_display_shapes在背景图上绘制更丰富的UI。或者更换background.bmp为你家宠物的照片打造完全个性化的提醒器。这个项目麻雀虽小五脏俱全。它带你走完了物联网设备开发的完整闭环从硬件选型、环境搭建、网络配置、服务调用到最终的用户交互逻辑实现。希望这个详细的制作指南不仅能帮你成功做出这个实用的喂食提醒时钟更能成为你踏入嵌入式物联网世界的一块坚实跳板。