news 2026/5/15 2:25:43

CircuitPython硬件开发实战:从微控制器编程到蓝牙交互应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CircuitPython硬件开发实战:从微控制器编程到蓝牙交互应用

1. CircuitPython:为硬件注入Python的灵魂

如果你对编程感兴趣,尤其是玩过树莓派之类的单板计算机,那你对Python肯定不陌生。但你是否想过,用同样简洁优雅的Python代码,去直接控制一块硬币大小的微控制器,让LED闪烁、读取温度、甚至通过蓝牙遥控小车?这就是CircuitPython带来的魔法。它不是一门新语言,而是Python在微控制器世界的一个“方言”版本。想象一下,你不需要复杂的编译工具链,不需要理解晦涩的寄存器配置,就像在电脑上写一个.py脚本一样,把文件拖到一块名为CIRCUITPY的U盘里,代码就自动运行了——这就是CircuitPython承诺并实现的开发体验。

我最初接触嵌入式开发时,面对各种C语言的makefile和底层驱动,没少头疼。直到用了CircuitPython,那种“所见即所得”的即时反馈感,让我找回了编程最初的乐趣。它特别适合教育、快速原型验证,以及任何希望将创意快速转化为实物交互的场合。本文将以Adafruit出品的明星开发板Circuit Playground Bluefruit(后文简称CPB)作为硬件平台,手把手带你从零开始,完成CircuitPython的安装、配置、编程,并深入其硬件交互的核心。无论你是编程新手,还是想寻找更高效硬件开发工具的老手,这篇文章都能给你一条清晰的路径。

2. 硬件平台深度解析:Circuit Playground Bluefruit

在开始写代码之前,我们必须先了解手中的“画布”。CPB不是一块普通的微控制器板,它更像一个为教育和创意项目量身定制的“瑞士军刀”。其核心是一颗Nordic的nRF52840芯片,这赋予了它蓝牙5.0的强大无线连接能力。但更吸引人的是板上集成的十几种传感器和外设,开箱即用,无需焊接。

2.1 核心功能引脚映射与电气特性

板载资源虽丰富,但要想精准控制,必须清楚每个引脚的定义。原始资料给出了引脚列表,但光知道编号不够,我们需要理解其背后的功能和操作逻辑。

数字引脚(Digital Pins)

  • D13 (板载红色LED):这是最经典的“Hello World”输出设备。在CircuitPython中,通过board.LED对象访问。将其设置为输出模式并切换高低电平,就能实现闪烁。这是你测试开发环境是否正常的第一站。
  • D11 (SPEAKER_ENABLE):这是一个电源使能引脚,而非音频信号引脚。它控制板载扬声器放大器的电源。默认内部上拉为高电平,放大器开启。如果你想在电池供电项目中节省功耗,需要将其设置为输出模式并输出低电平(LOW)来关闭放大器。这是一个典型的“低电平有效”控制逻辑。
  • D12 (SPEAKER):这才是真正的模拟音频输出引脚。你需要结合audiocoreaudiobusio等库来生成波形数据,并通过此引脚输出到扬声器。
  • D24, D25 (PDM麦克风):这是数字麦克风的时钟和数据引脚。PDM(脉冲密度调制)是一种数字音频格式,你需要使用audiobusio.PDMIn库来读取音频数据。这对于制作声控项目或录音非常有用。

模拟引脚(Analog Pins)

  • A8 (光线传感器):一个光敏电阻。使用analogio.AnalogIn读取其电压值,值越小,环境光越亮。注意,它的返回值是原始ADC值(通常0-65535),你可能需要映射到更直观的亮度百分比。
  • A9 (温度传感器):一个热敏电阻(NTC)。Adafruit专门提供了adafruit_thermistor库来将其读数转换为摄氏度或华氏度,这比直接读取模拟值并套用公式要方便准确得多。

