Bluetooth profile

Bluetooth® is a wireless technology standard that enables short-range data exchange (using UHF radio waves in the ISM band of 2.4 — 2.485GHz) between fixed devices, mobile devices, and personal building networks. Bluetooth technology was first developed by telecom giant Ericsson in 1994 as an alternative to RS232 data lines. Bluetooth can connect multiple devices, overcoming the problem of data synchronization. Today Bluetooth is governed by the Bluetooth Special Interest Group, or SIG. The Bluetooth Technology Alliance has more than 25,000 member companies around the world in telecommunications, computing, networking, and consumer electronics. IEEE classifies Bluetooth technology as IEEE 802.15.1, but no longer maintains this standard. The Bluetooth Technology Alliance oversees the development of bluetooth specifications, administers certification programs, and defends trademark rights. Manufacturers’ devices must meet the Standards of the Bluetooth Technology Alliance to enter the market as “Bluetooth devices”. Bluetooth technology has a proprietary network that can be issued to standards-compliant devices.

Bluetooth development

Bluetooth 1.1 Standard

1.1 is the earliest version, the transmission rate is about 748~ 810KB /s, because it is early design, easy to be affected by the interference of the same frequency products affect the communication quality.

Bluetooth 1.2 Standard

1.2 The same is only 748~ 810KB /s transmission rate, but added (improve Software) anti-jamming frequency hopping function.

Bluetooth 2.0 Standard

2.0 is an improved version of 1.2. It has a transmission rate of between 1.8M/s and 2.1M/s. It supports duplex mode, which allows simultaneous voice communication and file/high quality image transmission. The most widely used standard is Bluetooth2.0+EDR standard, which has been launched in 2004, support of Bluetooth2.0+EDR standard products also appeared in 2006. Although Bluetooth2.0+EDR standard made a lot of technical improvements, but from the 1.x standard continued to the configuration process complex and large power consumption of equipment still exist.

Bluetooth 2.1 Standard

The Bluetooth Technology Alliance today officially approved the Bluetooth version 2.1 specification, known as “Bluetooth 2.1+EDR”, for free use by future devices. Contemporary products with version 2.0 still occupy a large share of the Bluetooth market. Compared with version 2.0, the standby time has been increased by more than 2 times, and the technical standards have not changed fundamentally.

Bluetooth 3.0 Standard

On April 21, 2009, bluetooth technology alliance (BluetoothSIG) formally issued a new generation of standards “BluetoothCoreSpecificationVersion3.0 HighSpeed” (the bluetooth core specification version 3.0), At the heart of Bluetooth 3.0 is “Generation Alternatemac /PHY” (AMP), a new alternative RF technology that allows the Bluetooth protocol stack to dynamically select the right RF for any task. Bluetooth 3.0 improves data transfer rates to around 24Mbps(802.11 Wi-Fi can be called up when needed for high-speed data transfer). Bluetooth 3.0 is eight times faster than Bluetooth 2.0 and can easily be used to transfer data between VCRS and HDTVS, PC-to-PMP, and UMPC-to-printers, but both parties need to meet this standard to function.

Bluetooth 4.0 Standard

The bluetooth 4.0 specification was released on July 7, 2010. The new version features low power consumption, improved device compatibility between different Oems, and reduced latency. The theoretical maximum transmission speed is still 24Mbps(3MB/s), and the effective range is extended to 100 meters (up from 10 meters). The standard chip is used by a large number of mobile phones and tablets, such as apple’s TheNewiPad tablet computer, as well as Apple’s iPhone5, Meizu MX4, HTCOneX and other mobile phones with Bluetooth 4.0 function.

Bluetooth 4.1 Standard

Bluetooth 4.1 was released on December 6, 2013, and automatically coordinates data transmissions with LTE radio signals, theoretically reducing interference from other signals. Improvement is to improve the connection speed and more intelligent, reduced the amount of time between equipment to reconnect, for example, means that the user if out of the bluetooth 4.1 signal range and disconnected time is not long, when the user returned signal devices will be automatically connected to the scope of, shorter reaction time than bluetooth 4.0. A final improvement is to improve transmission efficiency. If a user is connected to a large number of devices, such as multiple wearables, messages from each other can be sent to the receiving device in real time. In addition, Bluetooth 4.1 also adds more flexibility for developers. This change is not significant for the average user, but it is important for software developers because bluetooth must be able to connect multiple devices at the same time in order to cope with the rise of wearable devices.

Bluetooth 4.2 Standard

