-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added Swift Package Manager information. Commented out a bunch of debug statements. * Added minimum iOS version for package. Also fixed several warnings. * Updated company name in copyright notices. * Added comments. * More documentation updates. * Incorporated Mac address calculation changes. * Updated naming of internal contents of package. * Another tweak to the package. * Marked classes public. * Marked additional classes public. * Marked a number of other classes and members public. * Marked additional fields public. * Marked accessor public. * Marked additional items public. * Marked additional class public. * Added some additional documentation. * Fixed more non-public items. * Added MIT license to header of all files. * Refactored DeviceManager to differentiate between Device and Probe. * Gave DeviceManager.probes getter/setter. * Marked a number of properties 'published' * Added framework documentation.
- Loading branch information
Showing
20 changed files
with
1,729 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,90 +1,14 @@ | ||
# Xcode | ||
# | ||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore | ||
|
||
## User settings | ||
.DS_Store | ||
/.build | ||
/Packages | ||
/*.xcodeproj | ||
xcuserdata/ | ||
|
||
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) | ||
*.xcscmblueprint | ||
*.xccheckout | ||
|
||
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) | ||
build/ | ||
DerivedData/ | ||
*.moved-aside | ||
*.pbxuser | ||
!default.pbxuser | ||
*.mode1v3 | ||
!default.mode1v3 | ||
*.mode2v3 | ||
!default.mode2v3 | ||
*.perspectivev3 | ||
!default.perspectivev3 | ||
|
||
## Obj-C/Swift specific | ||
*.hmap | ||
|
||
## App packaging | ||
*.ipa | ||
*.dSYM.zip | ||
*.dSYM | ||
|
||
## Playgrounds | ||
timeline.xctimeline | ||
playground.xcworkspace | ||
|
||
# Swift Package Manager | ||
# | ||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. | ||
# Packages/ | ||
# Package.pins | ||
# Package.resolved | ||
# *.xcodeproj | ||
# | ||
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata | ||
# hence it is not needed unless you have added a package configuration file to your project | ||
# .swiftpm | ||
|
||
.build/ | ||
|
||
# CocoaPods | ||
# | ||
# We recommend against adding the Pods directory to your .gitignore. However | ||
# you should judge for yourself, the pros and cons are mentioned at: | ||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control | ||
# | ||
# Pods/ | ||
# | ||
# Add this line if you want to avoid checking in source code from the Xcode workspace | ||
# *.xcworkspace | ||
|
||
# Carthage | ||
# | ||
# Add this line if you want to avoid checking in source code from Carthage dependencies. | ||
# Carthage/Checkouts | ||
|
||
Carthage/Build/ | ||
|
||
# Accio dependency management | ||
Dependencies/ | ||
.accio/ | ||
|
||
# fastlane | ||
# | ||
# It is recommended to not store the screenshots in the git repo. | ||
# Instead, use fastlane to re-generate the screenshots whenever they are needed. | ||
# For more information about the recommended setup visit: | ||
# https://docs.fastlane.tools/best-practices/source-control/#source-control | ||
|
||
fastlane/report.xml | ||
fastlane/Preview.html | ||
fastlane/screenshots/**/*.png | ||
fastlane/test_output | ||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata | ||
|
||
# Code Injection | ||
# | ||
# After new code Injection tools there's a generated folder /iOSInjectionProject | ||
# https://github.com/johnno1962/injectionforxcode | ||
# Swift package manager items | ||
Package.resolved | ||
|
||
iOSInjectionProject/ | ||
# Don't need this for this project as there are no shared | ||
# schemes for the library. | ||
.swiftpm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// swift-tools-version:5.5 | ||
// The swift-tools-version declares the minimum version of Swift required to build this package. | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "combustion-ios-ble", | ||
platforms: [ | ||
.iOS(.v13) | ||
], | ||
products: [ | ||
// Products define the executables and libraries a package produces, and make them visible to other packages. | ||
.library(name: "CombustionBLE", targets: ["CombustionBLE"]), | ||
], | ||
dependencies: [ | ||
// Dependencies declare other packages that this package depends on. | ||
.package(url: "https://github.com/apple/swift-collections", "1.0.0"..<"2.0.0") | ||
], | ||
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) | ||
], | ||
path: "Sources/CombustionBLE"), | ||
/* | ||
.testTarget( | ||
name: "combustion-ios-bleTests", | ||
dependencies: ["combustion-ios-ble"]), | ||
*/ | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,145 @@ | ||
# combustion-ios-ble | ||
Bluetooth Low Energy framework for communicating with Combustion, Inc. probe. | ||
# Combustion Inc. Temperature Probe BLE Library | ||
|
||
## Overview | ||
|
||
This package enables communication with Combustion Inc. temperature probes probes. It uses Apple's Combine framework, subclassing `ObservableObject` to enable reactive UI development in SwiftUI (and is compatible with Storyboard approaches as well). | ||
|
||
## Example project | ||
|
||
An example iOS app illustrating the use of this framework is available in the [combustion-ios-example](https://github.com/combustion-inc/combustion-ios-example) repository. | ||
|
||
## Usage information | ||
|
||
### Swift Package Manager | ||
|
||
Simply add this [Github repository](https://github.com/combustion-inc/combustion-ios-ble) to your project via [Swift Package Manager](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app). | ||
|
||
### Target capabilities | ||
|
||
The following Capabilities need to be added to your Target (Signing & Capabilities tab in Target settings): | ||
|
||
- Background BLE services | ||
- Enable `Uses Bluetooth LE Accessories` | ||
|
||
### Info.plist settings | ||
|
||
Additionally, the following entries must be added to your project's `Info.plist`: | ||
|
||
- Key: `Privacy - Bluetooth Always Usage Description` | ||
|
||
- Value: Description of reason for Bluetooth access, e.g. "Bluetooth is used to communicate with hardware products." | ||
|
||
- Key: `Privacy - Bluetooth Peripheral Usage Description` | ||
|
||
- Value: Description of reason for Bluetooth access, e.g. "Bluetooth is used to communicate with hardware products." | ||
|
||
## Important Classes | ||
|
||
The following classes provide key functionality to apps incorporating this framework. | ||
|
||
### `DeviceManager` | ||
|
||
`DeviceManager` is an observable singleton class that maintains a dictionary of `Probe` objects that have been discovered over BLE. | ||
|
||
#### Important members | ||
|
||
- `probes` - Observable dictionary of probes (key is a `String` BLE UUID identifier, value is the `Probe` object) | ||
- `getProbes()` - Function that returns array representation of the `Probe` objects in the `probes` dictionary. | ||
|
||
### `Probe` (subclass of `Device`) | ||
|
||
An instance of the `Probe` class representes an individual temperature probe that has been discovered via its advertising data. These are retrieved from the `DeviceManager.shared.probes` dictionary. | ||
|
||
### Important members | ||
|
||
- `serialNumber` - The Probe's unique serial number | ||
- `name` - String format of probe serial number | ||
- `macAddress` - Probe's MAC address | ||
- `macAddressString` - String representation of Probe's MAC address | ||
- `batteryLevel` - Battery level as reported by probe *NOTE: This is not yet implemented in probe firmware and will likely change to a boolean 'battery low' flag in the near future.* | ||
- `currentTemperatures` - `ProbeTemperatures` struct containing the most recent temperatures read by the Probe. | ||
- `currentTemperatures.values` - Array of these temperatures, in celsius, where `values[0]` is temperature sensor T1, and `values[7]` is temperature sensor T8. | ||
- T1 - High-precision temperature sensor in tip of probe | ||
- T2 - High-precision temperature sensor | ||
- T3 - MCU temperature sensor | ||
- T4 - High-precision temperature sensor | ||
- T5 - High-temperature thermistor | ||
- T6 - High-temperature thermistor | ||
- T7 - High-temperature thermistor | ||
- T8 - High-temperature thermistor on handle tip measuring ambient | ||
|
||
- `id` - iOS-provided UUID for the device | ||
|
||
- `rssi` - Signal strength between Probe and iOS device | ||
|
||
- `maintainingConnection` - Whether the app is currently attempting to maintain a connection with the `Probe`, as directed by the `connect()` and `disconnect()` methods. | ||
|
||
- `connect()` - Attempts to connect to device, and instructs framework to attempt to maintain a connection to this probe if it is lost. | ||
|
||
- `disconnect()` - Instruct framework to disconnect from this probe, and to no longer attempt to maintain a connection to it. | ||
|
||
- `stale` - `true` if no advertising data or notifications have been received from the Probe within the "staleness timeout" (15 seconds), or `false` if recent data has been received. | ||
|
||
- `status` - `DeviceStatus` struct containing device status information. | ||
- `minSequenceNumber` - Minimum sequence number of log records stored on the probe | ||
- `maxSequenceNumber` - Maximum sequence number of log records stored on the probe | ||
|
||
- `logsUpToDate` - Boolean value that indicates whether all log sequence numbers contained in the probe (determined by the `status` sequence number range) have been successfully retrieved and stored in the app's memory. | ||
|
||
- `temperatureLog` - `ProbeTemperatureLog` class instance containing all logged temperatures that have been retrieved from the device, and logic that coordinates automatically retrieving all past records when connected to a Probe. | ||
- Individual logged temperatures are provided in the `temperatureLog.dataPoints` array. These are instances of the struct `LoggedProbeDataPoint`, which contains the point's sequence number and corresponding `ProbeTemperatures` struct as explained above. | ||
|
||
## Useful functions | ||
|
||
The framework also provides `celsius()` and `fahrenheit()` functions that convert temperatures between these two formats. | ||
|
||
## Common usage examples | ||
|
||
### Importing this framework | ||
|
||
To use the Combustion BLE framework in your own Swift file, import it: | ||
|
||
```swift | ||
import CombustionBLE | ||
``` | ||
|
||
### Rendering list of probes | ||
|
||
In SwiftUI, a list of probes can be rendered like so: | ||
|
||
```swift | ||
struct EngineeringProbeList: View { | ||
@ObservedObject var deviceManager = DeviceManager.shared | ||
|
||
var body: some View { | ||
NavigationView { | ||
List { | ||
ForEach(deviceManager.probes.keys.sorted(), id: \.self) { key in | ||
if let probe = deviceManager.probes[key] { | ||
NavigationLink(destination: EngineeringProbeDetails(probe: probe)) { | ||
EngineeringProbeRow(probe: probe) | ||
} | ||
} | ||
} | ||
} | ||
.navigationTitle("Probes") | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Framework features coming soon | ||
|
||
The following features are planned for near-term development but are not yet implemented in this version of the Combustion BLE Framework. | ||
|
||
### Set ring color | ||
|
||
The framework will provide functions allowing a probe's identifying silicone ring color to be configured by the user (colors TBA). | ||
|
||
### Set numeric ID | ||
|
||
The framework will provide functions allowing a Probe's numeric ID (1-8) to be configured by the user. | ||
|
||
### Firmware update | ||
|
||
The framework will provide methods for updating a Probe's firmware with a signed firmware image. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// AdvertisingData.swift | ||
|
||
/*-- | ||
MIT License | ||
|
||
Copyright (c) 2021 Combustion Inc. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. | ||
--*/ | ||
|
||
import Foundation | ||
|
||
/// Enumeration of Combustion, Inc. product types. | ||
public enum CombustionProductType: UInt8 { | ||
case UNKNOWN = 0x00 | ||
case PROBE = 0x01 | ||
case NODE = 0x02 | ||
} | ||
|
||
|
||
/// Struct containing advertising data received from device. | ||
public struct AdvertisingData { | ||
/// Type of Combustion product | ||
public let type: CombustionProductType | ||
/// Product serial number | ||
public let serialNumber: UInt32 | ||
/// Latest temperatures read by device | ||
public let temperatures: ProbeTemperatures | ||
|
||
private enum Constants { | ||
// Locations of data in advertising packets | ||
static let PRODUCT_TYPE_RANGE = 2..<3 | ||
static let SERIAL_RANGE = 3..<7 | ||
static let TEMPERATURE_RANGE = 7..<20 | ||
} | ||
} | ||
|
||
extension AdvertisingData { | ||
init?(fromData : Data?) { | ||
guard let data = fromData else { return nil } | ||
guard data.count >= 19 else { return nil } | ||
|
||
// Product type (1 byte) | ||
let rawType = data.subdata(in: Constants.PRODUCT_TYPE_RANGE) | ||
let typeByte = [UInt8](rawType) | ||
type = CombustionProductType(rawValue: typeByte[0]) ?? .UNKNOWN | ||
|
||
// Device Serial number (4 bytes) | ||
// Reverse the byte order (this is a little-endian packed bitfield) | ||
let rawSerial = data.subdata(in: Constants.SERIAL_RANGE) | ||
var revSerial : [UInt8] = [] | ||
for byte in rawSerial { | ||
revSerial.insert(byte as UInt8, at: 0) | ||
} | ||
|
||
let serialArray = [UInt8](revSerial) | ||
var value: UInt32 = 0 | ||
for byte in serialArray { | ||
value = value << 8 | ||
value = value | UInt32(byte) | ||
} | ||
|
||
serialNumber = value | ||
|
||
|
||
// Temperatures (8 13-bit) values | ||
let tempData = data.subdata(in: Constants.TEMPERATURE_RANGE) | ||
temperatures = ProbeTemperatures.fromRawData(data: tempData) | ||
} | ||
} | ||
|
||
|
||
extension AdvertisingData { | ||
// Fake data initializer for previews | ||
public init?(fakeSerial: UInt32) { | ||
type = .PROBE | ||
temperatures = ProbeTemperatures.withFakeData() | ||
serialNumber = fakeSerial | ||
} | ||
} |
Oops, something went wrong.