内部连接与电源管理

  • D26, D28 (内部I2C):这两个引脚连接到了板载的LIS3DH三轴加速度计。在CircuitPython中,你可以直接通过board.ACCELEROMETER_SCLboard.ACCELEROMETER_SDA来访问,使用adafruit_lis3dh库轻松获取X、Y、Z轴的加速度值,用于检测倾斜、晃动或敲击。
  • D27 (加速度计中断):这个引脚允许加速度计在特定事件(如自由落体、单击、双击)发生时,主动触发一个中断信号通知主控,从而实现低功耗的事件监听,而不是不停地轮询查询。
  • D35 (传感器与NeoPixel电源引脚):这是板上最重要的电源管理引脚。它控制着光线传感器、温度传感器、麦克风以及那10个炫彩NeoPixel LED的供电。默认拉低(LOW)意味着供电开启。如果你想彻底关闭这些外设以节省电量(尤其是在电池模式下),需要将此引脚设置为输出模式并输出高电平(HIGH)。请注意:加速度计不受此引脚控制,这是为了支持“晃动唤醒”功能——即使其他传感器断电,你仍然可以通过晃动板子来触发中断,唤醒系统。

实操心得:电源管理的重要性在电池供电项目中,功耗是生命线。CPB的D11和D35是两个关键的节能开关。我的经验是:在代码初始化部分,明确设置这些电源控制引脚的状态。例如,如果你的项目不需要声音和灯光,就在setup阶段关闭它们。这能轻易地将整体静态电流从几十毫安降低到几毫安,显著延长电池寿命。别忘了,操作顺序应是先配置引脚模式为输出,再设置输出电平。

2.2 调试接口与启动模式

板子背面有三个小小的金属焊盘,标注为SWCLK、SWDIO和RESET。这是ARM Cortex-M系列芯片标准的SWD调试接口。对于大多数CircuitPython用户,你可能永远用不到它。它的主要用途是:

  1. 烧录原始固件:如果板子的UF2引导程序损坏,可以通过SWD接口使用专用的编程器(如J-Link、DAPLink)进行恢复。
  2. 深度调试:使用GDB等工具进行单步调试、查看寄存器,这属于更底层的开发范畴。

对于日常使用,我们只需要知道RESET按钮(板子中央的那个)的用法:双击它,可以让板子进入UF2引导加载模式,此时板子会模拟成一个名为CPLAYBTBOOT的U盘,用于拖放更新CircuitPython固件。这是更新系统最安全、最常用的方法。

3. CircuitPython系统安装与库部署实战

有了硬件认知,接下来就是搭建软件环境。CircuitPython的安装过程极其简单,这也是其设计哲学的核心体现。

3.1 固件下载与刷写

  1. 获取固件:访问 circuitpython.org ,在搜索框或板卡列表中找到“Circuit Playground Bluefruit”。关键点:务必下载与你的硬件版本完全匹配的.uf2文件。Adafruit有时会更新硬件修订版,用错固件可能导致部分功能异常。
  2. 进入引导模式:使用一条可靠的数据线(强调无数次:很多手机充电线只能充电,无法传输数据)将CPB连接至电脑。然后快速双击板子中央的复位按钮。成功的标志是:10个NeoPixel LED先全部变红,然后全部变绿。如果它们保持红色不动,请检查USB线或电脑端口。
  3. 拖放更新:电脑上会出现一个名为CPLAYBTBOOT的驱动器。将下载好的.uf2文件直接拖入这个驱动器。此时,LED会再次变红,CPLAYBTBOOT驱动器会消失,几秒钟后,一个名为CIRCUITPY的新驱动器会出现。恭喜,CircuitPython系统安装成功!

注意事项:UF2刷写失败排查

  • 驱动盘未出现:确保是双击,且节奏稍快。有时需要多试几次。也可以尝试单击后长按。
  • 刷写后CIRCUITPY盘未出现:等待10-15秒。如果仍未出现,尝试重新插拔USB。极少数情况下,可能需要手动复位(单击RESET按钮)。
  • 文件系统损坏:如果在刷写过程中意外断电或拔线,可能导致CIRCUITPY盘无法挂载。此时需要重新进入引导模式,再次刷写固件。你的代码文件可能会丢失,因此定期备份CIRCUITPY盘里的code.py等文件是必须养成的好习惯。

3.2 库文件管理与部署