On December 4, 2014, the latest Bluetooth 4.2 standard was released. The announcement of the Bluetooth 4.2 standard not only improves data transmission speed and privacy protection, but also enables the device to connect directly to the Internet via IPv6 and 6LoWPAN. The first is getting faster in terms of speed. Although Bluetooth version 4.1 is an improvement over previous versions, it is far from satisfying users’ needs and lacks advantages compared to Wi-Fi. The Bluetooth 4.2 standard can hold about 10 times as much data through Bluetooth Smart packets, and the speed of data transfer between two Bluetooth devices is 2.5 times faster. Secondly, the degree of privacy protection has also been strengthened by many users. As we know, Bluetooth 4.1 and its previous versions have some privacy security risks — after a connection, it automatically connects without further confirmation, which may cause privacy leakage. According to the new standard of Bluetooth 4.2, the Bluetooth signal must be approved by the user if it wants to connect or track the user’s device. Otherwise, the Bluetooth signal will not be able to connect or track the user’s device. The most anticipated feature, of course, is the new version’s ability to access the Internet via IPv6 and 6LoWPAN. As early as bluetooth 4.1, the Bluetooth Technology Alliance has tried to access, but due to the limitations of the transmission rate of the previous version and the incompatibility of the network chip, this function was not fully implemented. According to the Bluetooth Technology Alliance, the new Bluetooth 4.2 standard already provides direct Internet access via IPv6 and 6LoWPAN. On this basis, once IPv6 and 6LoWPAN are widely used, this feature will attract more attention. It should also be noted that some of the features of Bluetooth 4.2 will be available through software upgrades for older adapters, but not all of them. According to the Bluetooth Technology Alliance, “Privacy features may be available through firmware upgrades, depending on the manufacturer’s installation. The ability to speed up and expand packets will require hardware upgrades.” Bluetooth 4.0 is by far the most commonly used standard for consumer devices, but mobile platforms like Android Lollipop are starting to add native support for Bluetooth 4.1 and 4.2.

Bluetooth 5.0 Standard

On June 16, 2016, the Bluetooth Technology Alliance (SIG) officially launched bluetooth 5.0, the fifth generation of Bluetooth technology, which not only doubles the speed and quadruple the distance, but also optimizes the underlying functions of the Internet of Things. In terms of performance, Bluetooth 5.0 standard transmits twice as fast as the previous version of 4.2LE and has four times the effective distance, increasing the theoretical effective working distance between bluetooth transmitting and receiving devices to 300 meters. Additionally, Bluetooth 5.0 allows you to receive Beacon data, such as ads, beacons, location information, etc. without pairing, increasing the transmission rate by up to 8 times. At the same time, Bluetooth 5.0 standard is also optimized for the Internet of Things, faster and more power saving, aiming at lower power consumption and higher performance for smart home services. According to the Bluetooth Technology Alliance, there are now more than 8.2 billion Bluetooth devices worldwide. Bluetooth 5.0 is expected to be launched in late 2016 or early 2017. Flagship phones with Bluetooth 5.0 chips will be available in 2017, and Apple is expected to be one of the first manufacturers to use the technology.

CoreBluetooth

Central and Peripheral

The role in Bluetooth communication

In BLE communication, there are two main roles: Central and Peripheral. Similar to the traditional client-server architecture, one Peripheral side is the one providing data (equivalent to the server side); A Central is a party (equivalent to a client) that performs a specific task using data provided on a Peripheral end. Central a Peripheral device that can scan and listen to any broadcast information it is interested in. Data broadcasting and receiving need to be represented by a certain data structure. A service is one such data structure. The Peripheral end may contain one or more services or provide useful information about the strength of the connected signal. A service is a collection of data and data-related operations on a device. The services themselves are composed of features or contained services. A feature provides more detailed information about the service. After the connection between a Central end and the Peripheral end is successfully established, the Central end can find the complete set of services and features provided by the Peripheral end. A Central can also read and write values for service properties on the Peripheral side.

Representation of Central, Peripherals, and Peripheral data

When we use the local Central to interact with the Peripheral end, we perform an operation at the Central end of the BLE communication. Unless we set up a local Peripheral device, most Bluetooth interaction takes place at the Central end. (Basic operations on the Peripheral side will also be covered below). On the Central side, the local Central device is represented by the CBCentralManager object. This object is used to manage the operations that discover and connect Peripheral devices (CBPeripheral objects), including scanning, finding, and connecting. When we interact with a Peripheral device, we’re dealing primarily with its services and features. In the Core Bluetooth framework, services are a CBService object and features are a CBCharacteristic object. The following figure illustrates the basic structure of services and features on the Central side:





Serves feature relations.png

