Electric Compass

From Mech
Jump to: navigation, search
The electric compass used on Team JAB's robot for Design Competition 2009.



Written by: James Yeung (Team JAB, DC2009)

Diagram of the pin outs.
Manufacturer Devantech (aka Robot Electronics)
Voltage 5v
Current 20mA Typ.
Resolution 0.1 Degree
Accuracy 3-4 Degrees approx (after calibration)
Output 1 Timing Pulse 1mS to 37mS in 0.1mS increments
Output 2 I2C Interface, 0-255 and 0-3599
SCL Speed up to 1MHz
Weight 0.03 oz.
Size 32mm x 35mm

This great little compass was designed by Robot Electronics specifically for use in robots to aid navigation. The compass uses the Philips KMZ51 magnetic field sensor, which is sensitive enough to detect the Earth's magentic field. The two sensors are mounted at right angles to each other; the CMPS03 module uses this information to compute the direction of the horizontal component of the Earth's magnetic field to within 0.1 degree with an accuracy of 3-4 degrees. The CMPS03 outputs a unique number to represent the direction the robot is facing.

There are two ways to read the heading from the compass, I2C interface or PWM. My team and I were unable to get the I2C interface to work reliably and ended up using PWM. However, both methods are documented here and with more time, one should be able to get I2C to work.

I2C Interface

Diagram of the I2C communication timing.

I2C communication protocol with the compass module is the same as popular eeprom's such as the 24C04.. First send a start bit, the module address (0XC0) with the read/write bit low, then the register number you wish to read. This is followed by a repeated start and the module address again with the read/write bit high (0XC1). You now read one or two bytes for 8bit or 16bit registers respectively. 16bit registers are read high byte first. The compass has a 16 byte array of registers, some of which double up as 16 bit registers as follows:

Register Function
0 Software Revision Number
1 Compass Bearing as a byte, i.e. 0-255 for a full circle
2, 3 Compass Bearing as a word, i.e. 0-3599 for a full circle, representing 0-359.9 degrees.
4, 5 Internal Test - Sensor1 difference signal - 16 bit signed word
6, 7 Internal Test - Sensor2 difference signal - 16 bit signed word
8, 9 Internal Test - Calibration value 1 - 16 bit signed word
10, 11 Internal Test - Calibration value 2 - 16 bit signed word
12 Unused - Read as Zero
13 Unused - Read as Zero
14 Unused - Read as Undefined
15 Calibrate Command - Write 255 to perform calibration step. See text.

Register 0 is the Software revision number (8 at the time of writing). Register 1 is the bearing converted to a 0-255 value. This may be easier for some applications than 0-360 which requires two bytes. For those who require better resolution registers 2 and 3 (high byte first) are a 16 bit unsigned integer in the range 0-3599. This represents 0-359.9°. Registers 4 to 11 are internal test registers and 12,13 are unused. Register 14 is undefined. Don't read them if you don't want them - you'll just waste your I2C bandwidth. Register 15 is used to calibrate the compass.

The I2C interface does not have any pull-up resistors on the board, these should be provided elsewhere, most probably with the bus master. They are required on both the SCL and SDA lines, but only once for the whole bus, not on each module. I suggest a value of 1k8 if you are going to be working up to 400KHz and 1k2 or even 1k if you are going up to 1MHz. The compass is designed to work at up to the standard clock speed (SCL) of 100KHz, however the clock speed can be raised to 1MHZ providing the following precaution is taken:

At speeds above around 160KHz the CPU cannot respond fast enough to read the I2C data. Therefore a small delay of 50uS should be inserted either side of writing the register address. No delays are required anywhere else in the sequence. By doing this, I have tested the compass module up to 1.3MHz SCL clock speed. There is an example driver here using the HITECH PICC compiler for the PIC16F877.

