前言:此篇文章只針對第一次接觸藍牙的小白,文章中僅僅描述了如何連接藍牙、如何發(fā)送數(shù)據(jù) 、可能遇到的問題 等。對于其他專業(yè)性的名詞沒有做相應(yīng)的描述。
一、準(zhǔn)備工作
1、iOS使用藍牙 必須先允許定位,所以需要開啟定位。使用藍牙需要用戶同意才可使用 所以 工程info.plist 文件中需要添加 定位請求和藍牙請求的權(quán)限
定位權(quán)限
Privacy - Location When In Use Usage Description
藍牙權(quán)限
Privacy - Bluetooth Peripheral Usage Description
2、使用藍牙首先要準(zhǔn)備幾個UUID
服務(wù)UUID
#define BLE_SERVICE_UUID @"0000XXXX-0000-1000-8000-00805f9b34fb"
讀UUID
#define BLE_INDICATE_CHARACTERISTIC_UUID @"0000XXXX-0000-1000-8000-00805f9b34fb"
寫UUID
#define BLE_WRITE_CHARACTERISTIC_UUID @"0000XXXX-0000-1000-8000-00805f9b34fb"
這幾個UUID開發(fā)硬件的工程師會直接給你。作用后面會有所體驗
二、設(shè)置相關(guān)代碼
1、首先引入藍牙和定位所需框架
#import <SystemConfiguration/CaptiveNetwork.h>
#import <CoreLocation/CoreLocation.h>
#import <CoreBluetooth/CoreBluetooth.h>
2、其次引入代理
CBCentralManagerDelegate
CBPeripheralDelegate
3、然后聲明屬性
@property (nonatomic, strong) CBCentralManager * bluetoothManager;
@property (nonatomic, strong) CLLocationManager * locationManager;
@property (nonatomic, strong) CBPeripheral * currentPer;
4、設(shè)置定位
- (void)location {
NSString* phoneVersion = [[UIDevice currentDevice] systemVersion];
CGFloat version = [phoneVersion floatValue];
// 如果是iOS13 未開啟地理位置權(quán)限 需要提示一下
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined && version >= 13) {
self.locationManager = [[CLLocationManager alloc] init];
[self.locationManager requestWhenInUseAuthorization];
}
}
重點來了,以下代碼是藍牙相關(guān)的代碼
1、初始化 CBCentralManager
#pragma mark-- CBCentralManager的實例對象
- (void)initCBCentralManager{
NSDictionary *options = @{CBCentralManagerOptionShowPowerAlertKey: @YES};
CBCentralManager* cBCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:options];
self.bluetoothManager = cBCentralManager;
}
2、 CBCentralManagerDelegate回調(diào):返回藍牙的狀態(tài) 在此函數(shù)中可以提醒用戶打開藍牙等操作
#pragma mark-- CBCentralManagerDelegate回調(diào):(開啟/關(guān)閉藍牙,也就是藍牙的狀態(tài))
- (void)centralManagerDidUpdateState:(CBCentralManager*)central{
if (central.state == CBCentralManagerStatePoweredOn) {
NSLog(@"藍牙已經(jīng)打開");
} else if (central.state == CBManagerStatePoweredOff) {
NSLog(@"藍牙已經(jīng)關(guān)閉");
}
}
3、開始掃描藍牙設(shè)備
#pragma mark-- 開始掃描設(shè)備(也就是開始掃描藍牙)
- (void)scanDevice{
[self.bluetoothManager scanForPeripheralsWithServices:nil options:nil];
}
4、藍牙設(shè)備掃描結(jié)果回調(diào):在此方法中找到你想要連接的藍牙設(shè)備然后去連接設(shè)備
#pragma mark-- CBCentralManagerDelegate回調(diào):藍牙搜索結(jié)果回調(diào)
- (void)centralManager:(CBCentralManager*)central didDiscoverPeripheral:(CBPeripheral*)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber*)RSSI{
NSString *scanedName=advertisementData[@"kCBAdvDataLocalName"];
if (scanedName.length>0) {
NSLog(@"掃描到設(shè)備scanedName=:%@",scanedName);
}
if ([scanedName hasPrefix:@"aux-5802"]) {
self.currentPer = peripheral;
// 發(fā)現(xiàn)設(shè)備后,請求連接設(shè)備
[self connectedDevices];
}
}
#pragma mark-- 開始連接設(shè)備:
- (void)connectedDevices{
[self.bluetoothManager connectPeripheral:self.currentPer options:nil];
}
5、連接狀態(tài)回調(diào)
#pragma mark-- 鏈接狀態(tài)回調(diào)
///連接成功
- (void)centralManager:(CBCentralManager*)central didConnectPeripheral:(CBPeripheral*)peripheral{
NSLog(@"連接成功");
//連接成功停止搜索
[self.bluetoothManager stopScan];
self.currentPer = peripheral;
//連接成功后,掃描外圍設(shè)備的服務(wù)CBService:
[self scanDeviceCBService];
}
///連接失敗
- (void)centralManager:(CBCentralManager*)central didFailToConnectPeripheral:(CBPeripheral*)peripheral error:(NSError*)error{
NSLog(@"連接失敗---%@",error);
}
///斷開連接結(jié)果
- (void)centralManager:(CBCentralManager*)central didDisconnectPeripheral:(CBPeripheral*)peripheral error:(NSError*)error{
NSLog(@"斷開鏈接");
}
#pragma mark-- 連接成功后,掃描外圍設(shè)備的服務(wù)CBService:
- (void)scanDeviceCBService{
self.currentPer.delegate = self;
[ self.currentPer discoverServices:@[[CBUUID UUIDWithString:BLE_SERVICE_UUID]]];
}
6、 掃描服務(wù)結(jié)果會通過CBPeripheralDelegate回調(diào) 返回
#pragma mark-- 掃描服務(wù)結(jié)果會通過CBPeripheralDelegate回調(diào):
- (void)peripheral:(CBPeripheral*)peripheral didDiscoverServices:(NSError*)error{
for (CBService *service in peripheral.services) {
if ([service.UUID isEqual:[CBUUID UUIDWithString:BLE_SERVICE_UUID]]) {
//查找特征
[self.currentPer discoverCharacteristics:nil forService:service];
}
}
}
7、查詢特征結(jié)果會通過CBPeripheralDelegate回調(diào):返回
#pragma mark--查詢特征結(jié)果會通過CBPeripheralDelegate回調(diào):
-(void)peripheral:(CBPeripheral*)peripheral didDiscoverCharacteristicsForService:(CBService*)service error:(NSError*)error{
for (CBCharacteristic *character in service.characteristics) {
NSLog(@"%@",character);
if ([character.UUID isEqual:[CBUUID UUIDWithString:BLE_WRITE_CHARACTERISTIC_UUID]]) {
// 寫
//注意:1、這里的 writeValue: 傳的是一個NSData類型的數(shù)據(jù) 這個數(shù)據(jù)硬件需要什么 你傳什么就可以了。
//注意:2、這里的 type :你需要查看硬件給你返回的什么類型然后你填什么類型就可以了,這個類型可以打印 character 查看他的 properties屬性 如果硬件返回的是 0x04(CBCharacteristicPropertyWriteWithoutResponse) 那你就需要填寫對應(yīng)的(CBCharacteristicWriteWithoutResponse)即可,切記這一點 否則會報 讀或?qū)憶]有權(quán)限的 問題
[self.currentPer writeValue: data forCharacteristic:character type:CBCharacteristicWriteWithoutResponse];
}else if ([character.UUID isEqual:[CBUUID UUIDWithString:BLE_INDICATE_CHARACTERISTIC_UUID]]) {
// 讀
[self.currentPer setNotifyValue:YES forCharacteristic:character];
}
}
}
8、特征的數(shù)據(jù)也是通過CBPeripheralDelegate回調(diào)
#pragma mark-- 特征的數(shù)據(jù)也是通過CBPeripheralDelegate回調(diào):
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(@"peripheral:%@ \n\n characteristic:%@ \n\n error:%@",peripheral, characteristic, error);
[peripheral readValueForCharacteristic:characteristic];
}
9、藍牙返回數(shù)據(jù) 代理方法 :這里可能會出現(xiàn) Reading is not permitted 或則 Writing is not permitted 出現(xiàn)這個 問題不要著急 首先檢查 硬件給你的 讀寫UUID是否正確 如果正確 再次檢查 第七步 ( [self.currentPer writeValue: data forCharacteristic:character type:CBCharacteristicWriteWithoutResponse]; )中的 type是否與硬件返回的一致 確保這兩點基本沒有什么問題了
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"%@",error);
} else {
NSData *responseData = characteristic.value;
NSLog(@"%@",responseData);
//注意:藍牙返回的也是 NSData類型的數(shù)據(jù) 拿到數(shù)據(jù)解析數(shù)據(jù)就就可以了
[self auxCmdParse:responseData];//這一步是解析數(shù)據(jù)
}
}
補充:
向藍牙發(fā)送數(shù)據(jù)包(數(shù)據(jù)包的格式跟硬件協(xié)商好)
- (NSData*)getData{
NSString *ssid = self.ssid;
NSString *key= self.password;
NSString *paas_address= self.paas_address;
NSString *m2m_address= self.m2m_address;
NSString *apCode= self.apCode;;
NSMutableData *allMData = [[NSMutableData alloc] init];
NSData *ssidData = [ssid dataUsingEncoding: NSUTF8StringEncoding];
NSData *keyData = [key dataUsingEncoding: NSUTF8StringEncoding];
NSData *paas_addressData = [paas_address dataUsingEncoding:NSUTF8StringEncoding];
NSData *m2m_addressData = [m2m_address dataUsingEncoding:NSUTF8StringEncoding];
NSData *apcodeData = [apCode dataUsingEncoding:NSUTF8StringEncoding];
unsigned long length = 8+(ssidData.length)+(keyData.length)+(apcodeData.length)+(m2m_addressData.length)+(paas_addressData.length)+7;
Byte bhead[]= {0xa5, 0xa5, length & 0xFF, 0x00, 0x01, 0x00, 0x00,0x00};
[allMData appendBytes:bhead length:8];
Byte blen[]= {ssidData.length & 0xFF};
[allMData appendBytes:blen length:1];
if (blen[0]>0){
[allMData appendBytes:[ssidData bytes] length:blen[0]];
}
// //
blen[0]= keyData.length & 0xFF;
[allMData appendBytes:blen length:1];
if (blen[0]>0){
[allMData appendBytes:[keyData bytes] length:blen[0]];
}
blen[0]= apcodeData.length & 0xFF;
[allMData appendBytes:blen length:1];
if (blen[0]>0){
[allMData appendBytes:[apcodeData bytes] length:blen[0]];
}
// //
blen[0]= paas_addressData.length & 0xFF;
[allMData appendBytes:blen length:1];
if (blen[0]>0){
[allMData appendBytes:[paas_addressData bytes] length:blen[0]];
}
//
//
blen[0]= m2m_addressData.length & 0xFF;
[allMData appendBytes:blen length:1];
if (blen[0]>0){
[allMData appendBytes:[m2m_addressData bytes] length:blen[0]];
}
int crc= [CRC crc16CCIT:allMData andLen:(int)allMData.length];
Byte bcrc[]= {(crc>>8)&0x0ff, crc&0x0ff};
[allMData appendBytes:bcrc length:2];
return allMData;
}
接收藍牙發(fā)來的數(shù)據(jù)包(按照規(guī)定的順序解包)
- (Boolean)cmdParse:(NSData *)data{
Byte *b= (Byte *)[data bytes];
if (b[0]== 0xa5 && b[1]== 0xa5
&& b[4]== 0x01 && b[5]== 0x10
&& b[6]== 0x00 && b[7]== 0x00
&& b[8]== 0x00 && b[9]== 0x00){
int crcCal= [CRC crc16CCIT:data andLen:(int)data.length-2];
int crcRcv= ((b[data.length-2] & 0x0ff) << 8) | (b[data.length-1] & 0x0ff);
if (crcCal == crcRcv){
_auxRplMac= [NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X", b[11], b[12], b[13], b[14], b[15], b[16]];
_auxRplPasscode= [self toHexString:b start:18 Len:b[17]];
NSLog(@"%@",_auxRplMac);
NSLog(@"%@",_auxRplPasscode);
return true;
}
}
return false;
}
- (NSString *)toHexString:(Byte *)b start:(int)start Len:(int)len{
NSString *str= @"";
for (int i=0; i<len; i++){
str= [NSString stringWithFormat:@"%@%02X", str, b[start+i]];
}
return str;
}