connects to M5 stack
This commit is contained in:
parent
514e21f62b
commit
b44be99f83
|
@ -73,6 +73,14 @@
|
|||
F8A1586F22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1586E22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift */; };
|
||||
F8A1587122EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1587022EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift */; };
|
||||
F8A1587322EDC893007F5B5D /* ConstantsDexcomShare.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1587222EDC893007F5B5D /* ConstantsDexcomShare.swift */; };
|
||||
F8A389BA2315DC060010F405 /* M5StackBluetoothTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389B92315DC060010F405 /* M5StackBluetoothTransmitter.swift */; };
|
||||
F8A389BC231713580010F405 /* M5StackTransmitterOpCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389BB231713580010F405 /* M5StackTransmitterOpCode.swift */; };
|
||||
F8A389BF231DC8790010F405 /* M5StackAuthenticateTXMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389BE231DC8790010F405 /* M5StackAuthenticateTXMessage.swift */; };
|
||||
F8A389C3231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389C2231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift */; };
|
||||
F8A389C6231FBA110010F405 /* M5StackPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389C5231FBA110010F405 /* M5StackPacket.swift */; };
|
||||
F8A389C823203E3E0010F405 /* ConstantsM5Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389C723203E3E0010F405 /* ConstantsM5Stack.swift */; };
|
||||
F8A389CA232118E80010F405 /* M5StackDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389C9232118E80010F405 /* M5StackDelegate.swift */; };
|
||||
F8A389CC232191280010F405 /* M5StackUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389CB232191280010F405 /* M5StackUtilities.swift */; };
|
||||
F8A54AAD22D6859200934E7A /* SlopeParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AAC22D6859200934E7A /* SlopeParameters.swift */; };
|
||||
F8A54AAF22D686CD00934E7A /* NightScoutBgReading.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AAE22D686CD00934E7A /* NightScoutBgReading.swift */; };
|
||||
F8A54AB722D9111900934E7A /* CGMTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AB322D9111900934E7A /* CGMTransmitter.swift */; };
|
||||
|
@ -271,6 +279,14 @@
|
|||
F8A1586E22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsSuspensionPrevention.swift; sourceTree = "<group>"; };
|
||||
F8A1587022EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsSpeakReadingLanguages.swift; sourceTree = "<group>"; };
|
||||
F8A1587222EDC893007F5B5D /* ConstantsDexcomShare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsDexcomShare.swift; sourceTree = "<group>"; };
|
||||
F8A389B92315DC060010F405 /* M5StackBluetoothTransmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackBluetoothTransmitter.swift; sourceTree = "<group>"; };
|
||||
F8A389BB231713580010F405 /* M5StackTransmitterOpCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackTransmitterOpCode.swift; sourceTree = "<group>"; };
|
||||
F8A389BE231DC8790010F405 /* M5StackAuthenticateTXMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackAuthenticateTXMessage.swift; sourceTree = "<group>"; };
|
||||
F8A389C2231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackReadBlePassWordTxMessage.swift; sourceTree = "<group>"; };
|
||||
F8A389C5231FBA110010F405 /* M5StackPacket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackPacket.swift; sourceTree = "<group>"; };
|
||||
F8A389C723203E3E0010F405 /* ConstantsM5Stack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsM5Stack.swift; sourceTree = "<group>"; };
|
||||
F8A389C9232118E80010F405 /* M5StackDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackDelegate.swift; sourceTree = "<group>"; };
|
||||
F8A389CB232191280010F405 /* M5StackUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackUtilities.swift; sourceTree = "<group>"; };
|
||||
F8A54AAC22D6859200934E7A /* SlopeParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SlopeParameters.swift; sourceTree = "<group>"; };
|
||||
F8A54AAE22D686CD00934E7A /* NightScoutBgReading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightScoutBgReading.swift; sourceTree = "<group>"; };
|
||||
F8A54AB322D9111900934E7A /* CGMTransmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMTransmitter.swift; sourceTree = "<group>"; };
|
||||
|
@ -771,6 +787,7 @@
|
|||
F85DC2FC21D3E83100B9F74A /* Transmitter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F8A389B82315DB080010F405 /* M5Stack */,
|
||||
F8025E5521F3418400ECF0C0 /* CGMBluetoothTransmitter */,
|
||||
F85DC2FF21D3E83100B9F74A /* GenericBluetoothTransmitter */,
|
||||
);
|
||||
|
@ -786,6 +803,36 @@
|
|||
path = GenericBluetoothTransmitter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F8A389B82315DB080010F405 /* M5Stack */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F8A389C4231FB9B30010F405 /* Utilities */,
|
||||
F8A389BD231DC7DC0010F405 /* M5StackMessages */,
|
||||
F8A389B92315DC060010F405 /* M5StackBluetoothTransmitter.swift */,
|
||||
F8A389BB231713580010F405 /* M5StackTransmitterOpCode.swift */,
|
||||
);
|
||||
path = M5Stack;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F8A389BD231DC7DC0010F405 /* M5StackMessages */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F8A389BE231DC8790010F405 /* M5StackAuthenticateTXMessage.swift */,
|
||||
F8A389C2231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift */,
|
||||
);
|
||||
path = M5StackMessages;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F8A389C4231FB9B30010F405 /* Utilities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F8A389C5231FBA110010F405 /* M5StackPacket.swift */,
|
||||
F8A389C9232118E80010F405 /* M5StackDelegate.swift */,
|
||||
F8A389CB232191280010F405 /* M5StackUtilities.swift */,
|
||||
);
|
||||
path = Utilities;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F8A54AB222D9111900934E7A /* Generic */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1157,6 +1204,7 @@
|
|||
F8A1587222EDC893007F5B5D /* ConstantsDexcomShare.swift */,
|
||||
F856CE5A22EDC8E50083E436 /* ConstantsBluetoothPairing.swift */,
|
||||
F8EEDD5122FECE3800D2D610 /* ConstantsLibreOOP.swift */,
|
||||
F8A389C723203E3E0010F405 /* ConstantsM5Stack.swift */,
|
||||
);
|
||||
name = Constants;
|
||||
path = xdrip/Constants;
|
||||
|
@ -1390,6 +1438,7 @@
|
|||
F8A54ADF22D911BA00934E7A /* DexcomTransmitterOpCode.swift in Sources */,
|
||||
F81D6D4E22BFC762005EFAE2 /* TextsDexcomShareTestResult.swift in Sources */,
|
||||
F8A54AE922D911BA00934E7A /* AuthChallengeRxMessage.swift in Sources */,
|
||||
F8A389BA2315DC060010F405 /* M5StackBluetoothTransmitter.swift in Sources */,
|
||||
F8A54B0122D9179100934E7A /* LibreSensorState.swift in Sources */,
|
||||
F8EA6C8221B723BC0082976B /* Date.swift in Sources */,
|
||||
F8A54AE622D911BA00934E7A /* KeepAliveTxMessage.swift in Sources */,
|
||||
|
@ -1427,6 +1476,7 @@
|
|||
F81D6D4822BD5F62005EFAE2 /* DexcomShareUploadManager.swift in Sources */,
|
||||
F8A7406E22D9C0E700967CFC /* CGMBluconTransmitter.swift in Sources */,
|
||||
F8A1586B22EDB967007F5B5D /* ConstantsMaster.swift in Sources */,
|
||||
F8A389C3231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift in Sources */,
|
||||
F8B3A7B2226A0878004BA588 /* TextsAlerts.swift in Sources */,
|
||||
F8A54B0022D9179100934E7A /* LibreDataParser.swift in Sources */,
|
||||
F8025E5421EE8D2100ECF0C0 /* Libre1Calibrator.swift in Sources */,
|
||||
|
@ -1470,14 +1520,17 @@
|
|||
F8B3A81C227DEC92004BA588 /* AlertEntriesAccessor.swift in Sources */,
|
||||
F8A1585B22EDB7EA007F5B5D /* ConstantsDexcomG5.swift in Sources */,
|
||||
F8BDD452221DEAB2006EAB84 /* TextsSettingsView.swift in Sources */,
|
||||
F8A389BF231DC8790010F405 /* M5StackAuthenticateTXMessage.swift in Sources */,
|
||||
F8EEDD6423020FAD00D2D610 /* NoCalibrator.swift in Sources */,
|
||||
F8EEDD5422FF685400D2D610 /* NSMutableURLRequest.swift in Sources */,
|
||||
F897AAFB2201018800CDDD10 /* String.swift in Sources */,
|
||||
F8A389CA232118E80010F405 /* M5StackDelegate.swift in Sources */,
|
||||
F8B3A847227F090E004BA588 /* SettingsViewNightScoutSettingsViewModel.swift in Sources */,
|
||||
F8B3A79622635A25004BA588 /* AlertEntry+CoreDataClass.swift in Sources */,
|
||||
F8AC425E21ADEBD60078C348 /* AppDelegate.swift in Sources */,
|
||||
F821CF8E22AB090C005C1E43 /* DatePickerViewController.swift in Sources */,
|
||||
F8A1585322EDB602007F5B5D /* ConstantsBloodGlucose.swift in Sources */,
|
||||
F8A389C823203E3E0010F405 /* ConstantsM5Stack.swift in Sources */,
|
||||
F8A54AE322D911BA00934E7A /* AuthRequestRxMessage.swift in Sources */,
|
||||
F8A54AD722D911BA00934E7A /* CGMG6Transmitter.swift in Sources */,
|
||||
F81F9FF822861E6D0028C70F /* KeyValueObserverTimeKeeper.swift in Sources */,
|
||||
|
@ -1508,6 +1561,7 @@
|
|||
F821CF5F229BF43A005C1E43 /* ApplicationManager.swift in Sources */,
|
||||
F8B3A834227F08AC004BA588 /* PickerViewData.swift in Sources */,
|
||||
F8B3A79522635A25004BA588 /* AlertType+CoreDataProperties.swift in Sources */,
|
||||
F8A389BC231713580010F405 /* M5StackTransmitterOpCode.swift in Sources */,
|
||||
F8B3A84C227F090E004BA588 /* SettingsViewController.swift in Sources */,
|
||||
F8EEDD5222FECE3800D2D610 /* ConstantsLibreOOP.swift in Sources */,
|
||||
F8AC426021ADEBD60078C348 /* RootViewController.swift in Sources */,
|
||||
|
@ -1520,9 +1574,11 @@
|
|||
F8B3A7DF226E48C1004BA588 /* SoundPlayer.swift in Sources */,
|
||||
F8B3A820227DEC92004BA588 /* AlertTypesAccessor.swift in Sources */,
|
||||
F8B3A81E227DEC92004BA588 /* BgReadingsAccessor.swift in Sources */,
|
||||
F8A389C6231FBA110010F405 /* M5StackPacket.swift in Sources */,
|
||||
F8B3A846227F090E004BA588 /* SettingsViewTransmitterSettingsViewModel.swift in Sources */,
|
||||
F821CF6B229FC22D005C1E43 /* Endpoint.swift in Sources */,
|
||||
F821CF58229BF43A005C1E43 /* AlertManager.swift in Sources */,
|
||||
F8A389CC232191280010F405 /* M5StackUtilities.swift in Sources */,
|
||||
F8025C1121DA5E8F00ECF0C0 /* BluetoothTransmitterDelegate.swift in Sources */,
|
||||
F8A54AD922D911BA00934E7A /* TransmitterVersionTxMessage.swift in Sources */,
|
||||
F821CF5E229BF43A005C1E43 /* BgReading+NightScout.swift in Sources */,
|
||||
|
|
|
@ -51,6 +51,9 @@ enum ConstantsLog {
|
|||
/// droplet 1
|
||||
static let categoryCGMDroplet1 = "categoryCGMDroplet1"
|
||||
/// LibreOOPClient
|
||||
static let categoryLibreOOPClient = "LibreOOPClient"
|
||||
static let categoryLibreOOPClient = "categoryLibreOOPClient"
|
||||
/// for use in M5Stack
|
||||
static let categoryM5StackBluetoothTransmitter = "categoryM5StackBluetoothTransmitter"
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import Foundation
|
||||
|
||||
enum ConstantsM5Stack {
|
||||
|
||||
/// maximum time in milliseconds, between two packets
|
||||
///
|
||||
/// this is for the case where M5Stack sends a string to the app, split over multiple packets. There's maximum 17 bytes of useful information per packet. The packets should arrive within a predefined timeframe, otherwise it is to be considered as an error
|
||||
static let maximumTimeBetweenTwoPacketsInMs = 200
|
||||
|
||||
/// maximum BLE packet size
|
||||
static let maximumMBLEPacketsize = 20
|
||||
}
|
|
@ -58,7 +58,7 @@ enum ConstantsSounds: String, CaseIterable {
|
|||
return String(forSound.rawValue[soundNameRange])
|
||||
}
|
||||
|
||||
/// gets the soundFie for specific case
|
||||
/// gets the soundFile for specific case
|
||||
static func getSoundFile(forSound:ConstantsSounds) -> String {
|
||||
let indexOfBackSlash = forSound.rawValue.indexes(of: "/")
|
||||
let soundNameRange = forSound.rawValue.index(after: indexOfBackSlash[0])..<forSound.rawValue.endIndex
|
||||
|
|
|
@ -10,11 +10,12 @@ extension Date {
|
|||
return Double(self.timeIntervalSince1970 * 1000)
|
||||
}
|
||||
|
||||
/// returns Date in milliseconds as Int64
|
||||
/// returns Date in milliseconds as Int64, since 1.1.1970
|
||||
func toMillisecondsAsInt64() -> Int64 {
|
||||
return Int64((self.timeIntervalSince1970 * 1000.0).rounded())
|
||||
}
|
||||
|
||||
/// returns Date in seconds as Int64, since 1.1.1970
|
||||
func toSecondsAsInt64() -> Int64 {
|
||||
return Int64((self.timeIntervalSince1970).rounded())
|
||||
}
|
||||
|
@ -46,4 +47,9 @@ extension Date {
|
|||
return dateFormatter.string(from: self).appending("Z")
|
||||
}
|
||||
|
||||
/// returns seconds since 1.1.1970 local time for current timezone
|
||||
func toSecondsAsInt64Local() -> Int64 {
|
||||
let calendar = Calendar.current
|
||||
return (Date().toSecondsAsInt64() + Int64(calendar.timeZone.secondsFromGMT()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,11 @@ extension UserDefaults {
|
|||
/// license info accepted by user yes or no
|
||||
case licenseInfoAccepted = "licenseInfoAccepted"
|
||||
|
||||
// M5Stack
|
||||
|
||||
/// M5Stack blepassword, needed for authenticating App to M5Stack
|
||||
case M5StackBlePassword = "M5StackBlePassword"
|
||||
|
||||
// Other Settings (not user configurable)
|
||||
|
||||
// Bluetooth
|
||||
|
@ -459,7 +464,7 @@ extension UserDefaults {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - ===== Keep track of alert and info messages shown to the user ======
|
||||
// MARK: - Keep track of alert and info messages shown to the user
|
||||
|
||||
/// message shown when user starts a sensor, which tells that timing should be exact, was it already shown or not
|
||||
var startSensorTimeInfoGiven:Bool {
|
||||
|
@ -481,6 +486,18 @@ extension UserDefaults {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: M5Stack
|
||||
|
||||
/// M5StackBlePassword, used for authenticating xdrip app towards M5Stack
|
||||
var m5StackBlePassword: String? {
|
||||
get {
|
||||
return string(forKey: Key.M5StackBlePassword.rawValue)
|
||||
}
|
||||
set {
|
||||
set(newValue, forKey: Key.M5StackBlePassword.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ===== Other Settings ======
|
||||
|
||||
var bluetoothDeviceAddress: String? {
|
||||
|
|
|
@ -28,12 +28,13 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
|
|||
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
|
||||
/// - transmitterID: expected transmitterID, 5 characters
|
||||
init(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
|
||||
init(address:String?, name: String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
|
||||
// assign addressname and name or expected devicename
|
||||
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: nil)
|
||||
if let address = address {
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
|
||||
}
|
||||
|
||||
//assign CGMTransmitterDelegate
|
||||
|
|
|
@ -85,9 +85,6 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
/// is G5 reset necessary or not
|
||||
private var G5ResetRequested:Bool
|
||||
|
||||
// actual device address
|
||||
private var actualDeviceAddress:String?
|
||||
|
||||
/// used as parameter in call to cgmTransmitterDelegate.cgmTransmitterInfoReceived, when there's no glucosedata to send
|
||||
var emptyArray: [GlucoseData] = []
|
||||
|
||||
|
@ -101,8 +98,9 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
|
||||
/// - transmitterID: expected transmitterID, 6 characters
|
||||
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
|
||||
init?(address:String?, name: String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
|
||||
//verify if transmitterid is 6 chars and allowed chars
|
||||
guard transmitterID.count == 6 else {
|
||||
trace("transmitterID length not 6, init CGMG5Transmitter fails", log: log, type: .error)
|
||||
|
@ -119,8 +117,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
// assign addressname and name or expected devicename
|
||||
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "DEXCOM" + transmitterID[transmitterID.index(transmitterID.startIndex, offsetBy: 4)..<transmitterID.endIndex])
|
||||
if let address = address {
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
|
||||
actualDeviceAddress = address
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
|
||||
}
|
||||
|
||||
// set timestampoflastg5reading to 0
|
||||
|
@ -282,7 +279,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
// if status changed to poweredon, and if address = nil then superclass will not start the scanning
|
||||
// but for DexcomG5 we can start scanning
|
||||
if state == .poweredOn {
|
||||
if (actualDeviceAddress == nil) {
|
||||
if (address() == nil) {
|
||||
_ = startScanning()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,10 @@ class CGMG6Transmitter: CGMG5Transmitter {
|
|||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - transmitterID: expected transmitterID, 6 characters
|
||||
override init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
|
||||
override init?(address:String?, name: String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
|
||||
|
||||
// call super.init
|
||||
super.init(address: address, transmitterID: transmitterID, delegate: delegate)
|
||||
super.init(address: address, name: name, transmitterID: transmitterID, delegate: delegate)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -24,9 +24,6 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
/// for trace
|
||||
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryBlucon)
|
||||
|
||||
// actual device address
|
||||
private var actualDeviceAddress:String?
|
||||
|
||||
// used in parsing packet
|
||||
private var timeStampLastBgReading:Date
|
||||
|
||||
|
@ -70,18 +67,18 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - name: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - transmitterID: expected transmitterID
|
||||
/// - delegate : CGMTransmitterDelegate
|
||||
/// - sensorSerialNumber : is needed to allow detection of a new sensor.
|
||||
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?) {
|
||||
init?(address:String?, name: String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?) {
|
||||
|
||||
// assign addressname and name or expected devicename
|
||||
// start by using expected device name
|
||||
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: CGMBluconTransmitter.createExpectedDeviceName(transmitterIdSetByUser: transmitterID))
|
||||
if let address = address {
|
||||
// address not nil, means it already connected before, use that address
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
|
||||
actualDeviceAddress = address
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
|
||||
}
|
||||
|
||||
// initialize timeStampLastBgReading
|
||||
|
|
|
@ -56,17 +56,18 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
|
|||
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
|
||||
/// - delegate : CGMTransmitterDelegate intance
|
||||
/// - timeStampLastBgReading : timestamp of last bgReading
|
||||
/// - webOOPEnabled : enabled or not
|
||||
/// - oopWebSite : oop web site url to use, only used in case webOOPEnabled = true
|
||||
/// - oopWebToken : oop web token to use, only used in case webOOPEnabled = true
|
||||
init(address:String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String) {
|
||||
init(address:String?, name: String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String) {
|
||||
|
||||
// assign addressname and name or expected devicename
|
||||
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: expectedDeviceNameBubble)
|
||||
if let address = address {
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
|
||||
}
|
||||
|
||||
// initialize sensorSerialNumber
|
||||
|
|
|
@ -27,12 +27,13 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
// MARK: - Initialization
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
init(address:String?, delegate:CGMTransmitterDelegate) {
|
||||
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
|
||||
init(address:String?, name: String?, delegate:CGMTransmitterDelegate) {
|
||||
|
||||
// assign addressname and name or expected devicename
|
||||
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "limitter")
|
||||
if let address = address {
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
|
||||
}
|
||||
|
||||
// assign CGMTransmitterDelegate
|
||||
|
|
|
@ -92,12 +92,13 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
init?(address:String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date) {
|
||||
/// - name: if already connected before, then give here the name that was received during previous connect, if not give nil
|
||||
init?(address:String?, name: String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date) {
|
||||
|
||||
// assign addressname and name or expected devicename
|
||||
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "GNSentry")
|
||||
if let address = address {
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
|
||||
}
|
||||
|
||||
//initialize timeStampLastBgReading
|
||||
|
|
|
@ -55,17 +55,18 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
// MARK: - Initialization
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
|
||||
/// - delegate : CGMTransmitterDelegate intance
|
||||
/// - timeStampLastBgReading : timestamp of last bgReading
|
||||
/// - webOOPEnabled : enabled or not
|
||||
/// - oopWebSite : oop web site url to use, only used in case webOOPEnabled = true
|
||||
/// - oopWebToken : oop web token to use, only used in case webOOPEnabled = true
|
||||
init(address:String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String) {
|
||||
init(address:String?, name: String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String) {
|
||||
|
||||
// assign addressname and name or expected devicename
|
||||
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: expectedDeviceNameMiaoMiao)
|
||||
if let address = address {
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
|
||||
}
|
||||
|
||||
// assign CGMTransmitterDelegate
|
||||
|
@ -93,7 +94,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
|
||||
// MARK: - public functions
|
||||
|
||||
func sendStartReadingCommmand() -> Bool {
|
||||
func sendStartReadingCommand() -> Bool {
|
||||
if writeDataToPeripheral(data: Data.init([0xF0]), type: .withoutResponse) {
|
||||
return true
|
||||
} else {
|
||||
|
@ -121,7 +122,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
|
||||
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
|
||||
if error == nil && characteristic.isNotifying {
|
||||
_ = sendStartReadingCommmand()
|
||||
_ = sendStartReadingCommand()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,7 +169,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
resendPacketCounter = temp + 1
|
||||
if resendPacketCounter < maxPacketResendRequests {
|
||||
trace("in peripheral didUpdateValueFor, crc error encountered. New attempt launched", log: log, type: .info)
|
||||
_ = sendStartReadingCommmand()
|
||||
_ = sendStartReadingCommand()
|
||||
} else {
|
||||
trace("in peripheral didUpdateValueFor, crc error encountered. Maximum nr of attempts reached", log: log, type: .info)
|
||||
resendPacketCounter = 0
|
||||
|
@ -189,7 +190,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
if self.writeDataToPeripheral(data: Data.init([0xD3, 0x01]), type: .withoutResponse) {
|
||||
trace("in peripheralDidUpdateValueFor, successfully sent 0xD3 and 0x01, confirm sensor change to MiaoMiao", log: self.log, type: .info)
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(500)) {
|
||||
if !self.sendStartReadingCommmand() {
|
||||
if !self.sendStartReadingCommand() {
|
||||
trace("in peripheralDidUpdateValueFor, sendStartReadingCommmand failed", log: self.log, type: .error)
|
||||
} else {
|
||||
trace("in peripheralDidUpdateValueFor, successfully sent startReadingCommand to MiaoMiao", log: self.log, type: .info)
|
||||
|
@ -245,7 +246,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
|
||||
// immediately request a new reading
|
||||
// there's no check here to see if peripheral, characteristic, connection, etc.. exists, but that's no issue. If anything's missing, write will simply fail,
|
||||
_ = sendStartReadingCommmand()
|
||||
_ = sendStartReadingCommand()
|
||||
}
|
||||
|
||||
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {
|
||||
|
|
|
@ -14,6 +14,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
|
||||
/// the address of the transmitter. If nil then transmitter never connected, so we don't know the name.
|
||||
private var deviceAddress:String?
|
||||
|
||||
/// the name of the transmitter. If nil then transmitter never connected, so we don't know the name
|
||||
private var deviceName:String?
|
||||
|
||||
|
@ -74,11 +75,16 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
/// - CBUUID_ReceiveCharacteristic: receive characteristic uuid
|
||||
/// - CBUUID_WriteCharacteristic: write characteristic uuid
|
||||
init(addressAndName:BluetoothTransmitter.DeviceAddressAndName, CBUUID_Advertisement:String?, servicesCBUUIDs:[CBUUID], CBUUID_ReceiveCharacteristic:String, CBUUID_WriteCharacteristic:String, startScanningAfterInit:Bool) {
|
||||
|
||||
switch addressAndName {
|
||||
case .alreadyConnectedBefore(let newAddress):
|
||||
deviceAddress = newAddress
|
||||
|
||||
case .alreadyConnectedBefore(let addressAndName):
|
||||
deviceAddress = addressAndName.address
|
||||
deviceName = addressAndName.name
|
||||
|
||||
case .notYetConnected(let newexpectedName):
|
||||
expectedName = newexpectedName
|
||||
|
||||
}
|
||||
|
||||
//assign uuid's
|
||||
|
@ -118,7 +124,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
func disconnect() {
|
||||
if let peripheral = peripheral {
|
||||
if let centralManager = centralManager {
|
||||
trace("disconnect, disconnecting", log: log, type: .info)
|
||||
trace("disconnect, disconnecting, for peripheral with name %{public}@", log: log, type: .info, deviceName ?? "'unknown'")
|
||||
centralManager.cancelPeripheralConnection(peripheral)
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +132,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
|
||||
/// start bluetooth scanning for device
|
||||
func startScanning() -> BluetoothTransmitter.startScanningResult {
|
||||
|
||||
trace("in startScanning", log: log, type: .info)
|
||||
|
||||
//assign default returnvalue
|
||||
|
@ -182,11 +189,11 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
/// - returns: true if writeValue was successfully called, doesn't necessarily mean data is successvully written to peripheral
|
||||
func writeDataToPeripheral(data:Data, type:CBCharacteristicWriteType) -> Bool {
|
||||
if let peripheral = peripheral, let writeCharacteristic = writeCharacteristic {
|
||||
trace("in writeDataToPeripheral", log: log, type: .info)
|
||||
trace("in writeDataToPeripheral, for peripheral with name %{public}@, characteristic = %{public}@, data = %{public}@", log: log, type: .info, deviceName ?? "'unknown'", writeCharacteristic.uuid.uuidString, data.hexEncodedString())
|
||||
peripheral.writeValue(data, for: writeCharacteristic, type: type)
|
||||
return true
|
||||
} else {
|
||||
trace("in writeDataToPeripheral, failed because either peripheral or writeCharacteristic is nil", log: log, type: .error)
|
||||
trace("in writeDataToPeripheral, for peripheral with name %{public}@, failed because either peripheral or writeCharacteristic is nil", log: log, type: .error, deviceName ?? "'unknown'")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -195,11 +202,11 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
/// - returns: true if writeValue was successfully called, doesn't necessarily mean data is successvully written to peripheral
|
||||
func writeDataToPeripheral(data:Data, characteristicToWriteTo:CBCharacteristic, type:CBCharacteristicWriteType) -> Bool {
|
||||
if let peripheral = peripheral {
|
||||
trace("in writeDataToPeripheral for characteristic %{public}@", log: log, type: .info, characteristicToWriteTo.uuid.description)
|
||||
trace("in writeDataToPeripheral, for peripheral with name %{public}@, for characteristic %{public}@", log: log, type: .info, deviceName ?? "'unknown'", characteristicToWriteTo.uuid.description)
|
||||
peripheral.writeValue(data, for: characteristicToWriteTo, type: type)
|
||||
return true
|
||||
} else {
|
||||
trace("in writeDataToPeripheral, failed because either peripheral or characteristicToWriteTo is nil", log: log, type: .error)
|
||||
trace("in writeDataToPeripheral, for peripheral with name %{public}@, failed because either peripheral or characteristicToWriteTo is nil", log: log, type: .error, deviceName ?? "'unknown'")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -207,10 +214,10 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
/// calls setNotifyValue for characteristic with value enabled
|
||||
func setNotifyValue(_ enabled: Bool, for characteristic: CBCharacteristic) {
|
||||
if let peripheral = peripheral {
|
||||
trace("setNotifyValue, setting notify for characteristic %{public}@, to %{public}@", log: log, type: .info, characteristic.uuid.uuidString, enabled.description)
|
||||
trace("setNotifyValue, for peripheral with name %{public}@, setting notify for characteristic %{public}@, to %{public}@", log: log, type: .info, deviceName ?? "'unknown'", characteristic.uuid.uuidString, enabled.description)
|
||||
peripheral.setNotifyValue(enabled, for: characteristic)
|
||||
} else {
|
||||
trace("setNotifyValue, failed to set notify for characteristic %{public}@, to %{public}@", log: log, type: .error, characteristic.uuid.uuidString, enabled.description)
|
||||
trace("setNotifyValue, for peripheral with name %{public}@, failed to set notify for characteristic %{public}@, to %{public}@", log: log, type: .error, deviceName ?? "'unknown'", characteristic.uuid.uuidString, enabled.description)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,24 +250,23 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
/// the result of the attempt to try to find such device, is returned
|
||||
fileprivate func retrievePeripherals(_ central:CBCentralManager) -> Bool {
|
||||
if let deviceAddress = deviceAddress {
|
||||
debuglogging("in retrievePeripherals, deviceaddress is not nil")
|
||||
trace("in retrievePeripherals, deviceaddress is %{public}@", log: log, type: .info, deviceAddress)
|
||||
if let uuid = UUID(uuidString: deviceAddress) {
|
||||
debuglogging(" in retrievePeripherals, uuid is not nil")
|
||||
trace(" uuid is not nil", log: log, type: .info)
|
||||
var peripheralArr = central.retrievePeripherals(withIdentifiers: [uuid])
|
||||
if peripheralArr.count > 0 {
|
||||
peripheral = peripheralArr[0]
|
||||
if let peripheral = peripheral {
|
||||
debuglogging(" in retrievePeripherals, peripheral is not nil")
|
||||
trace("in retrievePeripherals, trying to connect", log: log, type: .info)
|
||||
trace(" trying to connect", log: log, type: .info)
|
||||
peripheral.delegate = self
|
||||
central.connect(peripheral, options: nil)
|
||||
return true
|
||||
} else {
|
||||
debuglogging(" in retrievePeripherals, peripheral isfstart nil")
|
||||
trace(" peripheral is nil", log: log, type: .info)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debuglogging(" in retrievePeripherals, uuid is nil")
|
||||
trace(" uuid is nil", log: log, type: .info)
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@ -309,7 +315,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
|
||||
timeStampLastStatusUpdate = Date()
|
||||
|
||||
trace("connected, will discover services", log: log, type: .info)
|
||||
trace("connected, for peripheral with name %{public}@, will discover services", log: log, type: .info, deviceName ?? "'unknown'")
|
||||
peripheral.discoverServices(servicesCBUUIDs)
|
||||
|
||||
bluetoothTransmitterDelegate?.centralManagerDidConnect(address: deviceAddress, name: deviceName)
|
||||
|
@ -319,9 +325,9 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
timeStampLastStatusUpdate = Date()
|
||||
|
||||
if let error = error {
|
||||
trace("failed to connect with error: %{public}@, will try again", log: log, type: .error , error.localizedDescription)
|
||||
trace("failed to connect, for peripheral with name %{public}@, with error: %{public}@, will try again", log: log, type: .error , deviceName ?? "'unknown'", error.localizedDescription)
|
||||
} else {
|
||||
trace("failed to connect, will try again", log: log, type: .error)
|
||||
trace("failed to connect, for peripheral with name %{public}@, will try again", log: log, type: .error, deviceName ?? "'unknown'")
|
||||
}
|
||||
|
||||
centralManager?.connect(peripheral, options: nil)
|
||||
|
@ -332,7 +338,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
||||
timeStampLastStatusUpdate = Date()
|
||||
|
||||
trace("in centralManagerDidUpdateState, new state is %{public}@", log: log, type: .info, "\(central.state.toString())")
|
||||
trace("in centralManagerDidUpdateState, for peripheral with name %{public}@, new state is %{public}@", log: log, type: .info, deviceName ?? "'unknown'", "\(central.state.toString())")
|
||||
|
||||
/// in case status changed to powered on and if device address known then try either to retrieveperipherals, or if that doesn't succeed, start scanning
|
||||
if central.state == .poweredOn, reconnectAfterDisconnect {
|
||||
|
@ -351,12 +357,12 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
timeStampLastStatusUpdate = Date()
|
||||
|
||||
if let error = error {
|
||||
trace("Did disconnect peripheral with error: %{public}@", log: log, type: .error , error.localizedDescription)
|
||||
trace("Did disconnect peripheral with name %{public}@, with error: %{public}@", log: log, type: .error , deviceName ?? "'unknown'", error.localizedDescription)
|
||||
}
|
||||
|
||||
// check if automatic reconnect is needed or not
|
||||
if !reconnectAfterDisconnect {
|
||||
trace("reconnectAfterDisconnect is false, will not try to reconnect", log: log, type: .info)
|
||||
trace(" reconnectAfterDisconnect is false, will not try to reconnect", log: log, type: .info)
|
||||
}
|
||||
|
||||
// if self.peripheral == nil, then a manual disconnect or something like that has occured, no need to reconnect
|
||||
|
@ -374,7 +380,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
||||
timeStampLastStatusUpdate = Date()
|
||||
|
||||
trace("didDiscoverServices", log: log, type: .info)
|
||||
trace("didDiscoverServices for peripheral with name %{public}@", log: log, type: .info, deviceName ?? "'unknown'")
|
||||
if let error = error {
|
||||
trace(" didDiscoverServices error: %{public}@", log: log, type: .error , "\(error.localizedDescription)")
|
||||
}
|
||||
|
@ -392,7 +398,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
|
||||
timeStampLastStatusUpdate = Date()
|
||||
|
||||
trace("didDiscoverCharacteristicsFor for service with uuid %{public}@", log: log, type: .info, String(describing:service.uuid))
|
||||
trace("didDiscoverCharacteristicsFor for peripheral with name %{public}@, for service with uuid %{public}@", log: log, type: .info, deviceName ?? "'unknown'", String(describing:service.uuid))
|
||||
|
||||
if let error = error {
|
||||
trace(" didDiscoverCharacteristicsFor error: %{public}@", log: log, type: .error , error.localizedDescription)
|
||||
|
@ -430,7 +436,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
timeStampLastStatusUpdate = Date()
|
||||
|
||||
if let error = error {
|
||||
trace("didUpdateNotificationStateFor characteristic %{public}@, characteristic description %{public}@, error = %{public}@", log: log, type: .error, String(describing: characteristic.uuid), String(characteristic.debugDescription), error.localizedDescription)
|
||||
trace("didUpdateNotificationStateFor for peripheral with name %{public}@, characteristic %{public}@, characteristic description %{public}@, error = %{public}@", log: log, type: .error, deviceName ?? "'unkonwn'", String(describing: characteristic.uuid), String(characteristic.debugDescription), error.localizedDescription)
|
||||
}
|
||||
|
||||
// call delegate
|
||||
|
@ -442,7 +448,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
timeStampLastStatusUpdate = Date()
|
||||
|
||||
if let error = error {
|
||||
trace("didUpdateValueFor characteristic %{public}@, characteristic description %{public}@, error = %{public}@, no further processing", log: log, type: .error, String(describing: characteristic.uuid), String(characteristic.debugDescription), error.localizedDescription)
|
||||
trace("didUpdateValueFor for peripheral with name %{public}@, characteristic %{public}@, characteristic description %{public}@, error = %{public}@, no further processing", log: log, type: .error, deviceName ?? "'unknown'", String(describing: characteristic.uuid), String(characteristic.debugDescription), error.localizedDescription)
|
||||
} else {
|
||||
bluetoothTransmitterDelegate?.peripheralDidUpdateValueFor(characteristic: characteristic, error: error)
|
||||
}
|
||||
|
@ -498,6 +504,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
return "connecting"
|
||||
case .other(let reason):
|
||||
return "other reason : " + reason
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -507,12 +514,17 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
/// * For an xDrip bridge, we don't expect a specific devicename, in which case the value stays nil
|
||||
/// * If we already connected to a device before, then we know it's address
|
||||
enum DeviceAddressAndName {
|
||||
/// we already connected to the device so we should know the address
|
||||
case alreadyConnectedBefore (address:String)
|
||||
|
||||
/// we already connected to the device so we should know the address and name
|
||||
///
|
||||
/// name kept optional, for in case it was not stored
|
||||
case alreadyConnectedBefore (address:String, name:String?)
|
||||
|
||||
/// * We never connected to the device, so we don't know it's name and address as the Device itself is going to send. We can only have an expected name,
|
||||
/// * example for G5, if transmitter id is ABCDEF, we expect as devicename DexcomEF.
|
||||
/// * For an xDrip bridge, we don't expect a specific devicename, in which case the value stays nil
|
||||
case notYetConnected (expectedName:String?)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,257 @@
|
|||
import Foundation
|
||||
import CoreBluetooth
|
||||
import os
|
||||
|
||||
/// characteristic is write and notify, not read. To get data from the M5Stack, xdrip app will write a specific opcode, then M5Stack will reply and the reply will arrive as notify. So we don't use .withResponse
|
||||
final class M5StackBluetoothTransmitter: BluetoothTransmitter, BluetoothTransmitterDelegate {
|
||||
|
||||
// MARK: - private properties
|
||||
|
||||
/// service to be discovered
|
||||
private let CBUUID_Service: String = "AF6E5F78-706A-43FB-B1F4-C27D7D5C762F"
|
||||
|
||||
/// transmit and receive characteristic
|
||||
private let CBUUID_TxRxCharacteristic: String = "6D810E9F-0983-4030-BDA7-C7C9A6A19C1C"
|
||||
|
||||
/// for trace
|
||||
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryM5StackBluetoothTransmitter)
|
||||
|
||||
/// blepassword, used for authenticating xdrip app towards M5Stack
|
||||
private var blePassword: String?
|
||||
|
||||
/// temporary storage for packages received from M5Stack, with the password in it
|
||||
private var blePasswordM5StackPacket = M5StackPacket()
|
||||
|
||||
/// will be used to pass data
|
||||
private(set) weak var m5StackDelegate:M5StackDelegate?
|
||||
|
||||
|
||||
// MARK: - initializer
|
||||
|
||||
/// - parameters:
|
||||
/// - address : if already conneted before then it should be known
|
||||
/// - name : if already conneted before then it should be known
|
||||
init(address:String?, name: String?, delegate:M5StackDelegate, blePassword: String?) {
|
||||
|
||||
// assign addressname and name or expected devicename
|
||||
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "M5_NightscoutMon")
|
||||
if let address = address {
|
||||
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
|
||||
}
|
||||
|
||||
self.blePassword = blePassword
|
||||
|
||||
self.m5StackDelegate = delegate
|
||||
|
||||
// call super
|
||||
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service)], CBUUID_ReceiveCharacteristic: CBUUID_TxRxCharacteristic, CBUUID_WriteCharacteristic: CBUUID_TxRxCharacteristic, startScanningAfterInit: false)
|
||||
|
||||
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
|
||||
bluetoothTransmitterDelegate = self
|
||||
|
||||
}
|
||||
|
||||
// MARK: public functions
|
||||
|
||||
/// writes calculated Value, timestamp and slopeName of the reading as string to M5Stack
|
||||
/// - reading will be rounded to whole number
|
||||
/// - timestamp will be sent as long in seconds since 1.1.1970, UTC
|
||||
/// - slopeName is literally copied
|
||||
/// reading and timestamp are written in one packet to the M5stack, seperated by blanc, slopeName is written in seperate packet
|
||||
/// - parameters:
|
||||
/// - readingValue : the value in mgdl
|
||||
/// - timeStamp : timestamp of the reading
|
||||
func writeBgReadingInfo(bgReading: BgReading) {
|
||||
|
||||
// create packets to send slopeName
|
||||
guard let packetWithSlopeName = M5StackUtilities.splitTextInBLEPackets(text: bgReading.slopeName, maxBytesInOneBLEPacket: ConstantsM5Stack.maximumMBLEPacketsize, opCode: M5StackTransmitterOpCodeTx.writeSlopeNameTx.rawValue) else {
|
||||
trace("in writeBgReading, failed to create packets to send slopeName", log: log, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
// send slopename
|
||||
for packet in packetWithSlopeName {
|
||||
if !writeDataToPeripheral(data: packet, type: .withoutResponse) {
|
||||
trace("in writeBgReading, failed to send packet with slopename", log: log, type: .error)
|
||||
} else {
|
||||
trace("successfully written slopename to M5Stack", log: log, type: .error)
|
||||
}
|
||||
}
|
||||
|
||||
// create text to send, reading value, one blanc, timestamp in seconds
|
||||
let textToSend = Int(round(bgReading.calculatedValue)).description + " " + bgReading.timeStamp.toSecondsAsInt64().description
|
||||
|
||||
// create packets to send reading and timestamp
|
||||
guard let packetsWithReadingAndTimestamp = M5StackUtilities.splitTextInBLEPackets(text: textToSend, maxBytesInOneBLEPacket: ConstantsM5Stack.maximumMBLEPacketsize, opCode: M5StackTransmitterOpCodeTx.bgReadingTx.rawValue) else {
|
||||
trace("in writeBgReading, failed to create packets to send reading and timestamp", log: log, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
// send reading and timestamp
|
||||
for packet in packetsWithReadingAndTimestamp {
|
||||
if !writeDataToPeripheral(data: packet, type: .withoutResponse) {
|
||||
trace("in writeBgReading, failed to send packet with reading and timestamp", log: log, type: .error)
|
||||
} else {
|
||||
trace("successfully written reading and timestamp to M5Stack", log: log, type: .error)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - BluetoothTransmitterDelegate functions
|
||||
|
||||
func centralManagerDidConnect(address: String?, name: String?) {
|
||||
debuglogging("TODO M5StackBluetoothTransmitter centralManagerDidConnect")
|
||||
}
|
||||
|
||||
func centralManagerDidFailToConnect(error: Error?) {
|
||||
debuglogging("TODO M5StackBluetoothTransmitter centralManagerDidFailToConnect")
|
||||
}
|
||||
|
||||
func centralManagerDidUpdateState(state: CBManagerState) {
|
||||
debuglogging("TODO M5StackBluetoothTransmitter centralManagerDidUpdateState, new state = " + state.toString())
|
||||
}
|
||||
|
||||
func centralManagerDidDisconnectPeripheral(error: Error?) {
|
||||
debuglogging("TODO M5StackBluetoothTransmitter centralcentralManagerDidDisconnectPeripheralManagerDidConnect")
|
||||
}
|
||||
|
||||
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
|
||||
|
||||
// check if subscribe to notifications succeeded
|
||||
if characteristic.isNotifying {
|
||||
|
||||
// time to send password to M5Stack, if there isn't one known in the settings then we assume that the user didn't set a password in the M5Stack config (M5NS.INI) and that we didn't connect to this M5Stack yet after the last M5Stack restart. So in that case we will request a random password
|
||||
if let blePassword = blePassword, let authenticateTxMessageData = M5StackAuthenticateTXMessage(password: blePassword).data {
|
||||
|
||||
if !writeDataToPeripheral(data: authenticateTxMessageData, type: .withoutResponse) {
|
||||
trace("failed to send authenticateTx to M5Stack", log: log, type: .error)
|
||||
} else {
|
||||
trace("successfully sent authenticateTx to M5Stack", log: log, type: .error)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if !writeDataToPeripheral(data: M5StackReadBlePassWordTxMessage().data, type: .withoutResponse) {
|
||||
trace("failed to send readBlePassWordTx to M5Stack", log: log, type: .error)
|
||||
} else {
|
||||
trace("successfully sent readBlePassWordTx to M5Stack", log: log, type: .error)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
trace("in peripheralDidUpdateNotificationStateFor, failed to subscribe for characteristic %{public}@", log: log, type: .error, characteristic.uuid.description)
|
||||
}
|
||||
}
|
||||
|
||||
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
|
||||
|
||||
//check if value is not nil
|
||||
guard let value = characteristic.value else {
|
||||
trace("in peripheral didUpdateValueFor, characteristic.value is nil", log: log, type: .info)
|
||||
return
|
||||
}
|
||||
|
||||
//only for logging
|
||||
let data = value.hexEncodedString()
|
||||
trace("in peripheral didUpdateValueFor, data = %{public}@", log: log, type: .debug, data)
|
||||
|
||||
// value length should be at least 1
|
||||
guard value.count > 0 else {
|
||||
trace(" value length is 0, no further processing", log: log, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
guard let opCode = M5StackTransmitterOpCodeRx(rawValue: value[0]) else {
|
||||
trace(" failed to create opCode", log: log, type: .error)
|
||||
return
|
||||
}
|
||||
trace(" opcode = %{public}@", log: log, type: .info, opCode.description)
|
||||
|
||||
switch opCode {
|
||||
|
||||
case .readBlePassWordRx:
|
||||
|
||||
blePasswordM5StackPacket.addNewPacket(value: value)
|
||||
|
||||
if let newBlePassword = blePasswordM5StackPacket.getText() {
|
||||
|
||||
self.blePassword = newBlePassword
|
||||
m5StackDelegate?.newBlePassWord(newBlePassword: newBlePassword)
|
||||
|
||||
sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack()
|
||||
}
|
||||
|
||||
case .authenticateSuccessRx:
|
||||
|
||||
m5StackDelegate?.authentication(success: true)
|
||||
|
||||
// even though not requested, and even if M5Stack may already have it, send the local time
|
||||
sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack()
|
||||
|
||||
case .authenticateFailureRx:
|
||||
m5StackDelegate?.authentication(success: false)
|
||||
|
||||
case .readBlePassWordError1Rx:
|
||||
m5StackDelegate?.blePasswordMissingInSettings()
|
||||
|
||||
case .readBlePassWordError2Rx:
|
||||
m5StackDelegate?.m5StackResetRequired()
|
||||
|
||||
case .readTimeStampRx:
|
||||
sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: helper functions
|
||||
|
||||
/// sends local time in seconds since 1.1.1970 and also timeoffset from UTC in seconds. to M5Stack
|
||||
///
|
||||
/// local time = UTC time + offset /// UTC time = local time - offset
|
||||
private func sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack() {
|
||||
|
||||
// NOTE : why creating multiple packets (splitTextInBLEPackets) even though it will all fit in one packet ?
|
||||
// goal is to be able to send longer strings, to M5 Stack. Then we could create one string which contains the timestamp and offset (split by blanc). But then for sure there will be multiple packets, and I didn't yet develop the logic to reconcatenate strings in the M5Stack
|
||||
|
||||
// create local time in seconds as Int64
|
||||
let localTimeInSeconds = Date().toSecondsAsInt64Local()
|
||||
|
||||
// create offset from UTC
|
||||
let uTCTimeInSeconds = Date().toSecondsAsInt64()
|
||||
let offSet = localTimeInSeconds - uTCTimeInSeconds
|
||||
|
||||
// create packets to send with offset
|
||||
guard let packetsWithOffset = M5StackUtilities.splitTextInBLEPackets(text: offSet.description, maxBytesInOneBLEPacket: ConstantsM5Stack.maximumMBLEPacketsize, opCode: M5StackTransmitterOpCodeTx.writeTimeOffsetTx.rawValue) else {
|
||||
trace(" failed to create packets for sending offset", log: log, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
// send the packets with offset
|
||||
for packet in packetsWithOffset {
|
||||
if !writeDataToPeripheral(data: packet, type: .withoutResponse) {
|
||||
trace(" failed to send packet with offset to M5Stack", log: log, type: .error)
|
||||
} else {
|
||||
trace(" successfully sent packet with offset to M5Stack", log: log, type: .error)
|
||||
}
|
||||
}
|
||||
|
||||
// create packets to send with local time
|
||||
guard let packetsWithLocalTime = M5StackUtilities.splitTextInBLEPackets(text: localTimeInSeconds.description, maxBytesInOneBLEPacket: ConstantsM5Stack.maximumMBLEPacketsize, opCode: M5StackTransmitterOpCodeTx.writeTimeStampTx.rawValue) else {
|
||||
trace(" failed to create packets for sending timestamp", log: log, type: .error)
|
||||
return
|
||||
}
|
||||
|
||||
// send packets with localtime
|
||||
for packet in packetsWithLocalTime {
|
||||
if !writeDataToPeripheral(data: packet, type: .withoutResponse) {
|
||||
trace(" failed to send packet with local timestamp to M5Stack", log: log, type: .error)
|
||||
} else {
|
||||
trace(" successfully sent packet with local timestamp to M5Stack", log: log, type: .error)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import Foundation
|
||||
|
||||
struct M5StackAuthenticateTXMessage {
|
||||
|
||||
let password: String
|
||||
|
||||
var data: Data? {
|
||||
|
||||
if password.count == 0 {return nil}
|
||||
|
||||
var data = Data([M5StackTransmitterOpCodeTx.authenticateTx.rawValue])
|
||||
|
||||
if let passwordAsData = password.data(using: .utf8) {
|
||||
data.append(passwordAsData)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import Foundation
|
||||
|
||||
struct M5StackReadBlePassWordTxMessage {
|
||||
|
||||
var data: Data {
|
||||
|
||||
let data = Data([M5StackTransmitterOpCodeTx.readBlePassWordTx.rawValue])
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
import Foundation
|
||||
|
||||
/// opcodes for writing to M5Stack
|
||||
enum M5StackTransmitterOpCodeTx: UInt8, CaseIterable {
|
||||
|
||||
/// client writes nightScouturl
|
||||
case writeNightScoutUrlTx = 0x01
|
||||
|
||||
/// client writes nightScoutToken
|
||||
case writeNightScoutTokenTx = 0x02
|
||||
|
||||
/// client writes mgdl, value 0 means mmol is used, value 1 is mgdl
|
||||
case writemgdlTx = 0x03
|
||||
|
||||
/// client writes brightness1, value between 1 and 100
|
||||
case writebrightness1Tx = 0x04
|
||||
|
||||
/// client writes brightness2, value between 1 and 100
|
||||
case writebrightness2Tx = 0x05
|
||||
|
||||
/// client writes brightness3, value between 1 and 100
|
||||
case writebrightness3Tx = 0x06
|
||||
|
||||
/// client writes wlan ssid, 2nd byte in data is used to indicate which wlan (from 1 to 10), next bytes is ssid as string
|
||||
case writeWlanSSIDTx = 0x07
|
||||
|
||||
/// client writes wlan Pass, 2nd byte in data is used to indicate which wlan (from 1 to 10), next bytes is Pass as string
|
||||
case writeWlanPassTx = 0x08
|
||||
|
||||
/// opcode to request the password
|
||||
///
|
||||
/// xdrip should only read the password if it doesn't have a password in the settings - M5Stack will send back the password only if it generated a random password during connection, not if it was set in the config ini file. In the latter case, the user should know it and set it in the app settings
|
||||
case readBlePassWordTx = 0x09
|
||||
|
||||
/// authenticate
|
||||
case authenticateTx = 0x0A
|
||||
|
||||
/// bg reading
|
||||
case bgReadingTx = 0x10
|
||||
|
||||
/// writes local time in seconds , seconds since 1.1.1970
|
||||
case writeTimeStampTx = 0x12
|
||||
|
||||
/// write slopeName to M5Stack
|
||||
case writeSlopeNameTx = 0x13
|
||||
|
||||
/// write offset, ie time difference in seconds between local time and utc time
|
||||
case writeTimeOffsetTx = 0x14
|
||||
}
|
||||
|
||||
/// opcodes for message from M5stack to app
|
||||
enum M5StackTransmitterOpCodeRx: UInt8, CaseIterable {
|
||||
|
||||
/// opcode to receive the password
|
||||
///
|
||||
/// xdrip should only read the password if it doesn't have a password in the settings - M5Stack will send back the password only if it generated a random password during first time connection, not if it was set in the config ini file. In the latter case, the user should know it and set it in the app settings
|
||||
case readBlePassWordRx = 0x0E
|
||||
|
||||
/// authenticate
|
||||
case authenticateSuccessRx = 0x0B
|
||||
|
||||
/// authenticate
|
||||
case authenticateFailureRx = 0x0C
|
||||
|
||||
/// readBlePassword failed, error 1 : User should set correct password in settings
|
||||
case readBlePassWordError1Rx = 0x0D
|
||||
|
||||
/// readBlePassword failed, error 2 : Password already known, user should reset M5Stack
|
||||
case readBlePassWordError2Rx = 0x0F
|
||||
|
||||
/// M5Stack requests timestamp in seconds, local time since 1.1.1970 !!
|
||||
case readTimeStampRx = 0x11
|
||||
|
||||
}
|
||||
|
||||
extension M5StackTransmitterOpCodeRx: CustomStringConvertible {
|
||||
public var description: String {
|
||||
|
||||
switch self {
|
||||
case .readBlePassWordRx:
|
||||
return "readBlePassWordRx"
|
||||
case .authenticateSuccessRx:
|
||||
return "authenticateSuccessRx"
|
||||
case .authenticateFailureRx:
|
||||
return "authenticateFailureRx"
|
||||
case .readBlePassWordError1Rx:
|
||||
return "readBlePassWordError1Rx"
|
||||
case .readBlePassWordError2Rx:
|
||||
return "readBlePassWordError2Rx"
|
||||
case .readTimeStampRx:
|
||||
return "readTimeStampRx"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import Foundation
|
||||
|
||||
protocol M5StackDelegate: AnyObject {
|
||||
|
||||
/// if a new ble password is received from M5Stack
|
||||
func newBlePassWord(newBlePassword: String)
|
||||
|
||||
/// did the app successfully authenticate towards M5Stack
|
||||
///
|
||||
/// in case of failure, then user should set the correct password in the M5Stack ini file, or, in case there's no password set in the ini file, switch off and on the M5Stack
|
||||
func authentication(success: Bool)
|
||||
|
||||
/// there's no ble password set, user should set it in the settings
|
||||
func blePasswordMissingInSettings()
|
||||
|
||||
/// it's an M5Stack without password configired in the ini file. xdrip app has been requesting temp password to M5Stack but this was already done once. M5Stack needs to be reset
|
||||
func m5StackResetRequired()
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
import Foundation
|
||||
|
||||
/// M5Stack may send strings in several packets. This class is there to compose to packets and allows to read the resulting string
|
||||
/// - byte 1 is the total number of packets that must be received before the string is complete
|
||||
/// - byte 2 is the number of the current packet being added
|
||||
/// - the remaining bytes represent the string
|
||||
/// if all packets are received, then the string can be retrieved with the text function
|
||||
class M5StackPacket {
|
||||
|
||||
// MARK: private properties
|
||||
|
||||
/// keep track of when last packet was received, if more than x milliseconds, then all data is reset
|
||||
private var timeStampLastPacket: Date?
|
||||
|
||||
/// total number of packets to receive
|
||||
private var totalNumberOfPacketsToReceive: Int?
|
||||
|
||||
/// the packets already received, only if the number of packets equals totalNumberOfPacketsToReceive then we've received all packets
|
||||
private var packetsReceived: [Data] = []
|
||||
|
||||
// MARK: public functions
|
||||
|
||||
/// gets the text, only returns a value of all packets were received
|
||||
func getText() -> String? {
|
||||
|
||||
// check that private properties are initialised
|
||||
guard timeStampLastPacket != nil, let totalNumberOfPacketsToReceive = totalNumberOfPacketsToReceive, packetsReceived.count > 0 else {return nil}
|
||||
|
||||
// check if all packets were received
|
||||
for i in 1...totalNumberOfPacketsToReceive {
|
||||
if packetsReceived[i - 1].count == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// read the string first as data
|
||||
var result = Data()
|
||||
for i in 1...totalNumberOfPacketsToReceive {
|
||||
|
||||
result.append(packetsReceived[i - 1])
|
||||
|
||||
}
|
||||
|
||||
// return the result
|
||||
return String(bytes: result, encoding: .utf8)
|
||||
|
||||
}
|
||||
|
||||
/// to be used when new packet received from M5Stack, value is the complete value received from M5Stack, inclusive opcode.
|
||||
///
|
||||
/// - byte 1 is the total number of packets that must be received before the string is complete
|
||||
/// - byte 2 is the number of the current packet being added
|
||||
/// - the remaining bytes represent the string
|
||||
/// if all packets are received, then the string can be retrieved with the text function
|
||||
func addNewPacket(value: Data) {
|
||||
|
||||
// length should be at least 3 bytes, otherwise just ignore
|
||||
if value.count < 3 {return}
|
||||
|
||||
// if more than 200 ms ago since last packet, then reset all properties, looks like a new
|
||||
if let timeStampLastPacket = timeStampLastPacket, Date().toMillisecondsAsInt64() - timeStampLastPacket.toMillisecondsAsInt64() > ConstantsM5Stack.maximumTimeBetweenTwoPacketsInMs {
|
||||
|
||||
resetAllProperties()
|
||||
}
|
||||
|
||||
// set timeStampLastPacket to now
|
||||
timeStampLastPacket = Date()
|
||||
|
||||
totalNumberOfPacketsToReceive = Int(value.uint8(position: 2))
|
||||
guard let totalNumberOfPacketsToReceive = totalNumberOfPacketsToReceive else {return}
|
||||
|
||||
// check packetsReceived, possibly still empty which means this would be the first packet being received
|
||||
if packetsReceived.count == 0 {
|
||||
// want to have an array of Data, size totalNumberOfPacketsToReceive, actual values will be set as we receive the packets, first one being just a few lines of code later
|
||||
for _ in 1...totalNumberOfPacketsToReceive {
|
||||
packetsReceived.append(Data())
|
||||
}
|
||||
}
|
||||
|
||||
// get number of this packet
|
||||
let numberOfThisPacket = Int(value.uint8(position: 1))
|
||||
guard numberOfThisPacket <= totalNumberOfPacketsToReceive else {return}// would be a coding error
|
||||
|
||||
// assign data
|
||||
packetsReceived[numberOfThisPacket - 1] = value.subdata(in: 3..<value.count)
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: private functions
|
||||
|
||||
/// sets all properties to nil
|
||||
private func resetAllProperties() {
|
||||
timeStampLastPacket = nil
|
||||
packetsReceived = []
|
||||
totalNumberOfPacketsToReceive = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import Foundation
|
||||
|
||||
class M5StackUtilities {
|
||||
|
||||
/// for sending text string to a ble peripheral, when text can be longer than maximum packet size.
|
||||
/// - parameters:
|
||||
/// - text : text that needs to be sent to peripheral
|
||||
/// - maxBytesInOneBLEPacket : maximum size of text in one packet, usually 20
|
||||
/// - opCode : opCode will be literally copied to first byte
|
||||
/// - returns:
|
||||
/// - array of data, each element is 1st byte opCode, 2nd byte total number of packets, 3rd byte the number of the packet, then the text utf8 encoded. returns nil if splitting fails, which probably means text could not be converted to data using .utf8
|
||||
static func splitTextInBLEPackets(text: String, maxBytesInOneBLEPacket: Int, opCode: UInt8) -> [Data]? {
|
||||
|
||||
guard let textAsData = text.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let sizeOfTextToSend = textAsData.count
|
||||
|
||||
var charactersAdded = 0
|
||||
|
||||
var totalNumberOfPacketsToSent = sizeOfTextToSend/(maxBytesInOneBLEPacket - 3)
|
||||
if sizeOfTextToSend > totalNumberOfPacketsToSent * (maxBytesInOneBLEPacket - 3) {
|
||||
totalNumberOfPacketsToSent += 1
|
||||
}
|
||||
|
||||
var returnValue = [Data]()
|
||||
|
||||
// number of the next packet to create
|
||||
var numberOfNextPacketToSend = 1
|
||||
|
||||
while charactersAdded < sizeOfTextToSend {
|
||||
|
||||
// calculate size of packet to send
|
||||
var sizeOfNextPacketToSend = maxBytesInOneBLEPacket
|
||||
if numberOfNextPacketToSend == totalNumberOfPacketsToSent {
|
||||
sizeOfNextPacketToSend = 3 + (sizeOfTextToSend - charactersAdded)//First byte = opcode, second byte is packet number, third byte = total number of packets, rest is content
|
||||
}
|
||||
|
||||
var dataToAppend = Data()
|
||||
dataToAppend.append(opCode)
|
||||
dataToAppend.append(UInt8(numberOfNextPacketToSend))
|
||||
dataToAppend.append(UInt8(totalNumberOfPacketsToSent))
|
||||
for i in 0..<(sizeOfNextPacketToSend - 3) {
|
||||
dataToAppend.append(textAsData[charactersAdded + i])
|
||||
}
|
||||
|
||||
returnValue.append(dataToAppend)
|
||||
|
||||
// increase charactersSent
|
||||
charactersAdded = charactersAdded + (sizeOfNextPacketToSend - 3);
|
||||
|
||||
// increase numberOfNextPacketToSend
|
||||
numberOfNextPacketToSend += 1
|
||||
|
||||
}
|
||||
|
||||
return returnValue
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -44,6 +44,9 @@ final class RootViewController: UIViewController {
|
|||
/// outlet for label that shows the current reading
|
||||
@IBOutlet weak var valueLabelOutlet: UILabel!
|
||||
|
||||
/// M5StackBluetoothTransmitter for testing
|
||||
var m5StackBluetoothTransmitter:M5StackBluetoothTransmitter?
|
||||
|
||||
// MARK: - Constants for ApplicationManager usage
|
||||
|
||||
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground - create updatelabelstimer
|
||||
|
@ -130,8 +133,7 @@ final class RootViewController: UIViewController {
|
|||
super.viewDidLoad()
|
||||
|
||||
// Setup Core Data Manager - setting up coreDataManager happens asynchronously
|
||||
// completion handler is called when finished. This gives the app time to already continue setup which is independent of coredata, like setting up the transmitter, start scanning
|
||||
// In the exceptional case that the transmitter would give a new reading before the DataManager is set up, then this new reading will be ignored
|
||||
// completion handler is called when finished. This gives the app time to already continue setup which is independent of coredata, like initializing the views
|
||||
coreDataManager = CoreDataManager(modelName: ConstantsCoreData.modelName, completion: {
|
||||
|
||||
self.setupApplicationData()
|
||||
|
@ -142,6 +144,8 @@ final class RootViewController: UIViewController {
|
|||
// create transmitter based on UserDefaults
|
||||
self.initializeCGMTransmitter()
|
||||
|
||||
self.m5StackBluetoothTransmitter = M5StackBluetoothTransmitter(address: nil, name: "M5_NightscoutMon", delegate: self, blePassword: nil)
|
||||
|
||||
})
|
||||
|
||||
// Setup View
|
||||
|
@ -272,10 +276,6 @@ final class RootViewController: UIViewController {
|
|||
/// - sensorTimeInMinutes : should be present only if it's the first reading(s) being processed for a specific sensor and is needed if it's a transmitterType that returns true to the function canDetectNewSensor
|
||||
private func processNewGlucoseData(glucoseData: inout [GlucoseData], sensorTimeInMinutes: Int?) {
|
||||
|
||||
for glucose in glucoseData {
|
||||
debuglogging(glucose.description)
|
||||
}
|
||||
|
||||
// check that calibrations and coredata manager is not nil
|
||||
guard let calibrationsAccessor = calibrationsAccessor, let coreDataManager = coreDataManager else {
|
||||
fatalError("in processNewCGMInfo, calibrations or coreDataManager is nil")
|
||||
|
@ -319,7 +319,7 @@ final class RootViewController: UIViewController {
|
|||
for (_, glucose) in glucoseData.enumerated().reversed() {
|
||||
if glucose.timeStamp > timeStampLastBgReading {
|
||||
|
||||
_ = calibrator.createNewBgReading(rawData: (Double)(glucose.glucoseLevelRaw), filteredData: (Double)(glucose.glucoseLevelRaw), timeStamp: glucose.timeStamp, sensor: activeSensor, last3Readings: &latest3BgReadings, lastCalibrationsForActiveSensorInLastXDays: &lastCalibrationsForActiveSensorInLastXDays, firstCalibration: firstCalibrationForActiveSensor, lastCalibration: lastCalibrationForActiveSensor, deviceName:UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
|
||||
_ = calibrator.createNewBgReading(rawData: (Double)(glucose.glucoseLevelRaw), filteredData: (Double)(glucose.glucoseLevelRaw), timeStamp: glucose.timeStamp, sensor: activeSensor, last3Readings: &latest3BgReadings, lastCalibrationsForActiveSensorInLastXDays: &lastCalibrationsForActiveSensorInLastXDays, firstCalibration: firstCalibrationForActiveSensor, lastCalibration: lastCalibrationForActiveSensor, deviceName: UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
|
||||
|
||||
// save the newly created bgreading permenantly in coredata
|
||||
coreDataManager.saveChanges()
|
||||
|
@ -351,25 +351,29 @@ final class RootViewController: UIViewController {
|
|||
updateLabels()
|
||||
}
|
||||
|
||||
if let nightScoutUploadManager = nightScoutUploadManager {
|
||||
nightScoutUploadManager.upload()
|
||||
}
|
||||
nightScoutUploadManager?.upload()
|
||||
|
||||
if let alertManager = alertManager {
|
||||
alertManager.checkAlerts(maxAgeOfLastBgReadingInSeconds: ConstantsMaster.maximumBgReadingAgeForAlertsInSeconds)
|
||||
}
|
||||
alertManager?.checkAlerts(maxAgeOfLastBgReadingInSeconds: ConstantsMaster.maximumBgReadingAgeForAlertsInSeconds)
|
||||
|
||||
if let healthKitManager = healthKitManager {
|
||||
healthKitManager.storeBgReadings()
|
||||
}
|
||||
healthKitManager?.storeBgReadings()
|
||||
|
||||
bgReadingSpeaker?.speakNewReading()
|
||||
|
||||
if let bgReadingSpeaker = bgReadingSpeaker {
|
||||
bgReadingSpeaker.speakNewReading()
|
||||
}
|
||||
dexcomShareUploadManager?.upload()
|
||||
|
||||
if let dexcomShareUploadManager = dexcomShareUploadManager {
|
||||
dexcomShareUploadManager.upload()
|
||||
//// temporary code
|
||||
// get latest reading, ignore sensor, rawdata, timestamp - only 1
|
||||
let lastReadings = bgReadingsAccessor.getLatestBgReadings(limit: 2, fromDate: nil, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
|
||||
|
||||
// if there's no readings, then no further processing
|
||||
if lastReadings.count > 0 {
|
||||
let lastReading = lastReadings[0]
|
||||
// if reading older dan 4.5 minutes, then no further processing
|
||||
if Date().timeIntervalSince(lastReading.timeStamp) < 4.5 * 60 {
|
||||
m5StackBluetoothTransmitter?.writeBgReadingInfo(bgReading: lastReading)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -551,12 +555,12 @@ final class RootViewController: UIViewController {
|
|||
if let calibrator = self.calibrator {
|
||||
if latestCalibrations.count == 0 {
|
||||
// calling initialCalibration will create two calibrations, they are returned also but we don't need them
|
||||
_ = calibrator.initialCalibration(firstCalibrationBgValue: valueAsDouble, firstCalibrationTimeStamp: Date(timeInterval: -(5*60), since: Date()), secondCalibrationBgValue: valueAsDouble, sensor: activeSensor, lastBgReadingsWithCalculatedValue0AndForSensor: &latestReadings, deviceName:UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
|
||||
_ = calibrator.initialCalibration(firstCalibrationBgValue: valueAsDouble, firstCalibrationTimeStamp: Date(timeInterval: -(5*60), since: Date()), secondCalibrationBgValue: valueAsDouble, sensor: activeSensor, lastBgReadingsWithCalculatedValue0AndForSensor: &latestReadings, deviceName: UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
|
||||
} else {
|
||||
// it's not the first calibration
|
||||
if let firstCalibrationForActiveSensor = calibrationsAccessor.firstCalibrationForActiveSensor(withActivesensor: activeSensor) {
|
||||
// calling createNewCalibration will create a new calibrations, it is returned but we don't need it
|
||||
_ = calibrator.createNewCalibration(bgValue: valueAsDouble, lastBgReading: latestReadings[0], sensor: activeSensor, lastCalibrationsForActiveSensorInLastXDays: &latestCalibrations, firstCalibration: firstCalibrationForActiveSensor, deviceName:UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
|
||||
_ = calibrator.createNewCalibration(bgValue: valueAsDouble, lastBgReading: latestReadings[0], sensor: activeSensor, lastCalibrationsForActiveSensorInLastXDays: &latestCalibrations, firstCalibration: firstCalibrationForActiveSensor, deviceName: UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -606,35 +610,35 @@ final class RootViewController: UIViewController {
|
|||
|
||||
case .dexcomG4:
|
||||
if let currentTransmitterId = UserDefaults.standard.transmitterId {
|
||||
cgmTransmitter = CGMG4xDripTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate:self)
|
||||
cgmTransmitter = CGMG4xDripTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, transmitterID: currentTransmitterId, delegate:self)
|
||||
}
|
||||
|
||||
case .dexcomG5:
|
||||
if let currentTransmitterId = UserDefaults.standard.transmitterId {
|
||||
cgmTransmitter = CGMG5Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self)
|
||||
cgmTransmitter = CGMG5Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, transmitterID: currentTransmitterId, delegate: self)
|
||||
}
|
||||
|
||||
case .dexcomG6:
|
||||
if let currentTransmitterId = UserDefaults.standard.transmitterId {
|
||||
cgmTransmitter = CGMG6Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self)
|
||||
cgmTransmitter = CGMG6Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, transmitterID: currentTransmitterId, delegate: self)
|
||||
}
|
||||
|
||||
case .miaomiao:
|
||||
cgmTransmitter = CGMMiaoMiaoTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), webOOPEnabled: UserDefaults.standard.webOOPEnabled, oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
|
||||
cgmTransmitter = CGMMiaoMiaoTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), webOOPEnabled: UserDefaults.standard.webOOPEnabled, oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
|
||||
|
||||
case .Bubble:
|
||||
cgmTransmitter = CGMBubbleTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber, webOOPEnabled: UserDefaults.standard.webOOPEnabled, oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
|
||||
cgmTransmitter = CGMBubbleTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber, webOOPEnabled: UserDefaults.standard.webOOPEnabled, oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
|
||||
|
||||
case .GNSentry:
|
||||
cgmTransmitter = CGMGNSEntryTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0))
|
||||
cgmTransmitter = CGMGNSEntryTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0))
|
||||
|
||||
case .Blucon:
|
||||
if let currentTransmitterId = UserDefaults.standard.transmitterId {
|
||||
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber)
|
||||
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber)
|
||||
}
|
||||
|
||||
case .Droplet1:
|
||||
cgmTransmitter = CGMDroplet1Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self)
|
||||
cgmTransmitter = CGMDroplet1Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, delegate: self)
|
||||
|
||||
}
|
||||
|
||||
|
@ -868,6 +872,14 @@ final class RootViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
// add action to start scanning for m5stack
|
||||
listOfActions["M5Stack"] = {(UIAlertAction) in
|
||||
|
||||
debuglogging("calling m5StackBluetoothTransmitter")
|
||||
self.m5StackBluetoothTransmitter?.startScanning()
|
||||
|
||||
}
|
||||
|
||||
// next action is to start or stop the sensor, can also be omitted depending on type of device - also not applicable for follower mode
|
||||
if let transmitterType = UserDefaults.standard.transmitterType {
|
||||
if !transmitterType.canDetectNewSensor() && UserDefaults.standard.isMaster {
|
||||
|
@ -1139,6 +1151,7 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
UserDefaults.standard.lastdisConnectTimestamp = Date()
|
||||
case .poweredOn:
|
||||
// user changes device bluetooth status to on
|
||||
debuglogging("in deviceDidUpdateBluetoothState, status = poweredon")
|
||||
if UserDefaults.standard.bluetoothDeviceAddress == nil, let cgmTransmitter = cgmTransmitter, let transmitterType = UserDefaults.standard.transmitterType, transmitterType.startScanningAfterInit() {
|
||||
// bluetoothDeviceAddress = nil, means app hasn't connected before to the transmitter
|
||||
// cgmTransmitter != nil, means user has configured transmitter type and transmitterid
|
||||
|
@ -1146,7 +1159,7 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
// possibly scanning is already running, but that's ok if we call the startScanning function again
|
||||
_ = cgmTransmitter.startScanning()
|
||||
}
|
||||
|
||||
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
|
@ -1455,4 +1468,24 @@ extension RootViewController:NightScoutFollowerDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
extension RootViewController: M5StackDelegate {
|
||||
|
||||
func newBlePassWord(newBlePassword: String) {
|
||||
UserDefaults.standard.m5StackBlePassword = newBlePassword
|
||||
}
|
||||
|
||||
func authentication(success: Bool) {
|
||||
debuglogging("successfully authenticated towards M5Stack")
|
||||
}
|
||||
|
||||
func blePasswordMissingInSettings() {
|
||||
debuglogging("ble password missing in settings")
|
||||
}
|
||||
|
||||
func m5StackResetRequired() {
|
||||
debuglogging("M5 Stack reset required")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue