1// Copyright 2015-present 650 Industries. All rights reserved. 2 3#import "EXKernel.h" 4#import "EXSensorManager.h" 5#import <CoreMotion/CoreMotion.h> 6 7@interface EXSensorManager () 8 9@property (nonatomic, strong) CMMotionManager *manager; 10@property (nonatomic, strong) CMAltimeter *altimeter; 11@property (nonatomic, strong) NSMutableDictionary *accelerometerHandlers; 12@property (nonatomic, strong) NSMutableDictionary *barometerHandlers; 13@property (nonatomic, strong) NSMutableDictionary *deviceMotionHandlers; 14@property (nonatomic, strong) NSMutableDictionary *gyroscopeHandlers; 15@property (nonatomic, strong) NSMutableDictionary *magnetometerHandlers; 16@property (nonatomic, strong) NSMutableDictionary *magnetometerUncalibratedHandlers; 17 18@end 19 20@implementation EXSensorManager 21 22- (instancetype)init 23{ 24 if (self = [super init]) { 25 _accelerometerHandlers = [[NSMutableDictionary alloc] init]; 26 _barometerHandlers = [[NSMutableDictionary alloc] init]; 27 _deviceMotionHandlers = [[NSMutableDictionary alloc] init]; 28 _gyroscopeHandlers = [[NSMutableDictionary alloc] init]; 29 _magnetometerHandlers = [[NSMutableDictionary alloc] init]; 30 _magnetometerUncalibratedHandlers = [[NSMutableDictionary alloc] init]; 31 } 32 return self; 33} 34 35- (CMMotionManager *)manager 36{ 37 if (!_manager) { 38 _manager = [[CMMotionManager alloc] init]; 39 } 40 return _manager; 41} 42 43- (CMAltimeter *)altimeter 44{ 45 if (!_altimeter) { 46 _altimeter = [[CMAltimeter alloc] init]; 47 } 48 return _altimeter; 49} 50 51 52- (void)dealloc 53{ 54 [self.manager stopAccelerometerUpdates]; 55 [self.manager stopDeviceMotionUpdates]; 56 [self.manager stopGyroUpdates]; 57 [self.manager stopMagnetometerUpdates]; 58 [self.altimeter stopRelativeAltitudeUpdates]; 59} 60 61- (void)sensorModuleDidSubscribeForAccelerometerUpdatesOfExperience:scopeKey 62 withHandler:(void (^)(NSDictionary *event))handlerBlock 63{ 64 if ([self.manager isAccelerometerAvailable]) { 65 self.accelerometerHandlers[scopeKey] = handlerBlock; 66 } 67 if (![self.manager isAccelerometerActive]) { 68 [self.manager setAccelerometerUpdateInterval:0.1f]; 69 [self.manager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *data, NSError *error) { 70 for (void (^handler)(NSDictionary *) in self.accelerometerHandlers.allValues) { 71 handler(@{ 72 @"x": [NSNumber numberWithDouble:data.acceleration.x], 73 @"y": [NSNumber numberWithDouble:data.acceleration.y], 74 @"z": [NSNumber numberWithDouble:data.acceleration.z] 75 }); 76 } 77 }]; 78 } 79} 80 81- (void)sensorModuleDidUnsubscribeForAccelerometerUpdatesOfExperience:scopeKey 82{ 83 [self.accelerometerHandlers removeObjectForKey:scopeKey]; 84 if (self.accelerometerHandlers.count == 0) { 85 [self.manager stopAccelerometerUpdates]; 86 } 87} 88 89- (void)setAccelerometerUpdateInterval:(NSTimeInterval)intervalMs 90{ 91 [self.manager setAccelerometerUpdateInterval:intervalMs]; 92} 93 94- (void)sensorModuleDidSubscribeForDeviceMotionUpdatesOfExperience:(NSString *)scopeKey 95 withHandler:(void (^)(NSDictionary *event))handlerBlock 96{ 97 if ([self.manager isDeviceMotionAvailable]) { 98 self.deviceMotionHandlers[scopeKey] = handlerBlock; 99 } 100 if (![self.manager isDeviceMotionActive]) { 101 [self activateDeviceMotionUpdates]; 102 } 103} 104 105- (void)sensorModuleDidUnsubscribeForDeviceMotionUpdatesOfExperience:(NSString *)scopeKey 106{ 107 [self.deviceMotionHandlers removeObjectForKey:scopeKey]; 108 if (self.deviceMotionHandlers.count == 0 && self.magnetometerHandlers.count == 0) { 109 [self.manager stopDeviceMotionUpdates]; 110 } 111} 112 113- (void)setDeviceMotionUpdateInterval:(NSTimeInterval)intervalMs 114{ 115 [self.manager setDeviceMotionUpdateInterval:intervalMs]; 116} 117 118- (void)sensorModuleDidSubscribeForGyroscopeUpdatesOfExperience:(NSString *)scopeKey 119 withHandler:(void (^)(NSDictionary *event))handlerBlock 120{ 121 if ([self.manager isGyroAvailable]) { 122 self.gyroscopeHandlers[scopeKey] = handlerBlock; 123 } 124 if (![self.manager isGyroActive]) { 125 [self.manager setGyroUpdateInterval:0.1f]; 126 [self.manager startGyroUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMGyroData *data, NSError *error) { 127 for (void (^handler)(NSDictionary *) in self.gyroscopeHandlers.allValues) { 128 handler(@{ 129 @"x": [NSNumber numberWithDouble:data.rotationRate.x], 130 @"y": [NSNumber numberWithDouble:data.rotationRate.y], 131 @"z": [NSNumber numberWithDouble:data.rotationRate.z] 132 }); 133 } 134 }]; 135 } 136} 137 138- (void)sensorModuleDidUnsubscribeForGyroscopeUpdatesOfExperience:(NSString *)scopeKey 139{ 140 [self.gyroscopeHandlers removeObjectForKey:scopeKey]; 141 if (self.gyroscopeHandlers.count == 0) { 142 [self.manager stopGyroUpdates]; 143 } 144} 145 146- (void)setGyroscopeUpdateInterval:(NSTimeInterval)intervalMs 147{ 148 [self.manager setGyroUpdateInterval:intervalMs]; 149} 150 151- (void)sensorModuleDidSubscribeForMagnetometerUpdatesOfExperience:(NSString *)scopeKey 152 withHandler:(void (^)(NSDictionary *event))handlerBlock 153{ 154 if ([self.manager isDeviceMotionAvailable]) { 155 self.magnetometerHandlers[scopeKey] = handlerBlock; 156 } 157 if (![self.manager isDeviceMotionActive]) { 158 [self activateDeviceMotionUpdates]; 159 } 160} 161 162- (void)sensorModuleDidUnsubscribeForMagnetometerUpdatesOfExperience:(NSString *)scopeKey 163{ 164 [self.magnetometerHandlers removeObjectForKey:scopeKey]; 165 if (self.deviceMotionHandlers.count == 0 && self.magnetometerHandlers.count == 0) { 166 [self.manager stopDeviceMotionUpdates]; 167 } 168} 169 170- (void)setMagnetometerUpdateInterval:(NSTimeInterval)intervalMs 171{ 172 [self.manager setDeviceMotionUpdateInterval:intervalMs]; 173} 174 175- (void)sensorModuleDidSubscribeForMagnetometerUncalibratedUpdatesOfExperience:(NSString *)scopeKey 176 withHandler:(void (^)(NSDictionary *event))handlerBlock 177{ 178 if ([self.manager isMagnetometerAvailable]) { 179 self.magnetometerUncalibratedHandlers[scopeKey] = handlerBlock; 180 } 181 if (![self.manager isMagnetometerActive]) { 182 [self.manager setMagnetometerUpdateInterval:0.1f]; 183 [self.manager startMagnetometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMMagnetometerData *data, NSError *error) { 184 for (void (^handler)(NSDictionary *) in self.magnetometerUncalibratedHandlers.allValues) { 185 handler(@{ 186 @"x": [NSNumber numberWithDouble:data.magneticField.x], 187 @"y": [NSNumber numberWithDouble:data.magneticField.y], 188 @"z": [NSNumber numberWithDouble:data.magneticField.z] 189 }); 190 } 191 }]; 192 } 193} 194 195- (void)sensorModuleDidUnsubscribeForMagnetometerUncalibratedUpdatesOfExperience:(NSString *)scopeKey 196{ 197 [self.magnetometerUncalibratedHandlers removeObjectForKey:scopeKey]; 198 if (self.magnetometerUncalibratedHandlers.count == 0) { 199 [self.manager stopMagnetometerUpdates]; 200 } 201} 202 203- (void)setMagnetometerUncalibratedUpdateInterval:(NSTimeInterval)intervalMs 204{ 205 [self.manager setMagnetometerUpdateInterval:intervalMs]; 206} 207 208- (float)getGravity 209{ 210 return EXGravity; 211} 212 213- (void)activateDeviceMotionUpdates 214{ 215 [self.manager setDeviceMotionUpdateInterval:0.1f]; 216 [self.manager 217 startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical 218 toQueue:[NSOperationQueue mainQueue] 219 withHandler:^(CMDeviceMotion *data, NSError *error) { 220 UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; 221 int orientationDegrees; 222 switch (orientation) { 223 case UIDeviceOrientationPortrait: 224 orientationDegrees = 0; 225 break; 226 case UIDeviceOrientationLandscapeLeft: 227 orientationDegrees = -90; 228 break; 229 case UIDeviceOrientationLandscapeRight: 230 orientationDegrees = 90; 231 break; 232 case UIDeviceOrientationPortraitUpsideDown: 233 orientationDegrees = 180; 234 break; 235 default: 236 orientationDegrees = 0; 237 break; 238 } 239 240 NSDictionary *result = @{ 241 @"acceleration": @{ 242 @"x": @(data.userAcceleration.x * EXGravity), 243 @"y": @(data.userAcceleration.y * EXGravity), 244 @"z": @(data.userAcceleration.z * EXGravity) 245 }, 246 @"accelerationIncludingGravity": @{ 247 @"x": @((data.userAcceleration.x + data.gravity.x) * EXGravity), 248 @"y": @((data.userAcceleration.y + data.gravity.y) * EXGravity), 249 @"z": @((data.userAcceleration.z + data.gravity.z) * EXGravity) 250 }, 251 @"rotation": @{ 252 @"alpha": @(data.attitude.yaw), 253 @"beta": @(data.attitude.pitch), 254 @"gamma": @(data.attitude.roll), 255 }, 256 @"rotationRate" :@{ 257 @"alpha": @(data.rotationRate.z), 258 @"beta": @(data.rotationRate.y), 259 @"gamma": @(data.rotationRate.x) 260 }, 261 @"orientation": @(orientationDegrees) 262 }; 263 264 // DeviceMotionUpdates handle DeviceMotion data as well as magnetic field 265 for (void (^handler)(NSDictionary *) in self.deviceMotionHandlers.allValues) { 266 handler(result); 267 } 268 269 for (void (^handler)(NSDictionary *) in self.magnetometerHandlers.allValues) { 270 handler(@{ 271 @"x": [NSNumber numberWithDouble:data.magneticField.field.x], 272 @"y": [NSNumber numberWithDouble:data.magneticField.field.y], 273 @"z": [NSNumber numberWithDouble:data.magneticField.field.z] 274 }); 275 } 276 }]; 277} 278 279- (void)sensorModuleDidSubscribeForBarometerUpdatesOfExperience:(NSString *)scopeKey withHandler:(void (^)(NSDictionary *event))handlerBlock 280{ 281 if ([self isBarometerAvailable]) { 282 _barometerHandlers[scopeKey] = handlerBlock; 283 } 284 285 __weak EXSensorManager *weakSelf = self; 286 [[self altimeter] startRelativeAltitudeUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAltitudeData * _Nullable data, NSError * _Nullable error) { 287 __strong EXSensorManager *strongSelf = weakSelf; 288 if (strongSelf && data) { 289 for (void (^handler)(NSDictionary *) in strongSelf.barometerHandlers.allValues) { 290 handler(@{ 291 @"pressure": @([data.pressure intValue] * 10), // conversion from kPa to hPa 292 @"relativeAltitude": data.relativeAltitude, 293 }); 294 } 295 } 296 }]; 297} 298 299- (void)sensorModuleDidUnsubscribeForBarometerUpdatesOfExperience:(NSString *)scopeKey 300{ 301 [_barometerHandlers removeObjectForKey:scopeKey]; 302 if (_barometerHandlers.count == 0) { 303 [_altimeter stopRelativeAltitudeUpdates]; 304 } 305} 306 307- (void)setBarometerUpdateInterval:(NSTimeInterval)intervalMs 308{ 309 // Do nothing 310} 311 312- (BOOL)isBarometerAvailable 313{ 314 return [CMAltimeter isRelativeAltitudeAvailable]; 315} 316 317- (BOOL)isAccelerometerAvailable { 318 return [self.manager isAccelerometerAvailable]; 319} 320 321- (BOOL)isDeviceMotionAvailable { 322 return [self.manager isDeviceMotionAvailable]; 323} 324 325- (BOOL)isGyroAvailable { 326 return [self.manager isGyroAvailable]; 327} 328 329- (BOOL)isMagnetometerAvailable { 330 return [self.manager isMagnetometerAvailable]; 331} 332 333- (BOOL)isMagnetometerUncalibratedAvailable { 334 return [self.manager isMagnetometerAvailable]; 335} 336 337@end 338