Apple provides BLE Peripheral functionality after OS X 10.9 and iOS 6, which allows the device to be treated as Peripheral. On the Peripheral side, the local Peripheral device is represented as a CBPeripheralManager object. These objects are used to manage publishing services and features to the local Peripheral device database and to advertise those services to the Central device. The Peripheral Manager is also used to respond to read and write requests from the Central end. The following figure shows a Peripheral role:





Peripheral Role. PNG

When we set data on a local Peripheral device, we’re actually dealing with mutable versions of services and features. In the Core Bluetooth framework, the local Peripheral service is represented by the CBMutableService object and the feature is represented by the CBMutableCharacteristic object. The following figure shows the basic structure of the local Peripheral service and feature:





Peripheral Services and features. PNG

Peripheral (Server) side operation

A Peripheral operation mainly involves the following steps:

Launch a Peripheral management object set services and features in the local Peripheral advertise services and features to the local database of the device and our service responds to read and write requests on the Central side of the connection and sends updated feature values to the subscription Central side We’ll explain each step separately with the code below

Launch a Peripheral Manager

To implement a Peripheral on a local device, we need to allocate and initialize a Peripheral manager instance, as shown in the code below

// Create a Peripheral manager
/ / we will be the current class as peripheralManager, therefore must implement CBPeripheralManagerDelegate
// If the second argument is specified as nil, the main queue is used by default
peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];Copy the code

After creating Peripheral manager, Peripheral manager will call proxy objects peripheralManagerDidUpdateState: method. We need to implement this method to ensure local devices support BLE.

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
    NSLog(@"Peripheral Manager Did Update State");
    switch (peripheral.state) {
        case CBPeripheralManagerStatePoweredOn:
            NSLog(@"CBPeripheralManagerStatePoweredOn");
            break;

        case CBPeripheralManagerStatePoweredOff:
            NSLog(@"CBPeripheralManagerStatePoweredOff");
            break;

        case CBPeripheralManagerStateUnsupported:
            NSLog(@"CBPeripheralManagerStateUnsupported");
            break;

        default:
            break; }}Copy the code
Set services and features

A local Peripheral database organizes services and features in a tree-like structure. So, when we set up services and features, we organized them into a tree structure.

A Peripheral service or feature is identified by a 128-bit Bluetooth-specified UUID, which is a CBUUID object. Although the SIG organization does not pre-define all service and feature UUids, the SIG has defined and published a number of approved UUids, which are simplified to 16 bits for ease of use. For example, the SIG defines a 16-bit UUID as the heartbeat service identifier (180D).

The CBUUID class provides methods to generate a CBUUID object from a string. When the string uses a predefined 16-bit UUID, Core Bluetooth uses it to pre-complete the 128-bit identifier automatically.

CBUUID *heartRateServiceUUID = [CBUUID UUIDWithString:@"180D"];Copy the code

Of course, we can also generate our own 128-bit UUID to identify our services and features. Using the uuidgen command on the command line generates a 128-bit UUID string, which we can then use to generate a CBUUID object.

Once the UUID object is generated, we can use this object to create our services and features, and then organize them into a tree structure.

The code to create the feature looks like this:

CBUUID *characteristicUUID1 = [CBUUID UUIDWithString:@"C22D1ECA-0F78-463B-8C21-688A517D7D2B"];
CBUUID *characteristicUUID2 = [CBUUID UUIDWithString:@"632FB3C9-2078-419B-83AA-DBC64B5B685A"];

CBMutableCharacteristic *character1 = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID1 properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];

CBMutableCharacteristic *character2 = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID2 properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsWriteable];Copy the code

We need to set the properties, values, and permissions for the feature. Property and permission values determine whether the property value is readable or writable, and whether the Central end of the connection can subscribe to the property value. In addition, if we specify a value for a property, the value is cached and its properties and permissions are set to be readable. If we want the property’s value to be writable, or expect it to be modified over the lifetime of the service to which the property belongs, we must specify nil.

After creating the feature, we can create a feature-related service and then associate the feature with the service, as shown in the following code:

CBUUID *serviceUUID = [CBUUID UUIDWithString:@"3655296F-96CE-44D4-912D-CD83F06E7E7E"];
CBMutableService *service = [[CBMutableService alloc] initWithType:serviceUUID primary:YES];
service.characteristics = @[character1, character2];    // Organize into a tree structureCopy the code

In the example above, the primary parameter is passed with YES, indicating that it is a primary service, that is, it describes the main functions of a device and can be referenced by other services. At the other end of the spectrum are secondary services, which only describe a service in the context of another service that references it.

Publish services and features

