嘿,各位蓝牙玩家、开发大神们~ 今天来聊个超实用的技能:在 Quecpython 平台上用 SPP 模式玩转蓝牙!不管你是想做无线串口通信,还是搞设备间数据互传,这篇指南都能让你秒变 “蓝牙达人”,小白也能轻松上手~

先搞懂:蓝牙 SPP 到底是个啥?

其实呀,SPP(Serial Port Profile,串口协议)就是经典蓝牙(Bluetooth BR/EDR)里的 “无线串口”!它能让两个设备像接了根无形的数据线一样,通过蓝牙模拟传统的 RS232 串口通信。简单说:有线串口能做的,它用蓝牙无线也能做,是不是很神奇?

SPP 能玩出哪些花样?

这可太多了!日常开发和生活中到处都能见到它的身影:

👉 蓝牙串口模块:比如咱们熟悉的 HC-05、HC-06、JDY-08,插上单片机就能用 SPP 无线传数据,告别杂乱的电线~
 👉 车载诊断:汽车 OBD2 读码器就是靠 SPP 和手机连接,实时读取车速、油耗这些数据,修车师傅的好帮手!
 👉 蓝牙打印机:超市小票、快递标签打印机,用 SPP 无线接收打印指令,摆在哪里都方便~
 👉 工业设备:工厂里的 PLC、单片机,不用布线就能通过 SPP 传数据,维护起来超省心!
 👉 日常小场景:比如蓝牙耳机(音频数据传输)、设备间传图片 / 文件,甚至自定义设备界面的 logo,都能靠它实现~

而且哦,SPP 支持最高 3M 速率传输,速度杠杠的!

哪些模组能玩 SPP?

Quecpython 平台下,这些 4G 模组都支持蓝牙 SPP,手里有的小伙伴可以直接开工:
 EC200U、EC600U、EG912U、EG915ULA_AB、EG915UEU_AB
 (没有的也别慌,先马住攻略,以后说不定就用上啦~)

划重点:Quecpython 开发 SPP 有多简单?

最爽的是,在 Quecpython 平台上玩蓝牙 SPP,根本不用啃复杂的底层协议栈!就像搭积木一样,调用几个简单的接口,分分钟搞定连接、传数据~ 是不是超省心?

手把手教你:蓝牙 SPP 初始化 + 通信全流程

下面咱们一步步来,从初始化到收发数据,跟着做就能搞定~

第一步:准备工作 —— 初始化蓝牙

首先得让蓝牙 “醒过来”,还得给它配个 “消息通知员”(回调函数),蓝牙有啥动静(比如连上了、收到数据了)都会通过它告诉你~

核心接口:bt.init(user_cb)

● 作用:初始化蓝牙,注册回调函数

● 回调函数(user_cb):蓝牙事件的 “消息中心”,比如 “蓝牙启动了”“收到数据了” 都会在这里通知你~

第二步:认识蓝牙的 “小动作”——

 事件 ID

蓝牙工作时会有各种 “状态通知”,咱们得知道这些通知代表啥,比如:

● BT_START_STATUS_IND(0):蓝牙启动了

● BT_SPP_CONNECT_IND(61):SPP 连接成功啦

● BT_SPP_RECV_DATA_IND(14):收到数据啦

● BT_SPP_DISCONNECT_IND(62):连接断开了

这些就像蓝牙的 “表情”,看懂了就能知道它在干嘛~

第三步:代码示例 —— 从启动到传数据全流程

咱们直接上代码,每一步都给你标清楚,小白也能看懂~

# 导入蓝牙模块,用于蓝牙相关操作
import bt
# 导入时间模块,用于延时等时间操作
import utime
# 导入多线程模块,用于创建事件处理线程
import _thread
# 从队列模块导入Queue,用于存储蓝牙事件消息
from queue import Queue
# 定义蓝牙设备名称,其他设备可通过该名称搜索到本设备
BT_NAME = 'QuecPython-SPP'
# 定义蓝牙事件字典,键为事件描述,值为事件ID,用于识别不同蓝牙事件
BT_EVENT = {
    'BT_START_STATUS_IND': 0,         # 蓝牙/ble启动状态通知
    'BT_STOP_STATUS_IND': 1,          # 蓝牙/ble停止状态通知
    'BT_SPP_INQUIRY_IND': 6,          # 蓝牙SPP查询到设备通知
    'BT_SPP_INQUIRY_END_IND': 7,      # 蓝牙SPP查询结束通知
    'BT_SPP_RECV_DATA_IND': 14,       # 蓝牙SPP接收数据通知
    'BT_SPP_CONNECT_IND': 61,         # 蓝牙SPP连接状态通知
    'BT_SPP_DISCONNECT_IND': 62,      # 蓝牙SPP断开连接通知
}
# 目标设备信息字典,存储要连接的设备名称和蓝牙地址
DST_DEVICE_INFO = {
    'dev_name':'iQOO Neo7 SE',# 要连接的目标设备蓝牙名称
    'bt_addr': None  # 目标设备蓝牙地址,初始为None,搜索到后赋值
}
# 蓝牙运行状态标记:0-未运行,1-运行中
BT_IS_RUN = 0
# 创建消息队列,最大容量30,用于缓存蓝牙回调函数传来的事件消息
msg_queue = Queue(30)
# 蓝牙回调函数:当蓝牙有事件发生时,系统会调用该函数
# 参数args:事件相关参数,包含事件ID、状态等信息
def bt_callback(args):
    global msg_queue
    # 将事件参数放入消息队列,供事件处理线程处理
    msg_queue.put(args)
