ble编程-外设发送数据到中心
一、外设
1.在外设的.h文件中定义如下

1 //周边管理者
2
3 @property (nonatomic , strong) CBPeripheralManager *peripheralManager;
4
5 // 中心
6
7 @property (nonatomic,strong) CBCentral *central;
8
9 // 特征
10
11 @property (nonatomic , strong) CBMutableCharacteristic *customCharacteristic;
12
13 // 服务
14
15 @property (nonatomic , strong) CBMutableService *customService;
16
17 // 向中心发送的数据
18
19 @property (strong, nonatomic) NSData *dataToSend;
20
21 @property (nonatomic, readwrite) NSInteger sendDataIndex;
2.然后在.m文件中进行如下操作
2.1在viewDidLoad方法中初始化外设和要发送的数据

1 // 初始化外设
2 self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
3 // 初始化数据
4 self.dataToSend = [@"snadjfkhaw加大困难的是咖啡euijferlfmn ksxncjxznvjeajfrnjadnfjasfndsafnjsadkfnjsa" dataUsingEncoding:NSUTF8StringEncoding];
2.2 Peripheral Manager被创建,状态改变时调用
CBPeripheralManagerStatePoweredOff 设备关闭状态
CBPeripheralManagerStateUnauthorized 未授权
CBPeripheralManagerStateUnsupported 您的设备不支持蓝牙4.0
CBPeripheralManagerStateResetting 重置状态
CBPeripheralManagerStateUnknown 未知状态

1 - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
2
3 {
4
5 switch (peripheral.state) {
6
7 case CBPeripheralManagerStatePoweredOn:
8
9 NSLog(@"蓝牙设备已经打开,可以扫描外设");
10
11 [self setupService];
12
13 break;
14
15 default:
16
17 NSLog(@"外设改变状态");
18
19 break;
20
21 }
22
23 }
24
25 - (void)setupService
26
27 {
28
29 //creates the characteristic UUID
30
31 CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID];
32
33 //create the characteristic
34
35 self.customCharacteristic = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID
36
37 properties:CBCharacteristicPropertyNotify
38
39 value:nil
40
41 permissions:CBAttributePermissionsReadable];
42
43 //create the service UUID
44
45 CBUUID *servieceUUID = [CBUUID UUIDWithString:kServiceUUID];
46
47 //creates the service and adds the charecteristic to it
48
49 self.customService = [[CBMutableService alloc] initWithType:servieceUUID primary:YES];
50
51 //sets the characteristics for this service
52
53 [self.customService setCharacteristics:@[self.customCharacteristic]];
54
55 // And add it to the peripheral manager
56
57 [self.peripheralManager addService:self.customService];
58
59 }
2.3 当你执行addService方法后执行如下回调

1 - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error {
2 if (error != nil) {
3 NSLog(@"%s,error = %@",__PRETTY_FUNCTION__, error.localizedDescription);
4 } else {
5 //starts advertising the service
6 [self.peripheralManager startAdvertising:@{CBAdvertisementDataLocalNameKey:@"服务门店",
7 CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:kServiceUUID]]}];
8
9 }
10 }
2.4 central订阅了characteristic的值,当更新值的时候peripheral会调用

1 - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
2 {
3 self.central = central;
4 if (_lastDeviceUUID == central.identifier) {
5 return;
6 }
7 _lastDeviceUUID = central.identifier;
8 self.sendDataIndex = 0;
9 [self sendData];
10 }
11
12
13 #pragma mark - 向客户端发消息
14 - (void)sendData {
15
16 // 标记是否为最后一次发送
17 static BOOL sendingEOM = NO;
18 // 1. 如果是最后一次发送
19 if (sendingEOM) {
20 // 只发送 "EOM" 表示结束
21 BOOL didSend = [self.peripheralManager updateValue:[@"EOM" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.customCharacteristic onSubscribedCentrals:@[self.central]];
22
23 // 1.1. 最后一次发送成功
24 if (didSend) {
25 sendingEOM = NO;
26 NSLog(@"Sent: EOM");
27 }
28 return;
29 }
30 // 2. 不是最后一次发送,且数据为空,则返回
31 if (self.sendDataIndex >= self.dataToSend.length) {
32 return;
33 }
34 // 3. 数据尚未发送完成,继续发送直到完成
35 BOOL didSend = YES;
36 while (didSend) {
37 // 3.1. 计算剩余多大数据量需要发送
38 NSInteger amountToSend = self.dataToSend.length - self.sendDataIndex;
39 // 不能大于20个字节
40 if (amountToSend > NOTIFY_MTU)
41 amountToSend = NOTIFY_MTU;
42 // 3.2. copy出我们想要发送的数据
43 NSData *chunk = [NSData dataWithBytes:self.dataToSend.bytes+self.sendDataIndex length:amountToSend];
44 // 3.3. 发送
45 didSend = [self.peripheralManager updateValue:chunk forCharacteristic:self.customCharacteristic onSubscribedCentrals:@[self.central]];
46 // 3.4. 如果没有发送成功,重新发送
47 // If it didn't work, drop out and wait for the callback
48 if (!didSend) {
49 NSLog(@"SEND ERROR");
50 return;
51 }
52 NSString *stringFromData = [[NSString alloc] initWithData:chunk encoding:NSUTF8StringEncoding];
53 NSLog(@"Sent: %@", stringFromData);
54 // 3.5. 发送成功,修改已经发送成功数据index值
55 self.sendDataIndex += amountToSend;
56 // 3.6. 如果是最后一次需要发送
57 if (self.sendDataIndex >= self.dataToSend.length) {
58 // 3.6.1. 把 标识是否为最后一次发送 改为YES
59 sendingEOM = YES;
60 // 3.6.2. 发送
61 BOOL eomSent = [self.peripheralManager updateValue:[@"EOM" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.customCharacteristic onSubscribedCentrals:@[self.central]];
62 if (eomSent) {
63 // 3.6.3. 发送成功,则我们已经完成这个功能
64 sendingEOM = NO;
65 NSLog(@"Sent: EOM");
66 }
67 return;
68 }
69 }
70 }
2.5 peripheral再次准备好发送Characteristic值的更新时候调用

1 - (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral
2 {
3 [self sendData];
4 }
2.6 当central取消订阅characteristic这个特征的值后调用方法

1 - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic
2 {
3 NSLog(@"central=%@设备拒绝请求,%s,",central,__PRETTY_FUNCTION__);
4 }
二、中心
1.在中心的.h文件中定义如下

1 /**
2 * 中心角色
3 */
4 @property (nonatomic , strong) CBCentralManager *centralManager;
5 @property (nonatomic , strong) CBPeripheral *peripheral;
6 @property (nonatomic, strong) CBCharacteristic *character;
7 @property (nonatomic , strong) NSMutableData *data;
2.然后在.m文件中进行如下操作
2.1在viewDidLoad方法中初始化中心和要发送的数据

1 // 1.建立中心角色,queue为执行队列,默认为主线程
2 self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
3 // 2.初始化显示控件
4 [self setupUI];
5 // 3.初始化data
6 _data = [[NSMutableData alloc] init];
2.2 CBCentralManagerDelegate代理方法
回调,返回当前设备蓝牙状态

1 #pragma mark - CBCentralManagerDelegate
2 #pragma mark 回调,返回当前设备蓝牙状态
3 - (void)centralManagerDidUpdateState:(CBCentralManager *)central {
4
5 switch (central.state) {
6 case CBCentralManagerStatePoweredOn:{
7 // 扫描外设(discover)
8 _messageLabel.text = @"蓝牙设备已经打开,可以扫描外设";
9 [self scan];
10 }
11 break;
12 default:
13 break;
14 }
15 }
16
17 #pragma mark - 扫描外设(discover) 超时停止扫描
18 - (void)scan{
19 /**
20 * 第一个参数指定了要搜寻的服务,如果传nil,表示搜寻搜所有的服务
21 * 第二个参数指定了CBCentralManagerScanOptionAllowDuplicatesKey为已发现的设备是否重复扫描,
22 * 如果是同一设备不会多次回调。
23 */
24 [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];
25
26 }
27
发现外设,回调

1 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
2
3 NSLog(@"找到了服务:%@,信号质量:%@",advertisementData,RSSI);
4 self.serviceName = [advertisementData objectForKey:@"kCBAdvDataLocalName"];
5
6 if (self.peripheral != peripheral) {
7 self.peripheral = peripheral;
8 NSLog(@"开始连接blueServer:%@",peripheral);
9 [self.centralManager connectPeripheral:self.peripheral options:nil];
10 }
11 }
连接失败,回调

1 - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
2 NSLog(@"Failed to connect to %@. (%@)", peripheral, [error localizedDescription]);
3 [self cleanup];
4 }
连接成功,回调

1 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
2 //因为一个peripheral可能不止提供一个service
3 NSLog(@"连接成功,进一步获取peripheral的service");
4 [self.centralManager stopScan];
5
6 // 每过 0.1 秒钟执行一次 readBLEServiceRSSI 方法
7 self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(readBLEServiceRSSI) userInfo:nil repeats:YES];
8
9 [self.data setLength:0];
10
11 // 设置代理
12 self.peripheral.delegate = self;
13
14 //查询符合条件的外设
15 [self.peripheral discoverServices:@[[CBUUID UUIDWithString:kServiceUUID]]];
16 }
17
18
失去连接,回调
1 - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
2 NSLog(@"Failed to connect to %@. (%@)", peripheral, [error localizedDescription]);
3 [self cleanup];
4 }
2.3 CBPeripheralDelegate代理方法
读取到RSSI值后的操作