Pin 7 is an input pin selecting either 50Hz (low) or 60Hz (high) operation. I added this option after noticing a jitter of around 1.5° in the output. The cause was the 50Hz mains field in my workshop. By converting in synchronism with the mains frequency this was reduced to around 0.2° . An internal conversion is done every 40mS (50Hz) or every 33.3mS (60Hz). The pin has an on-board pull-up can be left unconnected for 60Hz operation. There is no synchronism between the PWM or I2C outputs and the conversion. They both retrieve the most recent internal reading, which is continuously converted, whether it is used or not.

Pin 6 is used to calibrate the compass. The calibrate input (pin 6) has an on-board pull-up resistor and can be left unconnected after calibration.

Pins 5 and 8 are No Connect. Actually pin 8 is the processor reset line and has an on-board pull-up resistor. It is there so that we can program the processor chip after placement on the PCB.

Code for I2C

// This code will read register 1 from the compass to get a 0-255 resolution of the direction.
#include <18f4520.h>
#use delay(clock=20000000)                         // 20 MHz crystal on PCB
#use I2C(MASTER, SLOW, SCL=PIN_C3, SDA=PIN_C4)     // using hardware I2C, built into the PIC, make sure to include this line in any master I2C program

void main() {
      i2c_start();                                 // send start bit
      i2c_write(0xC0);                             // address of device (write bit LOW)
      i2c_write(0x01);                             // sending register number that we want to read from
      i2c_start();                                 // send start bit again
      i2c_write(0xC1);                             // address of device (write bit HIGH)
      data = i2c_read();                           // read from compass
      i2c_stop();                                  // send stop bit


Picture of the PWM signal from the electric compass.

The PWM signal is a pulse width modulated signal with the positive width of the pulse representing the angle. The pulse width varies from 1mS (0° ) to 36.99mS (359.9° ) – in other words 100uS/° with a +1mS offset. The signal goes low for 65mS between pulses, so the cycle time is 65mS + the pulse width - ie. 66ms-102ms. The pulse is generated by a 16 bit timer in the processor giving a 1uS resolution, however I would not recommend measuring this to anything better than 0.1° (10uS). Make sure you connect the I2C pins, SCL and SDA (pin 2 and 3), to the 5v supply via a couple of resistors if you are using the PWM as there are no pull-up resistors on these pins. Around 47k is ok, the values are not at all critical.

Code for PWM

#include <18f4520.h>
#DEVICE ADC=8                              // set ADC to 10 bit accuracy, or it could be just 8
#use delay(clock=20000000)                 // 20 MHz crystal on PCB

int compass;
int PWM_count=0;

void Timer2isr() {
      PWM_count++;                         // keep counting if PWM is high
      if(PWM_count != 0){                  // if PWM is low, store PWM_count to compass
         compass = PWM_count-10;
         PWM_count = 0;

void main() {

   setup_timer_2(T2_DIV_BY_4, 61, 2);      // 16KHz clock, interrupt every 4*50nS * 4 * (61+1) * 2 = 0.1mS
                                           // third argument means the clock is running 2x faster than the interrupt routine, if any

   while(TRUE) {


Picture of the wiring for calibration.

The easiest way to calibrate the compass is to wire it up with a pull down switch on pin 6. To calibrate the compass you only have to take the calibrate pin low and then high again for each of the four major compass points North, East, South, West. A simple push switch wired from pin 6 to ground is okay for this. The compass points can be set in any order, but all four points must be calibrated. I suggest you go in this order:
1. Set the compass module flat, pointing North. Press and release the button.
2. Set the compass module flat, pointing East. Press and release the button.
3. Set the compass module flat, pointing South. Press and release the button.
4. Set the compass module flat, pointing West. Press and release the button.

Other Notes

When mounting the compass on your robot, or any other project, try and keep it at least 5" away from any other magnetic, electronic or metallic parts, especially motors. We built a small "tower" for the compass, which was made from plastic.


Most of the information was either from our own experience or from the user manual, which can be found here.

Personal tools