安装完系统,CIRCUITPY驱动器里通常只有一个code.py和一个lib文件夹(可能初始为空)。CircuitPython通过lib文件夹来管理第三方库。

  1. 获取库文件包:再次访问 circuitpython.org ,找到“Libraries”页面,下载与你的CircuitPython版本号匹配的“CircuitPython Library Bundle”。这是一个包含所有官方及社区维护库的压缩包。
  2. 选择性安装:解压下载的Bundle,你会看到一个庞大的lib文件夹。千万不要把整个Bundle的lib文件夹拖进去!这会导致CIRCUITPY盘空间迅速耗尽。你应该只复制你项目需要的库。
    • 对于CPB的基础功能,通常需要以下库(从Bundle的lib中复制到CPB的lib中):
      • adafruit_ble/(文件夹):蓝牙功能核心库。
      • adafruit_bluefruit_connect/(文件夹):用于与Adafruit Bluefruit手机App通信的协议库。
      • adafruit_bus_device/(文件夹):底层总线设备支持库,许多传感器库依赖它。
      • adafruit_circuitplayground/(文件夹):核心库,它提供了访问CPB所有板载硬件(按钮、传感器、LED、扬声器等)的高级、简化接口,极大提升了开发效率。
      • adafruit_lis3dh.mpy:加速度计驱动。
      • adafruit_thermistor.mpy:温度传感器驱动。
      • neopixel.mpy:控制NeoPixel LED的库。
    • .mpy文件是预编译的字节码,加载速度比.py文件快,且能保护源代码。优先使用.mpy

库文件冲突与版本管理: 如果lib文件夹里同时存在一个库的.mpy文件和同名文件夹,CircuitPython会优先使用文件夹。确保没有重复文件。当CircuitPython升级后,建议也更新库Bundle,因为新版本的固件可能需要新版本的库配合工作。

4. 开发环境搭建与第一个程序

工欲善其事,必先利其器。虽然你可以用任何文本编辑器编辑code.py,但一个集成开发环境能让你事半功倍。

4.1 编辑器选择:为什么推荐Mu

Adafruit官方推荐Mu编辑器。它并非功能最强大的IDE,但它是为CircuitPython量身定做的,解决了嵌入式开发中最棘手的几个问题:

  • 内置串口控制台:无需额外打开终端软件,代码输出和错误信息直接显示在编辑器下方。
  • 一键进入REPL:点击按钮即可进入交互式编程环境。
  • 自动检测板卡:插入板子后,Mu能自动识别并配置串口。
  • 安全的文件保存:它确保文件被完整写入磁盘后才弹出,极大降低了因意外拔线导致文件系统损坏的风险。

安装与配置Mu

  1. 从 codewith.mu 下载对应你操作系统的安装包。
  2. 首次运行时,选择“CircuitPython”模式。
  3. 插入CPB,确保CIRCUITPY盘已挂载,然后启动Mu。编辑器底部应显示已连接到板子的串口。

替代方案: 如果你习惯其他编辑器(如VSCode、Thonny),完全可以。但请牢记一个黄金法则:在Windows上,写完代码后,必须在文件资源管理器中右键点击CIRCUITPY盘并选择“弹出”;在macOS/Linux上,需要在终端执行sync命令或在图形界面中“安全移除”硬件。这是为了确保操作系统将所有缓存数据写入磁盘,避免文件损坏。

4.2 编写、运行与调试你的第一个程序

让我们从经典的“闪烁LED”开始,但这次我们用CPB板载的红色LED(D13)。

  1. 打开代码文件:在Mu中,点击“加载”按钮,导航到CIRCUITPY盘,选择code.py。如果它是空的,就直接在新窗口中开始编写。
  2. 输入代码:将以下代码粘贴进去:
    import board import digitalio import time led = digitalio.DigitalInOut(board.LED) # 获取LED引脚对象 led.direction = digitalio.Direction.OUTPUT # 设置为输出模式 while True: # 主循环 led.value = True # 高电平,LED亮 time.sleep(0.5) # 等待0.5秒 led.value = False # 低电平,LED灭 time.sleep(0.5) # 再等待0.5秒
  3. 保存与自动运行:点击“保存”,Mu会将文件保存到CIRCUITPY盘根目录下的code.py。CircuitPython文件系统监控到code.py变化,会自动重启并运行新代码。你应该立刻看到板子上的红色LED开始以1秒为周期闪烁。

