Recently, I have studied the function of connecting bluetooth printer in iOS to print shopping receipts, and have a certain understanding of the use of BLE 4.0 in iOS. Here is my understanding of BLE 4.0.
Since many articles talk about CBCentralManager and CBPeripheralManager at the same time, it’s easy to be confused. IPhone is rarely used as bluetooth peripheral to broadcast data. Today, I will talk about the use of BLE 4.0 from the perspective of iOS app development.
concept
CBPeripheral Bluetooth peripherals, such as bluetooth bracelet, Bluetooth heartbeat monitor, Bluetooth printer. CBCentralManager Bluetooth peripherals management center. Associated with the Bluetooth hardware template of a mobile phone, it can obtain the status of bluetooth modules in a mobile phone, but it only manages Bluetooth peripherals.
CBService Indicates the service of a Bluetooth peripheral. Each bluetooth peripheral has zero or more services. Each Bluetooth service may contain zero or more Bluetooth services, or zero or more Bluetooth features.
CBCharacteristic Each Bluetooth feature contains data or information.
Analysis of the
Our general interaction is that app serves as the client, and the actual data of users is mostly stored on the server. Therefore, app client takes the initiative to obtain data from the server through the network interface, and then displays these data in APP.
Bluetooth is a little different. App is the CBCentralManager, but it is also the client. But the actual data is coming from the CBPeripheral, the Bluetooth bracelet or something like that, so CBPeripheral is a server, but the Bluetooth data transmission is the server broadcasting the data all the time, After the APP client connects to listen to a Bluetooth, it will receive the data display sent by it.
Bluetooth peripherals broadcast data regardless of whether other devices are connected to them.
Scenario 1 only involves reading data from bluetooth peripherals
The bluetooth bracelet
The Bluetooth bracelet keeps broadcasting the heartbeat and the number of steps. When our APP connects to the Bluetooth bracelet through Bluetooth, we can obtain the broadcast data in the proxy method of the peripheral, and then update the data in the UI of the APP.
Scenario two: Write data to bluetooth peripheral
Bluetooth printer
Bluetooth printer is an app that connects to bluetooth printer through Bluetooth and uses the proxy method of peripherals to write data into the Bluetooth printer, then the Bluetooth printer will automatically print out receipts.
Scenario 3 Two iOS devices Use the APP to transfer files to each other
A device cannot be both a peripheral and a management center.
It can broadcast and send data and get data from other devices, but it can only play one role. If iOS device A actively connects to device B via Bluetooth, then device A is CBCentral and device B is CBPeripheral. But if device B is connected to device A, then device B is CBCentral, and device A is CBPeripheral.
# Code in action
Step 1, create CBCentralManager. The second step is to scan the connected Bluetooth peripherals (only with the Bluetooth module turned on). Third, connect the target Bluetooth peripheral. The fourth step is to query the service under the target Bluetooth peripheral. The fifth step is to traverse the features in the service, get the data in the features or save some writable features, or set some feature values change, notify the active acquisition. Sixth, read the data from the property in the notification update property median method (with the notification set to YES). Seventh, read the values in the property. Step 8, if there is writable feature and need to write data to bluetooth peripheral, write data to bluetooth peripheral.
The first is to create a CBCentralManager in our app:
// create a management center. CBCentralManager *manager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];Copy the code
Once created, CBCentralManagerDelegate’s proxy method is called:
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSLog(@"% @",central);
switch (central.state) {
case CBCentralManagerStatePoweredOn:
NSLog(@"Open, available.");
[_manager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@(NO)}];
break;
case CBCentralManagerStatePoweredOff:
NSLog(@"Available, not open");
break;
case CBCentralManagerStateUnsupported:
NSLog(@"SDK not supported");
break;
case CBCentralManagerStateUnauthorized:
NSLog(@"Program not authorized");
break;
case CBCentralManagerStateResetting:
NSLog(@"CBCentralManagerStateResetting");
break;
case CBCentralManagerStateUnknown:
NSLog(@"CBCentralManagerStateUnknown");
break; }}Copy the code
This proxy method calls back when the state of the Bluetooth template changes. You should search and scan the list of available Bluetooth peripherals while Bluetooth is on. Scanning bluetooth peripherals is done by:
- (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options;
Copy the code
The first parameter is the CBUUID array of the service. We can search for bluetooth devices that have a certain type of service. When the Bluetooth peripherals are scanned, the delegate method of CBCentralManagerDelegate is called:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;
Copy the code
This method returns information about only one Bluetooth peripheral at a time. The second parameter is the scanned Bluetooth peripherals, the third parameter is the additional data in the Bluetooth peripherals, and RSSI is the signal strength parameter.
Because a bluetooth may be useless or a bluetooth may be repeatedly scanned, we need to remove some useless Bluetooth and replace the old Bluetooth peripherals (the parameters of the peripherals may change, not the data carried, but the parameters of the peripherals themselves).
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI
{
if (peripheral.name.length <= 0) {
return ;
}
NSLog(@"Discovered name:%@,identifier:%@,advertisementData:%@,RSSI:%@", peripheral.name, peripheral.identifier,advertisementData,RSSI);
if (self.deviceArray.count == 0) {
NSDictionary *dict = @{@"peripheral":peripheral, @"RSSI":RSSI};
[self.deviceArray addObject:dict];
} else {
BOOL isExist = NO;
for (int i = 0; i < self.deviceArray.count; i++) {
NSDictionary *dict = [self.deviceArray objectAtIndex:i];
CBPeripheral *per = dict[@"peripheral"];
if ([per.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]) {
isExist = YES;
NSDictionary *dict = @{@"peripheral":peripheral, @"RSSI":RSSI}; [_deviceArray replaceObjectAtIndex:i withObject:dict]; }}if(! isExist) { NSDictionary *dict = @{@"peripheral":peripheral, @"RSSI":RSSI};
[self.deviceArray addObject:dict];
}
}
[self.tableView reloadData];
}
Copy the code
This gives us a list of Bluetooth devices, which we can display in a table
Connect a Bluetooth peripheral in the cell click event:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dict = [self.deviceArray objectAtIndex:indexPath.row];
CBPeripheral *peripheral = dict[@"peripheral"]; / / to connect a bluetooth peripherals [self. Manager connectPeripheral: peripheral options:@{CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES)}]; // Set the proxy of the peripheral to query the services and features of the peripheral, as well as the data in the features. [peripheralsetDelegate:self]; // Now that you are connected to a Bluetooth, there is no need to continue scanning peripherals. [tableView deselectRowAtIndexPath:indexPath animated:YES]; }Copy the code
After a peripheral is successfully connected, look up the services it has
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@"didConnectPeripheral"); // Find peripheral discoverServices:nil. } - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error { NSLog(@"didFailToConnectPeripheral");
}
Copy the code
The proxy method to find the service is in the CBPeripheralDelegate:
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error
{
NSString *UUID = [peripheral.identifier UUIDString];
NSLog(@"didDiscoverServices:%@",UUID);
if (error) {
NSLog(@"Error");
return;
}
CBUUID *cbUUID = [CBUUID UUIDWithString:UUID];
NSLog(@"cbUUID:%@",cbUUID);
for (CBService *service in peripheral.services) {
NSLog(@"service:%@",service.UUID); // If we know the CBUUID of the feature we want to query, we can pass in the CBUUID array in parameter 1. [peripheral discoverCharacteristics:nilforService:service]; }}Copy the code
Then iterate over the features in the service:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error
{
if (error) {
NSLog(@"Error");
return;
}
for (CBCharacteristic *character inService. Characteristics) {/ / this is the properties of an enumeration type CBCharacteristicProperties properties = character. The properties;if(the properties & CBCharacteristicPropertyBroadcast) {/ / if it is broadcast features}if(the properties & CBCharacteristicPropertyRead) {/ / if you are reading property that can read the features of value [peripheralreadValueForCharacteristic:character];
}
if(the properties & CBCharacteristicPropertyWriteWithoutResponse) {/ / if you are writing values do not need to response features / / here can save the writing features, _chatacter = character; }if(the properties & CBCharacteristicPropertyWrite) {/ / if you are writing features, this should have some response}if(the properties & CBCharacteristicPropertyNotify) {/ / if having the features of notice, no response [peripheralsetNotifyValue:YES forCharacteristic:character]; }}}Copy the code
The proxy method for notification is then as follows:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error
{
if (error) {
NSLog(@"Error didUpdateNotification: %@",error);
return;
}
CBCharacteristicProperties properties = characteristic.properties;
if(the properties & CBCharacteristicPropertyRead) {/ / if you are reading property that can read the features of value [peripheralreadValueForCharacteristic:characteristic]; }}Copy the code
Read value from a feature as follows:
/ / read the result of the new value - (void) peripheral (CBPeripheral *) peripheral didUpdateValueForCharacteristic: (CBCharacteristic *)characteristic error:(NSError *)error {if (error) {
NSLog(@"Error: %@",error);
return;
}
NSData *data = characteristic.value;
if (data.length <= 0) {
return;
}
NSString *info = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"info:%@",info);
}
Copy the code
At this point, you are done retrieving the values that the Bluetooth peripheral broadcasts.
To write data to bluetooth peripherals, call the following method:
[peripheral writeValue:infoData forCharacteristic:_chatacter type:CBCharacteristicWriteWithoutResponse];
Copy the code
Except that the _chatacter argument here should be the writable property iterated over when iterating over the server’s properties. Data cannot be written to bluetooth peripherals if they do not have writable features.
In addition, the method of canceling the connection with a Bluetooth peripheral is as follows:
[self.manager cancelPeripheralConnection:peripheral];
Copy the code
CBCentralManagerDelegate also has a delegate method for disconnecting from Bluetooth:
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
Copy the code
IOS 10 supplement
The @ one feet kicked up remind: developer.apple.com/reference/c…
Important: To protect user privacy, an iOS app linked on or after iOS 10.0, and which accesses the Bluetooth interface, Must statically declare the intent to do so. Include the NSBluetoothPeripheralUsageDescription key in your app ‘s Info.plist file and provide a purpose string for this key. If your app attempts to access the Bluetooth interface Without a corresponding purpose string, your app exits. This key is supported in iOS 6.0 and later.
But I test in iOS 10.0.1 test, do not add NSBluetoothPeripheralUsageDescription, engineering can still normal use. Then add NSBluetoothPeripheralUsageDescription,
There is no notification like location, push, etc 😞 😞 😞 when the app starts. The bluetooth feature doesn’t have a list of apps that are currently allowed in its Settings, but apple is probably planning that in the future.
supplement
Given that people often ask why you can find bluetooth printers in projects, but not other phones?
That’s because bluetooth technology has developed from 1.x to 4.0, and the materials and technologies used in Bluetooth communication have changed. That’s why some printers support 2.0, 3.0, and 4.0. If you’re using the CoreBluetooth library and your printer doesn’t support bluetooth 4.0, you won’t be able to search for a bluetooth printer.
It’s unclear what technology is used to enable bluetooth search in the phone’s Settings, and whether it is compatible with 2.0, 3.0 or 4.0.
CoreBluetooth libraries are not the only one available in iOS.
Gamekit. framework: bluetooth communication framework prior to iOS7. It expired with iOS7, but most apps are still based on this framework.
MultipeerConnectivity. Framework: iOS7 began to introduce the new bluetooth communication development framework, is used to replace GameKit.
Corebluetooth. framework: A powerful Bluetooth development framework that requires devices to support Bluetooth 4.0.
Learn more about Bluetooth:
Bluetooth — Baidu Baike
Just look at the introduction of the three Bluetooth libraries in iOS
The basic use of Bluetooth is over here! Have fun!