低功耗蓝牙的核心交互始于广播阶段。在macOS开发中,利用Core Bluetooth框架不仅能实现中心设备扫描,还能将Mac或苹果设备转化为外设,主动向周围发送自定义广播数据。这种能力在物联网交互、室内定位以及设备配网等场景中具有极高的实用价值。

理解BLE广播包结构与Core Bluetooth外设角色
低功耗蓝牙的广播过程是设备在特定信道上周期性发送数据包的过程。一个完整的广播事件通常包含基础广播数据和扫描响应数据。基础广播数据包大小被严格限制在31字节以内,这包含了设备名称、支持的Service UUID以及部分自定义ManufacturerData。当扫描器接收到基础广播包后,可以向发起者发送扫描请求,设备随后返回扫描响应数据,这额外提供了31字节的空间,使得总可用广播数据扩展到62字节。理解这个容量限制是构建稳定BLE外设的前提。
在macOS和iOS生态中,Core Bluetooth框架提供了CBPeripheralManager类来管理本地的外设角色。要启动广播,首先需要实例化CBPeripheralManager对象,并指定一个遵循CBPeripheralManagerDelegate协议的代理对象。由于蓝牙硬件的初始化是异步的,系统会在蓝牙状态变为poweredOn后通知代理。只有在状态就绪后,才能安全地调用startAdvertising方法,否则会触发底层错误。
组装广播数据时,需要将特定的键值对放入NSDictionary中。Core Bluetooth预定义了一系列常量字符串作为键,例如CBAdvertisementDataLocalNameKey用于设置广播中的设备名称,CBAdvertisementDataServiceUUIDKey用于携带服务UUID。如果基础广播包中的数据量超过31字节,系统会自动进行截断或丢弃部分数据,因此在设计数据结构时必须精打细算,将最关键的信息放在基础包中。
广播自定义数据与扫描响应机制的实现
在实际开发中,仅仅广播设备名称往往无法满足业务需求,我们通常需要携带自定义的传感器数据或认证令牌。此时,扫描响应机制就显得尤为重要。当外设接收到中心设备的扫描请求时,会触发代理方法peripheralManagerDidReceiveReadRequest或者通过配置自动回复扫描响应数据。在Core Bluetooth中,可以通过CBAdvertisementDataServiceDataKey或CBAdvertisementDataManufacturerDataKey将自定义数据拆分到基础包和扫描响应包中。
为了最大化利用有限的广播字节,通常将较短的核心标识符放入基础广播包,而将较长的动态数据放入扫描响应。以下代码展示了如何配置一个包含自定义服务UUID和厂商数据的广播字典,并启动广播。需要注意的是,厂商数据的前两个字节必须是大端序的厂商ID,后续才是自定义的Payload。
// 导入CoreBluetooth框架
#import <CoreBluetooth/CoreBluetooth.h>
@interface MyPeripheralManager () <CBPeripheralManagerDelegate>
@property (strong, nonatomic) CBPeripheralManager *peripheralManager;
@end
@implementation MyPeripheralManager
- (void)initBluetooth {
// 初始化外设管理器,设置代理和并发队列
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue() options:nil];
}
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
if (peripheral.state == CBManagerStatePoweredOn) {
[self startAdvertising];
}
}
- (void)startAdvertising {
// 构建自定义服务UUID
CBUUID *serviceUUID = [CBUUID UUIDWithString:@"12345678-1234-1234-1234-1234567890AB"];
// 构建厂商数据 (假设厂商ID为0x004C)
// 注意:前两个字节为厂商ID,这里使用大端序
uint16_t manufacturerID = 0x004C;
NSData *manufacturerData = [NSData dataWithBytes:&manufacturerID length:2];
// 追加自定义的Payload数据
NSString *customPayload = @"SensorData:Temp25";
NSData *payloadData = [customPayload dataUsingEncoding:NSUTF8StringEncoding];
// 合并厂商ID和Payload
NSMutableData *fullManufacturerData = [NSMutableData dataWithData:manufacturerData];
[fullManufacturerData appendData:payloadData];
// 组装广播字典
NSDictionary *advertisementData = @{
CBAdvertisementDataLocalNameKey: @"MyCustomDevice",
CBAdvertisementDataServiceUUIDsKey: @[serviceUUID],
CBAdvertisementDataManufacturerDataKey: fullManufacturerData
};
// 启动广播
[self.peripheralManager startAdvertising:advertisementData];
}
@end
上述代码将设备名称、服务UUID和厂商数据同时放入了广播字典。Core Bluetooth在底层会智能地分配这些数据。如果总数据量超过31字节,系统会优先保证Local Name和Service UUID的完整性,而将部分ManufacturerData移至扫描响应包中。当中心设备主动发起扫描请求时,外设会自动将扫描响应包发送出去。这种机制对应用层是透明的,开发者无需手动处理扫描请求的接收与响应逻辑,极大地简化了开发流程。
深入Beacon设备模拟与底层广播限制
Beacon技术本质上是利用BLE广播来发送特定格式的标识符。以Apple的iBeacon为例,其广播数据被封装在ManufacturerData中,厂商ID固定为0x004C。随后的Payload包含20字节的数据:1字节的Subtype(固定为0x02),1字节的Subtype Length(固定为0x15),16字节的Proximity UUID,2字节的Major值,2字节的Minor值,以及1字节的TxPower值。通过精确组装这20字节数据,就可以让macOS设备伪装成一个标准的iBeacon基站。
下面这段代码演示了如何构建符合iBeacon规范的广播数据。我们将16进制的UUID字符串转换为16字节的NSUUID,然后将Major和Minor值以大端序拼接,最后加上TxPower校准值。将组装好的数据作为ManufacturerData传入广播字典,即可让周围的iOS设备通过Core Location框架识别到该Beacon信号。
- (void)startBeaconAdvertising {
// iBeacon的Proximity UUID
NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:@"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"];
// Major值 (例如: 1)
uint16_t major = 1;
// Minor值 (例如: 1)
uint16_t minor = 1;
// TxPower,用于距离估算的校准值 (例如: -59 dBm)
int8_t txPower = -59;
// 将UUID转换为字节流
uuid_t uuidBytes;
[proximityUUID getUUIDBytes:(unsigned char *)&uuidBytes];
// 构建iBeacon Payload (共20字节)
NSMutableData *beaconData = [NSMutableData dataWithCapacity:20];
// 1字节 Subtype (固定 0x02)
uint8_t subtype = 0x02;
[beaconData appendBytes:&subtype length:1];
// 1字节 Subtype Length (固定 0x15,即21,但这里通常指后续长度)
uint8_t subtypeLength = 0x15;
[beaconData appendBytes:&subtypeLength length:1];
// 16字节 Proximity UUID
[beaconData appendBytes:&uuidBytes length:16];
// 2字节 Major (大端序)
uint16_t majorBigEndian = CFSwapInt16HostToBig(major);
[beaconData appendBytes:&majorBigEndian length:2];
// 2字节 Minor (大端序)
uint16_t minorBigEndian = CFSwapInt16HostToBig(minor);
[beaconData appendBytes:&minorBigEndian length:2];
// 1字节 TxPower
[beaconData appendBytes:&txPower length:1];
// 组装完整的ManufacturerData (厂商ID 0x004C + Payload)
uint16_t appleCompanyID = 0x004C;
NSMutableData *manufacturerData = [NSMutableData dataWithBytes:&appleCompanyID length:2];
[manufacturerData appendData:beaconData];
// 启动广播
NSDictionary *advertisementData = @{
CBAdvertisementDataManufacturerDataKey: manufacturerData
};
[self.peripheralManager startAdvertising:advertisementData];
}
尽管macOS支持模拟Beacon广播,但在实际应用中存在一些系统级限制。首先,macOS和iOS对后台广播有严格限制,当应用进入后台后,系统可能会暂停广播或降低广播频率以节省电量。其次,Core Bluetooth不提供直接设置广播间隔的API,广播频率由系统根据当前设备状态、电量以及周围蓝牙环境动态调度。如果需要高频广播以实现精准的室内定位,可能需要考虑使用外接蓝牙模块而非依赖Mac内置的蓝牙芯片。此外,频繁修改广播数据并重新调用startAdvertising会导致广播中断,建议在数据变化时先调用stopAdvertising,修改数据后再重新启动广播,以保证广播流的连续性。
Core BluetoothBLE广播Beacon模拟修改时间:2026-08-30 15:43:30