news 2026/5/14 20:44:10

CircuitPython嵌入式开发入门:从Blink到I2C传感器实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CircuitPython嵌入式开发入门:从Blink到I2C传感器实战指南

1. 项目概述与核心价值

如果你对硬件编程的印象还停留在晦涩的C语言和复杂的寄存器配置,那么CircuitPython的出现,绝对能颠覆你的认知。简单来说,CircuitPython是Python语言在微控制器上的一个“方言”实现,它把Python的简洁和易读性带到了嵌入式世界。这意味着,你熟悉的print()while True:循环、列表和函数,现在可以直接用来控制LED、读取传感器、驱动电机。它的核心价值,就是极大地降低了嵌入式开发的门槛,让开发者,无论是学生、创客还是专业工程师,都能将更多精力聚焦在创意和逻辑实现上,而非底层硬件细节。

我最初接触嵌入式开发时,光是搭建交叉编译环境、理解内存映射、配置时钟树就耗费了大量时间。CircuitPython的出现,就像给这片硬核的领域开了一扇“快捷之门”。你只需要一块支持CircuitPython的开发板(比如Adafruit的Metro M4、Feather系列,或者国内常见的ESP32-S3等),通过USB线连接到电脑,它就会以一个U盘(名为CIRCUITPY)的形式出现。把你的Python代码(命名为code.py)拖进去,代码就会自动运行。这种“即写即运行”的体验,对于快速原型验证和教学来说,是革命性的。

本文将以一个典型的Adafruit硬件生态为例,手把手带你从最经典的“Hello World”——点亮一个LED(Blink)开始,逐步深入到数字输入、模拟信号读取,最终掌握如何通过I2C总线与复杂的传感器(如高精度温度传感器)通信。整个过程,你会看到CircuitPython如何将复杂的硬件操作,抽象成几句直观的Python代码。无论你是想做个智能温湿度计、一个可交互的灯光装置,还是更复杂的物联网节点,这篇指南都将为你打下坚实的实践基础。

2. 环境搭建与核心工具链解析

在开始写第一行代码之前,我们需要把“舞台”搭好。CircuitPython的开发环境极其轻量,几乎可以说是“零配置”。但这并不意味着我们可以忽略工具的选择和理解,合理的工具链能让你事半功倍。

2.1 硬件选型:为什么是Adafruit?

输入材料中多次提到了Adafruit的硬件,这并非偶然。Adafruit是CircuitPython项目的主要维护者和推动者之一。选择Adafruit的板子(如Metro M4 Express、ItsyBitsy M4)有以下几个实实在在的好处:

  1. 原生且稳定的支持:这些板子的固件由Adafruit官方维护和测试,更新及时,兼容性最好,几乎不会遇到奇怪的驱动或库问题。
  2. 丰富的示例和文档:Adafruit为每一款板子和传感器提供了极其详尽的教程、原理图、Fritzing接线图以及完整的CircuitPython代码示例,学习成本极低。
  3. 统一的STEMMA QT/Qwiic接口:这是Adafruit推广的一种即插即用的传感器连接标准,使用PH2.0或SH1.0的4芯接口,无需焊接,通过一根线缆即可完成供电(3.3V)、接地(GND)和I2C(SDA, SCL)连接,极大简化了硬件组装过程。文中提到的MCP9808温度传感器和电位器模块都具备此接口。

当然,CircuitPython社区也支持大量其他厂商的板子,如ESP32系列、Raspberry Pi Pico等。但对于初学者,从生态最完善的平台入手,能避免很多不必要的麻烦。

2.2 软件准备:编辑器与串行终端

你的代码编辑器可以是任何你喜欢的文本编辑器,如VS Code、Atom、甚至系统自带的记事本(不推荐)。但我强烈推荐使用专为CircuitPython优化的编辑器,如Mu EditorVisual Studio Code with CircuitPython插件

  • Mu Editor:这是Adafruit官方推荐的入门级IDE。它最大的优点是内置了串行终端(Serial Console)。在嵌入式开发中,串行终端是我们的“眼睛”和“嘴巴”,我们通过它来打印调试信息(print语句的输出)、查看传感器读数,甚至进行交互式编程(REPL)。Mu Editor一键即可打开串行终端,无需额外配置。
  • Visual Studio Code:如果你已经是VS Code的用户,安装“CircuitPython”扩展后,可以获得代码自动补全、库管理、串行终端等强大功能,开发体验更专业。

核心操作流程

  1. 访问CircuitPython官网,根据你的开发板型号下载对应的.uf2固件文件。
  2. 将开发板通过USB连接电脑,并使其进入引导加载模式(通常需要双击复位按钮)。此时电脑会识别出一个名为BOOT的U盘。
  3. 将下载的.uf2文件拖入BOOT盘。完成后,开发板会自动重启,并出现一个名为CIRCUITPY的新U盘。
  4. 打开你的编辑器,编写代码并保存为code.py,然后将其拖入CIRCUITPY盘的根目录。代码会自动运行。

注意CIRCUITPY盘是“活”的。当你保存code.py时,CircuitPython会立即重新加载并执行它。这意味着你可以在代码运行时修改并保存,新代码会覆盖旧代码并重新开始运行(取决于代码结构)。这是一种非常高效的开发方式。

3. 从Blink开始:理解硬件抽象与事件循环

“Blink”是嵌入式世界的“Hello World”。它的目标是让板载LED闪烁。别看它简单,却包含了CircuitPython编程几乎所有的核心概念。

3.1 代码逐行解读与硬件抽象思想

让我们仔细剖析输入材料中的Blink代码:

import time import board import digitalio 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 time, board, digitalio:这是导入模块。time用于控制时间延迟;board是一个关键模块,它抽象了当前这块特定开发板的所有引脚定义。你不需要去查数据手册找LED对应的是哪个GPIO引脚号,只需要使用board.LED这个常量。digitalio则是用于控制数字输入输出(Digital Input/Output)的核心库。
  • led = digitalio.DigitalInOut(board.LED):这一行是CircuitPython硬件抽象的精髓。它创建了一个DigitalInOut对象,并告诉这个对象:“你要操作的是这块板子上定义为LED的那个引脚”。这个对象led就成为了你在代码世界中控制那个物理LED的“手柄”。
  • led.direction = digitalio.Direction.OUTPUT:我们通过这个“手柄”来配置硬件。这里我们将引脚的方向设置为“输出”(OUTPUT),意味着我们将通过它向外部世界发送信号(高电平或低电平)来点亮或熄灭LED。如果我们要读取一个按钮的状态,则需要设置为INPUT
  • while True::这是一个无限循环。在嵌入式系统中,程序通常没有“结束”的概念,它会一直运行,持续响应或控制外部世界。这个循环就是程序的主逻辑。
  • led.value = True/Falsetime.sleep(0.5):在循环内,我们通过设置led.valueTrue(高电平,通常为3.3V)来点亮LED,为False(低电平,0V)来熄灭LED。time.sleep(0.5)让程序暂停0.5秒,从而产生闪烁效果。

为什么这种抽象如此重要?它实现了硬件无关性。你的代码逻辑(点亮、等待、熄灭、等待)是通用的。今天你在Adafruit Metro M4上运行,board.LED指向D13引脚;明天换到Feather ESP32-S3,你只需要更换固件,代码一行都不用改,因为board.LED在新板子的board模块中会自动映射到正确的引脚。这避免了因硬件更换而大量修改代码的麻烦。

3.2 深入原理:GPIO、上拉电阻与消抖

在进入下一个数字输入例子前,有必要理解两个关键硬件概念,这能帮你避开很多坑。

1. GPIO的工作模式: 一个GPIO引脚可以配置为输出(驱动LED、继电器)或输入(读取按钮、开关)。当配置为输入时,引脚处于高阻抗状态,其电平状态由外部电路决定。如果外部什么都不接(即“浮空”),引脚的电平可能是不确定的(受电磁干扰影响),读取的值会随机跳动。这就是为什么需要上拉或下拉电阻

  • 上拉电阻:在引脚和电源(如3.3V)之间连接一个电阻(通常4.7kΩ-10kΩ)。当按钮未按下时,引脚通过电阻被“拉”到高电平(True);按下按钮时,引脚直接接地,变为低电平(False)。这是最常用的方式。
  • 下拉电阻:在引脚和地(GND)之间连接电阻。未按下时为低电平,按下时为高电平。

CircuitPython的digitalio库提供了便捷的配置方式:button.switch_to_input(pull=digitalio.Pull.UP)。这行代码就启用了芯片内部的上拉电阻,无需外接物理电阻,非常方便。

