jww
jww

Reputation: 102454

CCMotionManager, accelerometerData, gyroData, magnetometerData return nil

I'm working from Apple's Event Handling Guide for iOS. I'm using the pull method described near startAccelerometerUpdates. The pull method is fine for my purposes since I only need to read from the sensor once.

I'm getting a nil from the calls to accelerometerData, gyroData and magnetometerData in the code below on an iPhone 4, iPad Mini, and iPad Retina. isAccelerometerAvailable,isGyroAvailable, andisMagnetometerAvailableeach returnYES`. Two questions:

How does one determine the result from startAccelerometerUpdates and startGyroUpdates? They are void functions, so can I assume it can never fail? (Otherwise, it would throw or return a BOOL).

How does one get the last error from the call to accelerometerData, gyroData and magnetometerData?

EDIT: I had to call [NSThread sleepForTimeInterval:0.150f] to get data out of the sensor. Anything less and the sensor does not produce valid data.

The sleep adds a third question: how long does one need to pause to ensure the data arrives when the hardware is present?

Thanks in advance.

static CryptoPP::RandomPool pool;
static dispatch_once_t once = 0;

dispatch_once(&once, ^{
    CryptoPP::SecByteBlock seed(32);
    CryptoPP::OS_GenerateRandomBlock(true, seed.data(), seed.size());
    pool.IncorporateEntropy(seed.data(), seed.size());
});

// First, send in all the uninitialized data. Then:
//  sesnors[0,1,2] use accelerometer, if available
//  sesnors[3,4,5] use gyroscope, if available
//  sesnors[6,7,8] use magnetometer, if available
CryptoPP::SecBlock<double> sensors(3 * 3);
pool.IncorporateEntropy(sensors.BytePtr(), sensors.SizeInBytes());

CMMotionManager* mgr = [[CMMotionManager alloc] init];
if(mgr) {

    [mgr startAccelerometerUpdates];
    [mgr startGyroUpdates];
    [mgr startMagnetometerUpdates];

    [NSThread sleepForTimeInterval:0.150f];

    if([mgr isAccelerometerAvailable]) {
        CMAccelerometerData* accelData = [mgr accelerometerData];
        if(accelData) {
            sensors[0] = [accelData acceleration].x;
            sensors[1] = [accelData acceleration].y;
            sensors[2] = [accelData acceleration].z;
        }
    }

    if([mgr isGyroAvailable]) {
        CMGyroData* gyroData = [mgr gyroData];
        if(gyroData) {
            sensors[3] = [gyroData rotationRate].x;
            sensors[4] = [gyroData rotationRate].y;
            sensors[5] = [gyroData rotationRate].z;
        }
    }

    if([mgr isMagnetometerAvailable]) {
        CMMagnetometerData* magnetData = [mgr magnetometerData];
        if(magnetData) {
            sensors[6] = [magnetData magneticField].x;
            sensors[7] = [magnetData magneticField].y;
            sensors[8] = [magnetData magneticField].z;
        }
    }

    pool.IncorporateEntropy(sensors.BytePtr(), sensors.SizeInBytes());

    [mgr stopMagnetometerUpdates];
    [mgr stopGyroUpdates];
    [mgr stopAccelerometerUpdates];
    [mgr release], mgr = nil;
}

Upvotes: 0

Views: 680

Answers (1)

Abhi Beckert
Abhi Beckert

Reputation: 33389

When you do this:

mgr startAccelerometerUpdates];
[mgr setAccelerometerUpdateInterval:0.01f];

That means you will not start receiving information until 0.01 seconds later, and even then you'll only receive updates if the main thread is idle.

You are not waiting 0.01 seconds before checking for data, you're actually waiting about 0.00000000001 seconds. And I'm not sure, but I think your main thread might need to be idle even if you do wait longer.

I think you should go back to the documentation and have a look how you're supposed to read information from the sensors.

I recommend using blocks to process the motion data:

[mManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
    // process data
}];

You could tell the accelerometer to stop immediately after receiving the first value, if you only want it once.

Upvotes: 1

Related Questions