| /*  | 
|  * Copyright Cypress Semiconductor Corporation, 2014-2015 All rights reserved.  | 
|  *  | 
|  * This software, associated documentation and materials ("Software") is  | 
|  * owned by Cypress Semiconductor Corporation ("Cypress") and is  | 
|  * protected by and subject to worldwide patent protection (UnitedStates and foreign), United States copyright laws and international  | 
|  * treaty provisions. Therefore, unless otherwise specified in a separate license agreement between you and Cypress, this Software  | 
|  * must be treated like any other copyrighted material. Reproduction,  | 
|  * modification, translation, compilation, or representation of this  | 
|  * Software in any other form (e.g., paper, magnetic, optical, silicon)  | 
|  * is prohibited without Cypress's express written permission.  | 
|  *  | 
|  * Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY  | 
|  * KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,  | 
|  * NONINFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS  | 
|  * FOR A PARTICULAR PURPOSE. Cypress reserves the right to make changes  | 
|  * to the Software without notice. Cypress does not assume any liability  | 
|  * arising out of the application or use of Software or any product or  | 
|  * circuit described in the Software. Cypress does not authorize its  | 
|  * products for use as critical components in any products where a  | 
|  * malfunction or failure may reasonably be expected to result in  | 
|  * significant injury or death ("High Risk Product"). By including  | 
|  * Cypress's product in a High Risk Product, the manufacturer of such  | 
|  * system or application assumes all risk of such use and in doing so  | 
|  * indemnifies Cypress against all liability.  | 
|  *  | 
|  * Use of this Software may be limited by and subject to the applicable  | 
|  * Cypress software license agreement.  | 
|  *  | 
|  *  | 
|  */  | 
|   | 
| #import "HRMModel.h"  | 
| #import "CBMoralManager.h"  | 
|   | 
| // Body location  | 
|   | 
| #define OTHER         @"Other"  | 
| #define CHEST         @"Chest"  | 
| #define WRIST         @"Wrist"  | 
| #define FINGER        @"Finger"  | 
| #define HAND          @"Hand"  | 
| #define EAR_LOBE      @"Ear Lobe"  | 
| #define FOOT          @"Foot"  | 
| #define LOCATION_NA   @"Body Location: N/A"  | 
|   | 
| /*!  | 
|  *  @class HRMModel  | 
|  *  | 
|  *  @discussion Class to handle the heart rate measurement service related operations  | 
|  *  | 
|  */  | 
|   | 
| @interface HRMModel()<cbCharacteristicManagerDelegate>  | 
| {  | 
|     void (^cbCharacteristicHandler)(BOOL success, NSError *error);  | 
|     void (^cbCharacteristicDiscoverHandler)(BOOL success, NSError *error);  | 
|   | 
| }  | 
|   | 
| @end  | 
|   | 
|   | 
| @implementation HRMModel  | 
|   | 
| @synthesize bpmValue;  | 
| @synthesize sensorLocation;  | 
| @synthesize RR_Interval;  | 
| @synthesize EnergyExpended;  | 
|   | 
|   | 
|   | 
|   | 
| /*!  | 
|  *  @method startDiscoverChar:  | 
|  *  | 
|  *  @discussion Discovers the specified characteristics of a service..  | 
|  */  | 
|   | 
|   | 
| -(void)startDiscoverChar:(void (^) (BOOL success, NSError *error))handler  | 
| {  | 
|     cbCharacteristicDiscoverHandler = handler;  | 
|     [[CBMoralManager sharedManager] setCbCharacteristicDelegate:self];  | 
|     [[[CBMoralManager sharedManager] myPeripheral] discoverCharacteristics:nil forService:[[CBMoralManager sharedManager] myService]];  | 
|       | 
| }  | 
|   | 
| /*!  | 
|  *  @method updateCharacteristicWithHandler:  | 
|  *  | 
|  *  @discussion Sets notifications or indications for the value of a specified characteristic.  | 
|  */  | 
|   | 
| -(void)updateCharacteristicWithHandler:(void (^) (BOOL success, NSError *error))handler  | 
| {  | 
|     cbCharacteristicHandler = handler;  | 
|       | 
|       | 
| }  | 
|   | 
|   | 
| /*!  | 
|  *  @method stopUpdate  | 
|  *  | 
|  *  @discussion Stop notifications or indications for the value of a specified characteristic.  | 
|  */  | 
|   | 
| -(void)stopUpdate  | 
| {  | 
|     cbCharacteristicHandler = nil;  | 
|     if ([[[CBMoralManager sharedManager] myService].UUID isEqual:HRM_HEART_RATE_SERVICE_UUID])  | 
|     {  | 
|         for (CBCharacteristic *aChar in [[CBMoralManager sharedManager] myService].characteristics)  | 
|         {  | 
|             if ([aChar.UUID isEqual:HRM_NOTIFICATIONS_CHARACTERISTIC_UUID])  | 
|             {  | 
|                 if (aChar.isNotifying)  | 
|                 {  | 
|                     [[[CBMoralManager sharedManager] myPeripheral] setNotifyValue:NO  forCharacteristic:aChar];  | 
|                     [Utilities logDataWithService:[ResourceHandler getServiceNameForUUID:HRM_HEART_RATE_SERVICE_UUID] characteristic:[ResourceHandler getCharacteristicNameForUUID:HRM_NOTIFICATIONS_CHARACTERISTIC_UUID] descriptor:nil operation:STOP_NOTIFY];  | 
|                 }  | 
|                   | 
|                 cbCharacteristicDiscoverHandler(YES,nil);  | 
|                 break;  | 
|             }  | 
|         }  | 
|     }  | 
|   | 
|       | 
| }  | 
|   | 
|   | 
| #pragma mark - CBCharecteristicManger  | 
|   | 
| /*!  | 
|  *  @method peripheral: didDiscoverCharacteristicsForService: error:  | 
|  *  | 
|  *  @discussion Method invoked when characteristics are discovered for a service  | 
|  *  | 
|  */  | 
|   | 
| -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error  | 
| {  | 
|     if ([service.UUID isEqual:HRM_HEART_RATE_SERVICE_UUID]){  | 
|         for (CBCharacteristic *aChar in service.characteristics){  | 
|             if ([aChar.UUID isEqual:HRM_NOTIFICATIONS_CHARACTERISTIC_UUID]){  | 
|                   | 
|                 [[[CBMoralManager sharedManager] myPeripheral] setNotifyValue:YES forCharacteristic:aChar];  | 
|                 [Utilities logDataWithService:[ResourceHandler getServiceNameForUUID:HRM_HEART_RATE_SERVICE_UUID] characteristic:[ResourceHandler getCharacteristicNameForUUID:HRM_NOTIFICATIONS_CHARACTERISTIC_UUID] descriptor:nil operation:START_NOTIFY];  | 
|                   | 
|                 cbCharacteristicDiscoverHandler(YES,nil);  | 
|             }  | 
|             else if([aChar.UUID isEqual:HRM_BODY_LOCATION_CHARACTERISTIC_UUID])  | 
|             {  | 
|                 [[[CBMoralManager sharedManager] myPeripheral] readValueForCharacteristic:aChar];  | 
|                 [Utilities logDataWithService:[ResourceHandler getServiceNameForUUID:HRM_HEART_RATE_SERVICE_UUID] characteristic:[ResourceHandler getCharacteristicNameForUUID:HRM_BODY_LOCATION_CHARACTERISTIC_UUID] descriptor:nil operation:READ_REQUEST];  | 
|             }  | 
|         }  | 
|     }  | 
|       | 
| }  | 
|   | 
| /*!  | 
|  *  @method peripheral: didUpdateValueForCharacteristic: error:  | 
|  *  | 
|  *  @discussion Method invoked when the characteristic value changes  | 
|  *  | 
|  */  | 
|   | 
| -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error  | 
| {  | 
|      if(error == nil)  | 
|      {  | 
|          if ([characteristic.UUID isEqual:HRM_NOTIFICATIONS_CHARACTERISTIC_UUID])  | 
|          {  | 
|              [self getHeartBPMData:characteristic error:error];  | 
|          }  | 
|          else if ([characteristic.UUID isEqual:HRM_BODY_LOCATION_CHARACTERISTIC_UUID])  | 
|          {  | 
|              [self getBodyLocation:characteristic];  | 
|          }  | 
|                | 
|         cbCharacteristicHandler(YES,nil);  | 
|   | 
|      }  | 
|      else  | 
|      {  | 
|          cbCharacteristicHandler(NO,error);  | 
|      }  | 
|   | 
| }  | 
|   | 
|   | 
| /*!  | 
|  *  @method getHeartBPMData:error  | 
|  *  | 
|  *  @discussion   Method to get the Heart Rate Measurement Value , Energy Expended, RR-Interval  | 
|  *  | 
|  */  | 
|   | 
| // Instance method to get the heart rate BPM information  | 
| - (void) getHeartBPMData:(CBCharacteristic *)characteristic error:(NSError *)error  | 
| {  | 
|     // Get the BPM //  | 
|     // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml //  | 
|       | 
|     // Convert the contents of the characteristic value to a data-object //  | 
|     NSData *data = [characteristic value];  | 
|       | 
|     // Get the byte sequence of the data-object //  | 
|     const uint8_t *reportData = [data bytes];  | 
|       | 
|     // Initialise the offset variable //  | 
|     NSUInteger offset = 1;  | 
|     // Initialise the bpm variable //  | 
|     uint16_t bpm = 0;  | 
|       | 
|       | 
|     // Next, obtain the first byte at index 0 in the array as defined by reportData[0] and mask out all but the 1st bit //  | 
|     // The result returned will either be 0, which means that the 2nd bit is not set, or 1 if it is set //  | 
|     // If the 2nd bit is not set, retrieve the BPM value at the second byte location at index 1 in the array //  | 
|     if ((reportData[0] & 0x01) == 0) {  | 
|         // Retrieve the BPM value for the Heart Rate Monitor  | 
|         bpm = reportData[1];  | 
|           | 
|         offset = offset + 1; // Plus 1 byte //  | 
|     }  | 
|     else {  | 
|         // If the second bit is set, retrieve the BPM value at second byte location at index 1 in the array and //  | 
|         // convert this to a 16-bit value based on the host’s native byte order //  | 
|         bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[1]));  | 
|           | 
|         offset =  offset + 2; // Plus 2 bytes //  | 
|     }  | 
|     self.bpmValue = bpm;//[NSString stringWithFormat:@"%d",bpm];  | 
|       | 
|       | 
|       | 
|     // Determine if EE data is present //  | 
|     // If the 3rd bit of the first byte is 1 this means there is EE data //  | 
|     // If so, increase offset with 2 bytes //  | 
|     if (reportData[0] & 0x08)  | 
|     {  | 
|         uint16_t expendedEnergy = 0;  | 
|         expendedEnergy = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[offset]));  | 
|         offset =  offset + 2; // Plus 2 bytes //  | 
|         self.EnergyExpended = [NSString stringWithFormat:@"%d",expendedEnergy];  | 
|     }  | 
|     else  | 
|     {  | 
|         self.EnergyExpended = @"0";  | 
|     }  | 
|       | 
|       | 
|       | 
|     // Determine if RR-interval data is present //  | 
|     // If the 4th bit of the first byte is 1 this means there is RR data //  | 
|     if (reportData[0] & 0x10)  | 
|     {  | 
|       | 
|         // The number of RR-interval values is total bytes left / 2 (size of uint16) //  | 
|           | 
|         NSUInteger length = [data length];  | 
|         NSUInteger count = (length - offset)/2;  | 
|         uint16_t RRinterval = 0 ;  | 
|         for (int i = 0; i < count; i++) {  | 
|               | 
|             // The unit for RR interval is 1/1024 seconds //  | 
|             RRinterval = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[offset]));  | 
|             RRinterval = ((double)RRinterval / 1024.0 ) * 1000.0;  | 
|             offset = offset + 2; // Plus 2 bytes //  | 
|             self.RR_Interval = [NSString stringWithFormat:@"%d",RRinterval];  | 
|   | 
|               | 
|         }  | 
|           | 
|           | 
|     }  | 
|       | 
|     [Utilities logDataWithService:[ResourceHandler getServiceNameForUUID:HRM_HEART_RATE_SERVICE_UUID] characteristic:[ResourceHandler getCharacteristicNameForUUID:HRM_NOTIFICATIONS_CHARACTERISTIC_UUID] descriptor:nil operation:[NSString stringWithFormat:@"%@%@ %@",NOTIFY_RESPONSE,DATA_SEPERATOR,[Utilities convertDataToLoggerFormat:data]]];  | 
|   | 
| }  | 
|   | 
| /*!  | 
|  *  @method getSensorContactStatus:  | 
|  *  | 
|  *  @discussion   Instance method to get the body location of the device is available or not  | 
|  *  | 
|  */  | 
|   | 
| -(BOOL)getSensorContactStatus:(CBCharacteristic *)characteristic  | 
| {  | 
|       | 
|     NSData *data = [characteristic value];      // 1  | 
|     const uint8_t *reportData = [data bytes];  | 
|     if((reportData[0] & 0x02) == 4)  | 
|         return YES;  | 
|     return NO;  | 
| }  | 
|   | 
| /*!  | 
|  *  @method getBodyLocation:  | 
|  *  | 
|  *  @discussion   Instance method to get the body location of the device  | 
|  *  | 
|  */  | 
| //  | 
| - (void) getBodyLocation:(CBCharacteristic *)characteristic  | 
| {  | 
|     NSData *sensorData = [characteristic value];  | 
|     uint8_t *bodyData = (uint8_t *)[sensorData bytes];  | 
|     if (bodyData ) {  | 
|         uint8_t bodyLocation = bodyData[0];  | 
|         NSString *Sensloc = @"";  | 
|         switch (bodyLocation)  | 
|         {  | 
|             case 0:  | 
|                 Sensloc = OTHER; break;  | 
|             case 1:  | 
|                 Sensloc = CHEST; break;  | 
|             case 2:  | 
|                 Sensloc = WRIST; break;  | 
|             case 3:  | 
|                 Sensloc = FINGER; break;  | 
|             case 4:  | 
|                 Sensloc = HAND; break;  | 
|             case 5:  | 
|                 Sensloc = EAR_LOBE; break;  | 
|             case 6:  | 
|                 Sensloc = FOOT; break;  | 
|             default:  | 
|                 break;  | 
|         }  | 
|         self.sensorLocation = Sensloc;  | 
|     }  | 
|     else {  | 
|         self.sensorLocation = LOCATION_NA;  | 
|     }  | 
|       | 
|      [Utilities logDataWithService:[ResourceHandler getServiceNameForUUID:characteristic.service.UUID] characteristic:[ResourceHandler getCharacteristicNameForUUID:characteristic.UUID] descriptor:nil operation:[NSString stringWithFormat:@"%@%@ %@",READ_RESPONSE,DATA_SEPERATOR,[Utilities convertDataToLoggerFormat:sensorData]]];  | 
| }  | 
|   | 
|   | 
|   | 
|   | 
| @end  |