news 2026/5/16 17:03:19

基于CircuitPython与NEC协议的红外通信实践:从遥控器解码到板间双向通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于CircuitPython与NEC协议的红外通信实践:从遥控器解码到板间双向通信

1. 项目概述与红外通信基础

红外通信这玩意儿,听起来挺“复古”,毕竟现在满世界都是Wi-Fi和蓝牙。但如果你玩过嵌入式开发或者物联网硬件,就会知道,在特定的场景下,红外(IR)依然是简单、可靠且成本极低的无线通信方案。它不像射频通信那样需要复杂的配对和协议栈,本质上就是“看得见就通,看不见就断”的光通信,特别适合做简单的遥控、触发和短指令传输。

这次我们用的核心硬件是Adafruit的Circuit Playground Express(后面简称CPX)。这块板子很有意思,它定位是教育板和快速原型开发板,把你能想到的常用传感器、LED、按钮都集成上去了,其中就包含了红外发射管(TX)和接收头(RX)。这意味着你不需要任何额外的杜邦线和模块,拿两块板子就能直接开始玩红外对话。我们的目标很明确:用CircuitPython写代码,让一块CPX的按钮按下时,通过红外光发送一组编码,另一块CPX收到后,能解码并执行相应的动作,比如点亮LED或者播放一个声音。

为什么选NEC编码?这是消费电子领域,特别是电视、空调遥控器里最普及的编码协议之一。它定义了一套清晰的脉冲宽度调制规则,用来表示逻辑“0”和“1”。协议本身不算复杂,但足够稳定,有很多现成的库支持。在CPX上,Adafruit提供的adafruit_irremote库已经帮我们封装好了NEC编码的解析和生成,让我们可以跳过枯燥的底层时序分析,直接关注应用逻辑。

2. 硬件准备与开发环境搭建

2.1 核心硬件清单

要完整复现本文的所有实验,你需要准备以下硬件:

  • Circuit Playground Express开发板 (2块):这是绝对的主角。确保你拿到的是Express版本,而不是经典的Circuit Playground,因为只有Express版本才板载了红外收发器。
  • USB数据线 (2根):用于给CPX供电和上传代码。Type-C接口的线现在更通用。
  • 电脑一台:Windows, macOS, Linux均可,用于编写和上传CircuitPython代码。
  • 可选:Adafruit NEC红外遥控器:型号通常是“Mini Remote Control”。这不是必须的,但有一个实体遥控器来做初步测试和信号抓取,会非常直观,有助于你理解红外通信的“语言”。

两块CPX是必须的,因为我们要实现双向通信演示。如果你只有一块,也可以先用遥控器测试接收功能,但就无法体验完整的“板对板”通信了。

2.2 CircuitPython固件与驱动安装

CPX出厂可能运行其他固件,要使用CircuitPython,第一步是“刷机”。

  1. 下载固件:访问CircuitPython官网,找到Circuit Playground Express的页面,下载最新的.uf2格式固件文件。
  2. 进入引导加载模式:用USB线连接CPX到电脑。先按住板子上的“复位”按钮(Reset),然后快速按一下旁边的“用户”按钮(User),之后松开复位按钮。此时,电脑上会弹出一个名为CPLAYBOOT的U盘。
  3. 刷入固件:将下载好的.uf2文件直接拖入CPLAYBOOTU盘。拖入后,U盘会自动弹出,板子将重启,并出现一个名为CIRCUITPY的新U盘。这表示CircuitPython固件已成功刷入。

CIRCUITPY这个U盘就是你的代码存储和运行空间。之后你所有的Python代码文件都放在这里。

2.3 必要库文件的获取与安装

CircuitPython的强大在于其丰富的库生态。我们需要用到两个核心库:

  1. adafruit_irremote:负责红外信号的编码与解码。
  2. adafruit_circuitplayground:提供访问CPX板载资源(按钮、LED、蜂鸣器等)的高级接口。

