From 5790839022f601f211bc020011525f3d56cc1790 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 12 Oct 2022 15:26:25 -0700 Subject: [PATCH 1/3] Start of connection timer UI --- Meshtastic/Helpers/BLEManager.swift | 32 ++++++----- Meshtastic/MeshtasticApp.swift | 13 ++--- Meshtastic/Views/Bluetooth/Connect.swift | 54 +++++++++++++------ Meshtastic/Views/Messages/Contacts.swift | 11 ++-- Meshtastic/Views/Settings/ShareChannels.swift | 30 ++++++----- 5 files changed, 85 insertions(+), 55 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 76b43972..23d65fb5 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -36,6 +36,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph @Published var isSwitchedOn: Bool = false @Published var isScanning: Bool = false + @Published var isConnecting: Bool = false @Published var isConnected: Bool = false /// Used to make sure we never get foold by old BLE packets @@ -44,7 +45,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph var timeoutTimer: Timer? var timeoutTimerCount = 0 var positionTimer: Timer? - let broadcastNodeNum: UInt32 = 4294967295 /* Meshtastic Service Details */ @@ -133,6 +133,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph let name: String = timerContext["name", default: "Unknown"] self.timeoutTimerCount += 1 + self.isConnecting = true + self.lastConnectionError = "" if timeoutTimerCount == 10 { @@ -141,17 +143,20 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral) } connectedPeripheral = nil - self.isConnected = false - - self.lastConnectionError = "🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(name)." - - if meshLoggingEnabled { MeshLogger.log(self.lastConnectionError + " This can occur when a device has been taken out of BLE range, or if a device is already connected to another phone, tablet or computer.") } - - self.timeoutTimerCount = 0 if self.timeoutTimer != nil { self.timeoutTimer!.invalidate() } + self.isConnected = false + self.isConnecting = false + + self.lastConnectionError = "🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(name). If you continue to get this message you may need to forget your device under settings > bluetooth." + + if meshLoggingEnabled { MeshLogger.log("🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(name). If you continue to get this message you may need to forget your device under settings > bluetooth.") } + + self.timeoutTimerCount = 0 + self.startScanning() + } else { @@ -165,6 +170,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if meshLoggingEnabled { MeshLogger.log("✅ BLE Connecting: \(peripheral.name ?? "Unknown")") } stopScanning() + self.isConnecting = true + self.lastConnectionError = "" if self.connectedPeripheral != nil { @@ -184,7 +191,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent // the timer from running on the main UI thread let context = ["name": "@\(peripheral.name ?? "Unknown")"] - self.timeoutTimer = Timer.scheduledTimer(timeInterval: 5.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) RunLoop.current.add(self.timeoutTimer!, forMode: .common) } @@ -235,6 +242,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Called when a peripheral is connected func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { + self.isConnecting = false self.isConnected = true if userSettings?.preferredPeripheralId.count ?? 0 < 1 { @@ -287,6 +295,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Start a scan so the disconnected peripheral is moved to the peripherals[] if it is awake self.startScanning() self.connectedPeripheral = nil + self.isConnecting = false if let e = error { @@ -378,9 +387,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph for characteristic in characteristics { switch characteristic.uuid { - case EOL_FROMRADIO_UUID: - if meshLoggingEnabled { MeshLogger.log("🚨 BLE did discover EOL_TORADIO characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") } - invalidVersion = true case TORADIO_UUID: @@ -474,6 +480,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph guard (connectedPeripheral!.peripheral.state == CBPeripheralState.connected) else { return } if FROMRADIO_characteristic == nil { + + if meshLoggingEnabled { MeshLogger.log("🚨 Unsupported Firmware Version Detected, unable to connect to device.") } invalidVersion = true return diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index 7f7f787d..96d1f899 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -23,6 +23,12 @@ struct MeshtasticAppleApp: App { .environment(\.managedObjectContext, persistenceController.container.viewContext) .environmentObject(bleManager) .environmentObject(userSettings) + .sheet(isPresented: $saveChannels) { + + SaveChannelQRCode(channelHash: channelSettings ?? "Empty Channel URL", validUrl: true) + .presentationDetents([.medium, .large]) + .presentationDragIndicator(.visible) + } .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in @@ -41,12 +47,7 @@ struct MeshtasticAppleApp: App { print("User wants to open Channel Settings URL: \(String(describing: incomingUrl!.relativeString))") } } - .sheet(isPresented: $saveChannels) { - - SaveChannelQRCode(channelHash: channelSettings ?? "Empty Channel URL", validUrl: true) - .presentationDetents([.medium, .large]) - .presentationDragIndicator(.visible) - } + .onOpenURL(perform: { (url) in print("Some sort of URL was received \(url)") diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 673415e0..575fad17 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -35,13 +35,13 @@ struct Connect: View { Section(header: Text("Connection Error").font(.title)) { - Text(bleManager.lastConnectionError).font(.title3).foregroundColor(.red) + Text(bleManager.lastConnectionError).font(.callout).foregroundColor(.red) } .textCase(nil) } - + Section(header: Text("Connected Radio").font(.title)) { - + if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected { HStack { @@ -65,10 +65,10 @@ struct Connect: View { .font(.caption).foregroundColor(Color.gray) } if bleManager.connectedPeripheral.subscribed { - Text("Properly Subscribed").font(.caption) + Text("Subscribed to mesh").font(.caption) .foregroundColor(.green) } else { - Text("Attempting to connect. . . ").font(.caption) + Text("Communicating with device. . . ").font(.caption) .foregroundColor(.orange) } @@ -135,20 +135,44 @@ struct Connect: View { } } else { - HStack { - Image(systemName: "antenna.radiowaves.left.and.right.slash") - .symbolRenderingMode(.hierarchical) - .imageScale(.large).foregroundColor(.red) - .padding(.trailing) - Text("No device connected").font(.title3) - } - .padding() - } + + if bleManager.isConnecting { + HStack { + Image(systemName: "antenna.radiowaves.left.and.right") + .symbolRenderingMode(.hierarchical) + .imageScale(.large).foregroundColor(.orange) + .padding(.trailing) + if bleManager.timeoutTimerCount == 0 { + Text("Connecting . . .") + .font(.title3) + .foregroundColor(.orange) + } else { + VStack { + Text("Connection Attempt \(bleManager.timeoutTimerCount) of 10") + .font(.callout) + .foregroundColor(.orange) + } + } + } + .padding() + + } else { + + HStack { + Image(systemName: "antenna.radiowaves.left.and.right.slash") + .symbolRenderingMode(.hierarchical) + .imageScale(.large).foregroundColor(.red) + .padding(.trailing) + Text("No device connected").font(.title3) + } + .padding() + } + } } .textCase(nil) - if bleManager.peripherals.count > 0 { + if self.bleManager.isScanning { Section(header: Text("Available Radios").font(.title)) { ForEach(bleManager.peripherals.filter({ $0.peripheral.state == CBPeripheralState.disconnected }).sorted(by: { $0.rssi > $1.rssi })) { peripheral in HStack { diff --git a/Meshtastic/Views/Messages/Contacts.swift b/Meshtastic/Views/Messages/Contacts.swift index 325ec948..c65a5254 100644 --- a/Meshtastic/Views/Messages/Contacts.swift +++ b/Meshtastic/Views/Messages/Contacts.swift @@ -28,8 +28,8 @@ struct Contacts: View { // Display Contacts for DM's on the Primary Channel // Display Contacts for the rest of the non admin channels - - List(users) { (user: UserEntity) in + List { + ForEach(users) { (user: UserEntity) in let connectedNodeNum = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0 @@ -131,6 +131,7 @@ struct Contacts: View { } } } + } .navigationTitle("Contacts") .navigationBarTitleDisplayMode(.inline) .navigationBarItems(leading: @@ -140,9 +141,3 @@ struct Contacts: View { .listStyle(PlainListStyle()) } } - -struct Contacts_Previews: PreviewProvider { - static var previews: some View { - Contacts() - } -} diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index cbc3a052..52c16ccc 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -77,7 +77,7 @@ struct ShareChannels: View { .font(.caption) .fontWeight(.bold) .padding(.trailing) - Text("Channel Name") + Text("Channel") .font(.caption) .fontWeight(.bold) .padding(.trailing) @@ -96,57 +96,59 @@ struct ShareChannels: View { .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 1) - Text((channel.name!.isEmpty ? "primary" : channel.name) ?? "primary") + Text((channel.name!.isEmpty ? "Primary" : channel.name) ?? "Primary") } else if channel.index == 1 { Toggle("Channel 1 Included", isOn: $includeChannel1) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 2 { Toggle("Channel 2 Included", isOn: $includeChannel2) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 3 { Toggle("Channel 3 Included", isOn: $includeChannel3) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 4 { Toggle("Channel 4 Included", isOn: $includeChannel4) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 5 { Toggle("Channel 5 Included", isOn: $includeChannel5) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 6 { Toggle("Channel 6 Included", isOn: $includeChannel6) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 7 { Toggle("Channel 7 Included", isOn: $includeChannel7) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } - if channel.role > 0 { - Image(systemName: "lock.fill") - .foregroundColor(.green) - } else { + + if channel.role == 0 { Image(systemName: "lock.slash") .foregroundColor(.gray) } + else if channel.role > 0 { + Image(systemName: "lock.fill") + .foregroundColor(.green) + } Spacer() } } @@ -285,7 +287,7 @@ struct ShareChannels: View { var channelSettings = ChannelSettings() channelSettings.name = ch.name! - channelSettings.psk = ch.psk ?? Data() + channelSettings.psk = ch.psk! channelSettings.id = UInt32(ch.id) channelSettings.uplinkEnabled = ch.uplinkEnabled channelSettings.downlinkEnabled = ch.downlinkEnabled From 8ffe59a8cc4cb948d141ae0b587187f03c7f6c4c Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 12 Oct 2022 15:46:53 -0700 Subject: [PATCH 2/3] Error text cleanup --- Meshtastic/Helpers/BLEManager.swift | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 23d65fb5..88157391 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -150,14 +150,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph self.isConnected = false self.isConnecting = false - self.lastConnectionError = "🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(name). If you continue to get this message you may need to forget your device under settings > bluetooth." + self.lastConnectionError = "🚨 Connection failed after \(timeoutTimerCount) attempts to connect to \(name). You may need to forget your device under Settings > Bluetooth." - if meshLoggingEnabled { MeshLogger.log("🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(name). If you continue to get this message you may need to forget your device under settings > bluetooth.") } + if meshLoggingEnabled { MeshLogger.log("🚨 BLE Connection failed after making \(timeoutTimerCount) attempts to connect to \(name). You may need to forget your device under Settings > Bluetooth.") } self.timeoutTimerCount = 0 self.startScanning() - } else { if meshLoggingEnabled { MeshLogger.log("🚨 BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)") } @@ -190,7 +189,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent // the timer from running on the main UI thread - let context = ["name": "@\(peripheral.name ?? "Unknown")"] + let context = ["name": "\(peripheral.name ?? "Unknown")"] self.timeoutTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true) RunLoop.current.add(self.timeoutTimer!, forMode: .common) } From 84210ea12319ec1e7fae18b827df15c157905ac2 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 12 Oct 2022 16:09:55 -0700 Subject: [PATCH 3/3] Update time and error messages --- Meshtastic/Helpers/BLEManager.swift | 32 +++++++++++------------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 88157391..1fa33d52 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -165,33 +165,24 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Connect to a specific peripheral func connectTo(peripheral: CBPeripheral) { - - if meshLoggingEnabled { MeshLogger.log("✅ BLE Connecting: \(peripheral.name ?? "Unknown")") } - stopScanning() - self.isConnecting = true - self.lastConnectionError = "" - - - if self.connectedPeripheral != nil { - - if meshLoggingEnabled { MeshLogger.log("â„šī¸ BLE Disconnecting from: \(self.connectedPeripheral.name) to connect to \(peripheral.name ?? "Unknown")") } - self.disconnectPeripheral() + isConnecting = true + lastConnectionError = "" + if connectedPeripheral != nil { + MeshLogger.log("â„šī¸ BLE Disconnecting from: \(connectedPeripheral.name) to connect to \(peripheral.name ?? "Unknown")") + disconnectPeripheral() } - - self.centralManager?.connect(peripheral) - + centralManager?.connect(peripheral) // Invalidate any existing timer - if self.timeoutTimer != nil { - - self.timeoutTimer!.invalidate() + if timeoutTimer != nil { + timeoutTimer!.invalidate() } - // Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent // the timer from running on the main UI thread let context = ["name": "\(peripheral.name ?? "Unknown")"] - self.timeoutTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true) - RunLoop.current.add(self.timeoutTimer!, forMode: .common) + timeoutTimer = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true) + RunLoop.current.add(timeoutTimer!, forMode: .common) + MeshLogger.log("â„šī¸ BLE Connecting: \(peripheral.name ?? "Unknown")") } // Disconnect Connected Peripheral @@ -203,6 +194,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph isConnected = false invalidVersion = false connectedVersion = "0.0.0" + startScanning() } // Called each time a peripheral is discovered