Merge pull request #255 from meshtastic/2.0.6_Working_Changes

Bluetooth refactoring
This commit is contained in:
Garth Vander Houwen 2022-12-05 19:51:05 -08:00 committed by GitHub
commit 147088b4dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 248 additions and 107 deletions

View file

@ -33,6 +33,8 @@
DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582528582E9B009B0E59 /* DeviceConfig.swift */; };
DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD415827285859C4009B0E59 /* TelemetryConfig.swift */; };
DD41582A28585C32009B0E59 /* RangeTestConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD41582928585C32009B0E59 /* RangeTestConfig.swift */; };
DD457184293C55CD000C49FB /* NordicDFU in Frameworks */ = {isa = PBXBuildFile; productRef = DD457183293C55CD000C49FB /* NordicDFU */; };
DD457188293C7E63000C49FB /* SignalStrengthIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD457187293C7E63000C49FB /* SignalStrengthIndicator.swift */; };
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CD26F103C600029299 /* NodeList.swift */; };
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D526F17ED900029299 /* CircleText.swift */; };
DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D826F3093800029299 /* MessageBubble.swift */; };
@ -148,6 +150,7 @@
DD41582528582E9B009B0E59 /* DeviceConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceConfig.swift; sourceTree = "<group>"; };
DD415827285859C4009B0E59 /* TelemetryConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryConfig.swift; sourceTree = "<group>"; };
DD41582928585C32009B0E59 /* RangeTestConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeTestConfig.swift; sourceTree = "<group>"; };
DD457187293C7E63000C49FB /* SignalStrengthIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalStrengthIndicator.swift; sourceTree = "<group>"; };
DD47E3CD26F103C600029299 /* NodeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeList.swift; sourceTree = "<group>"; };
DD47E3D526F17ED900029299 /* CircleText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleText.swift; sourceTree = "<group>"; };
DD47E3D826F3093800029299 /* MessageBubble.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBubble.swift; sourceTree = "<group>"; };
@ -233,6 +236,7 @@
buildActionMask = 2147483647;
files = (
C9697FA527933B8C00250207 /* SQLite in Frameworks */,
DD457184293C55CD000C49FB /* NordicDFU in Frameworks */,
DD5394FC276993AD00AD86B1 /* SwiftProtobuf in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -517,6 +521,7 @@
DDB6ABDA28B0AC6000384BA1 /* DistanceText.swift */,
DD3CC6BD28E4CD9800FA9159 /* BatteryGauge.swift */,
DD97E96528EFD9820056DDA4 /* MeshtasticLogo.swift */,
DD457187293C7E63000C49FB /* SignalStrengthIndicator.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -566,6 +571,7 @@
packageProductDependencies = (
DD5394FB276993AD00AD86B1 /* SwiftProtobuf */,
C9697FA427933B8C00250207 /* SQLite */,
DD457183293C55CD000C49FB /* NordicDFU */,
);
productName = MeshtasticClient;
productReference = DDC2E15426CE248E0042C5E4 /* Meshtastic.app */;
@ -643,6 +649,7 @@
packageReferences = (
DD5394FA276993AD00AD86B1 /* XCRemoteSwiftPackageReference "swift-protobuf" */,
C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */,
DD457182293C55CD000C49FB /* XCRemoteSwiftPackageReference "IOS-DFU-Library" */,
);
productRefGroup = DDC2E15526CE248E0042C5E4 /* Products */;
projectDirPath = "";
@ -707,6 +714,7 @@
buildActionMask = 2147483647;
files = (
DD4DED9027AD2975004BA27E /* cannedmessages.pb.swift in Sources */,
DD457188293C7E63000C49FB /* SignalStrengthIndicator.swift in Sources */,
DDCFF601285453A7005FA625 /* localonly.pb.swift in Sources */,
DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */,
DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */,
@ -1153,6 +1161,14 @@
minimumVersion = 0.9.2;
};
};
DD457182293C55CD000C49FB /* XCRemoteSwiftPackageReference "IOS-DFU-Library" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/NordicSemiconductor/IOS-DFU-Library";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 4.0.0;
};
};
DD5394FA276993AD00AD86B1 /* XCRemoteSwiftPackageReference "swift-protobuf" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-protobuf.git";
@ -1169,6 +1185,11 @@
package = C9697FA327933B8C00250207 /* XCRemoteSwiftPackageReference "SQLite.swift" */;
productName = SQLite;
};
DD457183293C55CD000C49FB /* NordicDFU */ = {
isa = XCSwiftPackageProductDependency;
package = DD457182293C55CD000C49FB /* XCRemoteSwiftPackageReference "IOS-DFU-Library" */;
productName = NordicDFU;
};
DD5394FB276993AD00AD86B1 /* SwiftProtobuf */ = {
isa = XCSwiftPackageProductDependency;
package = DD5394FA276993AD00AD86B1 /* XCRemoteSwiftPackageReference "swift-protobuf" */;

View file

@ -1,5 +1,14 @@
{
"pins" : [
{
"identity" : "ios-dfu-library",
"kind" : "remoteSourceControl",
"location" : "https://github.com/NordicSemiconductor/IOS-DFU-Library",
"state" : {
"revision" : "ec5364755f4fcdf68d62ff4cf796d22e7b935f40",
"version" : "4.13.0"
}
},
{
"identity" : "sqlite.swift",
"kind" : "remoteSourceControl",
@ -17,6 +26,15 @@
"revision" : "e1499bc69b9040b29184f7f2996f7bab467c1639",
"version" : "1.19.0"
}
},
{
"identity" : "zipfoundation",
"kind" : "remoteSourceControl",
"location" : "https://github.com/weichsel/ZIPFoundation",
"state" : {
"revision" : "ec32d62d412578542c0ffb7a6ce34d3e64b43b94",
"version" : "0.9.11"
}
}
],
"version" : 2

View file

@ -7,9 +7,7 @@ import MapKit
// ---------------------------------------------------------------------------------------
// Meshtastic BLE Device Manager
// ---------------------------------------------------------------------------------------
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
static let shared = BLEManager()
class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject {
private static var documentsFolder: URL {
do {
@ -20,24 +18,21 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
var context: NSManagedObjectContext?
var userSettings: UserSettings?
private var centralManager: CBCentralManager!
@Published var peripherals: [Peripheral]
@Published var peripherals: [Peripheral] = []
@Published var connectedPeripheral: Peripheral!
@Published var lastConnectionError: String
@Published var minimumVersion = "1.3.48"
@Published var connectedVersion: String
@Published var invalidVersion = false
@Published var preferredPeripheral = false
@Published var isSwitchedOn: Bool = false
@Published var isScanning: Bool = false
@Published var isConnecting: Bool = false
@Published var isConnected: Bool = false
@Published var isSubscribed: Bool = false
@Published var lastConnectionError: String
@Published var invalidVersion = false
public var minimumVersion = "1.3.48"
public var connectedVersion: String
public var preferredPeripheral = false
public var isSwitchedOn: Bool = false
public var isConnecting: Bool = false
public var isConnected: Bool = false
public var isSubscribed: Bool = false
/// Used to make sure we never get foold by old BLE packets
private var configNonce: UInt32 = 1
@ -57,52 +52,23 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
let FROMRADIO_UUID = CBUUID(string: "0x2C55E69E-4993-11ED-B878-0242AC120002")
let EOL_FROMRADIO_UUID = CBUUID(string: "0x8BA2BCC2-EE02-4A55-A531-C525C5E454D5")
let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453")
// Meshtastic DFU details
let DFUSERVICE_UUID = CBUUID(string : "cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30")
let DFUSIZE_UUID = CBUUID(string: "e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e")
let DFUDATA_UUID = CBUUID(string: "e272ebac-d463-4b98-bc84-5cc1a39ee517")
let DFUCRC32_UUID = CBUUID(string: "4826129c-c22a-43a3-b066-ce8f0d5bacc6")
let DFURESULT_UUID = CBUUID(string: "5e134862-7411-4424-ac4a-210937432c77")
let DFUREGION_UUID = CBUUID(string: "5e134862-7411-4424-ac4a-210937432c67")
var DFUSIZE_characteristic: CBCharacteristic?
var DFUDATA_characteristic: CBCharacteristic?
var DFUCRC32_characteristic: CBCharacteristic?
var DFURESULT_characteristic: CBCharacteristic?
var DFUREGION_characteristic: CBCharacteristic?
//private var meshLoggingEnabled: Bool = true
let meshLog = documentsFolder.appendingPathComponent("meshlog.txt")
// MARK: init BLEManager
override init() {
self.lastConnectionError = ""
self.connectedVersion = "0.0.0"
self.peripherals = [Peripheral]()
super.init()
// let bleQueue: DispatchQueue = DispatchQueue(label: "CentralManager")
centralManager = CBCentralManager(delegate: self, queue: nil)
}
// MARK: Bluetooth enabled/disabled for the app
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
isSwitchedOn = true
startScanning()
} else {
isSwitchedOn = false
}
}
// MARK: Scanning for BLE Devices
// Scan for nearby BLE devices using the Meshtastic BLE service ID
func startScanning() {
if isSwitchedOn {
centralManager.scanForPeripherals(withServices: [meshtasticServiceCBUUID], options: nil)
centralManager.scanForPeripherals(withServices: [meshtasticServiceCBUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
DispatchQueue.main.async {
self.isScanning = self.centralManager.isScanning
}
@ -192,36 +158,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
// Called each time a peripheral is discovered
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
var peripheralName: String = peripheral.name ?? "Unknown"
if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
peripheralName = name
}
let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: peripheralName, shortName: "????", longName: peripheralName, firmwareVersion: "Unknown", rssi: RSSI.intValue, lastUpdate: Date(), peripheral: peripheral)
let peripheralIndex = peripherals.firstIndex(where: { $0.id == newPeripheral.id })
if peripheralIndex != nil && newPeripheral.peripheral.state != CBPeripheralState.connected {
peripherals[peripheralIndex!] = newPeripheral
peripherals.remove(at: peripheralIndex!)
peripherals.append(newPeripheral)
} else {
if newPeripheral.peripheral.state != CBPeripheralState.connected {
peripherals.append(newPeripheral)
}
}
let today = Date()
let visibleDuration = Calendar.current.date(byAdding: .second, value: -5, to: today)!
peripherals.removeAll(where: { $0.lastUpdate < visibleDuration})
}
// Called when a peripheral is connected
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
isConnecting = false
isConnected = true
@ -254,7 +190,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return
}
// Discover Services
peripheral.discoverServices([meshtasticServiceCBUUID, DFUSERVICE_UUID])
peripheral.discoverServices([meshtasticServiceCBUUID])
MeshLogger.log("✅ BLE Connected: \(peripheral.name ?? "Unknown")")
}
@ -312,17 +248,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if service.uuid == meshtasticServiceCBUUID {
peripheral.discoverCharacteristics([TORADIO_UUID, FROMRADIO_UUID, FROMNUM_UUID], for: service)
MeshLogger.log("✅ BLE Service for Meshtastic discovered by \(peripheral.name ?? "Unknown")")
} else if (service.uuid == DFUSERVICE_UUID) {
peripheral.discoverCharacteristics([DFUDATA_UUID, DFUSIZE_UUID, DFUREGION_UUID, DFURESULT_UUID, DFUCRC32_UUID], for: service)
MeshLogger.log("✅ BLE Service for Meshtastic DFU discovered by \(peripheral.name ?? "Unknown")")
}
}
}
}
// MARK: Discover Characteristics Event
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let e = error {
MeshLogger.log("🚫 BLE Discover Characteristics error for \(peripheral.name ?? "Unknown") \(e) disconnecting device")
// Try and stop crashes when this error occurs
@ -348,26 +280,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
MeshLogger.log("✅ BLE did discover FROMNUM (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown")")
FROMNUM_characteristic = characteristic
peripheral.setNotifyValue(true, for: characteristic)
case DFUSIZE_UUID:
MeshLogger.log("✅ BLE did discover DFU Size characteristic for Meshtastic DFU by \(peripheral.name ?? "Unknown")")
DFUSIZE_characteristic = characteristic
case DFUDATA_UUID:
MeshLogger.log("✅ BLE did discover DFU Data characteristic for Meshtastic DFU by \(peripheral.name ?? "Unknown")")
DFUDATA_characteristic = characteristic
case DFUCRC32_UUID:
MeshLogger.log("✅ BLE did discover DFU CRC32 characteristic for Meshtastic DFU by \(peripheral.name ?? "Unknown")")
DFUCRC32_characteristic = characteristic
case DFURESULT_UUID:
MeshLogger.log("✅ BLE did discover DFU Result characteristic for Meshtastic DFU by \(peripheral.name ?? "Unknown")")
DFURESULT_characteristic = characteristic
case DFUREGION_UUID:
MeshLogger.log("✅ BLE did discover DFU Region characteristic for Meshtastic DFU by \(peripheral.name ?? "Unknown")")
DFUREGION_characteristic = characteristic
default:
break
@ -1608,3 +1520,56 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return false
}
}
// MARK: - CB Central Manager implmentation
extension BLEManager: CBCentralManagerDelegate {
// MARK: Bluetooth enabled/disabled
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == CBManagerState.poweredOn {
print("BLE powered on")
isSwitchedOn = true
startScanning()
}
else {
isSwitchedOn = false
}
var status = ""
switch central.state {
case .poweredOff:
status = "BLE is powered off"
case .poweredOn:
status = "BLE is poweredOn"
case .resetting:
status = "BLE is resetting"
case .unauthorized:
status = "BLE is unauthorized"
case .unknown:
status = "BLE is unknown"
case .unsupported:
status = "BLE is unsupported"
default:
status = "default"
}
print("BLEManager status: \(status)")
}
// Called each time a peripheral is discovered
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String
let device = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: name ?? "Unknown", shortName: "????", longName: name ?? "Unknown", firmwareVersion: "Unknown", rssi: RSSI.intValue, lastUpdate: Date(), peripheral: peripheral)
let index = peripherals.map { $0.peripheral }.firstIndex(of: peripheral)
if let peripheralIndex = index {
peripherals[peripheralIndex] = device
} else {
peripherals.append(device)
}
let today = Date()
let visibleDuration = Calendar.current.date(byAdding: .second, value: -5, to: today)!
self.peripherals.removeAll(where: { $0.lastUpdate < visibleDuration})
}
}

