无线遥控:接收和解码射频信号代码详解——小白篇

目录

功能描述

应用场景

工作原理

举例说明

小白须知

1. 无线电基础知识

2. ESP32 基础知识

3. 时间与脉冲的概念

4. 字节与位操作

5. 中断与回调函数

6. 基本的 Python 语法

7. 硬件连接

8. 调试与测试

9. 协议和数据包结构

进一步学习建议


之前介绍了【嵌入式设备】蓝牙鼠标遥控器-CSDN博客新产品,里面涉及到有一部分代码,想单独写一篇文章讲一下相关的知识点,作为知识储备。

先上代码(跟之前文章的代码一模一样)

from machine import Pin, Timer
import time

# 配置 GPIO 引脚用于接收 433MHz 射频信号
_ASK_MIN_BYTE_LEN_ = 3                  # 最小字节长度
_ASK_MAX_BYTE_LEN_ = 3                  # 最大字节长度
_ASK_MIN_NEW_FRAM_DETECT_TIME_ = 5000   # 最小帧检测时间间隔(微秒)
_ASK_TOLERANCE_ = 0.9                   # 脉冲宽度的容忍度

# 计算完整的 ASK 数据位长度(16 bits = 2 bytes + 1 start bit)
asklen = (_ASK_MAX_BYTE_LEN_ * 16 + 1)  
datalen_max = (_ASK_MAX_BYTE_LEN_ * 8)  # 射频数据最大位长度
datalen_min = (_ASK_MIN_BYTE_LEN_ * 8)  # 射频数据最小位长度

# 用于检测信号是否开始与结束的标志
detect_begin = False                    # 是否接收到射频开始信号
detect_end = False                      # 射频数据是否接收完成

# 用于存储脉冲时间长度的缓冲区
buffer_int = []
ask_time = 0                            # 记录上一个时间戳(微秒)


# 中断处理器,用于检测 433MHz 射频信号的变化
def irq_handler(pin):
    global buffer_int, ask_time, detect_begin, detect_end
    tackus = time.ticks_us()   # 获取当前时间戳(微秒)

    if not detect_end:  # 如果接收未完成
        dt = time.ticks_diff(tackus, ask_time)  # 计算脉冲间隔
        if not detect_begin:  # 检测是否是新的信号帧
            if dt > _ASK_MIN_NEW_FRAM_DETECT_TIME_ and pin.value():
                detect_begin = True   # 信号开始
                buffer_int = []       # 清空缓冲区
        else:
            if dt > _ASK_MIN_NEW_FRAM_DETECT_TIME_ and pin.value():
                # 如果达到信号结束条件且缓冲区长度符合
                if len(buffer_int) == asklen:
                    detect_end = True
                else:
                    detect_begin = False  # 重置检测开始标志
                    buffer_int = []       # 清空缓冲区
            else:
                buffer_int.append(dt)  # 记录脉冲时间

    ask_time = tackus  # 更新上一个时间戳


# 初始化射频接收引脚
def ask_init(pin):
    rf_pin = Pin(pin, Pin.IN)  # 将指定引脚设为输入模式
    rf_pin.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=irq_handler)  # 绑定中断处理器


