diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index aef514d9..6717fae4 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5226EB1DF10058C060 /* BLEManager.swift */; }; + DDAF8C5526EBA0530058C060 /* Bluetooth.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5426EBA0530058C060 /* Bluetooth.swift */; }; DDC2E15826CE248E0042C5E4 /* MeshtasticClientApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticClientApp.swift */; }; DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; }; DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; }; @@ -24,6 +26,7 @@ DDC2E1A226CE29AC0042C5E4 /* deviceData.json in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E18A26CE25690042C5E4 /* deviceData.json */; }; DDC2E1A426CE2F940042C5E4 /* DeviceBLE.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E1A326CE2F940042C5E4 /* DeviceBLE.swift */; }; DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */; }; + DDC2E1AB26DD89EC0042C5E4 /* packets.json in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E1AA26DD89EC0042C5E4 /* packets.json */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -44,6 +47,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + DDAF8C5226EB1DF10058C060 /* BLEManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEManager.swift; sourceTree = ""; }; + DDAF8C5426EBA0530058C060 /* Bluetooth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bluetooth.swift; sourceTree = ""; }; DDC2E15426CE248E0042C5E4 /* MeshtasticClient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MeshtasticClient.app; sourceTree = BUILT_PRODUCTS_DIR; }; DDC2E15726CE248E0042C5E4 /* MeshtasticClientApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticClientApp.swift; sourceTree = ""; }; DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -67,6 +72,7 @@ DDC2E19E26CE27630042C5E4 /* Device.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Device.swift; sourceTree = ""; }; DDC2E1A326CE2F940042C5E4 /* DeviceBLE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceBLE.swift; sourceTree = ""; }; DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationHelper.swift; sourceTree = ""; }; + DDC2E1AA26DD89EC0042C5E4 /* packets.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = packets.json; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -179,6 +185,7 @@ isa = PBXGroup; children = ( DDC2E18A26CE25690042C5E4 /* deviceData.json */, + DDC2E1AA26DD89EC0042C5E4 /* packets.json */, ); path = Resources; sourceTree = ""; @@ -199,6 +206,7 @@ DDC2E19626CE26840042C5E4 /* DeviceRow.swift */, DDC2E19826CE26940042C5E4 /* DeviceMap.swift */, DDC2E1A326CE2F940042C5E4 /* DeviceBLE.swift */, + DDAF8C5426EBA0530058C060 /* Bluetooth.swift */, ); path = Devices; sourceTree = ""; @@ -207,6 +215,7 @@ isa = PBXGroup; children = ( DDC2E19A26CE27150042C5E4 /* CircleImage.swift */, + DDAF8C5226EB1DF10058C060 /* BLEManager.swift */, ); path = Helpers; sourceTree = ""; @@ -325,6 +334,7 @@ DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */, DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */, DDC2E1A226CE29AC0042C5E4 /* deviceData.json in Resources */, + DDC2E1AB26DD89EC0042C5E4 /* packets.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -354,6 +364,8 @@ DDC2E19926CE26940042C5E4 /* DeviceMap.swift in Sources */, DDC2E19526CE26760042C5E4 /* DeviceDetail.swift in Sources */, DDC2E19726CE26840042C5E4 /* DeviceRow.swift in Sources */, + DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */, + DDAF8C5526EBA0530058C060 /* Bluetooth.swift in Sources */, DDC2E19126CE26290042C5E4 /* Messages.swift in Sources */, DDC2E19D26CE27580042C5E4 /* ModelData.swift in Sources */, DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */, diff --git a/MeshtasticClient/Info.plist b/MeshtasticClient/Info.plist index 7fdbeb36..97e59b03 100644 --- a/MeshtasticClient/Info.plist +++ b/MeshtasticClient/Info.plist @@ -37,12 +37,16 @@ armv7 + NSBluetoothAlwaysUsageDescription + We use bluetooth to connect to nearby Meshtastic Devices UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + Privacy – Bluetooth Always Usage Description + We use bluetooth to connect to nearby Meshtastic Devices UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait diff --git a/MeshtasticClient/Resources/packets.json b/MeshtasticClient/Resources/packets.json new file mode 100644 index 00000000..905da8a7 --- /dev/null +++ b/MeshtasticClient/Resources/packets.json @@ -0,0 +1,149 @@ +[ + { + "from":1133445808, + "to":1133445808, + "decoded":{ + "portnum":"ADMIN_APP", + "payload":"b"":\\x04\\x08\\x01\\x12\\x00", + "requestId":1506683854, + "admin":{ + "getChannelResponse":{ + "index":1, + "settings":{ + + } + }, + "raw":"get_channel_response"{ + "index":1 settings{ + + } + } + } + }, + "id":1481765942, + "rxTime":1630360112, + "hopLimit":3, + "priority":"RELIABLE", + "raw":"from":"1133445808 to":1133445808 decoded{ + "portnum":"ADMIN_APP payload":":\\004\\010\\001\\022\\000""request_id":1506683854 + }"id":"1481765942 rx_time":"1630360112 hop_limit":"3 priority":"RELIABLE", + "fromId":"!438f02b0", + "toId":"!438f02b0" + }{ + "num":1133445808, + "user":{ + "id":"!438f02b0", + "longName":"RAK DEV NODE", + "shortName":"RDN", + "macaddr":"92VDjwKw", + "hwModel":"RAK4631", + "raw":"id":"!438f02b0""long_name":"RAK DEV NODE""short_name":"RDN""macaddr":"\\367eC\\217\\002\\260""hw_model":RAK4631 + }, + "position":{ + "latitudeI":476021540, + "longitudeI":-1221532059, + "altitude":111, + "time":1630360110, + "latitude":47.602154, + "longitude":-122.15320589999999 + }, + "lastHeard":1630360100, + "lastReceived":{ + "from":1133445808, + "to":4294967295, + "decoded":{ + "portnum":"NODEINFO_APP", + "payload":"b""\n\t!438f02b0\\x12\\x0cRAK DEV NODE\\x1a\\x03RDN\"\\x06\\xf7eC\\x8f\\x02\\xb00\t", + "wantResponse":true, + "user":{ + "id":"!438f02b0", + "longName":"RAK DEV NODE", + "shortName":"RDN", + "macaddr":"92VDjwKw", + "hwModel":"RAK4631", + "raw":"id":"!438f02b0""long_name":"RAK DEV NODE""short_name":"RDN""macaddr":"\\367eC\\217\\002\\260""hw_model":RAK4631 + } + }, + "id":1481765939, + "rxTime":1630360100, + "hopLimit":3, + "priority":"BACKGROUND", + "raw":"from":"1133445808 to":4294967295 decoded{ + "portnum":"NODEINFO_APP payload":"\n\t!438f02b0\\022\\014RAK DEV NODE\\032\\003RDN\"\\006\\367eC\\217\\002\\2600\t""want_response":true + }"id":"1481765939 rx_time":"1630360100 hop_limit":"3 priority":"BACKGROUND", + "fromId":"!438f02b0", + "toId":"^all" + }, + "snr":"None", + "hopLimit":3 + }{ + "num":1000569662, + "user":{ + "id":"!3ba37b3e", + "longName":"RAK Solar 1", + "shortName":"RS1", + "macaddr":"1Kc7o3s+", + "hwModel":"RAK4631" + }, + "position":{ + "latitudeI":476022865, + "longitudeI":-1221531705, + "altitude":81, + "batteryLevel":4, + "time":1628091161, + "latitude":47.6022865, + "longitude":-122.15317049999999 + }, + "lastHeard":1630234276, + "snr":5.25 + }{ + "num":2792101487, + "user":{ + "id":"!a66c166f", + "longName":"RAK Solar 2", + "shortName":"RS2", + "macaddr":"8eambBZv", + "hwModel":"RAK4631" + }, + "position":{ + "batteryLevel":56, + "time":1628096176 + }, + "lastHeard":1628231149, + "snr":4.75 + }{ + "num":84682040, + "user":{ + "id":"!050c2538", + "longName":"Yellow Beam", + "shortName":"YB", + "macaddr":"PGEFDCU4", + "hwModel":"TBEAM" + }, + "position":{ + "latitudeI":476020441, + "longitudeI":-1221533598, + "altitude":65, + "batteryLevel":100, + "time":1628405738, + "latitude":47.6020441, + "longitude":-122.15335979999999 + }, + "lastHeard":1628405744, + "snr":4.5 + }{ + "num":2354994191, + "user":{ + "id":"!8c5e5c0f", + "longName":"RAK Solar 3", + "shortName":"RS3", + "macaddr":"+GKMXlwP", + "hwModel":"RAK4631" + }, + "position":{ + "batteryLevel":62 + }, + "lastHeard":1630252441, + "snr":4.75 + } +] diff --git a/MeshtasticClient/Views/Devices/Bluetooth.swift b/MeshtasticClient/Views/Devices/Bluetooth.swift new file mode 100644 index 00000000..34ae3821 --- /dev/null +++ b/MeshtasticClient/Views/Devices/Bluetooth.swift @@ -0,0 +1,4 @@ +import SwiftUI +import CoreBluetooth + + diff --git a/MeshtasticClient/Views/Devices/DeviceBLE.swift b/MeshtasticClient/Views/Devices/DeviceBLE.swift index d970c564..87aea3ef 100644 --- a/MeshtasticClient/Views/Devices/DeviceBLE.swift +++ b/MeshtasticClient/Views/Devices/DeviceBLE.swift @@ -5,7 +5,6 @@ // Created by Garth Vander Houwen on 8/18/21. // -import CoreBluetooth import SwiftUI import MapKit import CoreLocation @@ -14,32 +13,70 @@ struct DeviceBLE: View { @EnvironmentObject var modelData: ModelData + @ObservedObject var bleManager = BLEManager() + var devices: [Device] { modelData.devices } - var myPeripheal:CBPeripheral? - var myCharacteristic:CBCharacteristic? - var bleManager:CBCentralManager? - - let serviceUUID = CBUUID(string: "ab0828b1-198e-4351-b779-901fa0e0371e") - var body: some View { NavigationView { - ScrollView { - - - - + VStack { + + List(bleManager.peripherals) { peripheral in + HStack { + Text(peripheral.name) + Spacer() + Text(String(peripheral.rssi) + " dB") + } + }.frame(height: 300) + + Spacer() + + HStack { + VStack (spacing: 10) { + Button(action: { + self.bleManager.startScanning() + }) { + Text("Start Scanning") + } + Button(action: { + self.bleManager.stopScanning() + }) { + Text("Stop Scanning") + } + }.padding() + + Spacer() + + } + Spacer() } - .navigationTitle("Bluetooth") - - - - - - } + .navigationTitle("Nearby Devices") + .navigationBarItems(leading: + HStack { + Button(action: { + self.bleManager.startScanning() + }) { + Image(systemName: "arrow.clockwise.circle.fill").imageScale(.large) + }}, trailing: + HStack { + if bleManager.isSwitchedOn { + Text("Bluetooth: ON") + .foregroundColor(.green) + .font(.caption2) + } + else { + Text("Bluetooth: OFF") + .foregroundColor(.red) + .font(.caption2) + } + } + ) + }.navigationViewStyle(StackNavigationViewStyle()) + } + } diff --git a/MeshtasticClient/Views/Devices/DeviceHome.swift b/MeshtasticClient/Views/Devices/DeviceHome.swift index 76ee0b42..acf31292 100644 --- a/MeshtasticClient/Views/Devices/DeviceHome.swift +++ b/MeshtasticClient/Views/Devices/DeviceHome.swift @@ -1,14 +1,11 @@ // // DeviceHome.swift -// Landmarks // // Created by Garth Vander Houwen on 8/7/21. -// See LICENSE folder for app licensing information. // // Abstract: -// A view showing devices above a list of devices -// grouped by device. +// A view showing a list of devices that have been seen on the mesh network import SwiftUI @@ -36,7 +33,9 @@ struct DeviceHome: View { } } .navigationTitle("All Devices") - }.navigationViewStyle(StackNavigationViewStyle()) // Force Full screen master details + + + }.navigationViewStyle(StackNavigationViewStyle()) // Force Full screen master details } } diff --git a/MeshtasticClient/Views/Helpers/BLEManager.swift b/MeshtasticClient/Views/Helpers/BLEManager.swift new file mode 100644 index 00000000..45a9bdaf --- /dev/null +++ b/MeshtasticClient/Views/Helpers/BLEManager.swift @@ -0,0 +1,84 @@ +import Foundation +import CoreBluetooth + +struct Peripheral: Identifiable { + let id: Int + let name: String + let rssi: Int +} + +class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate { + + var myCentral: CBCentralManager! + private var meshtasticPeripheral: CBPeripheral! + @Published var isSwitchedOn = false + @Published var peripherals = [Peripheral]() + + let meshtasticServiceID = 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 + + override init() { + super.init() + + myCentral = CBCentralManager(delegate: self, queue: nil) + myCentral.delegate = self + + } + + + func centralManagerDidUpdateState(_ central: CBCentralManager) { + if central.state == .poweredOn { + isSwitchedOn = true + } + else { + isSwitchedOn = false + } + } + + + func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { + + print(peripheral) + + var peripheralName: String! + + if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String { + peripheralName = name + } + else { + peripheralName = "Unknown" + } + + let newPeripheral = Peripheral(id: 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() + } + + func connectToDevice(uuid:String) { + + // let meshtasticPeripheral = self.peripherals.first(where: { $0.id == uuid }) + if (meshtasticPeripheral == nil) { + return + } + // Attempt to connect to this device + myCentral.connect(meshtasticPeripheral, options: nil) + + // Retain the peripheral + + } + + +}