Initial BLE implementation

This commit is contained in:
Garth Vander Houwen 2021-09-10 07:41:26 -07:00
parent 190d9e2e41
commit b51e4c974f
7 changed files with 313 additions and 24 deletions

View file

@ -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 = "<group>"; };
DDAF8C5426EBA0530058C060 /* Bluetooth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bluetooth.swift; sourceTree = "<group>"; };
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 = "<group>"; };
DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@ -67,6 +72,7 @@
DDC2E19E26CE27630042C5E4 /* Device.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Device.swift; sourceTree = "<group>"; };
DDC2E1A326CE2F940042C5E4 /* DeviceBLE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceBLE.swift; sourceTree = "<group>"; };
DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationHelper.swift; sourceTree = "<group>"; };
DDC2E1AA26DD89EC0042C5E4 /* packets.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = packets.json; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -179,6 +185,7 @@
isa = PBXGroup;
children = (
DDC2E18A26CE25690042C5E4 /* deviceData.json */,
DDC2E1AA26DD89EC0042C5E4 /* packets.json */,
);
path = Resources;
sourceTree = "<group>";
@ -199,6 +206,7 @@
DDC2E19626CE26840042C5E4 /* DeviceRow.swift */,
DDC2E19826CE26940042C5E4 /* DeviceMap.swift */,
DDC2E1A326CE2F940042C5E4 /* DeviceBLE.swift */,
DDAF8C5426EBA0530058C060 /* Bluetooth.swift */,
);
path = Devices;
sourceTree = "<group>";
@ -207,6 +215,7 @@
isa = PBXGroup;
children = (
DDC2E19A26CE27150042C5E4 /* CircleImage.swift */,
DDAF8C5226EB1DF10058C060 /* BLEManager.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -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 */,

View file

@ -37,12 +37,16 @@
<array>
<string>armv7</string>
</array>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>Privacy Bluetooth Always Usage Description</key>
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>

View file

@ -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
}
]

View file

@ -0,0 +1,4 @@
import SwiftUI
import CoreBluetooth

View file

@ -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())
}
}

View file

@ -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
}
}

View file

@ -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
}
}