After creating services and features and organizing them into a tree structure, we need to publish these services to the local database on the device. We can do this using the addService: method of the CBPeripheralManager. The following code looks like this:

[peripheralManager addService:service];Copy the code

In the service invoke some methods to release, CBPeripheralManager object will invoke its agent peripheralManager: didAddService: error: method. If there is an error during the publishing process that prevents the server from being deployed, you can implement the proxy method to handle the error, as shown in the following code:

- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
{
    NSLog(@"Add Service");

    if (error)
    {
        NSLog(@"Error publishing service: %@", [error localizedDescription]); }}Copy the code

After publishing services and features to the device database, the service is cached and cannot be modified.

Advertising services

After the above steps, we can advertise these services to the Central end that is interested in the service. We can do this by calling the startAdvertising: method of the CBPeripheralManager instance, as shown in the following code:

[peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey: @[service.UUID]}];Copy the code

StartAdvertising: the parameter is a dictionary, Peripheral manager supports and support only two key values: CBAdvertisementDataLocalNameKey CBAdvertisementDataServiceUUIDsKey. These two values describe the details of the data. The value corresponding to the key value is expected to be an array representing multiple services.

When advertising services, CBPeripheralManager object invokes code peripheralManagerDidStartAdvertising: error: method, we can do it in the corresponding processing, as shown in the following code:

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
{
    NSLog(@"Start Advertising");

    if (error)
    {
        NSLog(@"Error advertising: %@"[errorlocalizedDescription]); }}Copy the code

After the AD service, the Central side can discover the device and initiate a connection.

Respond to read/write requests on the Central side

After we connect to the Central end, we may need to receive read and write requests from it, and we need to respond in an appropriate manner.

When the Central end of the connection request to read the characteristics of the value, the CBPeripheralManager invokes the proxy object peripheralManager: didReceiveReadRequest: method, The proxy method provides a CBATTRequest object to represent the Central side of the request, and we can use its properties to populate the request. The following code briefly shows this process:

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request
{
    // Check whether the requested feature is the specified feature
    if ([request.characteristic.UUID isEqual:cha1.UUID])
    {
        NSLog(@"Request character 1");

        // Make sure that the offset requested by the read request does not exceed the length range of the value of our property
        // Offset specifies the offset of the value to be read by the request
        if (request.offset > cha1.value.length)
        {
            [peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
            return;
        }

        // If the reading position is not out of bounds, the specified range of values in the property is assigned to the value property of the request.
        request.value = [cha1.value subdataWithRange:(NSRange){request.offset, cha1.value.length - request.offset}];

        // The request was successfully responded to
        [peripheralManager respondToRequest:request withResult:CBATTErrorSuccess]; }}Copy the code

In each call to the proxy object peripheralManager: didReceiveReadRequest: called when respondToRequest: withResult: method to make response to the request.

Write requests is similar to the above process, will be called proxy objects peripheralManager: didReceiveWriteRequests: method. The difference is that the proxy method gives us an array of one or more CBATTRequest objects, each representing a write request. We can use the value property of the request object to assign a value to our property property, as shown in the following code:

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests
{
     CBATTRequest *request = requests[0];

     cha1.value = request.value;

     [peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}Copy the code

The response processing is similar to the request.

Sends updated property values to the Central end of the subscription

If one or more Central ends subscribe to features of our service, we need to notify those Central ends when features change. To this end, a proxy object needs to implement peripheralManager: central: didSubscribeToCharacteristic: method. As follows:

- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic
{
    NSLog(@"Central subscribed to characteristic %@", characteristic);

    NSData *updatedData = characteristic.value;

    // Get the value of the updated property and send it to the Central end by calling the following method
    // The last parameter specifies to which Central we want to send changes, or if nil, to all connected Central
    // The method returns a BOOL indicating whether the changes were sent successfully, and NO if the queue used to send the updated values is filled
    BOOL didSendValue = [peripheralManager updateValue:updatedData forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:nil];

    NSLog(@"Send Success ? % @", (didSendValue ? @"YES" : @"NO"));
}Copy the code

In the above code, when the transmission queue have available space, CBPeripheralManager object invokes code peripheralManagerIsReadyToUpdateSubscribers: method. We can in this method call updateValue: forCharacteristic: onSubscribedCentrals: to send the value.

We use notifications to send a single packet to the subscribed Central. When we update subscription to Central, we should by calling a updateValue: forCharacteristic: onSubscribedCentrals: method will be the updated values in a notice.

Not all values are notified to be transmitted because of the variable size of the property’s values. If this happens, need to call on the Central end CBPeripheral instance readValueForCharacteristic: method to deal with, this method can obtain the value.

Central (Client) end operation

A Central terminal contains the following operations:

Start a Central end manager object that searches for and connects to the advertised Peripheral device queries for data after connecting to the Peripheral end sends a read or write request to the Peripheral end for a property value and receives notification when the Peripheral end property value changes We’ll explain each step separately with the code below

Start a Central manager

The CBCentralManager object represents a local Central device in Core Bluetooth, and we must allocate and initialize a CentralManager object when performing any BLE interaction. Create code like this:

// Specifies that the current class is a proxy object, so it needs to implement the CBCentralManagerDelegate protocol
// If queue is nil, the Central manager uses the main queue to send events
centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];Copy the code

In the options dictionary for some management center initialization attribute set nsstrings const CBCentralManagerOptionShowPowerAlertKey corresponding to an NSNumber type bool value, Is used to set is in the closed bluetooth pop-up prompt user nsstrings const CBCentralManagerOptionRestoreIdentifierKey corresponds to a nsstrings object, set identifier ID management center

Create a Central manager, manager object invokes the proxy objects centralManagerDidUpdateState: method. We need to implement this method to ensure local devices support BLE.

- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    NSLog(@"Central Update State");

    switch (central.state) {
        case CBCentralManagerStatePoweredOn:
            NSLog(@"CBCentralManagerStatePoweredOn");
            break;

        case CBCentralManagerStatePoweredOff:
            NSLog(@"CBCentralManagerStatePoweredOff");
            break;

        case CBCentralManagerStateUnsupported:
            NSLog(@"CBCentralManagerStateUnsupported");
            break;

        default:
            break; }}Copy the code
Find the Peripheral device being advertised

The Central end’s primary task is to discover the advertised Peripheral device for subsequent connection. We can call CBCentralManager instance scanForPeripheralsWithServices: options: method to find out is advertisement of Peripheral equipment. The following code looks like this:

// Find Peripheral devices
// If the first argument is passed nil, the manager returns all found Peripheral devices.
// Usually we specify an array of UUID objects to find a specific device
[centralManager scanForPeripheralsWithServices:nil options:nil];Copy the code

ServiceUUIDs are used to scan peripherals with a characteristic ID. Options are used to set key values for scanning properties such as whether to allow repeated scanning of the corresponding NSNumber bool (default: NO). Automatically to heavy nsstrings const CBCentralManagerScanOptionAllowDuplicatesKey; To scan device UUID array NSArray corresponding hovertree.com nsstrings const CBCentralManagerScanOptionSolicitedServiceUUIDsKey;

After the call the above method, CBCentralManager object found in each device called when proxy objects centralManager: didDiscoverPeripheral: advertisementData: RSSI: method.

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    NSLog(@"Discover name : %@", peripheral.name);

    // When we look on the Peripheral end, we can stop looking for other devices to save power
    [centralManager stopScan];

    NSLog(@"Scanning stop");
}Copy the code
Connecting Peripheral device

After the search to a Peripheral device, we can call CBCentralManager instance connectPeripheral: options: method to connect the Peripheral equipment. See the code below

[centralManager connectPeripheral:peripheral options:nil];Copy the code

In options, you can set the initial property keys of some connected devices as follows: If the Settings when connecting peripherals pop up a warning nsstrings const CBConnectPeripheralOptionNotifyOnConnectionKey; Corresponding NSNumber bool value, set up after the peripherals disconnected whether pop up a warning nsstrings const CBConnectPeripheralOptionNotifyOnDisconnectionKey; Corresponding NSNumber bool value, set up after the peripherals suspended connection whether pop up a warning nsstrings * const CBConnectPeripheralOptionNotifyOnNotificationKey;

If the connection is successful, then invokes the proxy object centralManager: didConnectPeripheral: method, we can implement this method to do the corresponding processing. Also, before we start interacting with the Peripheral device, we need to set up the Peripheral object’s proxy to ensure that the appropriate callbacks are received.

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"Peripheral Connected");

    peripheral.delegate = self;
}Copy the code
Find the service of the connected Peripheral device

Once the connection to the Peripheral device is established, we can query the data. First we need to look for the service available on the Peripheral device. Since the Peripheral device can advertise limited data, the actual service of the Peripheral device may be more than the service it advertises. We can call the Peripheral object discoverServices: method to find all the services. The following code looks like this:

[peripheral discoverServices:nil];Copy the code

Passing nil will look up all the services, but normally we’ll specify the service we’re interested in.