View file

@ -7,7 +7,7 @@ import CoreData
struct MeshtasticAppleApp: App {
let persistenceController = PersistenceController.shared
@ObservedObject private var bleManager: BLEManager = BLEManager.shared
@ObservedObject private var bleManager: BLEManager = BLEManager()
@ObservedObject private var userSettings: UserSettings = UserSettings()
@Environment(\.scenePhase) var scenePhase

View file

@ -23,4 +23,16 @@ struct Peripheral: Identifiable {
self.lastUpdate = lastUpdate
self.peripheral = peripheral
}
func getSignalStrength() -> SignalStrength {
if (NSNumber(value: rssi).compare(NSNumber(-65)) == ComparisonResult.orderedDescending) {
return SignalStrength.strong
}
else if (NSNumber(value: rssi).compare(NSNumber(-85)) == ComparisonResult.orderedDescending) {
return SignalStrength.normal
}
else {
return SignalStrength.weak
}
}
}

View file

@ -161,7 +161,7 @@ struct Connect: View {
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
ForEach(bleManager.peripherals.filter({ $0.peripheral.state == CBPeripheralState.disconnected }).sorted(by: { $0.name > $1.name })) { peripheral in
HStack {
Image(systemName: "circle.fill")
.imageScale(.large).foregroundColor(.gray)
@ -184,7 +184,9 @@ struct Connect: View {
Text(peripheral.name).font(.title3)
}
Spacer()
Text(String(peripheral.rssi) + " dB").font(.title3)
VStack {
SignalStrengthIndicator(signalStrength: peripheral.getSignalStrength())
}
}.padding([.bottom, .top])
}
}.textCase(nil)

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2022, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import Foundation
import SwiftUI
struct SignalStrengthIndicator: View {
let signalStrength: SignalStrength
var body: some View {
HStack {
ForEach(0..<3) { bar in
RoundedRectangle(cornerRadius: 3)
.divided(amount: (CGFloat(bar) + 1) / CGFloat(3))
.fill(getColor().opacity(bar <= signalStrength.rawValue ? 1 : 0.3))
.frame(width: 8, height: 30)
}
}
}
private func getColor() -> Color {
switch signalStrength {
case .weak:
return Color.red
case .normal:
return Color.yellow
case .strong:
return Color.green
}
}
}
struct Divided<S: Shape>: Shape {
var amount: CGFloat // Should be in range 0...1
var shape: S
func path(in rect: CGRect) -> Path {
shape.path(in: rect.divided(atDistance: amount * rect.height, from: .maxYEdge).slice)
}
}
extension Shape {
func divided(amount: CGFloat) -> Divided<Self> {
return Divided(amount: amount, shape: self)
}
}
enum SignalStrength : Int {
case weak = 0
case normal = 1
case strong = 2
}

