Get accelerometer and gyroscope data from apple watch (and not the iphone)? - ios

After seeing this question, I tried to code up a quick program that would save the watches accelerometer and gyroscope data to a file.
#implementation InterfaceController{
NSMutableArray *accData;
bool recording;
}
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
self.motionManager = [[CMMotionManager alloc] init];
[self.motionManager setAccelerometerUpdateInterval:.01];
}
- (IBAction)startStopRecording {
if (!recording){//We are starting to record.
recording = YES;
accData = [[NSMutableArray alloc] init];
[self.startRecording setTitle:#"Stop Recording"];
[self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
[accData addObject:[NSString stringWithFormat:#"%f, %f, %f", accelerometerData.acceleration.x, accelerometerData.acceleration.y, accelerometerData.acceleration.z]];
}];
}else{
recording = NO;//we are stopping the recording
[self.motionManager stopAccelerometerUpdates];
[self.startRecording setTitle:#"Start Recording"];
[InterfaceController openParentApplication:#{ #"accData": accData } reply:^(NSDictionary *replyInfo, NSError *error) { //this method saves the array to a csv file.
NSLog(#"Data has been saved.");
}];
}
}
I had plotted this data and for the life of me, no matter how hard I shook the watch, all my plots looked like this:
Until 8 hours later, I started to suspect that I wasn't grabbing the acceleration data from the watch, but rather from the phone (sitting still on the table next to me). I ran some tests and confirmed that this is exactly what is happening.
Which leads me to the original question. How do I pull acceleration/gyro/data from the watch and not from the iPhone?

The problem was that I wasn't running watchOS2. I assumed I was but it's still in beta and I hadn't installed it. The data I was getting was accelerometer data from the phone. Also, currently, you can only get acc data from the watch using watchOS2 and not gyro data.

you can use CoreMotion framework to get activity data.
while I can only get accel data, the gyro often return false.

Related

apple watch CMDeviceMotion is not giving me good readings

Currently, I am just recording a bunch of motion data and saving it to a file. However, when I plot the data, I am having a hard time believing I am getting the right readings. Here is my watch code:
- (IBAction)startStopRecording {
if (!recording){
NSLog(#"starting to record");
recording = YES;
data = [[NSMutableArray alloc] init];
[self.startRecording setTitle:#"Stop Recording"];
if (self.motionManager.deviceMotionAvailable) {
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
[data addObject:[NSString stringWithFormat:#"%f, %f, %f, %f, %f, %f, %f, %f, %f", motion.attitude.pitch, motion.attitude.roll, motion.attitude.yaw, motion.userAcceleration.x, motion.userAcceleration.y, motion.userAcceleration.z, motion.rotationRate.x, motion.rotationRate.y, motion.rotationRate.z]];
NSLog(#".");
}];
}
}else{
recording = NO;
NSLog(#"stopping recording");
[self.motionManager stopDeviceMotionUpdates];
[self.startRecording setTitle:#"Start Recording"];
[InterfaceController openParentApplication:#{ #"data": data } reply:^(NSDictionary *replyInfo, NSError *error) {
NSLog(#"Data has been saved.");
NSLog(#"replyInfo %#", replyInfo);
}];
}
}
The parent application just writes all the data to a file. I recorded the watch rotating back and forth on all three axes (pitch, then roll, then yaw):
And then when I plotted the data, this is what I got:
The yaw is so noisy that you can't see a signal at all in there. I also have a similar problem when plotting the acceleration after jerking the watch in three different directions. I can see spikes of acceleration, but they don't seem to be direction dependent. Any ideas on how to improve this? Am I missing something? Could I just have a bad sensor in my watch?
The reason is because I wasn't actually pulling data from the watch. It was pulling data from the phone. In order to pull data (currently only acc data is available from the watch) you need to have watchOS2 (currently in beta). Otherwise the watch will just get handed data from the phone.

Detect user arrived to a location and depart from a location pattern using CMMotionManager

I am working on app which requires to detect user arrived to place and depart from a place and time spent in that location.
I am reading CoreMotion Readings but unable to get any clue how to form patterns.
if (_motionManager == nil) {
_motionManager = [[CMMotionManager alloc]init];
}
_motionManager.deviceMotionUpdateInterval = 1/60;
[_motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
if (error) {
[_motionManager stopDeviceMotionUpdates];
NSLog(#"Error while reading accelerometer data: %#", error.description);
return;
}
[self getacceleration:motion];
}];
In filterMotionData I am trying to read device acceleration with motion.userAcceleration but not find any good solution.
- (void)getacceleration:(CMMotion*)motion {
CMAcceleration acceleration = motion.userAcceleration;
//Calculate Device acceleration
}
Here If I can calculate device acceleration then on slowing down device acceleration I can say user arrived to a Location and when as device acceleration increased for particular time period means user depart from a location.
I was exploring I came to know Moves app does the same for all version of devices.
I gone through CLVisit API provided by CLLocationManager but it won't give short stay of user in a location and also its notify very late.
Please guide if anyone work on same type of requirement.

HealthKit (iOS) won't deliver data in background (objC)

We're currently trying to get HealthKit to work in the background, in order to deliver steps data to our server when the App is closed.
For experimental purposes we've created a brand new iOS project in XCode, enabled HealhtKit and all background modes in Compabilities. After that, we pretty much run the code (see further down).
So what happens first is that the app ofcourse asks for the permissions, which we grant. What we're expecting is that the app should keep deliver the steps data every hour, to the server. But it doesnt do that, it seems like the app cant do anything when it's not active.
The app only deliver data when it gets resumed or started, but not at all from the background (Soft-closed / Hard-closed)
appdelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self setTypes];
return YES;
}
-(void) setTypes
{
self.healthStore = [[HKHealthStore alloc] init];
NSMutableSet* types = [[NSMutableSet alloc]init];
[types addObject:[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]];
[self.healthStore requestAuthorizationToShareTypes: types
readTypes: types
completion:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self observeQuantityType];
[self enableBackgroundDeliveryForQuantityType];
});
}];
}
-(void)enableBackgroundDeliveryForQuantityType{
[self.healthStore enableBackgroundDeliveryForType: [HKQuantityType quantityTypeForIdentifier: HKQuantityTypeIdentifierStepCount] frequency:HKUpdateFrequencyImmediate withCompletion:^(BOOL success, NSError *error) {
}];
}
-(void) observeQuantityType{
HKSampleType *quantityType = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKObserverQuery *query =
[[HKObserverQuery alloc]
initWithSampleType:quantityType
predicate:nil
updateHandler:^(HKObserverQuery *query,
HKObserverQueryCompletionHandler completionHandler,
NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (completionHandler) completionHandler();
[self getQuantityResult];
});
}];
[self.healthStore executeQuery:query];
}
-(void) getQuantityResult{
NSInteger limit = 0;
NSPredicate* predicate = nil;
NSString *endKey = HKSampleSortIdentifierEndDate;
NSSortDescriptor *endDate = [NSSortDescriptor sortDescriptorWithKey: endKey ascending: NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType: [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount]
predicate: predicate
limit: limit
sortDescriptors: #[endDate]
resultsHandler:^(HKSampleQuery *query, NSArray* results, NSError *error){
dispatch_async(dispatch_get_main_queue(), ^{
// sends the data using HTTP
[self sendData: [self resultAsNumber:results]];
});
}];
[self.healthStore executeQuery:query];
}
I found this out a little while ago when talking to someone from Apple. Apparently you can't access HK data in the background if the device is locked:
NOTE
Because the HealthKit store is encrypted, your app cannot read data
from the store when the phone is locked. This means your app may not
be able to access the store when it is launched in the background.
However, apps can still write data to the store, even when the phone
is locked. The store temporarily caches the data and saves it to the
encrypted store as soon as the phone is unlocked.
from:
https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/
I see something that might be causing an issue in your AppDelegate, particularly this line:
[[NSURLConnection alloc] initWithRequest:request delegate:self];
This is creating an NSURLConnection, but not starting it. Try changing it to this:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
Edit: After taking a second look at the docs
They recommend setting up your observer queries in your application didFinishLaunchingWithOptions: method. In your code above, you set the HKObserverQuery up in the authorization handler, which is called on a random background queue. Try making this change to set it up on the main thread:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self setTypes];
[self observeQuantityType];
return YES;
}
HKObserverQuery Reference