When call the above method, peripheral invokes the proxy object of peripheral: didDiscoverServices: method. Core Bluetooth creates an array of CBService objects, the elements of which are the services found on the Peripheral.

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    NSLog(@"Discover Service");

    for (CBService *service in peripheral.services)
    {
        NSLog(@"Discovered service %@", service); }}Copy the code
Find features in the service

Assuming we have found the service we are interested in, it is time to query the features in the service. In order to find the property of the service, we only need to call CBPeripheral class discoverCharacteristics: forService: method, as shown below:

NSLog(@"Discovering characteristics for service %@", service);
[peripheral discoverCharacteristics:nil forService:service];Copy the code

When found the characteristics of the particular service, peripheral object invokes the proxy objects peripheral: didDiscoverCharacteristicsForService: error: method. In this method, Core Bluetooth creates an array of CBCharacteristic objects, each element representing a feature object found. The following code looks like this:

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    NSLog(@"Discover Characteristics");
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        NSLog(@"Discovered characteristic %@", characteristic); }}Copy the code
Gets the value of the feature

A property contains a single value that contains Peripheral service information. After we get the property, we can get the value from the property. Just call CBPeripheral instance readValueForCharacteristic: method. As follows:

NSLog(@"Reading value for characteristic %@", characteristic);
[peripheral readValueForCharacteristic:characteristic];Copy the code

When we read the characteristic values in the peripheral object invokes the proxy object of peripheral: didUpdateValueForCharacteristic: error: method to get the value. If it succeeds, we can access the property through its value property, as shown below:

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    NSData *data = characteristic.value;

    NSLog(@"Data = %@", data);
}Copy the code
The value of the subscription feature

Although using readValueForCharacteristic: method reads feature values for some usage scenarios is very effective, but for the access to change the value of the is not very effective. For most variable values, we need to get them by subscription. When we subscribe to a property’s value, we receive notification from the Peripheral object when the value changes.

We can call CBPeripheral class setNotifyValue: forCharacteristic: method to subscribe to the characteristic values of interest. As follows:

[peripheral setNotifyValue:YES forCharacteristic:characteristic];Copy the code

When we try to subscribe feature values, is called peripheral object proxy objects of peripheral: didUpdateNotificationStateForCharacteristic: error: method. If the subscription fails, we can implement the proxy method to access the error, as follows:

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{...if (error)
    {
        NSLog(@"Error changing notification state: %@"[errorlocalizedDescription]); }}Copy the code

After successfully subscribing to the value of a feature, the Peripheral device notifies our application when the feature value changes.

Writes the value of the property

In some scenarios, we need to write the value of the property. For example, when we need to interact with a BLE digital thermostat, we may need to provide a value to the thermostat to set the temperature of the room. If the feature values can be written, we can by calling CBPeripheral instance writeValue: forCharacteristic: type: approach to writing values.

NSData *data = [NSData dataWithBytes:[@"test" UTF8String] length:@"test".length];
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];Copy the code

When attempting to write property values, we need to specify the type of write we want to perform. Above example specifies the writing type is CBCharacteristicWriteWithResponse, said peripheral let us know whether to write has been applied successfully.

Specify the writing type for CBCharacteristicWriteWithResponse peripheral object, when it calls the proxy object in response to the request of peripheral: didWriteValueForCharacteristic: error: method. If the write fails, we can handle the error message in this method.

Background processing

In the development of BLE related applications, the background processing of applications needs to be considered because of many resource constraints in the background. By default, most normal Core Bluetooth tasks are unavailable when the program is in the background or hanging, Central or Peripheral. However, we can declare that our application supports Core Bluetooth background execution mode, which allows applications to be woken up from suspended state to handle Bluetooth-related events.

However, even though our application supports Core Bluetooth background execution mode on both ends, it doesn’t run all the time. In some cases, the system may close our application to free up memory to make room for the current foreground application. In iOS7 and later, Core Bluetooth supports saving state information about Central and Peripheral manager objects and restoring that information when a program starts. We can use this feature to support long duration tasks associated with Bluetooth devices.

We will discuss these issues in detail below.

Only Foreground operations (Foreground Only) are supported

Most applications will be suspended for a short period of time once they are in the background, unless we request some specific background task. Our application was unable to continue performing Bluetooth-related tasks while handling the pending state.

At the Central end, the Foreground Only application cannot continue to scan and find the Peripheral device under the advertisement when entering the background or hanging. On the Peripheral side, you can’t advertise itself, and any access from the Central side will return an error.

