Hello,
I am trying to send 20-byte packets from an Bluetooth LE module to an app. It's kinda working... but I noticed that sometimes my packets are split or incomplete (when I print the size of the buffer I sometime get 16 then 4 or 5 then 15 instead of 20). I tried to write a couple of functions that would handle that but something is wrong with my code:I get error messages on line 120 :
Expected ';' joining multiclause condition
Initializer for conditional binding must have optional type not 'Data'
Basically it does not like the syntax of my guard let statement but I don't know why (is it because data is the result of a function ?)
Q1: what's wrong with line 120 ?
Q2: is there a better way to handle split packets than my attempt in func rebuiltPackets ?
Q3: is there a better way to convert bytes from a buffer into separated values than my attempt in func parsePackets ?
Thanks !
// ViewController.swift
import UIKit
import CoreBluetooth
let rxCharacteristicUUID = CBUUID(string: "6E400003-B5A3-F393-E0A9-E50E24DCCA9E")
let UARTserviceUUID = CBUUID(string: "6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
var dataBuf: Data! = nil
class ViewController: UIViewController {
// Variables
var myCentralManager: CBCentralManager!
var mySelectedDevices = [Int]()
var myDevice: CBPeripheral!
var myPeripherals = Array()
// IBOutlets
@IBOutlet weak var myTableView: UITableView!
@IBAction func didPressConnect(_ sender: Any) {
if (mySelectedDevices.count)<2 {
for d in mySelectedDevices {
myDevice = myPeripherals[d]
myDevice.delegate = self
myCentralManager.stopScan()
myCentralManager.connect(myDevice)
}
}
}
// Functions
override func viewDidLoad() {
super.viewDidLoad()
myCentralManager = CBCentralManager(delegate: self, queue: nil)
myTableView.dataSource = self
myTableView.delegate = self
}
}
// Class extensions
//**********************************************************************************************
extension ViewController: CBCentralManagerDelegate{
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
print("powered on")
myCentralManager.scanForPeripherals(withServices: nil, options: nil) // withServices: nil or [UARTserviceUUID]
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
if peripheral.name != nil {
myPeripherals.append(peripheral)
myPeripherals = myPeripherals.removingDuplicates()
myTableView.reloadData()
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("connected")
print(peripheral.name!)
peripheral.discoverServices([UARTserviceUUID]) //nil or [UARTserviceUUID]
myCentralManager.stopScan()
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print(" not connecting")
print(peripheral.name!) }
}
// from https://www.hackingwithswift.com/example-code/language/how-to-remove-duplicate-items-from-an-array
extension Array where Element: Hashable {
func removingDuplicates() -> [Element] {
var addedDict = [Element: Bool]()
return filter {
addedDict.updateValue(true, forKey: $0) == nil
}
}
mutating func removeDuplicates() {
self = self.removingDuplicates()
}
}
//**********************************************************************************************
extension ViewController:CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
print(service)
peripheral.discoverCharacteristics([rxCharacteristicUUID], for: service) //nil or [rxCharacteristicUUID]
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
print("characteritics")
for characteristic in characteristics {
print(characteristic)
if characteristic.properties.contains(.notify) {
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
guard let read = characteristic.value else {return}
if read.count == 20 {
let data = read
parsePacket(data: data)
} else {
guard let data = rebuiltPacket(data: read) where (data.count == 20) else {return}
parsePacket(data: data)
}
}
func parsePacket(data:Data) {
let myFirstByte:UInt8 = UInt8(data[0])
print("\(myFirstByte)")
//let mySecondByte:UInt16 = UInt16(data![1]) * 256 + UInt16(data![2])
//let myThirdByte:UInt16 = UInt16(data![3]) * 256 + UInt16(data![4])
}
func rebuiltPacket(data:Data)-> Data {
var fullPacket: Data! = nil
if dataBuf == nil {
dataBuf=data
} else if (dataBuf.count + data.count) == 20 {
fullPacket = dataBuf + data
}
dataBuf = nil
return fullPacket
}
}
//**********************************************************************************************
extension ViewController:UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let list = tableView.indexPathsForSelectedRows {
for l in list {
mySelectedDevices.append(l[1])
mySelectedDevices = mySelectedDevices.removingDuplicates()
}
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
if !cell.isSelected {
cell.accessoryType = .none
mySelectedDevices.removeAll()
}
}
}
func updateCount(_ tableView:UITableView) -> Int {
var count = 0
if let list = tableView.indexPathsForSelectedRows {
count = list.count
}
return count
}
}
//**********************************************************************************************
extension ViewController:UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myPeripherals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = myTableView.dequeueReusableCell(withIdentifier: "myReusableIdentifier")!
let myPeripheral = myPeripherals[indexPath.row]
myCell.textLabel?.text = myPeripheral.name
return myCell
}
}
return myCell
}
}