Bootstrap

【消息序列】详解(8):探秘物联网中设备广播服务

目录

一、概述

1.1. 定义与特点

1.2. 工作原理

1.3. 应用场景

1.4. 技术优势

二、截断寻呼(Truncated Page)流程

2.1. 截断寻呼的流程

2.2. 示例代码

2.3. 注意事项

三、无连接外围广播过程

3.1. 设备 A 启动无连接外围设备广播

3.2. 示例代码

3.3. 注意事项

四、同步序列(Synchronization Train)特性 

4.1. 同步序列启动的流程

4.2. 示例代码

五、广播(Connectionless Peripheral Broadcast)数据包的接收

5.1. 广播接收流程

5.2. 示例代码

 六、总结


CONNECTIONLESS PERIPHERAL BROADCAST SERVICES(CPB服务)是蓝牙技术中的一种特殊通信模式,旨在实现一对多的无连接数据广播。该服务允许一台中心设备作为发射器,定期向多台周边设备作为接收器广播数据包,而无需在它们之间建立传统的蓝牙连接。CPB服务通过同步队列和同步扫描机制确保数据的可靠传输,周边设备可以根据接收到的广播数据包进行相应的处理。

一、概述

1.1. 定义与特点

无连接周边广播(Connectionless Peripheral Broadcast,简称CPB)是蓝牙技术中的一种1:n(一台中心设备对多台周边设备)通信模式。在此模式下,一台中心设备作为发射器,向多台作为接收器的周边设备广播数据包,而无需建立传统的蓝牙连接。这种通信方式具有以下特点:

  • 单向广播:数据从中心设备向周边设备单向传输,没有数据接收确认机制。
  • 无需连接:周边设备无需与中心设备建立连接即可接收广播数据。
  • 广播信道:CPB采用的射频信道与低功耗蓝牙中的主广播射频信道一致,即2402MHz、2426MHz和2480MHz。

1.2. 工作原理

  • 同步队列:中心设备定期发送包含CPB时序、跳频序列和接入码信息的同步队列。需要同步于CPB的周边设备通过同步扫描即可获取这些信息,从而与中心设备保持同步。
  • 广播数据:在同步建立后,中心设备在指定的无连接周边广播时刻,通过CPB逻辑传输通道向周边设备广播数据包。
  • 接收数据:周边设备在同步于中心设备的时序和信道上接收广播数据,并进行处理。

1.3. 应用场景

无连接周边广播服务广泛应用于需要一对多通信的场景,如:

  • 室内定位:中心设备广播位置信息,周边设备(如智能手机、智能手表等)接收并处理这些信息,实现室内定位功能。
  • 信息推送:中心设备向周边设备广播通知、广告等信息,如商场内的促销信息、展览馆的展品信息等。
  • 健康监测:在健身房或医院等场所,中心设备广播健康监测数据,周边设备接收并显示这些数据,方便用户随时了解自己的健康状况。

1.4. 技术优势

CPB服务具有高效性、灵活性和可靠性等特点,适用于需要将数据从一台设备广播到多台设备的场景。与低功耗蓝牙(BLE)相比,CPB服务在数据传输速率和传输距离方面可能具有优势,但功耗可能相对较高。因此,在选择使用CPB服务时,需要根据具体应用场景和需求进行权衡。

  • 高效性:无连接周边广播服务采用单向广播方式,无需建立连接和进行数据确认,提高了数据传输的效率。
  • 灵活性:中心设备可以同时向多台周边设备广播数据,适用于需要一对多通信的场景。
  • 低功耗:周边设备在接收广播数据时处于低功耗状态,延长了设备的续航时间。

无连接周边广播服务是蓝牙技术中的一种重要通信模式,具有高效性、灵活性和低功耗等优点。随着物联网和智能设备的不断发展,无连接周边广播服务将在更多领域得到广泛应用。

二、截断寻呼(Truncated Page)流程

截断寻呼流程在蓝牙通信等相关场景中有着重要作用,主要用于设备之间建立连接或者重新连接等操作时,通过一种特定的、相对简化的寻呼方式来提高效率、节省资源等。

2.1. 截断寻呼的流程