代码解析

  • import:导入需要的模块。board模块包含了所有引脚的定义,digitalio用于控制数字输入输出,time提供延时功能。
  • digitalio.DigitalInOut():创建一个数字IO对象,并绑定到具体的引脚(board.LED)。
  • .direction:设置引脚的方向,OUTPUT表示我们控制它输出电平。
  • .value:设置输出电平,True1代表高电平(通常3.3V),False0代表低电平(0V)。
  • time.sleep():让程序暂停指定的秒数。这是最简单的控制节奏的方法。

修改与实验: 尝试将time.sleep(0.5)中的0.5改为0.11.0,保存后观察LED闪烁频率的变化。这就是CircuitPython的“编辑-保存-即时运行”循环,快速迭代的乐趣由此开始。

5. 高级交互:串口控制台与REPL

当你的代码不仅仅是闪烁LED,而是需要读取传感器数据、调试逻辑错误时,串口控制台和REPL就成了你最得力的助手。

5.1 串口控制台:程序的“输出窗口”

串口控制台是程序文本输出的显示终端。在Mu中,只需点击“串口”按钮即可打开。

使用print()进行输出和调试: 修改上面的闪烁代码,加入print语句:

import board import digitalio import time led = digitalio.DigitalInOut(board.LED) led.direction = digitalio.Direction.OUTPUT counter = 0 # 新增一个计数器 while True: led.value = True time.sleep(0.5) led.value = False time.sleep(0.5) counter += 1 # 计数器加1 print("LED has blinked", counter, "times.") # 打印信息到串口

保存后,打开Mu的串口控制台,你会看到不断输出的计数信息。这是**“打印调试法”** 的基石。你可以通过打印变量值、函数执行到哪一步等信息,来追踪程序的运行状态。

捕获与解读错误信息: 故意在代码中制造一个错误,比如将led.value = True写成led.value = Tru。保存后,LED会停止闪烁,串口控制台会显示类似这样的错误信息:

Traceback (most recent call last): File "code.py", line 10, in <module> NameError: name 'Tru' is not defined

这段信息极其宝贵:

  • Traceback:指出了错误发生时的调用栈。
  • File "code.py", line 10:明确告诉你错误发生在code.py文件的第10行。
  • NameError: name 'Tru' is not defined:错误类型和具体描述,这里告诉你Tru这个变量名未定义。

根据这些信息,你就能快速定位并修复拼写错误。

5.2 REPL:交互式Python命令行

如果说串口控制台是“听”程序说话,那么REPL(Read-Eval-Print Loop)就是让你和板子“对话”。在串口控制台打开的情况下,按Ctrl+C,程序会中断,并提示Press any key to enter the REPL。按任意键即可进入。

REPL能做什么?

  1. 探索模块和硬件
    >>> import board >>> dir(board) # 查看board模块下所有可用的引脚和属性 ['A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'ACCELEROMETER_INTERRUPT', 'ACCELEROMETER_SCL', ... , 'LED', 'LIGHT', 'SPEAKER', 'SPEAKER_ENABLE', 'TEMPERATURE'] >>> led = digitalio.DigitalInOut(board.LED) >>> led.direction = digitalio.Direction.OUTPUT >>> led.value = True # 立即点亮LED >>> led.value = False # 立即关闭LED
    你可以不写任何文件,直接测试某个引脚的功能。
  2. 执行单行代码或简单脚本:快速测试一个想法。
  3. 调试时检查变量状态:当程序在while True循环中运行时,你无法直接查看变量。但在REPL中,你可以中断程序,然后输入变量名来查看其当前值。
  4. 文件系统操作(需要os模块):列出文件、创建目录等。

退出REPL:输入Ctrl+D,板子会软复位并重新开始运行code.py中的程序。

实操心得:REPL与文件系统的关系在REL中进行的操作(如创建变量、导入模块)仅存在于当前的内存会话中,一旦按Ctrl+D复位,这些都会消失。它不会修改CIRCUITPY盘上的任何文件。如果你想永久保存测试成功的代码,必须将其复制到code.py或其他文件中。REPL是强大的实验沙盒,但不是持久化存储工具。

6. 利用板载硬件:从传感器到蓝牙

