From e23fe9a2cf3be561d0ec47091e4b40969e622134 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 23 Oct 2021 00:19:23 -0700 Subject: [PATCH] V 1.26.8 Additional cleanup of connection error handling --- Meshtastic Client.xcodeproj/project.pbxproj | 8 +- MeshtasticClient/Helpers/BLEManager.swift | 74 +++++++++-------- .../Views/Bluetooth/Connect.swift | 79 ++++++++++--------- 3 files changed, 85 insertions(+), 76 deletions(-) diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index e908738c..0f841316 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -668,11 +668,11 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.26.7; + MARKETING_VERSION = 1.26.8; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,6"; }; @@ -695,11 +695,11 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.26.7; + MARKETING_VERSION = 1.26.8; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,6"; }; diff --git a/MeshtasticClient/Helpers/BLEManager.swift b/MeshtasticClient/Helpers/BLEManager.swift index 62dceb25..443063bf 100644 --- a/MeshtasticClient/Helpers/BLEManager.swift +++ b/MeshtasticClient/Helpers/BLEManager.swift @@ -111,21 +111,22 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if runCount == 5 { - timeoutTimer?.invalidate() - runCount = 0 - if connectedPeripheral != nil { + if connectedPeripheral != nil && connectedPeripheral.peripheral.state != CBPeripheralState.connected { self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral) connectedNode = nil connectedPeripheral = nil + Logger.log("BLE Connecting Timeout Timer disconnected orphaned radio: \(name) in state: \(connectedPeripheral.peripheral.state.rawValue)") } - print("BLE Timeout Timer Fired \(runCount) Time(s) Connection Failed: \(name)") - Logger.log("BLE Timeout Timer Fired \(runCount) Time(s) Connection Failed: \(name)") + print("BLE Connecting 2 Second Timeout Timer Fired \(runCount) Time(s): \(name)") + Logger.log("BLE Connecting 2 Second Timeout Timer Fired \(runCount) Time(s): \(name)") + timeoutTimer?.invalidate() + runCount = 0 } else { - print("BLE Timeout Timer Fired \(runCount) Time(s): \(name)") - Logger.log("BLE Timeout Timer Fired \(runCount) Time(s): \(name)") + print("BLE Connecting 2 Second Timeout Timer Fired \(runCount) Time(s): \(name)") + Logger.log("BLE Connecting 2 Second Timeout Timer Fired \(runCount) Time(s): \(name)") } self.startScanning() } @@ -145,7 +146,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph self.centralManager?.connect(peripheral) let context = ["name": "@\(peripheral.name ?? "Unknown")"] - self.timeoutTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true) + self.timeoutTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true) } // Disconnect Device function @@ -185,6 +186,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph lastConnectionError = "" self.timeoutTimer!.invalidate() + self.runCount = 0 peripheral.delegate = self connectedPeripheral = peripherals.filter({ $0.peripheral.identifier == peripheral.identifier }).first @@ -197,59 +199,57 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph peripheral.discoverServices([meshtasticServiceCBUUID]) if meshLoggingEnabled { Logger.log("BLE Connected: \(peripheral.name ?? "Unknown")") } print("BLE Connected: \(peripheral.name ?? "Unknown")") - stopScanning() + peripherals.removeAll() } // Disconnect Peripheral Event func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { - // Start a Scan so the disconnected peripheral is moved to the peripherals[] if it is awake + // Start a scan so the disconnected peripheral is moved to the peripherals[] if it is awake self.startScanning() + self.connectedPeripheral = nil + self.connectedNode = nil if let e = error { let errorCode = (e as NSError).code - if errorCode == 6 { + if errorCode == 6 { // The connection has timed out unexpectedly. - // Error Code 6: The connection has timed out unexpectedly. // Happens when device is manually reset / powered off - lastConnectionError = "\(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within 5 seconds." - self.connectedNode = nil - self.connectedPeripheral = nil - if meshLoggingEnabled { Logger.log("BLE Reconnecting: \(peripheral.name ?? "Unknown")" ) } - print("Reconnecting to \(peripheral.name ?? "Unknown")") + // We will try and re-connect to this device + lastConnectionError = "\(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within 10 seconds." + if meshLoggingEnabled { Logger.log("BLE Reconnecting: \(peripheral.name ?? "Unknown")") } + print("BLE Reconnecting: \(peripheral.name ?? "Unknown")") self.connectTo(peripheral: peripheral) } else if errorCode == 7 { // The specified device has disconnected from us. // Seems to be what is received when a tbeam sleeps, immediately recconnecting does not work. - // Check if the last connected peripheral is still visible and then reconnect - connectedPeripheral = nil - connectedNode = nil lastConnectionError = e.localizedDescription - + print("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") + if meshLoggingEnabled { Logger.log("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") } } - else if errorCode == 14 { // Peer error that may require forgetting device in settings + else if errorCode == 14 { // Peer removed pairing information // Forgetting and reconnecting seems to be necessary so we need to show the user an error telling them to do that - connectedPeripheral = nil - connectedNode = nil - lastConnectionError = e.localizedDescription + lastConnectionError = "\(e.localizedDescription) This error usually cannot be fixed without forgetting the device unders Settings > Bluetooth and re-connecting to the radio." + if meshLoggingEnabled { Logger.log("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(lastConnectionError)") } } - print("Central disconnected because \(e)") - if meshLoggingEnabled { Logger.log("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") } + else { + + lastConnectionError = e.localizedDescription + print("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") + if meshLoggingEnabled { Logger.log("BLE Disconnected: \(peripheral.name ?? "Unknown") Error Code: \(errorCode) Error: \(e.localizedDescription)") } + } } else { // Disconnected without error which indicates user intent to disconnect - connectedPeripheral = nil - connectedNode = nil - if meshLoggingEnabled { Logger.log("BLE Disconnected: \(peripheral.name ?? "Unknown"): User Initiated Disconnect" ) } - print("Central disconnected! (no error)") + // Happens when swiping to disconnect + if meshLoggingEnabled { Logger.log("BLE Disconnected: \(peripheral.name ?? "Unknown"): User Initiated Disconnect") } + print("BLE Disconnected: \(peripheral.name ?? "Unknown"): User Initiated Disconnect") } - - print("Peripheral disconnected: " + peripheral.name!) } // Discover Services Event @@ -375,8 +375,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph } // Since the data is from the device itself we save all myInfo objects since they are always the most up to date if connectedNode != nil { + connectedNode.myInfo = myInfoModel - connectedNode.update(from: connectedNode.data) + let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == decodedInfo.myInfo.myNodeNum }) meshData.nodes.remove(at: nodeIndex!) meshData.nodes.append(connectedNode) @@ -443,7 +444,12 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph snr: decodedInfo.nodeInfo.snr) ) meshData.save() + meshData.load() if meshLoggingEnabled { Logger.log("BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.user.longName)") } + if connectedNode == nil { + + connectedNode = meshData.nodes.first(where: {$0.num == connectedPeripheral.myInfo!.myNodeNum}) + } } } // Handle assorted app packets diff --git a/MeshtasticClient/Views/Bluetooth/Connect.swift b/MeshtasticClient/Views/Bluetooth/Connect.swift index bb0960a4..cd792330 100644 --- a/MeshtasticClient/Views/Bluetooth/Connect.swift +++ b/MeshtasticClient/Views/Bluetooth/Connect.swift @@ -17,7 +17,6 @@ struct Connect: View { @EnvironmentObject var meshData: MeshData @EnvironmentObject var bleManager: BLEManager - @EnvironmentObject var userSettings: UserSettings @State var isPreferredRadio: Bool = false @@ -34,12 +33,12 @@ struct Connect: View { Section(header: Text("Connection Error").font(.title)) { - Text(bleManager.lastConnectionError).font(.headline).foregroundColor(.red) + Text(bleManager.lastConnectionError).font(.title3).foregroundColor(.red) } .textCase(nil) } - Section(header: Text("Connected Device").font(.title)) { + Section(header: Text("Connected Device").font(.title)) { if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected { HStack { @@ -50,6 +49,7 @@ struct Connect: View { .padding(.trailing) VStack (alignment: .leading) { + if bleManager.connectedNode != nil { Text(bleManager.connectedNode.user.longName).font(.title2) @@ -67,6 +67,7 @@ struct Connect: View { Spacer() VStack (alignment: .center) { + Text("Preferred").font(.caption2) Text("Radio").font(.caption2) Toggle("Preferred Radio", isOn: $isPreferredRadio) @@ -95,7 +96,6 @@ struct Connect: View { } } } - .padding([.top, .bottom]) .swipeActions { Button(role: .destructive) { @@ -109,6 +109,7 @@ struct Connect: View { Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash") } } + .padding([.top, .bottom]) } else { HStack{ @@ -123,37 +124,38 @@ struct Connect: View { } .textCase(nil) - - Section(header: Text("Available Devices").font(.title)) { - ForEach(bleManager.peripherals.filter({ $0.peripheral.state == CBPeripheralState.disconnected }).sorted(by: { $0.rssi > $1.rssi })) { peripheral in - HStack { - Image(systemName: "circle.fill") - .imageScale(.large).foregroundColor(.gray) - .padding(.trailing) - Button(action: { - self.bleManager.stopScanning() - if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected - { - self.bleManager.disconnectDevice() - } - self.bleManager.connectTo(peripheral: peripheral.peripheral) - if userSettings.preferredPeripheralId == peripheral.peripheral.identifier.uuidString { - - isPreferredRadio = true + + if bleManager.peripherals.count > 0 { + Section(header: Text("Available Devices").font(.title)) { + ForEach(bleManager.peripherals.filter({ $0.peripheral.state == CBPeripheralState.disconnected }).sorted(by: { $0.rssi > $1.rssi })) { peripheral in + HStack { + Image(systemName: "circle.fill") + .imageScale(.large).foregroundColor(.gray) + .padding(.trailing) + Button(action: { + self.bleManager.stopScanning() + if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == CBPeripheralState.connected + { + self.bleManager.disconnectDevice() + } + self.bleManager.connectTo(peripheral: peripheral.peripheral) + if userSettings.preferredPeripheralId == peripheral.peripheral.identifier.uuidString { + + isPreferredRadio = true + } + else { + + isPreferredRadio = false + } + }) { + Text(peripheral.name).font(.title3) } - else { - - isPreferredRadio = false - } - }) { - Text(peripheral.name).font(.title3) - } - Spacer() - Text(String(peripheral.rssi) + " dB").font(.title3) - }.padding([.bottom,.top]) - } - }.textCase(nil) - + Spacer() + Text(String(peripheral.rssi) + " dB").font(.title3) + }.padding([.bottom,.top]) + } + }.textCase(nil) + } } HStack (alignment: .center) { @@ -202,17 +204,18 @@ struct Connect: View { } .navigationViewStyle(StackNavigationViewStyle()) .onAppear(perform: { + + if bleManager.connectedPeripheral == nil { + bleManager.startScanning() + } if bleManager.connectedPeripheral != nil && userSettings.preferredPeripheralId == bleManager.connectedPeripheral.peripheral.identifier.uuidString { isPreferredRadio = true } else { isPreferredRadio = false - if bleManager.connectedPeripheral == nil { - bleManager.startScanning() - } } - } ) + }) } }