在蓝牙通信环境中,截断寻呼是一种用于快速确认设备间存在性和可用性的机制。当设备B想要与设备A建立连接或重新取得联系时,可以选择使用截断寻呼来减少数据交互量并加快响应速度。这种方法在设备电量有限或周围通信环境较复杂的情况下特别有用,因为它能够更高效地建立通信链路。 

  • 设备B发起截断寻呼:设备B(通过其控制器Controller B)使用截断寻呼方法向设备A发送寻呼请求。设备A通过发送HCI_Write_Scan_Enable命令来启用扫描,以便能够接收来自设备B的寻呼请求。
  • 寻呼请求与响应:设备A(通过其控制器Controller A)在接收到page请求后,会通过其控制器Controller A发送一个Page Response作为响应。这个响应表明设备A已经接收到了page请求,并准备进行进一步的通信。
  • 截断寻呼完成:当设备B成功接收到设备A的Page Response后,它会给Host上报一个HCI_Truncated_Page_Complete事件来标记截断page过程的完成。这个事件表明设备B已经成功与设备A建立了连接(尽管是临时的,仅用于page),并且不再需要继续发送page请求。
  • page超时:如果设备A达到了预定的Scan时间限制(由pagerespTO参数指定),则会发生Page Response超时。此时,设备A(通过其控制器Controller A)会向Host A上报一个HCI_Peripheral_Page_Response_Timeout事件来标记page response超时。

2.2. 示例代码

以下是一个简化的、概念性的代码框架,用于说明如何模拟截断寻呼流程。请注意,这个示例并不包含完整的蓝牙写有栈实现,而是展示了流程中的关键步骤和概念。

#include <stdio.h>
#include <stdlib.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

// 假设的HCI命令发送函数
// 在实际实现中,这个函数会调用蓝牙协议栈提供的API来发送HCI命令
int send_hci_command(uint16_t opcode, uint8_t *param, uint16_t param_len) {
    // 这里省略了实际的发送逻辑
    // 返回一个假设的成功值
    return 0; // 假设命令发送成功
}

// 假设的HCI事件处理函数
// 在实际实现中,这个函数会根据接收到的HCI事件类型进行相应处理
void handle_hci_event(uint8_t *event, uint16_t event_len) {
    // 这里省略了实际的处理逻辑
    // 仅打印一条消息表示接收到了HCI事件
    printf("Received HCI event\n");
}

// 设备A启用扫描的函数
int enable_scan_on_device_a() {
    uint8_t param[] = { 0x01, 0x00, 0x00 }; // 假设的参数,启用主动和被动扫描
    return send_hci_command(HCI_WRITE_SCAN_ENABLE, param, sizeof(param));
}

// 设备B发起截断寻呼的函数
int initiate_truncated_page_on_device_b(bdaddr_t *target_address) {
    // 构造截断寻呼请求的参数(这里省略了详细的参数构造)
    uint8_t param[18] = { /* ... */ }; // 需要包含目标设备的地址等信息
    return send_hci_command(/* HCI_TRUNCATED_PAGE 命令操作码 */, param, sizeof(param));
}

// 设备A处理寻呼响应的函数(在接收到寻呼请求后由蓝牙协议栈调用)
void device_a_handle_page_response() {
    // 这里省略了实际的响应处理逻辑
    // 例如,发送一个确认响应给设备B
    printf("Device A received page request and sent response\n");
}

// 设备B处理截断寻呼完成事件的函数
void device_b_handle_truncated_page_complete() {
    // 这里省略了实际的处理逻辑
    // 例如,通知上层应用已经成功与设备A建立了临时连接
    printf("Device B truncated page complete\n");
}

// 设备A处理寻呼响应超时的函数
void device_a_handle_page_response_timeout() {
    // 这里省略了实际的处理逻辑
    // 例如,通知上层应用寻呼请求超时
    printf("Device A page response timeout\n");
}

int main() {
    bdaddr_t target_address = { /* ... */ }; // 目标设备A的蓝牙地址

    // 设备A启用扫描
    if (enable_scan_on_device_a() != 0) {
        fprintf(stderr, "Failed to enable scan on device A\n");
        return EXIT_FAILURE;
    }

    // 设备B发起截断寻呼
    if (initiate_truncated_page_on_device_b(&target_address) != 0) {
        fprintf(stderr, "Failed to initiate truncated page on device B\n");
        return EXIT_FAILURE;
    }

    // 假设的等待和处理HCI事件循环(在实际实现中,这个循环会不断接收和处理HCI事件)
    // 注意:这个循环是简化的,并不包含实际的事件接收和处理逻辑
    while (1) {
        // 接收HCI事件(在实际实现中,这个调用会阻塞直到接收到一个HCI事件)
        // uint8_t event[256];
        // uint16_t event_len;
        // if (receive_hci_event(event, &event_len) == 0) {
        //     handle_hci_event(event, event_len);
        // }

        // 由于这是一个示例,我们仅模拟处理一些事件
        // 在实际情况下,这些事件处理函数应该由蓝牙协议栈在接收到相应事件时调用
        device_a_handle_page_response(); // 假设设备A已经处理了寻呼响应
        device_b_handle_truncated_page_complete(); // 假设设备B已经接收到了截断寻呼完成事件
        // 注意:在实际情况下,不应该在这里直接调用这些函数,而应该由事件驱动

        // 退出循环(在实际实现中,这个循环应该一直运行直到程序被显式地终止)
        break; // 仅用于示例,实际中应该有一个更合适的退出条件
    }

    // 注意:在实际实现中,应该有一个机制来处理寻呼响应超时的情况
    // 例如,如果设备A在预定时间内没有收到设备B的响应,则应该调用device_a_handle_page_response_timeout()

    return EXIT_SUCCESS;
}
  • HCI命令和操作码:示例中的HCI命令操作码(如HCI_WRITE_SCAN_ENABLEHCI_TRUNCATED_PAGE)是假设的,实际的操作码取决于蓝牙规范。

  • 参数构造:在initiate_truncated_page_on_device_b函数中,需要根据蓝牙协议栈和硬件规范来构造截断寻呼请求的参数。通常包括目标设备的蓝牙地址、寻呼参数等。

  • 事件处理:示例中的事件处理函数(如device_a_handle_page_responsedevice_b_handle_truncated_page_complete)是假设的,实际的处理逻辑取决于应用需求。需要根据接收到的HCI事件类型来调用相应的处理函数。

  • 事件循环:示例中的事件循环是简化的,并不包含实际的事件接收和处理逻辑。在实际实现中,需要有一个不断运行的事件循环来接收和处理来自蓝牙协议栈的HCI事件。

  • 超时处理:示例中没有包含寻呼响应超时的处理逻辑。在实际实现中,需要有一个机制来检测和处理寻呼响应超时的情况。

  • 依赖项:示例代码依赖于蓝牙协议栈提供的API。在编译和运行示例代码之前,需要确保已经安装了适当的蓝牙协议栈,并正确配置了编译环境。

  • 错误处理:示例代码中的错误处理是简化的。在实际实现中,需要添加更详细的错误处理逻辑来确保程序的健壮性。

2.3. 注意事项

  • 截断寻呼是一种用于在蓝牙设备之间建立临时连接的方法,通常用于设备发现、配对或连接建立过程中的某些阶段。

  • 在截断寻呼过程中,设备A和设备B之间不会建立完整的蓝牙连接,而是仅通过page请求和响应来确认彼此的存在和可用性。

  • page超时是截断page过程中的一个重要环节,它用于处理设备B在指定时间内未收到设备A响应的情况。在这种情况下,设备B会停止发送page请求,并可能采取其他措施来尝试与设备A建立连接。

三、无连接外围广播过程

在某些蓝牙应用场景下,设备 A 需要向周围的设备发送广播信息,且采用无连接的方式进行。这种无连接外围设备广播常用于不需要建立专门连接就能向特定范围内的设备传递一些通用信息的情况,比如设备 A 要向周边设备广播自身的状态信息、提供某种公共服务的相关通知等。通过这种广播方式,能方便快捷地将信息扩散出去,而无需事先与每个接收设备都建立一对一的连接,提高信息传播的效率和便捷性,所以设备 A 启动了这一广播流程。

3.1. 设备 A 启动无连接外围设备广播

无连接外围设备广播数据包的流程在蓝牙通信中为设备间无需建立连接即可传递信息提供了有效的途径,对于诸如设备状态通告、公共服务信息分享等应用场景有着重要的实现意义,有助于提升信息传播的便捷性和效率,同时也展现了蓝牙通信在不同通信模式下严谨且规范的操作机制。 

  • 设置保留的本地地址:在开始无连接外围广播之前,设备A(通过其控制器Controller A)首先使用HCI_Set_Reserved_LT_ADDR命令来设置一个保留的本地临时地址命令Reserved Local Address,LT_ADDR)。这个地址用于在无连接广播过程中唯一标识设备A。

  • 设置无连接外围广播数据:接下来,设备A使用HCI_Set_Connectionless_Peripheral_Broadcast_Data命令来配置要广播的数据。这些数据可能包括设备A的标识符、服务信息或其他相关信息,供设备B在接收到广播时进行处理。

  • 启用无连接外围广播:完成数据设置后,设备A通过发送HCI_Set_Connectionless_Peripheral_Broadcast命令来启用无连接外围广播功能。这个命令会指示设备A的控制器Controller A开始发送广播数据包。

  • 发送无连接外围广播数据包:一旦无连接外围广播被启用,设备A的控制器Controller A就会开始周期性地发送无连接外围广播数据包。这些数据包包含之前设置的广播数据,并被发送到设备B可以接收的广播频道上。

  • 接收广播数据包:设备B(通过其控制器Controller B)在扫描过程中接收到来自设备A的无连接外围广播数据包。设备B可以根据数据包中的信息来识别设备A,并决定是否进一步与设备A建立连接或进行其他操作。

3.2. 示例代码

以下是一个简化的C语言代码示例,用于说明如何实现无连接外围广播的流程。请注意,这只是一个概念性的示例,并不包含完整的蓝牙协议栈实现或错误处理。在实际应用中,需要使用蓝牙协议栈提供的API(如BlueZ、Bluedroid或厂商特定的SDK)来执行这些操作。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 假设这些是蓝牙协议栈提供的API函数(实际使用时需要替换为真实的API)
extern int HCI_Set_Reserved_LT_ADDR(const char* address);
extern int HCI_Set_Connectionless_Peripheral_Broadcast_Data(const unsigned char* data, int length);
extern int HCI_Set_Connectionless_Peripheral_Broadcast(int enable);

// 设备A的保留本地地址
#define RESERVED_LT_ADDR "AA:BB:CC:DD:EE:FF"

// 要广播的数据
#define BROADCAST_DATA "Hello, Device B!"

int main() {
    // 设置保留的本地地址
    if (HCI_Set_Reserved_LT_ADDR(RESERVED_LT_ADDR) != 0) {
        fprintf(stderr, "Failed to set reserved local address.\n");
        return EXIT_FAILURE;
    }
    printf("Reserved local address set successfully.\n");

    // 设置无连接外围广播数据
    unsigned char broadcast_data[] = BROADCAST_DATA;
    int data_length = strlen(BROADCAST_DATA);
    if (HCI_Set_Connectionless_Peripheral_Broadcast_Data(broadcast_data, data_length) != 0) {
        fprintf(stderr, "Failed to set connectionless peripheral broadcast data.\n");
        return EXIT_FAILURE;
    }
    printf("Connectionless peripheral broadcast data set successfully.\n");

    // 启用无连接外围广播
    if (HCI_Set_Connectionless_Peripheral_Broadcast(1) != 0) {
        fprintf(stderr, "Failed to enable connectionless peripheral broadcast.\n");
        return EXIT_FAILURE;
    }
    printf("Connectionless peripheral broadcast enabled successfully.\n");

    // 在这里,设备A会开始周期性地发送广播数据包
    // ...(实际代码中,这里可能是一个循环,等待用户中断或其他条件来停止广播)

    // 假设在某个时刻,我们决定停止广播
    if (HCI_Set_Connectionless_Peripheral_Broadcast(0) != 0) {
        fprintf(stderr, "Failed to disable connectionless peripheral broadcast.\n");
        return EXIT_FAILURE;
    }
    printf("Connectionless peripheral broadcast disabled successfully.\n");

    return EXIT_SUCCESS;
}
  • API函数HCI_Set_Reserved_LT_ADDRHCI_Set_Connectionless_Peripheral_Broadcast_DataHCI_Set_Connectionless_Peripheral_Broadcast是假设的API函数,用于说明流程。在实际应用中,需要使用蓝牙协议栈提供的真实API。

  • 错误处理:示例代码中的错误处理非常基础,仅用于说明。在实际应用中,需要更详细的错误处理逻辑。

  • 广播数据:在示例中,广播数据被硬编码为字符串。在实际应用中,可能需要从其他来源获取这些数据,如配置文件或用户输入。

  • 广播周期:示例代码没有显示设置广播周期。在实际应用中,可能需要配置广播间隔和其他相关参数。

  • 设备B的接收:示例代码仅涉及设备A的广播流程。设备B需要实现相应的扫描逻辑来接收这些广播数据包。

  • 蓝牙栈初始化:在实际应用中,需要在开始之前初始化蓝牙协议栈,并在结束时进行清理。这些步骤在示例代码中未显示。

  • 多线程/异步处理:蓝牙通信通常是异步的。在实际应用中,可能需要使用多线程或异步回调来处理蓝牙事件。  

