mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Complete BLE Scanning and connection
This commit is contained in:
parent
b51e4c974f
commit
693aec1fd3
3 changed files with 219 additions and 63 deletions
|
|
@ -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)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue