Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added CLI apps for scanner and thermometer #138

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
106 changes: 106 additions & 0 deletions Health Thermometer/DeviceInformation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// DeviceInformation.swift
// Health Thermometer
//
// Created by Nick Kibysh on 25/01/2024.
// Copyright © 2024 Nordic Semiconductor. All rights reserved.
//

import CoreBluetooth
import Foundation
import iOS_BLE_Library
import iOS_Bluetooth_Numbers_Database

public struct DeviceInformation: CustomDebugStringConvertible {
public var manufacturerName: String?
public var modelNumber: String?
public var serialNumber: String?
public var hardwareRevision: String?
public var firmwareRevision: String?
public var softwareRevision: String?
public var systemID: String?
public var ieee11073: String?

public var debugDescription: String {
var s = ""
if let manufacturerName = manufacturerName {
s += "Manufacturer Name: \(manufacturerName)\n"
}
if let modelNumber = modelNumber {
s += "Model Number: \(modelNumber)\n"
}
if let serialNumber = serialNumber {
s += "Serial Number: \(serialNumber)\n"
}
if let hardwareRevision = hardwareRevision {
s += "Hardware Revision: \(hardwareRevision)\n"
}
if let firmwareRevision = firmwareRevision {
s += "Firmware Revision: \(firmwareRevision)\n"
}
if let softwareRevision = softwareRevision {
s += "Software Revision: \(softwareRevision)\n"
}
if let systemID = systemID {
s += "System ID: \(systemID)\n"
}
if let ieee11073 = ieee11073 {
s += "IEEE 11073: \(ieee11073)\n"
}
return s
}
}

public func readDeviceInformation(from service: CBService, peripheral: Peripheral) async throws -> DeviceInformation {
var di = DeviceInformation()

if let c = service.characteristics?.first(where: { $0.uuid == CBUUID(string: Characteristic.manufacturerNameString.uuidString) }) {
if let data = try await peripheral.readValue(for: c).firstValue {
di.manufacturerName = String(data: data, encoding: .utf8)
}
}

if let c = service.characteristics?.first(where: { $0.uuid == CBUUID(string: Characteristic.modelNumberString.uuidString) }) {
if let data = try await peripheral.readValue(for: c).firstValue {
di.modelNumber = String(data: data, encoding: .utf8)
}
}

if let c = service.characteristics?.first(where: { $0.uuid == CBUUID(string: Characteristic.serialNumberString.uuidString) }) {
if let data = try await peripheral.readValue(for: c).firstValue {
di.serialNumber = String(data: data, encoding: .utf8)
}
}

if let c = service.characteristics?.first(where: { $0.uuid == CBUUID(string: Characteristic.hardwareRevisionString.uuidString) }) {
if let data = try await peripheral.readValue(for: c).firstValue {
di.hardwareRevision = String(data: data, encoding: .utf8)
}
}

if let c = service.characteristics?.first(where: { $0.uuid == CBUUID(string: Characteristic.firmwareRevisionString.uuidString) }) {
if let data = try await peripheral.readValue(for: c).firstValue {
di.firmwareRevision = String(data: data, encoding: .utf8)
}
}

if let c = service.characteristics?.first(where: { $0.uuid == CBUUID(string: Characteristic.softwareRevisionString.uuidString) }) {
if let data = try await peripheral.readValue(for: c).firstValue {
di.softwareRevision = String(data: data, encoding: .utf8)
}
}

if let c = service.characteristics?.first(where: { $0.uuid == CBUUID(string: Characteristic.systemId.uuidString) }) {
if let data = try await peripheral.readValue(for: c).firstValue {
di.systemID = String(data: data, encoding: .utf8)
}
}

if let c = service.characteristics?.first(where: { $0.uuid == CBUUID(string: Characteristic.ieee11073_20601RegulatoryCertificationDataList.uuidString) }) {
if let data = try await peripheral.readValue(for: c).firstValue {
di.ieee11073 = String(data: data, encoding: .utf8)
}
}

return di
}
68 changes: 68 additions & 0 deletions Health Thermometer/TemperatureMeasurement.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// TemperatureMeasurement.swift
// Health Thermometer
//
// Created by Nick Kibysh on 25/01/2024.
// Copyright © 2024 Nordic Semiconductor. All rights reserved.
//

import Foundation

struct ReservedFloatValues {
static let positiveInfinity: UInt32 = 0x007FFFFE
static let nan: UInt32 = 0x007FFFFF
static let nres: UInt32 = 0x00800000
static let reserved: UInt32 = 0x00800001
static let negativeInfinity: UInt32 = 0x00800002

static let firstReservedValue = ReservedFloatValues.positiveInfinity
}


func read<R: FixedWidthInteger>(_ data: Data, fromOffset offset: Int = 0) -> R {
let length = MemoryLayout<R>.size
guard offset + length <= data.count else { fatalError() }
return data.subdata(in: offset ..< offset + length).withUnsafeBytes { $0.load(as: R.self) }
}

func readFloat(_ data: Data, from offset: Int = 0) -> Float {
let tempData: UInt32 = read(data, fromOffset: offset)
var mantissa = Int32(tempData & 0x00FFFFFF)
let exponent = Int8(bitPattern: UInt8(tempData >> 24))

var output : Float32 = 0

if mantissa >= 0x800000 {
mantissa = -((0xFFFFFF + 1) - mantissa)
}
let magnitude = pow(10.0, Double(exponent))
output = Float32(mantissa) * Float32(magnitude)

return output
}

struct TemperatureMeasurement: CustomDebugStringConvertible {
enum Unit {
case fahrenheit, celsius
}

var temperature: Double?
var timestamp: Date?
var unit: Unit

init(data: Data) {
let flags: UInt8 = data[0]
let fahrenheit = flags & 0x01 == 0x01

unit = fahrenheit ? .fahrenheit : .celsius
temperature = Double(readFloat(data, from: 1))
}

var debugDescription: String {
var s = ""
if let temperature = temperature {
s += "\(temperature) \(unit == .celsius ? "°C" : "°F")"
}
return s
}
}
57 changes: 57 additions & 0 deletions Health Thermometer/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// main.swift
// Health Thermometer
//
// Created by Nick Kibysh on 25/01/2024.
// Copyright © 2024 Nordic Semiconductor. All rights reserved.
//

import Foundation
import iOS_BLE_Library
import iOS_Bluetooth_Numbers_Database

/**
You can find example of the Health Thermometer peripheral here: TODO add link
*/

/**
To discover the UUID of the peripheral you want to connect to, run the app and copy the UUID from the console.
*/
let cbPeripheral = try await scanAndConnect(to: "")

let peripheral = Peripheral(peripheral: cbPeripheral)

let services = try await peripheral.discoverServices(serviceUUIDs: nil).firstValue

print("ATTRIBUTE TABLE")
for service in services {
guard let s = Service.find(by: service.uuid) else { continue }
print("|-- \(s.name)")

let characteristics = try await peripheral.discoverCharacteristics(nil, for: service).firstValue
for characteristic in characteristics {
guard let c = Characteristic.find(by: characteristic.uuid) else { continue }
print("|---- \(c.name)")

let descriptors = try await peripheral.discoverDescriptors(for: characteristic).firstValue
for descriptor in descriptors {
guard let d = Descriptor.find(by: descriptor.uuid) else { continue }
print("|------ \(d.name)")
}
}
}

print("DEVICE INFORMATION:")
let deviceInfoService = (peripheral.peripheral.services?.first(where: { $0.uuid.uuidString == Service.deviceInformation.uuidString }))!
let deviceInfo = try await readDeviceInformation(from: deviceInfoService, peripheral: peripheral)
print(deviceInfo)

print("TEMPERATURE")
let temperatureService = (peripheral.peripheral.services?.first(where: { $0.uuid.uuidString == Service.healthThermometer.uuidString }))!
let tmCharacteristic = (temperatureService.characteristics?.first(where: { $0.uuid.uuidString == Characteristic.temperatureMeasurement.uuidString }))!
_ = try await peripheral.setNotifyValue(true, for: tmCharacteristic).firstValue

for try await t in peripheral.listenValues(for: tmCharacteristic).values {
let temp = TemperatureMeasurement(data: t)
print(temp)
}
Loading