Showing preview only (487K chars total). Download the full file or copy to clipboard to get everything.
Repository: UPetersen/LibreMonitor
Branch: master
Commit: 275b833a216b
Files: 73
Total size: 460.9 KB
Directory structure:
gitextract_hx6qj96v/
├── LICENSE.md
├── LibreMonitor/
│ ├── AppDelegate.swift
│ ├── Assets.xcassets/
│ │ └── AppIcon.appiconset/
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ └── LaunchScreen.storyboard
│ ├── BloodGlucose+CoreDataClass.swift
│ ├── BloodGlucose+CoreDataProperties.swift
│ ├── Bluetooth/
│ │ ├── Data_types+Extensions.swift
│ │ ├── NSData+CRC8.h
│ │ ├── NSData+CRC8.m
│ │ ├── NSData+SLIP.h
│ │ ├── NSData+SLIP.m
│ │ ├── SLIPBuffer.swift
│ │ ├── SimbleeManager.swift
│ │ ├── constants.h
│ │ └── data_types.h
│ ├── HeaderData+CoreDataClass.swift
│ ├── HeaderData+CoreDataProperties.swift
│ ├── Info.plist
│ ├── LibreMonitor-Bridging-Header.h
│ ├── LibreMonitor.entitlements
│ ├── LibreMonitor.xcdatamodeld/
│ │ ├── .xccurrentversion
│ │ └── LibreMonitor.xcdatamodel/
│ │ └── contents
│ ├── LibreMonitorUITests-Bridging-Header.h
│ ├── Main.storyboard
│ ├── Model/
│ │ ├── CRC.swift
│ │ ├── LibreSensor.swift
│ │ ├── Measurement.swift
│ │ ├── SensorData.swift
│ │ └── SensorState.swift
│ ├── ModelCoreData/
│ │ ├── BloodGlucose+CoreDataClass.swift
│ │ ├── BloodGlucose+CoreDataProperties.swift
│ │ ├── CoreDataStack.swift
│ │ ├── HeaderData+CoreDataClass.swift
│ │ ├── HeaderData+CoreDataProperties.swift
│ │ ├── Reader+CoreDataClass.swift
│ │ ├── Reader+CoreDataProperties.swift
│ │ ├── Sensor+CoreDataClass.swift
│ │ └── Sensor+CoreDataProperties.swift
│ ├── Reader+CoreDataClass.swift
│ ├── Reader+CoreDataProperties.swift
│ ├── Sensor+CoreDataClass.swift
│ ├── Sensor+CoreDataProperties.swift
│ ├── SimbleeCode/
│ │ ├── crc8.h
│ │ ├── crc8.m
│ │ ├── libUBP.h
│ │ └── libUBP.m
│ ├── ViewControllers/
│ │ ├── AdjustmentsTableViewController.swift
│ │ └── BloodSugarTableViewController.swift
│ └── Views/
│ ├── BloodSugarGraphView.swift
│ └── BloodSugarGraphViewTableViewCell.swift
├── LibreMonitor.ino
├── LibreMonitor.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ └── contents.xcworkspacedata
│ └── xcuserdata/
│ └── Uwe.xcuserdatad/
│ └── xcschemes/
│ ├── LibreMonitor.xcscheme
│ └── xcschememanagement.plist
├── LibreMonitor.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcuserdata/
│ └── Uwe.xcuserdatad/
│ └── xcdebugger/
│ └── Breakpoints_v2.xcbkptlist
├── LibreMonitorRFduino.ino
├── LibreMonitorTests/
│ ├── BluetoothTestData.swift
│ ├── Info.plist
│ ├── LibreMonitorTestSensorData.swift
│ ├── LibreMonitorTests-Bridging-Header.h
│ ├── LibreMonitorTests.swift
│ ├── SimbleeCode/
│ │ ├── crc8.h
│ │ ├── crc8.m
│ │ ├── libUBP.h
│ │ └── libUBP.m
│ └── TransmissionTests.swift
├── LibreMonitorUITests/
│ ├── Info.plist
│ └── LibreMonitorUITests.swift
├── Podfile
├── README.md
└── libUBP RFduino.cpp
================================================
FILE CONTENTS
================================================
================================================
FILE: LICENSE.md
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: LibreMonitor/AppDelegate.swift
================================================
//
// AppDelegate.swift
// LibreMonitor
//
// Created by Uwe Petersen on 14.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import UIKit
import CoreBluetooth
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
var window: UIWindow?
var coreDataStack = CoreDataStack()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Allow local notifications for iOS 10
let center = UNUserNotificationCenter.current()
let options: UNAuthorizationOptions = [.alert, .badge, .sound]
center.requestAuthorization(options: options) { (granted, error) in
if granted {
// application.registerForRemoteNotifications()
}
}
// Do not show a badge icon value unless data has been received
UIApplication.shared.applicationIconBadgeNumber = 0 // hide badge number
// Override point for customization after application launch.
// let splitViewController = self.window!.rootViewController as! UISplitViewController
// let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
// navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
// splitViewController.delegate = self
//
// let masterNavigationController = splitViewController.viewControllers[0] as! UINavigationController
// let controller = masterNavigationController.topViewController as! MasterViewController
// controller.managedObjectContext = self.persistentContainer.viewContext
print("In didFinishLaunchingWithOptions")
let tabBarController = self.window?.rootViewController as! UITabBarController
if let childViewControllers = tabBarController.viewControllers {
for childViewController in childViewControllers where childViewController is UINavigationController {
let navigationController = childViewController as! UINavigationController
let bloodSugarTableViewController = navigationController.topViewController as! BloodSugarTableViewController
// Set core data stack in view controller
bloodSugarTableViewController.coreDataStack = coreDataStack
}
}
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
print("In applicationWillResignActive")
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
print("In applicationDidEnterBackground")
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
print("In applicationWillEnterForeground")
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
print("In applicationDidBecomeActive")
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
print("In applicationWillTerminate")
// self.saveContext()
coreDataStack.saveContext()
}
// // MARK: - Split view
//
// func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
// guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
// guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
// if topAsDetailController.detailItem == nil {
// // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
// return true
// }
// return false
// }
// // MARK: - Core Data stack
//
// lazy var persistentContainer: NSPersistentContainer = {
// /*
// The persistent container for the application. This implementation
// creates and returns a container, having loaded the store for the
// application to it. This property is optional since there are legitimate
// error conditions that could cause the creation of the store to fail.
// */
// let container = NSPersistentContainer(name: "LibreMonitor")
// container.loadPersistentStores(completionHandler: { (storeDescription, error) in
// if let error = error as NSError? {
// // Replace this implementation with code to handle the error appropriately.
// // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//
// /*
// Typical reasons for an error here include:
// * The parent directory does not exist, cannot be created, or disallows writing.
// * The persistent store is not accessible, due to permissions or data protection when the device is locked.
// * The device is out of space.
// * The store could not be migrated to the current model version.
// Check the error message to determine what the actual problem was.
// */
// fatalError("Unresolved error \(error), \(error.userInfo)")
// }
// })
// return container
// }()
//
// // MARK: - Core Data Saving support
//
// func saveContext () {
// let context = persistentContainer.viewContext
// if context.hasChanges {
// do {
// try context.save()
// } catch {
// // Replace this implementation with code to handle the error appropriately.
// // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
// let nserror = error as NSError
// fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
// }
// }
// }
}
================================================
FILE: LibreMonitor/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: LibreMonitor/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
================================================
FILE: LibreMonitor/BloodGlucose+CoreDataClass.swift
================================================
//
// BloodGlucose+CoreDataClass.swift
// LibreMonitor
//
// Created by Uwe Petersen on 14.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
public class BloodGlucose: NSManagedObject {
}
================================================
FILE: LibreMonitor/BloodGlucose+CoreDataProperties.swift
================================================
//
// BloodGlucose+CoreDataProperties.swift
// LibreMonitor
//
// Created by Uwe Petersen on 14.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
extension BloodGlucose {
@nonobjc public class func fetchRequest() -> NSFetchRequest<BloodGlucose> {
return NSFetchRequest<BloodGlucose>(entityName: "BloodGlucose");
}
@NSManaged public var bytes: String?
@NSManaged public var date: NSDate?
@NSManaged public var dateString: String?
@NSManaged public var id: Int32
@NSManaged public var type: Int16
@NSManaged public var value: Double
@NSManaged public var sensor: Sensor?
}
================================================
FILE: LibreMonitor/Bluetooth/Data_types+Extensions.swift
================================================
//
// Data_types+Extensions.swift
// LibreMonitor
//
// Created by Uwe Petersen on 15.05.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
extension IDNDataType {
func deviceIDString() -> String {
var _self = self // make a copy of self to be able to access it via a pointer (does not work on self itself)
// Extrakt device ID by creating an array from the tuple
let deviceIDString = withUnsafePointer(to: &_self.deviceID, { (ptr) -> String? in
let uint8Ptr = unsafeBitCast(ptr, to: UnsafePointer<UInt8>.self)
var deviceIDString: String = String(format: "%02X", arguments: [uint8Ptr[0]])
for index in 1...12 {
deviceIDString += String(format: ":%02X", arguments: [uint8Ptr[index]])
}
return deviceIDString
})
print(deviceIDString!)
return deviceIDString ?? "no device id"
}
}
extension SystemInformationDataType {
func uidString() -> String {
var _self = self // make a copy of self to be able to access it via a pointer (does not work on self itself)
// Extrakt UID by creating an array from the tuple
let uidString = withUnsafePointer(to: &_self.uid, { (ptr) -> String? in
let uint8Ptr = unsafeBitCast(ptr, to: UnsafePointer<UInt8>.self)
var uidString: String = String(format: "%02X", arguments: [uint8Ptr[0]])
for index in 1...7 {
uidString += String(format: "%02X", arguments: [uint8Ptr[index]])
// uidString += String(format: ":%02X", arguments: [uint8Ptr[index]])
}
return uidString
})
print(uidString!)
return uidString ?? "no uid"
}
}
//extension RawDataType {
// func byteString() -> String {
//
// var _self = self // make a copy of self to be able to access it via a pointer (does not work on self itself)
//
// // Extrakt UID by creating an array from the tuple
//
// let byteString = withUnsafePointer(&_self.bytes, { (ptr) -> String? in
//
// let uint8Ptr = unsafeBitCast(ptr, UnsafePointer<UInt8>.self)
//
// var byteString: String = String(format: "%02X", arguments: [uint8Ptr[0]])
// for index in 1...5 {
// byteString += String(format: "%02X", arguments: [uint8Ptr[index]])
// // uidString += String(format: ":%02X", arguments: [uint8Ptr[index]])
// }
// return byteString
// })
//
// print(byteString!)
//
// return byteString ?? "no bytes"
// }
//}
================================================
FILE: LibreMonitor/Bluetooth/NSData+CRC8.h
================================================
//
// NSData+CRC8.h
// Bluetooth LE Test
//
// Created by Chas Conway on 12/11/13.
// Copyright (c) 2013 Chas Conway. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSData (CRC8)
- (signed char)CRC8Checksum;
//+ (signed char)CRC8ChecksumFromBuffer:(signed char *)dataBuffer bytesToRead:(signed char)bytesToRead;
+ (signed char)CRC8ChecksumFromBuffer:(signed char *)dataBuffer bytesToRead:(uint16_t)bytesToRead; // changed to uint16_t by Uwi on 2016-12-26
@end
================================================
FILE: LibreMonitor/Bluetooth/NSData+CRC8.m
================================================
//
// NSData+CRC8.m
// Bluetooth LE Test
//
// Created by Chas Conway on 12/11/13.
// Copyright (c) 2013 Chas Conway. All rights reserved.
//
#import "NSData+CRC8.h"
#define CRC8INIT 0x00
#define CRC8POLY 0x18 //0X18 = X^8+X^5+X^4+X^0
@implementation NSData (CRC8)
- (signed char)CRC8Checksum {
signed char *buffer = malloc(self.length);
[self getBytes:buffer length:self.length];
return [NSData CRC8ChecksumFromBuffer:buffer bytesToRead:self.length];
}
//+ (signed char)CRC8ChecksumFromBuffer:(signed char *)dataBuffer bytesToRead:(signed char)bytesToRead {
+ (signed char)CRC8ChecksumFromBuffer:(signed char *)dataBuffer bytesToRead:(uint16_t)bytesToRead { // changed to uint16_t by Uwi on 2016-12-26
signed char crc;
uint16_t loop_count;
signed char bit_counter;
signed char data;
signed char feedback_bit;
crc = CRC8INIT;
for (loop_count = 0; loop_count != bytesToRead; loop_count++)
{
data = dataBuffer[loop_count];
bit_counter = 8;
do {
feedback_bit = (crc ^ data) & 0x01;
if ( feedback_bit == 0x01 ) {
crc = crc ^ CRC8POLY;
}
crc = (crc >> 1) & 0x7F;
if ( feedback_bit == 0x01 ) {
crc = crc | 0x80;
}
data = data >> 1;
bit_counter--;
} while (bit_counter > 0);
}
return crc;
}
@end
================================================
FILE: LibreMonitor/Bluetooth/NSData+SLIP.h
================================================
//
// NSData+SLIP.h
// Arduino Greenhouse
//
// Created by Chas Conway on 5/23/14.
// Copyright (c) 2014 Chas Conway. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSData (SLIP)
- (NSIndexSet *)indexesOfEndBytes;
- (NSData *)unescapedData;
- (BOOL)beginsWithEndByte;
- (BOOL)endsWithEndByte;
@end
================================================
FILE: LibreMonitor/Bluetooth/NSData+SLIP.m
================================================
//
// NSData+SLIP.m
// Arduino Greenhouse
//
// Created by Chas Conway on 5/23/14.
// Copyright (c) 2014 Chas Conway. All rights reserved.
//
// Modified by Uwe Petersen
#import "NSData+SLIP.h"
// Serial Line IP (SLIP) escaping constants
#define ESCAPE_BYTE 0xDB
#define END_BYTE 0xC0
#define ESCAPED_ESCAPE_BYTE 0xDD
#define ESCAPED_END_BYTE 0xDC
const uint8_t escapeSequence[1] = {ESCAPE_BYTE};
const uint8_t endSequence[1] = {END_BYTE};
const uint8_t escapedEndSequence[2] = {ESCAPE_BYTE, ESCAPED_END_BYTE};
const uint8_t escapedEscapeSequence[2] = {ESCAPE_BYTE, ESCAPED_ESCAPE_BYTE};
@implementation NSData (SLIP)
- (NSIndexSet *)indexesOfEndBytes {
__block NSMutableIndexSet *endByteIndices = [NSMutableIndexSet new];
[self enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
for (NSInteger i = byteRange.location; i < (byteRange.location + byteRange.length); i++) { // For each byte in the range
uint8_t aByte = *((uint8_t *)bytes + i);
if (aByte == END_BYTE) [endByteIndices addIndex:i];
}
}];
return endByteIndices;
}
- (NSData *)unescapedData {
NSMutableData *outputData = [[NSMutableData alloc] initWithData:self];
BOOL done = NO;
NSUInteger notYetUnescaped = 0; // 2016-06-30, Uwe Petersen:
while (!done) { // Search for escaped END bytes
// 2016-06-30, Uwe Petersen:
NSRange resultRange = [outputData rangeOfData:[NSData dataWithBytes:escapedEndSequence length:2] options:0 range:NSMakeRange(notYetUnescaped, outputData.length - notYetUnescaped)];
// NSRange resultRange = [outputData rangeOfData:[NSData dataWithBytes:escapedEndSequence length:2] options:0 range:NSMakeRange(0, outputData.length)];
if (resultRange.location != NSNotFound) { // Found an occurance
// Replace that escaped occurance with the unescaped value
[outputData replaceBytesInRange:resultRange withBytes:endSequence length:1];
notYetUnescaped = resultRange.location + 1; // 2016-06-30, Uwe Petersen
} else { // Didn't find any more occurances, so exit while loop
done = YES;
}
}
done = NO;
notYetUnescaped = 0; // 2016-06-30, Uwe Petersen
while (!done) { // Search for escaped ESCAPE bytes
// 2016-06-30, Uwe Petersen:
NSRange resultRange = [outputData rangeOfData:[NSData dataWithBytes:escapedEscapeSequence length:2] options:0 range:NSMakeRange(notYetUnescaped, outputData.length - notYetUnescaped)];
// NSRange resultRange = [outputData rangeOfData:[NSData dataWithBytes:escapedEscapeSequence length:2] options:0 range:NSMakeRange(0, outputData.length)];
if (resultRange.location != NSNotFound) { // Found an occurance
// Replace that escaped occurance with the unescaped value
[outputData replaceBytesInRange:resultRange withBytes:escapeSequence length:1];
notYetUnescaped = resultRange.location + 1; // 2016-06-30, Uwe Petersen
} else { // Didn't find any more occurances, so exit while loop
done = YES;
}
}
return [NSData dataWithData:outputData];
}
- (BOOL)beginsWithEndByte {
NSIndexSet *endByteIndices = [self indexesOfEndBytes];
return [endByteIndices containsIndex:0];
}
- (BOOL)endsWithEndByte {
NSIndexSet *endByteIndices = [self indexesOfEndBytes];
return [endByteIndices containsIndex:(self.length - 1)];
}
@end
================================================
FILE: LibreMonitor/Bluetooth/SLIPBuffer.swift
================================================
//
// SLIPBuffer.swift
// UBA-Demo
//
// Created by Chas Conway on 2/4/15.
// Copyright (c) 2015 Chas Conway. All rights reserved.
//
// Modified by Uwe Petersen
import Foundation
let PacketIdentifierLength = MemoryLayout<UInt16>.size
let PacketFlagsLength = MemoryLayout<UInt8>.size
let PacketChecksumLength = MemoryLayout<UInt8>.size
protocol SLIPBufferDelegate {
func slipBufferReceivedPayload(_ payloadData: Data, payloadIdentifier: UInt16, txFlags: UInt8)
}
class SLIPBuffer {
var rxBuffer = Data()
var delegate:SLIPBufferDelegate?
/// Appends more escaped bytes (i.e. data that were received via bluetooth) to the buffer and scans the buffer for complete frames according to the Serial Line Internet Protocol (SLIP). If a complete frame is detected, its payload data is extracted and a delegate method is called.
///
/// The bytes that are appended are escaped bytes according to the serial line internet protocol (SLIP). SLIP works as follows:
/// - A transmission packet (the payload data) is appended by a special "END" byte, which distinguishes the datagram boundaries in the byte stream.
/// - If the END byte occurs in the payload data to be sent, the two byte sequence [ESC, ESC_END] is sent instead.
/// - If the ESC byte occurs in the payload data to be sent, the two byte sequence [ESC, ESC_ESC] is sent instead.
/// - Variants of the protocol may also begin transmission packets (the payload data) with an END byte (which is the case in this realization of SLIP)
///
/// The special bytes used by SLIP are:
/// - 0xC0 ... END -> Frame End (and Frame Beginning in this realization)
/// - 0xDB ... ESC -> Frame Escape
/// - 0xDC ... ESC_END -> Transposed Frame END
/// - 0xDD ... ESC_ESC -> Transposed Frame ESC
///
/// Reference: https://en.m.wikipedia.org/wiki/Serial_Line_Internet_Protocol
///
/// - parameter escapedData: data with escape bytes to be appended to the buffer
func appendEscapedBytes(_ escapedData: Data) {
rxBuffer.append(escapedData)
scanRxBufferForFrames()
}
/// Scans the buffer for complete frames according to the Serial Line Internet Protocol (SLIP). If a complete frame is detected, the crc is checked and the payload extracted and a delegate method called.
///
/// Extracting the payload means the following steps:
/// - Extract the payload from the SLIP frame:
/// - Remove the END byte at the beginning and at the end of the frame.
/// - If the original payload data had contained the special bytes END (0xC0) or ESC (0xDB) they where replaced by the special byte sequences [END, ESC_END] ([0xC0, 0xDC]) and [ESC, ESC_ESC] ([0xDB, 0xDD]) because of the SLIP and this has to be reversed.
/// - Check CRC and remove the rcr bytes:
/// - The datagram is appended with one byte containting a CRC8, calculated over the original payload data. This CRC8 is compared with the corresponding CRC8 of the payload data.
/// - If the CRCs are equal, a delegate method is called with the payload data (with the one appended CRC byte removed).
///
/// The serial line internet protocol (SLIP) works as follows:
/// - A transmission packet (the payload data) is appended by a special "END" byte, which distinguishes the datagram boundaries in the byte stream.
/// - If the END byte occurs in the payload data to be sent, the two byte sequence [ESC, ESC_END] is sent instead.
/// - If the ESC byte occurs in the payload data to be sent, the two byte sequence [ESC, ESC_ESC] is sent instead.
/// - Variants of the protocol may also begin transmission packets (the payload data) with an END byte (which is the case in this realization of SLIP)
///
/// The special bytes used by SLIP are:
/// - 0xC0 ... END -> Frame End (and Frame Beginning in this realization)
/// - 0xDB ... ESC -> Frame Escape
/// - 0xDC ... ESC_END -> Transposed Frame END
/// - 0xDD ... ESC_ESC -> Transposed Frame ESC
///
/// Reference: https://en.m.wikipedia.org/wiki/Serial_Line_Internet_Protocol
///
func scanRxBufferForFrames() {
// get indices of all END bytes
// TODO: idexesOfEndBytes is an Objective-C-extension of NSData. Reprogram this in Swift for data type "Data"
guard let endByteIndices = NSData.init(data: rxBuffer).indexesOfEndBytes() else {
return
}
// Loop over the END bytes and search for a complete frame, i.e. a sequence of bytes as follows: [END ... at-least-two-bytes ... END]
var previousEndByteIndex = NSNotFound
for endByteIndex in endByteIndices {
print(String(endByteIndex.description) as Any)
if (previousEndByteIndex != NSNotFound) {
if endByteIndex - previousEndByteIndex > 2 { // Contains at least one byte and checksum byte
print("Identified a potential SLIP frame")
print(self.rxBuffer.debugDescription)
// Extact the frame (END byte at beginning and end are aleady removed)
let escapedPacket = self.rxBuffer.subdata(in: Range((previousEndByteIndex + 1)..<endByteIndex))
// Decode the packet (undo SLIP)
self.decodeSLIPPacket(escapedPacket)
} else {
print("Ignoring improbable SLIP frame")
}
}
previousEndByteIndex = endByteIndex
}
// Remove byte in buffer up to, but not including, the previous END byte, i.e. cut of everything before the beginning of the frame.
if previousEndByteIndex != NSNotFound {
rxBuffer.removeSubrange(0..<previousEndByteIndex)
}
}
/// Decode the escape packet.
///
/// Decoding means that the ESC and END bytes that have been added to the packet to transfer it according to the slip (serial line internet protocol) are now removed from the escapedPacket. After that the delegate is called with the resulting unescaped packet.
///
/// - parameter escapedPacket: packet that still contains ESC and END bytes (as they were needed for the serial line internet protocol)
func decodeSLIPPacket(_ escapedPacket:Data) {
// Remove SLIP escaping
guard let unescapedPacket = (escapedPacket as NSData).unescaped() else {
return
}
// Extract embedded checksum from packet
var embeddedChecksumByte:UInt8 = 0
unescapedPacket.copyBytes(to: &embeddedChecksumByte, from: Range((unescapedPacket.count - PacketChecksumLength)..<unescapedPacket.count))
// 2016-07-30, Uwe Petersen: get unescaped packet without checksum
let unescapedPacketWithoutChecksum = unescapedPacket.subdata(in: Range(0..<(unescapedPacket.count-PacketChecksumLength)))
// Calculate checksum on payload bytes (2016-06-30, Uwe Petersen: seems to be an error to calculate on unescaped packet. Changed to escaped packet)
let checksummedData = escapedPacket.subdata(in: Range(0..<escapedPacket.count - PacketChecksumLength))
let calculatedChecksum = (checksummedData as NSData).crc8Checksum()
if UInt8(bitPattern: calculatedChecksum) == embeddedChecksumByte { // crc is calulated as Int8 and thus has to be converted to UInt8
if let aDelegate = delegate {
// Extract payload and payload ID. (2016-06-30, Uwe Petersen: seems to be an error to calculate on unescaped packet. Changed to escaped packet)
var identifier: UInt16 = 0;
let _ = unescapedPacketWithoutChecksum.copyBytes(to: UnsafeMutableBufferPointer(start: &identifier, count: 1), from: Range(0..<PacketIdentifierLength))
// 2016-06-30, Uwe Petersen: seems to be an error to calculate on unescaped packet. Changed to escaped packet
var txFlags: UInt8 = 0;
unescapedPacketWithoutChecksum.copyBytes(to: &txFlags, from: Range((PacketIdentifierLength-1)..<(PacketIdentifierLength-1+PacketFlagsLength)))
let payloadData = unescapedPacketWithoutChecksum.subdata(in: Range( (PacketIdentifierLength + PacketFlagsLength)..<unescapedPacketWithoutChecksum.count ))
// Notify delegate with payloadData
aDelegate.slipBufferReceivedPayload(payloadData, payloadIdentifier: identifier, txFlags: txFlags)
}
} else {
print("SLIP frame failed checksum")
}
}
}
================================================
FILE: LibreMonitor/Bluetooth/SimbleeManager.swift
================================================
//
// SimbleeManager.swift
// LibreMonitor
//
// Created by Uwe Petersen on 23.04.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
// How does the Simblee work?
//
// 1.) Services
/// The Simblee has only one service. By convention the service UUID is set to "2220" (this
// could be changed in the arduino code any time):
//
// Service:
// "<CBService: 0x14c72a810, isPrimary = YES, UUID = 2220>"
//
// 2.) Characteristics
// Simble provides three Characteristics: one read and two write characteristics. This is
// a "hard coded" feature of the simblee and cannot be changed. The debugDescription
// of these three characteristics is as follows:
//
// a) Read Characteristic:
// "<CBCharacteristic: 0x14c7664e0, UUID = 2221, properties = 0x12, value = (null), notifying = NO>"
// ... with properties:
// __C.CBCharacteristicProperties(rawValue: 18)
// Broadcast: [false]
// Read: [true]
// WriteWithoutResponse: [false]
// Write: [false]
// Notify: [true]
// Indicate: [false]
// AuthenticatedSignedWrites: [false]
// ExtendedProperties: [false]
// NotifyEncryptionRequired: [false]
// BroaIndicateEncryptionRequireddcast: [false]
//
// b) First Write Characteristic:
// "<CBCharacteristic: 0x14c766620, UUID = 2222, properties = 0xC, value = (null), notifying = NO>"
// ... with properties:
// __C.CBCharacteristicProperties(rawValue: 12)
// Broadcast: [false]
// Read: [false]
// WriteWithoutResponse: [true]
// Write: [true]
// Notify: [false]
// Indicate: [false]
// AuthenticatedSignedWrites: [false]
// ExtendedProperties: [false]
// NotifyEncryptionRequired: [false]
// BroaIndicateEncryptionRequireddcast: [false]
//
// c) Second Write Characteristic:
// "<CBCharacteristic: 0x14c766720, UUID = 2223, properties = 0xC, value = (null), notifying = NO>"
// ... with properties:
// __C.CBCharacteristicProperties(rawValue: 12)
// Broadcast: [false]
// Read: [false]
// WriteWithoutResponse: [true]
// Write: [true]
// Notify: [false]
// Indicate: [false]
// AuthenticatedSignedWrites: [false]
// ExtendedProperties: [false]
// NotifyEncryptionRequired: [false]
// BroaIndicateEncryptionRequireddcast: [false]
//
// 3.) Further information on Simblee services and characteristics can be found on
// http://forum.rfduino.com/index.php?topic=1066.15
//
import Foundation
import UIKit
import CoreBluetooth
public enum SimbleeManagerState: String {
case Unassigned = "Unassigned"
case Scanning = "Scanning"
case Disconnected = "Disconnected"
case DisconnectedManually = "Disconnected manually"
case Connecting = "Connecting"
case Connected = "Connected"
case Notifying = "Notifying"
}
protocol SimbleeManagerDelegate {
func simbleeManagerPeripheralStateChanged(_ state:SimbleeManagerState)
func simbleeManagerReceivedMessage(_ messageIdentifier:UInt16, txFlags:UInt8, payloadData:Data)
}
class SimbleeManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SLIPBufferDelegate {
// MARK: - Properties
var centralManager: CBCentralManager!
var peripheral: CBPeripheral?
var slipBuffer = SLIPBuffer()
fileprivate let serviceUUIDs:[CBUUID]? = [CBUUID(string: "2220")]
var BLEScanDuration = 3.0
var delegate: SimbleeManagerDelegate? {
didSet {
// Help delegate initialize by sending current state directly after delegate assignment
delegate?.simbleeManagerPeripheralStateChanged(state)
}
}
var state: SimbleeManagerState = .Unassigned {
didSet {
// Help delegate initialize by sending current state directly after delegate assignment
delegate?.simbleeManagerPeripheralStateChanged(state)
}
}
// MARK: - Mehods
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
slipBuffer.delegate = self
}
func scanForSimblee() {
if centralManager.state == .poweredOn {
print ("Start scanning for Simblee")
centralManager.scanForPeripherals(withServices: serviceUUIDs, options: nil)
state = .Scanning
}
}
func connect() {
if let peripheral = peripheral {
peripheral.delegate = self
centralManager.stopScan()
centralManager.connect(peripheral, options: nil)
state = .Connecting }
}
func disconnectManually() {
switch state {
case .Connected, .Connecting, .Notifying:
state = .DisconnectedManually // to avoid reconnect in didDisconnetPeripheral
centralManager.cancelPeripheralConnection(peripheral!)
default:
break
}
// if state == .Connected || peripheral?.state == .Connecting {
// centralManager.cancelPeripheralConnection(peripheral!)
// }
}
// MARK: - CBCentralManagerDelegate
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print("centralManagerDidUpdateState")
// TODO: maybe handle the case of bluetooth beeing switched of (and sometimes later) on again here by stopping and restarting scanning
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("didDiscoverPeripheral with name \(peripheral.name)")
self.peripheral = peripheral
connect()
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("didConnectPeripheral")
state = .Connected
// Discover all Services. This might be helpful if writing is needed some time
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("didFailToConnectPeripheral")
state = .Disconnected
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("didDisconnectPeripheral")
switch state {
case .DisconnectedManually:
state = .Disconnected
default:
state = .Disconnected
scanForSimblee()
}
// Keep this code in case you want it some later time: it is used for reconnection only in background mode
// state = .Disconnected
// // Start scanning, if disconnection occurred in background mode
// if UIApplication.sharedApplication().applicationState == .Background ||
// UIApplication.sharedApplication().applicationState == .Inactive {
// scanForSimblee()
// }
}
// MARK: - CBPeripheralDelegate
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("didDiscoverServices")
if let services = peripheral.services {
print("Discovered services on RFduino");
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
// print("Service: ")
// debugPrint(service.debugDescription)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
print("didDiscoverCharacteristicsForService");
if let error = error {
print("An error occured: \(error.localizedDescription)")
}
if let characteristics = service.characteristics {
for characteristic in characteristics {
// print("Characteristic: ")
// debugPrint(characteristic.debugDescription)
// print("... with properties: ")
// debugPrint(characteristic.properties)
// print("Broadcast: ", [characteristic.properties.contains(.Broadcast)])
// print("Read: ", [characteristic.properties.contains(.Read)])
// print("WriteWithoutResponse: ", [characteristic.properties.contains(.WriteWithoutResponse)])
// print("Write: ", [characteristic.properties.contains(.Write)])
// print("Notify: ", [characteristic.properties.contains(.Notify)])
// print("Indicate: ", [characteristic.properties.contains(.Indicate)])
// print("AuthenticatedSignedWrites: ", [characteristic.properties.contains(.AuthenticatedSignedWrites )])
// print("ExtendedProperties: ", [characteristic.properties.contains(.ExtendedProperties)])
// print("NotifyEncryptionRequired: ", [characteristic.properties.contains(.NotifyEncryptionRequired)])
// print("BroaIndicateEncryptionRequireddcast: ", [characteristic.properties.contains(.IndicateEncryptionRequired)])
// Choose the notifiying characteristic and Register to be notified whenever the simblee transmits
if (characteristic.properties.intersection(.notify)) == .notify {
peripheral.setNotifyValue(true, for: characteristic)
}
}
} else {
print("Discovered characteristics on RFduino, but no characteristics listed. There must be some error.");
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
print("didUpdateNotificationStateForCharacteristic")
if let error = error {
print("An error occured: \(error.localizedDescription)")
}
state = .Notifying
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("didUpdateValueForCharacteristic")
if let error = error {
print("Characteristic update error = \(error.localizedDescription)")
} else {
if let value = characteristic.value {
slipBuffer.appendEscapedBytes(value)
}
}
}
// MARK: - SLIPBufferDelegate
func slipBufferReceivedPayload(_ payloadData: Data, payloadIdentifier: UInt16, txFlags: UInt8) {
// Inform delegate
if let delegate = delegate {
delegate.simbleeManagerReceivedMessage(payloadIdentifier, txFlags: txFlags, payloadData: payloadData)
}
}
}
================================================
FILE: LibreMonitor/Bluetooth/constants.h
================================================
// Define unique identifiers for each data packet here. This identifier is transfered together with the data and can be used in the corresponding iOS app to identify a packet, even if there are packets of the same type
#define ALL_BYTES 0x1007 //
#define IDN_DATA 0x2001 //
#define SYSTEM_INFORMATION_DATA 0x2002 //
#define BATTERY_DATA 0x2005 //
================================================
FILE: LibreMonitor/Bluetooth/data_types.h
================================================
// Data types for data to be transfered
// Need a struct with one variable and the packed attribute to make this an array in swift (in a simple way
// Battery
typedef struct __attribute__((packed)) {
float voltage;
float temperature;
} BatteryDataType;
/// System information command response
/// @detail Contains the relevant information received with the system information command is already distributed into the follwing parameters. See the BM019 manual and the CR95HF manual for information on the the codes and flag meanings
/// @param resultCode 0x80 if no error
/// @param responseFlags
/// @param infoFlags bit 0 is set to 1 in case of error
/// @param errorCode error code
/// @param uid 8 bytes containing the uid, order already reversed, e.g. (colons only inserted for readability): E0:07:A0:00:00:0C:48:BD. All zeros in case of an error.
typedef struct __attribute__((packed)) {
uint8_t uid[8];
uint8_t resultCode;
uint8_t responseFlags;
uint8_t infoFlags;
uint8_t errorCode;
} SystemInformationDataType;
/// IDN command response, which is the device ID
/// @detail Contains the relevant information received with the IDN command is already distributed into the follwing parameters. See the BM019 manual and the CR95HF manual for information on the the codes and flag meanings
/// @param resultCode 0x00 if no error
/// @param deiceID 13 bytes containing the device ID.
typedef struct __attribute__((packed)) {
uint8_t resultCode;
uint8_t deviceID[13];
uint8_t romCRC[2];
} IDNDataType;
/// The first 344 bytes of data as read from FRAM of the Freestyle Libre Sensor
/// @detail Contains 24 byes of header, 296 bytes of body with blood sugar data
/// and 24 bytes of footer
/// @param bytes 344 bytes containing the the raw data
typedef struct __attribute__((packed)) {
uint8_t allBytes[344];
} AllBytesDataType;
================================================
FILE: LibreMonitor/HeaderData+CoreDataClass.swift
================================================
//
// HeaderData+CoreDataClass.swift
// LibreMonitor
//
// Created by Uwe Petersen on 14.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
@objc(HeaderData)
public class HeaderData: NSManagedObject {
}
================================================
FILE: LibreMonitor/HeaderData+CoreDataProperties.swift
================================================
//
// HeaderData+CoreDataProperties.swift
// LibreMonitor
//
// Created by Uwe Petersen on 14.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
extension HeaderData {
@nonobjc public class func fetchRequest() -> NSFetchRequest<HeaderData> {
return NSFetchRequest<HeaderData>(entityName: "HeaderData");
}
@NSManaged public var bytes: String?
@NSManaged public var date: NSDate?
}
================================================
FILE: LibreMonitor/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
<string>healthkit</string>
</array>
<key>UIStatusBarTintParameters</key>
<dict>
<key>UINavigationBar</key>
<dict>
<key>Style</key>
<string>UIBarStyleDefault</string>
<key>Translucent</key>
<false/>
</dict>
</dict>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
================================================
FILE: LibreMonitor/LibreMonitor-Bridging-Header.h
================================================
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "NSData+CRC8.h"
#import "NSData+SLIP.h"
#import "constants.h"
#import "data_types.h"
================================================
FILE: LibreMonitor/LibreMonitor.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.healthkit</key>
<true/>
</dict>
</plist>
================================================
FILE: LibreMonitor/LibreMonitor.xcdatamodeld/.xccurrentversion
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>LibreMonitor.xcdatamodel</string>
</dict>
</plist>
================================================
FILE: LibreMonitor/LibreMonitor.xcdatamodeld/LibreMonitor.xcdatamodel/contents
================================================
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1" systemVersion="11A491" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Event" representedClassName="Event" codeGenerationType="class">
<attribute name="timestamp" optional="YES" attributeType="Date"/>
</entity>
<elements>
<element name="Event" positionX="261" positionY="189" width="128" height="60"/>
</elements>
<entity name="BloodGlucose" representedClassName=".BloodGlucose" syncable="YES">
<attribute name="bytes" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="dateString" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="type" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="value" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES" indexed="YES" syncable="YES"/>
<relationship name="sensor" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Sensor" inverseName="bloodGlucose" inverseEntity="Sensor" syncable="YES"/>
</entity>
<entity name="HeaderData" representedClassName="HeaderData" syncable="YES">
<attribute name="bytes" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
</entity>
<entity name="Reader" representedClassName=".Reader" syncable="YES">
<attribute name="batteryVoltage" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="temperature" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uid" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="Sensor" representedClassName=".Sensor" syncable="YES">
<attribute name="lastScanDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="minutesSinceStart" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="startDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="uid" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="bloodGlucose" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="BloodGlucose" inverseName="sensor" inverseEntity="BloodGlucose" syncable="YES"/>
</entity>
<elements>
<element name="Event" positionX="207" positionY="-135" width="128" height="58"/>
<element name="BloodGlucose" positionX="43" positionY="81" width="128" height="148"/>
<element name="HeaderData" positionX="189" positionY="-18" width="128" height="75"/>
<element name="Reader" positionX="288" positionY="261" width="128" height="88"/>
<element name="Sensor" positionX="261" positionY="84" width="128" height="118"/>
</elements>
</model>
================================================
FILE: LibreMonitor/LibreMonitorUITests-Bridging-Header.h
================================================
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "NSData+CRC8.h"
#import "NSData+SLIP.h"
#import "constants.h"
#import "data_types.h"
================================================
FILE: LibreMonitor/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11201" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="W2V-7Y-4OF">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Blood Sugar Table View Controller-->
<scene sceneID="ImT-lg-bzr">
<objects>
<tableViewController modalPresentationStyle="overFullScreen" id="50o-9a-5MG" customClass="BloodSugarTableViewController" customModule="LibreMonitor" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="IGA-Af-Q7c">
<rect key="frame" x="0.0" y="64" width="375" height="554"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="geB-0N-HCr" detailTextLabel="7Ok-y7-eKF" style="IBUITableViewCellStyleValue2" id="KSA-Qo-0iw">
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KSA-Qo-0iw" id="f3z-vb-S0d">
<frame key="frameInset" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="geB-0N-HCr">
<frame key="frameInset" minX="15" minY="15" width="91" height="14.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Detail" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="7Ok-y7-eKF">
<frame key="frameInset" minX="112" minY="15" width="33" height="14.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="BloodSugarGraphTableViewCell" rowHeight="331" id="jdB-cO-2dn" customClass="BloodSugarGraphViewTableViewCell" customModule="LibreMonitor" customModuleProvider="target">
<rect key="frame" x="0.0" y="72" width="375" height="331"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jdB-cO-2dn" id="g5L-6G-F7W">
<frame key="frameInset" width="375" height="330.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="U1C-fo-eIu" customClass="BloodSugarGraphView" customModule="LibreMonitor" customModuleProvider="target">
<color key="backgroundColor" red="0.97488003280000002" green="0.63108614230000004" blue="0.54087663860000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="tintColor" red="0.21386732429999999" green="1" blue="0.15152120020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</subviews>
<constraints>
<constraint firstItem="U1C-fo-eIu" firstAttribute="width" secondItem="g5L-6G-F7W" secondAttribute="width" id="IJa-Mw-X6T"/>
<constraint firstItem="U1C-fo-eIu" firstAttribute="height" secondItem="g5L-6G-F7W" secondAttribute="height" id="Kzb-7c-cLU"/>
<constraint firstItem="U1C-fo-eIu" firstAttribute="centerY" secondItem="g5L-6G-F7W" secondAttribute="centerY" id="T67-rZ-fMN"/>
<constraint firstItem="U1C-fo-eIu" firstAttribute="centerX" secondItem="g5L-6G-F7W" secondAttribute="centerX" id="c4W-Lb-viC"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="barChartView" destination="U1C-fo-eIu" id="yRf-2P-7bE"/>
<outlet property="lineChartView" destination="U1C-fo-eIu" id="iPw-k8-YjO"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="50o-9a-5MG" id="Yga-5N-Ao6"/>
<outlet property="delegate" destination="50o-9a-5MG" id="f68-gz-v0t"/>
</connections>
</tableView>
<extendedEdge key="edgesForExtendedLayout"/>
<navigationItem key="navigationItem" id="LSU-c9-uSa"/>
<refreshControl key="refreshControl" opaque="NO" multipleTouchEnabled="YES" contentMode="center" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" id="ES1-fl-YYX">
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
<autoresizingMask key="autoresizingMask"/>
<attributedString key="attributedTitle">
<fragment content="Aktualisierung alle zwei Minuten im Status "Notifying".">
<attributes>
<font key="NSFont" metaFont="smallSystem"/>
<paragraphStyle key="NSParagraphStyle" alignment="center" lineBreakMode="wordWrapping" baseWritingDirection="natural" lineHeightMultiple="1" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
</attributedString>
<connections>
<action selector="doRefresh:" destination="50o-9a-5MG" eventType="valueChanged" id="dGD-br-dHx"/>
</connections>
</refreshControl>
<connections>
<segue destination="RRb-Vb-fEk" kind="showDetail" identifier="ChangeBloodGlucoseAdjustments" id="rkq-Eh-apj"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="rfD-Rn-XvB" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1239" y="1000"/>
</scene>
<!--Adjustments Table View Controller-->
<scene sceneID="KrO-eT-0KU">
<objects>
<tableViewController id="p1I-y2-sJ2" customClass="AdjustmentsTableViewController" customModule="LibreMonitor" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="6ty-D7-GsN">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<sections>
<tableViewSection headerTitle="Adjustments" footerTitle="Use offset and slope to adjust the values as good as possible to those of the Freestyle Libre Reader" id="raR-VJ-2GJ">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KhQ-e7-kta">
<rect key="frame" x="0.0" y="119.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KhQ-e7-kta" id="1J4-j6-1aL">
<frame key="frameInset" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Offset" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XaO-BA-G9S">
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="120" borderStyle="roundedRect" textAlignment="right" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="6rr-p0-qrk">
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="120" id="KIY-lx-ceo"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" keyboardType="numbersAndPunctuation"/>
</textField>
</subviews>
<constraints>
<constraint firstItem="6rr-p0-qrk" firstAttribute="baseline" secondItem="XaO-BA-G9S" secondAttribute="baseline" id="K1Q-va-Zzy"/>
<constraint firstItem="6rr-p0-qrk" firstAttribute="trailing" secondItem="1J4-j6-1aL" secondAttribute="trailingMargin" id="gJb-g7-heD"/>
<constraint firstItem="XaO-BA-G9S" firstAttribute="leading" secondItem="1J4-j6-1aL" secondAttribute="leadingMargin" constant="10" id="pgd-Lb-eVi"/>
<constraint firstItem="6rr-p0-qrk" firstAttribute="top" secondItem="1J4-j6-1aL" secondAttribute="topMargin" id="z9w-uj-8jJ"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="OHa-VS-zrB">
<rect key="frame" x="0.0" y="163.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="OHa-VS-zrB" id="JqF-JX-s9v">
<frame key="frameInset" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" preservesSuperviewLayoutMargins="YES" text="Slope" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zNa-Ly-leh">
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="13" borderStyle="roundedRect" textAlignment="right" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="f1a-bL-AF1">
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="120" id="sjq-Sh-ISQ"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" keyboardType="numbersAndPunctuation"/>
</textField>
</subviews>
<constraints>
<constraint firstItem="zNa-Ly-leh" firstAttribute="leading" secondItem="JqF-JX-s9v" secondAttribute="leadingMargin" constant="10" id="7gb-Gc-QoT"/>
<constraint firstItem="zNa-Ly-leh" firstAttribute="centerY" secondItem="JqF-JX-s9v" secondAttribute="centerY" id="AcC-oz-nwR"/>
<constraint firstItem="f1a-bL-AF1" firstAttribute="centerY" secondItem="zNa-Ly-leh" secondAttribute="centerY" id="qXh-Hq-0XW"/>
<constraint firstItem="f1a-bL-AF1" firstAttribute="trailing" secondItem="JqF-JX-s9v" secondAttribute="trailingMargin" id="zYl-eK-blb"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="p1I-y2-sJ2" id="iFC-8a-rKQ"/>
<outlet property="delegate" destination="p1I-y2-sJ2" id="4G7-sw-9L9"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="V9s-LS-E01"/>
<connections>
<outlet property="offsetTextField" destination="6rr-p0-qrk" id="MdT-ZU-Xrt"/>
<outlet property="slopeTextField" destination="f1a-bL-AF1" id="01j-ZC-4f6"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="g2J-9j-6dp" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2829" y="1000"/>
</scene>
<!--Grafik-->
<scene sceneID="ItB-yR-jGL">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="Aqc-Zr-wxr" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Grafik" id="m5f-C6-Iyo"/>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="D5j-St-ijw">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="50o-9a-5MG" kind="relationship" relationship="rootViewController" id="mac-75-ac4"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="6xi-6N-6sh" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="442" y="1000"/>
</scene>
<!--Tab Bar Controller-->
<scene sceneID="eSi-nN-T7n">
<objects>
<tabBarController automaticallyAdjustsScrollViewInsets="NO" id="W2V-7Y-4OF" sceneMemberID="viewController">
<toolbarItems/>
<tabBar key="tabBar" contentMode="scaleToFill" id="NAN-gf-Fb0">
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</tabBar>
<connections>
<segue destination="Aqc-Zr-wxr" kind="relationship" relationship="viewControllers" id="qst-Qa-KIQ"/>
<segue destination="XyS-H7-wRd" kind="relationship" relationship="viewControllers" id="dZb-aa-FZd"/>
</connections>
</tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iJp-75-dO7" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-370" y="1000"/>
</scene>
<!--Acknowledgements-->
<scene sceneID="Fu4-Xg-VqK">
<objects>
<viewController title="Acknowledgements" id="XyS-H7-wRd" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Cl1-q4-umi"/>
<viewControllerLayoutGuide type="bottom" id="4UX-Pj-Oe1"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="za1-0o-Q3C">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wh2-vh-DhE">
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<attributedString key="attributedText">
<fragment>
<string key="content">The following sets forth attribution notices for third party software that may be contained in portions of this product.
---
The following software or parts of it may be included in this product: </string>
<attributes>
<font key="NSFont" metaFont="system" size="10"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
<tabStops>
<textTab alignment="left" location="28">
<options/>
</textTab>
<textTab alignment="left" location="56">
<options/>
</textTab>
<textTab alignment="left" location="84">
<options/>
</textTab>
<textTab alignment="left" location="112">
<options/>
</textTab>
<textTab alignment="left" location="140">
<options/>
</textTab>
<textTab alignment="left" location="168">
<options/>
</textTab>
<textTab alignment="left" location="196">
<options/>
</textTab>
<textTab alignment="left" location="224">
<options/>
</textTab>
<textTab alignment="left" location="252">
<options/>
</textTab>
<textTab alignment="left" location="280">
<options/>
</textTab>
<textTab alignment="left" location="308">
<options/>
</textTab>
<textTab alignment="left" location="336">
<options/>
</textTab>
</tabStops>
</paragraphStyle>
</attributes>
</fragment>
<fragment content="CryptoSwift">
<attributes>
<font key="NSFont" size="12" name=".AppleSystemUIFont"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
<tabStops>
<textTab alignment="left" location="28">
<options/>
</textTab>
<textTab alignment="left" location="56">
<options/>
</textTab>
<textTab alignment="left" location="84">
<options/>
</textTab>
<textTab alignment="left" location="112">
<options/>
</textTab>
<textTab alignment="left" location="140">
<options/>
</textTab>
<textTab alignment="left" location="168">
<options/>
</textTab>
<textTab alignment="left" location="196">
<options/>
</textTab>
<textTab alignment="left" location="224">
<options/>
</textTab>
<textTab alignment="left" location="252">
<options/>
</textTab>
<textTab alignment="left" location="280">
<options/>
</textTab>
<textTab alignment="left" location="308">
<options/>
</textTab>
<textTab alignment="left" location="336">
<options/>
</textTab>
</tabStops>
</paragraphStyle>
</attributes>
</fragment>
<fragment>
<mutableString key="content">see https://github.com/krzyzanowskim/CryptoSwift
Copyright (C) 2014 Marcin Krzyżanowski <marcin.krzyzanowski@gmail.com>
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
- This notice may not be removed or altered from any source or binary distribution.
---
The following software or parts of it may be included in this product: </mutableString>
<attributes>
<font key="NSFont" metaFont="system" size="10"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
<tabStops>
<textTab alignment="left" location="28">
<options/>
</textTab>
<textTab alignment="left" location="56">
<options/>
</textTab>
<textTab alignment="left" location="84">
<options/>
</textTab>
<textTab alignment="left" location="112">
<options/>
</textTab>
<textTab alignment="left" location="140">
<options/>
</textTab>
<textTab alignment="left" location="168">
<options/>
</textTab>
<textTab alignment="left" location="196">
<options/>
</textTab>
<textTab alignment="left" location="224">
<options/>
</textTab>
<textTab alignment="left" location="252">
<options/>
</textTab>
<textTab alignment="left" location="280">
<options/>
</textTab>
<textTab alignment="left" location="308">
<options/>
</textTab>
<textTab alignment="left" location="336">
<options/>
</textTab>
</tabStops>
</paragraphStyle>
</attributes>
</fragment>
<fragment content="RFduinoUBP">
<attributes>
<font key="NSFont" size="12" name=".SFNSText"/>
<font key="NSOriginalFont" size="12" name=".SFNSText"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
<tabStops>
<textTab alignment="left" location="28">
<options/>
</textTab>
<textTab alignment="left" location="56">
<options/>
</textTab>
<textTab alignment="left" location="84">
<options/>
</textTab>
<textTab alignment="left" location="112">
<options/>
</textTab>
<textTab alignment="left" location="140">
<options/>
</textTab>
<textTab alignment="left" location="168">
<options/>
</textTab>
<textTab alignment="left" location="196">
<options/>
</textTab>
<textTab alignment="left" location="224">
<options/>
</textTab>
<textTab alignment="left" location="252">
<options/>
</textTab>
<textTab alignment="left" location="280">
<options/>
</textTab>
<textTab alignment="left" location="308">
<options/>
</textTab>
<textTab alignment="left" location="336">
<options/>
</textTab>
</tabStops>
</paragraphStyle>
</attributes>
</fragment>
<fragment>
<mutableString key="content">see https://github.com/cconway/RFduinoUBP
The MIT License (MIT)
Copyright (c) 2015 cconway
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. ---
The following software or parts of it may be included in this product: RFduinoUBP, see https://github.com/cconway/RFduinoUBP
---
The following software or parts of it may be included in this product: </mutableString>
<attributes>
<font key="NSFont" metaFont="system" size="10"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
<tabStops>
<textTab alignment="left" location="28">
<options/>
</textTab>
<textTab alignment="left" location="56">
<options/>
</textTab>
<textTab alignment="left" location="84">
<options/>
</textTab>
<textTab alignment="left" location="112">
<options/>
</textTab>
<textTab alignment="left" location="140">
<options/>
</textTab>
<textTab alignment="left" location="168">
<options/>
</textTab>
<textTab alignment="left" location="196">
<options/>
</textTab>
<textTab alignment="left" location="224">
<options/>
</textTab>
<textTab alignment="left" location="252">
<options/>
</textTab>
<textTab alignment="left" location="280">
<options/>
</textTab>
<textTab alignment="left" location="308">
<options/>
</textTab>
<textTab alignment="left" location="336">
<options/>
</textTab>
</tabStops>
</paragraphStyle>
</attributes>
</fragment>
<fragment content="Charts">
<attributes>
<font key="NSFont" size="12" name=".AppleSystemUIFont"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content" base64-UTF8="YES">
Cg
</string>
<attributes>
<font key="NSFont" size="10" name="HelveticaNeue"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0" allowsDefaultTighteningForTruncation="NO">
<tabStops>
<textTab alignment="left" location="28">
<options/>
</textTab>
<textTab alignment="left" location="56">
<options/>
</textTab>
<textTab alignment="left" location="84">
<options/>
</textTab>
<textTab alignment="left" location="112">
<options/>
</textTab>
<textTab alignment="left" location="140">
<options/>
</textTab>
<textTab alignment="left" location="168">
<options/>
</textTab>
<textTab alignment="left" location="196">
<options/>
</textTab>
<textTab alignment="left" location="224">
<options/>
</textTab>
<textTab alignment="left" location="252">
<options/>
</textTab>
<textTab alignment="left" location="280">
<options/>
</textTab>
<textTab alignment="left" location="308">
<options/>
</textTab>
<textTab alignment="left" location="336">
<options/>
</textTab>
</tabStops>
</paragraphStyle>
</attributes>
</fragment>
<fragment>
<mutableString key="content">see hhttps://github.com/danielgindi/Charts
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
</mutableString>
<attributes>
<font key="NSFont" metaFont="system" size="10"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
</attributedString>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="wh2-vh-DhE" firstAttribute="width" secondItem="za1-0o-Q3C" secondAttribute="width" id="00F-ce-7sG"/>
<constraint firstItem="wh2-vh-DhE" firstAttribute="top" secondItem="Cl1-q4-umi" secondAttribute="bottom" constant="8" symbolic="YES" id="dqZ-N9-a7w"/>
<constraint firstItem="wh2-vh-DhE" firstAttribute="centerX" secondItem="za1-0o-Q3C" secondAttribute="centerX" id="dxA-Vz-5Lr"/>
<constraint firstItem="wh2-vh-DhE" firstAttribute="height" secondItem="za1-0o-Q3C" secondAttribute="height" id="e5W-L2-DD4"/>
</constraints>
</view>
<tabBarItem key="tabBarItem" title="Aknwoledgements" id="cqu-02-OO6"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="KsD-cc-ZmE" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="410" y="1751"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="PcD-8F-rWp">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="RRb-Vb-fEk" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="TeE-cz-f4e">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="p1I-y2-sJ2" kind="relationship" relationship="rootViewController" id="8ew-YJ-gwy"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="eNH-XW-Zjn" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2031" y="1000"/>
</scene>
</scenes>
</document>
================================================
FILE: LibreMonitor/Model/CRC.swift
================================================
//
// CRC.swift
// LibreMonitor
//
// Created by Uwe Petersen on 26.07.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
//
// Part of this code is taken from
// CRC.swift
// CryptoSwift
//
// Created by Marcin Krzyzanowski on 25/08/14.
// Copyright (c) 2014 Marcin Krzyzanowski. All rights reserved.
//
import Foundation
final class Crc {
/// Table of precalculated crc16 values
static let crc16table: [UInt16] = [0, 4489, 8978, 12955, 17956, 22445, 25910, 29887, 35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735, 4225, 264, 13203, 8730, 22181, 18220, 30135, 25662, 40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510, 8450, 12427, 528, 5017, 26406, 30383, 17460, 21949, 44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797, 12675, 8202, 4753, 792, 30631, 26158, 21685, 17724, 48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572, 16900, 21389, 24854, 28831, 1056, 5545, 10034, 14011, 52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859, 21125, 17164, 29079, 24606, 5281, 1320, 14259, 9786, 57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634, 25350, 29327, 16404, 20893, 9506, 13483, 1584, 6073, 61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921, 29575, 25102, 20629, 16668, 13731, 9258, 5809, 1848, 65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696, 33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623, 2112, 6601, 11090, 15067, 20068, 24557, 28022, 31999, 38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398, 6337, 2376, 15315, 10842, 24293, 20332, 32247, 27774, 42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685, 10562, 14539, 2640, 7129, 28518, 32495, 19572, 24061, 46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460, 14787, 10314, 6865, 2904, 32743, 28270, 23797, 19836, 50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747, 19012, 23501, 26966, 30943, 3168, 7657, 12146, 16123, 54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522, 23237, 19276, 31191, 26718, 7393, 3432, 16371, 11898, 59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809, 27462, 31439, 18516, 23005, 11618, 15595, 3696, 8185, 63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584, 31687, 27214, 22741, 18780, 15843, 11370, 7921, 3960]
/// Calculates crc16. Taken from https://github.com/krzyzanowskim/CryptoSwift with modifications (reversing and byte swapping) to adjust for crc as used by Freestyle Libre
///
/// - parameter message: Array of bytes for which the crc is to be calculated
/// - parameter seed: seed for crc
///
/// - returns: crc16
static func crc16(_ message:[UInt8], seed: UInt16? = nil) -> UInt16 {
var crc: UInt16 = seed != nil ? seed! : 0x0000
// calculate crc
for chunk in BytesSequence(chunkSize: 256, data: message) {
for b in chunk {
crc = (crc >> 8) ^ crc16table[Int((crc ^ UInt16(b)) & 0xFF)]
}
}
// reverse the bits (modification by Uwe Petersen, 2016-06-059
var reverseCrc = UInt16(0)
for _ in 0..<16 {
reverseCrc = reverseCrc << 1 | crc & 1
crc >>= 1
}
// swap bytes and return (modification by Uwe Petersen, 2016-06-059
return reverseCrc.byteSwapped
}
/// Checks crc for an array of bytes.
///
/// Assumes that the first two bytes are the crc16 of the bytes array and compares the corresponding value with the crc16 calculated over the rest of the array of bytes.
///
/// - parameter bytes: Array of bytes with a crc in the first two bytes
///
/// - returns: true if crc is valid
static func hasValidCrc16InFirstTwoBytes(_ bytes: [UInt8]) -> Bool {
print(Array(bytes.dropFirst(2)))
let calculatedCrc = Crc.crc16(Array(bytes.dropFirst(2)), seed: 0xffff)
let enclosedCrc = (UInt16(bytes[0]) << 8) | UInt16(bytes[1])
// print(String(format: "Calculated crc is %X and enclosed crc is %x", arguments: [calculatedCrc, enclosedCrc]))
return calculatedCrc == enclosedCrc
}
}
/// Struct BytesSequence, taken from https://github.com/krzyzanowskim/CryptoSwift
struct BytesSequence: Sequence {
let chunkSize: Int
let data: Array<UInt8>
func makeIterator() -> AnyIterator<ArraySlice<UInt8>> {
var offset:Int = 0
return AnyIterator {
let end = Swift.min(self.chunkSize, self.data.count - offset)
let result = self.data[offset..<offset + end]
offset += result.count
return !result.isEmpty ? result : nil
}
}
}
================================================
FILE: LibreMonitor/Model/LibreSensor.swift
================================================
//
// LibreSensor.swift
// LibreMonitor
//
// Created by Uwe Petersen on 15.05.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
class LibreSensor {
var uid: String
lazy var serialNumber: String = {
// The serial number of the sensor can be derived from its uid.
//
// The numbers an letters of the serial number are coded a compressed scheme that uses only 32 numbers and letters,
// by omitting the letters B, I, O and S. This information is stored in consecutive units of five bits.
//
// The encding thus is as follows:
// index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// char: 0 1 2 3 4 5 6 7 8 9 A (B) C D E F G H (I) J K L M N (O) P Q R (S) T U V W X Y Z
//
// Example:
// Uid is E0 07 A0 00 00 25 90 5E, and the corresponding serial number is "0M00009DHCR"
// \ / \ /
// -+- -----+--------
// | |
// | +-- This part encodes the serial number, see below
// +-- Standard first two bytes, where 0x07 is the code for "Texas Instruments Tag-it™", see https://en.wikipedia.org/wiki/ISO/IEC_15693
//
// 1.) Convert the part without E007, i.e. A0 00 00 25 90 5E to binary representation
//
// A 0 0 0 0 0 2 5 9 0 5 E
// 1010 0000 0000 0000 0000 0000 0010 0101 1001 0000 0101 1110
//
// 2.) Split this binary array in units of five bits length from the beginning and pad with two zeros at the end and
// calculate the corresponding integer and retreive the corresponding char from the table above
//
// +-- 1010 0000 0000 0000 0000 0000 0010 0101 1001 0000 0101 1110 + 00
// |
// +-> 10100 00000 00000 00000 00000 01001 01100 10000 01011 11000
// | | | | | | | | | |
// | | | | | | | | | +- = 24 -> "R"
// | | | | | | | | +------- = 11 -> "C"
// | | | | | | | +------------- = 16 -> "H"
// | | | | | | +------------------- = 12 -> "D"
// | | | | | +------------------------- = 9 -> "9"
// | | | | +------------------------------- = 0 -> "0"
// | | | +------------------------------------- = 0 -> "0"
// | | +------------------------------------------- = 0 -> "0"
// | +------------------------------------------------- = 0 -> "0"
// +------------------------------------------------------- = 20 -> "M"
//
// 3.) Prepend "0" at the beginning an thus receive "0M00009DHCR"
let lookupTable = ["0","1","2","3","4","5","6","7","8","9","A","C","D","E","F","G","H","J","K","L","M","N","P","Q","R","T","U","V","W","X","Y","Z"]
let uidString = self.uid.substring(from: self.uid.characters.index(self.uid.startIndex, offsetBy: 4)) // "E007A0000025905E" -> "A0000025905E"
var serialNumber = ""
guard uidString.lengthOfBytes(using: String.Encoding.ascii) == 12,
let uidAsInt = Int(uidString, radix: 16) // "A0000025905E" -> 175921862905950
else {return ""}
let uidAsBinaryString = String(uidAsInt, radix: 2) + "00" // -> "10100000000000000000000000100101100100000101111000"
let length = uidAsBinaryString.lengthOfBytes(using: String.Encoding.ascii)
for index in stride(from: 0, to: length, by: 5) {
if index + 4 < length {
let startIndex = uidAsBinaryString.startIndex
let leftIndex = uidAsBinaryString.index(startIndex, offsetBy: index)
let rightIndex = uidAsBinaryString.index(startIndex, offsetBy: index+5)
let range = leftIndex..<rightIndex
let fiveBits = uidAsBinaryString.substring(with: range)
if let theInt = Int(fiveBits, radix: 2) , theInt >= 0 && theInt < lookupTable.count {
serialNumber += lookupTable[theInt] // "10100" -> 20 -> "M"
}
}
}
serialNumber = "0" + serialNumber // "M00009DHCR" -> "0M00009DHCR"
return serialNumber
}()
lazy var prettyUid: String = {
var prettyUid = self.uid
let length = self.uid.lengthOfBytes(using: String.Encoding.ascii)
for index in stride(from: 2, to: length, by: 2).reversed() {
prettyUid.insert(Character(":"), at: prettyUid.characters.index(prettyUid.startIndex, offsetBy: index))
}
return prettyUid
}()
init(withUID uid: String) {
self.uid = uid
}
}
================================================
FILE: LibreMonitor/Model/Measurement.swift
================================================
//
// Measurement.swift
// LibreMonitor
//
// Created by Uwe Petersen on 25.08.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
/// Structure for one glucose measurement including value, date and raw data bytes
struct Measurement {
/// The date for this measurement
let date: Date
/// The bytes as read from the sensor. All data is derived from this \"raw data"
let bytes: [UInt8]
/// The bytes as String
let byteString: String
/// The raw value as read from the sensor
let rawValue: Int
/// slope to calculate glucose from raw value in (mg/dl)/raw
let slope: Double
/// glucose offset to be added in mg/dl
let offset: Double
/// The glucose value in mg/dl
let glucose: Double
/// Initialize a new glucose measurement
///
/// - parameter bytes: raw data bytes as read from the sensor
/// - parameter slope: slope to calculate glucose from raw value in (mg/dl)/raw
/// - parameter offset: glucose offset to be added in mg/dl
/// - parameter date: date of the measurement
///
/// - returns: Measurement
init(bytes: [UInt8], slope: Double = 0.1, offset: Double = 0.0, date: Date) {
self.bytes = bytes
self.byteString = bytes.reduce("", {$0 + String(format: "%02X", arguments: [$1])})
self.rawValue = (Int(bytes[1]) << 8) & 0x0F00 + Int(bytes[0])
self.slope = slope
self.offset = offset
self.glucose = offset + slope * Double(rawValue)
self.date = date
}
var description: String {
return String("Glucose: \(glucose) (mg/dl), date: \(date), slope: \(slope), offset: \(offset),rawValue: \(rawValue), bytes: \(bytes)" )
}
}
================================================
FILE: LibreMonitor/Model/SensorData.swift
================================================
//
// SensorData
// LibreMonitor
//
// Created by Uwe Petersen on 26.07.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
/// Structure for data from Freestyle Libre sensor
/// To be initialized with the bytes as read via nfc. Provides all derived data.
struct SensorData {
/// Number of bytes of sensor data to be used (read only), i.e. 344 bytes (24 for header, 296 for body and 24 for footer)
let numberOfBytes = 344 // Header and body and footer of Freestyle Libre data (i.e. 40 blocks of 8 bytes)
/// Array of 344 bytes as read via nfc
let bytes: [UInt8]
/// Subarray of 24 header bytes
let header: [UInt8]
/// Subarray of 296 body bytes
let body: [UInt8]
/// Subarray of 24 footer bytes
let footer: [UInt8]
/// Date when data was read from sensor
let date: Date
/// Minutes (approx) since start of sensor
let minutesSinceStart: Int
/// Index on the next block of trend data that the sensor will measure and store
let nextTrendBlock: Int
/// Index on the next block of history data that the sensor will create from trend data and store
let nextHistoryBlock: Int
/// true if the header crc, stored in the first two bytes, is equal to the calculated crc
var hasValidHeaderCRC: Bool {
return Crc.hasValidCrc16InFirstTwoBytes(header)
}
/// true if the body crc, stored in the first two bytes, is equal to the calculated crc
var hasValidBodyCRC: Bool {
return Crc.hasValidCrc16InFirstTwoBytes(body)
}
/// true if the footer crc, stored in the first two bytes, is equal to the calculated crc
var hasValidFooterCRC: Bool {
return Crc.hasValidCrc16InFirstTwoBytes(footer)
}
/// Sensor state (ready, failure, starting etc.)
var state: SensorState {
switch header[4] {
case 01:
return SensorState.notYetStarted
case 02:
return SensorState.starting
case 03:
return SensorState.ready
case 04:
return SensorState.expired
case 05:
return SensorState.shutdown
case 06:
return SensorState.failure
default:
return SensorState.unknown
}
}
init?(bytes: [UInt8], date: Date = Date()) {
guard bytes.count == numberOfBytes else {
return nil
}
self.bytes = bytes
self.date = date
let headerRange = 0..<24 // 24 bytes, i.e. 3 blocks a 8 bytes
let bodyRange = 24..<320 // 296 bytes, i.e. 37 blocks a 8 bytes
let footerRange = 320..<344 // 24 bytes, i.e. 3 blocks a 8 bytes
self.header = Array(bytes[headerRange])
self.body = Array(bytes[bodyRange])
self.footer = Array(bytes[footerRange])
self.nextTrendBlock = Int(body[2])
self.nextHistoryBlock = Int(body[3])
self.minutesSinceStart = Int(body[293]) << 8 + Int(body[292])
}
/// Get array of 16 trend glucose measurements.
/// Each array is sorted such that the most recent value is at index 0 and corresponds to the time when the sensor was read, i.e. self.date. The following measurements are each one more minute behind, i.e. -1 minute, -2 mintes, -3 minutes, ... -15 minutes.
///
/// - parameter offset: offset in mg/dl that is added
/// - parameter slope: slope in (mg/dl)/ raw
///
/// - returns: Array of Measurements
func trendMeasurements(_ offset: Double = 0.0, slope: Double = 0.1) -> [Measurement] {
var measurements = [Measurement]()
// Trend data is stored in body from byte 4 to byte 4+96=100 in units of 6 bytes. Index on data such that most recent block is first.
for blockIndex in 0...15 {
var index = 4 + (nextTrendBlock - 1 - blockIndex) * 6 // runs backwards
if index < 4 {
index = index + 96 // if end of ring buffer is reached shift to beginning of ring buffer
}
let range = index..<index+6
let measurementBytes = Array(body[range])
let measurementDate = date.addingTimeInterval(Double(-60 * blockIndex))
let measurement = Measurement(bytes: measurementBytes, slope: slope, offset: offset, date: measurementDate)
measurements.append(measurement)
}
return measurements
}
/// Get array of 32 history glucose measurements.
/// Each array is sorted such that the most recent value is at index 0. This most recent value corresponds to -(minutesSinceStart - 3) % 15 + 3. The following measurements are each 15 more minutes behind, i.e. -15 minutes behind, -30 minutes, -45 minutes, ... .
///
/// - parameter offset: offset in mg/dl that is added
/// - parameter slope: slope in (mg/dl)/ raw
///
/// - returns: Array of Measurements
func historyMeasurements(_ offset: Double = 0.0, slope: Double = 0.1) -> [Measurement] {
let mostRecentHistoryDate = date.addingTimeInterval( 60.0 * -Double( (minutesSinceStart - 3) % 15 + 3 ) )
var measurements = [Measurement]()
// History data is stored in body from byte 100 to byte 100+192-1=291 in units of 6 bytes. Index on data such that most recent block is first.
for blockIndex in 0..<32 {
var index = 100 + (nextHistoryBlock - 1 - blockIndex) * 6 // runs backwards
if index < 100 {
index = index + 192 // if end of ring buffer is reached shift to beginning of ring buffer
}
let range = index..<index+6
let measurementBytes = Array(body[range])
let measurementDate = mostRecentHistoryDate.addingTimeInterval(Double(-900 * blockIndex)) // 900 = 60 * 15
let measurement = Measurement(bytes: measurementBytes, slope: slope, offset: offset, date: measurementDate)
measurements.append(measurement)
}
return measurements
}
}
================================================
FILE: LibreMonitor/Model/SensorState.swift
================================================
//
// SensorState.swift
// LibreMonitor
//
// Created by Uwe Petersen on 31.07.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
/// State of the freestyle libre sensor
///
/// - notYetStarted: 0x01 sensor not yet started
/// - starting: 0x02 sensor is in the starting phase
/// - ready: 0x03 sensor is ready, i.e. in normal operation mode
/// - stateFour: 0x04 state with yet unknown meaning
/// - expired: 0x05 sensor is expired
/// - failure: 0x06 sensor has an error
/// - unknown: any other state
enum SensorState {
case notYetStarted
case starting
case ready
case expired
case shutdown
case failure
case unknown
init(){
self = .unknown
}
var description: String {
switch self {
case .notYetStarted:
return "Sensor not yet startet"
case .starting:
return "Sensor in starting phase"
case .ready:
return "Sensor is ready"
case .expired:
return "Sensor is expired"
case .shutdown:
return "Sensor is shut down"
case .failure:
return "Sensor has failure"
default:
return "Unknown sensor state"
}
}
}
================================================
FILE: LibreMonitor/ModelCoreData/BloodGlucose+CoreDataClass.swift
================================================
//
// BloodGlucose+CoreDataClass.swift
// LibreMonitor
//
// Created by Uwe Petersen on 09.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
public class BloodGlucose: NSManagedObject {
}
================================================
FILE: LibreMonitor/ModelCoreData/BloodGlucose+CoreDataProperties.swift
================================================
//
// BloodGlucose+CoreDataProperties.swift
// LibreMonitor
//
// Created by Uwe Petersen on 09.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
extension BloodGlucose {
@nonobjc public class func fetchRequest() -> NSFetchRequest<BloodGlucose> {
return NSFetchRequest<BloodGlucose>(entityName: "BloodGlucose");
}
@NSManaged public var bytes: String?
@NSManaged public var dateString: String?
@NSManaged public var id: Int32
@NSManaged public var type: Int16
@NSManaged public var value: Double
@NSManaged public var date: NSDate?
@NSManaged public var sensor: Sensor?
}
================================================
FILE: LibreMonitor/ModelCoreData/CoreDataStack.swift
================================================
//
// CoreDataStack.swift
// LibreMonitor
//
// Created by Uwe Petersen on 13.04.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import UIKit
import CoreData
class CoreDataStack: NSObject {
// var managedObjectContext: NSManagedObjectContext
override init() {
}
// override init() {
//
// // This resource is the same name as your xcdatamodeld contained in your project.
// guard let modelURL = NSBundle.mainBundle().URLForResource("DataModel", withExtension:"momd") else {
// fatalError("Error loading model from bundle")
// }
//
// // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
// guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else {
// fatalError("Error initializing mom from: \(modelURL)")
// }
//
// let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
//
// managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
// managedObjectContext.persistentStoreCoordinator = psc
//
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
// let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
// let docURL = urls[urls.endIndex-1]
// /* The directory the application uses to store the Core Data store file.
// This code uses a file named "DataModel.sqlite" in the application's documents directory.
// */
// let storeURL = docURL.URLByAppendingPathComponent("DataModel.sqlite")
// do {
// try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil)
// } catch {
// fatalError("Error migrating store: \(error)")
// }
// }
// }
lazy var applicationDocumentsDirectory: URL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "UPP.LibreMonitor" in the application's documents Application Support directory.
var urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = Bundle.main.url(forResource: "LibreMonitor", withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}
}
================================================
FILE: LibreMonitor/ModelCoreData/HeaderData+CoreDataClass.swift
================================================
//
// HeaderData+CoreDataClass.swift
// LibreMonitor
//
// Created by Uwe Petersen on 09.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
@objc(HeaderData)
public class HeaderData: NSManagedObject {
}
================================================
FILE: LibreMonitor/ModelCoreData/HeaderData+CoreDataProperties.swift
================================================
//
// HeaderData+CoreDataProperties.swift
// LibreMonitor
//
// Created by Uwe Petersen on 09.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
extension HeaderData {
@nonobjc public class func fetchRequest() -> NSFetchRequest<HeaderData> {
return NSFetchRequest<HeaderData>(entityName: "HeaderData");
}
@NSManaged public var bytes: String?
@NSManaged public var date: NSDate?
}
================================================
FILE: LibreMonitor/ModelCoreData/Reader+CoreDataClass.swift
================================================
//
// Reader+CoreDataClass.swift
// LibreMonitor
//
// Created by Uwe Petersen on 09.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
public class Reader: NSManagedObject {
}
================================================
FILE: LibreMonitor/ModelCoreData/Reader+CoreDataProperties.swift
================================================
//
// Reader+CoreDataProperties.swift
// LibreMonitor
//
// Created by Uwe Petersen on 09.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
extension Reader {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Reader> {
return NSFetchRequest<Reader>(entityName: "Reader");
}
@NSManaged public var batteryVoltage: Double
@NSManaged public var temperature: Double
@NSManaged public var uid: String?
}
================================================
FILE: LibreMonitor/ModelCoreData/Sensor+CoreDataClass.swift
================================================
//
// Sensor+CoreDataClass.swift
// LibreMonitor
//
// Created by Uwe Petersen on 09.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
public class Sensor: NSManagedObject {
}
================================================
FILE: LibreMonitor/ModelCoreData/Sensor+CoreDataProperties.swift
================================================
//
// Sensor+CoreDataProperties.swift
// LibreMonitor
//
// Created by Uwe Petersen on 09.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
extension Sensor {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Sensor> {
return NSFetchRequest<Sensor>(entityName: "Sensor");
}
@NSManaged public var startDate: NSDate?
@NSManaged public var uid: String?
@NSManaged public var lastScanDate: NSDate?
@NSManaged public var minutesSinceStart: Int32
@NSManaged public var bloodGlucose: NSSet?
}
// MARK: Generated accessors for bloodGlucose
extension Sensor {
@objc(addBloodGlucoseObject:)
@NSManaged public func addToBloodGlucose(_ value: BloodGlucose)
@objc(removeBloodGlucoseObject:)
@NSManaged public func removeFromBloodGlucose(_ value: BloodGlucose)
@objc(addBloodGlucose:)
@NSManaged public func addToBloodGlucose(_ values: NSSet)
@objc(removeBloodGlucose:)
@NSManaged public func removeFromBloodGlucose(_ values: NSSet)
}
================================================
FILE: LibreMonitor/Reader+CoreDataClass.swift
================================================
//
// Reader+CoreDataClass.swift
// LibreMonitor
//
// Created by Uwe Petersen on 14.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
public class Reader: NSManagedObject {
}
================================================
FILE: LibreMonitor/Reader+CoreDataProperties.swift
================================================
//
// Reader+CoreDataProperties.swift
// LibreMonitor
//
// Created by Uwe Petersen on 14.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
extension Reader {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Reader> {
return NSFetchRequest<Reader>(entityName: "Reader");
}
@NSManaged public var batteryVoltage: Double
@NSManaged public var temperature: Double
@NSManaged public var uid: String?
}
================================================
FILE: LibreMonitor/Sensor+CoreDataClass.swift
================================================
//
// Sensor+CoreDataClass.swift
// LibreMonitor
//
// Created by Uwe Petersen on 14.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
public class Sensor: NSManagedObject {
}
================================================
FILE: LibreMonitor/Sensor+CoreDataProperties.swift
================================================
//
// Sensor+CoreDataProperties.swift
// LibreMonitor
//
// Created by Uwe Petersen on 14.10.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import CoreData
extension Sensor {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Sensor> {
return NSFetchRequest<Sensor>(entityName: "Sensor");
}
@NSManaged public var lastScanDate: NSDate?
@NSManaged public var minutesSinceStart: Int32
@NSManaged public var startDate: NSDate?
@NSManaged public var uid: String?
@NSManaged public var bloodGlucose: NSSet?
}
// MARK: Generated accessors for bloodGlucose
extension Sensor {
@objc(addBloodGlucoseObject:)
@NSManaged public func addToBloodGlucose(_ value: BloodGlucose)
@objc(removeBloodGlucoseObject:)
@NSManaged public func removeFromBloodGlucose(_ value: BloodGlucose)
@objc(addBloodGlucose:)
@NSManaged public func addToBloodGlucose(_ values: NSSet)
@objc(removeBloodGlucose:)
@NSManaged public func removeFromBloodGlucose(_ values: NSSet)
}
================================================
FILE: LibreMonitor/SimbleeCode/crc8.h
================================================
// LICENSES: [077915]
// -----------------------------------
// The contents of this file contains the aggregate of contributions
// covered under one or more licences. The full text of those licenses
// can be found in the "LICENSES" file at the top level of this project
// identified by the MD5 fingerprints listed above.
//
// Uwe Petersen: Modified version to test transmission part in iOS
#import <Foundation/Foundation.h>
typedef uint8_t byte;
byte CRC8(void *data_in, byte number_of_bytes_to_read);
================================================
FILE: LibreMonitor/SimbleeCode/crc8.m
================================================
// LICENSES: [077915]
// -----------------------------------
// The contents of this file contains the aggregate of contributions
// covered under one or more licences. The full text of those licenses
// can be found in the "LICENSES" file at the top level of this project
// identified by the MD5 fingerprints listed above.
//
//
// Uwe Petersen: Modified version to test transmission part in iOS
#include "crc8.h"
#define CRC8INIT 0x00
#define CRC8POLY 0x18 // 0X18 = X^8+X^5+X^4+X^0
byte CRC8(void *bytes, byte number_of_bytes_to_read) {
byte *data_in = (byte*) bytes;
byte crc;
uint16_t loop_count;
byte bit_counter;
byte data;
byte feedback_bit;
crc = CRC8INIT;
for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++)
{
data = data_in[loop_count];
bit_counter = 8;
do {
feedback_bit = (crc ^ data) & 0x01;
if ( feedback_bit == 0x01 ) {
crc = crc ^ CRC8POLY;
}
crc = (crc >> 1) & 0x7F;
if ( feedback_bit == 0x01 ) {
crc = crc | 0x80;
}
data = data >> 1;
bit_counter--;
} while (bit_counter > 0);
}
return crc;
}
================================================
FILE: LibreMonitor/SimbleeCode/libUBP.h
================================================
//
// Uwe Petersen: Modified version to test transmission part in iOS
#import <Foundation/Foundation.h>
typedef uint8_t byte;
#ifndef libUBP_h
#define libUBP_h
#endif
typedef enum {
UBP_TxFlagNone = 0 << 0,
UBP_TxFlagIsRPC = 1 << 0,
UBP_TxFlagRequiresACK = 1 << 1
} UBP_TxFlags;
// Public
void UBP_pump();
bool UBP_queuePacketTransmission(unsigned short packetIdentifier, UBP_TxFlags txFlags, const char *packetBytes, unsigned short byteCount);
//bool UBP_isBusy();
// 2016-06-26: Needed for tests
void getTxBuffer(char *txBuffer, int *txBufferLength);
void SimbleeBLE_onConnect();
void SimbleeBLE_onDisconnect();
// Private
void _UBP_pumpTxQueue();
//void _UBP_ingestRxBytes(char *receivedBytes, int byteCount);
int _UBP_makeEscapedCopy(const char *inputBuffer, unsigned short inputBufferLength, char *outputBuffer, unsigned short outputBufferLength);
int _UBP_makeUnEscapedCopy(const char *inputBuffer, unsigned short inputBufferLength, char *outputBuffer);
void _UBP_hostDisconnected();
// To be implemented by end-user externally
//extern void UBP_incomingChecksumFailed() __attribute__((weak));
//extern void UBP_receivedPacket(unsigned short packetIdentifier, UBP_TxFlags txFlags, void *packetBuffer) __attribute__((weak));
//extern void UBP_didAdvertise(bool start) __attribute__((weak));
//extern void UBP_didConnect() __attribute__((weak));
//extern void UBP_didDisconnect() __attribute__((weak));
================================================
FILE: LibreMonitor/SimbleeCode/libUBP.m
================================================
//
// Uwe Petersen: Modified version to test transmission part in iOS
#import "libUBP.h"
#import "crc8.h"
// Build-time configurations
//#define BUFFER_LENGTH 64 // Uwe changed this on 2016-01-04
#define BUFFER_LENGTH 400 // Uwe changed this on 2016-07-24
#define TX_CHUNK_SIZE 20
#define PACKET_ID_SIZE 2
// Serial Line IP (SLIP) escaping constants
#define ESCAPE_BYTE 0xDB
#define END_BYTE 0xC0
#define ESCAPED_ESCAPE_BYTE 0xDD
#define ESCAPED_END_BYTE 0xDC
const char escapeSequence_[1] = {ESCAPE_BYTE};
const char endSequence_[1] = {END_BYTE};
const char escapedEndSequence_[2] = {ESCAPE_BYTE, ESCAPED_END_BYTE};
const char escapedEscapeSequence_[2] = {ESCAPE_BYTE, ESCAPED_ESCAPE_BYTE};
// Buffers
char ubpTxBuffer[BUFFER_LENGTH];
int ubpTxBufferLength = 0;
int ubpTxBufferSentByteCount = 0;
char ubpRxBuffer[BUFFER_LENGTH];
int ubpRxBufferLength = 0;
char ubpUnescapedRxBufferBuffer[BUFFER_LENGTH];
int ubpUnescapedRxBufferBufferLength = 0;
// State Variables
bool UBP_isTxPending = false;
bool hostIsConnected = true;
//bool hostIsConnected = false;
// 2016-06-27: for testing purposes simulate simblee
char simbleeBuffer[BUFFER_LENGTH];
int simbleeBufferLength = 0;
/*
// Functions
bool UBP_isBusy() {
return UBP_isTxPending;
}
*/
void UBP_pump() {
_UBP_pumpTxQueue();
}
void _UBP_pumpTxQueue() {
if (UBP_isTxPending) {
char *nextByteToSend = ubpTxBuffer + ubpTxBufferSentByteCount;
// Try sending the next chunk
if (ubpTxBufferSentByteCount < ubpTxBufferLength && hostIsConnected) { // Haven't already sent all the bytes
int retryRemainingCount = 1000; // Limit the number of times we retry sending to avoid getting into an infinite loop
int remainingByteCount = ubpTxBufferLength - ubpTxBufferSentByteCount;
if (remainingByteCount >= TX_CHUNK_SIZE) { // Can fill the TX output buffer
// Send is queued (the ble stack delays send to the start of the next tx window)
while (hostIsConnected && retryRemainingCount > 0) {
retryRemainingCount--;
};
// 2016-06-27: This changed to the above to enable testing
// while ( SimbleeBLE.send(nextByteToSend, TX_CHUNK_SIZE) == false && hostIsConnected && retryRemainingCount > 0) {
// retryRemainingCount--;
// }; // send() returns false if all tx buffers in use (can't enqueue, try again later)
ubpTxBufferSentByteCount += TX_CHUNK_SIZE;
} else { // Only partial TX buffer remaining to send
// Send is queued (the ble stack delays send to the start of the next tx window)
while ( hostIsConnected && retryRemainingCount > 0) {
retryRemainingCount--;
}; // send() returns false if all tx buffers in use (can't enqueue, try again later)
// 2016-06-27: This changed to the above to enable testing
// while ( SimbleeBLE.send(nextByteToSend, remainingByteCount) == false && hostIsConnected && retryRemainingCount > 0) {
// retryRemainingCount--;
// }; // send() returns false if all tx buffers in use (can't enqueue, try again later)
ubpTxBufferSentByteCount += remainingByteCount;
UBP_isTxPending = false;
}
}
}
}
/*
void _UBP_ingestRxBytes(char *receivedBytes, int byteCount) {
Serial.print(byteCount);
Serial.println(" bytes receieved");
// NOTE: Assuming not called unless len > 0
// Determine what to do with incoming fragment
if ( *receivedBytes == END_BYTE ) { // Fragment has leading END byte, signals start of packet
// Set fragment as the beginning of the reconstruction buffer
memcpy(ubpRxBuffer, receivedBytes, byteCount);
ubpRxBufferLength = byteCount;
} else if (ubpRxBufferLength > 0) { // Already have fragments in the reconstruction buffer
// Append fragment to reconstruction buffer
memcpy(ubpRxBuffer + ubpRxBufferLength, receivedBytes, byteCount);
ubpRxBufferLength += byteCount;
}
// Check RX buffer for trailing END byte
if ( *(ubpRxBuffer + ubpRxBufferLength - 1) == END_BYTE) { // RX buffer ends with END byte, looks like packet is complete
byte firstNonControlIndex = 1;
byte escapedDataLength = ubpRxBufferLength - 2; // "- 2" for leading/trailing control chars
// Un-escape the incoming payload
ubpUnescapedRxBufferBufferLength = _UBP_makeUnEscapedCopy(ubpRxBuffer + firstNonControlIndex, escapedDataLength, ubpUnescapedRxBufferBuffer);
byte payloadDataLength = ubpUnescapedRxBufferBufferLength - 1; // -1 to account for checksum
// Calculate checksum over payload, i.e. all bytes except for last checksum byte)
char calculatedChecksum = CRC8(ubpUnescapedRxBufferBuffer, payloadDataLength * sizeof(byte));
// Extract embedded checksum value
char receivedChecksum = *(ubpUnescapedRxBufferBuffer + payloadDataLength); // NOTE: Omitting '-1' because checksum byte comes just after payloadDataLength
// Verify the checksum
if (calculatedChecksum == receivedChecksum) {
unsigned short packetIdentifier = *(ubpUnescapedRxBufferBuffer);
UBP_TxFlags txFlags = (UBP_TxFlags) *(ubpUnescapedRxBufferBuffer + PACKET_ID_SIZE);
if (UBP_receivedPacket) {
void *packetBuffer = (ubpUnescapedRxBufferBuffer + PACKET_ID_SIZE + 1); // skip <identifier length> + <tx flags length>
UBP_receivedPacket(packetIdentifier, txFlags, packetBuffer);
}
} else {
Serial.println("Incoming packet checksum invalid");
// Reset
ubpRxBufferLength = 0;
ubpUnescapedRxBufferBufferLength = 0;
if (UBP_incomingChecksumFailed) {
UBP_incomingChecksumFailed();
}
}
} // else haven't RX'd final fragment yet, keep waiting
}
*/
// only needed to retreive the data for testing
void getTxBuffer(char *txBuffer, int *txBufferLength) {
for (int i=0; i<ubpTxBufferLength; i++) {
txBuffer[i] = ubpTxBuffer[i];
}
*txBufferLength = ubpTxBufferLength;
}
bool UBP_queuePacketTransmission(unsigned short packetIdentifier, UBP_TxFlags txFlags, const char *packetBytes, unsigned short byteCount) {
if (UBP_isTxPending) { // Preexisting transmission still in progress
// Serial.println("Could not queue packet because preexisting transmission is still in progress");
return false;
} else { // Ready to queue a new transmission
if (hostIsConnected == false) return ubpTxBuffer;
ubpTxBufferLength = 0;
// Start off with the END_BYTE as required for SLIP
ubpTxBuffer[0] = END_BYTE;
ubpTxBufferLength++;
// Prepend the packet identifier
memcpy(ubpTxBuffer + ubpTxBufferLength, &packetIdentifier, sizeof(packetIdentifier)); // TODO: Escape the identifier
ubpTxBufferLength += sizeof(packetIdentifier);
// Append the transmission flags
ubpTxBuffer[ubpTxBufferLength] = txFlags;
ubpTxBufferLength++;
// Copy the escaped contents of packetBytes into the TX buffer following the packet identifier
int escapedByteCount = _UBP_makeEscapedCopy(packetBytes, byteCount, ubpTxBuffer + ubpTxBufferLength, BUFFER_LENGTH);
if (escapedByteCount != -1) { // Escaping succeeded
ubpTxBufferLength += escapedByteCount;
int payloadLength = ubpTxBufferLength - 1; // Length so far minus leading END byte //sizeof(packetIdentifier) + escapedByteCount; // <identifier length> + <escaped content length>
// Calculate and append checksum
byte checksumValue = CRC8(ubpTxBuffer + 1, payloadLength); // Checksum over all payload bytes (minus the leading END byte, checksum, and trailing END byte)
*(ubpTxBuffer + ubpTxBufferLength) = checksumValue;
ubpTxBufferLength++;
// Append trailing END byte
*(ubpTxBuffer + ubpTxBufferLength) = END_BYTE;
ubpTxBufferLength++;
// Mark as ready to begin transmission
ubpTxBufferSentByteCount = 0;
UBP_isTxPending = true;
} else {
return false; // Return false if we couldn't escape the content because it was going to overflow the output buffer
}
}
// FIXME: 2016-06-27: this was missing in the original code
return true;
}
int _UBP_makeEscapedCopy(const char *inputBuffer, unsigned short inputBufferLength, char *outputBuffer, unsigned short outputBufferLength) {
unsigned int bytesCopied = 0;
const char *inputBytes = inputBuffer; // Cast here to avoid compiler warnings later
for (char i = 0; i < inputBufferLength; i++) { // For each byte to append
// Escape any control characters. Refer to Serial Line IP (SLIP) spec.
char aByte = *(inputBytes + i);
if (aByte == (char) ESCAPE_BYTE) { // Escape an ESCAPE_BYTE
// if (aByte == ESCAPE_BYTE) { // Escape an ESCAPE_BYTE
if (bytesCopied + 1 >= outputBufferLength) return -1; // Would¥ overflow destination buffer
else {
*(outputBuffer + bytesCopied++) = ESCAPE_BYTE; // Write ESCAPE_BYTE to buffer and increment offset
*(outputBuffer + bytesCopied++) = ESCAPED_ESCAPE_BYTE; // Write escaped ESCAPE_BYTE to buffer and increment offset
}
// } else if (aByte == END_BYTE) { // Escape an END_BYTE
} else if (aByte == (char) END_BYTE) { // Escape an END_BYTE
if (bytesCopied + 1 >= outputBufferLength) return -1; // Would overflow destination buffer
else {
*(outputBuffer + bytesCopied++) = ESCAPE_BYTE; // Write ESCAPE_BYTE to buffer and increment offset
*(outputBuffer + bytesCopied++) = ESCAPED_END_BYTE; // Write escaped END_BYTE to buffer and increment offset
}
} else { // Not a control character
if (bytesCopied >= outputBufferLength) return -1; // Would overflow destination buffer
else *(outputBuffer + bytesCopied++) = aByte; // Copy the unmolested byte to the buffer and increment offset
}
}
return bytesCopied;
}
/*
int _UBP_makeUnEscapedCopy(const char *inputBuffer, unsigned short inputBufferLength, char *outputBuffer) {
bool done = false;
char * destinationBufferPtr = outputBuffer;
const char * sourceBufferPtr = inputBuffer;
// UNESCAPE END Sequence_
while (!done && (sourceBufferPtr - inputBuffer) < inputBufferLength) {
char * substringPtr = strstr(sourceBufferPtr, escapedEndSequence_);
if (substringPtr == NULL) done = true;
else {
// Copy bytes between last-copied byte and next escape byte
char lengthToCopy = substringPtr - sourceBufferPtr; // How many bytes between last byte copied and next escape byte
memcpy(destinationBufferPtr, sourceBufferPtr, lengthToCopy);
destinationBufferPtr += lengthToCopy;
sourceBufferPtr += lengthToCopy;
// Replace escaped source sequence with unescaped version during copy, increment pointer
memcpy(destinationBufferPtr, endSequence_, sizeof(endSequence_));
destinationBufferPtr += sizeof(endSequence_);
// Increment pointer past escaped end Sequence_
sourceBufferPtr += sizeof(escapedEndSequence_);
}
}
// UNESCAPE ESCAPE SEQUENCE
done = false;
while (!done && (sourceBufferPtr - inputBuffer) < inputBufferLength) {
char * substringPtr = strstr(sourceBufferPtr, escapedEscapeSequence_);
if (substringPtr == NULL) done = true;
else {
// Copy bytes between last-copied byte and next escape byte
char lengthToCopy = substringPtr - sourceBufferPtr; // How many bytes between last byte copied and next escape byte
memcpy(destinationBufferPtr, sourceBufferPtr, lengthToCopy);
destinationBufferPtr += lengthToCopy;
sourceBufferPtr += lengthToCopy;
// Replace escaped source sequence with unescaped version during copy, increment pointer
memcpy(destinationBufferPtr, escapeSequence_, sizeof(escapeSequence_));
destinationBufferPtr += sizeof(escapeSequence_);
// Increment pointer past escaped end Sequence_
sourceBufferPtr += sizeof(escapedEscapeSequence_);
}
}
// COPY ANY TRAILING BYTES
char lengthToCopy = (inputBuffer + inputBufferLength) - sourceBufferPtr; // How many bytes remain to be copied
memcpy(destinationBufferPtr, sourceBufferPtr, lengthToCopy);
destinationBufferPtr += lengthToCopy;
sourceBufferPtr += lengthToCopy;
return (destinationBufferPtr - outputBuffer); // Return the total number of bytes copied to the destination buffer
}
*/
void _UBP_hostDisconnected() {
hostIsConnected = false;
// Reset TX subsystem
UBP_isTxPending = false;
// Reset RX subsystem
ubpRxBufferLength = 0;
// Invoke user callback
// 2016-06-28: commentet out for testing
// if (UBP_didDisconnect) UBP_didDisconnect();
}
// RFduino EVENTS
// ----------------------------------------------------
//void SimbleeBLE_onAdvertisement(bool start) {
//
// if (UBP_didAdvertise) UBP_didAdvertise(start);
//}
//
void SimbleeBLE_onConnect() {
hostIsConnected = true;
// 2016-06-29: uncommented and added for testing purposes
// if (UBP_didConnect) UBP_didConnect();
UBP_isTxPending = false;
}
//void SimbleeBLE_onReceive(char *data, int len) {
//
// _UBP_ingestRxBytes(data, len);
//}
void SimbleeBLE_onDisconnect() {
_UBP_hostDisconnected();
}
================================================
FILE: LibreMonitor/ViewControllers/AdjustmentsTableViewController.swift
================================================
//
// AdjustmentsTableViewController.swift
// LibreMonitor
//
// Created by Uwe Petersen on 27.05.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import UIKit
class AdjustmentsTableViewController: UITableViewController, UITextFieldDelegate {
let numberFormatter = NumberFormatter()
@IBOutlet weak var offsetTextField: UITextField!
@IBOutlet weak var slopeTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
numberFormatter.numberStyle = .decimal
offsetTextField.delegate = self // for handling return key
slopeTextField.delegate = self
let bloodGlucoseOffset = UserDefaults.standard.double(forKey: "bloodGlucoseOffset")
let bloodGlucoseSlope = UserDefaults.standard.double(forKey: "bloodGlucoseSlope")
offsetTextField.text = numberFormatter.string(from: NSNumber(value: bloodGlucoseOffset))
slopeTextField.text = numberFormatter.string(from: NSNumber(value: bloodGlucoseSlope))
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(AdjustmentsTableViewController.didTapSaveButton))
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .undo, target: self, action: #selector(AdjustmentsTableViewController.didTapUndoButton))
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
checkInputForTextField(textField)
return true
}
func didTapSaveButton() {
checkInputForTextField(offsetTextField)
checkInputForTextField(slopeTextField)
}
func checkInputForTextField(_ textField: UITextField) {
guard let aNumber = numberFormatter.number(from: textField.text!) else {
displayAlertForTextField(textField)
return
}
switch textField {
case offsetTextField:
let bloodGlucoseOffset = Double(aNumber)
UserDefaults.standard.set(bloodGlucoseOffset, forKey: "bloodGlucoseOffset")
case slopeTextField:
let bloodGlucoseSlope = Double(aNumber)
UserDefaults.standard.set(bloodGlucoseSlope, forKey: "bloodGlucoseSlope")
default:
fatalError("Fatal Error in \(#file): textField not handled in switch case")
break
}
// update table view
NotificationCenter.default.post(name: Notification.Name(rawValue: "updateBloodSugarTableViewController"), object: self)
resignFirstResponder()
navigationController?.dismiss(animated: true, completion: nil)
}
func didTapUndoButton() {
resignFirstResponder()
navigationController?.dismiss(animated: true, completion: nil)
}
func displayAlertForTextField(_ textField: UITextField) {
let message = String(format: "\"\(textField.text!)\" ist kein gültiger Wert, bitte korrigieren.", [])
let alertController = UIAlertController(title: "Ungültige Eingabe", message: message, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
}
}
================================================
FILE: LibreMonitor/ViewControllers/BloodSugarTableViewController.swift
================================================
//
// TableViewController.swift
//
// Created by Uwe Petersen on 01.02.16.
// Copyright © 2016 Uwe Petersen. All rights reserved.
//
import Foundation
import UIKit
import CoreBluetooth
import CoreData
import UserNotifications
class BloodSugarTableViewController: UITableViewController, SimbleeManagerDelegate {
var coreDataStack = CoreDataStack()
var simbleeManager = SimbleeManager()
var sensorData: SensorData?
var trendMeasurements: [Measurement]?
var historyMeasurements: [Measurement]?
var batteryVoltage = 0.0
var bloodGlucoseOffset: Double!
var bloodGlucoseSlope: Double!
var sensor: LibreSensor?
var deviceID = "-"
var temperatureString = "_"
var timeInMinutesSinceStartOfSensor = 0
var timeOfLastScan = Date()
var transmissionDuration = TimeInterval()
var timeOfTransmissionStart = Date()
var dateFormatter = DateFormatter()
var timeFormatter = DateFormatter()
var notificationTimer = Timer()
var showNotification = true
/// Enum for the sections of this table view
fileprivate enum Section: Int {
case connectionData, generalData, graph, trendData, historyData
/// Count of enum cases (has to be adjusted to this/each very enum)
/// Source: http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum
static let count: Int = {
var max: Int = 0
while let _ = Section(rawValue: max) { max += 1 }
return max
}()
}
@IBAction func doRefresh(_ sender: UIRefreshControl) {
sender.endRefreshing()
tableView.reloadData()
}
// MARK: - View Controller life ciycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// self.navigationItem.leftBarButtonItem = self.editButtonItem()
simbleeManager.delegate = self
self.title = "LibreMonitor"
let connectButtonTitle = connectButtonTitleForState(simbleeManager.state)
let conncectButton = UIBarButtonItem(title: connectButtonTitle, style: .plain, target: self, action: #selector(BloodSugarTableViewController.didTapConnectButton))
self.navigationItem.rightBarButtonItem = conncectButton
bloodGlucoseOffset = UserDefaults.standard.double(forKey: "bloodGlucoseOffset")
bloodGlucoseSlope = UserDefaults.standard.double(forKey: "bloodGlucoseSlope")
if bloodGlucoseSlope <= 0.00001 {
bloodGlucoseSlope = 1.0
UserDefaults.standard.set(bloodGlucoseSlope, forKey: "bloodGlucoseSlope")
}
dateFormatter.dateFormat = "yyyy-MM-dd"
timeFormatter.dateFormat = "HH:mm:ss"
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
bloodGlucoseOffset = UserDefaults.standard.double(forKey: "bloodGlucoseOffset")
bloodGlucoseSlope = UserDefaults.standard.double(forKey: "bloodGlucoseSlope")
// Notification for updating table view after application did become active again
NotificationCenter.default.addObserver(self, selector: #selector(BloodSugarTableViewController.updateTableView), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
// Notification for updating table view after having changed the offset and/of slope
NotificationCenter.default.addObserver(self, selector: #selector(BloodSugarTableViewController.updateTableView), name: NSNotification.Name(rawValue: "updateBloodSugarTableViewController"), object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
super.viewWillDisappear(true)
}
func didTapConnectButton() {
switch (simbleeManager.state) {
case .Unassigned:
simbleeManager.scanForSimblee()
case .Scanning:
simbleeManager.centralManager.stopScan()
simbleeManager.state = .Disconnected
case .Connected, .Connecting, .Notifying:
simbleeManager.disconnectManually()
case .Disconnected, .DisconnectedManually:
simbleeManager.connect()
}
}
func updateTableView() {
self.tableView.reloadData()
}
// MARK: - Table View
override func numberOfSections(in tableView: UITableView) -> Int {
return Section.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch Section(rawValue: section)! {
case .connectionData: return 3
case .generalData: return 7
case .graph: return 1
case .trendData: return 16
case .historyData: return 32
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath as NSIndexPath).section == 2 {
// Draw graph
let cell = tableView.dequeueReusableCell(withIdentifier: "BloodSugarGraphTableViewCell", for: indexPath)
guard let theCell = cell as? BloodSugarGraphViewTableViewCell else {return cell}
if let trendMeasurements = trendMeasurements, let historyMeasurements = historyMeasurements {
theCell.lineChartView.trendMeasurements = trendMeasurements
theCell.lineChartView.historyMeasurements = historyMeasurements
theCell.lineChartView.setGlucoseCharts(trendMeasurements, historyMeasurements: historyMeasurements)
theCell.setNeedsDisplay()
theCell.lineChartView.setNeedsDisplay()
}
return theCell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
self.configureCell(cell, atIndexPath: indexPath)
return cell
}
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if (indexPath as NSIndexPath).section == 0 && (indexPath as NSIndexPath).row == 0 {
didTapConnectButton()
} else if (indexPath as NSIndexPath).section == 0 && (indexPath as NSIndexPath).row == 2 {
// ChangeBloodGlucoseAdjustments
performSegue(withIdentifier: "ChangeBloodGlucoseAdjustments", sender: self)
}
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 0: return "Connection"
case 1: return "General data"
case 2:
let seconds = (NSDate() as NSDate).timeIntervalSince(timeOfLastScan).truncatingRemainder(dividingBy: 60.0)
let minutes = (Date().timeIntervalSince(timeOfLastScan) - seconds) / 60.0
return String(format: "Graph from %2.0f:%02.0f minutes ago", arguments: [minutes, seconds])
case 3: return "Last 15 minutes"
case 4: return "Last eight hours"
case 5: return "Neue Letzte 15 Minuten"
case 6: return "Neue Letzte 8 Stunden"
default: return nil
}
}
func configureCell(_ cell: UITableViewCell, atIndexPath indexPath: IndexPath) {
cell.backgroundColor = UIColor.white
cell.detailTextLabel?.textColor = UIColor.black
cell.accessoryType = .none
switch Section(rawValue: (indexPath as NSIndexPath).section)! {
case .connectionData:
switch (indexPath as NSIndexPath).row {
case 0:
cell.textLabel?.text = "Simblee status"
cell.detailTextLabel?.text = simbleeManager.state.rawValue
cell.backgroundColor = colorForConnectionState()
case 1:
cell.textLabel?.text = "Last scan:"
if let sennsorData = sensorData {
cell.detailTextLabel?.text = String(format: "on \(dateFormatter.string(from: sennsorData.date as Date)), at \(timeFormatter.string(from: sennsorData.date as Date)) o'clock, in %.2f s", arguments: [transmissionDuration])
if Date().timeIntervalSince(sennsorData.date as Date) > 240.0 {
cell.backgroundColor = UIColor.red
}
}
case 2:
cell.textLabel?.text = "Offset / Slope:"
cell.detailTextLabel?.text = String(format: "%.0f mg/dl, %.4f", arguments: [bloodGlucoseOffset, bloodGlucoseSlope])
cell.accessoryType = .disclosureIndicator
default: break
}
case .generalData:
switch (indexPath as NSIndexPath).row {
case 0:
cell.textLabel?.text = "BM019 ID"
cell.detailTextLabel?.text = deviceID
case 1:
var crcString = String()
var color = UIColor()
if let sensorData = sensorData {
crcString += ", crcs: \(sensorData.hasValidHeaderCRC), \(sensorData.hasValidBodyCRC), \(sensorData.hasValidFooterCRC)"
color = colorForSensorState( (sensorData.hasValidHeaderCRC && sensorData.hasValidBodyCRC && sensorData.hasValidFooterCRC) )
} else {
crcString = ", nil"
color = UIColor.lightGray
}
cell.textLabel?.text = "Sensor SN"
if let sensor = sensor {
cell.detailTextLabel?.text = sensor.serialNumber + crcString // + " (" + sensor.prettyUid + ")"
} else {
cell.detailTextLabel?.text = ""
}
cell.backgroundColor = color
case 2:
cell.textLabel?.text = "Environment"
cell.detailTextLabel?.text = String(format: "%3.1f V", arguments: [batteryVoltage]) + ", " + temperatureString
if batteryVoltage < 3.0 {
cell.backgroundColor = UIColor.orange
}
case 3:
cell.textLabel?.text = "Blocks"
if let sennsorData = sensorData {
cell.detailTextLabel?.text = "Trend: \(sennsorData.nextTrendBlock), history: \(sennsorData.nextHistoryBlock), minutes: \(sennsorData.minutesSinceStart)"
}
case 4:
cell.textLabel?.text = "Glucose"
if let trendMeasurements = trendMeasurements {
let currentGlucose = trendMeasurements[0].glucose
let longDelta = currentGlucose - trendMeasurements[15].glucose
let shortDelta = (currentGlucose - trendMeasurements[8].glucose) * 2.0 * 16.0/15.0
let longPrediction = currentGlucose + longDelta
let shortPrediction = currentGlucose + shortDelta
cell.detailTextLabel?.text = String(format: "%0.0f, Delta: %0.0f (%0.0f), Prognosis: %0.0f (%0.0f)", arguments: [currentGlucose, longDelta, shortDelta, longPrediction, shortPrediction])
if longPrediction < 70.0 || shortPrediction < 70.0 || longPrediction > 180.0 || shortPrediction > 180.0 || (
gitextract_hx6qj96v/ ├── LICENSE.md ├── LibreMonitor/ │ ├── AppDelegate.swift │ ├── Assets.xcassets/ │ │ └── AppIcon.appiconset/ │ │ └── Contents.json │ ├── Base.lproj/ │ │ └── LaunchScreen.storyboard │ ├── BloodGlucose+CoreDataClass.swift │ ├── BloodGlucose+CoreDataProperties.swift │ ├── Bluetooth/ │ │ ├── Data_types+Extensions.swift │ │ ├── NSData+CRC8.h │ │ ├── NSData+CRC8.m │ │ ├── NSData+SLIP.h │ │ ├── NSData+SLIP.m │ │ ├── SLIPBuffer.swift │ │ ├── SimbleeManager.swift │ │ ├── constants.h │ │ └── data_types.h │ ├── HeaderData+CoreDataClass.swift │ ├── HeaderData+CoreDataProperties.swift │ ├── Info.plist │ ├── LibreMonitor-Bridging-Header.h │ ├── LibreMonitor.entitlements │ ├── LibreMonitor.xcdatamodeld/ │ │ ├── .xccurrentversion │ │ └── LibreMonitor.xcdatamodel/ │ │ └── contents │ ├── LibreMonitorUITests-Bridging-Header.h │ ├── Main.storyboard │ ├── Model/ │ │ ├── CRC.swift │ │ ├── LibreSensor.swift │ │ ├── Measurement.swift │ │ ├── SensorData.swift │ │ └── SensorState.swift │ ├── ModelCoreData/ │ │ ├── BloodGlucose+CoreDataClass.swift │ │ ├── BloodGlucose+CoreDataProperties.swift │ │ ├── CoreDataStack.swift │ │ ├── HeaderData+CoreDataClass.swift │ │ ├── HeaderData+CoreDataProperties.swift │ │ ├── Reader+CoreDataClass.swift │ │ ├── Reader+CoreDataProperties.swift │ │ ├── Sensor+CoreDataClass.swift │ │ └── Sensor+CoreDataProperties.swift │ ├── Reader+CoreDataClass.swift │ ├── Reader+CoreDataProperties.swift │ ├── Sensor+CoreDataClass.swift │ ├── Sensor+CoreDataProperties.swift │ ├── SimbleeCode/ │ │ ├── crc8.h │ │ ├── crc8.m │ │ ├── libUBP.h │ │ └── libUBP.m │ ├── ViewControllers/ │ │ ├── AdjustmentsTableViewController.swift │ │ └── BloodSugarTableViewController.swift │ └── Views/ │ ├── BloodSugarGraphView.swift │ └── BloodSugarGraphViewTableViewCell.swift ├── LibreMonitor.ino ├── LibreMonitor.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ └── contents.xcworkspacedata │ └── xcuserdata/ │ └── Uwe.xcuserdatad/ │ └── xcschemes/ │ ├── LibreMonitor.xcscheme │ └── xcschememanagement.plist ├── LibreMonitor.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcuserdata/ │ └── Uwe.xcuserdatad/ │ └── xcdebugger/ │ └── Breakpoints_v2.xcbkptlist ├── LibreMonitorRFduino.ino ├── LibreMonitorTests/ │ ├── BluetoothTestData.swift │ ├── Info.plist │ ├── LibreMonitorTestSensorData.swift │ ├── LibreMonitorTests-Bridging-Header.h │ ├── LibreMonitorTests.swift │ ├── SimbleeCode/ │ │ ├── crc8.h │ │ ├── crc8.m │ │ ├── libUBP.h │ │ └── libUBP.m │ └── TransmissionTests.swift ├── LibreMonitorUITests/ │ ├── Info.plist │ └── LibreMonitorUITests.swift ├── Podfile ├── README.md └── libUBP RFduino.cpp
SYMBOL INDEX (25 symbols across 6 files)
FILE: LibreMonitor/Bluetooth/data_types.h
type BatteryDataType (line 6) | typedef struct __attribute__((packed)) {
type SystemInformationDataType (line 19) | typedef struct __attribute__((packed)) {
type IDNDataType (line 32) | typedef struct __attribute__((packed)) {
type AllBytesDataType (line 43) | typedef struct __attribute__((packed)) {
FILE: LibreMonitor/SimbleeCode/crc8.h
type byte (line 14) | typedef uint8_t byte;
FILE: LibreMonitor/SimbleeCode/libUBP.h
type byte (line 6) | typedef uint8_t byte;
type UBP_TxFlags (line 14) | typedef enum {
FILE: LibreMonitorTests/SimbleeCode/crc8.h
type byte (line 14) | typedef uint8_t byte;
FILE: LibreMonitorTests/SimbleeCode/libUBP.h
type byte (line 6) | typedef uint8_t byte;
type UBP_TxFlags (line 14) | typedef enum {
FILE: libUBP RFduino.cpp
function UBP_isBusy (line 99) | bool UBP_isBusy() {
function UBP_pump (line 104) | void UBP_pump() {
function _UBP_ingestRxBytes (line 161) | void _UBP_ingestRxBytes(char *receivedBytes, int byteCount) {
function UBP_queuePacketTransmission (line 229) | bool UBP_queuePacketTransmission(unsigned short packetIdentifier, UBP_Tx...
function _UBP_makeEscapedCopy (line 301) | int _UBP_makeEscapedCopy(const char *inputBuffer, unsigned short inputBu...
function _UBP_makeUnEscapedCopy (line 350) | int _UBP_makeUnEscapedCopy(const char *inputBuffer, unsigned short input...
function _UBP_hostDisconnected (line 410) | void _UBP_hostDisconnected() {
function SimbleeBLE_onAdvertisement (line 428) | void SimbleeBLE_onAdvertisement(bool start) {
function SimbleeBLE_onConnect (line 431) | void SimbleeBLE_onConnect() {
function SimbleeBLE_onReceive (line 436) | void SimbleeBLE_onReceive(char *data, int len) {
function SimbleeBLE_onDisconnect (line 440) | void SimbleeBLE_onDisconnect() {
function RFduinoBLE_onAdvertisement (line 444) | void RFduinoBLE_onAdvertisement(bool start) {
function RFduinoBLE_onConnect (line 448) | void RFduinoBLE_onConnect() {
function RFduinoBLE_onReceive (line 453) | void RFduinoBLE_onReceive(char *data, int len) {
function RFduinoBLE_onDisconnect (line 457) | void RFduinoBLE_onDisconnect() {
Condensed preview — 73 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (498K chars).
[
{
"path": "LICENSE.md",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "LibreMonitor/AppDelegate.swift",
"chars": 7832,
"preview": "//\n// AppDelegate.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 14.10.16.\n// Copyright © 2016 Uwe Petersen."
},
{
"path": "LibreMonitor/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 1495,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"size\" : \"20x20\",\n \"scale\" : \"2x\"\n },\n {\n \"idiom\""
},
{
"path": "LibreMonitor/Base.lproj/LaunchScreen.storyboard",
"chars": 1740,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "LibreMonitor/BloodGlucose+CoreDataClass.swift",
"chars": 245,
"preview": "//\n// BloodGlucose+CoreDataClass.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 14.10.16.\n// Copyright © 201"
},
{
"path": "LibreMonitor/BloodGlucose+CoreDataProperties.swift",
"chars": 674,
"preview": "//\n// BloodGlucose+CoreDataProperties.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 14.10.16.\n// Copyright "
},
{
"path": "LibreMonitor/Bluetooth/Data_types+Extensions.swift",
"chars": 2856,
"preview": "//\n// Data_types+Extensions.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 15.05.16.\n// Copyright © 2016 Uwe"
},
{
"path": "LibreMonitor/Bluetooth/NSData+CRC8.h",
"chars": 491,
"preview": "//\n// NSData+CRC8.h\n// Bluetooth LE Test\n//\n// Created by Chas Conway on 12/11/13.\n// Copyright (c) 2013 Chas Conway"
},
{
"path": "LibreMonitor/Bluetooth/NSData+CRC8.m",
"chars": 1489,
"preview": "//\n// NSData+CRC8.m\n// Bluetooth LE Test\n//\n// Created by Chas Conway on 12/11/13.\n// Copyright (c) 2013 Chas Conway"
},
{
"path": "LibreMonitor/Bluetooth/NSData+SLIP.h",
"chars": 329,
"preview": "//\n// NSData+SLIP.h\n// Arduino Greenhouse\n//\n// Created by Chas Conway on 5/23/14.\n// Copyright (c) 2014 Chas Conway"
},
{
"path": "LibreMonitor/Bluetooth/NSData+SLIP.m",
"chars": 3357,
"preview": "//\n// NSData+SLIP.m\n// Arduino Greenhouse\n//\n// Created by Chas Conway on 5/23/14.\n// Copyright (c) 2014 Chas Conway"
},
{
"path": "LibreMonitor/Bluetooth/SLIPBuffer.swift",
"chars": 8673,
"preview": "//\n// SLIPBuffer.swift\n// UBA-Demo\n//\n// Created by Chas Conway on 2/4/15.\n// Copyright (c) 2015 Chas Conway. All ri"
},
{
"path": "LibreMonitor/Bluetooth/SimbleeManager.swift",
"chars": 11940,
"preview": "//\n// SimbleeManager.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 23.04.16.\n// Copyright © 2016 Uwe Peters"
},
{
"path": "LibreMonitor/Bluetooth/constants.h",
"chars": 359,
"preview": "// Define unique identifiers for each data packet here. This identifier is transfered together with the data and can be "
},
{
"path": "LibreMonitor/Bluetooth/data_types.h",
"chars": 1883,
"preview": "// Data types for data to be transfered\n// Need a struct with one variable and the packed attribute to make this an arra"
},
{
"path": "LibreMonitor/HeaderData+CoreDataClass.swift",
"chars": 258,
"preview": "//\n// HeaderData+CoreDataClass.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 14.10.16.\n// Copyright © 2016 "
},
{
"path": "LibreMonitor/HeaderData+CoreDataProperties.swift",
"chars": 462,
"preview": "//\n// HeaderData+CoreDataProperties.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 14.10.16.\n// Copyright © "
},
{
"path": "LibreMonitor/Info.plist",
"chars": 1798,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "LibreMonitor/LibreMonitor-Bridging-Header.h",
"chars": 197,
"preview": "//\n// Use this file to import your target's public headers that you would like to expose to Swift.\n//\n#import \"NSData+C"
},
{
"path": "LibreMonitor/LibreMonitor.entitlements",
"chars": 239,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "LibreMonitor/LibreMonitor.xcdatamodeld/.xccurrentversion",
"chars": 265,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "LibreMonitor/LibreMonitor.xcdatamodeld/LibreMonitor.xcdatamodel/contents",
"chars": 3552,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<model type=\"com.apple.IDECoreDataModeler.DataModel\" documentVer"
},
{
"path": "LibreMonitor/LibreMonitorUITests-Bridging-Header.h",
"chars": 196,
"preview": "//\n// Use this file to import your target's public headers that you would like to expose to Swift.\n//\n#import \"NSData+C"
},
{
"path": "LibreMonitor/Main.storyboard",
"chars": 67395,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "LibreMonitor/Model/CRC.swift",
"chars": 4617,
"preview": "//\n// CRC.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 26.07.16.\n// Copyright © 2016 Uwe Petersen. All rig"
},
{
"path": "LibreMonitor/Model/LibreSensor.swift",
"chars": 5245,
"preview": "//\n// LibreSensor.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 15.05.16.\n// Copyright © 2016 Uwe Petersen."
},
{
"path": "LibreMonitor/Model/Measurement.swift",
"chars": 1728,
"preview": "//\n// Measurement.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 25.08.16.\n// Copyright © 2016 Uwe Petersen."
},
{
"path": "LibreMonitor/Model/SensorData.swift",
"chars": 6163,
"preview": "//\n// SensorData\n// LibreMonitor\n//\n// Created by Uwe Petersen on 26.07.16.\n// Copyright © 2016 Uwe Petersen. All ri"
},
{
"path": "LibreMonitor/Model/SensorState.swift",
"chars": 1289,
"preview": "//\n// SensorState.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 31.07.16.\n// Copyright © 2016 Uwe Petersen."
},
{
"path": "LibreMonitor/ModelCoreData/BloodGlucose+CoreDataClass.swift",
"chars": 245,
"preview": "//\n// BloodGlucose+CoreDataClass.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 09.10.16.\n// Copyright © 201"
},
{
"path": "LibreMonitor/ModelCoreData/BloodGlucose+CoreDataProperties.swift",
"chars": 673,
"preview": "//\n// BloodGlucose+CoreDataProperties.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 09.10.16.\n// Copyright "
},
{
"path": "LibreMonitor/ModelCoreData/CoreDataStack.swift",
"chars": 5840,
"preview": "//\n// CoreDataStack.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 13.04.16.\n// Copyright © 2016 Uwe Peterse"
},
{
"path": "LibreMonitor/ModelCoreData/HeaderData+CoreDataClass.swift",
"chars": 258,
"preview": "//\n// HeaderData+CoreDataClass.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 09.10.16.\n// Copyright © 2016 "
},
{
"path": "LibreMonitor/ModelCoreData/HeaderData+CoreDataProperties.swift",
"chars": 462,
"preview": "//\n// HeaderData+CoreDataProperties.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 09.10.16.\n// Copyright © "
},
{
"path": "LibreMonitor/ModelCoreData/Reader+CoreDataClass.swift",
"chars": 233,
"preview": "//\n// Reader+CoreDataClass.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 09.10.16.\n// Copyright © 2016 Uwe "
},
{
"path": "LibreMonitor/ModelCoreData/Reader+CoreDataProperties.swift",
"chars": 495,
"preview": "//\n// Reader+CoreDataProperties.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 09.10.16.\n// Copyright © 2016"
},
{
"path": "LibreMonitor/ModelCoreData/Sensor+CoreDataClass.swift",
"chars": 233,
"preview": "//\n// Sensor+CoreDataClass.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 09.10.16.\n// Copyright © 2016 Uwe "
},
{
"path": "LibreMonitor/ModelCoreData/Sensor+CoreDataProperties.swift",
"chars": 1063,
"preview": "//\n// Sensor+CoreDataProperties.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 09.10.16.\n// Copyright © 2016"
},
{
"path": "LibreMonitor/Reader+CoreDataClass.swift",
"chars": 233,
"preview": "//\n// Reader+CoreDataClass.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 14.10.16.\n// Copyright © 2016 Uwe "
},
{
"path": "LibreMonitor/Reader+CoreDataProperties.swift",
"chars": 495,
"preview": "//\n// Reader+CoreDataProperties.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 14.10.16.\n// Copyright © 2016"
},
{
"path": "LibreMonitor/Sensor+CoreDataClass.swift",
"chars": 233,
"preview": "//\n// Sensor+CoreDataClass.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 14.10.16.\n// Copyright © 2016 Uwe "
},
{
"path": "LibreMonitor/Sensor+CoreDataProperties.swift",
"chars": 1064,
"preview": "//\n// Sensor+CoreDataProperties.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 14.10.16.\n// Copyright © 2016"
},
{
"path": "LibreMonitor/SimbleeCode/crc8.h",
"chars": 519,
"preview": "// LICENSES: [077915]\n// -----------------------------------\n// The contents of this file contains the aggregate of cont"
},
{
"path": "LibreMonitor/SimbleeCode/crc8.m",
"chars": 1328,
"preview": "// LICENSES: [077915]\n// -----------------------------------\n// The contents of this file contains the aggregate of cont"
},
{
"path": "LibreMonitor/SimbleeCode/libUBP.h",
"chars": 1457,
"preview": "//\n// Uwe Petersen: Modified version to test transmission part in iOS\n\n#import <Foundation/Foundation.h>\n\ntypedef uint8_"
},
{
"path": "LibreMonitor/SimbleeCode/libUBP.m",
"chars": 14711,
"preview": "//\n// Uwe Petersen: Modified version to test transmission part in iOS\n\n#import \"libUBP.h\"\n#import \"crc8.h\"\n\n// Build-tim"
},
{
"path": "LibreMonitor/ViewControllers/AdjustmentsTableViewController.swift",
"chars": 3374,
"preview": "//\n// AdjustmentsTableViewController.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 27.05.16.\n// Copyright ©"
},
{
"path": "LibreMonitor/ViewControllers/BloodSugarTableViewController.swift",
"chars": 28538,
"preview": "//\n// TableViewController.swift\n//\n// Created by Uwe Petersen on 01.02.16.\n// Copyright © 2016 Uwe Petersen. All righ"
},
{
"path": "LibreMonitor/Views/BloodSugarGraphView.swift",
"chars": 6087,
"preview": "//\n// BloodSugarGraphView.swift\n//\n// Created by Uwe Petersen on 23.03.16.\n//\n// Blood sugar data ranges from time of"
},
{
"path": "LibreMonitor/Views/BloodSugarGraphViewTableViewCell.swift",
"chars": 650,
"preview": "//\n// BloodSugarGraphViewTableViewCell.swift\n//\n// Created by Uwe Petersen on 23.03.16.\n//\n\nimport Foundation\nimport U"
},
{
"path": "LibreMonitor.ino",
"chars": 30615,
"preview": "///\n/// LibreMonitor\n/// \n/// Copyright (c) 2015 Uwe Petersen, all right reserved\n///\n/// Wiring connections:\n///\n"
},
{
"path": "LibreMonitor.xcodeproj/project.pbxproj",
"chars": 50855,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "LibreMonitor.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 157,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:LibreMonitor.xc"
},
{
"path": "LibreMonitor.xcodeproj/xcuserdata/Uwe.xcuserdatad/xcschemes/LibreMonitor.xcscheme",
"chars": 4257,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0820\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "LibreMonitor.xcodeproj/xcuserdata/Uwe.xcuserdatad/xcschemes/xcschememanagement.plist",
"chars": 664,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "LibreMonitor.xcworkspace/contents.xcworkspacedata",
"chars": 230,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:LibreMonitor.x"
},
{
"path": "LibreMonitor.xcworkspace/xcuserdata/Uwe.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist",
"chars": 8231,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Bucket\n type = \"0\"\n version = \"2.0\">\n <Breakpoints>\n <BreakpointProxy"
},
{
"path": "LibreMonitorRFduino.ino",
"chars": 33251,
"preview": "//#include <RFduinoBLE.h>\n\n///\n/// LibreMonitor\n/// \n/// Copyright (c) 2015 Uwe Petersen, all right reserved\n///\n///"
},
{
"path": "LibreMonitorTests/BluetoothTestData.swift",
"chars": 54564,
"preview": "//\n// BluetoothTestData.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 29.01.17.\n// Copyright © 2017 Uwe Pet"
},
{
"path": "LibreMonitorTests/Info.plist",
"chars": 680,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "LibreMonitorTests/LibreMonitorTestSensorData.swift",
"chars": 11719,
"preview": "//\n// LibreMonitorTestSensorData.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 28.07.16.\n// Copyright © 201"
},
{
"path": "LibreMonitorTests/LibreMonitorTests-Bridging-Header.h",
"chars": 232,
"preview": "//\n// Use this file to import your target's public headers that you would like to expose to Swift.\n//\n#import \"NSData+C"
},
{
"path": "LibreMonitorTests/LibreMonitorTests.swift",
"chars": 11306,
"preview": "//\n// LibreMonitorTests.swift\n// LibreMonitorTests\n//\n// Created by Uwe Petersen on 09.10.16.\n// Copyright © 2016 Uw"
},
{
"path": "LibreMonitorTests/SimbleeCode/crc8.h",
"chars": 648,
"preview": "// LICENSES: [077915]\n// -----------------------------------\n// The contents of this file contains the aggregate of cont"
},
{
"path": "LibreMonitorTests/SimbleeCode/crc8.m",
"chars": 1455,
"preview": "// LICENSES: [077915]\n// -----------------------------------\n// The contents of this file contains the aggregate of cont"
},
{
"path": "LibreMonitorTests/SimbleeCode/libUBP.h",
"chars": 1457,
"preview": "//\n// Uwe Petersen: Modified version to test transmission part in iOS\n\n#import <Foundation/Foundation.h>\n\ntypedef uint8_"
},
{
"path": "LibreMonitorTests/SimbleeCode/libUBP.m",
"chars": 15006,
"preview": "//\n// Uwe Petersen: Modified version to test transmission part in iOS\n\n#import \"libUBP.h\"\n#import \"crc8.h\"\n\n// Build-tim"
},
{
"path": "LibreMonitorTests/TransmissionTests.swift",
"chars": 1987,
"preview": "//\n// TransmissionTests.swift\n// LibreMonitor\n//\n// Created by Uwe Petersen on 03.02.17.\n// Copyright © 2017 Uwe Pet"
},
{
"path": "LibreMonitorUITests/Info.plist",
"chars": 680,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "LibreMonitorUITests/LibreMonitorUITests.swift",
"chars": 1259,
"preview": "//\n// LibreMonitorUITests.swift\n// LibreMonitorUITests\n//\n// Created by Uwe Petersen on 14.10.16.\n// Copyright © 201"
},
{
"path": "Podfile",
"chars": 613,
"preview": "# Uncomment this line to define a global platform for your project\n# platform :ios, '10.0'\n# Uncomment this line if you'"
},
{
"path": "README.md",
"chars": 8734,
"preview": "# LibreMonitor - Monitor your Freestyle Libre\n\nLibreMonitor is a little DIY device that uses near field communication to"
},
{
"path": "libUBP RFduino.cpp",
"chars": 17023,
"preview": "//\n// libUBP.cpp \n// C++ code\n// ----------------------------------\n// Developed with embedXcode+ \n// http://embedXcode."
}
]
About this extraction
This page contains the full source code of the UPetersen/LibreMonitor GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 73 files (460.9 KB), approximately 128.6k tokens, and a symbol index with 25 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.