When Foreground Only application is suspended, all Bluetooth-related events will be put into a queue by the system. When Foreground application is entered, the system will send these events to our application. That is, Core Bluetooth provides a way to alert the user when certain events occur. Users can use these prompts to decide whether or not to bring the application to the foreground. We introduced in the Central and Peripheral connectPeripheral: options: method, in this method is called, we can equipment options parameters to set these tips:

CBConnectPeripheralOptionNotifyOnConnectionKey: when applications pending, if there is a connection is successful, if we want to system for the specified peripheral displays a prompt, using the key value. CBConnectPeripheralOptionNotifyOnDisconnectionKey: when applications pending, if the connection is disconnected, if we want the system for the specified peripheral shows a disconnected when prompted, use the key values. CBConnectPeripheralOptionNotifyOnNotificationKey: when the application hangs, using the key value indicates that as long as it receives the given peripheral side will display a prompt notice.

Core Bluetooth background execution mode

We can set the Core Bluetooth background execution mode in the info.plist file to enable the application to perform bluetooth-related tasks in the background. When an application declares this feature, the system wakes it up to allow it to handle Bluetooth-related tasks. This feature is useful for applications that interact with a BLE that periodically sends data.

There are two Core Bluetooth background execution modes, one for Central side operations and one for Peripheral side operations. If our application implements both of these functions, we need to declare support for both modes. We need to add UIBackgroundModes keys to the info.plist file with either or both of the following values:

bluetooth-central(App communicates using CoreBluetooth)

bluetooth-peripheral(App shares data using CoreBluetooth)

  • Bluetooth – central pattern

If bluetooth-Central is set, our application can still look up and connect to Peripheral devices and look up data while in the background. In addition, the system wakes up our application when the CBCentralManagerDelegate or CBPeripheralDelegate delegate delegate methods are called, allowing the application to handle events such as establishing or disconnecting a connection.

Although we can perform many Bluetooth-related tasks in the background, it is important to remember that the application is not the same when scanning Peripheral devices in the foreground and background. When our application scans Peripheral devices in the background,

CBCentralManagerScanOptionAllowDuplicatesKey scan option will be ignored, the same to the Peripheral found multiple events will be aggregated into a discovery. If multiple applications scanning Peripheral devices are in the background, the interval between the Central device scanning AD data increases. It turns out that spotting an AD Peripheral device can take a long time. These processes are useful in minimizing radio usage and improving battery life on iOS devices.

  • Bluetooth – peripheral mode

If we set bluetooth-Peripheral values, our application will wake up in the background to handle read, write, and subscribe requests from the connected Central side, and Core Bluetooth also allows us to advertise in the background. Similar to the Central terminal, you need to pay attention to the operation differences between the front and back ends. Especially in advertising, there are the following differences:

CBAdvertisementDataLocalNameKey advertising key value will be ignored, Peripheral to the local name will not be advertising CBAdvertisementDataServiceUUIDsKey key UUID of all services are placed in a “overflow” area, they can only be the display to scan their network devices found. If multiple applications are advertising in the background, the interval between the Peripheral devices sending the AD package will be longer.

Perform long-term tasks in the background

While it is recommended to complete background tasks as quickly as possible, some should still need to use Core Bluetooth to perform a long task. This is where saving and restoring the state is involved.

State saving and recovery

Because state saving and recovery are built into Core Bluetooth, our program can opt for this feature to let the system save the state of the Central and Peripheral manager and continue performing some Bluetooth-related tasks even when the program is no longer running. When one of these tasks is complete, the system restarts the program in the background, and the program can be restored to its previous state to process events. Core Bluetooth supports state saving and recovery on the Central side, Peripheral side, or both.

On the Central side, the system saves the state of the Central manager object when the closing program frees memory (if there are multiple Central managers, we can choose which manager the system tracks). For a given CBCentralManager object, the system tracks the following information:

On the Peripheral side, for a given CBPeripheralManager object, the system tracks the following information:

Peripheral Manager advertising data services and features that the Peripheral manager publishes to the device database the Central end of the value of the features that subscribe to the Peripheral Manager when the system reboots the program into the background, We can reinitialize our program’s Central and Peripheral managers and restore state. We’ll take a closer look at how to use state saving and recovery.

Added state saving and recovery support

State saving and recovery in Core Bluetooth is an optional feature that requires program support to work. We can add support for this feature by following these steps:

Optional state saving and recovery when allocating and initializing a Central or Peripheral manager object. (Required) Reinitialize the Central or Peripheral Manager object when the system restarts the program (required) implement the appropriate recovery agent method (Optional) Update the Central or Peripheral Manager initialization process by optionally adding state save and recovery

