Skip to content

Commit

Permalink
Merge pull request #31 from combustion-inc/develop
Browse files Browse the repository at this point in the history
Release v0.10.3
  • Loading branch information
jjohnstz authored Sep 26, 2022
2 parents 0f82496 + 016cc56 commit d92b6c8
Show file tree
Hide file tree
Showing 16 changed files with 277 additions and 77 deletions.
18 changes: 18 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
{
"object": {
"pins": [
{
"package": "NordicDFU",
"repositoryURL": "https://github.com/NordicSemiconductor/IOS-DFU-Library",
"state": {
"branch": null,
"revision": "f73213f2de57c8c53700725ee2cff6cd35ef66fe",
"version": "4.11.1"
}
},
{
"package": "swift-collections",
"repositoryURL": "https://github.com/apple/swift-collections",
Expand All @@ -9,6 +18,15 @@
"revision": "48254824bb4248676bf7ce56014ff57b142b77eb",
"version": "1.0.2"
}
},
{
"package": "ZIPFoundation",
"repositoryURL": "https://github.com/weichsel/ZIPFoundation",
"state": {
"branch": null,
"revision": "ec32d62d412578542c0ffb7a6ce34d3e64b43b94",
"version": "0.9.11"
}
}
]
},
Expand Down
6 changes: 4 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ let package = Package(
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/apple/swift-collections", "1.0.0"..<"2.0.0")
.package(url: "https://github.com/apple/swift-collections", "1.0.0"..<"2.0.0"),
.package(url: "https://github.com/NordicSemiconductor/IOS-DFU-Library", .upToNextMajor(from: "4.11.1"))
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "CombustionBLE",
dependencies: [
.product(name: "Collections", package: "swift-collections", condition: nil)
.product(name: "Collections", package: "swift-collections"),
.product(name: "NordicDFU", package: "IOS-DFU-Library")
],
path: "Sources/CombustionBLE"),
/*
Expand Down
23 changes: 20 additions & 3 deletions Sources/CombustionBLE/BleManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ SOFTWARE.

import Foundation
import CoreBluetooth
import NordicDFU


protocol BleManagerDelegate: AnyObject {
func didConnectTo(identifier: UUID)
func didFailToConnectTo(identifier: UUID)
func didDisconnectFrom(identifier: UUID)
func handleSetIDResponse(identifier: UUID, success: Bool)
func handleSetColorResponse(identifier: UUID, success: Bool)
func updateDeviceWithAdvertising(advertising: AdvertisingData, rssi: NSNumber, identifier: UUID)
func updateDeviceWithAdvertising(advertising: AdvertisingData, isConnectable: Bool, rssi: NSNumber, identifier: UUID)
func updateDeviceWithLogResponse(identifier: UUID, logResponse: LogResponse)
func updateDeviceWithSessionInformation(identifier: UUID, sessionInformation: SessionInformation)
func updateDeviceWithStatus(identifier: UUID, status: DeviceStatus)
Expand Down Expand Up @@ -87,6 +89,18 @@ class BleManager : NSObject {
}
}

func startFirmwareUpdate(device: Device, dfu: DFUFirmware) {
if let connectionPeripheral = getConnectedPeripheral(identifier: device.identifier) {
let initiator = DFUServiceInitiator().with(firmware: dfu)

initiator.delegate = device
initiator.progressDelegate = device


initiator.start(target: connectionPeripheral)
}
}

private func getConnectedPeripheral(identifier: String) -> CBPeripheral? {
let uuid = UUID(uuidString: identifier)
let devicePeripherals = peripherals.filter { $0.identifier == uuid }
Expand Down Expand Up @@ -123,6 +137,7 @@ extension BleManager: CBCentralManagerDelegate{
advertisementData: [String : Any], rssi RSSI: NSNumber) {

let manufatureData: Data = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data ?? Data()
let isConnectable = advertisementData[CBAdvertisementDataIsConnectable] as? Bool ?? false

if let advData = AdvertisingData(fromData: manufatureData) {
// For now, only add probes.
Expand All @@ -131,7 +146,10 @@ extension BleManager: CBCentralManagerDelegate{
// Store peripheral reference for later use
peripherals.insert(peripheral)

delegate?.updateDeviceWithAdvertising(advertising: advData, rssi: RSSI, identifier: peripheral.identifier)
delegate?.updateDeviceWithAdvertising(advertising: advData,
isConnectable: isConnectable,
rssi: RSSI,
identifier: peripheral.identifier)
} else {
// print("Ignoring device with type \(advData.type)")
}
Expand Down Expand Up @@ -288,5 +306,4 @@ extension BleManager: CBPeripheralDelegate {
}
}
}

}
61 changes: 60 additions & 1 deletion Sources/CombustionBLE/Device.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ SOFTWARE.
--*/