3.3. 注意事项

  • 无连接外围广播允许设备在不建立完整连接的情况下发送和接收数据。这对于需要快速广播信息而不需要持续连接的设备来说非常有用。

  • 在无连接外围广播过程中,设备A和设备B之间不会建立完整的蓝牙连接。广播数据包是单向发送的,设备B只能接收这些数据包而不能直接回复。
  • 设备A可能需要配置一些额外的参数来优化无连接外围广播的性能,如广播间隔、广播频道等。这些参数可以通过相应的HCI命令进行设置。

四、同步序列(Synchronization Train)特性 

在一些对时间同步要求较高或者需要按照特定节奏、顺序来传递和接收信息的蓝牙通信应用场景中,同步序列功能就发挥了重要作用。例如,在多设备协同工作且各设备的操作需要精准同步的情况下,或者是进行周期性数据广播且接收方要依据固定时间间隔来准确接收处理数据时,设备 A 作为发送方就需要启动同步序列,以此来建立一种有规律、可同步的信息传输模式,方便设备 B 等接收设备能更好地与它进行协调工作,准确接收广播数据包并进行后续处理。

4.1. 同步序列启动的流程

在无连接的外围设备广播场景中,设备A会周期性地发送广播数据包,而设备B则负责接收这些数据包。同步序列功能增强了这种广播机制,通过发送一系列预定义的、格式化的数据包,使得设备B能够更可靠地接收并处理来自设备A的信息。

  • HCI_Write_Synchronization_Train_Parameters:设备A开始同步序列的广播过程。通常涉及设置一系列参数,如广播间隔、广播数据包的内容等。可以将其理解为是为整个同步序列的运行制定一套详细的 “规则手册”,只有明确了这些参数,后续的同步序列才能按照既定的节奏和要求进行运转,是启动同步序列操作前至关重要的准备工作,确保整个过程有章可循。设备A通过发送HCI_Write_Synchronization_Train_Parameters命令来配置这些参数。蓝牙硬件(或控制器A)在接收到参数设置命令后,会返回一个HCI_Command_Complete事件,表示参数已成功设置。

  • HCI_Start_Synchronization_Train:设备A的主机(Host A)通过控制器A发送HCI_Start_Synchronization_Train命令来启动同步序列的广播。控制器A在接收到该命令后,会返回一个HCI_Command_Status事件,表示命令已接收并正在处理。

  • Synchronization Train packet:一旦同步序列启动,设备A的控制器A会开始周期性地发送同步序列数据包。这些数据包包含设备A的标识符、服务信息或其他相关信息,供设备B在接收到广播时进行处理。它们就像是按照固定车次、固定时间出发的 “列车”,沿着蓝牙通信链路驶向设备 B 等接收设备所在的方向,是实现信息有规律、同步传输的核心载体,也是整个同步序列功能在实际操作中的关键体现,确保接收设备能够依据这种规律来准确接收和处理信息。

  • HCI_Receive_Synchronization_Train:在设备 B 端,Host B会发送 HCI_Receive_Synchronization_Train 命令告知Controller B(设备B的控制器)来接收同步序列数据包。发出该命令后,Controller B会返回 HCI_Command_Status事件信息给Host B,确认设备 B 的接收准备命令已被系统接收并进入正常处理流程,保障设备 B 后续能够顺利开展接收操作,确保整个同步序列的发送与接收环节能够有效衔接起来。

  • HCI_Synchronization_Train_Received:当设备 B 成功接收到设备 A 发送过来的同步序列数据包后,Controller B会向设备 Host B 发送 HCI_Synchronization_Train_Received 事件,告知Host B 它已经接收到了相应的数据包,可以进行后续的处理工作(比如对数据包中的数据进行解析、应用等操作),同时也向整个蓝牙通信系统反馈了接收情况,保障信息传递的准确性和完整性。

  • synchronization_trainTO(超时):在同步序列的运行过程中,如果出现设备A达到了预定的时间限制,就会触发 synchronization_trainTO 机制来表示这种超时现象。Controller A会向设备 Host A 发送 HCI_Synchronization_Train_Complete事件,告知Host A 此次同步序列操作已经全部完成,整个流程顺利结束,意味着设备 A 可以根据此次同步序列的执行情况(如是否成功、有无异常等)来决定后续是否需要再次启动或者调整相关操作,保障整个通信过程的完整性以及对同步序列操作的有效管理。

  • HCI_Synchronization_Train_Complete:当同步序列广播完成(例如,达到了预定的数据包数量或时间限制)时,设备A的控制器A会发送一个HCI_Synchronization_Train_Complete事件来通知Host A。标志着同步序列广播过程的结束。