现在,让我们运用前面学到的知识,结合adafruit_circuitplayground这个高级库,来真正玩转CPB的丰富功能。这个库将复杂的硬件操作封装成了简单的函数调用。

6.1 读取传感器与控制输出

创建一个新的code.py,使用以下代码来综合测试多个传感器和输出设备:

import time from adafruit_circuitplayground import cp # 导入CPB库的简化别名 # 设置NeoPixel亮度,避免太刺眼 cp.pixels.brightness = 0.05 while True: # 1. 读取传感器数据 light = cp.light # 光线传感器值 (0-255) temp_c = cp.temperature # 温度 (摄氏度) accel_x, accel_y, accel_z = cp.acceleration # 加速度 (米/秒²) button_a = cp.button_a # A按钮状态 button_b = cp.button_b # B按钮状态 switch = cp.switch # 滑动开关状态 (True/False) # 2. 根据传感器数据控制输出 # 用光线值控制第一个NeoPixel的蓝色分量 blue_intensity = int(light) cp.pixels[0] = (0, 0, min(blue_intensity, 255)) # 用温度控制第二个NeoPixel的红色分量 (假设15-35摄氏度范围) temp_ratio = (temp_c - 15) / 20.0 # 映射到0-1 temp_ratio = max(0.0, min(1.0, temp_ratio)) # 限制在0-1之间 red_intensity = int(temp_ratio * 255) cp.pixels[1] = (red_intensity, 0, 0) # 检测晃动(加速度变化大) if abs(accel_x) > 15 or abs(accel_y) > 15 or abs(accel_z) > 15: cp.play_tone(440, 0.1) # 播放440Hz音调0.1秒 # 按钮A控制蜂鸣器 if button_a: cp.play_tone(523, 0.2) # C5音调 # 滑动开关控制所有NeoPixel的开关 if switch: cp.pixels.fill((0, 0, 0)) # 关灯 else: # 开关打开时,恢复前两个像素的颜色 pass # 颜色已在上面设置 # 3. 打印数据到串口控制台 print(f"Light: {light:3d}, Temp: {temp_c:5.1f}C, Accel: ({accel_x:6.2f}, {accel_y:6.2f}, {accel_z:6.2f})") print(f"Button A: {button_a}, B: {button_b}, Switch: {switch}") print("-" * 40) time.sleep(0.5) # 降低采样率,让输出更易读

代码深度解析

  • 简化访问from adafruit_circuitplayground import cp让你可以通过cp这个对象访问所有功能,无需记忆复杂的引脚定义。
  • 传感器融合:这个例子展示了如何将多个传感器的读数(光、温度、加速度)映射到不同的输出(LED颜色、声音),实现简单的环境交互。
  • 数据打印格式化print(f"...")使用了f-string,{light:3d}表示将light整数格式化为3位宽,{temp_c:5.1f}表示将温度格式化为总宽5位、保留1位小数的浮点数。这让串口输出更加整齐易读。
  • 防抖与阈值:晃动检测使用了绝对值(abs)和阈值(15 m/s²),这是处理模拟传感器数据的常见技巧,可以过滤掉微小的震动干扰。

6.2 蓝牙低功耗(BLE)初探

CPB的“Bluefruit”之名就源于其强大的蓝牙功能。使用BLE,你可以让手机和板子无线通信。

基础概念:在BLE中,设备扮演不同的角色:外设(Peripheral,如CPB,广播数据)和中心设备(Central,如手机,扫描并连接)。CPB通常作为外设,广播一些服务(Service),比如电池电量、自定义数据等。

一个简单的BLE广播示例(需要adafruit_ble库):

import time from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService ble = BLERadio() # 创建BLE无线电对象 uart = UARTService() # 创建一个UART服务,这是最通用的数据通道 advertisement = ProvideServicesAdvertisement(uart) # 创建包含UART服务的广播包 print("CircuitPython BLE UART Peripheral") print("Broadcasting...") while True: # 开始广播,等待连接 ble.start_advertising(advertisement) while not ble.connected: pass # 等待连接 print("Connected!") # 连接后,进入数据交换循环 while ble.connected: # 如果串口有数据输入,就读取并回显 if uart.in_waiting: raw_bytes = uart.read(uart.in_waiting) text = raw_bytes.decode('utf-8').strip() print("Received:", text) response = f"Echo: {text}\n" uart.write(response.encode('utf-8')) time.sleep(0.1) print("Disconnected.")