View file

@ -19,6 +19,8 @@ struct DeviceConfig: View {
@State var hasChanges = false
@State var deviceRole = 0
@State var buzzerGPIO = 0
@State var buttonGPIO = 0
@State var serialEnabled = true
@State var debugLogEnabled = false
@ -55,6 +57,30 @@ struct DeviceConfig: View {
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
Section(header: Text("GPIO")) {
Picker("Button GPIO", selection: $buttonGPIO) {
ForEach(0..<40) {
if $0 == 0 {
Text("Unset")
} else {
Text("Pin \($0)")
}
}
}
.pickerStyle(DefaultPickerStyle())
Picker("Buzzer GPIO", selection: $buzzerGPIO) {
ForEach(0..<40) {
if $0 == 0 {
Text("Unset")
} else {
Text("Pin \($0)")
}
}
}
.pickerStyle(DefaultPickerStyle())
}
}
.disabled(bleManager.connectedPeripheral == nil)
@ -135,6 +161,8 @@ struct DeviceConfig: View {
dc.role = DeviceRoles(rawValue: deviceRole)!.protoEnumValue()
dc.serialEnabled = serialEnabled
dc.debugLogEnabled = debugLogEnabled
dc.buttonGpio = UInt32(buttonGPIO)
dc.buzzerGpio = UInt32(buzzerGPIO)
let adminMessageId = bleManager.saveDeviceConfig(config: dc, fromUser: node!.user!, toUser: node!.user!)
@ -169,6 +197,8 @@ struct DeviceConfig: View {
self.deviceRole = Int(node?.deviceConfig?.role ?? 0)
self.serialEnabled = (node?.deviceConfig?.serialEnabled ?? true)
self.debugLogEnabled = node?.deviceConfig?.debugLogEnabled ?? false
self.buttonGPIO = Int(node?.deviceConfig?.buttonGpio ?? 0)
self.buzzerGPIO = Int(node?.deviceConfig?.buzzerGpio ?? 0)
self.hasChanges = false
}
.onChange(of: deviceRole) { newRole in
@ -192,5 +222,19 @@ struct DeviceConfig: View {
if newDebugLog != node!.deviceConfig!.debugLogEnabled { hasChanges = true }
}
}
.onChange(of: buttonGPIO) { newButtonGPIO in
if node != nil && node!.deviceConfig != nil {
if newButtonGPIO != node!.deviceConfig!.buttonGpio { hasChanges = true }
}
}
.onChange(of: buzzerGPIO) { newBuzzerGPIO in
if node != nil && node!.deviceConfig != nil {
if newBuzzerGPIO != node!.deviceConfig!.buttonGpio { hasChanges = true }
}
}
}
}

View file

@ -236,7 +236,7 @@ struct CannedMessagesConfig: View {
/// Can be e.g. "rotEnc1", "upDownEnc1", "cardkb", or keyword "_any"
cmc.allowInputSource = "rotEnc1"
} else if updown1Enabled {
cmc.allowInputSource = "_any"
cmc.allowInputSource = "upDown1"
} else {
cmc.allowInputSource = "_any"
}
@ -287,7 +287,7 @@ struct CannedMessagesConfig: View {
if newPreset == 1 {
// RAK Rotary Encoder
updown1Enabled = false
updown1Enabled = true
rotary1Enabled = false
inputbrokerPinA = 4
inputbrokerPinB = 10

View file

@ -142,6 +142,7 @@ struct ExternalNotificationConfig: View {
enc.active = active
enc.output = UInt32(output)
enc.outputMs = UInt32(outputMilliseconds)
enc.usePwm = usePWM
let adminMessageId = bleManager.saveExternalNotificationModuleConfig(config: enc, fromUser: node!.user!, toUser: node!.user!)
if adminMessageId > 0{
// Should show a saved successfully alert once I know that to be true