Bluetooth BlueTooth Low Energy (BLE)

Keywords: Mobile encoding Attribute

  • BLE:(Bluetooth low energy) Bluetooth 4.0 device is also called BLE because of its low power consumption.

  • peripheral,central: peripheral and central device, the device that initiates the link is central (generally referred to as mobile phone), and the device that is linked is peripheral (sports bracelet)

  • service and characteristic: Each device will provide services and features, similar to the API of the server, but with different structure. Each device will have many services, and each service contains many fields. The permissions of these fields are generally divided into read, write and notify, which are the specific operations we need to operate after connecting the device.

  • Description: Each characteristic can correspond to one or more descriptions used to describe the information or attributes of a characteristic (eg. range, unit of measurement)

Basic knowledge of Bluetooth

The core of the CoreBluetooth framework is actually two things: peripheral and central, which have a set of related API s and classes, respectively.



  • These two groups of apif correspond to different business common: the left side is called center mode, which is the scene where your app is the center and other peripherals are connected; the right side is called peripheral mode, which uses mobile phones as peripherals to connect other central devices.

  • Services and characteristics

    • Each device will have one or more services

    • Each service has one or more features

    • Features are specific key-value pairs, where data is provided

    • Each feature attribute is divided into: read, write, notification, and so on.

  • The relationship between peripherals, services and features

             

BLE central mode flow

  • 1. Establishing a central role

  • 2. Scanning Peripheral

  • 3. Connect Peripheral

  • 4. Discover Services and Characteristics in Scanning Peripherals

    • 4.1 Access to Peripheral services

    • 4.2 Get Characteristics of the peripheral, get the values of Characteristics, get the values of Descriptor and Descriptor of Characteristics.

  • 5. Explore and Interact with peripherals using features

  • 6. Subscribe to Characteristic notifications

  • 7. Disconnect

BLE peripheral mode flow

  • 1. Start a Peripheral management object

  • 2. Local peripheral settings services, features, descriptions, permissions, etc.

  • 3.peripheral Sends Advertisements

  • 4. Set up the proxy methods for processing subscriptions, canceling subscriptions, reading characteristics, and writing characteristics.

Status of Bluetooth Devices

  • Standby: The device does not transmit and transmit data and is not connected to any peripherals.

  • 2. Advertiser: Periodic broadcast state

  • 3. Scanner: Actively search for the device being broadcast

  • 4. Initiator: Initiator: Initiator Actively Initiates Connections to Scanning Devices

  • 5. Master: Connect to other devices as master device.

  • 6. Slave: As a link from device to other device

Five Working States of Bluetooth Equipment

  • Standby

  • Advertising

  • Scanning

  • Initiating

  • Connected


/*******************************************************************************************************/

Status of Bluetooth Devices

  • Standby: The device does not transmit and transmit data and is not connected to any peripherals.

  • 2. Advertiser: Periodic broadcast state

  • 3. Scanner: Actively search for the device being broadcast

  • 4. Initiator: Initiator: Initiator Actively Initiates Connections to Scanning Devices

  • 5. Master: Connect to other devices as master device.

  • 6. Slave: As a link from device to other device

Five Working States of Bluetooth Equipment

  • Standby

  • Advertising

  • Scanning

  • Initiating

  • Connected

Implementation code

Center Equipment Code:

#import "CentralViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>

#define SERVICE_UUID @"307846BD-EDB4-4E3B-A0A7-52B2E5AFA760"
#define CHARACTERISTIC_UUID @"C430B109-A222-44A9-B75D-49D89BD97642"
#define BLUETOOTH_PIC_END @"PIC_END"
#define BLUETOOTH_TEXT_END @"TEXT_END"
#define MAX_BYTES 20

@interface CentralViewController ()<CBCentralManagerDelegate,CBPeripheralDelegate>
{
    CBCentralManager * _manager;
    
    UILabel * _statusLb;
    UILabel * _showLb;
}

@property (nonatomic,strong) CBPeripheral * discovedPeripheral;
@property (nonatomic,strong) NSMutableData * data;

@end

@implementation CentralViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    // Create a central manager object
    _manager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
    _data = [NSMutableData data];
    
    _statusLb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)];
    _statusLb.text = @"Scanning peripherals...";
    [self.view addSubview:_statusLb];
    
    _showLb = [[UILabel alloc] initWithFrame:CGRectMake(0, 30, 320, 100)];
    _showLb.numberOfLines = 0;
    _showLb.text = @"Accepted data:";
    [self.view addSubview:_showLb];
}

-(void)scan{
    // Whether the central equipment is allowed to receive the information of the equipment that has been monitored many times, so as to monitor the signal strength of the peripheral equipment connection, so as to decide whether to increase the broadcasting intensity, and how much power will be consumed for YES.
    [_manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]
                                                options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
}

