清泛IT社区

搜索
App Inventor 2 中文社区 轻松创建Apps

扫码访问移动社区 移动社区,您的掌上技术专家

关注我,精彩不错过! 关注我,精彩不错过!

扫码安装最新版AI伴侣 最新版AI伴侣v2.69

Aia Store .aia 源码一站式解决方案 发布日志AI2连接测试ai2Starter模拟器

开通VIP送小红花App Inventor 2 拓展有奖征文 VIP会员享专有教程,免费赠送基础版*技术支持服务! AI2入门必读中文文档中文教程IoT专题

查看: 526|回复: 0
打印 上一主题 下一主题

[经验分享] 【BLE技术内幕】BLE技术揭秘

  • TA的每日心情
    奋斗
    4 小时前
  • 签到天数: 93 天

    [LV.6]常住居民II

    295

    主题

    471

    帖子

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    10862

    AI2中文网VIP弹球达人接水果达人撸猫达人

    跳转到指定楼层
    楼主
    发表于 2024-02-05 16:37:49 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
    文章源自:http://doc.iotxx.com/index.php?title=BLE技术揭秘

    BLE技术揭秘


    BLE是低功耗蓝牙的英文缩写(Bluetooth Low Energy),是蓝牙4.0版本起开始支持的新的、低功耗版本的蓝牙技术规范。
    蓝牙技术联盟(Bluetooth SIG)在2010年发布了跨时代的蓝牙4.0,它并不是蓝牙3.0的简单升级版本,而是全新的技术架构,蓝牙4.0版本分两种模式:单模蓝牙和双模蓝牙。
    常见的蓝牙音箱,是典型的双模蓝牙,它需要传输大量的音频数据。而小米手环,蓝牙温度计则属于单模蓝牙。行业里一般不讲单模蓝牙,而是统一称为低功耗蓝牙。
    如今,蓝牙5.0已经发布和应用,4倍通信速度、2倍的通信距离以及Mesh组网特性,将使蓝牙成为物联网领域的重要的技术之一。
    本文我们将由表及里,由浅入深,全方位的揭秘低功耗蓝牙技术。

    2 BLE特点
    低功耗蓝牙瞄准多个市场,特别是移动智能终端,智能家居,互联设备等领域,主要特点包括:
    • 低功耗,使用纽扣电池就可以运行数月至数年。
    • 快连接,毫秒级的连接速度,传统蓝牙甚至长达数分钟。
    • 远距离,长达数百米的通信距离,而传统蓝牙通常10米左右。
    蓝牙联盟沿用经典蓝牙的规范内容,为低功耗蓝牙定义了一些标准Profile,Profile理解为数据规范,只要遵守该规范,任意厂家的蓝牙设备,均可以相互连接与通信,例如无线蓝牙键盘鼠标,无论是安卓或是iOS还是Windows,均是即插即用,这便是“标准”的力量。低功耗蓝牙支持的标准Profile有:
    • HID,用于无线鼠标,键盘或其他遥控设备。
    • BatteryServices,电池状态服务,用于告知电池电量状态。
    • HRP,心率计Profile,用于心率采集。等等。
    另外,低功耗蓝牙还可以自定义Profile,伴随着智能手机的发展和普及,低功耗蓝牙的这个特性得到了发扬光大,同时也拓宽了低功耗蓝牙的应用领域。例如,可以自定义一个开关量的Profile,数据01表示开灯,数据00表示关灯,然后手机发送数据01和00就可以控制灯的亮和灭。类似的应用案例有很多,下面总结应用特点
    • 支持自定义Profile,可以收发任意格式的数据,如01和00
    • 支持自定义设备,支持任意设备的连接和通信,例如智能蓝牙插座等。

    提示:低功耗蓝牙的Profile均基于GATT(通用属性规范,后面会详解)之上,如HID over GATT。也就是说,经典蓝牙中的HID规范与低功耗蓝牙中的HID规范用的是两个不同的通道。

    3.1 角色
    BLE设备角色主要分为两种角色,主机(Master或Central)和从机(Peripheral),当主机和从机建立连接之后才能相互收发数据
    • 主机,主机可以发起对从机的扫描连接。例如手机,通常作为BLE的主机设备
    • 从机,从机只能广播并等待主机的连接。例如智能手环,是作为BLE的从机设备
    另外还有观察者(Observer)和广播者(Broadcaster),这两种角色不常使用,但也十分有用,例如iBeacon,就可以使用广播者角色来做,只需要广播特定内容即可。
    • 观察者,观察者角色监听空中的广播事件,和主机唯一的区别是不能发起连接,只能持续扫描从机。
    • 广播者,广播者可以持续广播信息,和从机的唯一区别是不能被主机连接,只能广播数据
    蓝牙协议栈没有限制设备的角色范围,同一个BLE设备,可以作为主机,也可以作为从机,我们称之为主从一体,主从一体的好处是,每个BLE设备都是对等的,可以发起连接,也可以被别人连接,更加实用。
    3.3 扫描
    扫描是主机监听从机广播数据包和发送扫描请求的过程,主机通过扫描,可以获取到从机的广播包以及扫描回应数据包,主机可以对已扫描到的从机设备发起连接请求,从而连接从机设备并通信。
    扫描动作有两个比较重要的时间参数:扫描窗口和扫描间隔,如果扫描窗口等于扫描间隔,那么主机将一直处于扫描状态之中,持续监听从机广播包。

    • 被动扫描,主机监听广播信道的数据,当接收到广播包时,协议栈将向上层(也就是应用层,用户可编程)传递广播包。
    • 主动扫描,主动扫描除了完成被动扫描的动作外,还会向从机发送一个扫描请求,从机收到该请求时,会再次发送一个称作扫描回应的广播包。
    所以,主动扫描比被动扫描,可以多收到扫描回应数据包。
    3.4.1 连接参数
    Connection Interval连接间隔,两次连接事件之间的时间间隔称为连接间隔。1.25 ms为单位,范围从最小值7.5 ms到最大值4.0 s
    Slave Latency从机延迟,如果从机没有要发送的数据,则可以跳过连接事件,继续保持睡眠节省电量。
    Supervision Time-out监控超时,是两次成功连接事件之间的最长时间。如果在此时间内没有成功的连接事件,设备将终止连接并返回到未连接状态。该参数值以10 ms为单位,监控超时值可以从最小值10(100 ms)到3200(32.0 s)。超时必须大于有效的连接间隔。

    3.4.3 有效连接间隔
    Effective Connection Interval有效连接间隔等于两个连接事件之间的时间跨度,假设从机跳过最大数量的连接事件,且允许从机延迟(如果从机延迟设置为0,则有效连接间隔等于实际连接间隔,)。
    从机延迟表示可以跳过的最大事件数。该数字的范围可以从最小值0(意味着不能跳过连接事件)到最大值499。最大值不能使有效连接间隔(见下列公式)大于16秒。间隔可以使用以下公式计算:
    Effective Connection Interval = (Connection Interval) × (1 + [Slave Latency])
    Consider the following example:
    • Connection Interval: 80 (100 ms)
    • Slave Latency: 4
    • Effective Connection Interval: (100 ms) × (1 + 4) = 500 ms
    当没有数据从从机发送到主机时,从机每500ms一个连接事件交互一次。
    3.4.5 连接参数的优化考量
    在许多应用中,从机跳过最大连接事件数。选择正确的连接参数组在低功耗蓝牙设备的功率优化中起重要作用。以下列表给出了连接参数设置中权衡的总体概述。
    减少连接间隔如下:
    • 增加两个设备的功耗
    • 增加双向吞吐量
    • 减少任一方向发送数据的时间
    增加连接间隔如下:
    • 降低两个设备的功耗
    • 降低双向吞吐量
    • 增加任一方向发送数据的时间
    减少从机延迟(或将其设置为零)如下:
    • 增加外围设备的功耗
    • 减少外围设备接收从中央设备发送的数据的时间
    增加从机延迟如下:
    • 在周边没有数据发送期间,可以降低外设的功耗到主机设备
    • 增加外设设备接收从主机设备发送的数据的时间
    3.6 断开
    主机或从机都可以发起断开连接请求,对方会收到该请求,然后断开连接恢复连接前的状态。
    3.7.1 步骤1:上电初始化
    主机、从机上电后(不分先后顺序),首先进行协议栈初始化和相关功能调用,如下图所示。

    • 主机设备,主机初始化时,需要设置设备类型,设置用于扫描的相关参数,初始化GATT等协议相关的参数。(下一章节详细介绍何为GATT)
    • 从机设备,从机初始化时,需要设置设备名称,广播相关参数,从机Profile等。从机一般会立即开启广播,也可以等待一个事件来触发广播,例如按键触发。
    3.7.3 步骤3:发现从机设备
    当主机扫描到从机时,可以返回已扫描到的从机相关信息,例如可以提取到下图中的从机设备名称,从机MAC地址,从机的RSSI信号值等数据。
    因此,有些应用在从机的广播包或者扫描回应包中添加自定义字段,这样就可以被主机通过扫描的方式拿到数据。

    3.7.5 步骤5:成功连接从机
    当从机收到连接请求后,双方成功建立连接,此时双方的状态均变为已连接状态。
    然后主机可以调用协议栈提供的接口函数来获取从机的服务。

    3.7.7 步骤7:成功获取服务
    如下图所示,主机成功获取到从机的服务,例如获取到UUID为0xFFF0的Services,该Service有两个特征值,分别是具有读写属性的0xFFF1,以及具有通知属性的0xFFF2。
    读写属性是指主机可以读写该特征值的内容。而通知属性是指从机可以通过该特征值向主机发送数据。

    3.7.9 步骤9:从机向主机发送数据
    从机可以通过Norify的方式主动向主机发送数据,例如下图,从机通过特征值0xFFF2发送了一条Notify通知,数据内容为:1234

    3.7.11 步骤11:成功断开连接
    从机收到主机发来的断开请求,此刻状态变为已断开。

    4.1 功能框图
    在本节中,我们列举两家典型的蓝牙芯片厂家:TI和Noridc,来深入了解低功耗蓝牙协议栈。
    下图是TI的CC26系列芯片协议栈结构图,

    下图是Nordic的nRF52系列芯片的协议栈结构图。

    4.2.1 控制器Controller
    • Physical Layer,简称:PHY,物理层。PHY层用来指定BLE所用的无线频段,调制解调方式和方法等。PHY层做得好不好,直接决定整个BLE芯片的功耗,灵敏度以及selectivity等射频指标。
    • Link Layer,简称:LL,链路层。LL层是整个BLE协议栈的核心,也是BLE协议栈的难点和重点。像Nordic的BLE协议栈能同时支持20个link(连接),就是LL层的功劳。LL层要做的事情非常多,比如具体选择哪个射频通道进行通信,怎么识别空中数据包,具体在哪个时间点把数据包发送出去,怎么保证数据的完整性,ACK如何接收,如何进行重传,以及如何对链路进行管理和控制等等。LL层只负责把数据发出去或者收回来,对数据进行怎样的解析则交给上面的GAP或者ATT
    • Host Controller Interface,简称:HCI。协议栈应用开发中,我们会经常看到HCI的身影,它对上Host提供Controller的功能接口,所以称作Host Controller Interface。
    4.2.3 应用Application
    应用层是用户开发实际蓝牙应用的地方,包含必要的协议栈参数设置,以及各种功能函数的调用。我们分别从蓝牙从机和蓝牙主机两种设备来分析。
    • 蓝牙从机
      • 相关硬件和基础服务初始化
      • 设置广播参数:广播数据,广播间隔,扫描回应等参数或者数据。
      • 设置Profile:添加从机服务、特征是,还有设置回调函数用于接收主机数据等。
      • 设置绑定管理参数(可选)
      • 启动广播,开始运行。
      • 等待相关事件,及事件处理,例如收到主机发来的数据,被链接等等。
    • 蓝牙主机
      • 相关硬件和基础服务初始化
      • 设置扫描参数。
      • 设置连接参数。
      • 设置绑定管理参数(可选)
      • 启动协议栈,开始运行。
      • 等待相关事件,及事件处理,例如扫描事件,从机的Notify事件等等。

    5.1 GAP和GATT区别
    区分GAP和GATT很重要。
    • GAP  定义了 BLE网络堆栈的一般拓扑。
    • GATT  详细描述了一旦设备建立连接后如何传输属性(数据)。
    GATT特别关注如何根据其描述的规则格式化打包和发送数据。在BLE网络堆栈中,属性协议(ATT)与GATT紧密对齐,GATT直接位于ATT的顶部。GATT实际上使用ATT来描述如何从两个连接的设备交换数据。
    5.2.1 建立联系(Connecting)
    设备可以通过采用GAP中指定的以下角色来加入BLE网络:
    A、广播(Broadcasting):这些角色不必显式地相互连接即可传输数据。
    • 广播者(Broadcaster):广播公共数据包的设备,例如可以广播按下按钮的时间。
    • 观察者(Observer):侦听广播者发送的广告包中数据的设备。广播者和观察者之间没有任何连接。/2、/2
    B、连接(Connecting):这些角色必须显式连接和握手才能传输数据。这些角色比广播角色更常用。
    • 从机设备(Peripheral):  通过广播,告知其他设备自己的存在,以便主机设备可以建立连接。连接后,从机设备不再向其他主机设备广播数据,而是保持与主机设备的连接。
      • 从机设备功耗低,因为它们只需要定期发送信标即可。主机设备负责开始与从机设备的通信。
      • 手环是BLE外设的一个示例。

    • 主机设备(Central):一种通过侦听广播包来启动与从机设备的连接的设备。主机设备可以连接到许多其他从机设备。
      • 当主机设备要连接时,它将请求连接数据包发送到从机设备。如果从机设备接受来自主机设备的请求,则建立连接。
      • 当您的手机连接到手环时,就是BLE Central设备的一个示例。

    5.3 通用属性配置文件(GATT)5.3.2 客户端与服务端的关系
    一个示例如下:手环采集了心跳信息,希望计算机读取该信息。手环充当服务端并提供信息。手机充当客户端,读取该信息。
    GAP和GATT模型角色基本上彼此独立从机设备或主机设备都可以充当服务端或客户端,这取决于数据的流动方式。
    在一般的主从机通信时,主机可以通过读写从机的属性,实现接收和发送数据给从机,从机可以通过发送通知的方式实现与主机的通信。因此,一般从机是作为GATT的服务端,主机作为GATT的客户端。
    6.1 发送数据包
    假设有设备A和设备B,设备A要把自己的电量状态83%(十六进制表示为0x53)发给设备B,该怎么做呢?作为一个开发者,他希望越简单越好,对他而言,他希望调用一个简单的API就能完成这件事,比如send(0x53),实际上我们的BLE协议栈就是这样设计的,开发者只需调用send(0x53)就可以把数据发送出去了,其余的事情BLE协议栈帮你搞定。很多人会想,BLE协议栈是不是直接在物理层就把0x53发出去,就如下图所示:

    这种方式初看起来挺美的,但由于很多细节没有考虑到,实际是不可行的。首先,它没有考虑用哪一个射频信道来进行传输,在不更改API的情况下,我们只能对协议栈进行分层,为此引入LL层,开发者还是调用send(0x53),send(0x53)再调用send_LL(0x53,2402M)(注:2402M为信道频率)。这里还有一个问题,设备B怎么知道这个数据包是发给自己的还是其他人的,为此BLE引入access address概念,用来指明接收者身份,其中,0x8E89BED6这个access address比较特殊,它表示要发给周边所有设备,即广播。如果你要一对一的进行通信(BLE协议将其称为连接),即设备A的数据包只能设备B接收,同样设备B的数据包只能设备A接收,那么就必须生成一个独特的随机access address以标识设备A和设备B两者之间的连接。
    6.3 连接方式
    到底什么叫连接(connection)?像有线UART,很容易理解,就是用线(Rx和Tx等)把设备A和设备B相连,即为连接。用“线”把两个设备相连,实际是让2个设备有共同的通信媒介,并让两者时钟同步起来。蓝牙连接有何尝不是这个道理,所谓设备A和设备B建立蓝牙连接,就是指设备A和设备B两者一对一“同步”成功,其具体包含以下几方面:
    • 设备A和设备B对接下来要使用的物理信道达成一致
    • 设备A和设备B双方建立一个共同的时间锚点,也就是说,把双方的时间原点变成同一个点
    • 设备A和设备B两者时钟同步成功,即双方都知道对方什么时候发送数据包什么时候接收数据包
    • 连接成功后,设备A和设备B通信流程如下所示:

    如上图所示,一旦设备A和设备B连接成功(此种情况下,我们把设备A称为Master或者Central,把设备B称为Slave或者Peripheral),设备A将周期性以CI(connection interval)为间隔向设备B发送数据包,而设备B也周期性地以CI为间隔打开射频接收窗口以接收设备A的数据包。同时按照蓝牙spec要求,设备B收到设备A数据包150us后,设备B切换到发送状态,把自己的数据发给设备A;设备A则切换到接收状态,接收设备B发过来的数据。由此可见,连接状态下,设备A和设备B的射频发送和接收窗口都是周期性地有计划地开和关,而且开的时间非常短,从而大大降低系统功耗并大大提高系统效率。
    现在我们看看连接状态下是如何把数据0x53发送出去的,从中大家可以体会到蓝牙协议栈分层的妙处。
    • 对开发者来说,很简单,他只需要调用send(0x53)
    • GATT层定义数据的类型和分组,方便起见,我们用0x0013表示电量这种数据类型,这样GATT层把数据打包成130053(小端模式!)
    • ATT层用来选择具体的通信命令,比如读/写/notify/indicate等,这里选择notify命令0x1B,这样数据包变成了:1B130053
    • L2CAP用来指定connection interval(连接间隔),比如每10ms同步一次(CI不体现在数据包中),同时指定逻辑通道编号0004(表示ATT命令),最后把ATT数据长度0x0004加在包头,这样数据就变为:040004001B130053
    • LL层要做的工作很多,首先LL层需要指定用哪个物理信道进行传输(物理信道不体现在数据包中),然后再给此连接分配一个Access address(0x50655DAB)以标识此连接只为设备A和设备B直连服务,然后加上LL header和payload length字段,LL header标识此packet为数据packet,而不是control packet等,payload length为整个L2CAP字段的长度,最后加上CRC24字段,以保证整个packet的数据完整性,所以数据包最后变成:
      • AAAB5D65501E08040004001B130053D550F6
        • AA – 前导帧(preamble)
        • 0x50655DAB – 访问地址(access address)
        • 1E – LL帧头字段(LL header)
        • 08 – 有效数据包长度(payload length)
        • 04000400 – ATT数据长度,以及L2CAP通道编号
        • 1B – notify command
        • 0x0013 – 电量数据handle
        • 0x53 – 真正要发送的电量数据
        • 0xF650D5 – CRC24值


    虽然开发者只调用了 send(0x53),但由于低功耗蓝牙协议栈层层打包,最后空中实际传输的数据将变成下图所示的模样,这就既满足了低功耗蓝牙通信的需求,又让用户API变得简单,可谓一箭双雕!
    希望通过这个例子,让大家对协议栈的各层作用有个初步的印象。



    BLE%E6%8A%80%E6%9C%AF-%E5%8F%91%E9%80%81%E6%95%B0%E6%8D%AE3.png (8.88 KB, 下载次数: 284)

    BLE%E6%8A%80%E6%9C%AF-%E5%8F%91%E9%80%81%E6%95%B0%E6%8D%AE3.png

    BLE%E6%8A%80%E6%9C%AF-%E5%8F%91%E9%80%81%E6%95%B0%E6%8D%AE2.png (9.43 KB, 下载次数: 265)

    BLE%E6%8A%80%E6%9C%AF-%E5%8F%91%E9%80%81%E6%95%B0%E6%8D%AE2.png

    BLE%E6%8A%80%E6%9C%AF-%E8%93%9D%E7%89%99%E5%8D%8F%E8%AE%AE%E6%A0%B8%E5%BF%83.png (98.7 KB, 下载次数: 272)

    BLE%E6%8A%80%E6%9C%AF-%E8%93%9D%E7%89%99%E5%8D%8F%E8%AE%AE%E6%A0%B8%E5%BF%83.png

    BLE%E6%8A%80%E6%9C%AF-%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B10-%E8%AF%B7%E6%B1%82%E6%96%AD%E5%BC%80.png (9.57 KB, 下载次数: 282)

    BLE%E6%8A%80%E6%9C%AF-%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B10-%E8%AF%B7%E6%B1%82%E6%96%AD%E5%BC%80.png

    BLE%E6%8A%80%E6%9C%AF-%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B8-%E4%B8%BB%E6%9C%BAWrite%E6%95%B0%E6%8D%AE.png (10.25 KB, 下载次数: 244)

    BLE%E6%8A%80%E6%9C%AF-%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B8-%E4%B8%BB%E6%9C%BAWrite%E6%95%B0%E6%8D%AE.png

    BLE%E6%8A%80%E6%9C%AF-%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B6-%E8%8E%B7%E5%8F%96%E6%9C%8D%E5%8A%A1.png (9.84 KB, 下载次数: 260)

    BLE%E6%8A%80%E6%9C%AF-%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B6-%E8%8E%B7%E5%8F%96%E6%9C%8D%E5%8A%A1.png

    BLE%E6%8A%80%E6%9C%AF-%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B4-%E5%8F%91%E9%80%81%E8%BF%9E%E6%8E%A5%E8%AF%B7%E6%B1%82.png (9.75 KB, 下载次数: 271)

    BLE%E6%8A%80%E6%9C%AF-%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B4-%E5%8F%91%E9%80%81%E8%BF%9E%E6%8E%A5%E8%AF%B7%E6%B1%82.png

    BLE%E6%8A%80%E6%9C%AF-%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B2-%E6%89%AB%E6%8F%8F.png (9.55 KB, 下载次数: 264)

    BLE%E6%8A%80%E6%9C%AF-%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B2-%E6%89%AB%E6%8F%8F.png

    BLE%E6%8A%80%E6%9C%AF-%E8%99%9A%E6%8B%9F%E4%B8%BB%E4%BB%8E%E6%9C%BA.png (21.46 KB, 下载次数: 285)

    BLE%E6%8A%80%E6%9C%AF-%E8%99%9A%E6%8B%9F%E4%B8%BB%E4%BB%8E%E6%9C%BA.png

    BLE%E6%8A%80%E6%9C%AF-Profile%E4%B8%8EService.png (43.9 KB, 下载次数: 284)

    BLE%E6%8A%80%E6%9C%AF-Profile%E4%B8%8EService.png

    BLE%E6%8A%80%E6%9C%AF-GATT%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%92%8C%E5%AE%A2%E6%88%B7%E7%AB%AF.png (23.67 KB, 下载次数: 261)

    BLE%E6%8A%80%E6%9C%AF-GATT%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%92%8C%E5%AE%A2%E6%88%B7%E7%AB%AF.png

    BLE%E6%8A%80%E6%9C%AF_%E8%BF%9E%E6%8E%A5%E9%97%B4%E9%9A%94%E4%B8%8E%E8%BF%9E%E6%8E%A5%E4%BA%8B%E4%BB%B6.png (66.69 KB, 下载次数: 271)

    BLE%E6%8A%80%E6%9C%AF_%E8%BF%9E%E6%8E%A5%E9%97%B4%E9%9A%94%E4%B8%8E%E8%BF%9E%E6%8E%A5%E4%BA%8B%E4%BB%B6.png

    BLE%E6%8A%80%E6%9C%AF-%E5%B9%BF%E6%92%AD%E9%97%B4%E9%9A%94%E5%81%8F%E5%B7%AE.png (52.7 KB, 下载次数: 284)

    BLE%E6%8A%80%E6%9C%AF-%E5%B9%BF%E6%92%AD%E9%97%B4%E9%9A%94%E5%81%8F%E5%B7%AE.png

    BLE%E6%8A%80%E6%9C%AF-%E5%B9%BF%E6%92%AD%E4%BF%A1%E9%81%93.png (25.71 KB, 下载次数: 273)

    BLE%E6%8A%80%E6%9C%AF-%E5%B9%BF%E6%92%AD%E4%BF%A1%E9%81%93.png

    BLE%E6%8A%80%E6%9C%AF_%E8%93%9D%E7%89%99%E5%88%86%E7%B1%BB.png (66.96 KB, 下载次数: 287)

    BLE%E6%8A%80%E6%9C%AF_%E8%93%9D%E7%89%99%E5%88%86%E7%B1%BB.png
    App Inventor 2 中文网 - MIT同步更新的中文本土化平台!v2.69更新日志
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    © 2024 tsingfun.com, Inc.  沪ICP备2020034476号-1  沪公网安备31011702000040号

    GMT+8, 2024-05-13 13:02 , Processed in 0.014614 second(s), 32 queries .