蓝牙常见名称和缩写
- 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
#import
#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
@end
@interface XMBlueToothManager : NSObject
@property (nonatomic , weak) iddelegate;
@property (nonatomic , strong) NSMutableArray *peripherals;//查找到的所有的外设
@property (nonatomic , copy) XMDidReadRSSIAtChannel readRSSIAtChannel;
@property (nonatomic , copy) XMBlockOnDisconnectAtChannel disconnectAtChannel;
@property (nonatomic , copy) XMDidUpdateNotificationStateForCharacteristicAtChannel notiUpdateAtChannel;
@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.scheduledTimer(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)
}
Demo下载地址