-(void)clearUp{
    if (![self.discovedPeripheral isConnected]) {
        return;
    }
    
    if (self.discovedPeripheral.services!=nil) {
        for (CBService*server in self.discovedPeripheral.services) {
            
            if (server.characteristics!=nil) {
                for (CBCharacteristic*chatacter in server.characteristics) {
                    
                    if ([chatacter.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_UUID]]) {
                        
                        // Do you subscribe?
                        if (chatacter.isNotifying) {
                            // If subscription cancels
                            [self.discovedPeripheral setNotifyValue:NO forCharacteristic:chatacter];
                            return;
                        }
                        
                    }
                    
                }
            }
        }
    }
    // Connect without subscription disconnect
    [_manager cancelPeripheralConnection:self.discovedPeripheral];
    
}

#pragma mark - CBCentralManagerDelegate
// Detection of central equipment status
-(void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    if (central.state!=CBCentralManagerStatePoweredOn) {
        NSLog(@"Bluetooth off");
        return;
    }
    // Open detection
    [self scan];
}

// When the same UUID signal broadcasted by peripheral devices is found, the function is called to RSSI to receive the signal strength indication close enough to connect.
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    NSLog(@"Discovered %@ at %@", peripheral.name, RSSI);
    // Is it the peripheral equipment we're listening to?
    if (self.discovedPeripheral != peripheral) {
        self.discovedPeripheral = peripheral;
        // Connecting peripheral
        [_manager connectPeripheral:peripheral options:nil];
        NSLog(@"Connecting to peripheral %@", peripheral);
    }
}

// After connecting peripherals, we need to find the service characteristics of peripherals.
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    // When the connection is complete, the detection is stopped.
    [_manager stopScan];
    
    [self.data setLength:0];
    // Ensure that we receive callback proxy functions after peripheral devices are connected
    peripheral.delegate=self;
    // Let peripherals find services that match the UUID we send
    // Generate UUID commands: uuidgen
    [peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]];
}

// Discovery of services
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    
    if (error) {
        NSLog(@"Errordiscover:%@",error.localizedDescription);
        [self clearUp];
        return;
    }
    // Find the features we want
    // Traversing peripherals
    for (CBService*server in peripheral.services) {
        // Find the characteristics of the specified UUID
        [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:CHARACTERISTIC_UUID]] forService:server];
    }
    
}

// When we discover the transport service feature, we subscribe to it to tell peripherals that we want the data held by the feature.
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    if (error) {
        NSLog(@"error  %@",[error localizedDescription]);
        [self clearUp];
        return;
    }
    // Inspection characteristics
    for (CBCharacteristic*characteristic in service.characteristics) {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_UUID]]) {
            // There are features from the periphery. If you find them, subscribe to them.
            // If the first parameter is yes, the proxy method peripheral:didUpdateValueForCharacteristic:error: is allowed to listen for changes in the characteristics of the second parameter.
            // Subscription characteristics
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            NSLog(@"Subscription success");
        }
    }
}

// Peripheral devices let us know if our subscription and unsubscribe happened
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error) {
        NSLog(@"error  %@",error.localizedDescription);
    }
    // Exit if it's not the feature we want
    if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_UUID]]) {
        return;
    }
    
    if (characteristic.isNotifying) {
        NSLog(@"Peripheral Characteristic Notification Begins");
        _statusLb.text = @"Connection Successful Waiting for Data Acceptance";
    }else{
        NSLog(@"The end of peripheral device feature notification, that is, the user wants to go offline or leave%@",characteristic);
        // Disconnect
        [_manager cancelPeripheralConnection:peripheral];
        
    }
}

// Accept data from Bluetooth
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error) {
        return;
    }
    // characteristic.value is the data contained in the feature
    NSString * stringFromData=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];

    if ([stringFromData isEqualToString:BLUETOOTH_PIC_END]) {
        // Accept text
        NSString * str= [[NSString alloc]initWithData:self.data encoding:NSUTF8StringEncoding];
        _showLb.text = str;
        self.data.length = 0;
#if 0
        // Accept pictures
        UIImage * img = [UIImage imageWithData:self.data];
        UIImageView * imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 150, 320, 200)];
        imgView.image = img;
        [self.view addSubview:imgView];
#endif
        
#if 0
        // Unsubscribe and disconnect Bluetooth connection
        [peripheral setNotifyValue:NO forCharacteristic:characteristic];
        [_manager cancelPeripheralConnection:peripheral];
#endif
    }else{
        // Data is not delivered, continue to transfer data
        [self.data appendData:characteristic.value];
        
    }
    
}

@end

The peripheral implementation code:


#import "PeripheralViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>

@interface PeripheralViewController ()<CBPeripheralManagerDelegate>
{
    UILabel * _statusLb;
    UITextField * _inputView;
    
