百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

MicroPython蓝牙BLE例程实操(三)

bigegpt 2024-09-05 12:13 3 浏览

前两节中,我们首先直接用实际例程,体验了MicroPython中蓝牙BLE的实际运行效果,接着分析了其主要的程序代码逻辑,详见[MicroPython蓝牙BLE例程实操(一) ]和[MicroPython蓝牙BLE例程实操(二) ]。

其中,构建蓝牙BLE广播报文的函数——advertising_payload是在单独的模块(.py文件)中自己实现的。由此,也从另外一个角度反映出,MicroPython只提供了蓝牙BLE的“低阶”接口:“低阶”到广播报文都需要自己实现,而非系统模块集成。当然,考虑到MicoPython作为一个通用的平台,又运行于资源有限的微控制器上,其只负责最核心的功能,而将业务场景有关的代码实现剥离出来由用户自己实现,似乎也是无可厚非的了。

两个蓝牙设备想要建立连接,首先需要外设设备向外广播,然后中心设备才能搜索到该设备,再发起连接请求。外设设备的广播报文中包含设备的相关信息,比如设备名称,设备具有的服务UUID等等,中心设备可以根据这些信息决定其是不是自己关心的设备,以及要不要发起对该设备的连接请求。

基本的广播数据包格式如下:

每个广播数据包都是31字节,数据包中又分为有效(significant)数据部分和无效(non-significant)数据部分。其中,无效数据部分全为零,仅仅是为了凑够31字节而存在。而有效数据部分,又由若干个数据单元组成,每个数据单元的格式为:

1字节长度+n字节(通常也是1字节)类型+n字节类型特定数据

这里的类型,用于表明这个数据单元代表什么,比如标记(Flag),设备名,或者UUID等等。我们来看例程中是怎么实现的:

from micropython import const
import struct
import bluetooth

# Advertising payloads are repeated packets of the following form:
#   1 byte data length (N + 1)
#   1 byte type (see constants below)
#   N bytes type-specific data

#常量定义
_ADV_TYPE_FLAGS = const(0x01)
_ADV_TYPE_NAME = const(0x09)
_ADV_TYPE_UUID16_COMPLETE = const(0x3)
_ADV_TYPE_UUID32_COMPLETE = const(0x5)
_ADV_TYPE_UUID128_COMPLETE = const(0x7)
_ADV_TYPE_UUID16_MORE = const(0x2)
_ADV_TYPE_UUID32_MORE = const(0x4)
_ADV_TYPE_UUID128_MORE = const(0x6)
_ADV_TYPE_APPEARANCE = const(0x19)


#构建广播数据包
def advertising_payload(limited_disc=False, br_edr=False, name=None, services=None, appearance=0):
    payload = bytearray()

    #构建广播数据单元
    def _append(adv_type, value):
        nonlocal payload
        payload += struct.pack("BB", len(value) + 1, adv_type) + value

    #标记类型数据单元
    _append(
        _ADV_TYPE_FLAGS,
        struct.pack("B", (0x01 if limited_disc else 0x02) + (0x18 if br_edr else 0x04)),
    )

    #设备名类型数据单元
    if name:
        _append(_ADV_TYPE_NAME, name)

    #服务UUID类型数据单元
    if services:
        for uuid in services:
            b = bytes(uuid)
            if len(b) == 2:
                _append(_ADV_TYPE_UUID16_COMPLETE, b)
            elif len(b) == 4:
                _append(_ADV_TYPE_UUID32_COMPLETE, b)
            elif len(b) == 16:
                _append(_ADV_TYPE_UUID128_COMPLETE, b)

    #外观类型数据单元
    # See org.bluetooth.characteristic.gap.appearance.xml
    if appearance:
        _append(_ADV_TYPE_APPEARANCE, struct.pack("<h", appearance))

    return payload