# 解码逻辑,用于将接收到的脉冲时间转化为字节数据
def decodeData(buffer):
    # 初始化高低电平时间范围
    buffer_time_high = [0, 0]
    buffer_time_low = [0, 0]

    # 判断初始的两个脉冲时间,确定高低电平
    if buffer[0] > buffer[1]:
        buffer_time_high = [buffer[0] - int(_ASK_TOLERANCE_ * buffer[0]), buffer[0] + int(_ASK_TOLERANCE_ * buffer[0])]
        buffer_time_low = [buffer[1] - int(_ASK_TOLERANCE_ * buffer[1]), buffer[1] + int(_ASK_TOLERANCE_ * buffer[1])]
    elif buffer[0] < buffer[1]:
        buffer_time_high = [buffer[1] - int(_ASK_TOLERANCE_ * buffer[1]), buffer[1] + int(_ASK_TOLERANCE_ * buffer[1])]
        buffer_time_low = [buffer[0] - int(_ASK_TOLERANCE_ * buffer[0]), buffer[0] + int(_ASK_TOLERANCE_ * buffer[0])]
    else:
        return None  # 如果两个时间相等,返回 None

    data_bit = 0  # 当前解码的位位置
    data_byte = [0, 0, 0]  # 存储解码后的字节数据
    i = 0
    index = len(buffer)

    # 遍历缓冲区,按位解码数据
    while i < index - 1:
        if (buffer[i] > buffer_time_low[0] and buffer[i] < buffer_time_low[1] and
                buffer[i+1] > buffer_time_high[0] and buffer[i+1] < buffer_time_high[1]):
            data_bit += 1  # 检测到 '0' 位
        elif (buffer[i+1] > buffer_time_low[0] and buffer[i+1] < buffer_time_low[1] and
              buffer[i] > buffer_time_high[0] and buffer[i] < buffer_time_high[1]):
            data_byte[data_bit // 8] |= 0x80 >> (data_bit % 8)  # 设置对应的 '1' 位
            data_bit += 1
        else:
            break  # 遇到无效脉冲对,退出循环

        i += 2  # 每次处理两个脉冲

    # 校验数据位的有效性
    if data_bit % 8 != 0 or data_bit > datalen_max or data_bit < datalen_min:
        return None  # 位数不符合长度要求,返回 None

    return data_byte  # 返回解码后的字节数组


# 接收数据函数,用于检测数据是否接收完成并解码
def reciveData():
    global detect_begin, detect_end, buffer_int
    if detect_end:  # 如果接收结束
        dat = decodeData(buffer_int)
        detect_end = False
        detect_begin = False
        return dat  # 返回解码后的数据
    return None  # 数据未接收完成时返回 None


# 主函数,初始化接收并进入循环
def main():
    global detect_begin, detect_end, buffer_int
    ask_init(16)  # 初始化 GPIO16 引脚为接收引脚

    while True:
        time.sleep_ms(1)  # 短暂延迟,防止 CPU 过载
        if detect_end:  # 检测到接收结束
            print(len(buffer_int), buffer_int)  # 打印缓冲区长度和脉冲时间
            dat = decodeData(buffer_int)
            if dat:  # 打印解码后的字节数据
                print('data:%02x%02x%02x' % (dat[0], dat[1], dat[2]))
            detect_end = False
            detect_begin = False
            time.sleep_ms(100)  # 等待 100 毫秒以防止重复触发

# 入口点,执行主函数
if __name__ == '__main__':
    main()

这段代码是用于接收和解码 433MHz ASK(Amplitude Shift Keying)射频信号 的。

具体来说,它的功能包括:

功能描述

  1. 接收 433MHz 射频信号

    • 使用 ESP32 的一个 GPIO 引脚连接到 433MHz 射频接收模块。
    • 当射频信号接收到数据时,引脚的电平发生变化(上升或下降),触发中断。
  2. 记录脉冲时间

    • 中断处理器 irq_handler 用于检测信号的变化并记录每个脉冲的时间间隔。
    • 这些脉冲时间间隔代表了数据的 "0" 和 "1" 位信息。
  3. 解码数据

    • 在接收到完整的数据包后,decodeData 函数分析脉冲时间间隔,根据高低电平的持续时间判断二进制数据。
    • 解码后的数据存储在字节数组中。
  4. 输出结果

    • 主程序 main 持续运行检测是否接收到完整的数据包。
    • 如果接收完成并且解码成功,程序会打印解码后的数据(以十六进制格式输出)。

应用场景

该代码主要用于以下应用场景:

  1. 遥控信号解码

    可用于接收和解码常见的 433MHz 遥控器(如车库门遥控器、无线开关等)发出的信号,识别按键操作。
  2. 无线传感器数据接收

    许多无线传感器(如温湿度传感器)也使用 433MHz 进行数据传输,代码可以用来接收这些传感器的信号。
  3. 简单的无线通信

    在 DIY 项目中,可以使用 433MHz 发射器和接收器进行简单的无线数据传输,例如无线遥控器、无线报警系统等。

工作原理

  1. 接收信号

    • ESP32 通过 GPIO 引脚连接 433MHz 接收模块。
    • 中断处理器 irq_handler 记录信号上升和下降沿的时间间隔。
  2. 检测信号开始与结束

    • 当检测到信号电平变化且间隔时间超过设定值(_ASK_MIN_NEW_FRAM_DETECT_TIME_),认为是新的一帧开始。
    • 持续记录脉冲时间,直到信号结束。
  3. 解码脉冲

    • 根据时间间隔区分高电平和低电平的脉冲。
    • 将脉冲序列转化为二进制数据,再解析为字节形式。
  4. 输出数据

    打印出接收到的完整数据包,用于后续处理或调试。

举例说明

假设你有一个 433MHz 的无线遥控器(例如,控制 LED 灯开关的遥控器)。当你按下遥控器上的某个按钮时,它会发送对应的 433MHz ASK 信号。ESP32 通过接收模块捕捉到该信号,并通过上述代码记录并解码按钮按下时发送的数据包。

  • 用户按下按钮:遥控器发送 ASK 信号。
  • ESP32 捕获信号:中断处理器记录脉冲时间。
  • 解码信号:解码函数将脉冲时间转换为按键对应的数据。
  • 输出结果:ESP32 打印出对应的按钮编码(如 data: 0x1a2b3c)。

这类应用通常出现在无线遥控设备的反向工程、信号分析和 DIY 项目中。


小白须知

如果你是最最基础第一次了解这个的小白,可以通过以下内容来了解这方面的相关内容

1. 无线电基础知识

  • 什么是 433MHz
    • 433MHz 是一种常见的无线射频(RF)通信频率,属于 ISM(工业、科学、医学)频段。许多低功耗的无线设备,如遥控器、无线门铃、无线传感器等,使用这个频段进行通信。
  • ASK 调制(Amplitude Shift Keying)
    • ASK 是一种调制方式,通过改变载波信号的振幅来表示二进制数据。例如:
      • 高振幅(1):表示二进制 "1"
      • 低振幅(0):表示二进制 "0"
    • 接收端通过检测信号振幅的变化来还原原始的二进制数据。

2. ESP32 基础知识

  • GPIO 引脚
    • ESP32 的 GPIO(通用输入输出引脚)可以配置为输入模式,用于接收外部信号。代码中 ask_init(16) 是初始化 GPIO16 为输入引脚,用于连接 433MHz 接收模块。
  • 中断
    • 中断是一种机制,当外部信号(如引脚电平变化)发生时,立即执行预设的函数(称为中断处理器)。irq_handler 就是中断处理器,它在引脚电平发生变化时被触发,用于记录脉冲时间。

3. 时间与脉冲的概念

  • 脉冲信号
    • 在 ASK 调制中,数据通过一系列高低电平的脉冲传输。每对脉冲的持续时间代表 "0" 或 "1"。
  • 时间间隔
    • 通过记录脉冲上升和下降沿的时间间隔,可以确定接收到的信号是 "0" 还是 "1"。
  • 时间戳与微秒
    • ESP32 使用 time.ticks_us() 获取当前时间戳(微秒),通过计算时间差来记录脉冲间隔。

4. 字节与位操作

  • 字节与位
    • 计算机中的数据通常以字节为单位(1 字节 = 8 位)。在接收和解码过程中,需要逐位解析脉冲时间并将其转换为字节数据。
  • 位操作
    • 在代码中,使用位运算(如 |= 0x80 >> (data_bit % 8)))将接收到的位数据保存到字节数组中。位操作是处理低层次数据的一种常用方法。

5. 中断与回调函数

  • 中断(Interrupts)
    • 在程序中,中断是一种事件驱动机制。通常,ESP32 处于等待状态(或执行其他任务),一旦接收到射频信号(电平变化),中断触发器会调用 irq_handler 进行处理,而不会等待主程序检查输入信号。
  • 回调函数
    • irq_handler 是中断的回调函数,当 GPIO 引脚检测到上升沿或下降沿时,该函数会被调用执行。

6. 基本的 Python 语法

  • 函数定义
    • 代码中使用了多个函数(如 ask_initdecodeDatareciveData),每个函数执行特定的任务,使用 def 关键字定义。
  • 循环与条件判断
    • while True 是一个无限循环,用于持续检查是否接收到完整数据包。if 语句用于条件判断,例如检查 detect_end 是否为 True
  • 格式化输出
    • print('data:%02x%02x%02x' % (dat[0], dat[1], dat[2])) 是一种格式化输出,将数据以十六进制形式打印出来。

7. 硬件连接

  • ESP32 与 433MHz 接收模块的连接
    • 需要将 433MHz 接收模块的数据引脚连接到 ESP32 的 GPIO 引脚(例如 GPIO16)。
    • 接收模块通常有 3 个引脚:VCC(电源)、GND(地)和 DATA(数据输出)。
    • 供电时,接收模块会检测空气中的 433MHz ASK 信号,并在 DATA 引脚上输出相应的高低电平变化。

8. 调试与测试

  • 使用串口监视器
    • 使用 ESP32 进行调试时,通常通过串口监视器查看 print 输出的日志,检查接收到的脉冲时间和解码结果。
  • 信号捕捉
    • 可以使用无线遥控器或 433MHz 发射模块测试该代码的功能,观察接收到的数据是否与预期一致。

9. 协议和数据包结构

  • 协议理解
    • 不同的设备和遥控器可能使用不同的数据包协议。该代码中假设每个数据包为 3 字节(24 位)的固定长度。
    • 学习了解常见的 433MHz 协议(如 PT2262、EV1527)会有助于进一步调试和扩展应用。

进一步学习建议

  • Python 编程:熟悉 Python 的基本语法、函数、循环、条件判断、位操作等。
  • ESP32 开发:学习 ESP32 的基础开发,包括 GPIO 配置、中断使用等。
  • 无线通信:学习无线射频通信的基本原理、调制解调方式(如 ASK、FSK)。
  • 电子硬件:了解基本的电路连接和使用,如 ESP32 与 433MHz 接收模块的连接。

通过掌握这些基础知识,您可以更好地理解并改进这段 433MHz 射频信号接收与解码的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cici_ovo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值