1. CircuitPython入门:为什么选择它,以及它能为你带来什么
如果你对物联网、智能硬件或者DIY电子项目感兴趣,但又对传统嵌入式开发的复杂环境(比如安装编译器、配置烧录工具、处理复杂的C语言指针)感到头疼,那么CircuitPython可能就是为你量身定做的“敲门砖”。我第一次接触它,是在为一个快速原型项目寻找解决方案时,被其“即插即用”的特性所吸引。简单来说,CircuitPython让微控制器编程变得像在U盘里编辑文本文件一样简单。
它的核心价值在于极低的入门门槛和快速的迭代周期。你不需要安装任何复杂的IDE或工具链,只需要一块支持CircuitPython的开发板(如Adafruit的Feather系列、RP2040系列等)和一根USB数据线。当你将开发板连接到电脑时,它会显示为一个名为CIRCUITPY的U盘。你的所有代码、库文件都放在这个U盘里。编辑code.py文件,保存,程序就会自动重启并运行。这种“编辑-保存-运行”的循环,将开发反馈时间缩短到了秒级,对于学习和快速验证想法来说,效率是颠覆性的。
这尤其适合以下几类人:教育工作者和学生,可以绕过环境配置的坑,直接聚焦于编程逻辑和硬件交互;创客和艺术家,能够快速将创意转化为可交互的物理原型;有Python基础的开发者,希望将技能扩展到硬件领域,无需从头学习一套新的语法和开发模式。接下来,我将带你从零开始,完成一次完整的CircuitPython之旅,涵盖安装、编程、调试以及那些官方手册里不会细说的“踩坑”经验。
2. 硬件准备与安全须知:避开那些“毁板子”的坑
在兴奋地开始编程之前,我们必须先聊聊硬件安全。这是所有嵌入式开发的第一步,也是最容易付出昂贵代价的一步。根据我多年的经验,大部分硬件损坏都源于电源接错或电压超标。
2.1 正确供电:生命线不能接错
以常见的Adafruit Feather开发板为例,它通常有多个电源输入引脚:USB口、电池接口(BAT)、以及3.3V输出/输入引脚。这里有几个必须牢记的铁律:
绝对禁止向电池接口(BAT)接入7.4V或更高电压的RC电池。这是一个非常常见的错误。很多航模电池标称7.4V(2S锂电),其满电电压可达8.4V,远超大部分微控制器(如ESP32、nRF52840、RP2040)的电源管理芯片承受范围。接入的瞬间,轻则烧毁电源芯片,重则直接击穿主控,板子就彻底“变砖”了。电池接口设计初衷是接单节锂电(3.7V-4.2V)或通过板载稳压器降压的电源。
谨慎使用外部3.3V电源。理论上,你可以绕过板载稳压器,直接向
3V引脚提供精确的3.3V电源。但强烈不推荐这样做,原因有三:- 功能缺失:板载的使能引脚(
EN)可能失效,这意味着你无法通过软件控制板子的复位或断电。 - 供电不全:这种方式可能无法为
BAT或USB引脚相关的电路供电。有些Feather扩展板(Wings)会从这些引脚取电,导致外设无法工作。 - 风险未知:可能引发不可预料的逻辑电平冲突或电流倒灌,长期使用有损坏风险。
- 功能缺失:板载的使能引脚(
切勿从USB引脚反向灌入5V电源。
USB引脚是用于从电脑取电的。如果你外接一个5V电源接到USB和GND上,相当于在向电脑的USB口“反供电”。当你再把USB线插回电脑时,两个5V电源可能冲突,轻则导致开发板或电脑USB端口保护性关闭,重则可能损坏电脑的USB控制器。这绝对是一个需要避免的危险操作。
实操心得:对于绝大多数学习和原型开发场景,最安全、最推荐的方式就是只使用USB线供电。它稳定、安全,且能同时完成供电和编程通信两件事。只有在项目最终部署、需要移动供电时,才考虑通过
BAT引脚连接一个标称3.7V的锂电池。在连接任何外部电源前,请务必用万用表确认电压。
2.2 开发板选型与识别
CircuitPython支持众多开发板,核心是看主控芯片。常见的有:
- 基于RP2040的板子:如Raspberry Pi Pico、Adafruit Feather RP2040,性能强、价格低、外设丰富,是当前入门首选。
- 基于ESP32-S2/S3的板子:如Adafruit Feather ESP32-S2,自带Wi-Fi,适合物联网项目。
- 基于nRF52840的板子:如Adafruit Feather nRF52840,自带蓝牙低功耗(BLE)。
不同板子的LED配置也不同,这会影响你的第一个Blink程序:
- 大部分板子:有一颗单色(通常是红色)LED,连接到固定的
board.LED引脚。 - 特殊板子:如KB2040、QT Py、Trinkey系列,它们没有单色LED,而是有一颗RGB NeoPixel灯。此时,标准的LED闪烁程序需要改用NeoPixel库来控制。
3. 软件环境搭建:从固件烧录到编辑器选择
3.1 下载与安装CircuitPython固件
第一步是让开发板“学会”CircuitPython语言。这个过程叫“烧录固件”。
- 确定你的板子型号:去 circuitpython.org 官网,在列表中找到你手头开发板的精确型号。务必选对,不同板子的固件不通用。
- 下载
.uf2文件:点击对应板子的下载链接,你会得到一个后缀为.uf2的文件。这是一个特殊的镜像文件,可以被开发板的引导程序(bootloader)识别并写入。 - 进入Bootloader模式:
- 对于RP2040系板子(如Pico):这是最通用的方式。按住板子上标有
BOOT或BOOTSEL的按钮(不要松手),然后短暂按一下RESET按钮,最后再松开BOOT按钮。此时,电脑上会出现一个名为RPI-RP2的U盘。 - 通用方法:先不插USB线,按住
BOOT按钮,然后插入USB线,等待RPI-RP2盘符出现后再松开按钮。
- 对于RP2040系板子(如Pico):这是最通用的方式。按住板子上标有
- 拖放固件:将下载好的
.uf2文件直接拖拽到RPI-RP2盘符里。拖入后,RPI-RP2盘符会消失,稍等片刻,一个名为CIRCUITPY的新盘符会出现。恭喜,固件烧录完成!
避坑指南:如果电脑没有弹出
RPI-RP2盘符,99%的原因是使用了只能充电不能传输数据的USB线。请务必换一根确认可以传输数据的USB线。这是我遇到最多的新手问题。
3.2 编辑器的选择与配置:Mu还是其他?
有了CIRCUITPY盘符,你可以用任何文本编辑器(如VS Code、Sublime Text、甚至记事本)来编辑code.py。但强烈建议初学者使用Mu Editor,它是为教育场景和CircuitPython量身定制的。
为什么推荐Mu?
- 开箱即用:内置串行控制台(Serial Console),无需额外配置终端软件。
- 自动检测板子:打开即自动连接,省去配置端口的麻烦。
- 模式专一:针对CircuitPython优化了代码高亮、缩进和上传逻辑。
- 安全写入:它采用安全的方式保存文件,极大降低了因突然拔线导致文件系统损坏的风险。
安装与启动Mu:
- 从 codewith.mu 下载对应你操作系统的安装包。
- 安装后首次打开,会提示选择模式,务必选择“CircuitPython”。
- 在插入CircuitPython开发板的状态下启动Mu,编辑器底部会自动出现串行控制台面板。如果没出现,点击工具栏上的“串行”按钮即可。
如果你不想用Mu:也可以使用其他编辑器(如Thonny,或VS Code+插件),但必须注意一个关键操作:在Windows上,编辑完文件后,需要在资源管理器中对CIRCUITPY盘符点击“弹出”;在Linux/Mac上,需要在终端执行sync命令。这是为了确保操作系统将缓存的数据真正写入U盘,否则直接拔线可能导致代码丢失或文件系统损坏。Mu编辑器帮你自动完成了这一步。
macOS用户的特别警告:在macOS Sonoma 14.1至14.3版本中,存在一个系统Bug,会导致向小容量驱动器(如CIRCUITPY)写入文件时延迟或出错。虽然14.4版修复了错误,但代价是写入速度大幅下降。解决方案:一是升级到更新版本的系统;二是在代码开头添加一行import supervisor; supervisor.runtime.autoreload = False来禁用自动重载,但这会失去“保存即运行”的便利性。最稳妥的办法还是在保存后,手动在终端执行sync命令。
4. 第一个程序:深入理解“闪烁的LED”
让我们从经典的“Hello, World!”硬件版——闪烁LED开始。打开CIRCUITPY驱动器,你会看到一个现成的code.py文件,内容就是让LED闪烁。
4.1 代码逐行解析
import board import digitalio import time led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT while True: led.value = True time.sleep(0.5) led.value = False time.sleep(0.5)第一部分:导入模块
import board:导入板子定义模块。它就像一个“地图”,告诉你板子上每个硬件资源(如LED引脚、I2C接口、模拟输入等)在代码里叫什么名字。board.LED就代表板载LED连接的那个引脚。import digitalio:导入数字输入输出模块。我们要控制的LED是一个数字信号(要么高电平/开,要么低电平/关),这个模块提供了操作数字引脚的所有功能。import time:导入时间模块。主要用它的sleep函数来让程序暂停一段时间,实现闪烁间隔。
第二部分:硬件配置
led = digitalio.DigitalInOut(board.LED):这行代码做了两件事。首先,digitalio.DigitalInOut()创建了一个代表数字引脚的对象。然后,board.LED作为参数传进去,告诉这个对象:“你要控制的是板载LED对应的那个物理引脚。”最后,把这个对象赋值给变量led,方便后面反复调用。led.direction = digitalio.Direction.OUTPUT:设置这个引脚的方向为“输出”。因为我们要用代码控制LED亮灭(向引脚输出信号),而不是读取外部信号(输入)。这是最关键的一步配置,方向错了,代码就不会有反应。
第三部分:主循环
while True::一个无限循环。只要板子通电,循环内的代码就会一遍又一遍地执行。这是嵌入式程序的典型结构,因为硬件需要持续工作。led.value = True:将led引脚设置为高电平(在大多数板子上,高电平点亮LED)。time.sleep(0.5):程序暂停0.5秒。在这0.5秒内,LED保持点亮状态。led.value = False:将led引脚设置为低电平,LED熄灭。time.sleep(0.5):再暂停0.5秒,LED保持熄灭。- 循环回到开头,周而复始,LED就实现了1Hz(亮0.5秒,灭0.5秒)的闪烁。
4.2 动手修改与实验
理解代码后,就可以开始“玩”了。这是CircuitPython最有趣的部分。
- 改变闪烁频率:将
sleep函数里的0.5改成0.1,保存。你会发现LED闪烁得快如蜂鸣。再改成1.0,它又会慢得像呼吸灯。通过修改这两个数字,你可以创造出任何频率的闪烁模式。 - 创造摩尔斯电码:你可以用不同时长的亮灭来代表“点”和“划”。例如,SOS信号(··· --- ···)可以这样写:
# 点函数 def dot(): led.value = True time.sleep(0.2) led.value = False time.sleep(0.2) # 划函数 def dash(): led.value = True time.sleep(0.6) led.value = False time.sleep(0.2) while True: # S ··· dot(); dot(); dot(); time.sleep(0.6) # O --- dash(); dash(); dash(); time.sleep(0.6) # S ··· dot(); dot(); dot(); time.sleep(2.0) - 如果没有单色LED:对于KB2040、QT Py等板子,你需要使用NeoPixel库。代码会变成这样:
import board, neopixel, time pixel = neopixel.NeoPixel(board.NEOPIXEL, 1) # 初始化一个NeoPixel while True: pixel[0] = (255, 0, 0) # 红色,格式为(R, G, B) time.sleep(0.5) pixel[0] = (0, 0, 0) # 熄灭 time.sleep(0.5)
注意事项:每次保存
code.py文件,CircuitPython都会自动重启并运行新代码。如果你发现修改后没反应,请检查:1. 是否保存到了正确的CIRCUITPY盘符下的code.py;2. 是否有其他同名文件(如main.py)存在,CircuitPython会按code.txt->code.py->main.txt->main.py的顺序执行第一个找到的文件。
5. 核心调试工具:串行控制台与REPL
当你的代码没有按预期工作时,串行控制台(Serial Console)和REPL就是你最好的朋友。它们是你与开发板“对话”的窗口。
5.1 串行控制台:程序的“黑匣子”
串行控制台用于显示程序运行时打印(print)出来的信息,以及运行时产生的错误信息(Traceback)。
在Mu中使用串行控制台:
- 确保开发板已通过USB连接,并且Mu处于CircuitPython模式。
- 点击工具栏上的“串行”按钮,编辑器下方会打开控制台面板。
- 在
code.py中添加一句print("Hello, CircuitPython!")到循环中,保存文件。 - 观察控制台,你会看到“Hello, CircuitPython!”这句话在不停地输出,同时板载LED在闪烁。这说明你的程序在正常运行,并且打印语句生效了。
串行控制台的核心价值——调试: 假设你的代码有错误,比如不小心把True写成了Tru。保存后,LED停止闪烁,板子状态灯可能异常。此时打开串行控制台,你会看到类似这样的信息:
Traceback (most recent call last): File "code.py", line 10, in <module> NameError: name 'Tru' is not defined这就是“错误追踪”(Traceback)。它明确告诉你:错误发生在code.py文件的第10行,错误类型是NameError,原因是Tru这个变量名没有被定义。根据这个线索,你就能快速定位并修复拼写错误。这种“打印调试法”(print debugging)在嵌入式开发中极其常用,你可以通过在不同位置打印变量值或状态信息,来跟踪程序的执行流程。
5.2 REPL:交互式编程利器
REPL(Read-Evaluate-Print-Loop)是一个交互式Python环境。你可以在这里输入一行代码,它立刻执行并返回结果,非常适合测试硬件、尝试新想法或排查复杂问题。
如何进入REPL:
- 首先确保串行控制台已连接(在Mu中已打开串行面板)。
- 在串行控制台里,按下键盘的
Ctrl+C。这会中断当前正在运行的任何程序。 - 如果程序正在运行,你会看到
Press any key to enter the REPL. Use CTRL-D to reload.的提示,此时按任意键即可进入。 - 如果
code.py是空的或没有循环,按Ctrl+C后可能直接进入REPL。 - 进入后,你会看到
>>>提示符,以及类似Adafruit CircuitPython 8.2.10 on 2024-01-01; Adafruit Feather RP2040 with rp2040的欢迎信息。
REPL能做什么:
- 测试硬件:不用写完整的程序文件,直接测试某个引脚。例如:
>>> import board, digitalio, time >>> led = digitalio.DigitalInOut(board.LED) >>> led.direction = digitalio.Direction.OUTPUT >>> led.value = True # LED应该立刻点亮 >>> led.value = False # LED熄灭 - 探索模块:查看一个模块里有什么函数和属性。
>>> import board >>> dir(board) # 列出board模块的所有属性,看看板子有哪些可用引脚 - 计算与调试:当作一个计算器,或者检查变量的当前值。
- 退出与重载:输入
Ctrl+D会软复位开发板,并重新运行code.py中的程序。这是退出REPL并返回正常运行的常用方法。
实操心得:当你的程序陷入死循环或行为异常,导致你无法通过保存新代码来覆盖时,REPL是救星。你可以进入REPL,直接修改内存中的对象状态,或者导入
storage模块暂时禁用文件系统写保护,从而修复code.py文件。
6. 高级救援与故障排除
即使再小心,也难免会遇到CIRCUITPY驱动器不显示、文件系统只读甚至代码完全“卡死”的情况。别慌,CircuitPython设计了相应的恢复机制。
6.1 安全模式:只读访问的救命稻草
安全模式(Safe Mode)是一种特殊的启动状态,在此模式下:
- 不执行
boot.py和code.py中的任何用户代码。 - 禁用自动重载(auto-reload)。
CIRCUITPY驱动器以只读方式挂载(在某些情况下可写)。
什么时候需要安全模式?
- 你写了一段错误的
boot.py代码,禁用了CIRCUITPY驱动器。 - 你的
code.py里有死循环或错误,导致板子无响应,无法正常保存新文件。 - 文件系统出现轻微逻辑错误,需要修复。
如何进入安全模式?核心时机是在板子启动或复位后的最初1秒钟内。
- 给板子上电或按下复位键(RESET)。
- 在接下来的1秒内,再次按下复位键。可以理解为“缓慢地双击复位键”(快速双击是进入Bootloader模式)。
- 如果成功,板载状态LED会快速闪烁黄灯三次作为提示。此时,电脑上应该会出现
CIRCUITPY驱动器(可能是只读的)。
在安全模式下,你可以删除或修改导致问题的code.py或boot.py文件。修复完成后,再次按下复位键(或重新插拔USB),板子就会正常启动并运行新的代码。
6.2 “核弹”级重置:UF2刷机
如果板子“砖”得非常彻底,连安全模式都进不去,或者CIRCUITPY驱动器彻底消失,我们就需要用到最后的“大招”——使用“nuke” UF2文件进行闪存擦除。
操作步骤:
- 从Adafruit的仓库下载对应你芯片的“flash erase” UF2文件(例如,RP2040芯片的)。
- 让板子进入Bootloader模式(方法同烧录固件:按住BOOT键再按RESET,出现
RPI-RP2盘符)。 - 将“nuke” UF2文件拖入
RPI-RP2盘符。 - 完成后,板子会重启。此时闪存已被完全清空,
CIRCUITPY盘符也不会出现。 - 你需要重新执行第3.1节的步骤,再次拖入正常的CircuitPython固件UF2文件,进行全新安装。
警告:此操作会清除板上所有数据,包括你的代码、库文件和任何保存的设置。因此,定期将CIRCUITPY驱动器中重要的代码备份到电脑上,是一个必须养成的好习惯。
6.3 常见问题速查表
下表汇总了开发过程中最常见的问题及解决方法:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
电脑不识别RPI-RP2或CIRCUITPY盘符 | 1. 使用了仅充电的USB线。 2. 驱动程序问题(Win7旧系统)。 3. 板子彻底损坏。 | 1.更换为数据线。 2. 为Win7安装Adafruit驱动程序。 3. 尝试“nuke”后重刷固件。 |
CIRCUITPY盘符为只读,无法保存文件 | 1. 在boot.py中设置了storage.remount("/", readonly=True)。2. 文件系统逻辑错误。 | 1. 进入安全模式,删除或修改boot.py。2. 备份数据后,考虑重刷固件。 |
| 代码修改后,板子无任何反应 | 1. 存在main.py或code.txt,优先于code.py执行。2. 代码有语法错误,启动即崩溃。 3. 代码进入了死循环且无状态反馈。 | 1. 检查CIRCUITPY根目录,移除或重命名main.py/code.txt。2. 查看串行控制台的错误信息。 3. 添加 print语句或LED状态指示,帮助调试。 |
| 串行控制台无输出或乱码 | 1. 波特率设置错误(CircuitPython固定为115200)。 2. 其他程序占用了串口。 3. (Linux) modemmanager服务干扰。 | 1. 在终端软件中确认波特率为115200。 2. 关闭可能占用串口的软件(如Arduino IDE)。 3. 在Linux终端执行: sudo apt purge modemmanager。 |
| 保存文件时报错或文件消失 | 1. (非Mu编辑器) 未执行“弹出”或sync操作就拔线。2. macOS Sonoma特定版本的已知Bug。 | 1.务必在保存后执行“安全弹出”或sync。2. 升级macOS,或在代码中禁用自动重载并手动 sync。 |
| 板载LED不亮(非NeoPixel板子) | 1. 代码错误,如引脚名错误、方向未设置。 2. 对于某些板子,LED是低电平点亮。 | 1. 检查board.LED是否是你的板子支持的名称。2. 尝试将 led.value = True改为False。 |
7. 项目进阶与资源拓展
当你熟练掌握了LED闪烁、串口打印和REPL调试后,就可以向真正的项目迈进了。CircuitPython的强大之处在于其丰富的硬件抽象层和社区库。
下一步可以探索:
- 传感器与执行器:通过
adafruit_bus_device和传感器特定库(如adafruit_bmp280、adafruit_dht),轻松读取温湿度、气压、距离等数据,或控制舵机、电机。 - 网络连接:对于ESP32-S2/S3或Wiznet以太网扩展板,可以使用
adafruit_requests库进行HTTP请求,实现物联网数据上报。 - 显示与交互:使用
adafruit_displayio库驱动OLED、TFT屏幕,或使用adafruit_debouncer库处理按钮输入,创建交互式界面。 - 文件与数据记录:利用
storage和adafruit_sdcard模块,将传感器数据记录到CIRCUITPY驱动器或外接SD卡中。
资源获取:
- 官方库合集:绝大多数传感器和驱动库都由Adafruit维护,可以在 CircuitPython Library Bundle 页面下载。将下载的
lib文件夹中的对应库文件,复制到你的CIRCUITPY驱动器的lib文件夹内即可使用。 - 学习指南:Adafruit的 Learn平台 上有数以千计的CircuitPython项目教程,从基础到高级,涵盖几乎所有硬件。
- 社区支持:Adafruit的 Discord频道 和 论坛 非常活跃,遇到棘手问题时,在这里提问往往能得到快速解答。
我个人最深的一个体会是,CircuitPython将“探索”和“迭代”的成本降到了最低。你不再需要为一个想法去搭建复杂的工程、处理繁琐的编译下载过程。就像在玩一个高级的电子积木,所有的模块(库)都是即插即用的,所有的代码修改都是即时生效的。这种即时反馈的乐趣,是推动持续学习和创造的最大动力。最后一个小技巧:给你的CIRCUITPY驱动器起个有意义的项目名(在Mac/Linux下可以重命名卷标),当你同时进行多个项目时,这能有效避免混淆。