mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Initial BLE implementation
This commit is contained in:
parent
190d9e2e41
commit
b51e4c974f
7 changed files with 313 additions and 24 deletions
|
|
@ -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 */,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
149
MeshtasticClient/Resources/packets.json
Normal file
149
MeshtasticClient/Resources/packets.json
Normal 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
|
||||
}
|
||||
]
|
||||
4
MeshtasticClient/Views/Devices/Bluetooth.swift
Normal file
4
MeshtasticClient/Views/Devices/Bluetooth.swift
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import SwiftUI
|
||||
import CoreBluetooth
|
||||
|
||||
|
||||
|
|
@ -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())
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
84
MeshtasticClient/Views/Helpers/BLEManager.swift
Normal file
84
MeshtasticClient/Views/Helpers/BLEManager.swift
Normal 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
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue