之前一直做的是Android,公司IOS端突然要同时进行多个项目,IOS同学表示压力山大,所以临危受命由我来完成项目中关于BLE通信的功能模块,由于之前做过Android版本的,并且运行状况良好,一直都比较稳定,因此分享出来,也希望大家能提出好的建议。
总共有4个swift文件。
如图:
BLEManager用于管理中心蓝牙提供扫描,延时停止扫描等功能
BLEModel是用于按照嵌入式规定的帧格式发送指令,解析指令,未加如重连。
CRC16 是用于进行CRC16校验用的
Utils 工具
附上主要代码:
// Created by JamesWang on 2018/8/13.
// BLE蓝牙的管理类,单例模式
//
//
//
import Foundation
import CoreBluetooth
import UIKit
class BLEManager:NSObject {
//Drip的Serviceuuid
let SERVICE_UUID: String = "0000ffe0-0000-1000-8000-00805f9b34fb"
//Drip的Characteristic UUID
let CHARACTERISTIC_UUID: String = "0000ffe1-0000-1000-8000-00805f9b34fb"
//断开连接
public static let STATUS_DISCONNECT:Int8 = 2
//连接失败
public static let STATUS_CONNECT_FAIL:Int8 = 1
//连接成功
public static let STATUS_CONNECT_SUCCESS:Int8 = 0
//单例模式
static let instance = BLEManager()
//蓝牙中心管理器
private var centralManager: CBCentralManager?
//扫描到的设备的集合
open var scanDevices = [CBPeripheral]()
/// 返回ble状态 用作权限处理
///
/// - Returns:
func getBLEState() -> Int {
return (centralManager?.state.rawValue)!
}
//连接状态的设备集合
private var bleModels = [BLEModel]()
//代理集合
var bleListeners = [BLEListener]()
//初始化
private override init() {
super.init()
print("init")
centralManager = CBCentralManager.init(delegate: self, queue: .main)
}
/// 添加监听接口
/// 如果之前有就覆盖
/// - Parameter listener: 监听接口
open func addListener(listener:BLEListener) {
for (index,lis) in bleListeners.enumerated() {
if(lis.getTag() == listener.getTag()) {
bleListeners.remove(at: index)
break
}
}
bleListeners.append(listener)
}
/// 清空监听
open func clearListener(){
bleListeners.removeAll()
}
/// 按照tag删除指定的listener
///
/// - Parameter tag: 提前设置好的tag
open func removeListenerByTag(tag:String) {
for (index,listener) in bleListeners.enumerated() {
if(listener.getTag() == tag) {
bleListeners.remove(at: index)
}
}
}
//扫描设备
open func scan(){
//扫描之前先清空所有的
openbluetooth()
scanDevices.removeAll(keepingCapacity: false)
centralManager?.scanForPeripherals(withServices: [CBUUID.init(string:SERVICE_UUID)], options: nil)
}
/// 开始扫描,并且在指定的seconds秒之后停止扫描
///
/// - Parameter seconds: 指定的秒数
open func scanWithStopDelay(seconds:Int) {
scan()
DispatchQueue.global().async {
sleep(UInt32(seconds))
self.stopscan()
}
}
//停止扫描
open func stopscan() {
centralManager?.stopScan()
}
/// 添加一个设备
///
/// - Parameter blemodel: 设备模型
func addBLEModel(blemodel:BLEModel) {
for model in bleModels {
if(model.bleName == blemodel.bleName) {
return
}
}
bleModels.append(blemodel)
}
/// 按照蓝牙名称返回对应设备
/// - Parameter name: 要获取的设备蓝牙名称
/// - Returns: 返回一个对应的设备,如果没有找到则返回nil
open func getBLEModelByName(name:String) -> BLEModel?{
for model in bleModels {
if(model.bleName == name) {
return model
}
}
return nil
}
/// 外界通过该方法进行与设备的连接
///
/// - Parameter peripheral: 外设
func connect(peripheral: CBPeripheral){
centralManager?.connect(peripheral, options: nil)
}
/// 断开连接
///
/// - Parameter peripheral: <#peripheral description#>
func disconnect(peripheral:CBPeripheral) {
centralManager?.cancelPeripheralConnection(peripheral)
}
/// 另外一种断开连接的方法
///
/// - Parameter name: 外设的名称 注意大小写
func disconnect(name:String) {
let blemodel = getBLEModelByName(name: name)
disconnect(peripheral:(blemodel?.peripheral)!)
}
}
//ble事件的代理
@objc protocol BLEListener:NSObjectProtocol {
//必须设置tag
func getTag() -> String
/// 状态变更
///
/// - Parameters:
/// - peripheral: 外设
/// - status: 状态
/// 0 连接成功
/// 1 连接失败
/// 2 断开连接
/// - Returns:
@objc optional func bleStatusChange(peripheral:CBPeripheral,status:Int8)
//发现新设备
@objc optional func bleNewDevice(peripheral:CBPeripheral,RSSI:NSNumber)
//读取rssi值
@objc optional func bleRssi(peripheral:CBPeripheral,RSSI:NSNumber)
/// 接受到数据 整帧的数据
///
/// - Parameters:
/// - data: 数据 具体协议参照文档
@objc optional func bleData(data:[Any])
}
//拓展出来蓝牙状态管理
extension BLEManager: CBCentralManagerDelegate{
func openbluetooth() {
if(centralManager?.state != .poweredOn) {
showAlert()
}
}
//判断手机蓝牙状态
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print(central.state.rawValue)
switch central.state {
case .poweredOn:
print("可用")
case .resetting:
print("重置中")
case .unsupported:
print("不支持")
case .unauthorized:
print("未验证")
case .poweredOff:
print("未启动")
case .unknown:
print("未知的")
}
}
/** 发现符合要求的外设 */
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
// 根据外设名称来过滤
print(peripheral.name!)
//如果不包含 就加入
if(!scanDevices.contains(peripheral)) {
scanDevices.append(peripheral)
for listener in bleListeners {
listener.bleNewDevice?(peripheral: peripheral,RSSI:RSSI)
}
}
}
// 连接成功
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
self.centralManager?.stopScan()
peripheral.discoverServices([CBUUID.init(string: SERVICE_UUID)])
let bleModel = BLEModel(p:peripheral)
peripheral.delegate = bleModel
addBLEModel(blemodel: bleModel)
print("连接成功 \(peripheral.identifier)")
for listener in bleListeners {
listener.bleStatusChange?(peripheral: peripheral, status: BLEManager.STATUS_CONNECT_SUCCESS)
}
}
//连接失败
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("连接失败")
for listener in bleListeners {
listener.bleStatusChange?(peripheral: peripheral, status: BLEManager.STATUS_CONNECT_FAIL)
}
}
//断开连接
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("断开连接")
// 重新连接
// central.connect(peripheral, options: nil)
for listener in bleListeners {
listener.bleStatusChange?(peripheral: peripheral, status: BLEManager.STATUS_DISCONNECT)
}
}
}
//
// BLEModel.swift
// 蓝牙中心设备Swift
//
// Created by JamesWang on 2018/8/15.
// 负责与设备通信的模块
import Foundation
import CoreBluetooth
class BLEModel:NSObject {
var sn:String = "" //设备的sn编号 需要先调用sendRequestSN
var status:Int8 = 0 //设备状态
var coldTem:Float32 = 0 //冷水温度
var hotTem:Float32 = 0 //热水温度
var statusProgress:UInt8 = 0 //当前状态的进度值
var electricStatusX:Int8 = 0 //x轴电机状态
var electricStatusY:Int8 = 0 //y轴电机状态
var electricStatusZ:Int8 = 0//z轴电机状态
var electricStatusR:Int8 = 0 //r轴电机状态
var waterBoxStatus:UInt8 = 0 //水箱状态
var brewTime:Int32 = 0 //累计冲泡时间
//////////////////////////////////////////////////
let CMD_01:UInt8 = 0x01 //获取设备序列号
let CMD_81:UInt8 = 0x81// 返回设备序列号
/// 获取设备序列号
func sendRequestSN() {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(4)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_01)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 解析SN
/// 6~33 SN28个字节,高位在前
/// - Parameter data:
func convert81(data:[UInt8]) {
if(data.count != 36) {
return
}
var array:[UInt8] = []
for (index,item) in data.enumerated() {
if(index >= 6 && index <= 33) {
array.append(item)
}
}
sn = String.init(data:Data.init(bytes: array),encoding: String.Encoding.utf8).unsafelyUnwrapped
//将所有的状态数据post出去
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_81,sn))
}
}
//////////////////////////////////////////////////
let CMD_03:UInt8 = 0x03 //获取设备状态
let CMD_83:UInt8 = 0x83 //返回设备状态
/// 请求获取设备状态
func sendRequestStatus(){
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
data.append(int16value: 4)
//设备ID
data.append(0x01)
//命令
data.append(CMD_03)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 解析设备状态
/// 例如: 66 ee 00 16 01 83 00 41 B2 66 66 41 B2 66 66 00 00 00 00 00 00 10 00 00 01 01
/// 字节范围 含义
/// 0-1 帧头
/// 2-3 帧长
/// 4 设备ID 0:保留 1:Drip 2:Drop
/// 5 命令字 0x81
/// 6 状态字 0:空闲(唤醒态) 1:睡眠 2:待机(唤醒态但未工作,工作取消) 3:查找中心点 4:冲泡状态 5:冲泡中止状态 6:湿滤纸态 -1:设备故障 Int8
/// 7-10 冷水的温度 Float32
/// 11-14 热水的温度 Float32
/// 15 状态值 指当前状态的进度值: 查找中心点:0 -未找到中心点 1-已找到中心点 2-正在查找 冲泡状态代表进度百分比 UInt8
/// 16 电机故障信息 Bit0~Bit1:X轴 Bit2~Bit3:Y轴 Bit4~Bit5:Z轴 Bit6~Bit7:R轴
/// 17 水箱故障信息
/// 18-21 累计冲泡时间 Int32
/// 22 预留
/// 23 预留
/// 24-25 CRC16
/// - Parameter data:
func convert83(data:[UInt8]) {
// assert(data.count == 18,"data count != 18 \(data)")
if(data.count != 26) {
return
}
status = Int8.init(bitPattern: data[6])
//获取到冷水温度
coldTem = Utils.convertFloat(i1: data[7], i2: data[8], i3: data[9], i4: data[10])
//获取到热水温度
hotTem = Utils.convertFloat(i1: data[11], i2: data[12], i3: data[13], i4: data[14])
//获取到状态进度值
statusProgress = data[15]
//电机故障信息
electricStatusX = Int8.init(bitPattern: (data[16] & 0x03))
electricStatusY = Int8.init(bitPattern: (data[16] >> 2 & 0x03))
electricStatusZ = Int8.init(bitPattern: (data[16] >> 4 & 0x03))
electricStatusR = Int8.init(bitPattern: (data[16] >> 6 & 0x03))
waterBoxStatus = data[17]
brewTime = Utils.convertInt(i1: Int(data[18]), i2: Int(data[19]), i3: Int(data[20]), i4: Int(data[21]))
print("解析到数据了 \(status) \(coldTem) \(hotTem) \(statusProgress)")
let bledata:[Any] = generateBLEData(value:peripheral?.name ?? "",CMD_83,status,coldTem,hotTem,statusProgress,electricStatusX,electricStatusY,electricStatusZ,electricStatusR,waterBoxStatus,brewTime)
//将所有的状态数据post出去
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: bledata)
}
}
//////////////////////////////////////////////////
let CMD_04:UInt8 = 0x04 //设置设备参数
let CMD_84:UInt8 = 0x84 //返回设置参数响应
/// 设置设备参数
///
/// - Parameters:
/// - sleepMode: 休眠模式 UInt8 0: 没有休眠模式 1:休眠模式1(台上休眠) 2:休眠模式2(台下休眠)默认
/// - idleTime: 休眠开启时间 空闲时间 UInt16 0~65535 秒
/// - preWater: 预先出水量 UInt8 0~255 毫升,将管内冷水回抽后,预泵出的热水水量
/// - boilerTem: 锅炉温度 Float32 热水温度值
/// - totalTrip: z轴总行程 UInt16 Z轴上升的最大高度(默认475mm)
/// - searchHeight: 寻找高度 UInt16 寻找咖啡杯时,Z轴上升的高度值,根据不同的杯子、咖啡量进行调节匹配。
/// - armLength: 上臂总长度 UInt16 Z轴行程=冲泡高度+上臂长度(默认220mm)
/// - horizontalOffset: 水平偏移 Int8 设置水平方向像素偏移值(-80~80)
/// - verticalOffset: 垂直偏移 Int8 设置垂直方向像素偏移值(-60~60)
/// - angleWake: 唤醒角度 UInt8 唤醒时R轴旋转的角度 范围0~198
func sendSetDatas(sleepMode:UInt8,idleTime:UInt16,preWater:UInt8,boilerTem:Float32,totalTrip:UInt16,searchHeight:UInt16,armLength:UInt16,horizontalOffset:Int8,verticalOffset:Int8,angleWake:UInt8)
{
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
data.append(int16value: 21)
//设备ID
data.append(0x01)
//命令
data.append(CMD_04)
//休眠模式 UInt8
data.append(sleepMode)
//休眠开启时间 空闲时间 UInt16
data.append(uint16value: idleTime)
//预先出水量 UInt8
data.append(preWater)
//锅炉温度 Float32
data.append(float32value: boilerTem)
//z轴总行程 UInt16
data.append(uint16value: totalTrip)
//寻找高度 UInt16
data.append(uint16value: searchHeight)
//上臂总长度 UInt16
data.append(uint16value: armLength)
//horizontalOffset: 水平偏移 Int8
data.append(UInt8.init(bitPattern: horizontalOffset))
//verticalOffset: 垂直偏移 Int8
data.append(UInt8.init(bitPattern: verticalOffset))
//angleWake UInt8
data.append(angleWake)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 返回设置设备参数
/// 例如: 66 ee 00 03 02 82 00 42 d5
/// 字节范围 含义
/// 0-1 帧头
/// 2-3 帧长
/// 4 帧类别
/// 5 命令字
/// 6 0:OK 1:失败
/// 7-8 CRC校验
/// 因为此帧只会传递过来ok结果 因此暂时认为一但接受到该帧就认为是参数设置成功了
/// - Parameter data:
func convert84(data:[UInt8]) {
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_84,data[6]))
}
}
//////////////////////////////////////////////////
let CMD_05:UInt8 = 0x05 //获取设备参数
let CMD_85:UInt8 = 0x85 //返回设备参数
/// 请求设备参数
func sendRequestDatas() {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
data.append(int16value: 4)
//设备ID
data.append(0x01)
//命令
data.append(CMD_05)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 解析设备参数
/// 例如:
/// 字节范围 含义
/// 0-1 帧头
/// 2-3 帧长
/// 4 设备类别 drip01
/// 5 命令字 0x85
/// 6 休眠模式 0: 没有休眠模式 1:休眠模式1(上面) 2:休眠模式2(下面)默认 UInt8
/// 7-8 休眠开启时间 0~65536 秒 UInt16
/// 9 预先出水量 0~255 毫升 UInt8
/// 10-13 锅炉的温度 温度 (浮点数) Float32
/// 14-15 总行程 Z轴总行程(默认485mm) L Int16
/// 16-17 寻找高度 寻找咖啡杯时,Z轴上升的高度值,根据不同的杯子、咖啡量进行调节匹配 UInt16
/// 18-19 上臂长度 (默认230mm) B UInt16
/// 20 水平偏移 设置水平方向像素偏移值(-80~80) Int8
/// 21 垂直偏移 设置垂直方向像素偏移值(-60~60) Int8
/// 22 角度值 R轴唤醒偏移角度 范围0~255 UInt8
/// 23-24 CRC校验
/// 因为此帧只会传递过来ok结果 因此暂时认为一但接受到该帧就认为是参数设置成功了
/// - Parameter data:
func convert85(data:[UInt8]) {
if(data.count != 25) {
return
}
//长度ok 开始具体的解析
//创建一个存储参数的集合 将参数存储到该集合中
var bledata:[Any] = []
bledata.append(peripheral?.name ?? "")
bledata.append(CMD_85)
//存入休眠模式 无符号UInt8
bledata.append(data[6])
//存入休眠开启时间 无符号UInt16
bledata.append(Utils.convertUShort(i1: data[7], i2: data[8]))
//存入预先出水量 无符号UInt8
bledata.append(data[9])
//存入锅炉的温度 Float32
bledata.append(Utils.convertFloat(i1: data[10], i2: data[11], i3: data[12], i4: data[13]))
//存入Z总行程 有符号 Int16
bledata.append(Utils.convertShort(i1: data[14], i2: data[15]))
//寻找高度 无符号 UInt16
bledata.append(Utils.convertUShort(i1: data[16], i2: data[17]))
//上臂长度 无符号 UInt16
bledata.append(Utils.convertUShort(i1: data[18], i2: data[19]))
//水平偏移 Int8
bledata.append(Int8.init(bitPattern: data[20]))
//垂直偏移 Int8
bledata.append(Int8.init(bitPattern: data[21]))
//角度值 R轴唤醒偏移角度 UInt8
bledata.append(data[22])
//将所有的状态数据post出去 需要在
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: bledata)
}
}
//////////////////////////////////////////////////
let CMD_06:UInt8 = 0x06 //切换通信模式
let CMD_86:UInt8 = 0x86 //返回切换通信模式
/// 切换通信模式
func sendRequestSwitchMode() {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
data.append(int16value: 4)
//设备ID
data.append(0x01)
//命令
data.append(CMD_06)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 切换通信模式响应
///
/// 字节范围 含义
/// 0-1 帧头
/// 2-3 帧长
/// 4 帧类别
/// 5 命令字 0X84
/// 6-7 CRC16
/// - Parameter data:
func convert86(data:[UInt8]){
if(data.count != 8) {
return
}
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_86))
}
}
//////////////////////////////////////////////////
let CMD_07:UInt8 = 0x07 //WiFi信息配置
let CMD_87:UInt8 = 0x87 //返回WiFi信息配置
/// 设置wifi的ssid
///
/// - Parameter ssid: ssid
func sendSetWifiSSID(ssid:String) {
let byteArray = [UInt8](ssid.utf8)
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(byteArray.count + 5)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_07)
//控制字
data.append(UInt8.init(bitPattern: 0x01))
//ssid
data.append(contentsOf: byteArray)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 设置wifi的密码
///
/// - Parameter pwd: wifi密码
func sendSetWifiPwd(pwd:String) {
let byteArray = [UInt8](pwd.utf8)
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(byteArray.count + 5)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_07)
//控制字
data.append(UInt8.init(bitPattern: 0x02))
//密码
data.append(contentsOf: byteArray)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
sendAsync(data: data.toArray(type: UInt8.self))
}
//6 控制字 1:SSID2:Password
func convert87(data:[UInt8]) {
if(data.count != 9) {
return
}
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_87,data[6]))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_08:UInt8 = 0x08 //写冲泡文件
let CMD_88:UInt8 = 0x88 //返回写冲泡文件
/// 发送Profile名称
///总长度不要超过100个字节
/// - Parameter profileName: profile的名称
private func sendProfileName(profileName:String) -> Bool{
let byteArray = [UInt8](profileName.utf8)
loopcount = 0 //循环号归0
if(byteArray.count > 100) {
print("总长度不要超过100个字节,count= \(byteArray.count)" )
return false
}
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(byteArray.count + 6)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_08)
//控制字
data.append(UInt8.init(bitPattern: 0x00))
//文件内容
data.append(contentsOf: byteArray)
//循环号
data.append(loopcount)
loopcount += 1
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
//尝试发送次数为3次
A:for _ in (0..<3) {
send(data: data.toArray(type: UInt8.self))
let start = Int(Date().timeIntervalSince1970) //获取系统当前秒数
while(Int(Date().timeIntervalSince1970) - start < BLEModel.MAX_WAIT && !isNameCome && !isResendCome && !isErrorCome) {
}
print("isnamecome = \(isNameCome)")
if(isNameCome) {
isNameCome = false //重置
return true
}
else {
continue A
}
}
return false
}
/// 发送设置Profile的总长度
///
/// - Parameter profileSize: profile的大小
private func sendProfileSize(profileSize:UInt32) -> Bool {
print("sendProfileSize")
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(10)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_08)
//控制字
data.append(UInt8.init(bitPattern: 0x01))
//文件内容
data.append(uint32value: profileSize)
//循环号
data.append(loopcount)
loopcount += 1
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//尝试发送次数为3次
A:for _ in (0..<3) {
send(data: data.toArray(type: UInt8.self))
let start = Int(Date().timeIntervalSince1970) //获取系统当前秒数
while(Int(Date().timeIntervalSince1970) - start < BLEModel.MAX_WAIT && !isSizeCome && !isResendCome && !isErrorCome) {}
if(isSizeCome) {
isSizeCome = false //重置
return true
}
else {
continue A
}
}
return false
}
/// 按照1K的限制来切割数据
/// 需要在子线程中发送profile
///
/// - Parameter profileContent:
private func sendProfileContent(profileContent:String) ->Bool {
print(profileContent)
var byteArray:[UInt8] = [UInt8](profileContent.utf8)//将profile转换为[UInt8]
let arrayCount = byteArray.count / BLEModel.LIMIT_SIZE + 1
var bufArrays:[[UInt8]] = []
for _ in (0.. 0) {
buf.append(byteArray.removeFirst())
}
bufArrays.append(buf)
}
Big:
for array in bufArrays {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(array.count + 6)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_08)
//控制字
data.append(UInt8.init(bitPattern: 0x02))
//内容
data.append(contentsOf: array)
//循环号
data.append(loopcount)
loopcount += 1
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
A:
for _ in (0..<3) {
//发送数据
send(data: data.toArray(type: UInt8.self))
let start = Int(Date().timeIntervalSince1970) //获取系统当前秒数
while (Int(Date().timeIntervalSince1970) - start < BLEModel.MAX_WAIT && !isContentCome && !isResendCome && !isErrorCome) {}
if (isContentCome) {
isContentCome = false
continue Big
} else {
continue A
}
}
return false
}
return true
}
/// 发送传输结束
private func sendProfileFinish() -> Bool{
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(10)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_08)
//控制字
data.append(UInt8.init(bitPattern: 0x03))
//循环号
data.append(loopcount)
loopcount += 1
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
send(data: data.toArray(type: UInt8.self))
//尝试发送次数为3次
A:for _ in (0..<3) {
send(data: data.toArray(type: UInt8.self))
let start = Int(Date().timeIntervalSince1970) //获取系统当前秒数
while(Int(Date().timeIntervalSince1970) - start < BLEModel.MAX_WAIT && !isFinishCome && !isResendCome && !isErrorCome) {}
if(isSizeCome) {
isSizeCome = false //重置
return true
}
else {
continue A
}
}
return false
}
/// 取消传输
func sendProfileCancel() {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(10)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_08)
//控制字
data.append(UInt8.init(bitPattern: 0x04))
//循环号
data.append(loopcount)
loopcount += 1
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
send(data: data.toArray(type: UInt8.self))
}
/// 发送profile
///
/// - Parameters:
/// - name: profile的名称
/// - content: profile的内容
/// - isDefault: 是否设置为默认Profile
func sendProfile(name:String,content:String,isDefault:Bool) {
isNameCome = false //名称发送是否正常响应
isResendCome = false//重新发送是否正常响应
isErrorCome = false//失败错误发送是否正常响应
isSizeCome = false //大小发送是否正常响应
isContentCome = false //内容发送是否正常响应
isFinishCome = false //完成发送是否正常响应
isCancelCome = false //取消发送是否正常响应
isSetDefaultCome = false //设置为默认是否为正常响应
isJustSend = false //设置为是否只是发送Profile的flag
DispatchQueue.global().async {
let byteArray:[UInt8] = [UInt8](content.utf8)//将profile转换为[UInt8]
if (isDefault) {
self.isJustSend = true
if(!self.sendSetDefaultProfile(name:name)) { //发送是否设置为默认
return
}
} else {
self.isJustSend = false
}
if (!self.sendProfileName(profileName: name)) { //发送profile名称
return
}
if (!self.sendProfileSize(profileSize:UInt32(byteArray.count))) { //发送大小
return
}
if (!self.sendProfileContent(profileContent:content)) { //发送内容
return
}
if (!self.sendProfileFinish()) { //发送是否结束
return
}
}
}
/// 返回传送Profile状态
///
/// - Parameter data: <#data description#>
func convert88(data:[UInt8]) {
if(data.count != 10) {
return
}
let status = Int8.init(bitPattern: data[6])
print("convert85 = \(status)")
switch status {
case 0: //文件名传输成功
isNameCome = true
case 1://1:文件总长度
isSizeCome = true
case 2: //传输中
isContentCome = true
case 3: //传输结束 检查是否只是发送 判断是否开始搜寻cup
isFinishCome = true
if (!isJustSend) {
sendSetStatus(status:0x04);//查找中心点
}
case 4: //取消传输
isCancelCome = true
case 5://检验错误 重发
isResendCome = true
case -1: //故障
isErrorCome = true
default:
break
}
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_88,status))
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_09:UInt8 = 0x09 //控制设备运行状态
let CMD_89:UInt8 = 0x89 //返回控制设备运行状态
/// 设置设备状态 0:空闲
/// 1:进入休眠
/// 2:唤醒设备
/// 3:进入IAP模式
/// 4:查找中心点
/// 5:停止查找中心点(回到复位位置)
/// 6:开始冲泡
/// 7:暂停冲泡
/// 8:继续冲泡
/// 9:结束冲泡
/// see BLEModel.ACTION...
func sendSetStatus(status:Int8) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(5)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_09)
//控制字
data.append(UInt8.init(bitPattern: status))
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 返回控制设备运行状态
///
/// - Parameter data: <#data description#>
func convert89(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_89,data[6]))
}
}
//////////////////////////////////////////////////
let CMD_0A:UInt8 = 0x0A //设置本地默认冲泡文件
let CMD_8A:UInt8 = 0x8A //返回设置本地默认冲泡文件
/// 设置为默认的Profile
///
/// - Parameter name: profile的名称
/// - Returns: 是否设置成功
private func sendSetDefaultProfile(name:String) -> Bool {
self.isSetDefaultCome = false
let byteArray = [UInt8](name.utf8)
loopcount = 0 //循环号归0
if(byteArray.count > 100) {
print("总长度不要超过100个字节,count= \(byteArray.count)" )
return false
}
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(byteArray.count + 6)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_0A)
//文件内容
data.append(contentsOf: byteArray)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
//尝试发送次数为3次
A:for _ in (0..<3) {
send(data: data.toArray(type: UInt8.self))
//获取系统当前秒数
let start = Int(Date().timeIntervalSince1970)
while(Int(Date().timeIntervalSince1970) - start < BLEModel.MAX_WAIT && !isSetDefaultCome && !isResendCome && !isErrorCome) {
}
if(isSetDefaultCome) {
//重置
isSetDefaultCome = false
return true
}
else {
continue A
}
}
return false
}
/// 返回设置本地默认冲泡文件
///
/// - Parameter data: <#data description#>
func convert8A(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_8A,data[6]))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_0B:UInt8 = 0x0B //设置灯头颜色值
let CMD_8B:UInt8 = 0x8B //返回灯头颜色设置
/// 设置灯头颜色值
///
/// - Parameters:
/// - mode: 0:RGB 1:HSV
/// - ledMode: 0:长亮 1:单闪 2:双闪 3:呼吸 4::关闭
/// - first: R/H值 mode为0时表示R值, mode为1时表示H值, R范围:0~255 H范围:0~360
/// - second: G/S值
/// - third: B/V值
func sendSetColor(mode:UInt8,ledMode:UInt8,first:UInt8,second:UInt8,third:UInt8) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(9)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_0B)
//颜色模式
data.append(mode)
//LED模式
data.append(ledMode)
//R/H值
data.append(first)
//G/S值
data.append(second)
// B/V 值
data.append(third)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 返回灯头颜色设置
///
/// - Parameter data: <#data description#>
func convert8B(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_8B,data[6]))
}
}
//////////////////////////////////////////////////
let CMD_0C:UInt8 = 0x0C //出水模式设置
let CMD_8C:UInt8 = 0x8C //返回出水模式设置
///出水模式设置
/// - Parameters:
/// - size: 出水量0~0xffff ml
/// - tem: 出水温度 水温:常温~95℃
/// - time: 出水时间 单位:0~255秒 UInt8
/// - height: 出水高度 0~240mm
/// - type: 杯型 0~255
func sendSetOutWaterMode(size:UInt16,tem:UInt8,time:UInt8,height:UInt8,type:UInt8) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(10)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_0C)
//出水量
data.append(uint16value: size)
//出水温度
data.append(tem)
//出水时间
data.append(time)
//出水高度
data.append(height)
//杯型
data.append(type)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 返回出水模式设置
///
/// - Parameter data: <#data description#>
func convert8C(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_8C,data[6]))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_0D:UInt8 = 0x0D //读取R轴编码器值
let CMD_8D:UInt8 = 0x8D //返回读取R轴编码器值
///读取R轴编码器值
func sendRequestREncodeValue(){
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(4)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_0D)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 返回R轴编码器值
///
/// - Parameter data: <#data description#>
func convert8D(data:[UInt8]) {
if(data.count != 10) {
return
}
let rEncodeValue:UInt16 = Utils.convertUShort(i1: data[6], i2: data[7])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_8D,rEncodeValue))
}
}
//////////////////////////////////////////////////
let CMD_0E:UInt8 = 0x0D //设置R轴编码器值(直立态)
let CMD_8E:UInt8 = 0x8D //返回R轴编码器设置结果
///设置R轴编码器值(直立态)
///
/// - Parameter value: R轴编码器值
func sendSetREncodeStraight(value:UInt16) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(6)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_0E)
//R轴编码器值(直立态)
data.append(uint16value: value)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 返回R轴编码器设置结果
///
/// - Parameter data: <#data description#>
func convert8E(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_8E,data[6]))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_0F:UInt8 = 0x0F //设置R轴角度(相对值)
let CMD_8F:UInt8 = 0x8F //返回R轴角度(绝对值)
///设置R轴角度(相对值)
///
/// - Parameter angle: 角度值 取值范围:-180°~ 180° 正值:顺时针转;负值:逆时针转
func sendSetRAngleRelative(angle:Float32) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(8)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_0F)
//R轴角度 相对值
data.append(float32value: angle)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 32.返回R轴角度(绝对值)
///
/// - Parameter data: 6-9
func convert8F(data:[UInt8]) {
if(data.count != 12) {
return
}
let angle:Float = Utils.convertFloat(i1: data[6], i2: data[7], i3: data[8], i4: data[9])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_8F,angle))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_10:UInt8 = 0x10 //设置X轴位置
let CMD_90:UInt8 = 0x90 //设置X轴位置响应
///设置X轴位置
///
/// - Parameter offSet: <#offSet description#>
func sendPositionX(offSet:UInt8) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(5)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_10)
//R轴角度 相对值
data.append(offSet)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 设置X轴位置响应
///
/// - Parameter data: <#data description#>
func convert90(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_90,data[6]))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_11:UInt8 = 0x11 //设置Y轴位置
let CMD_91:UInt8 = 0x91 //设置Y轴位置响应
///设置Y
///
/// - Parameter offSet: <#offSet description#>
func sendAngleY(offSet:UInt8) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(5)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_11)
//R轴角度 相对值
data.append(offSet)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
func convert91(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_91,data[6]))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_12:UInt8 = 0x12 //设置Z轴位置
let CMD_92:UInt8 = 0x92 //设置Z轴位置响应
///设置Z轴位置
///
/// - Parameter offSet: <#offSet description#>
func sendPositionZ(offSet:UInt8) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(5)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_12)
//R轴角度 相对值
data.append(offSet)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
func convert92(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_92,data[6]))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_13:UInt8 = 0x13 //设置寻找模式
let CMD_93:UInt8 = 0x93 //设置寻找模式响应
/// 设置寻找模式
///
/// - Parameter mode: 寻找模式 0:寻找中心点模式 1:定点冲泡模式
func sendSetSearchMode(mode:UInt8) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(5)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_13)
//寻找模式
data.append(mode)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 设置寻找模式响应
///
/// - Parameter data: <#data description#>
func convert93(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_93,data[6]))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_14:UInt8 = 0x14 //设置咖啡颜色LAB值(摄像头识别的色块)
let CMD_94:UInt8 = 0x94 //返回LAB值设置状态
/// 设置咖啡颜色LAB值(摄像头识别的色块)
///
/// - Parameters:
/// - minL: L最小值 取值范围:0~100
/// - maxL: L最大值 取值范围:0~100
/// - minA: A最小值 取值范围:-120~120
/// - maxA: A最大值 取值范围:-120~120
/// - minB: B最小值 取值范围:-120~120
/// - maxB: B最大值 取值范围:-120~120
func sendSetCoffeeColorLABValue(minL:Int8,maxL:Int8,minA:Int8,maxA:Int8,minB:Int8,maxB:Int8) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(10)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_14)
//minL: L最小值
data.append(UInt8.init(bitPattern: minL))
//maxL: L最大值
data.append(UInt8.init(bitPattern: maxL))
//minA
data.append(UInt8.init(bitPattern: minA))
//maxA
data.append(UInt8.init(bitPattern: maxA))
//minB
data.append(UInt8.init(bitPattern: minB))
//maxB
data.append(UInt8.init(bitPattern: maxB))
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// 返回LAB值设置状态
///
/// - Parameter data: <#data description#>
func convert94(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_94,data[6]))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let CMD_15:UInt8 = 0x15 //设置咖啡识别色块尺寸(摄像头识别的色块)
let CMD_95:UInt8 = 0x95 //返回识别色块尺寸设置状态
///设置咖啡识别色块尺寸(摄像头识别的色块)
///
/// - Parameters:
/// - minWidth: 最小宽度
/// - maxWidth: 最大宽度
/// - minHeight: 最小高度
/// - maxHeight: 最大高度
func sendSetCoffeeSize(minWidth:UInt8,maxWidth:UInt8,minHeight:UInt8,maxHeight:UInt8) {
var data = Data()
//组装头
data.append(contentsOf: HEAD)
//帧长
let len:Int16 = (Int16)(8)
data.append(int16value: len)
//设备ID
data.append(0x01)
//命令
data.append(CMD_15)
//minW
data.append(minWidth)
//maxW
data.append(maxWidth)
//minH
data.append(minHeight)
//maxH
data.append(maxHeight)
//CRC16检验
data.append(contentsOf: CRC16.instance.getCRCResult(data:data.toArray(type: UInt8.self)))
//发送数据
sendAsync(data: data.toArray(type: UInt8.self))
}
/// <#Description#>
///
/// - Parameter data: <#data description#>
func convert95(data:[UInt8]) {
if(data.count != 9) {
return
}
// let status:Int8 = Int8.init(bitPattern: data[6])
for listener in BLEManager.instance.bleListeners {
listener.bleData?(data: generateBLEData(value: peripheral?.name ?? "",CMD_95,data[6]))
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
let MAX_COUNT = 2048
let semaphore = DispatchSemaphore(value: 1)
var peripheral: CBPeripheral?
var bleName:String//设备蓝牙名称
let HEAD:[UInt8] = [0x66,0xee]
private var characteristic: CBCharacteristic?
let queue = DispatchQueue.global()
private var data=[UInt8]()//缓存数组
private var lock:NSLock = NSLock()
private var sendLock:NSLock = NSLock()
init(p: CBPeripheral) {
peripheral = p
bleName=(peripheral?.name)!
}
var loopcount:UInt8 = 0 //循环号
public static let STATUS_IDLE:Int8 = 0x00 //空闲
public static let STATUS_SLEEP:Int8 = 0x01 //睡眠
public static let STATUS_STANDBY:Int8 = 0x02 // 待机
public static let STATUS_SEARCHING:Int8 = 0x03 //查找中心点
public static let STATUS_WORKING:Int8 = 0x04 //冲泡状态
public static let STATUS_PAUSE:Int8 = 0x05 //冲泡中止状态
public static let STATUS_WET_PAPER:Int8 = 0x06//湿滤纸态
public static let STATUS_ERROR:Int8 = -1 //设备故障
public static let ACTION_IDLE:Int8 = 0x00//设置空闲动作
public static let ACTION_SLEEP:Int8 = 0x01 //设置休眠动作
public static let ACTION_WAKE:Int8 = 0x02 //设置唤醒动作
public static let ACTION_IAP:Int8 = 0x03 //设置IAP
public static let ACTION_SEARCH:Int8 = 0x04 //设置查找中心点动作
public static let ACTION_PAUSE_SEARCH:Int8 = 0x05 //设置停止查找动作
public static let ACTION_START_BREW:Int8 = 0x06 //设置开始冲泡动作
public static let ACTION_PAUSE_BREW:Int8 = 0x07//暂停冲泡动作
public static let ACTION_RESUME_BREW:Int8 = 0x08 //继续冲泡动作
public static let ACTION_END_BREW:Int8 = 0x09 //结束冲泡动作
public static let LIMIT_SIZE = 1000 //单包最大长度
public static let MAX_WAIT = 100 //发送Profile timeout 为3秒
private var isNameCome:Bool = false //名称发送是否正常响应
private var isResendCome:Bool = false//重新发送是否正常响应
private var isErrorCome:Bool = false//失败错误发送是否正常响应
private var isSizeCome:Bool = false //大小发送是否正常响应
private var isContentCome:Bool = false //内容发送是否正常响应
private var isFinishCome:Bool = false //完成发送是否正常响应
private var isCancelCome:Bool = false //取消发送是否正常响应
private var isSetDefaultCome:Bool = false //设置为默认是否为正常响应
private var isJustSend:Bool = false //设置为是否只是发送Profile的flag
}
// MARK: - 通信协议
extension BLEModel{
func generateBLEData(value : Any...) -> [Any]{
var val:[Any] = []
for item in value {
val.append(item)
}
print("genate data = \(val)")
return val
}
//解析现有数据
func convert(){
if(self.data.count > self.MAX_COUNT) {//当字节数组大于MAX_COUNT就清空
self.data.removeAll()
return
}
else if(self.data.count > 5) {//只有大于5个字节的时候才去解析
let h0 = self.data[0]
let h1 = self.data[1]
//只有是帧头才会解析下去
if(h0 == 0x66 && h1 == 0xee) {
let len0 = self.data[2]
let len1 = self.data[3]
let frameLen:Int16 = Utils.convertShort(i1: len0, i2: len1)
// assert(self.data.count >= frameLen + 2 + 1,"总长度小于单帧长度")
if(self.data.count < frameLen + 2 + 1) {//如果总长度小于该帧长度 就不解析
return
}
else {
let _ = self.data[4]//设备类别 1drip
let frameCmd = self.data[5]
let cmddata = Array(self.data[0...Int(frameLen+3)])//已确定data中有一帧的数据量直接截取前部分作为一个单独的Data处理
self.data.removeSubrange(0...Int(frameLen+3))//删除这一部分帧
switch frameCmd {
case CMD_81://返回设备序列号
convert81(data:cmddata)
case CMD_83://返回设备状态
convert83(data:cmddata)
case CMD_84://返回设置设备参数
convert84(data:cmddata)
case CMD_85://解析设备参数
convert85(data:cmddata)
case CMD_86://切换通信模式响应
convert86(data:cmddata)
case CMD_87://返回WiFi信息配置
convert87(data:cmddata)
case CMD_88://返回传送Profile状态
convert88(data:cmddata)
case CMD_89://返回控制设备运行状态
convert89(data:cmddata)
case CMD_8A://返回设置本地默认冲泡文件
convert8A(data:cmddata)
case CMD_8B://返回灯头颜色设置
convert8B(data:cmddata)
case CMD_8C://返回出水模式设置
convert8C(data:cmddata)
case CMD_8D://返回读取R轴编码器值
convert8D(data:cmddata)
case CMD_8E://返回R轴编码器设置结果
convert8E(data:cmddata)
case CMD_8F://返回R轴角度(绝对值)
convert8F(data:cmddata)
case CMD_90://设置X轴位置响应
convert90(data:cmddata)
case CMD_91://返回R轴角度(绝对值)
convert91(data:cmddata)
case CMD_92://设置Y轴位置响应
convert92(data:cmddata)
case CMD_93://设置寻找模式响应
convert93(data:cmddata)
case CMD_94://返回LAB值设置状态
convert94(data:cmddata)
case CMD_95://返回识别色块尺寸设置状态
convert95(data:cmddata)
default:
break
}
convert()
}
} else {//如果不是头信息 就删除第一个字节 继续解析
self.data.removeFirst()
print("继续解析")
convert()//继续解析
}
}
}
/// 向蓝牙外设发送数据
///
/// - Parameter data: 要发送的数据
func send(data:[UInt8] ){
self.sendLock.lock()
var d:[UInt8] = []
d.append(contentsOf: data)
let mtu = 20//单次传输最大字节数
var dt:[UInt8] = []
while(d.count>0) {
while(dt.count < mtu && d.count > 0) {
dt.append(d.removeFirst())
}
self.semaphore.wait()
//发送数据
self.peripheral?.writeValue(Data.init(bytes: dt), for: self.characteristic!,type:CBCharacteristicWriteType.withResponse)
dt.removeAll()
}
self.sendLock.unlock()
}
/// 向蓝牙外设发送数据
///
/// - Parameter data: 要发送的数据
func sendAsync(data:[UInt8] ){
DispatchQueue.global().async {
self.send(data:data)
}
}
}
extension BLEModel:CBPeripheralDelegate{
/** 发现服务 */
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service: CBService in peripheral.services! {
print("外设中的服务有:\(service)")
}
//本例的外设中只有一个服务
let service = peripheral.services?.last
// 根据UUID寻找服务中的特征
peripheral.discoverCharacteristics([CBUUID.init(string: BLEManager.instance.CHARACTERISTIC_UUID)], for: service!)
//peripheral.discoverCharacteristics(nil, for: service!)
}
/** 发现特征 */
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic: CBCharacteristic in service.characteristics! {
print("外设中的特征有:\(characteristic)")
}
self.characteristic = service.characteristics?.last
// 读取特征里的数据
peripheral.readValue(for: self.characteristic!)
// 订阅
peripheral.setNotifyValue(true, for: self.characteristic!)
peripheral.readRSSI()
}
/** 订阅状态 */
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if let error = error {
print("订阅失败: \(error)")
return
}
if characteristic.isNotifying {
print("订阅成功")
} else {
print("取消订阅")
}
}
//接收到数据
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
lock.lock()
let tdarray = characteristic.value.unsafelyUnwrapped.toArray(type: UInt8.self)
print("新数据=\(Utils.byteArrayToHexString(tdarray))")
for b in tdarray {
self.data.append(b)
}
//开始解析
convert()
lock.unlock()
}
//写入数据
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
// print("写入数据\(characteristic) \(error.debugDescription) \(error?.localizedDescription)")
//释放信号量
semaphore.signal()
}
//接收到RSSI
func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI:NSNumber, error: Error?){
DispatchQueue.main.asyncAfter(deadline:DispatchTime.now() + 2,execute:{
peripheral.readRSSI()
})
}
}
//
// CRC16.swift
//
// Created by JamesWang on 2018/8/22.
//
import Foundation
class CRC16:NSObject{
//单例模式
static let instance = CRC16()
private var crcTable: [Int] = []
///多项式 CRC-16/XMODEM
private let gPloy = 0x1021
//初始化
private override init() {
super.init()
computeCrcTable()
}
/// 传入[UInt8]返回CRC16检验数据
///
/// - Parameter data: <#data description#>
/// - Returns: <#return value description#>
func getCRCResult (data: [UInt8]) -> [UInt8] {
var crc = getCrc(data:data)
var crcArr: [UInt8] = [0,0]
//// Swift3.0
for i in (0..<2).reversed() {
crcArr[i] = UInt8(crc % 256)
crc >>= 8
}
// for (var i = 1; i >= 0; i -= 1) {
//
// }
return crcArr
}
/**
Generate CRC16 Code of 0-255
*/
private func computeCrcTable() {
for i in 0..<256 {
crcTable.append(getCrcOfByte(aByte:i))
}
}
private func getCrcOfByte(aByte: Int) -> Int {
var value = aByte << 8
for _ in 0 ..< 8 {
if (value & 0x8000) != 0 {
value = (value << 1) ^ gPloy
}else {
value = value << 1
}
}
value = value & 0xFFFF
return value
}
private func getCrc(data: [UInt8]) -> UInt16 {
var crc = 0
let dataInt: [Int] = data.map{Int( $0) }
let length = data.count
for i in 0 ..< length {
crc = ((crc & 0xFF) << 8) ^ crcTable[(((crc & 0xFF00) >> 8) ^ dataInt[i]) & 0xFF]
}
crc = crc & 0xFFFF
return UInt16(crc)
}
}
//
// Utils.swift
// 蓝牙中心设备Swift
//
// Created by JamesWang on 2018/8/17.
//
import Foundation
import UIKit
public class Utils {
/// 将四个int8转为一个int32
///
/// - Parameters:
/// - i1: 高位
/// - i2: <#i2 description#>
/// - i3: <#i3 description#>
/// - i4: 低位
/// - Returns: <#return value description#>
public static func convertInt(i1:Int,i2:Int,i3:Int,i4:Int) ->Int32{
var val:Int
val = ((i1 << 24))
val = val | (i2 << 16)
val = val | (i3 << 8)
val = val | i4
return (Int32)(val)
}
/// 将四个int按照高低位转换为Float
///
/// - Parameters:
/// - i1: 高位
/// - i2:
/// - i3: <#i3 description#>
/// - i4: 低位
/// - Returns: <#return value description#>
public static func convertFloat(i1:UInt8,i2:UInt8,i3:UInt8,i4:UInt8) -> Float {
var float32value:Float32 = 0
let data:[UInt8] = [i4,i3,i2,i1]
memcpy(&float32value, data, 4)
// print("value = \(float32value)")
return float32value
}
/// 将两个Int8转为一个int
///
/// - Parameters:
/// - i1: 高位
/// - i2: 低位
/// - Returns:
public static func convertUShort(i1:UInt8,i2:UInt8) -> UInt16 {
return UInt16((i1 << 8) | (i2 & 0x00ff))
}
/// 将两个Int8转为一个int
///
/// - Parameters:
/// - i1: 高位
/// - i2: 低位
/// - Returns:
public static func convertShort(i1:UInt8,i2:UInt8) -> Int16 {
return Int16((i1 << 8) | (i2 & 0x00ff))
}
// MARK: - Constants
// This is used by the byteArrayToHexString() method
private static let CHexLookup : [Character] =
[ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ]
// Mark: - Public methods
///将一个数组转换为16进制的字符串 中间用空格分隔
public static func byteArrayToHexString(_ byteArray : [UInt8]) -> String {
var stringToReturn = ""
for oneByte in byteArray {
let asInt = Int(oneByte)
stringToReturn.append(Utils.CHexLookup[asInt >> 4])
stringToReturn.append(Utils.CHexLookup[asInt & 0x0f])
stringToReturn.append(" ")
}
return stringToReturn
}
}
extension Int16 {
func toBytes() -> Data {
var data = Data()
data.append((UInt8)(self >> 8 & 0x00ff))
data.append((UInt8)(self & 0x00ff))
return data
}
}
extension UInt16{
func toBytes() -> Data {
var data = Data()
data.append((UInt8)(self >> 8 & 0x00ff))
data.append((UInt8)(self & 0x00ff))
return data
}
}
extension UInt32 {
func toBytes() -> Data {
var data = Data()
data.append((UInt8)(self >> 24 & 0x00ff))
data.append((UInt8)(self >> 16 & 0x00ff))
data.append((UInt8)(self >> 8 & 0x00ff))
data.append((UInt8)(self & 0x00ff))
return data
}
}
extension Data {
init(from value: T) {
var value = value
self.init(buffer: UnsafeBufferPointer(start: &value, count: 1))
}
func to(type: T.Type) -> T {
return self.withUnsafeBytes { $0.pointee }
}
init(fromArray values: [T]) {
var values = values
self.init(buffer: UnsafeBufferPointer(start: &values, count: values.count))
}
func toArray(type: T.Type) -> [T] {
return self.withUnsafeBytes {
[T](UnsafeBufferPointer(start: $0, count: self.count/MemoryLayout.stride))
}
}
mutating func append(uint16value:UInt16) {
self.append(uint16value.toBytes())
}
mutating func append(int16value:Int16) {
self.append(int16value.toBytes())
}
mutating func append(uint32value:UInt32) {
self.append(uint32value.toBytes())
}
mutating func append(float32value:Float32) {
let data = Data(from:float32value)
let temp:[UInt8] = data.toArray(type: UInt8.self)
self.append(contentsOf:temp.reversed())
}
/// 从Data中读取一个UInt8数据
///
/// - Returns: success 是否读取成功 uint8vale 读取到的值
mutating func readUInt8() -> (success:Bool, uint8value:UInt8) {
var uint8value:UInt8 = 0
var success:Bool = false
if(self.count < 1) {
success = false
}
else {
uint8value = self.first!
success = true
self.removeFirst()
}
return (success,uint8value)
}
mutating func subdata(in range: CountableClosedRange) -> Data
{
return self.subdata(in: range.lowerBound.. (success:Bool, uint16value:UInt16) {
var uint16value:UInt16 = 0
var success:Bool = false
if(self.count >= 2) {
let bytes: [UInt8] = self.subdata(in: 0...1).toArray(type: UInt8.self)
self.removeSubrange(0...1)
let u0:UInt16 = UInt16(bytes[0])
let u1:UInt16 = UInt16(bytes[1])
uint16value = UInt16((u0 << 8) | (u1 & 0x00ff))
success = true
}
return (success,uint16value)
}
/// 从Data中读取一个UInt32数据
///
/// - Returns: success 是否读取成功 uint32value 读取到的值
mutating func readUInt32() -> (success:Bool, uint32value:UInt32) {
var uint32value:UInt32 = 0
var success:Bool = false
if(self.count >= 4) {
let bytes: [UInt8] = self.subdata(in: 0...3).toArray(type: UInt8.self)
self.removeSubrange(0...3)
let u0:UInt32 = UInt32(bytes[0])
let u1:UInt32 = UInt32(bytes[1])
let u2:UInt32 = UInt32(bytes[2])
let u3:UInt32 = UInt32(bytes[3])
print("\(u0)\(u1)\(u2)\(u3)")
uint32value = UInt32( (u0 << 24) | (u1 << 16) | (u2 << 8) | u3)
success = true
}
return (success,uint32value)
}
/// 从Data中读取一个Float32数据
///
/// - Returns: success 是否读取成功 float32value 读取到的值
mutating func readFloat32() -> (success:Bool, float32value:Float32) {
var float32value:Float32 = 0
let success:Bool = false
if(self.count >= 4) {
let bytes: [UInt8] = self.subdata(in: 0...3).toArray(type: UInt8.self)
self.removeSubrange(0...3)
memcpy(&float32value, bytes, 4)
}
return (success,float32value)
}
mutating func hexEncodedString(options: HexEncodingOptions = []) -> String {
let hexDigits = Array((options.contains(.upperCase) ? "0123456789ABCDEF" : "0123456789abcdef").utf16)
var chars: [unichar] = []
chars.reserveCapacity(2 * count)
for byte in self {
chars.append(hexDigits[Int(byte / 16)])
chars.append(hexDigits[Int(byte % 16)])
}
return String(utf16CodeUnits: chars, count: chars.count)
}
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
}
func showAlert() {
let alertController = UIAlertController(title: "系统提示",message: "请前往设置打开蓝牙", preferredStyle: UIAlertControllerStyle.alert)
let cancelAction = UIAlertAction(title: "取消", style: UIAlertActionStyle.cancel, handler: nil)
let okAction = UIAlertAction(title: "设置", style: UIAlertActionStyle.default,
handler: {
action in
let bluetoothurl = URL(string: UIApplicationOpenSettingsURLString)
if let url = bluetoothurl, UIApplication.shared.canOpenURL(url)
{
UIApplication.shared.openURL(url)
}
else {
print("打不开")
}
})
alertController.addAction(cancelAction)
alertController.addAction(okAction)
UIApplication.shared.windows[0].rootViewController?.present(alertController, animated: true, completion: nil)
}
备注:
因为之前没有接触过IOS开发,在进行蓝牙开发的时候,发现手机若在蓝牙设置中打开蓝牙,但是app启动后仍然会提示蓝牙状态处于未启动状态,如果是通过控制中心打开的蓝牙则不会有这个问题,网上找了很多资料,可能是ios的一个bug