2. 按钮消抖: 机械按钮在按下和弹起的瞬间,内部的金属触点会发生物理弹跳,导致在几毫秒内电平快速变化多次。如果不处理,代码可能会误判为多次按下。解决方法是在检测到按键动作后,加入一个短暂的延迟(如20-50毫秒),避开抖动期,再读取稳定的状态。这是一个非常重要的实战技巧。

4. 模拟世界的大门:ADC与电压读取

数字信号非0即1,但真实世界是连续的。温度、光线强度、声音大小都是模拟量。微控制器通过**模数转换器(ADC)**来感知这个连续的世界。

4.1 ADC原理与分辨率

ADC就像一个非常快速的“标尺”,它持续测量输入引脚的电压,并将其转换为一个数字值。这个“标尺”的精度就是分辨率,通常用位数表示。CircuitPython中ADC的典型分辨率是16位。

  • 16位分辨率:意味着ADC可以将参考电压(通常是3.3V)划分为 2^16 = 65536 个不同的等级。每个等级代表一个电压值。
  • 转换公式电压值 = (读取的原始值 / 65535) * 参考电压。这就是示例中get_voltage函数所做的事情:(pin.value * 3.3) / 65535

所以,当你旋转电位器,analog_pin.value会在0到65535之间变化,通过上述公式就能换算成0V到3.3V之间的实际电压。电位器在这里充当了一个分压器:旋钮改变中间抽头对地和对电源的电阻比例,从而输出一个0V到3.3V之间可调的电压。

4.2 精度、噪声与滤波

在实际项目中,直接读取一次的ADC值往往是不准确的,会掺杂着电源噪声、数字电路干扰等。你会看到数值在小范围内不停跳动。

提升读数稳定性的技巧

  1. 多次采样取平均:这是最有效的方法。连续读取N次(比如10次),然后计算平均值。
    def read_avg(pin, times=10): sum = 0 for _ in range(times): sum += pin.value return sum / times
  2. 软件滤波:如使用滑动平均滤波或中值滤波算法,能更好地消除偶发的尖峰噪声。
  3. 硬件滤波:在ADC输入引脚和地之间并联一个0.1uF的电容,可以滤除高频噪声。
  4. 注意参考电压:ADC的精度依赖于参考电压的稳定性。如果板载的3.3V稳压器质量一般,或者系统功耗变化大,参考电压可能会波动,影响ADC精度。对于高精度测量,可以考虑使用外部精密基准电压源。

5. 驾驭色彩:NeoPixel编程详解

NeoPixel是Adafruit对WS2812B这类智能RGB LED的商标。单个NeoPixel内部集成了红、绿、蓝三个LED芯片和一个控制芯片,仅通过一根数据线(Din)即可实现级联控制,编程非常灵活。

5.1 颜色模型与亮度控制

NeoPixel使用RGB颜色模型,每个颜色分量(R, G, B)的取值范围是0-255。这为我们提供了256 * 256 * 256 ≈ 1677万种颜色可能。

  • 颜色设置pixel.fill((255, 0, 0))设置为红色。注意这里是元组(R, G, B)
  • 亮度控制pixel.brightness = 0.3。这是一个全局属性,影响该条NeoPixel上所有LED。亮度值范围0.0到1.0。极其重要的注意事项:亮度控制是通过PWM(脉宽调制)在软件层面实现的,即快速开关LED来模拟变暗。这意味着设置brightness=0.5并不会减少LED的电流,在显示纯色时,它仍然消耗着最大亮度时的瞬时电流,只是时间减半。对于电池供电项目,要省电更应该通过直接降低RGB值(如(127,0,0)暗红色)来实现,而非依赖brightness属性。

5.2 制作彩虹与动画效果

示例中的彩虹效果使用了colorwheel函数,它将一个0-255的色轮值映射成一个RGB元组。但制作更复杂的动画,需要理解状态和时间管理。

一个简单的呼吸灯效果实现

import math ... pixel = neopixel.NeoPixel(board.NEOPIXEL, 1) pixel.brightness = 0.1 # 初始亮度设低一点 while True: # 使用正弦函数产生0~1之间平滑变化的亮度系数 for i in range(0, 360, 5): # 0到360度,步进5度 brightness = (math.sin(math.radians(i)) + 1) / 2.0 # 将正弦值(-1~1)映射到0~1 pixel[0] = (int(255 * brightness), 0, 0) # 红色呼吸 # 或者保持颜色,用全局亮度:pixel.brightness = brightness * 0.3 time.sleep(0.02)