import Foundation
import NordicDFU

/// Struct containing info about a thermometer device.
public class Device : ObservableObject {
Expand Down Expand Up @@ -54,6 +55,9 @@ public class Device : ObservableObject {
/// Current connection state of device
@Published public internal(set) var connectionState: ConnectionState = .disconnected

/// Connectable flag set in advertising packet
@Published public internal(set) var isConnectable = false

/// Signal strength to device
@Published public internal(set) var rssi: Int

Expand All @@ -63,6 +67,26 @@ public class Device : ObservableObject {
/// Tracks whether the data has gone stale (no new data in some time)
@Published public internal(set) var stale = false

/// DFU state
@Published public private(set) var dfuState: DFUState?

public struct DFUErrorMessage {
public let error: DFUError
public let message: String
}

/// DFU error message
@Published public private(set) var dfuError: DFUErrorMessage?

public struct DFUUploadProgress {
public let part: Int
public let totalParts: Int
public let progress: Int
}

/// DFU Upload progress
@Published public private(set) var dfuUploadProgress: DFUUploadProgress?

/// Time at which device was last updated
internal var lastUpdateTime = Date()

Expand All @@ -82,6 +106,22 @@ public class Device : ObservableObject {

func updateDeviceStale() {
stale = Date().timeIntervalSince(lastUpdateTime) > Constants.STALE_TIMEOUT


// If device data is stale, assume its not longer connectable
if(stale) {
isConnectable = false
}
}

public func isDFURunning() -> Bool {
guard let dfuState = dfuState else { return false }

if(dfuState == .completed) {
return false
}

return true
}
}

Expand All @@ -95,7 +135,6 @@ extension Device {
/// Attempt to connect to the device.
public func connect() {
// Mark that we should maintain a connection to this device.
// TODO - this doesn't seem to be propagating back to the UI??
maintainingConnection = true

if(connectionState != .connected) {
Expand Down Expand Up @@ -123,3 +162,23 @@ extension Device: Hashable {
hasher.combine(identifier)
}
}

extension Device: DFUServiceDelegate {
public func dfuStateDidChange(to state: DFUState) {
dfuState = state
}

public func dfuError(_ error: DFUError, didOccurWithMessage message: String) {
dfuError = DFUErrorMessage(error: error, message: message)
}
}

extension Device: DFUProgressDelegate {
public func dfuProgressDidChange(for part: Int,
outOf totalParts: Int,
to progress: Int,
currentSpeedBytesPerSecond: Double,
avgSpeedBytesPerSecond: Double) {
dfuUploadProgress = DFUUploadProgress(part: part, totalParts: totalParts, progress: progress)
}
}
21 changes: 16 additions & 5 deletions Sources/CombustionBLE/DeviceManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ SOFTWARE.

import Foundation
import SwiftUI

import NordicDFU

/// Singleton that provides list of detected Devices
/// (either via Bluetooth or from a list in the Cloud)
Expand Down Expand Up @@ -162,11 +162,22 @@ public class DeviceManager : ObservableObject {
/// - parameter ProbeColor: New Probe color
public func setProbeColor(_ device: Device, color: ProbeColor, completionHandler: @escaping (Bool) -> Void) {
setColorCompetionHandlers[device.identifier] = MessageHandler(timeSent: Date(), handler: completionHandler)

let request = SetColorRequest(color: color)
BleManager.shared.sendRequest(identifier: device.identifier, request: request)
}

public func runSoftwareUpgrade(_ device: Device, otaFile: URL) -> Bool {
do {
let dfu = try DFUFirmware(urlToZipFile: otaFile)
BleManager.shared.startFirmwareUpdate(device: device, dfu: dfu)
return true
}
catch {
return false
}
}

private func checkForMessageTimeout(messageHandlers: inout [String: MessageHandler]) {
let currentTime = Date()

Expand Down Expand Up @@ -225,14 +236,14 @@ extension DeviceManager : BleManagerDelegate {
}
}

func updateDeviceWithAdvertising(advertising: AdvertisingData, rssi: NSNumber, identifier: UUID) {
func updateDeviceWithAdvertising(advertising: AdvertisingData, isConnectable: Bool, rssi: NSNumber, identifier: UUID) {
if devices[identifier.uuidString] != nil {
if let probe = devices[identifier.uuidString] as? Probe {
probe.updateWithAdvertising(advertising, RSSI: rssi)
probe.updateWithAdvertising(advertising, isConnectable: isConnectable, RSSI: rssi)
}
}
else {
let device = Probe(advertising, RSSI: rssi, identifier: identifier)
let device = Probe(advertising, isConnectable: isConnectable, RSSI: rssi, identifier: identifier)
addDevice(device: device)
}
}
Expand Down
40 changes: 24 additions & 16 deletions Sources/CombustionBLE/Probe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ public class Probe : Device {
/// Time at which probe instant read was last updated
internal var lastInstantRead: Date?

public init(_ advertising: AdvertisingData, RSSI: NSNumber, identifier: UUID) {
public init(_ advertising: AdvertisingData, isConnectable: Bool, RSSI: NSNumber, identifier: UUID) {
serialNumber = advertising.serialNumber
id = advertising.id
color = advertising.color

super.init(identifier: identifier, RSSI: RSSI)

updateWithAdvertising(advertising, RSSI: RSSI)
updateWithAdvertising(advertising, isConnectable: isConnectable, RSSI: RSSI)
}

override func updateConnectionState(_ state: ConnectionState) {
Expand Down Expand Up @@ -108,21 +108,29 @@ extension Probe {
}


func updateWithAdvertising(_ advertising: AdvertisingData, RSSI: NSNumber) {
if(advertising.mode == .Normal) {
currentTemperatures = advertising.temperatures
}
else if(advertising.mode == .InstantRead ){
updateInstantRead(advertising.temperatures.values[0])
}
func updateWithAdvertising(_ advertising: AdvertisingData, isConnectable: Bool, RSSI: NSNumber) {
// Always update probe RSSI and isConnectable flag
self.rssi = RSSI.intValue
self.isConnectable = isConnectable

rssi = RSSI.intValue

id = advertising.id
color = advertising.color
batteryStatus = advertising.batteryStatus

lastUpdateTime = Date()
// Only update rest of data if not connected to probe. Otherwise, rely on status
// notifications to update data
if(connectionState != .connected)
{
if(advertising.mode == .Normal) {
currentTemperatures = advertising.temperatures
}
else if(advertising.mode == .InstantRead ){
updateInstantRead(advertising.temperatures.values[0])
}


id = advertising.id
color = advertising.color
batteryStatus = advertising.batteryStatus

lastUpdateTime = Date()
}
}

/// Updates the Device based on newly-received DeviceStatus message. Requests missing records.
Expand Down
4 changes: 2 additions & 2 deletions Sources/CombustionBLE/SimulatedProbe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class SimulatedProbe: Probe {
init() {
let advertising = AdvertisingData(fakeSerial: UInt32.random(in: 0 ..< UINT32_MAX),
fakeTemperatures: ProbeTemperatures.withRandomData())
super.init(advertising, RSSI: SimulatedProbe.randomeRSSI(), identifier: UUID())
super.init(advertising, isConnectable: true, RSSI: SimulatedProbe.randomeRSSI(), identifier: UUID())

firmareVersion = "v1.2.3"
hardwareRevision = "v0.31-A1"
Expand Down Expand Up @@ -64,7 +64,7 @@ class SimulatedProbe: Probe {
private func updateFakeAdvertising() {
let advertising = AdvertisingData(fakeSerial: UInt32.random(in: 0 ..< UINT32_MAX),
fakeTemperatures: ProbeTemperatures.withRandomData())
updateWithAdvertising(advertising, RSSI: SimulatedProbe.randomeRSSI())
updateWithAdvertising(advertising, isConnectable: true, RSSI: SimulatedProbe.randomeRSSI())
}

private func updateFakeStatus() {
Expand Down
18 changes: 5 additions & 13 deletions Sources/CombustionBLE/UART/LogRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,15 @@ SOFTWARE.
import Foundation

class LogRequest: Request {
static let PAYLOAD_LENGTH: UInt8 = 8

init(minSequence: UInt32, maxSequence: UInt32) {
super.init(payloadLength: LogRequest.PAYLOAD_LENGTH, type: .Log)
var payload = Data()

var min = minSequence
let minData = Data(bytes: &min, count: MemoryLayout.size(ofValue: min))
self.data[Request.HEADER_SIZE + 0] = minData[0]
self.data[Request.HEADER_SIZE + 1] = minData[1]
self.data[Request.HEADER_SIZE + 2] = minData[2]
self.data[Request.HEADER_SIZE + 3] = minData[3]
payload.append(Data(bytes: &min, count: MemoryLayout.size(ofValue: min)))

var max = maxSequence
let maxData = Data(bytes: &max, count: MemoryLayout.size(ofValue: max))
self.data[Request.HEADER_SIZE + 4] = maxData[0]
self.data[Request.HEADER_SIZE + 5] = maxData[1]
self.data[Request.HEADER_SIZE + 6] = maxData[2]
self.data[Request.HEADER_SIZE + 7] = maxData[3]
payload.append(Data(bytes: &max, count: MemoryLayout.size(ofValue: max)))

super.init(payload: payload, type: .Log)
}
}
Loading

0 comments on commit d92b6c8

Please sign in to comment.