获取库文件最简单的方法是访问Adafruit的CircuitPython库包发布页面,下载最新的“Library Bundle”。这个压缩包里包含了所有官方和维护的库。解压后,找到我们需要的两个库文件夹(例如adafruit_irremote.mpyadafruit_circuitplayground),将它们整体复制到CIRCUITPYU盘根目录下的lib文件夹中。如果还没有lib文件夹,就新建一个。

注意:务必确保库的版本与你的CircuitPython固件版本大致匹配。通常下载最新版的库包与最新固件配合使用,可以避免兼容性问题。

3. 红外信号接收:解码遥控器的指令

在让两块板子对话之前,我们先用一个标准的红外遥控器来测试接收功能。这相当于先学会“听”,再学“说”。这个步骤能验证你的红外接收硬件和基础解码代码是否工作正常。

3.1 代码解析:监听与解码

将以下代码保存为CIRCUITPY盘根目录下的code.py,板子会自动运行。

# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries # SPDX-License-Identifier: MIT import pulseio import board import adafruit_irremote # 创建一个脉冲输入对象,监听IR_RX引脚上的信号 # maxlen=120 设定脉冲缓冲区大小,idle_state=True 表示空闲时引脚为高电平 pulsein = pulseio.PulseIn(board.IR_RX, maxlen=120, idle_state=True) # 创建一个通用解码器对象 decoder = adafruit_irremote.GenericDecode() while True: # 读取一组脉冲 pulses = decoder.read_pulses(pulsein) try: # 尝试将脉冲解码为数字码 received_code = decoder.decode_bits(pulses) except adafruit_irremote.IRNECRepeatException: # 捕获到NEC重复码(短码),通常为长按按钮时发出,此处忽略 # print("NEC repeat!") continue except adafruit_irremote.IRDecodeException as e: # 解码失败,可能是信号失真或非NEC编码 # print("Failed to decode: ", e.args) continue print("NEC红外编码接收: ", received_code) # 根据接收到的编码执行不同动作(以Adafruit迷你遥控器为例) if received_code == [255, 2, 255, 0]: print("接收到 NEC 音量-") if received_code == [255, 2, 127, 128]: print("接收到 NEC 播放/暂停") if received_code == [255, 2, 191, 64]: print("接收到 NEC 音量+")

代码关键点解读:

  • PulseIn对象:这是底层硬件抽象。红外接收头输出的是一连串高低电平变化的脉冲,PulseIn负责精确记录每个高电平和低电平的持续时间(以微秒为单位)。
  • GenericDecode:这个解码器默认适配NEC编码的脉冲时序。它知道如何将一串时长不同的脉冲,解析成对应的逻辑位(0和1),并最终组合成我们看到的4字节数组(如[255, 2, 191, 64])。
  • 异常处理:这是工业级代码的体现。IRNECRepeatException处理的是遥控器长按时发出的“重复码”,它是一个简短的信号,告诉设备“继续执行上一个指令”。IRDecodeException则处理所有其他解码错误,比如信号受到日光灯干扰、距离太远信号弱、或者根本不是NEC协议。

3.2 实操测试与信号抓取

  1. 将代码上传到CPX后,打开串行监视器(推荐使用Mu编辑器,它内置了串行监视器,或者使用Thonny、VS Code with CircuitPython插件)。
  2. 将遥控器对准CPX板子中心附近的红外接收头(标有RX)。
  3. 按下遥控器上的按键。你会在串行监视器中看到类似NEC红外编码接收: [255, 2, 191, 64]的输出,以及对应的按钮提示。
  4. 重要技巧:如果你使用的是其他品牌遥控器,可能无法直接显示按钮名称。但解码出的数组(如[12, 34, 56, 78])就是该按键的“身份证”。记录下每个按键对应的数组,你就可以在代码的if语句中替换它们,从而自定义你的遥控器映射。

避坑指南:测试时,确保没有强烈的直射光源(特别是白炽灯、太阳光)照射在红外接收头上,这些光源包含丰富的红外光谱,会产生噪声脉冲,导致解码失败。在室内日光灯环境下通常问题不大。

4. 红外信号发射:让CPX成为发送端

