purpose

Recently, the company is working on an iOS Bluetooth project. In the process of development, some basic knowledge related to it has been briefly sorted out and shared here. The arrangement includes the following:

IOS Bluetooth development keywords

A brief introduction to Bluetooth

CoreBluetooth framework

Data transfer between Bluetooth peripherals and central devices

Bluetooth development for iOS is implemented around the CoreBluetooth framework.

Let’s start with the basic concepts of iOS Bluetooth development.

A, iOS Bluetooth development keywords

Center device: it is used to scan the surrounding Bluetooth hardware, for example, through your mobile phone bluetooth to scan and connect to the smart bracelet, at this time your mobile phone is the center device.

Peripheral: the device being scanned. For example, when you connect a smart bracelet with your phone’s Bluetooth scan, it’s a peripheral.

Central equipment and external equipment

Broadcast: The peripheral continuously transmits bluetooth signals that can be scanned by the central device.

Peripherals radio

Services: Peripherals broadcast and run with services, which can be understood as a function module, which can be read by the central device. Peripherals can have multiple services.

A unit in a service. A service can have multiple characteristics. Characteristics have a value, which is the value of read and write data.

Services and features. PNG

UUID: identifies services and features. We can use UUID to pick the services and features we need.

Two, a brief introduction of Bluetooth

Slacker: Bluetooth Encyclopedia

Bluetooth? : is a short-range wireless communication technology that can realize short-range data exchange (using UHF radio waves in ISM band 2.4 — 2.485GHz) between fixed equipment, mobile devices and building personal domain networks. Bluetooth 4.2 was released on December 2, 2014. At the time of this publication, the latest version of Bluetooth was 4.2.

CoreBluetooth framework

Bluetooth development hierarchy diagram

As shown in the figure above, The Bluetooth development framework CoreBluetooth in iOS is on top of the Bluetooth low-power protocol stack. When we develop, we only use CoreBluetooth this framework, through which we can easily achieve the development of peripherals or central devices.

CoreBluetooth can be divided into two modules, central and peripheral, each of which has its own set of apis for us to use.

PNG for central devices and peripherals

On the left is the development class for the central device. We usually use CBCentralManager for related operations.

CBCentralManager: Bluetooth center device management class, used for unified scheduling center device development CBPeripheral: Bluetooth peripherals, such as Bluetooth bracelet, heart rate monitor. CBService: Services of bluetooth peripherals. There can be zero or more services. CBCharacteristic: Features of a service. Each Bluetooth service can have zero or more features, including data information. CBUUID: An ID that can be understood as a service or feature and can be used to select the desired service and feature. On the right are the peripheral development related classes, which are typically coded around the CBPeripheralManager.

CBPeripheralManager: A central management class for Bluetooth peripherals. CBCentral: Bluetooth central device, such as a mobile phone used to connect to a Bluetooth bracelet. CBMutableService: Multiple services can be added during peripheral development, so CBMutableService is used to create the added service. CBMutableCharacteristic: There can be multiple features per service, which is used when peripheral development adds features to services. CBATTRequest: read or write requests. Its instance object has a value property that loads data from peripherals for Bluetooth read or write requests. This parameter is typically found in callback methods that peripheral writes or reads.

Peripherals add services and features

4. IOS Bluetooth Peripherals (Demo)

Peripheral manager

1. First import the CoreBluetooth framework and comply with the protocol

# import / / observe CBPeripheralManagerDelegate protocol @ interface ViewController () 2, create a peripheral management object, with an attribute to strong reference to the object. And set up the proxy at creation time, declaring which thread to put it on.

@property (nonatomic, strong) CBPeripheralManager *peripheralManager;

// Create a peripheral manager, Self will callback peripheralManagerDidUpdateState method. PeripheralManager = [[CBPeripheralManager alloc] initWithDelegate: self queue:dispatch_get_main_queue()]; 3. When CBPeripheralManager is created, the method to determine bluetooth status is called back. Create Service and Characteristics for peripherals when bluetooth status is fine.