这个例子展示了如何利用数学函数创造平滑的过渡效果,而不是生硬地跳变。对于多颗LED的灯带,你可以为每一颗LED维护一个独立的相位或颜色值,从而创造出流水、波浪等复杂动画。

6. I2C总线通信:连接传感器的标准方式

当项目需要连接多个传感器时,像之前那样每个传感器占用几个GPIO引脚的方式很快就会让引脚不够用。I2C总线正是为解决这个问题而生。

6.1 I2C协议精讲:控制器、目标与地址

I2C是一种同步、半双工、多主多从的串行总线。它只需要两根线:

  • SCL(Serial Clock):时钟线,由控制器(通常是你的单片机)产生,用于同步数据。
  • SDA(Serial Data):数据线,用于双向传输数据。

关键特性与实战要点

  1. 开漏输出与上拉电阻:I2C总线上的设备采用“开漏输出”模式。简单理解,设备只能把线拉低(接地),不能主动拉高。总线的高电平状态需要靠上拉电阻(通常4.7kΩ)连接到电源(3.3V)来实现。这就是为什么绝大多数I2C模块(包括Adafruit的)都内置了上拉电阻。如果你自己用芯片搭建电路,或者连接多个模块时发现通信不稳定,首先要检查上拉电阻是否已接(或是否需要加大阻值)。
  2. 7位地址:每个I2C设备都有一个唯一的7位地址(通常会在数据手册中给出)。例如,MCP9808温度传感器的默认地址是0x18(十六进制)。控制器通过发送这个地址来呼叫目标设备。地址范围是0x080x77。你可以使用下面的扫描程序来发现总线上所有设备的地址。
  3. 多设备连接:所有设备的SCL和SDA分别并联在一起,然后接到控制器的SCL和SDA引脚上。只要地址不同,它们就可以和谐共处。

6.2 总线扫描与MCP9808温度传感器实战

在连接一个新传感器之前,进行总线扫描是标准操作,可以确认硬件连接是否正确,以及获取设备的实际地址。

I2C总线扫描代码

import board import busio i2c = busio.I2C(board.SCL, board.SDA) while not i2c.try_lock(): # 尝试获取I2C锁 pass try: print("I2C addresses found:", [hex(addr) for addr in i2c.scan()]) finally: i2c.unlock() # 操作完成后务必解锁!

运行这段代码,如果连接了MCP9808,你会在串行终端看到类似I2C addresses found: ['0x18']的输出。

与MCP9808通信并读取温度: 这里比示例更进一步,我们直接使用adafruit_mcp9808这个针对该传感器的专用库。你需要先将这个库(通常是一个.mpy文件)下载并放入CIRCUITPY盘的lib文件夹中。

import time import board import adafruit_mcp9808 # 创建I2C对象 i2c = board.I2C() # 对于支持`board.I2C()`的板子,这是最简洁的方式 # 或者使用 busio.I2C(board.SCL, board.SDA) 用于自定义引脚 # 创建传感器对象 # 地址0x18是MCP9808的默认地址。如果模块上的地址跳线被改变,则需要相应修改。 sensor = adafruit_mcp9808.MCP9808(i2c, address=0x18) while True: # 读取温度,默认单位是摄氏度 temp_celsius = sensor.temperature print(f"Temperature: {temp_celsius:.2f} C") # 转换为华氏度 temp_fahrenheit = temp_celsius * 9 / 5 + 32 print(f"Temperature: {temp_fahrenheit:.2f} F") print("-" * 20) time.sleep(2)

代码解析与避坑指南

  • board.I2C():这是一个高级封装,它自动使用板子默认的I2C引脚。对于标准板子,这比手动指定board.SCLboard.SDA更可靠。
  • adafruit_mcp9808.MCP9808(i2c):传感器库的核心就是提供一个高级的、面向对象的接口。你不需要知道具体的寄存器地址和读写协议,只需要调用sensor.temperature这个属性即可。库的开发者已经把这些底层细节封装好了。
  • 常见问题1:找不到传感器。首先运行扫描程序,确认地址是否正确、接线是否牢靠(特别是GND一定要共地)、上拉电阻是否工作(模块一般已内置)。其次,检查I2C总线速度,有些老传感器不支持高速模式,可以尝试初始化时指定频率:i2c = board.I2C(frequency=100000)(100kHz)。
  • 常见问题2:读取值不稳定或为None。检查电源是否稳定,传感器可能对电压波动敏感。确保代码中给了传感器足够的初始化时间,可以在import后加一个短暂的time.sleep(0.1)

