Complete BLE Scanning and connection

This commit is contained in:
Garth Vander Houwen 2021-09-10 21:50:54 -07:00
parent b51e4c974f
commit 693aec1fd3
3 changed files with 219 additions and 63 deletions

View file

@ -5,7 +5,7 @@ Abstract: Default App View
import SwiftUI
struct ContentView: View {
@State private var selection: Tab = .devices
@State private var selection: Tab = .ble
enum Tab {
case messages
@ -18,28 +18,26 @@ struct ContentView: View {
var body: some View {
TabView(selection: $selection) {
DeviceHome()
.tabItem {
Label("Devices", systemImage: "flipphone")
}
.tag(Tab.devices)
DeviceMap()
.tabItem {
Label("Mesh Map", systemImage: "map")
}
.tag(Tab.map)
Messages()
.tabItem {
Label("Messages", systemImage: "message")
}
.tag(Tab.messages)
DeviceMap()
.tabItem {
Label("Mesh Map", systemImage: "map")
}
.tag(Tab.map)
DeviceHome()
.tabItem {
Label("Devices", systemImage: "flipphone")
}
.tag(Tab.devices)
DeviceBLE()
.tabItem {
Label("Bluetooth", systemImage: "dot.radiowaves.left.and.right")
}
.tag(Tab.ble)
}
}
}

View file

@ -24,17 +24,40 @@ struct DeviceBLE: View {
NavigationView {
VStack {
List(bleManager.peripherals) { peripheral in
HStack {
Text(peripheral.name)
Spacer()
Text(String(peripheral.rssi) + " dB")
}
}.frame(height: 300)
List {
Section(header: Text("Connected Device")) {
if(bleManager.connectedPeripheral != nil){
HStack{
Image(systemName: "dot.radiowaves.left.and.right").imageScale(.medium).foregroundColor(.green)
Text(bleManager.connectedPeripheral.name!)
Spacer()
// print(bleManager.meshtasticPeripheral)
}
}
}.textCase(nil)
Section(header: Text("Other Meshtastic Devices")) {
ForEach(bleManager.peripherals.sorted(by: { $0.rssi > $1.rssi })) { peripheral in
HStack {
Image(systemName: "circle.fill").imageScale(.medium).foregroundColor(.gray)
Button(action: {
self.bleManager.stopScanning()
self.bleManager.disconnectDevice()
self.bleManager.connectToDevice(id: peripheral.id)
}) {
Text(peripheral.name)
}
Spacer()
Text(String(peripheral.rssi) + " dB")
}
}
}.textCase(nil)
}
// Image(systemName: "dot.radiowaves.left.and.right").imageScale(.medium).foregroundColor(.green)//.rotationEffect(Angle(degrees: 90))
Spacer()
HStack {
VStack (spacing: 10) {
Button(action: {
@ -60,7 +83,7 @@ struct DeviceBLE: View {
Button(action: {
self.bleManager.startScanning()
}) {
Image(systemName: "arrow.clockwise.circle.fill").imageScale(.large)
Image(systemName: "arrow.clockwise.circle").imageScale(.large)
}}, trailing:
HStack {
if bleManager.isSwitchedOn {

View file

@ -2,19 +2,32 @@ import Foundation
import CoreBluetooth
struct Peripheral: Identifiable {
let id: Int
let id: String
let index: Int
let name: String
let rssi: Int
}
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate {
//---------------------------------------------------------------------------------------
// Meshtastic BLE Device Manager
//---------------------------------------------------------------------------------------
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
// Data
private var centralManager: CBCentralManager!
@Published var connectedPeripheral: CBPeripheral!
@Published var peripheralArray = [CBPeripheral]()
private var rssiArray = [NSNumber]()
private var timer = Timer()
var myCentral: CBCentralManager!
private var meshtasticPeripheral: CBPeripheral!
@Published var isSwitchedOn = false
@Published var peripherals = [Peripheral]()
let meshtasticServiceID = CBUUID(string: "0x6BA1B218-15A8-461F-9FA8-5DCAE273EAFD")
var TORADIO_characteristic: CBCharacteristic!
var FROMRADIO_characteristic: CBCharacteristic!
var FROMNUM_characteristic: CBCharacteristic!
let meshtasticServiceCBUUID = CBUUID(string: "0x6BA1B218-15A8-461F-9FA8-5DCAE273EAFD")
let TORADIO_UUID = CBUUID(string: "0xF75C76D2-129E-4DAD-A1DD-7866124401E7")
let FROMRADIO_UUID = CBUUID(string: "0x8BA2BCC2-EE02-4A55-A531-C525C5E454D5")
let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453") //Notify
@ -22,12 +35,14 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate {
override init() {
super.init()
myCentral = CBCentralManager(delegate: self, queue: nil)
myCentral.delegate = self
centralManager = CBCentralManager(delegate: self, queue: nil)
centralManager.delegate = self
}
//---------------------------------------------------------------------------------------
// Check for Bluetooth Connectivity
//---------------------------------------------------------------------------------------
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
isSwitchedOn = true
@ -36,49 +51,169 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate {
isSwitchedOn = false
}
}
//---------------------------------------------------------------------------------------
// Scan for nearby BLE devices using the Meshtastic BLE service ID
//---------------------------------------------------------------------------------------
func startScanning() {
// Remove Existing Data
peripherals.removeAll()
peripheralArray.removeAll()
rssiArray.removeAll()
// Start Scanning
print("Start Scanning")
centralManager.scanForPeripherals(withServices: [meshtasticServiceCBUUID])
// scanningLabel.text = "Scanning..." }
//Timer.scheduledTimer(withTimeInterval: 15, repeats: false) {_ in
// self.stopScanning()
//}
}
//---------------------------------------------------------------------------------------
// Stop Scanning For BLE Devices
//---------------------------------------------------------------------------------------
func stopScanning() {
print("Stop Scanning")
centralManager.stopScan()
}
//---------------------------------------------------------------------------------------
// Connect to a Device via UUID
//---------------------------------------------------------------------------------------
func connectToDevice(id: String) {
connectedPeripheral = peripheralArray.filter({ $0.identifier.uuidString == id }).first
centralManager?.connect(connectedPeripheral!)
}
//---------------------------------------------------------------------------------------
// Disconnect Device function
//---------------------------------------------------------------------------------------
func disconnectDevice(){
if connectedPeripheral != nil {
centralManager?.cancelPeripheralConnection(connectedPeripheral!)
}
}
//---------------------------------------------------------------------------------------
// Discover Peripheral Event
//---------------------------------------------------------------------------------------
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(peripheral)
//print(peripheral)
if peripheralArray.contains(peripheral) {
print("Duplicate Found.")
} else {
print("Adding peripheral: " + peripheral.name!);
peripheralArray.append(peripheral)
rssiArray.append(RSSI)
}
var peripheralName: String!
if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
peripheralName = name
peripheralName = peripheral.name
if peripheral.name == nil {
if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
peripheralName = name
}
else {
peripheralName = "Unknown"
}
}
else {
peripheralName = "Unknown"
}
let newPeripheral = Peripheral(id: peripherals.count, name: peripheralName, rssi: RSSI.intValue)
print(newPeripheral)
let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, index: peripherals.count, name: peripheralName, rssi: RSSI.intValue)
//print(newPeripheral)
peripherals.append(newPeripheral)
}
func startScanning() {
print("startScanning")
peripherals = [];
myCentral.scanForPeripherals(withServices: [meshtasticServiceID])
}
func stopScanning() {
print("stopScanning")
myCentral.stopScan()
//---------------------------------------------------------------------------------------
// Connect Peripheral Event
//---------------------------------------------------------------------------------------
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Peripheral connected: " + peripheral.name!)
peripheral.delegate = self
peripheral.discoverServices(nil)
self.startScanning()
}
func connectToDevice(uuid:String) {
//---------------------------------------------------------------------------------------
// Disconnect Peripheral Event
//---------------------------------------------------------------------------------------
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?)
{
if(peripheral.identifier == connectedPeripheral.identifier){
connectedPeripheral = nil
}
print("Peripheral disconnected: " + peripheral.name!)
self.startScanning()
}
//---------------------------------------------------------------------------------------
// Discover Services Event
//---------------------------------------------------------------------------------------
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
// let meshtasticPeripheral = self.peripherals.first(where: { $0.id == uuid })
if (meshtasticPeripheral == nil) {
return
guard let services = peripheral.services else { return }
for service in services
{
print("Service discovered: " + service.uuid.uuidString)
if (service.uuid == meshtasticServiceCBUUID)
{
print ("Meshtastic service OK")
peripheral.discoverCharacteristics(nil, for: service)
}
// Attempt to connect to this device
myCentral.connect(meshtasticPeripheral, options: nil)
// Retain the peripheral
}
}
//---------------------------------------------------------------------------------------
// Discover Characteristics Event
//---------------------------------------------------------------------------------------
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
switch characteristic.uuid
{
case TORADIO_UUID:
print("TORADIO characteristic OK")
TORADIO_characteristic = characteristic
// var toRadio: ToRadio = ToRadio()
// toRadio.wantConfigID = 32168
// let binaryData: Data = try! toRadio.serializedData()
// peripheral.writeValue(binaryData, for: characteristic, type: .withResponse)
break
case FROMRADIO_UUID:
print("FROMRADIO characteristic OK")
FROMRADIO_characteristic = characteristic
peripheral.readValue(for: FROMRADIO_characteristic)
break
case FROMNUM_UUID:
print("FROMNUM (Notify) characteristic OK")
FROMNUM_characteristic = characteristic
peripheral.setNotifyValue(true, for: characteristic)
break
default:
break
}
}
}
//---------------------------------------------------------------------------------------
// Update Characteristic Event
//---------------------------------------------------------------------------------------
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,
error: Error?) {
switch characteristic.uuid {
case FROMRADIO_UUID:
print(characteristic.value ?? "no value")
default:
print("Unhandled Characteristic UUID: \(characteristic.uuid)")
}
}
}