To optionally include state saving and recovery features, provide a unique recovery identifier when allocating and initializing the Central or Peripheral manager. The recovery flag is a string of notes that Core Bluetooth and programs use to identify the Central or Peripheral manager. The value of the string is only meaningful in its own code, but this string tells Core Bluetooth that we need to save the state of the object. Core Bluetooth saves only those objects with a recovery identifier.

When, for example, to implement the Central, for the sake of selective join state save and restore feature, the initialization CBCentralManager object, you can specify initialization option CBCentralManagerOptionRestoreIdentifierKey, and provide a resume, The following code looks like this:

centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@ {CBCentralManagerOptionRestoreIdentifierKey: @"restoreIdentifier"}];Copy the code

To implement the Peripheral operation also similar, we only use the option CBPeripheralManagerOptionRestoreIdentifierKey keys.

Because a program can have more than one Central or Peripheral manager, you need to make sure that the recovery token is unique so that the system can distinguish between these manager objects.

Reinitializes the Central or Peripheral Manager object

When the system reboot goes into the background, the first thing we need to do is reinitialize these objects with the recovery flag. If our application has only one Central manager or Peripheral manager, and the Peripheral manager exists for the entire life of the application, we don’t need to do any more processing later on. But if we have more than one manager, or if the manager does not exist for the entire life of the program, then when the system restarts the application, we need to know which manager to reinitialize. We can use the program application proxy objects: didFinishLaunchingWithOptions: method, using the appropriate boot option button to access the object manager list (the list is closed is the system for the program to save).

The following code shows that when the program is restarted, we get recovery identifiers for all Central manager objects:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    NSArray *centralManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];

    // TODO:.

    return YES;
}Copy the code

With this list of recovery flags, we can reinitialize the manager objects we need.

Implement the appropriate recovery agent method

After reinitializing the Central or Peripheral Manager objects, we restore them by synchronizing their state using the Bluetooth system’s state. At this point, we need to implement some recovery proxy methods. For the Central manager, we realize centralManager: willRestoreState: proxy method; For Peripheral manager manager, we implement peripheralManager: willRestoreState: proxy method.

For an opt-in save-and-restore application, these methods are the first methods invoked when the program launches into the background to perform some Bluetooth-related tasks. For non selective join characteristics of application, will first call centralManagerDidUpdateState: and peripheralManagerDidUpdateState: proxy method.

In both agent methods, the last parameter is a dictionary containing information about the manager saved when the program is closed. Shown in the following code, we can use CBCentralManagerRestoredStatePeripheralsKey button to get connected or try to connect to the Central manager all Peripheral equipment list:

- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary *)state
{
    NSArray *peripherals = state[CBCentralManagerRestoredStatePeripheralsKey];

    // TODO: ...
}Copy the code

Once we have this list, we can do what we need to do.

Update the initialization process

After implementing the previous three steps, we might want to update our manager’s initialization process. While this step is optional, it is useful if you want to verify that the task is working properly. For example, our program may be turned off in the process of parsing data from a connected Peripheral device. When the program uses this Peripheral device to restore, it has no way of knowing where the data was processed. We need to make sure that the program picks up where the data operation left off.

And the code below shows the centralManagerDidUpdateState: proxy method of initialization program operation, we can find out the success of found restored Peripheral equipment specified services:

NSUInteger serviceUUIDIndex = [peripheral.services indexOfObjectPassingTest:^BOOL(CBService *obj, NSUInteger index, BOOL *stop) {
        return [obj.UUID isEqual:myServiceUUIDString];
    }];


    if (serviceUUIDIndex == NSNotFound) { [peripheral discoverServices:@[myServiceUUIDString]]; . }Copy the code

As mentioned in the example above, if the system closes the application while the program finishes searching for the service, it begins parsing the recovered Peripheral data at that point of closure by calling the discoverServices: method. If the program successfully searches for services, we can confirm that the correct features have been searched. By updating the initialization process, we can ensure that the right method is called at the right time.

While we may need to declare that the application supports Core Bluetooth background execution mode to accomplish a specific task, background operations should always be considered carefully. Because performing too many Bluetooth-related tasks requires using the iOS device’s built-in radio, which can affect battery life, try to minimize tasks performed in the background. Any application that is awakened by a Bluetooth-related task should process the task as soon as possible and re-suspend when it is finished.

Here are some basic guidelines:

The application should be session-based and provide an interface that allows the user to decide when to start and end the distribution of Bluetooth-related events. Once awakened, an application has about 10 seconds to complete its task. Ideally, the application should complete the task and suspend as soon as possible. The system can limit or even kill background tasks that take too long to execute. When an application is woken up, it should not perform some unimportant operation.