iOS 蓝牙技术
蓝牙常见名称和缩写
- MFI ======= make for ipad ,iphone, itouch 专们为苹果设备制作的设备
- BLE ==== buletouch low energy,蓝牙4.0设备因为低耗电,所以也叫做BLE
- peripheral,central == 外设和中心,发起连接的时central,被连接的设备为perilheral
- service and characteristic === 服务和特征 每个设备会提供服务和特征,类似于服务端的api,但是机构不同。每个外设会有很多服务,每个服务中包含很多字段,这些字段的权限一般分为 读read,写write,通知notiy几种,就是我们连接设备后具体需要操作的内容。
- Description 每个characteristic可以对应一个或多个Description用户描述characteristic的信息或属性
MFI === 开发使用ExternalAccessory 框架
4.0 BLE === 开发使用CoreBluetooth 框架
蓝牙基础知识
CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心。对应他们分别有一组相关的API和类
- 这两组api分别对应不同的业务场景,左侧叫做中心模式,就是以你的app作为中心,连接其他的外设的场景,而右侧称为外设模式,使用手机作为外设别其他中心设备操作的场景。
- 服务和特征,特征的属性(service and characteristic):
每个设备都会有一些服务,每个服务里面都会有一些特征,特征就是具体键值对,提供数据的地方。每个特征属性分为这么几种:读,写,通知这么几种方式。
//objcetive c特征的定义枚举 typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) { CBCharacteristicPropertyBroadcast = 0x01, CBCharacteristicPropertyRead = 0x02, CBCharacteristicPropertyWriteWithoutResponse = 0x04, CBCharacteristicPropertyWrite = 0x08, CBCharacteristicPropertyNotify = 0x10, CBCharacteristicPropertyIndicate = 0x20, CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40, CBCharacteristicPropertyExtendedProperties = 0x80, CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100, CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200 };
外设、服务、特征间的关系
蓝牙中心模式流程
-
建立中心角色
-
扫描外设(discover)
-
连接外设(connect)
-
扫描外设中的服务和特征(discover)
4.1 获取外设的services
4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值
-
与外设做数据交互(explore and interact)
-
订阅Characteristic的通知
-
断开连接(disconnect)
蓝牙外设模式流程
-
启动一个Peripheral管理对象
-
本地Peripheral设置服务,特性,描述,权限等等
-
Peripheral发送广告
-
设置处理订阅、取消订阅、读characteristic、写characteristic的委托方法
蓝牙设备状态
-
待机状态(standby):设备没有传输和发送数据,并且没有连接到任何设
-
广播状态(Advertiser):周期性广播状态
-
扫描状态(Scanner):主动寻找正在广播的设备
-
发起链接状态(Initiator):主动向扫描设备发起连接。
-
主设备(Master):作为主设备连接到其他设备。
-
从设备(Slave):作为从设备连接到其他设备。
蓝牙设备的五种工作状态
准备(standby)
广播(advertising)
监听扫描(Scanning
发起连接(Initiating)
已连接(Connected)
蓝牙和版本的使用限制
蓝牙2.0 === 越狱设备
蓝牙4.0 === iOS 6 以上
MFI认证设备(Make For ipod/ipad/iphone) === 无限制
XMBlueToothManager
XMBlueToothManager是对BabyBluetooth的二次封装,由于BabyBluetooth中链式函数的使用,导致不能在swift工程中使用,XMBlueToothManager完全弥补了这样的问题,全部使用block回调。代码实现起来方便好用。
#import <Foundation/Foundation.h> #import <CoreBluetooth/CoreBluetooth.h> #import "BabyBluetooth.h" #import "SVProgressHUD.h" #define XMSERVICEBLOCK (CBPeripheral *peripheral, NSError *error) #define XMCHARCTICBLOCK (CBPeripheral *peripheral, CBService *service, NSError *error) #define XMVALUEFORCHARCTIC (CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error) #define XMDISCOVERDSCRIPFORCH (CBPeripheral *peripheral, CBCharacteristic *characteristic, NSError *error) #define XMREADVALUEFORDES (CBPeripheral *peripheral, CBDescriptor *descriptor, NSError *error) /** 扫描到设备 @param central 外设中心 @param peripheral 外设 @param advertisementData advertisementData @param RSSI RSSI */ typedef void(^XMPeripheralsBlock)(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI); /** 发现设备服务 @param peripheral 外设 @param error error */ typedef void(^XMDiscoverServices)XMSERVICEBLOCK; /** 发现设备的Characteristics @param peripheral peripheral @param service service @param error error */ typedef void(^XMDiscoverCharacteristics)XMCHARCTICBLOCK; /** 读取到的特征值 @param peripheral peripheral @param characteristics characteristics @param error error */ typedef void(^XMReadValueForCharacteristic)XMVALUEFORCHARCTIC; /** 读取到特征描述 @param peripheral peripheral @param characteristic characteristic @param error error */ typedef void(^XMDiscoverDescriptorsForCharacteristic)XMDISCOVERDSCRIPFORCH; /** 读取Descriptor @param peripheral peripheral @param descriptor descriptor @param error error */ typedef void(^XMReadValueForDescriptors)XMREADVALUEFORDES; /** 设置查找设备的过滤器 @param peripheralName peripheralName @param advertisementData advertisementData @param RSSI RSSI */ typedef BOOL(^XMsetFilterOnDiscoverPeripherals)(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI); /** 链接成功与否 @param state state */ typedef void(^XMConnectStateBlock)(BOOL state); /** 断开连接 @param central central @param peripheral peripheral @param error error */ typedef void(^XMDisconnectAtChannel)(CBCentralManager *central, CBPeripheral *peripheral, NSError *error); /** 发现服务 */ typedef void(^XMDiscoverServicesAtChannel)XMSERVICEBLOCK; /** 发现特征 */ typedef void(^XMDiscoverCharacteristicsAtChannel)XMCHARCTICBLOCK; /** 读取到service的特征值 */ typedef void(^XMReadValueForCharacteristicAtChannel)XMVALUEFORCHARCTIC; /** 发现特征描述 */ typedef void(^XMDiscoverDescriptorsForCharacteristicAtChannel)XMDISCOVERDSCRIPFORCH; /** 读取特定频道的描述信息 */ typedef void(^XMReadValueForDescriptorsAtChannel)XMREADVALUEFORDES; typedef void(^XMReadRSSI)(NSNumber *RSSI, NSError *error); typedef void(^XMPeripheralManagerDidUpdateState)(CBPeripheralManager *peripheral); typedef void(^XMDidReadRSSIAtChannel)(NSNumber *RSSI, NSError *error); typedef void(^XMBlockOnDisconnectAtChannel)(CBCentralManager *central, CBPeripheral *peripheral, NSError *error); typedef void(^XMDidUpdateNotificationStateForCharacteristicAtChannel)(CBCharacteristic *characteristic, NSError *error); typedef void(^XMDidWriteValueForCharacteristicAtChannel)(CBCharacteristic *characteristic, NSError *error); @protocol XMBlueToothDelegate <NSObject> @end @interface XMBlueToothManager : NSObject @property (nonatomic , weak) id<XMBlueToothDelegate>delegate; @property (nonatomic , strong) NSMutableArray *peripherals;//查找到的所有的外设 @property (nonatomic , copy) XMDidReadRSSIAtChannel readRSSIAtChannel; @property (nonatomic , copy) XMBlockOnDisconnectAtChannel disconnectAtChannel; @property (nonatomic , copy) XMDidUpdateNotificationStateForCharacteristicAtChannel notiUpdate 3ff7 AtChannel; @property (nonatomic , copy) XMDidWriteValueForCharacteristicAtChannel didWriteData; /** 单例初始化 */ + (XMBlueToothManager *)defaultManager; /** 取消之前的连接 */ - (void)cancleAllConnect; /** 开始扫描外设 */ - (void)beginToScan; /** 扫描多久之后停止扫描 @param time 限制时间(s) */ - (void)beginToScanWithLimitTime:(int)time; /** 停止扫描 */ - (void)cancleScan; /** 扫描到外设的代理 @param block block */ - (void)xm_discoverPeripherals:(XMPeripheralsBlock)block; /** 发现设备服务 @param block block */ - (void)xm_discoverPeripheralService:(XMDiscoverServices)block; /** 发现Characteristic @param block block */ - (void)xm_discoverCharacteristics:(XMDiscoverCharacteristics)block; /** 读取特征值 @param block block */ - (void)xm_readValueForCharacteristic:(XMReadValueForCharacteristic)block; /** 读取特征描述 @param block block */ - (void)xm_discoverDescriptorsForCharacteristic:(XMDiscoverDescriptorsForCharacteristic)block; /** 读取Descriptors @param block block */ - (void)xm_readValueForDescriptors:(XMReadValueForDescriptors)block; /** 设置设备查找的过滤条件 @param block block */ - (void)xm_setFilterOnDiscoverPeripherals:(XMsetFilterOnDiscoverPeripherals)block; /** 连接外设 @param channel channel */ - (void)connectToPeripheralWithChannel:(NSString *)channel peripheral:(CBPeripheral *)peripheral; /** 连接成功与否 @param block block */ - (void)xm_connectState:(XMConnectStateBlock)block; /** 断开连接失败的回调 @param block block */ - (void)xm_disconnectAtChannelBlock:(XMDisconnectAtChannel)block; /** 发现服务 @param block block */ - (void)xm_discoverServicesAtChannel:(XMDiscoverServicesAtChannel)block; /** 发现特征 @param block block */ - (void)xm_xmDiscoverCharacteristicsAtChannel:(XMDiscoverCharacteristicsAtChannel)block; /** 读取特征值 @param block block */ - (void)xm_readValueForCharacterAtChannel:(XMReadValueForCharacteristicAtChannel)block; /** 发现特定频道的特征描述 @param block block */ - (void)xm_discoverDescriptorsForCharacteristicAtChannel:(XMDiscoverDescriptorsForCharacteristicAtChannel)block; /** 读取特定频道的特征描述 @param block block */ - (void)xm_readValueForDescriptorsAtChannel:(XMReadValueForDescriptorsAtChannel)block; /** 读取RSSI @param block block */ - (void)xm_readRSSI:(XMReadRSSI)block; /** 外设状态更新 @param block block */ - (void)xm_peripheralManagerDidUpdateState:(XMPeripheralManagerDidUpdateState)block; /** 添加断开重连的设备 @param peripheral peripheral */ - (void)xm_addAutoReconnectPeripheral:(CBPeripheral *)peripheral; /** 读取特定频道的rssi @param block block */ - (void)xm_didReadRSSIAtChannel:(XMDidReadRSSIAtChannel)block; /** 读取详细信息 @param channel chanel @param characteristic 当前的characteristic @param currPeripheral 当前的currPeripheral */ - (void)readDetailValueOfCharacteristicWithChannel:(NSString *)channel characteristic:(CBCharacteristic *)characteristic currPeripheral:(CBPeripheral *)currPeripheral; /** 外设断开连接的回调 @param block block */ - (void)xm_blockOnDisconnectAtChannel:(XMBlockOnDisconnectAtChannel)block; /** 订阅状态发生改变 @param block block */ - (void)xm_didUpdateNotificationStateForCharacteristicAtChannel:(XMDidUpdateNotificationStateForCharacteristicAtChannel)block; /** 写数据成功的回调 @param block block */ - (void)xm_didWriteValueForCharacteristicAtChannel:(XMDidWriteValueForCharacteristicAtChannel)block; /** 给外设写入数据 @param data data */ - (void)writeData:(NSData *)data ToPeripheral:(CBPeripheral *)peripheral forCharacteristic:(CBCharacteristic *)characteristic; /** 订阅数据 @param peripheral peripheral @param characteristic characteristic */ - (void)xm_setNotifiyWithPeripheral:(CBPeripheral *)peripheral forCharacteristic:(CBCharacteristic *)characteristic block:(XMReadValueForCharacteristic)block; /** 发现当前连接的设备 @return peripherals */ - (NSArray *)xmFindConnectedPeripherals; /** 取消订阅 @param peripheral peripheral @param characteristic characteristic */ - (void)xm_cancleNotifyWith:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic;
扫描设备
import UIKit class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource { @IBOutlet weak var tableView: UITableView! var peripherals = NSMutableArray() var peripheralsAD = NSMutableArray() var manager = XMBlueToothManager() var dataSource = NSMutableArray() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. SVProgressHUD.showInfo(withStatus: "准备打开设备") self.peripherals = NSMutableArray.init() self.peripheralsAD = NSMutableArray.init() self.dataSource = NSMutableArray.init() self.manager = XMBlueToothManager.default() self.setDelegate() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) self.manager.cancleAllConnect() self.manager.beginToScan() } func setDelegate() { self.manager.xm_discoverPeripherals { (central, peripheral, advertisementData, RSSI) in // XMLog(message: (peripheral?.name,RSSI)) self.insertTableView(peripheral: peripheral!, advertisementData: advertisementData! as NSDictionary) } self.manager.xm_setFilter { (peripheralName, advertisementData, RSSI) -> Bool in if peripheralName != nil{//设置过滤条件 return true }else{ return false } } } func insertTableView(peripheral:CBPeripheral,advertisementData:NSDictionary) -> Void { if !self.peripherals.contains(peripheral) { let indexPath = NSIndexPath.init(row: self.peripherals.count, section: 0) self.peripherals.add(peripheral) self.peripheralsAD.add(advertisementData) self.tableView.insertRows(at: [indexPath as IndexPath], with: .automatic) } self.tableView.reloadData() } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.peripherals.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) let peripheral = self.peripherals.object(at: indexPath.row) as! CBPeripheral let ad = peripheralsAD.object(at: indexPath.row) as! NSDictionary cell.accessoryType = .disclosureIndicator cell.selectionStyle = .none; var localName = String() if (ad.object(forKey: "kCBAdvDataLocalName") != nil) { localName = ad.object(forKey: "kCBAdvDataLocalName") as! String }else{ localName = peripheral.name! } cell.textLabel?.text = localName cell.detailTextLabel?.text = "读取中..."; if (ad.object(forKey: "kCBAdvDataServiceUUIDs") != nil) { let serviceUUIDs = ad.object(forKey: "kCBAdvDataServiceUUIDs") as! NSArray if serviceUUIDs.count > 0 { cell.detailTextLabel?.text = String.init(format: "%lu个services", serviceUUIDs.count) }else{ cell.detailTextLabel?.text = "0个services" } } return cell } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { self.manager.cancleScan() let perVC = segue.destination as! PeripheralViewController perVC.manager = self.manager; let cell = sender as! UITableViewCell let indexPath = self.tableView.indexPath(for: cell) let per = self.peripherals.object(at: (indexPath?.row)!) as! CBPeripheral perVC.currPeripher = per; perVC.chuanzhi(currentPeripheral: per) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
连接设备
import UIKit import CoreBluetooth class PeripheralViewController: UIViewController,UITableViewDelegate,UITableViewDataSource { @IBOutlet weak var tableView: UITableView! var manager = XMBlueToothManager() var services = NSMutableArray() var currPeripher:CBPeripheral? let channel:String = "perpheral" override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.services = NSMutableArray.init(); SVProgressHUD.showInfo(withStatus: "准备连接设备") let button = UIButton.init(type: .custom) button.frame = CGRect.init(x: 0, y: 0, width: 30, height: 30) button.setTitle("😸", for: .normal) button.setTitleColor(UIColor.black, for: .normal) self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(customView: button) button.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside) } func buttonAction(_ sender:UIButton) -> Void { let array = self.manager.xmFindConnectedPeripherals() XMLog(message: array) } func chuanzhi(currentPeripheral:CBPeripheral) -> Void { XMLog(message: currentPeripheral) self.currPeripher = currentPeripheral //连接外设 self.manager.connectToPeripheral(withChannel: self.channel, peripheral: currentPeripheral) self.manager.xm_discoverServices { (peripheral, error) in for s:CBService in (peripheral?.services)! { self.insertSectionToTableView(service: s) } } self.manager.xm_xmDiscoverCharacteristics(atChannel: { (peripheral, service, error) in self.insertRowToTableView(service: service!) }) self.manager.xm_readValueForCharacter { (peripheral, characteristics, error) in } self.manager.xm_readValueForDescriptors { (peripheral, descriptor, error) in } self.manager.xm_discoverDescriptorsForCharacteristic { (peripheral, characteristics, error) in } self.manager.xm_disconnect { (central, peripheral, error) in } self.manager.xm_connectState { (state) in XMLog(message: state) } } func insertSectionToTableView(service:CBService) -> Void { XMLog(message: service.uuid.uuidString) let info = XMPeripheralInfo.init() info.serviceUUID = service.uuid self.services.add(info) let indexSet = NSIndexSet.init(index: self.services.count - 1) self.tableView.insertSections(indexSet as IndexSet, with: .automatic) } func insertRowToTableView(service:CBService) -> Void{ var sect:Int = -1 for (index,item) in self.services.enumerated() { if ((item as! XMPeripheralInfo).serviceUUID == service.uuid) { sect = index; } } if sect != -1 { let info = self.services.object(at: sect) as! XMPeripheralInfo for (index,item) in (service.characteristics?.enumerated())! { info.characteristics.add(item as CBCharacteristic) let indexPath = NSIndexPath.init(row: index, section: sect) self.tableView.insertRows(at: [indexPath as IndexPath], with: .automatic) } } } func numberOfSections(in tableView: UITableView) -> Int { return self.services.count } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let info = self.services.object(at: section) as! XMPeripheralInfo return info.characteristics.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let info = self.services.object(at: indexPath.section) as! XMPeripheralInfo let characteristic = info.characteristics.object(at: indexPath.row) as! CBCharacteristic let cell = tableView.dequeueReusableCell(withIdentifier: "cell") cell?.textLabel?.text = String.init(format: "%@", characteristic.uuid.uuidString) cell?.detailTextLabel?.text = characteristic.description return cell! } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 50 } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let label = UILabel.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 50)) let info = self.services.object(at: section) as! XMPeripheralInfo label.text = String.init(format: "%@", info.serviceUUID) label.backgroundColor = UIColor.black label.textColor = UIColor.white return label } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // Get the new view controller using segue.destinationViewController. // Pass the selected object to the new view controller. let vc = segue.destination as! CharacteristicViewController let cell = sender as! UITableViewCell let indexPath = self.tableView.indexPath(for: cell) vc.currPeripheral = self.currPeripher let info = self.services.object(at: (indexPath?.section)!) as! XMPeripheralInfo let characteristic = info.characteristics.object(at: (indexPath?.row)!) as! CBCharacteristic vc.characteristic = characteristic vc.mananger = self.manager } }
读取数据和写入数据
import UIKit class CharacteristicViewController: UIViewController,UITableViewDelegate,UITableViewDataSource { @IBOutlet weak var tableView: UITableView! var mananger = XMBlueToothManager() var sect = NSMutableArray() var readValueArray = NSMutableArray() var descriptors = NSMutableArray() var characteristic:CBCharacteristic? var currPeripheral:CBPeripheral? let channel = "CBCharacteristic" override func viewDidLoad() { super.viewDidLoad() self.sect = NSMutableArray.init(array: ["read value","write value","desc","properties"]) self.readValueArray = NSMutableArray.init() self.descriptors = NSMutableArray.init() self.mananger.readDetailValueOfCharacteristic(withChannel: self.channel, characteristic: self.characteristic, currPeripheral: self.currPeripheral) self.managerDelegate() self.creatUI() } func creatUI() { let headView = UIView.init(frame: CGRect.init(x: 0, y: 64, width: KSCREEN_WIDTH, height: 100)) headView.backgroundColor = UIColor.darkGray self.view.addSubview(headView) let array = [self.currPeripheral?.name,String.init(format: "%@", (self.characteristic?.uuid)!),self.characteristic?.uuid.uuidString] for (index,item) in array.enumerated() { let label = UILabel.init(frame: CGRect.init(x: 0, y: 30*index, width: Int(KSCREEN_WIDTH), height: 30)) label.text = item label.backgroundColor = UIColor.white label.font = UIFont.systemFont(ofSize: 18) headView.addSubview(label) } self.tableView.frame = CGRect.init(x: 0, y: (120 + 64), width: KSCREEN_WIDTH, height: (KSCREEN_HEIGHT - 64.0 - 120)) let timer = Timer.schedule 4000 dTimer(timeInterval: 0.5, target: self, selector: #selector(read), userInfo: nil, repeats: true) timer.fire() } func read() { self.currPeripheral?.readRSSI() } /// 根据信号强度算距离 /// /// - Parameter RSSI: 信号强度值 /// - Returns: 距离 func getDistanceWith(RSSI:NSNumber) -> CGFloat { let power:Float = abs(RSSI.floatValue - 49.0)/(10*4.0) return CGFloat(powf(10.0, power)/10.0) } func managerDelegate(){ self.mananger.xm_readValueForCharacter { (peripheral, characteristics, error) in if ((characteristics?.value) != nil){ let data = (characteristics?.value)! as Data let string = String.init(data: data, encoding: .utf8) if string != nil{ XMLog(message: "数据为空") } self.insertReadValues(characteristics: characteristics!) } } self.mananger.xm_discoverDescriptorsForCharacteristic { (peripheral, characteristic, error) in for item:CBDescriptor in (characteristic?.descriptors)!{ self.insertDescriptor(descriptor: item) } } self.mananger.xm_readValueForDescriptors { (peripheral, descriptor, error) in for (index,item) in self.descriptors.enumerated(){ if (item as! CBDescriptor) == descriptor{ let cell = self.tableView.cellForRow(at: IndexPath.init(row: index, section: 0)) cell?.detailTextLabel?.text = String.init(format: "%@", descriptor?.value as! CVarArg) } } } //订阅状态发生改变 self.mananger.xm_didUpdateNotificationStateForCharacteristic { (characteristic, error) in XMLog(message: (characteristic?.uuid,(characteristic?.isNotifying)! ? "on" : "off")) } //实时读取外设的RSSI self.mananger.xm_didReadRSSI { (RSSI, error) in XMLog(message: ("setBlockOnDidReadRSSI:\(RSSI)")) let distance:CGFloat? = self.getDistanceWith(RSSI: RSSI!) XMLog(message: distance) } self.mananger.xm_readRSSI { (RSSI, error) in XMLog(message: RSSI) } self.mananger.xm_blockOnDisconnect { (central, peripheral, error) in XMLog(message: "连接失败") } //添加重连设备 self.mananger.xm_addAutoReconnectPeripheral(self.currPeripheral) //写出值成功的回调 self.mananger.xm_didWriteValueForCharacteristic { (characteristic, error) in if error == nil{ SVProgressHUD.showInfo(withStatus: "写入成功啦") } } } func setNotifiy(sender:UIButton) { let btn = sender if self.currPeripheral?.state == .disconnected { SVProgressHUD.showInfo(withStatus: "peripheral已经断开连接,请重新连接") return } if (self.characteristic?.properties.contains(.notify))! || (self.characteristic?.properties.contains(.indicate))! { if (self.characteristic?.isNotifying)! { self.mananger.xm_cancleNotify(with: self.currPeripheral, characteristic: self.characteristic) btn.setTitle("通知", for: .normal) }else{ self.currPeripheral?.setNotifyValue(true, for: self.characteristic!) self.mananger.xm_setNotifiy(with: self.currPeripheral, for: self.characteristic, block: { (peripheral, characteristic, error) in if error == nil{ SVProgressHUD.showInfo(withStatus: "接收到订阅的数据") }else{ SVProgressHUD.showError(withStatus: error as! String!) } self.insertReadValues(characteristics: characteristic!) }) btn.setTitle("取消通知", for: .normal) } }else{ SVProgressHUD.showInfo(withStatus: "这个characteristic没有nofity的权限") } } func insertReadValues(characteristics:CBCharacteristic) -> Void { var string:String? if (characteristics.value != nil) { string = String.init(data: characteristics.value!, encoding: .utf8) } if string == "" { print(string as Any) } self.readValueArray.add(string as Any) let indexPath = IndexPath.init(row: self.readValueArray.count-1, section: 0) self.tableView.insertRows(at: [indexPath as IndexPath], with: .automatic) self.tableView.scrollToRow(at: indexPath as IndexPath, at: .middle, animated: true) } func insertDescriptor(descriptor:CBDescriptor) -> Void { self.descriptors.add(descriptor) let indexPath = NSIndexPath.init(row: self.descriptors.count - 1, section: 2) as IndexPath self.tableView.insertRows(at: [indexPath], with: .automatic) } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell") switch indexPath.section { case 0: cell?.textLabel?.text = self.readValueArray.object(at: indexPath.row) as? String let formatter = DateFormatter.init() formatter.dateFormat = "yyyy-MM-dd hh:mm:ss" cell?.detailTextLabel?.text = formatter.string(from: NSDate.init() as Date) break case 1: cell?.textLabel?.text = "write a new value" break case 2: let descriptor = self.descriptors[indexPath.row] as! CBDescriptor cell?.textLabel?.text = String.init(format: "%@", descriptor.uuid) break case 3: let p = self.characteristic?.properties cell?.textLabel?.text = "" if (p?.contains(.broadcast))! { cell?.textLabel?.text = cell?.textLabel?.text?.appending(" | Broadcast") } if (p?.contains(.read))! { cell?.textLabel?.text = cell?.textLabel?.text?.appending(" | Read") } if (p?.contains(.writeWithoutResponse))! { cell?.textLabel?.text = cell?.textLabel?.text?.appending(" | WriteWithoutResponse") } if (p?.contains(.write))! { cell?.textLabel?.text = cell?.textLabel?.text?.appending(" | Write") } if (p?.contains(.notify))! { cell?.textLabel?.text = cell?.textLabel?.text?.appending(" | Notify") } if (p?.contains(.indicate))! { cell?.textLabel?.text = cell?.textLabel?.text?.appending(" | Indicate") } if (p?.contains(.authenticatedSignedWrites))! { cell?.textLabel?.text = cell?.textLabel?.text?.appending(" | AuthenticatedSignedWrites") } if (p?.contains(.extendedProperties))! { cell?.textLabel?.text = cell?.textLabel?.text?.appending(" | ExtendedProperties") } break default: break } return cell! } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch section { case 0: return self.readValueArray.count case 1: return 1 case 2: return self.descriptors.count case 3: return 1 default: return 0 } } func numberOfSections(in tableView: UITableView) -> Int { return self.sect.count } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { switch section { case 1: let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: KSCREEN_WIDTH, height: 30)) view.backgroundColor = UIColor.darkGray let title = UILabel.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 30)) title.text = self.sect[section] as? String title.textColor = UIColor.white view.addSubview(title) let notiButton = UIButton.init(type: .custom) notiButton.frame = CGRect.init(x: 100, y: 0, width: 100, height: 30) notiButton.setTitle((self.characteristic?.isNotifying)! ?"取消通知":"通知", for: .normal) notiButton.backgroundColor = UIColor.darkGray notiButton.addTarget(self, action: #selector(setNotifiy(sender:)), for: .touchUpInside) if (self.characteristic?.isNotifying)! { self.mananger.xm_setNotifiy(with: self.currPeripheral, for: self.characteristic, block: { (peripheral, characteristics, error) in self.insertReadValues(characteristics: characteristics!) }) } view.addSubview(notiButton) let writeButton = UIButton.init(type: .custom) writeButton.frame = CGRect.init(x: 200, y: 0, width: 100, height: 30) writeButton.setTitle("写(0x01)", for: .normal) writeButton.backgroundColor = UIColor.darkGray writeButton.addTarget(self, action: #selector(writeValue), for: .touchUpInside) view.addSubview(writeButton) return view default: let label = UILabel.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 30)) label.text = self.sect[section] as? String label.textColor = UIColor.white label.backgroundColor = UIColor.darkGray return label } } func writeValue(){ // 写入五位随机数 let num = arc4random()%100000 let string = String.init(format: "%d", num) let data = string.data(using: .utf8) self.mananger.write(data, to: self.currPeripheral, for: self.characteristic) } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 30 } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) }
- iOS CoreBluetooth 教程蓝牙技术
- iOS蓝牙技术
- IOS:蓝牙技术
- iOS开发之蓝牙4.0技术(详解)
- 浅谈iOS中的蓝牙技术(一) --GameKit.framework
- 浅谈iOS中的蓝牙技术(二) -- CoreBluetooth
- [置顶] iOS进阶--蓝牙技术
- iOS开发之蓝牙4.0技术完美实现
- 浅谈iOS中的蓝牙技术(二) -- CoreBluetooth
- iOS 蓝牙 技术
- ios开发之蓝牙4.0技术
- iOS - 蓝牙技术(一) - GameKit框架
- iOS开发之蓝牙4.0技术完美实现
- 学习ios蓝牙技术,仿写lightblue
- iOS 蓝牙通信技术(EAP和BLE)
- 学习ios蓝牙技术,仿写lightblue
- 蜂窝教育iOS培训:让技术武装您 企业重用您
- 今日分享-ios蓝牙
- 【腾讯Bugly干货分享】移动App入侵与逆向破解技术-iOS篇
- 【解决方法】【技术】22个iOS开发的小技巧