学会了“听”,现在来学“说”。我们将编写另一段代码,让CPX通过板载按钮来触发红外信号的发射,模拟一个遥控器。

4.1 代码解析:编码与发射

将以下代码保存到第二块CPX的code.py中。

# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries # SPDX-License-Identifier: MIT import time from adafruit_circuitplayground.express import cpx import adafruit_irremote import pulseio import board # 创建一个脉冲输出对象,用于从IR_TX引脚发射38KHz载波的红外信号 # 38KHz是红外遥控的通用载波频率,duty_cycle=2**15设置50%占空比 pulseout = pulseio.PulseOut(board.IR_TX, frequency=38000, duty_cycle=2 ** 15) # 创建一个通用编码器,配置为NEC协议格式 encoder = adafruit_irremote.GenericTransmit( header=[9000, 4500], # NEC起始信号:9ms高脉冲,4.5ms低脉冲 one=[560, 1700], # 逻辑“1”:560us高,1700us低 zero=[560, 560], # 逻辑“0”:560us高,560us低 trail=0 # 结束后的低电平时间 ) while True: if cpx.button_a: print("按钮 A 被按下!\n") cpx.red_led = True # 点亮红色LED作为发送指示 # 发射代表“音量-”的NEC编码 encoder.transmit(pulseout, [255, 2, 255, 0]) cpx.red_led = False time.sleep(0.2) # 短暂延时,防止按键抖动和给接收端处理时间 if cpx.button_b: print("按钮 B 被按下!\n") cpx.red_led = True # 发射代表“音量+”的NEC编码 encoder.transmit(pulseout, [255, 2, 191, 64]) cpx.red_led = False time.sleep(0.2)

核心原理剖析:

  • PulseOut与载波:红外发射二极管不能直接发送数字信号。它需要被一个38KHz的方波(载波)“调制”。PulseOut对象在后台以38KHz的频率快速开关红外管。当我们调用transmit时,编码器控制的是这个38KHz载波的“包络”,即让载波按照NEC协议规定的时序(如9ms有载波,4.5ms无载波)来发射。人眼看不见38KHz的闪烁,但红外接收头专门检测这个频率,从而过滤掉环境中的其他红外噪声。
  • GenericTransmit参数:这行代码是NEC协议的“密码本”。
    • header:起始信号,像一个大声的“注意!我要开始说话了!”,帮助接收端同步。
    • onezero:定义了逻辑1和逻辑0的脉冲宽度组合。NEC协议使用“脉冲位置调制”,通过低电平的持续时间来区分1和0(1700us vs 560us)。
    • trail:单个信号结束后的低电平时间。
  • 四字节数据[255, 2, 191, 64]这样的四字节数组是NEC协议的标准数据格式。它通常包含地址码、命令码及其反码,用于错误校验。在实验中,我们直接使用了从遥控器抓取到的完整四字节码。

4.2 双向通信测试

现在你有了两块CPX:一块运行3.1节的接收代码(接收端),一块运行上面的发射代码(发射端)。

  1. 用两根USB线将两块板子分别连接到电脑,打开两个串行监视器窗口。
  2. 将两块板子的红外发射头和接收头大致对准(不需要非常精确,有一定角度容差)。
  3. 按下发射端(第二块板子)的A按钮。你会在发射端的串口看到按钮 A 被按下!,同时在接收端的串口看到NEC红外编码接收: [255, 2, 255, 0]接收到 NEC 音量-
  4. 这个实验验证了从编码、发射、空中传输到接收、解码的完整链路是通的。红色LED的闪烁提供了直观的发送视觉反馈。

5. 红外信号应用:触发多彩互动反馈

通信链路打通了,接下来就是赋予它实际意义。我们将升级接收端的代码,让它在收到特定红外指令时,不再只是在串口打印文字,而是驱动板载的NeoPixel LED和蜂鸣器做出反应。

5.1 代码解析:从信号到动作

替换第一块CPX(接收端)上的code.py为以下代码:

# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries # SPDX-License-Identifier: MIT import pulseio import board import adafruit_irremote from adafruit_circuitplayground.express import cpx pulsein = pulseio.PulseIn(board.IR_RX, maxlen=120, idle_state=True) decoder = adafruit_irremote.GenericDecode() while True: pulses = decoder.read_pulses(pulsein) try: received_code = decoder.decode_bits(pulses) except adafruit_irremote.IRNECRepeatException: continue except adafruit_irremote.IRDecodeException as e: continue print("NEC红外编码接收: ", received_code) if received_code == [255, 2, 255, 0]: # 对应发射端按钮A print("接收到按钮A信号") # 将所有10个NeoPixel LED设置为紫色 (R, G, B) cpx.pixels.fill((130, 0, 100)) cpx.pixels.brightness = 0.1 # 可选:调低亮度保护眼睛 if received_code == [255, 2, 191, 64]: # 对应发射端按钮B print("接收到按钮B信号") # 关闭所有LED cpx.pixels.fill((0, 0, 0)) # 播放一个262Hz(中央C)的音调,持续1秒 cpx.play_tone(262, 1)

应用层设计要点:

  • 事件驱动:整个程序的核心是一个等待红外信号的循环。一旦解码成功,就进入if判断分支,执行对应的动作。这是一种典型的事件驱动编程模型,在物联网和交互设备中非常常见。
  • 资源调用cpx.pixels.fill()cpx.play_tone()adafruit_circuitplayground库提供的上层API,它隐藏了控制WS2812 LED和压电蜂鸣器的底层PWM细节,让我们用一行代码就能实现复杂功能。
  • 扩展性:这两个if分支就是你的“动作触发器”。你可以轻松地扩展它:
    • cpx.pixels[i] = (R, G, B)控制单个LED。
    • cpx.start_tone(freq)cpx.stop_tone()实现更灵活的声音控制。
    • 引入adafruit_motor库来控制舵机转动。
    • 甚至可以通过import storageaudioio来播放存储在本地的WAV文件。

5.2 项目构思与扩展

现在,你已经掌握了一个完整的无线触发与控制链路。可以构思一些有趣的项目:

  • 无线门铃/提醒器:将发射端放在门口,接收端放在书房。按下按钮,书房里的CPX亮灯并响铃。
  • 简易遥控小车:用CPX控制一个小车底盘(需连接电机驱动模块)。按钮A/B分别控制左转/右转或前进/后退。
  • 智能家居触发器:接收端收到信号后,通过串口或Wi-Fi模块(如AirLift)向家庭服务器发送一个HTTP请求,从而打开智能灯或播放音乐。
  • 多人游戏控制器:制作多个发射端,用一个接收端来判断谁先按下按钮,实现抢答器功能。

关键在于,红外通信在这里扮演了一个低成本、低延迟、高可靠性的私有无线触发通道的角色。对于不需要传输大量数据(如图像、音频流),只需要发送简单控制指令的应用,它是绝佳选择。

6. 深度调试与常见问题排查

在实际操作中,你可能会遇到信号收不到、解码错误等问题。下面是一个基于经验的排查清单,可以帮助你快速定位问题。

