IOS--swift BLE藍芽通訊管理(多裝置)
阿新 • • 發佈:2018-11-14
之前一直做的是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..<arrayCount) {
var buf:[UInt8] = [] //建立一個臨時陣列集合
while(buf.count < BLEModel.LIMIT_SIZE && byteArray.count > 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)")