    // How many bytes are currently sent
    unsigned long _sendBytes;
    // Whether to send completed
    BOOL _finish;
}
// Peripheral Equipment Management Category
@property(nonatomic,strong)CBPeripheralManager*peripheralManager;
// Variable service characteristics
@property(nonatomic,strong)CBMutableCharacteristic*transferCharacteristic;
@end

@implementation PeripheralViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.peripheralManager=[[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
    
    _statusLb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)];
    _statusLb.text = @"In transmission broadcasting...";
    [self.view addSubview:_statusLb];
    
    _inputView = [[UITextField alloc] initWithFrame:CGRectMake(0, 30, 270, 30)];
    _inputView.placeholder = @"Messages to be sent via Bluetooth...";
    [self.view addSubview:_inputView];
    
    UIButton * b = [UIButton buttonWithType:UIButtonTypeSystem];
    [b setFrame:CGRectMake(270, 30, 50, 30)];
    [b setBackgroundColor:[UIColor grayColor]];
    [b setTitle:@"Send out" forState:UIControlStateNormal];
    [b addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:b];
}

/*
 Cut and send binary data to send 20 bytes at a time until the end of sending 
 If the sending is complete, then the last pic_end string is sent to the center.
 */
- (void)click
{
    // If Bluetooth data is sent out, the last string "pic_end" is sent to the center to indicate that the transmission is completed.
    if (_finish) {
        //The third parameter represents the sending of the specified central device with our subscription, returning a Boolean value representing the success of the sending.
        BOOL didSend=[self.peripheralManager updateValue:[BLUETOOTH_PIC_END dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil];
        
        if (didSend) {
            //Complete sending
            _finish = NO;
            _sendBytes = 0;
            NSLog(@"Send completion");
        }
        //If we don't send, we'll quit and wait.
        //Peripheral Manager IsReadyToUpdate Subscribers to call sendData again to send data
        return;
    }
    // If you are not sending Bluetooth End, you are sending data.
    
    // Send text
    NSData * sendData=[_inputView.text dataUsingEncoding:NSUTF8StringEncoding];

#if 0
    // Send pictures
    NSData * sendData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"1" ofType:@"png"]];
#endif
    
    //Determine if there is any data left
    if (_sendBytes >= sendData.length) {
        //No data, just exit.
        return;
    }
    //If the data is not sent, send it unless the callback fails or we send it.
    BOOL didSend=YES;
    while (didSend) {
        //Send the next piece of data and figure out how big it is.
        NSInteger amountToSend=sendData.length-_sendBytes;
        if (amountToSend>MAX_BYTES) {
            //If the remaining data is still greater than 20 bytes, then I can transfer up to 20 bytes.
            amountToSend=MAX_BYTES;
        }
        //Cutting out the data I want to send + sendDataIndex is how many bytes to begin with and how much to cut backwards.
        NSData*chunk=[NSData dataWithBytes:sendData.bytes+_sendBytes length:amountToSend];
        //Send out
        didSend=[self.peripheralManager updateValue:chunk forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil];
        
        //If not, wait for the callback to send
        if (!didSend) {
            return;
        }else{
            _sendBytes+=amountToSend;
            //Determine whether or not the message has been sent
            if (_sendBytes>=sendData.length) {
                //When the sending is complete, start sending the end label Bluetooth END
                _finish = YES;
                [self performSelector:@selector(click) withObject:nil afterDelay:0.1];
            }
        }
        
    }

    
    [self.view endEditing:YES];
}

#pragma mark - CBPeripheralManagerDelegate
// Detection state
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
    if (peripheral.state!=CBPeripheralManagerStatePoweredOn) {
        return;
    }
    // Start service
    // Start variable service properties: Notify allows unanswered service features to send data to central devices, and permissions: read communication properties are read-only
    self.transferCharacteristic=[[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:CHARACTERISTIC_UUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];
    // Is it the first or second time to create a service primary
    CBMutableService*transferService=[[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:SERVICE_UUID] primary:YES];
    // Add features to services
    transferService.characteristics=@[self.transferCharacteristic];
    // Add services to management
    [self.peripheralManager addService:transferService];
    
    // Transmit the broadcast, marked TRANSFER_SERVICE_UUID as the received value for each other's observation, and two sides should correspond to each other.
    [self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:SERVICE_UUID]]}];
}

// Subscription features successfully started sending data
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
    NSLog(@"Subscription success");
    _statusLb.text = @"Successful connection to send messages";
}

// Called when the central device ends subscription
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic
{
    NSLog(@"End subscription");
    _statusLb.text = @"Connection disconnect";
}

// The sending queue is full and needs to be sent again
-(void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral
{
    // NSLog(@ "Send Queue Full to Send Again");
    [self click];
}

@end




Posted by datona on Sun, 21 Apr 2019 15:42:33 -0700