# 打开/usr/test.txt文件,模式为a+(追加+可读),用于存储接收的蓝牙数据
f=open('usr/test.txt','a+')
# 蓝牙事件处理线程函数:循环从消息队列获取事件并处理
def bt_event_proc_task():
    # 声明使用全局变量
    global msg_queue
    global BT_IS_RUN
    global DST_DEVICE_INFO
    while True:
        print('wait msg...')
        # 从消息队列获取消息,若无消息则阻塞等待
        msg = msg_queue.get()
        # 解析事件ID(第一个参数为事件ID)
        event_id = msg[0]
        # 解析状态(第二个参数为状态:0-成功,非0-失败)
        status = msg[1]
        # 处理蓝牙启动状态通知事件
        if event_id == BT_EVENT['BT_START_STATUS_IND']:
            print('event: BT_START_STATUS_IND')
            # 若启动成功
            if status == 0:
                print('BT start successfully.')
                # 更新蓝牙运行状态为运行中
                BT_IS_RUN = 1
                print('Set BT name to {}'.format(BT_NAME))
                # 设置蓝牙设备名称
                retval = bt.setLocalName(0, BT_NAME)
                if retval != -1:
                    print('BT name set successfully.')
                else:
                    print('BT name set failed.')
                    # 若设置名称失败,停止蓝牙
                    bt.stop()
                    continue
                # 设置蓝牙可见模式(3表示可见)
                retval = bt.setVisibleMode(3)
                if retval == 0:
                    # 获取当前可见模式并验证
                    mode = bt.getVisibleMode()
                    if mode == 3:
                        print('BT visible mode set successfully.')
                    else:
                        print('BT visible mode set failed.')
                        bt.stop()
                        continue
                else:
                    print('BT visible mode set failed.')
                    bt.stop()
                    continue
                # 以下为查询设备相关代码(默认注释,取消注释可启用)
                # retval = bt.startInquiry(15)
                # if retval != 0:
                #     print('Inquiry error.')
                #     bt.stop()
                #     continue
            # 若启动失败
            else:
                print('BT start failed.')
                bt.stop()
                continue
        # 处理蓝牙停止状态通知事件
        elif event_id == BT_EVENT['BT_STOP_STATUS_IND']:
            print('event: BT_STOP_STATUS_IND')
            if status == 0:
                # 更新蓝牙运行状态为未运行
                BT_IS_RUN = 0
                print('BT stop successfully.')
            else:
                print('BT stop failed.')
            # 以下为释放SPP和蓝牙资源的代码(默认注释)
            # retval = bt.sppRelease()
            # if retval == 0:
            #     print('SPP release successfully.')
            # else:
            #     print('SPP release failed.')
            # retval = bt.release()
            # if retval == 0:
            #     print('BT release successfully.')
            # else:
            #     print('BT release failed.')
            # break
            # 延时5秒后重新启动蓝牙
            utime.sleep(5)
            bt.start()
            bt.setVisibleMode(3)
        # 处理SPP查询到设备通知事件
        elif event_id == BT_EVENT['BT_SPP_INQUIRY_IND']:
            print('event: BT_SPP_INQUIRY_IND')
            if status == 0:
                # 解析信号强度(第三个参数为rssi)
                rssi = msg[2]
                # 解析设备名称(第五个参数为设备名称)
                name = msg[4]
                # 解析设备地址(第六个参数为设备地址)
                addr = msg[5]
                # 格式化MAC地址(转换为xx:xx:xx:xx:xx:xx格式)
                mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0])
                print('name: {}, addr: {}, rssi: {}'.format(name, mac, rssi))
                # 若查询到目标设备
                if name == DST_DEVICE_INFO['dev_name']:
                    print('The target device is found, device name {}'.format(name))
                    # 记录目标设备地址
                    DST_DEVICE_INFO['bt_addr'] = addr
                    # 取消查询(已找到目标设备)
                    retval = bt.cancelInquiry()
                    if retval != 0:
                        print('cancel inquiry failed.')
                        continue
            else:
                print('BT inquiry failed.')
                bt.stop()
                continue
        # 处理SPP接收数据通知事件
        elif event_id == BT_EVENT['BT_SPP_RECV_DATA_IND']:
            # print('event: BT_SPP_RECV_DATA_IND')
            # 若接收数据成功
            if status == 0:
                # 解析接收的数据(第四个参数为数据)
                data = msg[3]
                # 将数据写入文件
                f.write(data)
                print('recvbytes data: {}'.format(data))
                # 以下为发送响应数据的代码(默认注释,取消注释可启用)
                # send_data = 'I have received the data you sent.'
                # print('send data: {}'.format(send_data))
                # retval = bt.sppSend(send_data)
                if retval != 0:
                    print('send data faied.')
            else:
                print('Recv data failed.')
                bt.stop()
                continue
        # 处理SPP连接通知事件
        elif event_id == BT_EVENT['BT_SPP_CONNECT_IND']:
            print('event: BT_SPP_CONNECT_IND')
            # 若连接成功
            if status == 0:
                # 解析连接状态(第三个参数为连接状态)
                conn_sta = msg[2]
                # 解析设备地址(第四个参数为设备地址)
                addr = msg[3]
                # 格式化MAC地址
                mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0])
                print('SPP connect successful, conn_sta = {}, addr {}'.format(conn_sta, mac))
            else:
                print('Connect failed.')
                bt.stop()
                continue
        # 处理SPP断开连接通知事件
        elif event_id == BT_EVENT['BT_SPP_DISCONNECT_IND']:
            print('event: BT_SPP_DISCONNECT_IND')
            # 解析连接状态(第三个参数为连接状态)
            conn_sta = msg[2]
            # 解析设备地址(第四个参数为设备地址)
            addr = msg[3]
            # 格式化MAC地址
            mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0])
            print('SPP disconnect successful, conn_sta = {}, addr {}'.format(conn_sta, mac))
            # 关闭文件
            f.close()
            bt.stop()
            continue