现象可能原因排查步骤与解决方案
串口无任何输出1. 代码未运行
2. 串口监视器未正确连接
3. 板子供电不足
1. 检查CIRCUITPY盘根目录下是否有code.py,确认板载LED是否正常亮起。
2. 关闭并重新打开串口监视器,确认选择了正确的串口端口(如COM3,/dev/ttyACM0)。
3. 尝试更换USB线或连接到电脑不同USB口,确保供电稳定。
按下遥控器/按钮,串口有输出但解码失败1. 环境红外干扰强
2. 距离太远或角度偏差太大
3. 发射与接收未对准
4. 电池电量不足(遥控器)
1. 移至远离窗户、白炽灯的环境下测试。
2. 将遥控器或发射板靠近接收头(10厘米内)再试。
3. 确保发射管的TX与接收头的RX大致在同一直线上。
4. 更换遥控器电池。
接收端持续打印乱码或重复解码1. 环境光(如日光灯)产生稳定干扰脉冲
2. 接收端代码中未正确过滤重复码
1. 这是最常见的问题。尝试用手或纸板在接收头周围稍作遮挡,看乱码是否停止。如果停止,说明环境光干扰严重,需为接收头加装遮光罩(如一小段热缩管)。
2. 确认代码中IRNECRepeatException异常已被捕获并continue
发射端LED亮,但接收端无反应1. 发射端代码载波频率设置错误
2. 接收头损坏或型号不匹配(非38KHz)
3. 发射的数据编码与接收端预期不匹配
1. 确认发射代码中PulseOutfrequency=38000
2. 这是硬件问题。CPX板载的接收头是标准的38KHz一体化接收头,一般不会坏。可用手机摄像头(大部分手机摄像头能感应红外光)对准发射管,按下按钮时观察摄像头屏幕,应能看到发射管发出微弱的紫白色光点。
3. 检查发射encoder.transmit中的数据数组是否与接收端if判断里的数组完全一致。
通信距离非常短(<10厘米)1. 发射管驱动电流不足
2. 接收头灵敏度下降
1. CPX板载的红外发射管驱动能力有限,这是其物理限制。如需增加距离,可以考虑外接一个红外发射模块,并用三极管放大驱动电流。
2. 检查接收头前是否有污渍遮挡。

一个高级调试技巧:原始脉冲分析如果问题复杂,可以修改接收端代码,在decoder.read_pulses(pulsein)之后,直接打印pulses这个列表。这个列表里存储的是原始的高电平、低电平持续时间(微秒)。你可以将这个数据复制出来,与标准的NEC协议时序(起始头9ms/4.5ms,逻辑1为560us/1700us,逻辑0为560us/560us)进行对比。这能帮你判断是发射的信号本身就不对,还是在传输过程中受到了干扰。

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

为Claude Code配置Taotoken密钥与模型以解决访问限制

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 为Claude Code配置Taotoken密钥与模型以解决访问限制 Claude Code 作为一款高效的编程助手&#xff0c;其原生服务在某些网络环境下…

作者头像 李华
网站建设 2026/5/16 17:03:01

RT-Thread睿擎派硬件生态解析:从MIPI屏到4G模块的工业应用实战

1. 项目概述&#xff1a;为睿擎派注入“灵魂”的硬件生态最近在捣鼓RT-Thread的睿擎派开发板&#xff0c;发现官方悄咪咪地上新了一整套硬件配件&#xff0c;从显示、视觉到联网、调试&#xff0c;几乎把工业应用里最核心的外设都给配齐了。这感觉就像你买了一台性能不错的主机…

作者头像 李华
网站建设 2026/5/16 17:02:17

从MultiWii到BetaFlight:MSP协议演进史与V2版本升级避坑指南

从MultiWii到BetaFlight&#xff1a;MSP协议演进史与V2版本升级避坑指南 十年前&#xff0c;当第一批DIY无人机爱好者还在用Arduino和加速度计拼凑飞行控制器时&#xff0c;没人能预料到那个简陋的串口通信协议会成为今天FPV生态系统的基石。MSP协议就像一位沉默的翻译官&#…

作者头像 李华
网站建设 2026/5/16 16:59:37

GHelper终极指南:3步解决华硕笔记本性能控制难题

GHelper终极指南&#xff1a;3步解决华硕笔记本性能控制难题 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Expertb…

作者头像 李华
网站建设 2026/5/16 16:59:23

Nacos权限体系实战:从零构建用户、角色与资源隔离

1. 为什么需要Nacos权限体系 最近在帮一家电商公司做微服务改造&#xff0c;他们遇到一个典型问题&#xff1a;十几个项目组共用同一个Nacos服务&#xff0c;结果A组误删了B组的配置&#xff0c;线上直接炸锅。这种场景下&#xff0c;资源隔离和权限控制就成了刚需。 Nacos的权…

作者头像 李华
网站建设 2026/5/16 16:59:15

如何在3分钟内安装并使用VideoDownloadHelper下载任何网页视频?

如何在3分钟内安装并使用VideoDownloadHelper下载任何网页视频&#xff1f; 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 您是否经常遇到想…

作者头像 李华