4.2. 示例代码

以下是一个简化的代码示例,用于模拟同步序列启动的流程。请注意,这个示例并不包含实际的蓝牙通信或HCI命令的实现,而是用函数调用来模拟这些过程。在实际应用中,需要使用特定的蓝牙协议栈或API来实现这些功能。

#include <stdio.h>
#include <stdbool.h>

// 模拟HCI命令和事件的回调函数类型
typedef void (*HCI_Callback)(const char* event_name, const char* data);

// 模拟设备A和设备B的结构体
typedef struct {
    HCI_Callback callback;
} Device;

// 模拟发送HCI命令的函数
void send_hci_command(Device* device, const char* command_name, const char* data, HCI_Callback callback) {
    printf("Sending HCI command: %s with data: %s\n", command_name, data ? data : "NULL");
    // 在这里,我们简单地调用回调函数来模拟HCI事件的返回
    if (callback) {
        callback(command_name, data);
    }
}

// 模拟HCI命令完成的回调函数
void hci_command_complete_callback(const char* event_name, const char* data) {
    printf("HCI Command Complete: %s\n", event_name);
}

// 模拟同步序列参数设置的函数
void set_synchronization_train_parameters(Device* deviceA) {
    send_hci_command(deviceA, "HCI_Write_Synchronization_Train_Parameters", "params", hci_command_complete_callback);
}

// 模拟启动同步序列的函数
void start_synchronization_train(Device* deviceA) {
    send_hci_command(deviceA, "HCI_Start_Synchronization_Train", "NULL", NULL); // 这里可能不需要回调,因为启动是异步的
    // 在实际情况下,可能需要在某个时刻模拟接收到HCI_Command_Status事件
    printf("HCI Command Status: HCI_Start_Synchronization_Train\n");
}

// 模拟接收同步序列数据包的回调函数
void receive_synchronization_train_callback(const char* event_name, const char* data) {
    printf("HCI Receive Synchronization Train: %s\n", event_name);
    // 在这里,可能需要处理接收到的数据包
}

// 模拟同步序列数据包接收完成的回调函数
void synchronization_train_received_callback(const char* event_name, const char* data) {
    printf("HCI Synchronization Train Received: %s\n", event_name);
    // 在这里,可能需要处理接收完成后的逻辑
}

// 模拟同步序列超时的回调函数(注意:在实际应用中,超时可能由底层蓝牙协议栈处理)
void synchronization_train_timeout_callback(const char* event_name, const char* data) {
    printf("Synchronization Train Timeout: %s\n", event_name);
    // 在这里,可能需要处理超时逻辑
}

// 模拟同步序列完成的回调函数
void synchronization_train_complete_callback(const char* event_name, const char* data) {
    printf("HCI Synchronization Train Complete: %s\n", event_name);
    // 在这里,可能需要处理同步序列完成后的逻辑
}

