Edward Hasted
Edward Hasted

Reputation: 3433

Swift CMMotionManager getting Roll, Pitch, Yaw to work

I am having difficulty getting the syntax right to return the roll, pitch, yaw in Swift correctly. The accelerometers all work fine but can't sort this one out. Here's the material code:

var roll: Double    = 0.0
var pitch: Double   = 0.0
var yaw: Double     = 0.0    
var manager         = CMMotionManager()

override func viewDidLoad() {
     manager.startDeviceMotionUpdatesToQueue(NSOperationQueue.currentQueue()!, withHandler: { (rpyData: CMDeviceMotionHandler?, NSError) -> Void in self.outputRPY(rpyData!.deviceMotion)
     if(NSError != nil) {
          print("\(NSError)")
     }
   })
}

func outputRPY(data: CMDeviceMotion){
    roll    = data.attitude.roll
    pitch   = data.attitude.pitch
    yaw     = data.attitude.yaw
    rollLabel.text  = String(format: "%.2f°", roll)
    pitchLabel.text = String(format: "%.2f°", pitch)
    rollLabel.text  = String(format: "%.2f°", yaw)
}

Upvotes: 1

Views: 2102

Answers (2)

Edward Hasted
Edward Hasted

Reputation: 3433

The thing that was throwing me is that the Roll, Pitch, Yaw appear to work differently from the accelerometers, gyros, magnetometers. Here's the fresh off the press code:

 var manager         = CMMotionManager()

 override func viewDidLoad() {   
    super.viewDidLoad()
    manager.deviceMotionUpdateInterval  = 0.2
    manager.startDeviceMotionUpdatesToQueue(NSOperationQueue.currentQueue()!, withHandler: {(motionData: CMDeviceMotion?, NSError) -> Void in self.outputRPY(motionData!)
        if (NSError != nil){
            print("\(NSError)")
        }
    })
 }

 func outputRPY(data: CMDeviceMotion){
    let rpyattitude = manager.deviceMotion!.attitude
    roll    = rpyattitude.roll * (180.0 / M_PI)
    pitch   = rpyattitude.pitch * (180.0 / M_PI)
    yaw     = rpyattitude.yaw * (180.0 / M_PI)     
    rollLabel.text  = String(format: "%.2f°", roll)
    pitchLabel.text = String(format: "%.2f°", pitch)
    yawLabel.text   = String(format: "%.2f°", yaw)
}

Upvotes: 1

Mrwerdo
Mrwerdo

Reputation: 388

The 2nd parameter type should be a NSError? (Aka an optional) and in the body of your closure, you are comparing a type to a value.

In all, it should look like this:

manager.startDeviceMotionUpdatesToQueue(NSOperationQueue.currentQueue()!, withHandler: { (deviceMotion: CMDeviceMotion?, err: NSError?) -> Void in 
    self.outputRPY(deviceMotion!)
    if(err != nil) {
        print("\(err)")
    }
}

You could also use optional binding to be more swifter... :)

if error = err {
    print("\(error)")
}

Edit:

After testing on my home machine, I realised that there was one more issue. CMDeviceMotionHandler declares the type of the closure (or function pointer, if you will). It is declared as:

public typealias CMDeviceMotionHandler = (CMDeviceMotion?, NSError?) -> Void

So your closure (or handler) should be like:

{ (deviceMotion: CMDeviceMotion?, error: NSError?) -> Void in
    // code goes here
}

Finally, your second parameter is making the identifier NSError refer to a variable, when it is already a type (which is legal). I personally wouldn't name it as such, since it can be easy confuse with the type NSError. Maybe you could perhaps name it nserror, which would avoid any confusion with the existing type.

The first code snippet should just copy-paste into your project.

Upvotes: 1

Related Questions