5

I would like an action to take place when the phone is stationary for 2 seconds. I've searched for ages around google and stack overflow. I discovered that "Accelerometer DidAccelerate" has been depreciated and that CoreMotion is the replacement. Everything I have seen has been to do with the 'shaking' motion. I've tried reading through apple's documentation but It just confuses me!

Basically, I want the app to detect that the g-forces on the phone have remained within a small limit for a certain amount of time (suggesting that the phone has been laid down on a table or something) and for it to call and instance or make the app do something.

Any help would be greatly appreciated.

Invalid Memory
  • 717
  • 7
  • 23
  • There's example code in the [apple docs](http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/motion_event_basics/motion_event_basics.html) – Mike Pollard May 29 '13 at 11:23

2 Answers2

4

You can do something like this:

CMMotionManager *mManager = [[CMMotionManager alloc] init];

if ([mManager isAccelerometerAvailable] == YES) {
    __block float lastActivityBefore = 0.0;
    [mManager setAccelerometerUpdateInterval:0.1];
    [mManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {

        double totalAcceleration = sqrt(accelerometerData.acceleration.x * accelerometerData.acceleration.x + accelerometerData.acceleration.y * accelerometerData.acceleration.y + accelerometerData.acceleration.z * accelerometerData.acceleration.z);
        if(totalAcceleration < SOME_LIMIT)
            lastActivityBefore = lastActivityBefore + 0.1;
        else
            lastActivityBefore = 0.0;

        if(lastActivityBefore >= 2.0)
        {
            //do something
        }
    }];
}

Accelerometer will show some minimal acceleration even if your device is steady, so you should make a testing in order to determine SOME_LIMIT value.

Also be advised that you should have only one instance CMMotionManager class in your app, so you're better to put it in your AppDelegate and initialize it only once.

codeplasma
  • 515
  • 3
  • 6
4

It's similar to the problem described in Simple iPhone motion detect. The basic setup for CMMotionManager is described in the Apple docs like Mike Pollard stated in his comment. I recommend especially the Handling Processed Device Motion Data section.

What you then need is CMDeviceMotion.userAcceleration which contains the pure acceleration without gravity.

CMMotionManager *motionManager = [[CMMotionManager alloc] init];
// UPDATE: set interval to 0.02 sec
motionManager.deviceMotionUpdateInterval = 1.0 / 50.0;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] 
        withHandler:^(CMDeviceMotion *deviceMotion, NSError *error) {
    CMAcceleration userAcceleration = deviceMotion.userAcceleration;
    double totalAcceleration = sqrt(userAcceleration.x * userAcceleration.x + 
        userAcceleration.y * userAcceleration.y + userAcceleration.z * userAcceleration.z);
    // UPDATE: print debug information
    NSLog (@"total=%f x=%f y=%f z=%f", totalAcceleration, userAcceleration.x, userAcceleration.y, userAcceleration.z);
    // if(totalAcceleration < SOME_LIMIT) ...

Then proceed like codeplasma has described in his answer above.

Also be aware that the solution might not be precise if used in the underground, bus, etc. because of external accelerations.

Community
  • 1
  • 1
Kay
  • 12,918
  • 4
  • 55
  • 77
  • Thanks for the help, but I'm having problems (as always). I'm not getting any errors, but it simply isn't working. The action i've told it to carry out, isn't being carried out. I tried making the SOME_LIMIT as big as 100, but to no avail. What would you suggest I do? What might I have forgotten or done wrong? – Invalid Memory May 29 '13 at 20:36
  • Ensure that the handler is called at all: Some NSLog statement, just to be on the safe side. The average value of `totalAcceleration` should be 1 (it's measured in _g_). So print out `accelerometerData.acceleration` and `totalAcceleration` to see the value. Tell me but it's night in Europe so probably we will see tomorrow :-) – Kay May 29 '13 at 21:20
  • Good morning :) And how do I do that? I'm only familiar with simple `NSLog(@"test")` kinds of thing (I tried `NSLog(@"%f", totalAcceleration)` but I don't think it's right and it complained about the "use of undeclared identifier"). Anyway, I tried putting `NSLog(@"test")` into the `//do something` (in codeplasma's code) bit (in codeplasma's code) but nothing showed up in the console however it did show up when i put it in places like `ViewDidLoad`. – Invalid Memory May 30 '13 at 07:29
  • s. updated answer: interval and NSLog, I marked the changes with `// UPDATE: ...` – Kay May 30 '13 at 08:41
  • I tried the code but still nothing. Any NSLogs I put after `[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *deviceMotion, NSError *error) {` doesn't come out. I've added the CoreMotion framework, is there something else I need to do apart from that? – Invalid Memory May 30 '13 at 09:42
  • Try the pull approach from the docs i.e. without a handler. If nothing goes, what about the provided samples like pARK. If this runs well on your device, compare it with your own code. – Kay May 30 '13 at 13:18
  • I fixed it! All I had to do was put the code in the `ViewDidLoad` section. Using `startDeviceMotionupdates` didn't work. Thanks very much for the help you gave me. Without the code you gave me I probably would have just given up. So it turns out I just had it in the wrong place... Doh! – Invalid Memory May 31 '13 at 17:31
  • Cool. As half of the code was provided by codeplasma, I am going to upvote his answer too :-) – Kay May 31 '13 at 17:39
  • Can anybody please tell me the value of SOME_LIMIT ? – Bandish Dave Feb 24 '21 at 05:54