int main() {
    // 创建设备A和设备B的实例
    Device deviceA;
    Device deviceB;

    // 设置设备A的回调函数(用于模拟HCI事件的返回)
    deviceA.callback = synchronization_train_complete_callback;

    // 设置同步序列参数
    set_synchronization_train_parameters(&deviceA);

    // 启动同步序列
    start_synchronization_train(&deviceA);

    // 在这里,可能需要模拟设备B接收同步序列数据包的逻辑
    // 由于这是异步的,我们简单地使用回调函数来模拟接收过程
    // 注意:在实际应用中,设备B的接收逻辑应该由蓝牙栈或底层驱动处理
    send_hci_command(&deviceB, "HCI_Receive_Synchronization_Train", "NULL", receive_synchronization_train_callback);
    // 假设接收成功,触发接收完成事件
    synchronization_train_received_callback("HCI_Synchronization_Train_Received", "data");

    // 模拟同步序列完成(这里可能是由设备A的控制器在内部触发的)
    synchronization_train_complete_callback("HCI_Synchronization_Train_Complete", "NULL");

    // 注意:超时回调在这个示例中没有显式触发,因为它通常是由底层蓝牙栈在达到时间限制时自动处理的

    return 0;
}

在这个示例中,我们使用了函数调用来模拟发送HCI命令和接收HCI事件的过程。每个命令或事件都有一个对应的回调函数,用于模拟实际蓝牙通信中可能发生的各种情况。然而,请注意,这个示例并不包含实际的蓝牙通信逻辑,而是用于展示同步序列启动流程的一个概念性框架。在实际应用中,需要使用特定的蓝牙协议栈或API来实现这些功能,并根据蓝牙规范来处理各种命令和事件。 

五、广播(Connectionless Peripheral Broadcast)数据包的接收

广播接收机制在蓝牙通信中为设备间实现信息共享、状态感知等应用场景提供了有效的途径,使得设备 B 能够及时获取设备 A 广播的信息,进而依据这些信息开展后续的相关操作,有助于提升蓝牙通信在多设备环境下信息传播和利用的效率。

5.1. 广播接收流程

在蓝牙通信应用场景里,设备 B 出于获取设备 A 广播信息的需求,例如设备 A 会不定期广播自身状态信息、提供的某些服务相关数据等,而设备 B 需要利用这些信息来做进一步决策(如判断是否要与设备 A 建立连接、根据其状态调整自身配置等),所以要启动接收广播数据包的流程,使其自身处于能够监听并接收来自设备 A 广播内容的状态,进而实现信息从设备 A 到设备 B 的传递。

  • Connectionless Peripheral Broadcast packet(设备广播数据包):在设备 B 开始操作前,设备 A 可能已经在按照其广播机制持续发送无连接外围设备广播数据包了。这些数据包承载着设备 A 想要对外广播的各种信息,在链路层面上朝着设备 B 所在方向等广播范围内进行传播,它们是整个广播通信的核心内容载体,虽然此时设备 B 还未完成接收准备,但数据包已经在发送途中,后续设备 B 的操作就是为了能准确捕获并处理这些数据包。

  • HCI_Set_Connectionless_Peripheral_Broadcast_Receive(设置广播接收命令):Host B发送 HCI_Set_Connectionless_Peripheral_Broadcast_Receive 命令配置Controller B 自身的接收功能,使其能够识别并接收来自设备 A 的广播数据包。它相当于为设备 B 的 “接收天线” 进行调谐和设置参数,告诉设备 B 应该去关注哪些类型的广播、按照什么样的规则来接收等,是设备 B 启动接收流程的关键准备步骤,确保其具备接收相应广播数据包的基础能力

  • HCI_Connectionless_Peripheral_Broadcast_Receive(外围设备广播接收状态):随着设备 B 完成接收功能配置,对于每个接收到的广播数据包,设备B都会触发HCI_Connectionless_Peripheral_Broadcast_Receive 事件,它就像是一个实时的 “接收指示灯”,展示着设备 B 的接收情况,确保设备 B 能够不断地捕获广播数据包并进行相应处理,同时也向整个蓝牙通信系统反馈其接收工作的持续进展。

5.2. 示例代码

以下是一个简化的示例代码框架,用于说明如何设置接收无连接外围设备广播数据包。请注意,实际的实现将依赖于特定的蓝牙协议栈和硬件平台,因此以下代码仅作为概念性示例。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

// 假设有一个函数用于发送HCI命令
// 这个函数在实际应用中需要由蓝牙协议栈或硬件接口库提供
int send_hci_command(uint16_t opcode, uint8_t *param, uint8_t param_len) {
    // 这里省略了实际的发送逻辑
    // 返回一个假设的成功值
    return 0; // 成功
}

// 假设有一个函数用于处理HCI事件
// 这个函数在实际应用中需要根据具体的事件类型进行处理
void handle_hci_event(uint8_t *event, uint16_t event_len) {
    // 这里省略了实际的处理逻辑
    // 例如,检查事件类型并调用相应的处理函数
    printf("Received HCI event\n");
}

