Merge pull request #179 from meshtastic/firmware_version_sheet

Firmware version sheet
This commit is contained in:
Garth Vander Houwen 2022-09-27 06:54:16 -07:00 committed by GitHub
commit 8a7d15840e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 163 additions and 185 deletions

View file

@ -43,7 +43,6 @@
DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193742862F6E600E59241 /* ExternalNotificationConfig.swift */; };
DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */; };
DD6193792863875F00E59241 /* SerialConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193782863875F00E59241 /* SerialConfig.swift */; };
DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B85A728009258000ACD6B /* ShareChannel.swift */; };
DD73FD1128750779000852D6 /* LocationHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD73FD1028750779000852D6 /* LocationHistory.swift */; };
DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */; };
DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */; };
@ -157,7 +156,6 @@
DD6193742862F6E600E59241 /* ExternalNotificationConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalNotificationConfig.swift; sourceTree = "<group>"; };
DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfig.swift; sourceTree = "<group>"; };
DD6193782863875F00E59241 /* SerialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfig.swift; sourceTree = "<group>"; };
DD6B85A728009258000ACD6B /* ShareChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareChannel.swift; sourceTree = "<group>"; };
DD73FD1028750779000852D6 /* LocationHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationHistory.swift; sourceTree = "<group>"; };
DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLogger.swift; sourceTree = "<group>"; };
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = "<group>"; };
@ -300,7 +298,6 @@
DD3501882852FC3B000FC853 /* Settings.swift */,
DD4A911D2708C65400501B7E /* AppSettings.swift */,
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */,
DD6B85A728009258000ACD6B /* ShareChannel.swift */,
DD86D40B287F401000BAEB7A /* SaveChannelQRCode.swift */,
DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */,
DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */,
@ -711,7 +708,6 @@
DD4C158C2824A91E0032668E /* module_config.pb.swift in Sources */,
DDB6ABE828B141AF00384BA1 /* WiFiModes.swift in Sources */,
DD4F23CD28779A3C001D37CB /* TelemetryLog.swift in Sources */,
DD6B85A828009258000ACD6B /* ShareChannel.swift in Sources */,
DDB6ABD628AE742000384BA1 /* BluetoothConfig.swift in Sources */,
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */,
DDC4D568275499A500A4208E /* Persistence.swift in Sources */,

View file

