基于CircuitPython与Matrix Portal打造可定制网络信息滚动显示器
1. 项目概述:打造你的网络信息滚动看板
如果你和我一样,喜欢在工作室或桌面上放一个能实时显示信息的“小玩意儿”,比如最新的技术文章标题、天气状况,甚至是股票价格,那么这个项目绝对会让你兴奋。今天我们要聊的,就是如何用一块Adafruit Matrix Portal开发板和CircuitPython,亲手打造一个能联网、能自动更新内容的滚动信息显示器。
简单来说,这个东西的核心就是一个“信息抓取器+显示屏”。它内置了Wi-Fi模块,可以定时(比如每小时)去网上指定的地方(我们称之为API接口)抓取最新的数据,然后把数据解析出来,以滚动字幕的形式显示在一块炫酷的RGB LED点阵屏上。你不再需要手动更新内容,一切交给网络和代码自动完成。
我选择Adafruit Matrix Portal作为硬件平台,原因很简单:它把最麻烦的部分都集成好了。它本质上是一块基于ATSAMD51微控制器、并板载了ESP32 Wi-Fi协处理器的开发板,专门为驱动HUB75接口的RGB LED矩阵屏设计。这意味着你不用再为如何连接屏幕、如何驱动Wi-Fi而头疼,只需要关注最核心的逻辑——写Python代码去获取和处理数据。CircuitPython则让写代码这件事变得像在电脑上操作U盘一样简单:把代码文件拖到开发板的存储盘里,它就能立刻运行。
这个项目的价值远不止显示Adafruit的教程更新。它为你提供了一个可高度定制化的物联网信息显示终端模板。一旦你理解了从网络获取数据到屏幕显示的这个完整流程,你就可以轻松地替换数据源。无论是从新闻网站抓取头条,从气象API获取天气,还是从你自建的服务器拉取待办事项,原理都是相通的。接下来,我会带你从硬件组装、环境配置,到代码编写、原理剖析,一步步完成这个项目,并分享我在实践中踩过的坑和总结的技巧。
2. 核心硬件选型与组装要点
工欲善其事,必先利其器。硬件是项目的基石,选对并装好,能避免后续很多莫名其妙的麻烦。
2.1 硬件清单深度解析
根据项目指南,核心部件如下:
- Adafruit Matrix Portal M4:主控板。它是大脑,负责运行CircuitPython程序、处理数据并通过HUB75接口控制屏幕。其集成的ESP32 Wi-Fi模块是关键。
- 64x32 RGB LED矩阵屏:输出设备。有多个版本可选,区别主要在于像素间距(Pitch),比如3mm、4mm、5mm、6mm,还有柔性版本。
- 如何选择?这取决于你的观看距离和安装环境。像素间距越大,单个LED之间的距离越大,在相同分辨率(64x32)下,整块屏幕的物理尺寸就越大。例如,5mm间距的屏幕会比3mm的大不少。如果你打算放在书桌近处观看,3mm或4mm的细腻度更好;如果想挂在墙上远距离观看,5mm或6mm的会更醒目。柔性屏则适合需要弯曲或贴在非平面上的创意场景。
- 5V电源:至关重要!LED矩阵屏功耗不低,尤其在显示白色或高亮度内容时。必须使用5V、至少2.5A(推荐3A或以上)的电源。USB线供电通常无法满足持续电流需求,会导致屏幕闪烁、颜色异常甚至主板重启。官方推荐的Raspberry Pi 5.1V 3A电源是个稳妥的选择。
- USB数据线:用于给主板供电和编程。务必使用一条能传输数据的USB-C线,很多手机充电线只有供电功能,会导致电脑无法识别设备。
- (可选)LED柔光亚克力板:对于4mm及以下间距的屏幕,强烈建议加装。它能有效混合相邻LED的光点,让显示的文字和图形边缘更柔和,减少“颗粒感”,在室内灯光下也能显著降低反光,提升视觉质感。
- (可选)支架或固定件:如可调节金属丝支架、3D打印边框或强力磁铁脚,用于摆放或悬挂成品。
注意:电源是头号“杀手”。我曾因使用一个标称5V 2A的劣质电源,导致屏幕在滚动显示白色文字时随机出现横条纹和复位。更换为足额3A电源后问题立刻消失。LED屏的峰值电流可能很高,电源的余量一定要留足。
2.2 硬件组装步骤与避坑指南
组装过程看似简单,但有几个细节一疏忽就会导致失败。
1. 准备MatrixPortal供电端子这是第一个坑。MatrixPortal板子上有两个带保护膜的铜柱(Standoffs),用于给LED屏幕供电。你必须用镊子或指甲小心翼翼地揭掉铜柱顶部的橙色保护贴膜。如果不揭掉,螺丝端子(Spade Connectors)就无法与铜柱良好接触,屏幕将无法通电。我遇到过新手朋友因为没撕膜,折腾半天以为是屏幕坏了。
2. 连接电源线与屏幕将配套的红色(+5V)和黑色(GND)导线用螺丝固定在对应的铜柱上。然后,将四芯电源插头连接到LED矩阵屏的电源接口。这里有个防呆设计,插头只有一个方向能插入,对准板子上的丝印标识即可。
3. 连接MatrixPortal与屏幕将MatrixPortal板子插入屏幕左侧的16针(8x2)排母插座。方向至关重要:确保屏幕上印刷的白色箭头指向右上方(当你面对屏幕正面时)。同时,MatrixPortal板子应该略微伸出屏幕边缘,这样你才能从正面按到板子上的复位按钮。如果插反了,不仅无法工作,还可能损坏设备。如果屏幕PCB上有塑料凸起妨碍板子插到底,可以用斜口钳小心剪掉。
4. (可选)安装柔光亚克力板如果你购买了亚克力板,测量并切割至与屏幕外框匹配的尺寸。使用带细齿的锯子(如线锯或台锯)切割效果最好。不建议尝试用美工刀划痕后掰断,极易导致边缘不平整或整块板子开裂。粘贴时,推荐使用“Uglu Dashes”这类透明强力双面胶点,在亚克力板四角和长边中点各贴一个,然后将亚克力板(磨砂面朝外)对准屏幕按压20秒即可。它粘得牢,日后如需更换也相对容易移除。
3. 软件环境搭建与网络配置
硬件就绪后,我们就要给它注入灵魂——软件。整个过程就像给一台新电脑安装操作系统和配置网络。
3.1 安装CircuitPython固件
CircuitPython是MicroPython的一个分支,由Adafruit优化,特别适合教育和快速原型开发。它的最大优点是“所见即所得”的文件系统编程。
- 下载固件:访问 circuitpython.org ,搜索“Matrix Portal M4”,下载最新的
.uf2固件文件。 - 进入引导加载模式:用USB线连接MatrixPortal和电脑。快速双击板子上的复位按钮(RESET)。此时,板载的NeoPixel RGB LED会变成绿色,电脑上会出现一个名为
MATRIXBOOT的U盘。如果LED变红,说明进入引导模式失败,检查USB线或换一个USB端口再试。 - 刷入固件:将下载好的
adafruit_circuitpython_*.uf2文件拖入MATRIXBOOT盘。LED会闪烁,MATRIXBOOT盘会自动弹出,随后出现一个名为CIRCUITPY的新盘符。至此,CircuitPython系统安装完成。
3.2 配置Wi-Fi连接:settings.toml文件的奥秘
在物联网项目中,如何安全地管理Wi-Fi密码和API密钥是个问题。CircuitPython 8之后引入了settings.toml文件,它类似于一个环境变量配置文件,将敏感信息与主程序代码分离。
- 创建
settings.toml文件:在CIRCUITPY盘的根目录下,用任何文本编辑器(如VS Code、Mu编辑器、甚至记事本)新建一个文件,命名为settings.toml(注意扩展名)。 - 编辑内容:对于本项目,我们需要配置Wi-Fi和Adafruit IO(用于获取网络时间)。文件内容如下:
CIRCUITPY_WIFI_SSID = "你的Wi-Fi名称" CIRCUITPY_WIFI_PASSWORD = "你的Wi-Fi密码" AIO_USERNAME = "你的Adafruit IO用户名" AIO_KEY = "你的Adafruit IO Active Key"关键细节与避坑点:
- 变量名必须准确:
CIRCUITPY_WIFI_SSID和CIRCUITPY_WIFI_PASSWORD是CircuitPython网络库识别Wi-Fi凭证的固定键名,不能写错。Adafruit IO的用户名和密钥的键名则可能因库版本而异,本项目使用的是AIO_USERNAME和AIO_KEY。务必检查代码中os.getenv()函数读取的键名是否与此处一致,这是网络连接失败的最常见原因之一。 - 字符串与引号:所有值都必须用双引号包裹。
- 字符编码:保存文件时,确保编码为UTF-8 without BOM。在某些Windows编辑器中(如旧版记事本),默认保存为带BOM的UTF-8,可能导致CircuitPython无法正确解析。
- 文件位置:必须放在
CIRCUITPY盘的根目录,不能放在任何文件夹里。
实操心得:管理多个项目的秘密。如果你有多个不同的CircuitPython项目,每个都需要不同的API密钥,频繁修改根目录的
settings.toml很麻烦。我的技巧是:在电脑上为每个项目维护一个独立的settings.toml文件,部署时再复制到开发板。或者,在代码中尝试读取多个可能的键名,并给出清晰的错误提示。
3.3 安装必要的库文件
CircuitPython通过lib文件夹来管理第三方库。我们需要下载本项目所需的库文件包。
- 下载项目包:在Adafruit学习系统的项目页面,找到“Download Project Bundle”按钮并点击。这会下载一个包含所有必需库和主程序
code.py的ZIP文件。 - 解压并部署:解压ZIP文件,进入解压后的文件夹,你会看到
lib文件夹和code.py文件。将lib文件夹整体拖入CIRCUITPY盘。如果盘中已存在lib文件夹,则合并内容。同时,将项目专用的字体文件(如IBMPlexMono-Medium-24_jep.bdf)也复制到CIRCUITPY根目录。 - 库的作用:
adafruit_esp32spi/adafruit_requests:用于ESP32 Wi-Fi连接和HTTP请求。adafruit_display_text/adafruit_bitmap_font:用于在矩阵屏上显示文本和加载字体。adafruit_portalbase:提供了一些与MatrixPortal硬件交互的便利功能。
4. 代码深度解析与自定义改造
现在,我们进入最核心的部分——理解并驾驭代码。我将以项目原始的“新教程滚动显示器”代码为蓝本,拆解其逻辑,并告诉你如何将其改造成显示任意REST API数据的通用工具。
4.1 主程序逻辑框架拆解
项目的code.py主要遵循以下流程,这是一个典型的物联网应用循环:
graph TD A[上电启动] --> B[初始化硬件<br>屏幕、Wi-Fi]; B --> C[连接Wi-Fi网络]; C --> D[从Adafruit IO获取网络时间]; D --> E{是否到达<br>数据更新周期?}; E -- 是 --> F[向目标API发送HTTP GET请求]; E -- 否 --> G[继续滚动显示当前内容]; F --> H[接收并解析JSON响应]; H --> I[提取所需字段<br>如文章标题]; I --> J[格式化文本<br>准备显示]; J --> K[更新显示缓冲区<br>开始滚动]; K --> L[等待短暂时间]; L --> E;这个循环确保了设备在无人值守的情况下,能定期获取新数据并更新显示。
4.2 关键代码段剖析与自定义
让我们深入几个关键函数,看看如何动手修改。
1. 网络连接与WiFiManager原始代码使用了WiFiManager类,它比简单的连接更健壮,能自动处理网络断开重连。
import wifi import socketpool import adafruit_requests from adafruit_portalbase import WiFiManager # 从 settings.toml 读取配置 ssid = os.getenv("CIRCUITPY_WIFI_SSID") psk = os.getenv("CIRCUITPY_WIFI_PASSWORD") # 创建WiFi管理器,并指定状态LED(这里用板载NeoPixel) status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2) wifi_manager = WiFiManager( socket_pool, esp, ssid, psk, status_pixel=status_pixel ) # 尝试连接,如果失败会闪烁LED并重试 wifi_manager.connect()如何自定义:如果你的网络环境复杂(如需要企业认证),可能需要更复杂的连接逻辑。WiFiManager的connect()方法在底层调用了esp.connect_AP()。你可以查看其源码,在异常处理部分加入自己的逻辑,比如记录错误到文件。
2. 获取与解析API数据这是项目的核心。代码定期从一个URL获取JSON数据。
DATA_SOURCE = "https://learn.adafruit.com/api/guides?format=json" def update_data(): try: print("Fetching data from", DATA_SOURCE) response = wifi_manager.get(DATA_SOURCE) # 使用WiFiManager的get方法 json_data = response.json() # 假设API返回一个包含“guides”列表的JSON guides = json_data.get("guides", []) titles = [guide.get("title", "No Title") for guide in guides[:5]] # 取前5个标题 return titles except Exception as e: print("Failed to get data:", e) return ["Network Error"] # 返回错误信息如何自定义:
- 更换数据源:将
DATA_SOURCE替换成任何返回JSON的公开API地址。例如,天气API(api.openweathermap.org)、新闻API(newsapi.org)。 - 解析不同结构:你需要根据新API返回的JSON结构修改解析代码。使用
json_data.get(‘key’)方法比直接使用json_data[‘key’]更安全,因为它能在键不存在时返回None或默认值,避免程序崩溃。 - 添加请求参数:许多API需要查询参数或API密钥。你可以这样构造请求:
API_KEY = os.getenv(“MY_API_KEY”) headers = {“X-API-Key”: API_KEY} response = wifi_manager.get(DATA_SOURCE, headers=headers) - 错误处理:网络请求可能因各种原因失败。务必像示例中一样使用
try…except包裹,并设置一个友好的回退显示内容,如“更新中…”或上次成功获取的数据。
3. 在矩阵屏上显示文本在低分辨率的LED屏上显示美观的文字需要一些技巧。
import displayio from adafruit_display_text import bitmap_label from adafruit_bitmap_font import bitmap_font # 创建显示组 group = displayio.Group() # 加载自定义字体(比默认字体好看) font = bitmap_font.load_font(“/IBMPlexMono-Medium-24_jep.bdf”) # 创建文本标签 text_area = bitmap_label.Label(font, text=“Loading…”, color=0xFFFFFF) text_area.x = display.width # 初始位置放在屏幕右侧 text_area.y = display.height // 2 group.append(text_area) display.show(group) # 滚动动画 def scroll_text(text): text_area.text = text text_area.x = display.width # 重置到右侧 while text_area.x > -text_area.bounding_box[2]: # 直到文字完全滚出左侧 text_area.x -= 1 time.sleep(0.02) # 控制滚动速度如何自定义:
- 字体:你可以使用任何
.bdf格式的位图字体。Adafruit提供了转换工具,可以将电脑上的TTF字体转换为.bdf。字号要匹配你的屏幕分辨率,64像素高的屏幕,24-32点的字体通常比较合适。 - 颜色:RGB LED可以显示任何颜色。
0xFFFFFF是白色,0xFF0000是红色,0x00FF00是绿色,0x0000FF是蓝色。你可以根据内容类型改变颜色(比如错误信息用红色,天气温度用蓝色或红色)。 - 滚动效果:上面的简单滚动逻辑会阻塞主循环。更优雅的做法是利用
time.monotonic()进行非阻塞动画,或者使用adafruit_display_text的scrolling_label模块(如果可用)。 - 多行显示:对于64x32的屏幕,通常只显示一行滚动文本。如果你有更高(如64x64)的屏幕,可以创建多个
Label对象,分别设置不同的y坐标来显示多行信息。
4.3 改造实例:创建一个天气预报显示器
假设我们想让它显示本地天气。我们以OpenWeatherMap的免费API为例。
修改
settings.toml:CIRCUITPY_WIFI_SSID = “your_wifi” CIRCUITPY_WIFI_PASSWORD = “your_password” OPENWEATHER_API_KEY = “your_32_char_api_key” CITY = “London,UK”修改数据获取函数:
import os OPENWEATHER_API_KEY = os.getenv(“OPENWEATHER_API_KEY”) CITY = os.getenv(“CITY”, “London,UK”) # 提供默认值 WEATHER_URL = f“http://api.openweathermap.org/data/2.5/weather?q={CITY}&appid={OPENWEATHER_API_KEY}&units=metric” def update_weather(): try: response = wifi_manager.get(WEATHER_URL) data = response.json() temp = data[‘main’][‘temp’] desc = data[‘weather’][0][‘description’] city = data[‘name’] # 格式化显示文本 return f“{city}: {temp:.1f}C, {desc}” except Exception as e: print(“Weather fetch failed:”, e) return “Weather Unavailable”调整主循环:将主循环中调用
update_data()的地方改为调用update_weather(),并可以设置不同的更新间隔(天气数据不需要像新闻那样频繁更新,每10-30分钟一次即可)。
通过这样的改造,你就拥有了一个完全个性化的网络信息显示器。关键在于理解数据流:发起请求 -> 接收响应 -> 解析JSON -> 提取字段 -> 格式化字符串 -> 输出到屏幕。
5. 常见问题排查与性能优化
即使按照步骤操作,也可能会遇到问题。这里我总结了一些常见故障和解决方法。
5.1 连接与网络问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| Wi-Fi无法连接 | 1.settings.toml配置错误2. 信号太弱 3. 网络需要网页认证(如酒店网络) | 1. 检查settings.toml文件名、路径、键名拼写、引号。2. 通过串口终端打印 ssid和password变量确认已读取。3. 将设备靠近路由器。 4. CircuitPython标准库不支持网页认证,需使用能跳过认证的网络。 |
| 能连Wi-Fi但无法访问互联网 | 1. DNS问题 2. 防火墙/网络策略限制 | 1. 在代码中尝试ping一个IP地址(如8.8.8.8)代替域名,判断是DNS还是路由问题。2. 检查路由器是否对IoT设备有访问限制。 |
| API请求返回错误(如403,404) | 1. API URL错误 2. API密钥无效或过期 3. 请求频率超限 | 1. 在电脑浏览器中测试API URL和密钥是否正确。 2. 检查API服务商对免费密钥的调用频率限制。 |
| 偶尔网络超时或断开 | 1. 网络不稳定 2. ESP32协处理器进入错误状态 | 1. 使用WiFiManager,它内置了重连逻辑。2. 在代码中捕获异常,并加入延时重试机制。 3. 极端情况下,可以添加一个看门狗,在长时间无响应时软重启ESP32 ( esp.reset())。 |
串口终端是救命稻草:务必学会使用串口监视器(如Mu编辑器内置的、Arduino IDE的串口监视器或Putty)。将print()语句 strategically地插入代码中(如连接前、请求前后、解析数据后),所有运行状态和错误信息都会输出到这里,是诊断问题的唯一途径。
5.2 显示与性能问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 屏幕闪烁、花屏、乱码 | 1.电源不足!(最常见) 2. 数据线接触不良 3. 刷新率设置不当 | 1.立即检查电源!确保使用5V 3A以上电源,且MicroUSB/USB-C线能承载大电流。 2. 重新插紧MatrixPortal与屏幕之间的排线。 3. 在初始化显示对象时,尝试降低亮度。 |
| 文字显示不全或乱码 | 1. 字体文件未正确加载或损坏 2. 文本编码问题 | 1. 确认字体文件(.bdf)已放入CIRCUITPY根目录,路径正确。2. 尝试使用默认的 terminalio.FONT看是否正常。3. 确保显示的文本字符串是纯英文或已正确处理的Unicode字符。CircuitPython对非ASCII字符支持有限。 |
| 滚动动画卡顿、不流畅 | 1. 主循环中有耗时操作(如网络请求)阻塞了动画 2. 代码效率低 | 1.将网络请求与动画循环分离。使用状态机和time.monotonic()进行非阻塞定时。例如,记录上次请求时间,每3600秒(1小时)才执行一次请求,其余时间专心做动画。2. 避免在动画循环中进行复杂的字符串处理或文件操作。 |
| 内存不足错误(MemoryError) | 1. 加载了过大的字体或图像 2. 网络响应数据过大 3. 内存泄漏 | 1. 使用更小的字体文件。 2. 解析API响应时,只提取需要的数据字段,不要将整个巨大的JSON字符串保存在变量中。 3. 确保及时关闭网络响应( response.close())或使用with requests.get() as response:上下文管理器。 |
5.3 高级优化与扩展思路
当项目稳定运行后,你可以考虑以下优化和扩展:
- 降低功耗:如果你希望用电池供电,可以:
- 在无网络活动时,降低屏幕亮度或间歇性关闭屏幕。
- 使用ESP32的深度睡眠模式,但MatrixPortal的硬件设计可能使其复杂化。更简单的方法是让主循环在每次数据显示后,用
time.sleep()休眠较长时间。
- 增加交互性:利用MatrixPortal板载的按钮(A和B)。你可以编程定义短按切换显示内容(如天气/新闻),长按进入配置模式等。
- 本地缓存:为了防止网络中断时屏幕空白,可以在文件系统中创建一个简单的缓存。每次成功获取新数据后,将其写入一个
cache.txt文件。下次启动或网络失败时,从缓存文件读取旧数据显示。 - 使用更健壮的时间同步:原项目依赖Adafruit IO获取时间。你可以添加备选方案,例如从公开的NTP服务器同步,或者使用一个外置的RTC(实时时钟)模块,这样即使在断网时也能保持时间准确。
- 制作外壳与最终部署:为你的显示器设计一个3D打印外壳或找一个合适的相框,将亚克力板、屏幕和主板固定其中,隐藏走线,让它从一个开发原型变成一个精致的桌面摆件或墙饰。
这个项目的魅力在于,它从一个具体的例子出发,却为你打开了一扇通往个性化物联网设备的大门。从硬件连接、软件配置到代码编写和调试,你完整地走通了一个嵌入式网络应用的全流程。最让我有成就感的一刻,不是代码第一次运行成功,而是当我随意修改了几行代码,它就按照我的意愿,开始显示我真正关心的信息时。希望你的显示器也能早日展示出独一无二的内容。