// 设置接收无连接外围设备广播数据包的函数
int set_connectionless_peripheral_broadcast_receive() {
    // 定义HCI_Set_Connectionless_Peripheral_Broadcast_Receive命令的参数
    // 注意:这里的参数是假设的,实际参数取决于蓝牙协议栈和硬件规范
    uint8_t param[] = {
        // 假设的参数,例如启用接收的标志、过滤条件等
        0x01, // 启用接收
        // 其他可能的参数...
    };

    // 发送HCI_Set_Connectionless_Peripheral_Broadcast_Receive命令
    int result = send_hci_command(0xXXXX, // 这里需要填入实际的HCI命令操作码
                                  param,
                                  sizeof(param));

    if (result != 0) {
        // 命令发送失败
        fprintf(stderr, "Failed to send HCI Set Connectionless Peripheral Broadcast Receive command\n");
        return -1;
    }

    // 命令发送成功,等待接收广播数据包的事件
    // 注意:这里应该有一个事件循环来不断接收和处理HCI事件
    // 由于这是一个简化的示例,我们仅打印一条消息来表示等待接收
    printf("Waiting for Connectionless Peripheral Broadcast packets...\n");

    // 在实际的应用中,这里应该有一个循环来不断调用handle_hci_event来处理接收到的HCI事件
    // 例如:
    /*
    while (1) {
        uint8_t event[256]; // 假设的事件缓冲区大小
        uint16_t event_len; // 事件长度

        // 接收HCI事件(这个函数在实际应用中需要由蓝牙堆栈或硬件接口库提供)
        // result = receive_hci_event(event, &event_len);

        // 如果接收成功,则处理事件
        // if (result == 0) {
        //     handle_hci_event(event, event_len);
        // }
    }
    */

    // 由于这是一个示例,我们直接返回成功
    return 0;
}

int main() {
    // 初始化蓝牙协议栈和硬件(这个步骤在实际应用中需要完成)
    // 例如:初始化HCI层、打开蓝牙适配器等

    // 设置接收无连接外围设备广播数据包
    int result = set_connectionless_peripheral_broadcast_receive();

    if (result != 0) {
        // 设置接收失败
        fprintf(stderr, "Failed to set up connectionless peripheral broadcast receive\n");
        return EXIT_FAILURE;
    }

    // 在实际应用中,这里应该有一个无限循环来保持程序运行并处理接收到的广播数据包
    // 由于这是一个示例,我们仅打印一条消息并退出
    printf("Broadcast receive setup complete. Exiting now (this is just an example).\n");

    return EXIT_SUCCESS;
}
  • HCI命令和操作码:示例中的0xXXXX需要替换为实际的HCI命令操作码。这个操作码取决于蓝牙协议规范。

  • 参数param数组中的参数是假设的,实际参数取决于蓝牙规范。

  • 事件处理:示例中的handle_hci_event函数需要根据实际的事件类型进行处理。这通常涉及解析事件数据包并根据其内容执行相应的操作。

  • 事件循环:示例中注释掉的事件循环是实际接收和处理广播数据包所必需的。在实际应用中,应该有一个循环来不断调用handle_hci_event来处理接收到的HCI事件。

  • 初始化:示例中的初始化步骤(如初始化蓝牙协议栈和硬件)需要在实际应用中完成。这通常涉及调用蓝牙协议栈提供的API来打开蓝牙适配器、初始化HCI层等。

  • 依赖项:示例代码依赖于蓝牙协议栈和硬件接口库提供的函数。这些函数在实际应用中需要由相应的库提供。因此,在编译和运行示例代码之前,需要确保已经安装了适当的蓝牙协议栈和硬件接口库,并正确配置了编译环境。

 六、总结

CPB服务是经典蓝牙技术中的一种重要通信模式,通过无连接的数据广播方式实现一对多的数据传输。该服务具有高效、灵活和可靠的特点,适用于需要将数据从中心设备广播到多台周边设备的场景。CPB服务通过同步队列和同步扫描机制确保数据的可靠传输,并在多个领域得到了广泛应用。然而,与BLE相比,CPB服务在功耗方面可能较高,因此在选择使用时需要综合考虑具体应用场景和需求。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;