Equipment state of bluetooth CBManagerStateUnknown = 0, unknown CBManagerStateResetting CBManagerStateUnsupported reset, Does not support CBManagerStateUnauthorized, not verify CBManagerStatePoweredOff, don’t start CBManagerStatePoweredOn, available * /

  • (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { if (peripheral.state == CBManagerStatePoweredOn) {

    / / create the Service (Service) and Characteristics (features) [self setupServiceAndCharacteristics]; / / according to the service of the UUID began broadcasting [self. PeripheralManager startAdvertising: @ {CBAdvertisementDataServiceUUIDsKey: @ [[CBUUID UUIDWithString:SERVICE_UUID]]}];Copy the code

    }}

You can start by using macros to make two identity strings that create UUids for services and characteristics. Finally, the created characteristics are put into the service, and the service is put into the central manager.

#define SERVICE_UUID @”CDD1″ #define CHARACTERISTIC_UUID @”CDD2″

/** Create services and features */

  • (void)setupServiceAndCharacteristics {

  • // Create a service

    CBUUID *serviceID = [CBUUID UUIDWithString:SERVICE_UUID]; CBMutableService *service = [[CBMutableService alloc] initWithType:serviceID primary:YES]; / / create the service characteristics of CBUUID * characteristicID = [CBUUID UUIDWithString: CHARACTERISTIC_UUID]; CBMutableCharacteristic *characteristic = [ [CBMutableCharacteristic alloc] initWithType:characteristicID properties: CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite | CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable ]; Characteristics = @[characteristic]; [self.peripheralManager addService:service];

    // To manually send data to the central device self.characteristic = characteristic;

}

Note CBCharacteristicPropertyNotify this parameters, only set up the parameters, can subscribe to this feature in the center of equipment.

In general, two features can be set in the development, one for sending data, and the other for receiving data written by the central device. We only set one feature here for convenience.

Finally, a property is used to obtain this feature, which is used for sending data separately later. Data writing and reading are ultimately completed through the feature.

4. This method is called back by the central device when it reads data from the peripheral.

/** Callback when the central device reads data */

  • (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
  • / / in the request data, send data in a text box to the center equipment request here. The value = [self. TextField. Text dataUsingEncoding: NSUTF8StringEncoding]; / / success response request [peripheral respondToRequest: request withResult: CBATTErrorSuccess];

}

5. When the central device writes data, the peripheral calls the following method.

/** Callback when the central device writes data */

  • (void)peripheralManager (CBPeripheralManager *) Peripheral didReceiveWriteRequests (NSArray *)requests {// Requests for writing data CBATTRequest *request = requests.lastObject; / / to write the data showed that the self in the text box. The textField. Text = [[nsstrings alloc] initWithData: request. The value encoding: NSUTF8StringEncoding]; } 6. There is another way to actively send data to the central device.

/** Sends data to the central device via fixed features */

  • (IBAction)didClickPost:(id)sender {
  • BOOL sendSuccess = [self.peripheralManager updateValue:[self.textField.text dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.characteristic onSubscribedCentrals:nil]; If (sendSuccess) {NSLog(@” data sent successfully “); }else {NSLog(@” failed to send data “); }

}

7, the central device subscription success when the callback.

/** subscribed successfully callback */ -(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic { NSLog(@”%s”,FUNCTION); } 8. Callback when the central device unsubscribes.

1 2 3 4 /** unsubscribe callback */ -(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic { NSLog(@”%s”,FUNCTION); } This is the basic implementation process of iOS Bluetooth peripherals, of course, there are more areas that can be further handled, which requires more time to learn and experiment.

Let’s move on to the main part of iOS Bluetooth development, the implementation of the central device, which is the role that mobile apps usually play.

5. IOS Bluetooth Center Device (Demo)

Central device manager

1. As with peripheral development, import the CoreBluetooth framework first.

Different from peripheral development, the development of central equipment needs to follow the following two protocols.

@interface ViewController () 3, create the central manager with a strong reference to the property, also set the proxy and select thread.

@property (nonatomic, strong) CBCentralManager *centralManager;

// Create a central device manager, Will the callback centralManagerDidUpdateState self. CentralManager = [[CBCentralManager alloc] initWithDelegate: self queue:dispatch_get_main_queue()]; 4. When creating a managed object in the center, the following methods are called back to determine the Bluetooth status of the center device. When the Bluetooth status is fine, you can scan the required peripherals according to the UUID of the peripheral service. So the natural thought was to define a macro that was the same as the peripheral UUID.

*/ #define Service_uid @”CDD1″ #define Characteristic_uid @”CDD2″

  • (void)centralManagerDidUpdateState:(CBCentralManager *)central {
  • If (central.state == CBManagerStatePoweredOn) {NSLog(@” Bluetooth available “); // Scan peripherals against SERVICE_UUID. If SERVICE_UUID is not set, The scan all bluetooth devices [central scanForPeripheralsWithServices: @ [[CBUUID UUIDWithString: SERVICE_UUID]] options: nil]; } the if (central state = = CBCentralManagerStateUnsupported) {NSLog (@ “the device does not support bluetooth”); } the if (central state = = CBCentralManagerStatePoweredOff) {NSLog (@ “bluetooth closed”); }

}

5. After the peripheral is scanned, the following method is called back, and you can continue to set filter criteria in this method, for example, select by the prefix of the peripheral name, and connect if the condition is met.

/** If a peripheral meets the requirement, call */

  • (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral AdvertisementData :(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {// strong reference self.peripheral = peripheral;

/ / if ([peripheral name hasPrefix: @ “WH”]) {/ / / / may, according to a peripheral name to filter the peripherals / / [central connectPeripheral: peripheral options: nil]; / /}

/ / connected peripherals [central connectPeripheral: peripheral options: nil];Copy the code

} 7, when the connection is successful, the following method is brought up. To save power, ask the central device to stop scanning when the peripherals are connected, and don’t forget to set up a proxy for the connected peripherals. In this method, a service lookup is performed based on the UUID.

/** The connection succeeded */

  • (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
  • // can stopScan [self.centralmanager stopScan]; // Set peripheral. Delegate = self; [CBUUID UUIDWithString:SERVICE_UUID]] [Peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]] NSLog(@” successful connection “);

}

8. Connection failures and disconnections also have their own callback methods. When disconnected, we can set up automatic reconnection and customize the code according to the project requirements.

/ connection failure callback * / * * – (void) centralManager: (CBCentralManager *) central didFailToConnectPeripheral: (peripheral CBPeripheral *) Error :(NSError *)error {NSLog(@” connection failed “); }

/** Disconnect */

  • (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
  • NSLog(@” disconnect “); / / disconnect connection can be set to [central connectPeripheral: peripheral options: nil]; }

9. Let’s start with the proxy methods.

The first step is to discover ways to serve. This method iterates through the services to find the ones you need. Since the peripherals above only have one service, I will simply fetch the last lastObject in the service.

After the service is found, the coherent action continues to look for features in the service based on their UUID.

/** Discover service */

  • (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {

    For (CBService *service in peripheral. Services) {NSLog(@” all services: %@”,service); }

    / / here is only one service, so direct access CBService * service = peripheral services. LastObject; / / according to the characteristics in UUID for service [peripheral discoverCharacteristics: @ [[CBUUID UUIDWithString: CHARACTERISTIC_UUID]] forService:service];

}

The following method does a lot of things.

Once a feature is discovered, like a service, you can iterate over the feature, find the different feature based on the documentation given by the peripheral developer, and take action accordingly.

My peripheral only has one feature set, so I get the feature directly from lastObject.

Again, there are two features that can be set up in development, one for sending data and one for receiving data written by the central device.

A feature is referenced by a property in order to write data or send instructions to the peripheral later.

ReadValueForCharacteristic method is directly read the data on the once this characteristic.

/** Found feature callback */

  • (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {

    For (CBCharacteristic *characteristic in service.characteristics) {NSLog(@” all characteristic: %@”, characteristic); // Get the UUID from the peripheral developer for different features that do different things, such as read features and write features}

    / / here for only a feature of writing data need to use this feature when the self. The characteristic = service. Characteristics. LastObject;

    / / this characteristic directly read data, will be called didUpdateValueForCharacteristic [peripheral readValueForCharacteristic: self. The characteristic];

    / / subscription notice [peripheral setNotifyValue: YES forCharacteristic: self. The characteristic]; }

SetNotifyValue :(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic method is to subscribe to this characteristic. After the subscription is successful, the characteristic worth changing in the peripheral can be monitored.

11. The following methods come in handy when the status of a subscription changes.

/** changes to the subscription state */ -(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {

If (error) {NSLog(@" subscribe failed "); NSLog(@"%@",error); {} the if (characteristic isNotifying) NSLog (@ "subscribe to success"); } else {NSLog(@" unsubscribe "); }Copy the code

}

12. Peripherals can send data to the central device, and the central device can read data from peripherals, and this method will be called back when this happens. Get the original data through the value attribute in the special, and then parse the data according to the requirements.

/** Received data callback */

  • (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
  • NSData *data = characteristic. Value; self.textField.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; }

13. The central device can write data to peripherals, or send requests or instructions to peripherals. What to do when these operations need to be carried out?

I first convert the data I’m writing to NSData format, and then based on the characteristics of the data I’m writing to, Use the method writeValue:(NSData)data forCharacteristic:(CBCharacteristic)characteristic Type: (CBCharacteristicWriteType) type for data to write. When writing data, System will also callback method the peripheral (CBPeripheral) peripheral didWriteValueForCharacteristic: nonnull CBCharacteristic) characteristic Error error: (nullable NSError *).

/** Write data */

  • (IBAction)didClickPost:(id)sender {
  • / / use NSData type to write NSData * data = [self. TextField. Text dataUsingEncoding: NSUTF8StringEncoding]; / / according to the features of the self characteristic to write data [self. The peripheral writeValue: data forCharacteristic: self. The characteristic type:CBCharacteristicWriteWithResponse]; }

/** Write data callback */

  • (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic Error :(nullable NSError *)error {NSLog(@” write successfully “); }

14. How does the central device actively read data from peripherals?

Object with connecting peripherals to invoke readValueForCharacteristic method, and is going to read the characteristics of the data as a parameter, this can take the initiative to take a data. Go to the callback method in step 12 and get the data for this time in the value property of the attribute.

/** Read data */

  • (IBAction)didClickGet:(id)sender {
  • [self.peripheral readValueForCharacteristic:self.characteristic];

}

Afterword.

The development of central equipment needs to be carried out with peripherals. Generally, hardware engineers or embedded engineers will provide communication protocols and operate the various requirements of the project according to the protocols.