7. 项目集成与高级技巧:构建一个环境监测站

现在,让我们把前面学到的所有知识整合起来,构建一个简单的综合项目:一个能显示温度(通过I2C的MCP9808)和光线强度(通过模拟输入的电位器模拟)的微型环境监测站,并用板载NeoPixel来直观显示状态(例如,用颜色表示温度范围)。

7.1 系统设计与接线

所需组件

  1. 支持CircuitPython的开发板(如Adafruit Metro M4 Express)
  2. MCP9808高精度I2C温度传感器模块(带STEMMA QT接口)
  3. 10K线性电位器(或光线传感器模块,如ADS1115读取的模拟光敏电阻)
  4. 若干跳线
  5. (可选)外接NeoPixel灯环或灯带,用于更炫酷的显示。

接线

  • MCP9808:使用STEMMA QT线缆直接连接到板子的STEMMA QT端口(通常共享I2C引脚)。如果没有,则手动连接:VIN -> 3.3V, GND -> GND, SCL -> SCL, SDA -> SDA。
  • 电位器:两侧引脚分别接3.3V和GND,中间引脚(滑片)接A0模拟输入引脚。
  • NeoPixel:使用板载的即可。

7.2 集成代码实现

import time import board import analogio import neopixel import adafruit_mcp9808 # 1. 初始化硬件 # 板载NeoPixel pixel = neopixel.NeoPixel(board.NEOPIXEL, 1) pixel.brightness = 0.2 # 模拟光线传感器(用电位器模拟) light_sensor = analogio.AnalogIn(board.A0) # I2C温度传感器 i2c = board.I2C() # 使用默认I2C总线 temperature_sensor = adafruit_mcp9808.MCP9808(i2c) def read_light_level(): """读取光线强度,返回0-100%的百分比""" # 多次采样取平均,减少噪声 samples = 10 raw_sum = 0 for _ in range(samples): raw_sum += light_sensor.value time.sleep(0.001) raw_avg = raw_sum / samples # 将16位ADC值转换为百分比 percentage = (raw_avg / 65535) * 100 return round(percentage, 1) def update_neopixel(temp_c): """根据温度更新NeoPixel颜色""" if temp_c < 20: # 低温,蓝色 color = (0, 0, 255) elif temp_c < 26: # 舒适温度,绿色 color = (0, 255, 0) else: # 高温,红色 color = (255, 0, 0) pixel.fill(color) print("环境监测站启动...") print("==================") while True: try: # 读取温度 temp_c = temperature_sensor.temperature temp_f = temp_c * 9 / 5 + 32 # 读取光线 light = read_light_level() # 更新NeoPixel状态 update_neopixel(temp_c) # 打印到串行终端 print(f"时间: {time.monotonic():.1f}s") print(f"温度: {temp_c:.2f} C | {temp_f:.2f} F") print(f"光线: {light}%") print("-" * 30) except Exception as e: # 异常处理,例如I2C通信错误 print(f"读取传感器时出错: {e}") pixel.fill((255, 255, 0)) # 出错时显示黄色 time.sleep(5) # 出错后等待时间长一些再重试 continue time.sleep(5) # 每5秒采样一次

7.3 代码优化与部署思考

这个项目虽然简单,但已经具备了物联网终端设备的雏形。你可以在此基础上进行扩展:

  1. 数据记录:利用SD卡模块(如示例中所示),将温度和光线数据连同时间戳写入CSV文件,实现长时间的数据记录。
  2. 无线传输:如果使用带有Wi-Fi的开发板(如ESP32-S2/S3),可以集成adafruit_esp32spiwifi库,将数据定期发送到云平台(如Adafruit IO、Thingspeak)或私有服务器。
  3. 低功耗优化:对于电池供电项目,需要优化功耗。主要策略包括:
    • 尽可能使用time.sleep()让主控芯片进入休眠状态。
    • 在不读取时,关闭传感器电源(如果支持)或将其置于低功耗模式。
    • 降低系统时钟频率(CircuitPython支持)。
    • 使用analogio时,读取完成后将引脚对象deinit()掉。
  4. 使用asyncio进行多任务管理:当需要同时控制LED动画、读取多个传感器、响应按钮事件时,一个大的while True循环会变得难以管理。CircuitPython支持asyncio库,可以编写协作式多任务代码,让逻辑更清晰。

