Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion dashpilot-ios/dashpilot/BLE/DashKitOtaUpdate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ final class DashKitOtaUpdate: DashKitGattListener {
private var dataChar: CBCharacteristic?
private var statusChar: CBCharacteristic?

// The CAN stream is paused for the upload — its high-rate notifications
// compete with the OTA writes for connection-event bandwidth. Resumed on
// error/cancel; after a successful upload the DashKit reboots and the
// data source re-subscribes on reconnect.
private var canChar: CBCharacteristic?
private var canWasNotifying = false

init(manager: DashKitBleManager) {
self.manager = manager
}
Expand All @@ -54,14 +61,23 @@ final class DashKitOtaUpdate: DashKitGattListener {

func cancel() {
manager.removeGattListener(self)
resumeCanNotifications()
firmware = nil
peripheral = nil
ctrlChar = nil
dataChar = nil
statusChar = nil
canChar = nil
setState(.idle)
}

private func resumeCanNotifications() {
if canWasNotifying, let peripheral, let canChar {
peripheral.setNotifyValue(true, for: canChar)
}
canWasNotifying = false
}

private func setState(_ newState: OtaState) {
DispatchQueue.main.async { self.state = newState }
}
Expand All @@ -81,6 +97,15 @@ final class DashKitOtaUpdate: DashKitGattListener {
setState(.error("OTA characteristics not found"))
return
}
canChar = peripheral.services?
.first { $0.uuid == DashKitGatt.canService }?
.characteristics?
.first { $0.uuid == DashKitGatt.canCharacteristic }
if let canChar, canChar.isNotifying {
canWasNotifying = true
peripheral.setNotifyValue(false, for: canChar)
print("[DashKitOta] paused CAN notifications for the upload")
}
peripheral.setNotifyValue(true, for: statusChar)
print("[DashKitOta] OTA service ready, subscribing to status")
}
Expand All @@ -89,6 +114,7 @@ final class DashKitOtaUpdate: DashKitGattListener {
guard characteristic.uuid == DashKitGatt.otaStatusCharacteristic else { return }
if error != nil {
setState(.error("Failed to enable OTA notifications"))
resumeCanNotifications()
return
}
sendBeginCommand()
Expand All @@ -104,6 +130,7 @@ final class DashKitOtaUpdate: DashKitGattListener {
characteristic.uuid == DashKitGatt.otaDataCharacteristic else { return }
if let error {
setState(.error("Write failed (\(error.localizedDescription))"))
resumeCanNotifications()
return
}
sendNextChunk()
Expand All @@ -121,6 +148,8 @@ final class DashKitOtaUpdate: DashKitGattListener {
ctrlChar = nil
dataChar = nil
statusChar = nil
canChar = nil
canWasNotifying = false
}

// MARK: - Upload
Expand All @@ -145,7 +174,9 @@ final class DashKitOtaUpdate: DashKitGattListener {
guard let fw = firmware, let peripheral, let data = dataChar else { return }
guard firmwareOffset < fw.count else { return }

let maxLen = peripheral.maximumWriteValueLength(for: .withResponse)
// .withoutResponse reports the true MTU-3 payload; .withResponse
// reports 512, which turns each chunk into a slow ATT long write.
let maxLen = peripheral.maximumWriteValueLength(for: .withoutResponse)
let chunkSize = min(maxLen, fw.count - firmwareOffset)
let chunk = fw.subdata(in: firmwareOffset..<(firmwareOffset + chunkSize))
firmwareOffset += chunkSize
Expand Down Expand Up @@ -175,6 +206,7 @@ final class DashKitOtaUpdate: DashKitGattListener {
print("[DashKitOta] OTA error from device: 0x\(String(errCode, radix: 16))")
setState(.error("Device reported error (0x\(String(errCode, radix: 16)))"))
manager.removeGattListener(self)
resumeCanNotifications()
firmware = nil
default:
break
Expand Down
Loading