# 主函数:初始化并启动蓝牙SPP
def main():
    global BT_IS_RUN
    # 启动蓝牙事件处理线程
    _thread.start_new_thread(bt_event_proc_task, ())
    # 初始化蓝牙(注册回调函数)
    retval = bt.init(bt_callback)
    if retval == 0:
        print('BT init successful.')
    else:
        print('BT init failed.')
        return -1
    # 初始化SPP
    retval = bt.sppInit()
    if retval == 0:
        print('SPP init successful.')
    else:
        print('SPP init failed.')
        return -1
    # 启动蓝牙
    retval = bt.start()
    if retval == 0:
        print('BT start successful.')
    else:
        print('BT start failed.')
        # 若启动失败,释放SPP资源
        retval = bt.sppRelease()
        if retval == 0:
            print('SPP release successful.')
        else:
            print('SPP release failed.')
        return -1
    # 循环监测蓝牙运行状态
    count = 0
    while True:
        utime.sleep(1)
        count += 1
        # 获取当前时间
        cur_time = utime.localtime()
        # 格式化时间戳(时:分:秒)
        timestamp = "{:02d}:{:02d}:{:02d}".format(cur_time[3], cur_time[4], cur_time[5])
        # 每5秒打印一次运行状态
        if count % 5 == 0:
            if BT_IS_RUN == 1:
                print('[{}] BT SPP is running, count = {}......'.format(timestamp, count))
                print('')
            else:
                print('BT SPP has stopped running, ready to exit.')
                break
# 若脚本作为主程序运行,则执行main函数
if __name__ == '__main__':
main()

第四步:手机配合测试 —— 秒变

 “蓝牙数据中转站”

代码跑起来后,咱们用手机配合测试一下:

1、手机下载 “blue-spp” 软件(专门玩 SPP 的工具)

2、打开手机蓝牙,搜索到咱们设置的蓝牙名(比如 “QuecPython-SPP”)

3、连接成功后,在手机上输入点文字,模组就能收到啦~反过来模组也能发数据给手机

亲测有效,数据嗖嗖传,比插线方便多了!

图片

图片

图片

总结一下:SPP 模式超实用,上手超简单!

今天的蓝牙 SPP 小课堂就到这里~ 简单说:

● SPP 是经典蓝牙的 “无线串口”,能模拟有线通信,传数据超方便

● 支持 EC200U、EC600U 等多款模组,手头有的赶紧试

● Quecpython 平台调用接口就行,不用啃底层,小白也能玩

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