Skip to content

Commit df1fdac

Browse files
committed
Fix compile error, fix bugs, add copy action to about fields
1 parent 53e62fc commit df1fdac

File tree

5 files changed

+148
-4
lines changed

5 files changed

+148
-4
lines changed

InfiniLink.xcodeproj/project.pbxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@
487487
isa = PBXGroup;
488488
children = (
489489
E0A7C08F2CB1EE8F0042A12D /* DFUUpdater.swift */,
490+
E04396162D2A410100A13B90 /* DFUUpdaterCustom.swift */,
490491
E05999FD2CB462BF00D64E0B /* ChartManager.swift */,
491492
E0A7C0902CB1EE8F0042A12D /* DownloadManager.swift */,
492493
E05999FF2CB46EB100D64E0B /* StepCountManager.swift */,
@@ -608,7 +609,6 @@
608609
E0A7C07E2CB1B3300042A12D /* BLEFS.swift */,
609610
E0C7C8512CAF14870043DA2D /* SetTime.swift */,
610611
E07D8F232CC012FC005C1325 /* DeviceManager.swift */,
611-
E04396162D2A410100A13B90 /* DFUUpdaterCustom.swift */,
612612
);
613613
path = BLE;
614614
sourceTree = "<group>";

InfiniLink/BLE/DeviceManager.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class DeviceManager: ObservableObject {
112112
newDevice.manufacturer = ""
113113
newDevice.modelNumber = ""
114114
newDevice.serial = ""
115+
newDevice.stepsGoal = 10000
115116

116117
do {
117118
try context.save()

InfiniLink/Core/Settings/General/About/AboutSettingsView.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ struct AboutRowView: View {
7373
Spacer()
7474
Text(value)
7575
.foregroundStyle(.gray)
76+
.contextMenu {
77+
Button("Copy") {
78+
UIPasteboard.general.string = value
79+
}
80+
}
7681
}
7782
}
7883
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//
2+
// DFUUpdaterCustom.swift
3+
// InfiniLink
4+
//
5+
// Created by Liam Willey on 1/4/25.
6+
//
7+
8+
import Foundation
9+
import Zip
10+
11+
struct DFUManifest: Codable {
12+
struct InitPacketData: Codable {
13+
let application_version: Int
14+
let device_revision: Int
15+
let device_type: Int
16+
let firmware_crc16: Int
17+
let softdevice_req: [Int]
18+
}
19+
struct FirmwareApplication: Codable {
20+
let bin_file: String
21+
let dat_file: String
22+
let init_packet_data: InitPacketData
23+
}
24+
struct Manifest: Codable {
25+
let application: FirmwareApplication
26+
let dfu_version: Double
27+
}
28+
29+
let manifest: Manifest
30+
}
31+
32+
class DFUUpdaterCustom: ObservableObject {
33+
static let shared = DFUUpdaterCustom()
34+
35+
let bleManager = BLEManager.shared
36+
let dfuUpdater = DFUUpdater.shared
37+
38+
func startDFU() {
39+
let start = Data([0x01, 0x04])
40+
let initPacket = Data([0x02, 0x00])
41+
let datPacketSent = Data([0x02, 0x01])
42+
let packetReceiptInterval = Data([0x08, 0x0A])
43+
44+
if let infiniTime = bleManager.infiniTime {
45+
/*
46+
MARK: Step One
47+
For the first step, write `0x01`, `0x04` to the control point characteristic. This will signal InfiniTime that a DFU upgrade is to be started.
48+
*/
49+
infiniTime.writeValue(start, for: bleManager.dfuControlPointCharacteristic, type: .withResponse)
50+
51+
do {
52+
let unzipDirectory = try Zip.quickUnzipFile(dfuUpdater.firmwareURL)
53+
let manifest = unzipDirectory.appendingPathComponent("manifest.json")
54+
let jsonData = try Data(contentsOf: manifest)
55+
56+
let decoder = JSONDecoder()
57+
let decodedManifest = try decoder.decode(DFUManifest.self, from: jsonData)
58+
59+
/*
60+
MARK: Step Two
61+
In step two, send the total size in bytes of the firmware file to the packet characteristic. This value should be an unsigned 32-bit integer encoded as little-endian. In front of this integer should be 8 null bytes. This is because there are three items that can be updated and each 4 bytes is for one of those. The last four are for the InfiniTime application, so those are the ones that need to be set.
62+
*/
63+
let fileSize = try Data(contentsOf: unzipDirectory.appendingPathComponent(decodedManifest.manifest.application.bin_file)).count
64+
print("Firmware size: \(fileSize) bytes")
65+
66+
var data = Data(repeating: 0, count: 8)
67+
let sizeBytes = withUnsafeBytes(of: UInt32(fileSize).littleEndian) { Data($0) }
68+
data.append(sizeBytes)
69+
70+
print("Data to send: \(data.map { String(format: "%02X", $0) }.joined(separator: " "))")
71+
72+
infiniTime.writeValue(data, for: bleManager.dfuPacketCharacteristic, type: .withResponse)
73+
74+
/*
75+
MARK: Step Three
76+
Before running step three, wait for a response from the control point. This response should be `0x10`, `0x01`, `0x01` which indicates a successful DFU start. In step three, send `0x02`, `0x00` to the control point. This will signal InfiniTime to expect the init packet on the packet characteristic.
77+
*/
78+
// TODO: wait for response
79+
infiniTime.writeValue(initPacket, for: bleManager.dfuControlPointCharacteristic, type: .withResponse)
80+
81+
/*
82+
MARK: Step Four
83+
The previous step prepared InfiniTime for this one. In this step, send the contents of the .dat init packet file to the packet characteristic.
84+
*/
85+
let datData = try Data(contentsOf: unzipDirectory.appendingPathComponent(decodedManifest.manifest.application.dat_file))
86+
infiniTime.writeValue(datData, for: bleManager.dfuPacketCharacteristic, type: .withResponse)
87+
/*
88+
After this, send `0x02`, `0x01` indicating that the packet has been sent.
89+
*/
90+
infiniTime.writeValue(datPacketSent, for: bleManager.dfuControlPointCharacteristic, type: .withResponse)
91+
92+
/*
93+
MARK: Step Five
94+
Before running this step, wait to receive `0x10`, `0x02`, `0x01` which indicates that the packet has been received. During this step, send the packet receipt interval to the control point. The firmware file will be sent in segments of 20 bytes each. The packet receipt interval indicates how many segments should be received before sending a receipt containing the amount of bytes received so that it can be confirmed to be the same as the amount sent. This is very useful for detecting packet loss. `itd` uses `0x08`, `0x0A` which indicates 10 segments.
95+
*/
96+
// TODO: wait for response
97+
infiniTime.writeValue(packetReceiptInterval, for: bleManager.dfuControlPointCharacteristic, type: .withResponse)
98+
99+
/*
100+
MARK: Step Six
101+
Write `0x03` to the control point, indicating that the firmware will be sent next on the packet characteristic.
102+
*/
103+
infiniTime.writeValue(Data([0x03]), for: bleManager.dfuControlPointCharacteristic, type: .withResponse)
104+
105+
/*
106+
MARK: Step Seven
107+
This step is the most difficult. Here, the actual firmware is sent to InfiniTime.
108+
109+
As mentioned before, the firmware file must be split up into segments of 20 bytes each and sent to the packet characteristic one by one. Every 10 segments (or whatever you have set the interval to), check for a response starting with `0x11`. The rest of the response will be the amount of bytes received encoded as a little-endian unsigned 32-bit integer. Confirm that this matches the amount of bytes sent, and then continue sending more segments.
110+
*/
111+
let firmwareData = try Data(contentsOf: unzipDirectory.appending(path: decodedManifest.manifest.application.bin_file))
112+
let firmwareSegments = firmwareData.split(separator: Data([0x08, 0x0A]))
113+
114+
for (_, segment) in firmwareSegments.enumerated() {
115+
infiniTime.writeValue(segment, for: bleManager.dfuPacketCharacteristic, type: .withResponse)
116+
// TODO: wait for response
117+
}
118+
119+
/*
120+
MARK: Step Eight
121+
Before running this step, wait to receive `0x10`, `0x03`, `0x01` which indicates a successful receipt of the firmware image. In this step, write `0x04` to the control point to signal InfiniTime to validate the image it has received.
122+
*/
123+
infiniTime.writeValue(Data([0x04]), for: bleManager.dfuControlPointCharacteristic, type: .withResponse)
124+
125+
/*
126+
MARK: Step Nine
127+
Before running this step, wait to receive `0x10`, `0x04`, `0x01` which indicates that the image has been validated. In this step, send `0x05` to the control point as a command with no response. This signals InfiniTime to activate the new firmware and reboot.
128+
*/
129+
// TODO: wait for response
130+
infiniTime.writeValue(Data([0x05]), for: bleManager.dfuControlPointCharacteristic, type: .withResponse)
131+
} catch {
132+
log("\(error.localizedDescription)", caller: "DFUUpdaterCustom")
133+
}
134+
}
135+
}
136+
}

InfiniLink/Utils/MusicController.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,17 @@ class MusicController {
3434
initialize()
3535
}
3636

37-
@objc func onNotificationReceipt(_ notification: NSNotification) {
37+
@objc func onPlaybackChange(_ notification: NSNotification) {
3838
musicPlaying = musicPlayer.playbackState.rawValue
39+
}
40+
@objc func onNowPlayingChange(_ notification: NSNotification) {
3941
updateMusicInformation(songInfo: getCurrentSongInfo())
4042
}
4143

4244
func initialize() {
4345
musicPlayer.beginGeneratingPlaybackNotifications()
44-
NotificationCenter.default.addObserver(self, selector: #selector(self.onNotificationReceipt(_:)), name: .MPMusicPlayerControllerPlaybackStateDidChange, object: nil)
45-
NotificationCenter.default.addObserver(self, selector: #selector(self.onNotificationReceipt(_:)), name: .MPMusicPlayerControllerNowPlayingItemDidChange, object: nil)
46+
NotificationCenter.default.addObserver(self, selector: #selector(self.onPlaybackChange(_:)), name: .MPMusicPlayerControllerPlaybackStateDidChange, object: nil)
47+
NotificationCenter.default.addObserver(self, selector: #selector(self.onNowPlayingChange(_:)), name: .MPMusicPlayerControllerNowPlayingItemDidChange, object: nil)
4648
}
4749

4850
func controlMusic(controlNumber: Int) {

0 commit comments

Comments
 (0)