Code for Devantech CMPS03 compass

I recently acquired a Devantech CMPS03 magnetic compass module. I was unable to find any examples of Arduino code for it using its PWM output, but I was able to write a simple program for testing it. I tried using both the PulseIn() function and interrupts, and found that using interrupts yielded the best results. Having ofter been the beneficiary of using code made available by others, I am pleased to offer this code for anyone to use freely, should they find it useful. Here is the code:

// Tests CMPS03 compass module using interrupts
// Gives more repeatable readings than using PulseIn()
//
// For excellent discussion of methods for using interrupts,
// See:  http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
//       http://rcarduino.blogspot.co.uk/2012/04/how-to-read-multiple-rc-channels-draft.html
//       http://rcarduino.blogspot.com/
//       http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

// On the CMPS03:
// Connect Pin 1 to +5v
// Connect Pin 9 to gnd
// Connect Pin 4 to Pin 3 (Interrupt 1) on the Arduino
// I left CMPS03 pins 2 and 3 open despite statement in the data sheet that they should be clamped high

#define PWM_PIN 3  // Pin 3 on the Arduino digital i/o pins
#define COMPASS_FLAG 1  // This will be used to tell if there is a new interrupt
#define RC_FLAG 2

// shared variables are updated by the ISR and read by loop().
// In loop() we immediately take local copies so that the ISR can keep ownership of the
// shared ones. To access these in loop we first turn interrupts off with noInterrupts()
// we take a copy to use in loop and then turn interrupts back on as quickly as possible
// This ensures that we are able to receive new signals
volatile uint8_t bUpdateFlagsShared; // holds the update flags defined above
volatile uint16_t unCompassInShared;
volatile uint16_t unRCInShared;

// These are used to record the rising edge of a pulse in the ISR functions
// They do not need to be volatile as they are only used in the ISRs. If we wanted
// to refer to these in loop and the ISR then they would need to be declared volatile
uint32_t ulCompassStart;

void setup(){
  Serial.begin(57600);     // Enable Serial for testing
  pinMode(PWM_PIN, INPUT);
  attachInterrupt(1,ISRforCompass,HIGH);  // Use Arduino Hardware Interrupt 1, pin 3
}

void loop(){
  float heading = 0;
  static uint16_t unCompassIn;
  static uint8_t bUpdateFlags;  // local copy of update flags
  if(bUpdateFlagsShared){  // Has there been a new interrupt?
    noInterrupts(); // Yes, turn interrupts off while we make copies of the shared variables
    bUpdateFlags = bUpdateFlagsShared;  // Copy the flags
    if(bUpdateFlags & COMPASS_FLAG)unCompassIn = unCompassInShared;  // Get throttle input
    bUpdateFlagsShared = 0;  // Reset the shared flag
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    heading = ((float)unCompassIn)/100;
    if(heading > 360) heading = heading - 360;
    Serial.print("Compass heading: "); Serial.println((float)heading, 1);
    bUpdateFlags = 0;
  }
  Serial.println("looping");
  delay(1000);  // So we can look at the output
}

// Interrupt-service routine (ISR)
void ISRforCompass(){
  // if the pin is high, its a rising edge of the signal pulse, so lets record its value
  if(digitalRead(PWM_PIN) == HIGH){  // Start of servo command
    ulCompassStart = micros();
  }
  else{  // End of the pulse
    unCompassInShared = (uint16_t)(micros() - ulCompassStart);  // Get the elapsed time (us)
    bUpdateFlagsShared |= COMPASS_FLAG;  // Update flag and indicate new Throttle input
  }
}

Hope this helps someone.

Ted