Python中struct模块对应C语言中的结构体,其可将结构体中的数据打包成字节串。比如上述代码中,struct.pack函数第一个参数为格式字符串,其表示该结构体是如何构建的:"B"表示其中包含一字节数据,而"BB"则表示其中包含两字节数据。上述代码中,定义了_append子函数用于构建一个一个的广播数据单元,其格式正好与前面提到的标准广播数据单元的格式相符合。

上述广播数据包中,包含的第一个数据单元为“标记(Flag)”类型数据单元。蓝牙BLE可用该类型数据单元表明设备是有限可发现(LE Limited Discoverable)还是普通可发现(General Discoverable),还可表明设备是支持双模(同时支持经典蓝牙和蓝牙BLE)还是不支持经典蓝牙(BR/EDR模式),仅支持蓝牙BLE。之后,依据参数传递情况决定是否加入设备名数据单元,服务UUID数据单元,和设备外观数据单元。

(:所谓有限可发现是指该设备发送广播报文时,只是在每个周期中的一段时间之内广播,其余时间不广播。而常规可发现是指该设备没有时间限制,一直发送广播报文。)

设备名比较容易理解,上述服务UUID的定义中都有COMPLETE后缀,那么还有对应INCOMPLETE的类型吗?确实如此。如果该设备有两个服务,只广播了其中一个,那么就是INCOMPLETE的,否则,就是COMPLETE的。那外观(APPEARANCE)又是什么呢,其实就是告诉接收广播报文的设备,本设备应该用什么样的图标外观进行显示,是用一个耳机的图标,还是用一个HID键盘的图标等等。

如此看来,蓝牙规范确实详实啊,基本所有细节都有考虑!这也是其保证各种设备能够广泛兼容的一个措施:自定义功能特性越少,越能保证规范范围内各设备之间的兼容性。

该模块中还有其它函数如下:

#解析payload中指定adv_type的内容
def decode_field(payload, adv_type):
    i = 0
    result = []
    while i + 1 < len(payload):
        if payload[i + 1] == adv_type:
            result.append(payload[i + 2 : i + payload[i] + 1])
        i += 1 + payload[i]
    return result
    
#解析设备名,以字符串形式返回
def decode_name(payload):
    n = decode_field(payload, _ADV_TYPE_NAME)
    return str(n[0], "utf-8") if n else ""

#解析服务
def decode_services(payload):
    services = []
    for u in decode_field(payload, _ADV_TYPE_UUID16_COMPLETE):
        services.append(bluetooth.UUID(struct.unpack("<h", u)[0]))
    for u in decode_field(payload, _ADV_TYPE_UUID32_COMPLETE):
        services.append(bluetooth.UUID(struct.unpack("<d", u)[0]))
    for u in decode_field(payload, _ADV_TYPE_UUID128_COMPLETE):
        services.append(bluetooth.UUID(u))
    return services

