Skip to content

Commit

Permalink
Merge pull request #378 from NordicSemiconductor/develop
Browse files Browse the repository at this point in the history
Version 4.8.0
  • Loading branch information
philips77 authored Jul 31, 2020
2 parents b0a042a + 710d9d9 commit fc7e0fb
Show file tree
Hide file tree
Showing 14 changed files with 153 additions and 50 deletions.
4 changes: 4 additions & 0 deletions Example/Pods/Pods.xcodeproj/project.pbxproj

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@ class DFUViewController: UIViewController, CBCentralManagerDelegate, DFUServiceD
dfuInitiator.delegate = self
dfuInitiator.progressDelegate = self
dfuInitiator.logger = self
dfuInitiator.dataObjectPreparationDelay = 0.4 // sec

// Uncomment if you don't want resume feature in Secure DFU.
// See: https://github.com/NordicSemiconductor/IOS-Pods-DFU-Library/pull/264
// dfuInitiator.disableResume = true

// Uncomment only if Legacy DFU is us using address+1 in bootloader mode.
// See: https://github.com/NordicSemiconductor/Android-DFU-Library/issues/262#issuecomment-665493850
// dfuInitiator.forceScanningForNewAddressInLegacyDfu = true

// Here would be a good chance to change the UUIDs to your custom UUIDs

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ let package = Package(
dependencies: [
.package(
url: "https://github.com/NordicSemiconductor/IOS-Pods-DFU-Library",
.upToNextMajor(from: "<Desired Version, e.g. 4.7.2>")
.upToNextMajor(from: "<Desired Version, e.g. 4.8.0>")
)
],
targets: [.target(name: "<Your Target Name>", dependencies: ["NordicDFU"])]
Expand Down
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
### Changelog
- **4.8.0**
- Feature: Option to force scanning for Legacy DFU bootloader after jumping using Buttonless Service (#374).
- Feature: Option to set connection timeout (#369).
- Feature: Option to set data object preparation delay (#377).

- **4.7.2**
- Improvement: Report error when bluetooth is turned off (#371).
- Bugfix: Fixed Carthage configuration (shared schemes) (#370).
Expand Down
2 changes: 1 addition & 1 deletion iOSDFULibrary.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "iOSDFULibrary"
s.version = "4.7.2"
s.version = "4.8.0"
s.summary = "This repository contains a tested library for iOS 9+ devices to perform Device Firmware Update on the nRF5x devices"
s.description = <<-DESC
The nRF5x Series chips are flash-based SoCs, and as such they represent the most flexible solution available. A key feature of the nRF5x Series and their associated software architecture and S-Series SoftDevices is the possibility for Over-The-Air Device Firmware Upgrade (OTA-DFU). See Figure 1. OTA-DFU allows firmware upgrades to be issued and downloaded to products in the field via the cloud and so enables OEMs to fix bugs and introduce new features to products that are already out on the market. This brings added security and flexibility to product development when using the nRF5x Series SoCs.
Expand Down
59 changes: 59 additions & 0 deletions iOSDFULibrary/Classes/Implementation/DFUServiceInitiator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,63 @@ import CoreBluetooth
*/
@objc public var forceDfu = false

/**
By default, the Legacy DFU bootloader starting from SDK 7.1, when enabled using
buttonless service, advertises with the same Bluetooth address as the application
using direct advertisement. This complies with the Bluetooth specification.
However, starting from iOS 13.x, iPhones and iPads use random addresses on each
connection and do not expect direct advertising unless bonded. This causes thiose
packets being missed and not reported to the library, making reconnection to the
bootloader and proceeding with DFU impossible.
A solution requires modifying either the bootloader not to use the direct advertising,
or the application not to share the peer data with bootloader, in which case it will
advertise undirectly using address +1, like it does when the switch to bootloader mode
is initiated with a button. After such modification, setting this flag to true will make the
library scan for the bootloader using `DFUPeripheralSelector`.
Setting this flag to true without modifying the booloader behavior will break the DFU,
as the direct advertising packets are empty and will not pass the default
`DFUPeripheralSelector`.
- since: 4.8.0
*/
@objc public var forceScanningForNewAddressInLegacyDfu = false

/**
Connection timeout.
When the DFU target does not connect before the time runs out, a timeout error
is reported.
- since: 4.8.0
*/
@objc public var connectionTimeout: TimeInterval = 10.0

/**
Duration of a delay, that the service will wait before sending each data object in
Secure DFU. The delay will be done after a data object is created, and before
any data byte is sent. The default value is 0, which disables this feature for the
second and following data objects, but the first one will be delayed by 0.4 sec.
It has been found, that a delay of at least 0.3 sec reduces the risk of packet lose
(the bootloader needs some time to prepare flash memory) on DFU bootloader from
SDK 15, 16 and 17. The delay does not have to be longer than 0.4 sec, as according to
performed tests, such delay is sufficient.
The longer the delay, the more time DFU will take to complete (delay will be repeated for
each data object (4096 bytes)). However, with too small delay a packet lose may occur,
causing the service to enable PRN and set them to 1 making DFU process very, very slow
(but reliable).
The recommended delay is from 0.3 to 0.4 second if your DFU bootloader is from
SDK 15, 16 or 17. Older bootloaders do not need this delay.
This variable is ignored in Legacy DFU.
- since: 4.8.0
*/
@objc public var dataObjectPreparationDelay: TimeInterval = 0.0

/**
In SDK 14.0.0 a new feature was added to the Buttonless DFU for non-bonded
devices which allows to send a unique name to the device before it is switched
Expand Down Expand Up @@ -243,6 +300,8 @@ import CoreBluetooth

/**
Disable the ability for the DFU process to resume from where it was.
- since: 4.3.0
*/
@objc public var disableResume: Bool = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,11 @@ internal class BaseDFUPeripheral<TD : BasePeripheralDelegate> : NSObject, BaseDF
/// A flag set when upload has been aborted.
fileprivate var aborted: Bool = false
/// Connection timer cancels connection attempt if the device doesn't
/// connect within 10 seconds.
/// connect before the time runs out.
private var connectionTimer: DispatchSourceTimer?
/// Connection timeout.
/// - since: 4.8.0
private let connectionTimeout: TimeInterval

init(_ initiator: DFUServiceInitiator, _ logger: LoggerHelper) {
self.centralManager = initiator.centralManager
Expand All @@ -122,6 +125,7 @@ internal class BaseDFUPeripheral<TD : BasePeripheralDelegate> : NSObject, BaseDF
self.logger = logger
self.experimentalButtonlessServiceInSecureDfuEnabled = initiator.enableUnsafeExperimentalButtonlessServiceInSecureDfu
self.uuidHelper = initiator.uuidHelper
self.connectionTimeout = initiator.connectionTimeout

super.init()
}
Expand Down Expand Up @@ -470,11 +474,12 @@ internal class BaseDFUPeripheral<TD : BasePeripheralDelegate> : NSObject, BaseDF
connectionTimer?.setEventHandler {
if let peripheral = self.peripheral {
self.connectionTimer?.cancel()
self.logger.w("Connection timeout!")
self.logger.d("centralManager.cancelPeripheralConnection(peripheral)")
self.centralManager.cancelPeripheralConnection(peripheral)
}
}
connectionTimer?.schedule(deadline: .now() + 10.0)
connectionTimer?.schedule(deadline: .now() + connectionTimeout)
connectionTimer?.resume()
logger.d("centralManager.connect(peripheral, options: nil)")
centralManager.connect(peripheral!, options: nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ internal class LegacyDFUExecutor : DFUExecutor, LegacyDFUPeripheralDelegate {
delegate {
$0.dfuStateDidChange(to: .enablingDfuMode)
}
peripheral.jumpToBootloader()
peripheral.jumpToBootloader(
forceNewAddress: initiator.forceScanningForNewAddressInLegacyDfu
)
} else {
// The device is ready to proceed with DFU.
peripheral.sendStartDfu(withFirmwareType: firmware.currentPartType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,13 @@ internal class LegacyDFUPeripheral : BaseCommonDFUPeripheral<LegacyDFUExecutor,

/**
Switches target device to the DFU Bootloader mode.
- parameter forceNewAddress: Set to true if the bootloader is expected to adveritse
with a different address than when in app mode.
*/
func jumpToBootloader() {
func jumpToBootloader(forceNewAddress: Bool) {
jumpingToBootloader = true
newAddressExpected = dfuService!.newAddressExpected
newAddressExpected = dfuService!.newAddressExpected || forceNewAddress
dfuService!.jumpToBootloaderMode(
// On success, the device gets disconnected and
// `centralManager(_:didDisconnectPeripheral:error)` will be called.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,10 @@ internal class SecureDFUExecutor : DFUExecutor, SecureDFUPeripheralDelegate {
logger.i("Data object \(currentRangeIdx + 1)/\(firmwareRanges!.count) created")
// For SDK 15.x and 16 the bootloader needs some time before it's ready to receive data.
// Otherwise, some packets may be discarded and the received checksum will not match.
if currentRangeIdx == 0 {
logger.d("wait(400)")
initiator.queue.asyncAfter(deadline: .now() + .milliseconds(400)) {
if currentRangeIdx == 0 || initiator.dataObjectPreparationDelay > 0 {
let delay = initiator.dataObjectPreparationDelay > 0 ? initiator.dataObjectPreparationDelay : 0.4
logger.d("wait(\(Int(delay * 1000))")
initiator.queue.asyncAfter(deadline: .now() + delay) {
self.sendDataObject(self.currentRangeIdx) // -> peripheralDidReceiveObject() will be called.
}
} else {
Expand Down

0 comments on commit fc7e0fb

Please sign in to comment.