1 - (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(NSError *)error {
2 NSLog(@"%s,%@",__PRETTY_FUNCTION__,peripheral);
3 int rssi = abs([peripheral.RSSI intValue]);
4 CGFloat ci = (rssi - 49) / (10 * 4.);
5
6 self.messageLabel.text = [NSString stringWithFormat:@"发现BLT4.0热点:%@,距离:%.1fm",self.serviceName,pow(10,ci)];
7 }
找到我们刚刚指定的服务

1 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
2 if (error) {
3 NSLog(@"%s,error=%@", __PRETTY_FUNCTION__, error.localizedDescription);
4 [self cleanup];
5 return;
6 } else {
7 for (CBService *service in peripheral.services) {
8 NSLog(@"发现UUID=%@的服务",service.UUID);
9 NSLog(@"开始检测这个服务的特征码...");
10 if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]]) {
11 [self.peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:kCharacteristicUUID]] forService:service];
12 }
13 }
14 }
15 }
如果一个特征被检测到,回调

1 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
2 if (error) {
3 NSLog(@"%s,%@",__PRETTY_FUNCTION__,error);
4 [self cleanup];
5 return;
6 } else {
7 if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]]) {
8 for (CBCharacteristic *characteristic in service.characteristics) {
9 if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCharacteristicUUID]]) {
10 [peripheral setNotifyValue:YES
11 forCharacteristic:characteristic];
12 self.character=characteristic;
13 }
14 }
15 }
16 }
17 }
处理蓝牙发过来的数据

1 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
2 if (error) {
3 NSLog(@"didUpdateValueForCharacteristic - Error discovering characteristics: %@", [error localizedDescription]);
4 return;
5 }
6 NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
7 // 如果我们获得了所有的数据
8 if ([stringFromData isEqualToString:@"EOM"]) {
9 // 显示数据
10 [self alarm:self.data];
11 // 取消订阅
12 [peripheral setNotifyValue:NO forCharacteristic:characteristic];
13 // 让中心失去和设备的连接
14 [self.centralManager cancelPeripheralConnection:peripheral];
15 }
16 // 否则,拼接数据
17 [self.data appendData:characteristic.value];
18 // Log it
19 NSLog(@"Received: %@", stringFromData);
20 }
外围设备是否被注销

1 - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
2 if (error) {
3 NSLog(@"%s,%@",__PRETTY_FUNCTION__,error.localizedDescription);
4 }
5 if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:kCharacteristicUUID]]) {
6 return;
7 }
8 //已经发送通知
9 if (characteristic.isNotifying) {
10 NSLog(@"Notification began on %@",characteristic);
11 } else {
12 //Notification has stopped
13 //so disconnect from the peripheral
14 NSLog(@"Notification stopped on %@ Disconnecting",characteristic);
15 [self.centralManager cancelPeripheralConnection:self.peripheral];
16 }
17 }
2.4 出现错误或者结束连接时调用(私有方法)
1 #pragma mark - Call this when things either go wrong, or you're done with the connection.
2 - (void)cleanup {
3 // Don't do anything if we're not connected
4 if (!self.peripheral.isConnected) {
5 return;
6 }
7 // See if we are subscribed to a characteristic on the peripheral
8 if (self.peripheral.services != nil) {
9 for (CBService *service in self.peripheral.services) {
10 if (service.characteristics != nil) {
11 for (CBCharacteristic *characteristic in service.characteristics) {
12 if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCharacteristicUUID]]) {
13 if (characteristic.isNotifying) {
14 // It is notifying, so unsubscribe
15 [self.peripheral setNotifyValue:NO forCharacteristic:characteristic];
16 // And we're done.
17 return;
18 }
19 }
20 }
21 }
22 }
23 }
24 // If we've got this far, we're connected, but we're not subscribed, so we just disconnect
25 [self.centralManager cancelPeripheralConnection:self.peripheral];
26 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!