#构造单元测试函数,以验证本模块其它函数工作是否正常
def demo():
    payload = advertising_payload(
        name="micropython",
        services=[bluetooth.UUID(0x181A), bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")],
    )
    print(payload)
    print(decode_name(payload))
    print(decode_services(payload))

#作为模块单独运行时的主入口
if __name__ == "__main__":
    demo()

上述函数基本都是配合advertising_payload函数,用于验证其是否工作正常。如果直接运行该模块,__name__系统变量为__main__,从最后面的代码来看,则直接运行的是demo函数,其使用advertising_payload函数构造了一个广播报文,并进行各个域的解析,以验证所构造的报文是否正确。从单元测试的角度来讲,这也可以说是Python编程中一种比较常规的范式了。

相关推荐

Linux gron 命令使用详解(linux gminer)

简介gron是一个独特的命令行工具,用于将JSON数据转换为离散的、易于grep处理的赋值语句格式。它的名字来源于"grepableon"或"grepable...

【Linux】——从0到1的学习,让你熟练掌握,带你玩转Linu

学习Linux并掌握Java环境配置及SpringBoot项目部署是一个系统化的过程,以下是从零开始的详细指南,帮助你逐步掌握这些技能。一、Linux基础入门1.安装Linux系统选择发行版:推荐...

Linux常用的shell命令汇总(linux中shell的作用)

本文介绍Linux系统下常用的系统级命令,包括软硬件查看、修改命令,有CPU、内存、硬盘、网络、系统管理等命令。说明命令是在Centos6.464位的虚拟机系统进行测试的。本文介绍的命令都会在此C...

零成本搭建个人加密文件保险柜(适用于 Win11 和 Linux)

不依赖收费软件操作简单,小白也能跟着做支持双系统,跨平台使用实现数据加密、防删除、防泄露内容通俗无技术门槛,秒懂秒用使用工具简介我们将使用两个核心工具:工具名用途系统支持Veracrypt创建加密虚...

如何在 Linux 中使用 Gzip 命令?(linux怎么用gzip命令)

gzip(GNUzip)是Linux系统中一个开源的压缩工具,用于压缩和解压缩文件。它基于DEFLATE算法,广泛应用于文件压缩、备份和数据传输。gzip生成的文件通常带有.gz后缀,压缩效率...

Linux 必备的20个核心知识点(linux内核知识点)

学习和使用Linux所必备的20个核心知识点。这些知识点涵盖了从基础操作到系统管理和网络概念,是构建扎实Linux技能的基础。Linux必备的20个知识点1.Linux文件系统层级标...

谷歌 ChromeOS 已支持 7z、iso、tar 文件格式

IT之家6月21日消息,谷歌ChromeOS在管理文件方面进行了改进,新增了对7z、iso和tar等格式的支持。从5月的ChromeOS101更新开始,ChromeOS...

如何在 Linux 中提取 Tar Bz2 文件?

在深入解压方法之前,我们先来了解.tar.bz2文件的本质。.tar.bz2是一种组合文件格式,包含两个步骤:Tar(TapeArchive):tar是一种归档工具,用于将多个文件或目录打包...

如何在 CentOS 7/8 上安装 Kitematic Docker 管理器

Kitematic是一款流行的Docker图形界面管理平台,适用于Ubuntu、macOS和Windows操作系统。然而,其他发行版(如CentOS、OpenSUSE、Fedora、R...

Nacos3.0重磅来袭!全面拥抱AI,单机及集群模式安装详细教程!

之前和大家分享过JDK17的多版本管理及详细安装过程,然后在项目升级完jdk17后又发现之前的注册和配置中心nacos又用不了,原因是之前的nacos1.3版本的,版本太老了,已经无法适配当前新的JD...

爬虫搞崩网站后,程序员自制“Zip炸弹”反击,6刀服务器成功扛住4.6万请求

在这个爬虫横行的时代,越来越多开发者深受其害:有人怒斥OpenAI的爬虫疯狂“偷”数据,7人团队十年心血的网站一夜崩溃;也有人被爬虫逼到极限,最后只好封掉整个巴西的访问才勉强止血。但本文作者却走...

Ubuntu 操作系统常用命令详解(ubuntu必学的60个命令)

UbuntuLinux是一款流行的开源操作系统,广泛应用于服务器、开发、学习等场景。命令行是Ubuntu的灵魂,也是高效、稳定管理系统的利器。本文按照各大常用领域,详细总结Ubuntu必学...

Linux面板8.0.54 测试版-已上线(linux主机面板)

Linux面板8.0.54测试版【增加】[网站]Java项目新增刷新列表按钮【增加】[网站]PHP项目-Apache-服务新增守护进程功能【增加】[网站]Python项目创建/删除网站时新增同时创建...

开源三剑客——构建私有云世界的基石

公共云原生的浪潮正在席卷这个世界,亚马逊AWS、谷歌GCP和微软的Azure年收入增长超过了30%,越来越多的公司和个人开始将自己的服务部署到云环境中,大型数据中心的规模经济带来了成本的降低,可以在保...

2.2k star,一款业界领先的私有云+在线文档管理系统

简介kodbox可道云(原KodExplorer)是业内领先的企业私有云和在线文档管理系统,为个人网站、企业私有云部署、网络存储、在线文档管理、在线办公等提供安全可控,简便易用、可高度定制的私有云产品...