@ -29,7 +29,10 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
@Published var connectedPeripheral: Peripheral!
@Published var lastConnectionError: String
@Published var minimumVersion = "1.3.41"
@Published var connectedVersion: String
@Published var invalidVersion = false
@Published var preferredPeripheral = false
@Published var isSwitchedOn: Bool = false
@Published var isScanning: Bool = false
@ -41,6 +44,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
var timeoutTimer: Timer?
var timeoutTimerCount = 0
var configTimeoutTimer: Timer?
var positionTimer: Timer?
let broadcastNodeNum: UInt32 = 4294967295
@ -102,7 +107,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if isSwitchedOn {
centralManager.scanForPeripherals(withServices: [meshtasticServiceCBUUID], options: nil)
self.isScanning = self.centralManager.isScanning
isScanning = centralManager.isScanning
print("✅ Scanning Started")
}
@ -114,7 +119,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if centralManager.isScanning {
self.centralManager.stopScan()
self.isScanning = self.centralManager.isScanning
isScanning = centralManager.isScanning
print("🛑 Stopped Scanning")
}
}
@ -155,6 +160,20 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
if meshLoggingEnabled { MeshLogger.log("🚨 BLE Connecting 2 Second Timeout Timer Fired \(timeoutTimerCount) Time(s): \(name)") }
}
}
// MARK: BLE Connect functions
/// The action after the timeout-timer has fired
///
/// - Parameters:
/// - timer: The time that fired the event
///
@objc func configTimeoutTimerFired(timer: Timer) {
self.lastConnectionError = "🚨 Update your firmware"
self.connectedVersion = "1.2.65"
self.invalidVersion = true
self.timeoutTimer!.invalidate()
}
// Connect to a specific peripheral
func connectTo(peripheral: CBPeripheral) {
@ -170,7 +189,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
self.disconnectPeripheral()
}
self.connectedVersion = "0.0.0"
self.centralManager?.connect(peripheral)
// Invalidate any existing timer
@ -192,6 +210,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
guard let connectedPeripheral = connectedPeripheral else { return }
self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
self.isConnected = false
self.invalidVersion = false
self.connectedVersion = "0.0.0"
}
// Called each time a peripheral is discovered
@ -416,10 +436,45 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
}
if (![FROMNUM_characteristic, FROMNUM_characteristic, TORADIO_characteristic].contains(nil)) {
sendWantConfig()
self.configTimeoutTimer = Timer.scheduledTimer(timeInterval: TimeInterval(7), target: self, selector: #selector(configTimeoutTimerFired), userInfo: context, repeats: false)
RunLoop.current.add(self.configTimeoutTimer!, forMode: .common)
}
}
func requestDeviceMetadata() {
guard (connectedPeripheral!.peripheral.state == CBPeripheralState.connected) else { return }
MeshLogger.log(" Requesting Device Metadata for \(connectedPeripheral!.peripheral.name ?? "Unknown")")
var adminPacket = AdminMessage()
adminPacket.getDeviceMetadataRequest = 0
var meshPacket: MeshPacket = MeshPacket()
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = true
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
dataMessage.wantResponse = true
meshPacket.decoded = dataMessage
var toRadio: ToRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
connectedPeripheral!.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
// Either Read the config complete value or from num notify value
connectedPeripheral!.peripheral.readValue(for: FROMRADIO_characteristic)
}
func sendWantConfig() {
guard (connectedPeripheral!.peripheral.state == CBPeripheralState.connected) else { return }
@ -504,22 +559,35 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
nowKnown = true
connectedVersion = String(version)
let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, meshLogging: meshLoggingEnabled, context: context!)
let supportedVersion = connectedVersion == "0.0.0" || self.minimumVersion.compare(connectedVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(connectedVersion, options: .numeric) == .orderedSame
if myInfo != nil {
if !supportedVersion {
self.connectedPeripheral.bitrate = myInfo!.bitrate
self.connectedPeripheral.num = myInfo!.myNodeNum
invalidVersion = true
self.lastConnectionError = "🚨 Update your firmware"
self.connectedPeripheral.firmwareVersion = myInfo!.firmwareVersion ?? "Unknown"
self.connectedPeripheral.name = myInfo!.bleName ?? "Unknown"
self.connectedPeripheral.longName = myInfo!.bleName ?? "Unknown"
self.connectedPeripheral.maxChannels = myInfo!.maxChannels
return
} else {
let myInfo = myInfoPacket(myInfo: decodedInfo.myInfo, meshLogging: meshLoggingEnabled, context: context!)
if myInfo != nil {
self.connectedPeripheral.bitrate = myInfo!.bitrate
self.connectedPeripheral.num = myInfo!.myNodeNum
self.connectedPeripheral.firmwareVersion = myInfo!.firmwareVersion ?? "Unknown"
self.connectedPeripheral.name = myInfo!.bleName ?? "Unknown"
self.connectedPeripheral.longName = myInfo!.bleName ?? "Unknown"
self.connectedPeripheral.maxChannels = myInfo!.maxChannels
}
}
}
// NodeInfo
if decodedInfo.nodeInfo.num != 0 {
if decodedInfo.nodeInfo.num != 0 && !invalidVersion {
nowKnown = true
let nodeInfo = nodeInfoPacket(nodeInfo: decodedInfo.nodeInfo, meshLogging: meshLoggingEnabled, context: context!)
@ -540,14 +608,14 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
}
// Config
if decodedInfo.config.isInitialized {
if decodedInfo.config.isInitialized && !invalidVersion {
nowKnown = true
localConfig(config: decodedInfo.config, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
}
// Module Config
if decodedInfo.moduleConfig.isInitialized {
if decodedInfo.moduleConfig.isInitialized && !invalidVersion {
nowKnown = true
moduleConfig(config: decodedInfo.moduleConfig, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
@ -567,9 +635,17 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
case .waypointApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Waypoint App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .nodeinfoApp:
nodeInfoAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
if !invalidVersion {
nodeInfoAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
}
case .routingApp:
routingPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
if !invalidVersion {
routingPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
}
case .adminApp:
adminAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
case .replyApp:
@ -583,7 +659,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
case .rangeTestApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Range Test App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .telemetryApp:
telemetryPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
if !invalidVersion {
telemetryPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!)
}
case .textMessageCompressedApp:
if meshLoggingEnabled { MeshLogger.log(" MESH PACKET received for Text Message Compressed App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
case .zpsApp:
@ -636,7 +716,12 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
if decodedInfo.configCompleteID != 0 && decodedInfo.configCompleteID == configNonce {
if configTimeoutTimer != nil {
configTimeoutTimer?.invalidate()
}
if meshLoggingEnabled { MeshLogger.log("🤜 BLE Config Complete Packet Id: \(decodedInfo.configCompleteID)") }
self.connectedPeripheral.subscribed = true
peripherals.removeAll(where: { $0.peripheral.state == CBPeripheralState.disconnected })
@ -657,6 +742,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
}
return
}
case FROMNUM_UUID :
@ -1320,7 +1406,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
var adminPacket = AdminMessage()
adminPacket.getChannelRequest = channelIndex
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
@ -1342,23 +1427,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
let binaryData: Data = try! toRadio.serializedData()
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
do {
try context!.save()
if meshLoggingEnabled { MeshLogger.log("💾 Saved a Get Channel Request Admin Message for node: \(String(connectedPeripheral.num))") }
if meshLoggingEnabled { MeshLogger.log("🛎️ Send Get Channel \(channelIndex) Request Admin Message for node: \(String(connectedPeripheral.num))") }
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
return true
} catch {
context!.rollback()
let nsError = error as NSError
print("💥 Error Inserting New Core Data MessageEntity: \(nsError)")
}
}
return false

View file

@ -19,9 +19,7 @@ struct Connect: View {
@State var initialLoad: Bool = true
@State var isPreferredRadio: Bool = false
@State var firmwareVersion = "0.0.0"
@State var minimumVersion = "1.3.41"
@State var invalidVersion = false
@State var invalidFirmwareVersion = false
var body: some View {
@ -100,13 +98,15 @@ struct Connect: View {
}
userSettings.preferredPeripheralId = bleManager.connectedPeripheral!.peripheral.identifier.uuidString
bleManager.preferredPeripheral = true
} else {
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.identifier.uuidString == userSettings.preferredPeripheralId {
userSettings.preferredPeripheralId = ""
userSettings.preferredPeripheralName = ""
bleManager.preferredPeripheral = false
}
}
}
@ -260,22 +260,15 @@ struct Connect: View {
)
}
.navigationViewStyle(StackNavigationViewStyle())
.sheet(isPresented: $invalidVersion) {
.sheet(isPresented: $invalidFirmwareVersion, onDismiss: didDismissSheet) {
InvalidVersion(errorText: "1.3 Beta this version of the app supports only version \(minimumVersion) and above. Your device has been disconnected.")
InvalidVersion(minimumVersion: self.bleManager.minimumVersion, version: self.bleManager.connectedVersion)
}
.onChange(of: (self.bleManager.connectedVersion)) { ic in
.onChange(of: (self.bleManager.invalidVersion)) { cv in
firmwareVersion = self.bleManager.connectedVersion
let supportedVersion = firmwareVersion == "0.0.0" || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedSame
invalidFirmwareVersion = self.bleManager.invalidVersion
invalidVersion = !supportedVersion
if invalidVersion {
bleManager.disconnectPeripheral()
}
}
.onAppear(perform: {
@ -303,6 +296,10 @@ struct Connect: View {
}
})
}
func didDismissSheet() {
bleManager.disconnectPeripheral()
}
}
struct Connect_Previews: PreviewProvider {

View file

@ -8,29 +8,62 @@ import SwiftUI
struct InvalidVersion: View {
@State var errorText = ""
@State var minimumVersion = ""
@State var version = ""
var body: some View {
VStack {
Text("🚨 Unsupported Firmware Version")
Text("Update your firmware")
.font(.largeTitle)
.foregroundColor(.red)
.foregroundColor(.orange)
Text(errorText)
.font(.title2)
.padding()
Divider()
Text("Version 1.3 includes breaking changes to devices and the client apps. The version 1.3 app does not support 1.2 nodes, there is one build for 1.2 under Versions & Build Groups in TestFlight that will be available until the end of November 2022.")
.font(.callout)
.padding()
VStack {
Text("The Meshtastic Apple apps support firmware version \(minimumVersion) and above. You are running version \(version)")
.font(.title2)
.padding(.bottom)
Link("Firmware update docs", destination: URL(string: "https://meshtastic.org/docs/getting-started/flashing-firmware/")!)
.font(.title)
.padding()
Link("Additional help", destination: URL(string: "https://meshtastic.org/docs/faq")!)
.font(.title)
.padding()
}
.padding()
Link("Upgrade your Firmware", destination: URL(string: "https://meshtastic.org/docs/getting-started/flashing-firmware/")!)
Text("Only manual firmware upgrade methods are working for version 1.3.")
.padding()
if version == "1.2.65" {
Divider()
.padding(.top)
VStack{
Text("🦕 Your device is Version 1.2 🦖 ☄️")
.font(.title3)
.foregroundColor(.orange)
.padding(.bottom)
Text("Version 1.3 includes breaking changes to devices and the client apps. The version 1.3 app does not support 1.2 or 1.0 nodes.")
.font(.caption)
.padding([.leading, .trailing])
Text("There is a build for 1.2 EOL under Other Versions in TestFlight that will be available until the end of November 2022.")
.font(.caption)
.padding()
Link("Version 1.2 End of life (EOL) Info", destination: URL(string: "https://meshtastic.org/docs/1.2-End-of-life/")!)
.font(.callout)
}.padding()
}
}
}
}

View file

@ -37,15 +37,6 @@ struct Settings: View {
Section("Radio Configuration") {
NavigationLink {
ShareChannel(node: nodes.first(where: { $0.num == connectedNodeNum }))
} label: {
Image(systemName: "qrcode")
.symbolRenderingMode(.hierarchical)
Text("Share Channel QR Code")
}
.disabled(bleManager.connectedPeripheral == nil)
NavigationLink {
UserConfig(node: nodes.first(where: { $0.num == connectedNodeNum }))
} label: {

View file

@ -1,112 +0,0 @@
//
// ShareChannel.swift
// MeshtasticApple
//
// Created by Garth Vander Houwen on 4/8/22.
//
import SwiftUI
import CoreData
import CoreImage.CIFilterBuiltins
struct QrCodeImage {
let context = CIContext()
func generateQRCode(from text: String) -> UIImage {
var qrImage = UIImage(systemName: "xmark.circle") ?? UIImage()
let data = Data(text.utf8)
let filter = CIFilter.qrCodeGenerator()
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 20, y: 20)
if let outputImage = filter.outputImage?.transformed(by: transform) {
if let image = context.createCGImage(
outputImage,
from: outputImage.extent) {
qrImage = UIImage(cgImage: image)
}
}
return qrImage
}
}
struct ShareChannel: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@EnvironmentObject var userSettings: UserSettings
var node: NodeInfoEntity?
@State private var text = "https://meshtastic.org/E/#test"
var qrCodeImage = QrCodeImage()
var body: some View {
VStack {
GeometryReader { bounds in
let smallest = min(bounds.size.width, bounds.size.height)
ScrollView {
VStack {
Text("Scan the QR code below with the Apple or Android device you would like to share your channel settings with.")
.fixedSize(horizontal: false, vertical: true)
.font(.callout)
let image = qrCodeImage.generateQRCode(from: text)
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(
minWidth: smallest * 0.8,
maxWidth: smallest * 0.8,
minHeight: smallest * 0.8,
maxHeight: smallest * 0.8,
alignment: .center
)
if node != nil && node!.loRaConfig != nil {
HStack {
let preset = ModemPresets(rawValue: Int(node!.loRaConfig!.modemPreset))
Text("Modem Preset \(preset!.description)").font(.title3)
}
}
VStack {
Text("Number of Channels: \(node!.myInfo!.maxChannels)").font(.title2)
ForEach(node!.myInfo!.channels?.array.sorted(by: { ($0 as! ChannelEntity).index < ($1 as! ChannelEntity).index }) as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in
VStack {
Text("Channel: \(channel.index) Name: \(channel.name ?? "")")
}
}
}
}
.frame(width: bounds.size.width, height: bounds.size.height)
}
}
.navigationTitle("Share Channel")
.navigationBarTitleDisplayMode(.automatic)
.navigationBarItems(trailing:
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
})
.onAppear {
self.bleManager.context = context
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}