CMMotionManager updateinterval not set

I'm using the CMMotionManager to gather accelerometer data. I am trying to set the update interval to every half second with the following:
[_motionManager setDeviceMotionUpdateInterval:.5];
[_motionManager startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init]
withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self performSelectorOnMainThread:#selector(update:) withObject:accelerometerData waitUntilDone:NO];
});}];
yet I receive updates far more frequently than every half second. Any idea why?
Wasn't setting the update interval for accelerometer itself.
[_motionManager setAccelerometerUpdateInterval:.5];

iPhone Motion - EXC BAD ACCESS

I'm beginning coding with the DeviceMotion class. After following Apple's documenation, i have the following:
- (void)viewDidLoad {
[super viewDidLoad];
myMM = [[CMMotionManager alloc] init];
myMM.deviceMotionUpdateInterval = 1.0/30.0;
theQ = [[NSOperationQueue currentQueue] retain];
motionHandler = ^ (CMDeviceMotion *motionData, NSError *error) {
if (motionData.rotationRate.z > 5.5 || motionData.rotationRate.z < -5.5) {
NSLog(#"Rotation of Z."); // Reference A
}
};
-(IBAction)toggleClick{
NSLog(#"toggle");
if(myMM.gyroAvailable){
if(myMM.deviceMotionActive){
NSLog(#"Stopping Motion Updates..");
[myMM stopDeviceMotionUpdates];
} else {
NSLog(#"Starting Motion Updates..");
[myMM startDeviceMotionUpdatesToQueue:theQ withHandler:motionHandler];
}
}
else {
NSLog(#"No motion available. Quit!");
}
This code works fine, however when I want to do any code except an NSLog (even something as simple as incrementing an integer) in place of the 'reference A', I get an EXEC Bad Access in the console.
I've looked around, and all I've found is that it's a memory leak of sorts. Does anyone know whats going on? If not, how can I figure it out? I'm pretty inexperienced with Instruments, but if I'm pointed in the right direction I'd be much appreciated.
EXC_BAD_ACCESS is an OS-level exception meaning that you are trying to access memory that doesn't belong to you. I think this has something to do with your block being local to the scope, so once it goes out of scope, it is destroyed. You need to create a copy of it on the heap.
Try this answer from the renowned Dave DeLong. Also, as with the normal Cocoa memory management rules, don't forget to release it if you've made a copy.
For example:
motionHandler = Block_copy(^ (CMDeviceMotion *motionData, NSError *error) {
if (motionData.rotationRate.z > 5.5 || motionData.rotationRate.z < -5.5) {
NSLog(#"Rotation of Z."); // Reference A
}
});
// and then later:
- (void) dealloc
{
[motionHandler release];
//and all others.
[super dealloc];
}

Resources