From 510fce75b1d2982ee279a9312c1c50d930755d09 Mon Sep 17 00:00:00 2001 From: Pavel Ivashkov Date: Wed, 17 May 2017 19:48:07 +0300 Subject: [PATCH] Fix #34 Subscribe to scratch characteristic updates --- .../project.pbxproj | 16 ++ source/PTDBean.m | 29 ++- source/Profiles/Scratch/ScratchProfile.h | 34 +++ source/Profiles/Scratch/ScratchProfile.m | 228 ++++++++++++++++++ source/Public/PTDBean.h | 1 + 5 files changed, 306 insertions(+), 2 deletions(-) create mode 100644 source/Profiles/Scratch/ScratchProfile.h create mode 100644 source/Profiles/Scratch/ScratchProfile.m diff --git a/Bean OSX Static Library/Bean OSX Library.xcodeproj/project.pbxproj b/Bean OSX Static Library/Bean OSX Library.xcodeproj/project.pbxproj index 115223c..7601383 100644 --- a/Bean OSX Static Library/Bean OSX Library.xcodeproj/project.pbxproj +++ b/Bean OSX Static Library/Bean OSX Library.xcodeproj/project.pbxproj @@ -103,6 +103,8 @@ A9448BF11C7B94DE0031DA2C /* IOBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52DE25C018AAC53900E69929 /* IOBluetooth.framework */; }; A94C7A6B1C7CC89B0018A161 /* blink.hex in Resources */ = {isa = PBXBuildFile; fileRef = A94C7A6A1C7CC89B0018A161 /* blink.hex */; }; A99DECD01C7F4B32007F506D /* Firmware Images in Resources */ = {isa = PBXBuildFile; fileRef = A99DECCF1C7F4B32007F506D /* Firmware Images */; }; + C42299DA1ECCABFE00BE65B6 /* ScratchProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = C42299D81ECCABFE00BE65B6 /* ScratchProfile.m */; }; + C42299DB1ECCABFE00BE65B6 /* ScratchProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = C42299D81ECCABFE00BE65B6 /* ScratchProfile.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -223,6 +225,8 @@ A9448BEF1C7B94D10031DA2C /* IOBluetoothUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOBluetoothUI.framework; path = System/Library/Frameworks/IOBluetoothUI.framework; sourceTree = SDKROOT; }; A94C7A6A1C7CC89B0018A161 /* blink.hex */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = blink.hex; sourceTree = ""; }; A99DECCF1C7F4B32007F506D /* Firmware Images */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "Firmware Images"; sourceTree = ""; }; + C42299D71ECCABFE00BE65B6 /* ScratchProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScratchProfile.h; sourceTree = ""; }; + C42299D81ECCABFE00BE65B6 /* ScratchProfile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScratchProfile.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -511,6 +515,7 @@ 52D34E9418A9B890002F0C59 /* Device Info */, 52D34E9718A9B890002F0C59 /* Gatt Serial */, 52D34EA518A9B890002F0C59 /* OAD */, + C42299D61ECCABDC00BE65B6 /* Scratch */, ); name = "BLE Profiles"; sourceTree = ""; @@ -586,6 +591,15 @@ path = Resources; sourceTree = ""; }; + C42299D61ECCABDC00BE65B6 /* Scratch */ = { + isa = PBXGroup; + children = ( + C42299D71ECCABFE00BE65B6 /* ScratchProfile.h */, + C42299D81ECCABFE00BE65B6 /* ScratchProfile.m */, + ); + path = Scratch; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -775,6 +789,7 @@ 52D34EC218A9B890002F0C59 /* OadProfile.m in Sources */, 969FDE98191A9DE80068B99A /* PTDBeanManager.m in Sources */, 969FDE97191A9DE80068B99A /* PTDBean.m in Sources */, + C42299DA1ECCABFE00BE65B6 /* ScratchProfile.m in Sources */, 52BC523718D96A610083A031 /* AppMessagingLayer.m in Sources */, 36539CB81C7CEB5700F4926F /* PTDIntelHexLine.m in Sources */, 52D34EB518A9B890002F0C59 /* GattSerialProfile.m in Sources */, @@ -813,6 +828,7 @@ files = ( 2F6B5991196CA72E000CC6E8 /* PTDBleDevice.m in Sources */, 364BD51F1C8F46E40065893B /* BEAN_Helper.m in Sources */, + C42299DB1ECCABFE00BE65B6 /* ScratchProfile.m in Sources */, 96506ECA1937E82A00D02440 /* BatteryProfile.m in Sources */, 520C8EE0191C31970086C530 /* BleProfile.m in Sources */, 520C8EDE191C31780086C530 /* NSData+CRC.m in Sources */, diff --git a/source/PTDBean.m b/source/PTDBean.m index 4ccfbf0..6fe1eac 100644 --- a/source/PTDBean.m +++ b/source/PTDBean.m @@ -21,7 +21,7 @@ } BeanArduinoOADLocalState; @interface PTDBean () + DevInfoProfileDelegate, ScratchProfileDelegate> #pragma mark - Header readonly overrides @@ -47,6 +47,7 @@ @implementation PTDBean OadProfile* oad_profile; GattSerialProfile* gatt_serial_profile; BatteryProfile* battery_profile; + ScratchProfile* scratch_profile; NSData* arduinoFwImage; NSInteger arduinoFwImage_chunkIndex; @@ -374,7 +375,8 @@ -(id)initWithPeripheral:(CBPeripheral*)peripheral beanManager:(id + +#import "BleProfile.h" + +#define BEAN_SCRATCH_SERVICE_UUID PUNCHTHROUGHDESIGN_128_UUID(@"FF20") +#define BEAN_SCRATCH_BANK1_CHARACTERISTIC_UUID PUNCHTHROUGHDESIGN_128_UUID(@"FF21") +#define BEAN_SCRATCH_BANK2_CHARACTERISTIC_UUID PUNCHTHROUGHDESIGN_128_UUID(@"FF22") +#define BEAN_SCRATCH_BANK3_CHARACTERISTIC_UUID PUNCHTHROUGHDESIGN_128_UUID(@"FF23") +#define BEAN_SCRATCH_BANK4_CHARACTERISTIC_UUID PUNCHTHROUGHDESIGN_128_UUID(@"FF24") +#define BEAN_SCRATCH_BANK5_CHARACTERISTIC_UUID PUNCHTHROUGHDESIGN_128_UUID(@"FF25") + + +@protocol ScratchProfileDelegate + +@optional + +- (void)didUpdateScratchBank:(NSInteger)bank data:(NSData*)data; + +@end + + +@interface ScratchProfile : BleProfile + +@property (nonatomic, weak) id delegate; + +@property (nonatomic, strong) NSData *scratchBank1; +@property (nonatomic, strong) NSData *scratchBank2; +@property (nonatomic, strong) NSData *scratchBank3; +@property (nonatomic, strong) NSData *scratchBank4; +@property (nonatomic, strong) NSData *scratchBank5; + +- (BOOL)readScratchBank:(NSInteger)bank; + +@end diff --git a/source/Profiles/Scratch/ScratchProfile.m b/source/Profiles/Scratch/ScratchProfile.m new file mode 100644 index 0000000..53a483f --- /dev/null +++ b/source/Profiles/Scratch/ScratchProfile.m @@ -0,0 +1,228 @@ +#import "ScratchProfile.h" + + +@interface ScratchProfile () + +@property (nonatomic, strong) CBService *service_scratch; +@property (nonatomic, strong) CBCharacteristic *characteristic_bank1; +@property (nonatomic, strong) CBCharacteristic *characteristic_bank2; +@property (nonatomic, strong) CBCharacteristic *characteristic_bank3; +@property (nonatomic, strong) CBCharacteristic *characteristic_bank4; +@property (nonatomic, strong) CBCharacteristic *characteristic_bank5; + +@end + + +@implementation ScratchProfile + +@dynamic delegate; // Delegate is already synthesized by BleProfile + ++ (void)load +{ + [super registerProfile:self serviceUUID:BEAN_SCRATCH_SERVICE_UUID]; +} + +- (id)initWithService:(CBService *)service +{ + self = [super init]; + if (!self) return nil; + + self.service_scratch = service; + peripheral = service.peripheral; + + return self; +} + +- (void)validate +{ + NSArray *characteristics = @[[CBUUID UUIDWithString:BEAN_SCRATCH_BANK1_CHARACTERISTIC_UUID], + [CBUUID UUIDWithString:BEAN_SCRATCH_BANK2_CHARACTERISTIC_UUID], + [CBUUID UUIDWithString:BEAN_SCRATCH_BANK3_CHARACTERISTIC_UUID], + [CBUUID UUIDWithString:BEAN_SCRATCH_BANK4_CHARACTERISTIC_UUID], + [CBUUID UUIDWithString:BEAN_SCRATCH_BANK5_CHARACTERISTIC_UUID]]; + [peripheral discoverCharacteristics:characteristics forService:self.service_scratch]; + [self __notifyValidity]; +} + +- (BOOL)isValid:(NSError **)error +{ + return (self.service_scratch && + self.characteristic_bank1 && + self.characteristic_bank2 && + self.characteristic_bank3 && + self.characteristic_bank4 && + self.characteristic_bank5 + ); +} + + +- (BOOL)readScratchBank:(NSInteger)bank { + switch (bank) { + case 1: + if (!self.characteristic_bank1) return NO; + [peripheral readValueForCharacteristic:self.characteristic_bank1]; + break; + case 2: + if (!self.characteristic_bank2) return NO; + [peripheral readValueForCharacteristic:self.characteristic_bank2]; + break; + case 3: + if (!self.characteristic_bank3) return NO; + [peripheral readValueForCharacteristic:self.characteristic_bank3]; + break; + case 4: + if (!self.characteristic_bank4) return NO; + [peripheral readValueForCharacteristic:self.characteristic_bank4]; + break; + case 5: + if (!self.characteristic_bank5) return NO; + [peripheral readValueForCharacteristic:self.characteristic_bank5]; + break; + default: + return NO; + } + return YES; +} + +#pragma mark Private Methods + +/** + * Process the characteristics discovered and store the relevant ones into local variables for future use. + */ +- (void)__processCharacteristics +{ + if (!self.service_scratch) return; + if (!self.service_scratch.characteristics) return; + + for (CBCharacteristic *characteristic in self.service_scratch.characteristics) { + if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:BEAN_SCRATCH_BANK1_CHARACTERISTIC_UUID]]) { + self.characteristic_bank1 = characteristic; + } else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:BEAN_SCRATCH_BANK2_CHARACTERISTIC_UUID]]) { + self.characteristic_bank2 = characteristic; + } else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:BEAN_SCRATCH_BANK3_CHARACTERISTIC_UUID]]) { + self.characteristic_bank3 = characteristic; + } else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:BEAN_SCRATCH_BANK4_CHARACTERISTIC_UUID]]) { + self.characteristic_bank4 = characteristic; + } else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:BEAN_SCRATCH_BANK5_CHARACTERISTIC_UUID]]) { + self.characteristic_bank5 = characteristic; + } + } +} + + +#pragma mark CBPeripheralDelegate callbacks + +- (void)peripheral:(CBPeripheral *)aPeripheral didDiscoverCharacteristicsForService:(CBService *)service + error:(NSError *)error +{ + if (![service isEqual:self.service_scratch]) return; + + if (error) { + PTDLog(@"%@: Discovery of Device Information characteristics was unsuccessful", self.class.description); + return; + } + [self __processCharacteristics]; + + if (!self.characteristic_bank1) { + PTDLog(@"%@: Did not find Scratch Bank 1 characteristic", self.class.description); + return; + }; + if (!self.characteristic_bank2) { + PTDLog(@"%@: Did not find Scratch Bank 2 characteristic", self.class.description); + return; + }; + if (!self.characteristic_bank3) { + PTDLog(@"%@: Did not find Scratch Bank 3 characteristic", self.class.description); + return; + }; + if (!self.characteristic_bank4) { + PTDLog(@"%@: Did not find Scratch Bank 4 characteristic", self.class.description); + return; + }; + if (!self.characteristic_bank5) { + PTDLog(@"%@: Did not find Scratch Bank 5 characteristic", self.class.description); + return; + }; + + PTDLog(@"%@: Found all Scratch characteristics", self.class.description); + + [peripheral setNotifyValue:TRUE forCharacteristic:self.characteristic_bank1]; + [peripheral setNotifyValue:TRUE forCharacteristic:self.characteristic_bank2]; + [peripheral setNotifyValue:TRUE forCharacteristic:self.characteristic_bank3]; + [peripheral setNotifyValue:TRUE forCharacteristic:self.characteristic_bank4]; + [peripheral setNotifyValue:TRUE forCharacteristic:self.characteristic_bank5]; +} + +- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic + error:(NSError *)error +{ + if (error) { + PTDLog(@"Error reading characteristic: %@, %@", characteristic.UUID, error); + return; + } + + if ([characteristic isEqual:self.characteristic_bank1]) { + self.scratchBank1 = characteristic.value; + PTDLog(@"%@: Scratch Bank 1 updated", self.class.description); + if (self.delegate && [self.delegate respondsToSelector:@selector(didUpdateScratchBank:data:)]) { + [self.delegate didUpdateScratchBank:1 data:self.scratchBank1]; + } + + } else if ([characteristic isEqual:self.characteristic_bank2]) { + self.scratchBank2 = characteristic.value; + PTDLog(@"%@: Scratch Bank 2 updated", self.class.description); + if (self.delegate && [self.delegate respondsToSelector:@selector(didUpdateScratchBank:data:)]) { + [self.delegate didUpdateScratchBank:2 data:self.scratchBank1]; + } + + } else if ([characteristic isEqual:self.characteristic_bank3]) { + self.scratchBank3 = characteristic.value; + PTDLog(@"%@: Scratch Bank 3 updated", self.class.description); + if (self.delegate && [self.delegate respondsToSelector:@selector(didUpdateScratchBank:data:)]) { + [self.delegate didUpdateScratchBank:3 data:self.scratchBank1]; + } + + } else if ([characteristic isEqual:self.characteristic_bank4]) { + self.scratchBank4 = characteristic.value; + PTDLog(@"%@: Scratch Bank 4 updated", self.class.description); + if (self.delegate && [self.delegate respondsToSelector:@selector(didUpdateScratchBank:data:)]) { + [self.delegate didUpdateScratchBank:4 data:self.scratchBank1]; + } + + } else if ([characteristic isEqual:self.characteristic_bank5]) { + self.scratchBank5 = characteristic.value; + PTDLog(@"%@: Scratch Bank 5 updated", self.class.description); + if (self.delegate && [self.delegate respondsToSelector:@selector(didUpdateScratchBank:data:)]) { + [self.delegate didUpdateScratchBank:5 data:self.scratchBank1]; + } + } +} + +- (void)peripheral:(CBPeripheral *)aPeripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic + error:(NSError *)error +{ + int bank = 0; + + if ([characteristic isEqual:self.characteristic_bank1]) { + bank = 1; + } else if ([characteristic isEqual:self.characteristic_bank2]) { + bank = 2; + } else if ([characteristic isEqual:self.characteristic_bank3]) { + bank = 3; + } else if ([characteristic isEqual:self.characteristic_bank4]) { + bank = 4; + } else if ([characteristic isEqual:self.characteristic_bank5]) { + bank = 5; + } + + if (error) { + PTDLog(@"%@: Error trying to set Scratch Bank %d Characteristic to \"Notify\"", self.class.description, bank); + return; + } + + PTDLog(@"%@: Scratch Bank %d Characteristic set to \"Notify\"", self.class.description, bank); + + [peripheral readValueForCharacteristic:characteristic]; +} + +@end diff --git a/source/Public/PTDBean.h b/source/Public/PTDBean.h index d48895e..39005e8 100644 --- a/source/Public/PTDBean.h +++ b/source/Public/PTDBean.h @@ -15,6 +15,7 @@ #endif #import "PTDBleDevice.h" #import "BatteryProfile.h" +#import "ScratchProfile.h" #import "PTDFirmwareHelper.h" #define ARDUINO_OAD_GENERIC_TIMEOUT_SEC 3