这段代码让CPB作为一个BLE UART外设。你可以使用手机上的“Bluefruit Connect”或“nRF Connect”等App扫描并连接它,然后在App的UART控制台中发送文本,CPB会回显同样的内容。

注意事项:BLE开发要点

  1. 功耗:广播和保持连接都会消耗电量。在电池项目中,需要合理设计广播间隔和连接参数。
  2. 服务与特征:BLE通信基于GATT协议,核心是“服务”和“特征”。UARTService是Adafruit定义的一个兼容其App的通用服务。你也可以定义自己的自定义服务。
  3. 连接稳定性:无线环境复杂,代码中需要有连接断开的重连机制。上面的例子在断开后会回到广播状态,等待下一次连接。
  4. 数据格式:通过BLE传输的是原始字节。发送和接收时需要进行编码(encode)和解码(decode),通常使用UTF-8。

7. 项目实战:制作一个环境监测数据记录器

让我们综合运用所学,构建一个稍复杂的项目:一个通过蓝牙将光线和温度数据发送到手机,并本地用NeoPixel显示大致温度范围的数据记录器。

项目目标

  1. 每10秒读取一次光线和温度传感器数据。
  2. 用板载的10个NeoPixel LED显示温度趋势(例如,低温蓝色,高温红色)。
  3. 通过BLE UART,将带有时间戳的数据发送到连接的手机App。
  4. 通过板载按钮A控制数据记录的启停。

代码实现

import time import board import digitalio from adafruit_circuitplayground import cp from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService # --- 初始化 --- ble = BLERadio() uart = UARTService() advertisement = ProvideServicesAdvertisement(uart) cp.pixels.brightness = 0.1 logging_active = False # 数据记录开关 # 配置按钮A为输入,并启用内部上拉电阻(按下去是False) button_a = digitalio.DigitalInOut(board.BUTTON_A) button_a.switch_to_input(pull=digitalio.Pull.UP) last_press_time = 0 debounce_delay = 0.3 # 防抖延时,秒 def update_leds_by_temp(temp_c): """根据温度更新NeoPixel颜色 (假设范围15-35°C)""" cp.pixels.fill((0, 0, 0)) # 先清空 temp_min, temp_max = 15, 35 # 将温度映射到0-9的LED索引 ratio = (temp_c - temp_min) / (temp_max - temp_min) ratio = max(0.0, min(1.0, ratio)) # 钳制在0-1 led_index = int(ratio * 9) # 0到9 # 计算颜色:从蓝(冷)到红(热) red = int(ratio * 255) blue = int((1 - ratio) * 255) cp.pixels[led_index] = (red, 0, blue) def send_data_over_ble(light_val, temp_val, timestamp): """如果BLE已连接,则发送数据""" if ble.connected: data_string = f"{timestamp:.1f}s, Light:{light_val:3d}, Temp:{temp_val:5.1f}C\n" try: uart.write(data_string.encode('utf-8')) except OSError: # 写入失败,可能连接已断开 print("BLE write failed, connection may be lost.") # --- 主循环 --- print("Environmental Data Logger Started") print("Press Button A to start/stop logging.") print("Waiting for BLE connection...") last_log_time = time.monotonic() log_interval = 10.0 # 记录间隔10秒 start_time = time.monotonic() while True: current_time = time.monotonic() elapsed_time = current_time - start_time # 1. 按钮处理(带防抖) if not button_a.value: # 按钮被按下(低电平) if (current_time - last_press_time) > debounce_delay: logging_active = not logging_active # 切换状态 status = "STARTED" if logging_active else "STOPPED" print(f"Logging {status}.") cp.play_tone(262 if logging_active else 196, 0.2) # 不同提示音 last_press_time = current_time # 等待按钮释放,避免在按下期间重复触发 while not button_a.value: time.sleep(0.05) # 2. 数据记录与发送逻辑 if logging_active and (current_time - last_log_time) >= log_interval: light_val = cp.light temp_val = cp.temperature # 更新LED显示 update_leds_by_temp(temp_val) # 发送数据 send_data_over_ble(light_val, temp_val, elapsed_time) # 本地串口也打印一份 print(f"[{elapsed_time:6.1f}s] Light: {light_val:3d}, Temp: {temp_val:5.1f}C") last_log_time = current_time # 3. BLE连接管理 if not ble.connected and not ble.advertising: print("BLE not connected, starting advertising...") ble.start_advertising(advertisement) elif ble.connected and ble.advertising: ble.stop_advertising() print("Connected! Stopping advertisement.") # 4. 非阻塞延时,保持系统响应 time.sleep(0.1)

项目代码要点解析

  1. 状态管理:使用logging_active布尔变量全局控制记录功能的开关。
  2. 按钮防抖:机械按钮在按下和释放时会产生电压抖动,导致多次误触发。通过记录上次按下时间并设置一个debounce_delay(如0.3秒),可以有效地过滤掉抖动。
  3. 非阻塞设计:主循环中的time.sleep(0.1)很短,确保了系统能及时响应按钮事件和BLE连接事件,同时又不会过度消耗CPU。
  4. 资源清理:在BLE连接成功后,调用ble.stop_advertising()停止广播,这是良好的节能和网络礼仪。
  5. 错误处理:在send_data_over_ble函数中,使用try-except捕获可能的OSError(如连接意外中断),避免程序因写操作失败而崩溃。
  6. 时间管理:使用time.monotonic()获取单调递增的时间,它不受系统时钟调整的影响,适合测量时间间隔。

如何测试

  1. 将代码保存到CPB的code.py
  2. 打开Mu的串口控制台,你会看到启动信息。
  3. 按下按钮A,听到提示音,记录开始,NeoPixel会根据温度亮起。
  4. 用手机打开“Adafruit Bluefruit Connect” App,扫描并连接你的CPB设备。
  5. 在App的UART控制台(或Plotter界面),你将看到每隔10秒传来的数据。
  6. 再次按下按钮A,记录停止。

这个项目涵盖了传感器读取、状态控制、用户输入、硬件输出、无线通信和基本的程序结构设计,是一个完整的CircuitPython应用雏形。你可以在此基础上扩展,比如将数据保存到CIRCUITPY盘的文件中,增加更多传感器,或者改变BLE通信协议来控制其他设备。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/15 2:25:10

树莓派电子墨水屏与音频扩展板开发指南:从硬件连接到Python编程

1. 项目概述与核心价值如果你手头有一块树莓派&#xff0c;并且对那种断电也能保持显示、看起来像印刷纸张一样的电子墨水屏&#xff08;E-Paper&#xff09;感兴趣&#xff0c;那么Adafruit的这款E-Ink Bonnet扩展板绝对值得你深入研究。我最初接触这个板子&#xff0c;是想做…

作者头像 李华
网站建设 2026/5/15 2:19:05

基于Simulink图形化建模求解一阶时变偏微分方程

1. 项目概述&#xff1a;从工程视角看一阶时变偏微分方程在工程系统建模与仿真领域&#xff0c;我们常常会遇到一类描述物理量在空间和时间上同时演化的数学模型&#xff0c;这就是偏微分方程。其中&#xff0c;一阶时变偏微分方程&#xff0c;比如对流方程、传输方程&#xff…

作者头像 李华
网站建设 2026/5/15 2:17:48

独立开发者如何利用Taotoken模型广场为不同项目选型

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 独立开发者如何利用Taotoken模型广场为不同项目选型 作为一名独立开发者&#xff0c;你可能同时维护着多个AI项目。一个可能是需要…

作者头像 李华
网站建设 2026/5/15 2:15:03

儿童房 书房健康照明设计:国标 RG0/UGR<19/Ra≥90 武汉家装实用指南

摘要家里装儿童房、书房&#xff0c;灯光真不是随便装个灯就行。尤其武汉本地家庭&#xff0c;孩子长期在家写作业、看书&#xff0c;灯光选不对&#xff0c;很容易眼疲劳、揉眼睛、注意力不集中。本文结合实际家装经验&#xff0c;照着国标要求&#xff0c;用大白话讲清无蓝光…

作者头像 李华