8. 调试心法与常见问题速查

嵌入式开发三分靠写,七分靠调。以下是我多年调试CircuitPython项目积累下的经验。

问题1:代码不运行,CIRCUITPY盘符消失或出现boot_out.txt错误

  • 原因code.py中存在语法错误或运行时致命错误,导致CircuitPython崩溃并进入安全模式。
  • 解决:连接串行终端(如Mu Editor),查看输出的错误信息。错误信息会明确指出出错的行和原因。常见的有缩进错误、未导入的模块、名称拼写错误等。修正后保存,板子会自动重启。

问题2:I2C设备扫描不到

  • 检查清单
    1. 供电:确保传感器VIN接3.3V(多数Adafruit模块是3.3V逻辑),GND与主板共地。
    2. 接线:确认SDA、SCL没有接反,接触良好。
    3. 上拉电阻:确认总线上有上拉电阻(模块通常内置)。如果连接线较长或多个设备,可以尝试减小上拉电阻值(如2.2kΩ)以增强驱动能力。
    4. 地址冲突:确保总线上没有两个设备使用相同的I2C地址。有些传感器可以通过焊接跳线来改变地址。
    5. 代码:确认使用了正确的I2C引脚(board.SCL/board.SDA),并且初始化代码(i2c = board.I2C())在创建传感器对象之前执行。

问题3:模拟读数跳动剧烈

  • 解决
    1. 实施软件滤波,如多次采样取平均。
    2. 在ADC输入引脚和GND之间并联一个0.1uF-1uF的陶瓷电容,滤除高频噪声。
    3. 检查电源是否干净,电机、继电器等大功率设备可能会在电源线上产生噪声,考虑为模拟部分使用独立的线性稳压电源或LC滤波电路。

问题4:NeoPixel不亮或颜色异常

  • 检查
    1. 电源:单个NeoPixel在白色全亮时可能消耗60mA电流。多个NeoPixel必须使用外部电源供电,切勿直接从单片机引脚取电,否则会烧毁芯片或导致复位。数据线(Din)仍需连接单片机。
    2. 接地共地:外部电源的地必须与单片机的地连接在一起。
    3. 数据线:NeoPixel对时序要求严格,数据线不宜过长(一般不超过0.5米)。如果必须延长,可以考虑在数据线上串联一个100-500欧姆的电阻,并在NeoPixel数据输入端对地加一个约100pF的电容,以改善信号质量。
    4. 第一个像素点损坏:如果灯带中只有第一个不亮,后面的正常,很可能是第一个像素点被浪涌电流击穿。确保上电顺序稳定,或使用逻辑电平转换器(如果单片机是3.3V而灯带是5V逻辑)。

问题5:内存不足(MemoryError)

  • CircuitPython运行在资源有限的微控制器上,内存(RAM)尤其宝贵。
  • 优化策略
    • 使用.mpy格式的库文件(压缩的字节码),比.py文件更省空间。
    • 避免在循环中创建大型列表或字符串。使用bytearray代替list存储大量数据。
    • 及时使用del删除不再需要的大对象。
    • 如果使用位图或大量字体,考虑将它们存放在SD卡上而非闪存中。

最后,保持耐心,善用串行终端打印调试信息(print是你的好朋友),多查阅官方文档和社区论坛。CircuitPython的生态非常活跃,你遇到的绝大多数问题,很可能已经有人解决并分享出来了。从让一颗LED闪烁,到让传感器网络协同工作,每一步的实践都会让你对硬件和软件如何对话有更深的理解。

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

当机械键盘遇上开源设计:重新定义键帽制造的自由边界

当机械键盘遇上开源设计&#xff1a;重新定义键帽制造的自由边界 【免费下载链接】cherry-mx-keycaps 3D models of Chery MX keycaps 项目地址: https://gitcode.com/gh_mirrors/ch/cherry-mx-keycaps 你是否曾因找不到特定尺寸的键帽而放弃个性化键盘的构想&#xff1…

作者头像 李华
网站建设 2026/5/14 20:34:13

通过curl命令快速测试Taotoken的API密钥与连通性

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 通过curl命令快速测试Taotoken的API密钥与连通性 在将大模型服务集成到应用或脚本之前&#xff0c;一个关键的验证步骤是确认API密…

作者头像 李华