Basics of Design CC Blog Research & Design Hub

3-Axis Digital Compass IC Modules

Written by Jeff Bachiochi

Making Sense from Confusion

Different manufacturers make 3-axis digital compass ICs with the same part numbers but different register sets and library incompatibility, or with different part numbers but the same I2C address. In this month’s column, Jeff clears up some of the confusion related to several different modules, so that you can use any of them in your projects.

  • How do 3-axis digital compass IC modules work?
  • What are different 3-axis digital compass IC module devices
  • How can I use a 3-axis digital compass IC module in a project?

  • 3-axis digital compass IC module 

3-axis digital compass ICs are multi-chip, surface-mount modules that measure a magnetic field in three perpendicular axes. They are used for low-field magnetic sensing in many personal navigation devices, including applications such as consumer electronics, mobile phones, and motor vehicle navigation systems.

I became interested in these compass devices when many of my robots lost traction in one wheel, and even with optical encoders, they wouldn’t remain on the expected path. That’s when I began looking for other ways to handle direction control. With a compass I could make course corrections based on magnetic readings, no mater how much a wheel slipped.

The HMC5883 3-Axis Digital Compass IC from Honeywell has been around for about 20 years. Sadly, Honeywell announced its EOL (End of Line) 2021. A slightly improved part, the HMC5983 was its successor, but it also is now EOL by Honeywell. You’ll still find these all over the Internet. What’s a guy to do? These are so inexpensive and easy to use, why give up on them?

As it turns out, you don’t have to. You do have to be aware that even though there are others in the marketplace with similar names, they are not always identical to the Honeywell chips. Although they are based on magneto-resistivity, use the same footprint 3mm x 3mm (16-Pin LPCC/LGA), and speak I2C, their register sets are different. This means they require different libraries.

Figure 1Figure 2, and Figure 3 show the part markings for three manufacturers’ products: the original Honeywell HMC5883, Memsic’s MMC5983, and QST Corporation’s QMC5883P, respectively. With the datasheets in hand, it was clear that while similar, since each part has a different set of registers, you couldn’t use the same library for all of them. This has led many to think their part wasn’t functioning. So let’s look into this conundrum and try to make sense out of these modules, so you can use any of them in your projects.

FIGURE 1
My HMC5883L by Honeywell.
FIGURE 1
My HMC5883L by Honeywell.
My HMC5883L by Honeywell.

FIGURE 2
My MMC5983MA by Mensic Inc.
My HMC5883L by Honeywell.

FIGURE 2
My MMC5983MA by Mensic Inc.

FIGURE 3
My QMC5883L by QST Corp..
FIGURE 3
My QMC5883L by QST Corp..
IDENTIFY

All three modules use an I2C interface. An I2C interface comprises two I/O lines, a clock line (SCL), and a data line (SDA). I have set up an I2C interface using a 4-pin, 0.1” connector. This is my standard configuration for most I2C work.

For this project, I wired a ESP32 module to a Arduino prototyping board, along with a 4-pin connector to which I can connect an I2C device. You can see this in Figure 4. It has 3.3V wired to pin1, with ground on pin2, SCL on pin3 and SDA on pin4. I am using the ESP32’s default I/O pins for I2C, which is SDA on D21 and SCL on D22. With this setup, I can wire up each ‘5883 using a mating connector, and they will all get power and communications through the same interface. Only one of the devices is a pin-for-pin match, so each will get its own cable wired to match my standard connector.

FIGURE 4
I mounted an ESP32 to an Arduino prototyping board. The 4-pin connector is wired with +3.3V and ground plus the two standard I2C pins. All my test modules plug into this I2C bus.
FIGURE 4
I mounted an ESP32 to an Arduino prototyping board. The 4-pin connector is wired with +3.3V and ground plus the two standard I2C pins. All my test modules plug into this I2C bus.

Let’s use a scan program to look at all the possible 7-bit addresses, and find the active address that will allow us to communicate with each device. I2C uses two separate addresses to communicate; therefore, only 128 possible addresses can be used in the standard 8-bit addressing scheme. The LSB (least significant bit) determines the communication function, where 1 = a read command and 0 = a write command. The upper 7-bits contain the actual 7-bit address, which is shifted 1 bit to the left. So the 7-bit address of 0x36 (b’0110110’) becomes the address I2C 0x6C (b’01101100’). This allows the “new” LSB to become the read/write bit, 0x6C (b’01101100’) for writing to the device and 0x6D (b’01101101’) for reading from the device—two distinct I2C addresses.

All our devices are register driven; that is, they all have a number of registers that you can interact with all through the same I2C address. Since multiple devices with different I2C addresses can be daisy chained, all using the same SCL and SDA, they must be smart enough to know their own addresses and either listen up for an address match or shut up if the address doesn’t match.

The first byte of any transfer will be the I2C address. Matching devices interact with the transfer, and non-matching devices go idle for the complete transfer. Every byte received must be acknowledged by a ninth bit. If there is no device with a matching I2C address, no one acknowledges and bit 9 remains high. The matching device must acknowledge by pulling the SDA line low for bit 9. If the sender sees an acknowledge, it knows there is someone to talk to. This is how we can find active devices. Our scan routine tries to communicate with all possible 7-bit addresses. But first, we need to lay down a little ground work.

PREPARATION

Let’s get our application set up for some user integration, by defining a menu to display the choices a user has for interacting with the application, as shown in Listing 1.

//--------------------------------------// start initialize//--------------------------------------#include “Wire.h”#define useControlCodes 1	// 1=yes, 0=nobyte error;		// i2c errorbyte addressI2C;		// addressI2C int nDevices;		// count of found deviceschar charCount;		// user input character countbyte menuChoice = 0;		// menu choice selected 0=nonebyte myNumber;		// user input valuebyte myData[20];		// user input arraybyte myHex[4];		// HEX characters to convertbyte menuCount = 1;		// number of menu choiceschar *menuArray[] = {  “1 - Scan for device(s)”	};			// menu items//--------------------------------------// end initialize//--------------------------------------//--------------------------------------// start setup()//--------------------------------------void setup() {  Serial.begin(115200);	// start UART  Serial.println(“ftb404 Magneto-resistive Sensor”);
			// sign on message 1  Serial.println(“I2C Interogator”); // sign on message 2  Wire.begin();		// start I2C  displayMenu();		/ display our menu}//--------------------------------------// end setup()//--------------------------------------//--------------------------------------// start loop()//--------------------------------------void loop() {  getUserInput();		// get user input  processUserInput();		// process user input}//--------------------------------------// end loop()//--------------------------------------

LISTING 1 
Initialization code for our Arduino application. Note that only the wire (I2C) library will be used for this application. No libraries specific to a compass device are necessary. The serial output can contain control codes to optionally format the serial output suitable for a serial terminal program.

At this point, when the user types in a “1,” the getUserInput() routine passes this value as menuChoice to the processUserInput() routine. In this routine we can direct program flow based on the menu selection. Presently, there is one, scanForI2CHit(). Here, we go through all possible 7-bit address I2Cs, looking for a connected device to respond to its address. Two commands are used for this:

Wire.beginTransmission(address);

error = Wire.endTransmission();

The first command begins an I2C write to address. As mentioned earlier, if a device with that address is connected, it must acknowledge the address by pulling the data line low after receiving the 8-bit I2C address (for the nineth bit time period). The second command ends the transmission and returns any error. If the transmission was acknowledged, the error = 0 and we have found a device. Note that we didn’t actually ask for or provide any data here; we just looked for a someone to acknowledge that they heard their address.

Since we know what address we just used, we can display a message, increment nDevices and save the address as addressI2C. (I might want to keep an array of “found” devices, if I had multiple devices connected to this I2C bus.) Here, all recognized devices are displayed, but only the last one will be saved to addressI2C. With only one device connected at a time, I know which address goes with which device, instead of having to guess which is which.

By referring to the datasheets (see Circuit Cellar’s Article Materials and Resources webpage), I know what address to expect from each manufacturer. These are added to the application as constants.

const byte addressHMC = 0x1E;

const byte addressMMC = 0x30;

const byte addressQMCP = 0x58;

const byte addressQMCL = 0x0D;

When a device is found, the constants are searched for a match to our “found” addressI2C. If one is identified, we now know which device we are working with. This is critical, especially if you want to use a library. I’ve seen many a forum entry complaining that such and such a library doesn’t work. When you use a library, the actual addressI2C used is hidden by the library functions. You can investigate this by searching through the library files (.h or .c) to find the actual address used. If it does not match your device, it won’t work.

DATASHEETS

The datasheets discussed in this column can be downloaded from the Circuit Cellar Article Materials and Resources webpage.

The great thing about not using libraries is that you must obtain the datasheet to be able to make any sense of your device. For this project I had to collect a few datasheets. I don’t know why I assumed all these ‘5883’s datasheets would be the same. I created a function to display the register list from each datasheet (Listing 2). It will be used as a reminder to the users, when they wish to read or write to one of the device’s registers.

//--------------------------------------// start showAvailable() function//--------------------------------------void showAvailable(){switch(addressI2C)  {    case addressHMC:                    // HMC5x83        Serial.println(“0 - ConfigurationRegsterA  R/W”);        Serial.println(“1 - ConfigurationRegsterB  R/W”);        Serial.println(“2 - ModeRegster            R/W”);        Serial.println(“3 - X Regster (MSB)        R”);        Serial.println(“4 - X Regster (LSB)        R”);        Serial.println(“5 - Z Regster (MSB)        R”);       Serial.println(“6 - Z Regster (LSB)        R”);       Serial.println(“7 - Y Regster (MSB)        R”);        Serial.println(“8 - Y Regster (LSB)        R”);        Serial.println(“9 - StatusRegsterA         R”);        Serial.println(“A - IdentificationRegsterA R”);        Serial.println(“B - IdentificationRegsterB R”);        Serial.println(“C - IdentificationRegsterC R”);      break;      case addressMMC:                    // MMC5x83      Serial.println(“0 - X Regster (LSB)        R”);        Serial.println(“1 - X Regster (MSB)        R”);        Serial.println(“2 - Y Regster (LSB)        R”);       Serial.println(“3 - Y Regster (MSB)        R”);       Serial.println(“4 - Z Regster (LSB)        R”);        Serial.println(“5 - Z Regster (MSB)        R”);        Serial.println(“6 - Temperature            R”);         Serial.println(“7 - Status               W/R”);        Serial.println(“7 - Control 0            W/R”);        Serial.println(“8 - Control 1            W/R”);        Serial.println(“9 - Control 2            W/R”);        Serial.println(“A - Motion Detection X   W/R”);         Serial.println(“B - Motion Detection Y   W/R”);        Serial.println(“C - Motion Detection Z   W/R”);        Serial.println(“D - Product ID             R”);        break;      case addressQMCL:                   // QMC5883L      Serial.println(“0 - X Regster (LSB)        R”);        Serial.println(“1 - X Regster (MSB)        R”);        Serial.println(“2 - Z Regster (LSB)        R”);       Serial.println(“3 - Z Regster (MSB)        R”);       Serial.println(“4 - Y Regster (LSB)        R”);        Serial.println(“5 - Y Regster (MSB)        R”);        Serial.println(“6 - Status                 R”);         Serial.println(“7 - Temperature (LSB)      R”);        Serial.println(“8 - Temperature (MSB)      R”);        Serial.println(“9 - Control 1            W/R”);        Serial.println(“A - Control 2            W/R”);         Serial.println(“B - Set/Reset            W/R”);        Serial.println(“C - reserved               R”);        Serial.println(“D - Chip ID                R”);         break;        case addressQMCP:                   // QMC5883P      Serial.println(“0 - Chip ID                R”);             Serial.println(“1 - X Regster (LSB)        R”);        Serial.println(“2 - X Regster (MSB)        R”);        Serial.println(“3 - Z Regster (LSB)        R”);       Serial.println(“4 - Z Regster (MSB)        R”);       Serial.println(“5 - Y Regster (LSB)        R”);        Serial.println(“6 - Y Regster (MSB)        R”);        Serial.println(“7 - Status                 R”);         Serial.println(“8 - Control 1              R”);        Serial.println(“9 - Control 2              R”);        Serial.println(“A - Mode                 W/R”);        Serial.println(“B - Set/Reset            W/R”);         break;      default:      Serial.println(“?”);         break;     }}//--------------------------------------// end showAvailable() function//--------------------------------------

LISTING 2 
Register List Code. This function is called when a user wants to read or write to a device. Based on the address I2C, one of these will be displayed. Note that the STATUS register is different for each device. So even with the correct addressI2C, you still need to know where in the device to look for a function. 

Compass Specifications: While we are talking about datasheets, I should go over a few of the device specifications that may be of interest (Table 1). Each device has similar characteristics, but some may have slightly faster conversions, better linearity, or higher resolution.

TABLE 1 
Comparison of four characteristics pulled from the datasheets of four different modules. All devices are 3.3V and consume <1mA of current. If exposed to high G (5,000G max), each device has degaussing circuitry that can remove the magnetic offset to the magneto-sensors. %FS stands for "percent of Full Scale."
TABLE 1
Comparison of four characteristics pulled from the datasheets of four different modules. All devices are 3.3V and consume <1mA of current. If exposed to high G (5,000G max), each device has degaussing circuitry that can remove the magnetic offset to the magneto-sensors. %FS stands for “percent of Full Scale.”

As I mentioned in the introduction to this month’s column, I became interested in 3-axis digital compasses when many of my robots wouldn’t stay on their expected path, because they had lost traction in one wheel, and even optical encoders didn’t help. The best PID can’t make up for slippage! That’s when I began exploring other ways to handle direction control, and found that a compass allowed me to make course corrections based on magnetic readings, no matter how much a wheel slipped.

Having taught scouts how to use a map and compass, it was easy to show how a compass needle could be skewed by the close proximity of anything made of iron. You must be aware of your surroundings. Once I found a compass along a trail while hiking. Somehow the needle had become magnetized with the opposite polarity. It always pointed south instead of north. I kept it to demonstrate the need for some common sense.

Electronic compasses could also be tricked by vast amounts of local iron. In most cases you won’t run into this issue. However, each of these devices, has a built-in set/reset circuit. Its purpose is to remove any residual magnetic polarization resulting from exposure of the magneto-resistive element to strong external magnetic sources. A change in temperature can also affect the elements, but can be nullified with the same circuitry.

Storage and Conversion Factors: Each of the three magneto-resistive sensors (one for each axis—X, Y, and Z) is made of a nickel-iron (Permalloy) thin-film and patterned as resistive strip elements in a bridge configuration. Aligned to a common sensitive axis, they provide a positive voltage change with magnetic fields increasing in the sensitive direction. Individual ADCs measure the magnetic field strength of each axis. These conversions are available as three word values in six consecutive byte registers.

The HMC compass stores conversions as a 12-bit bipolar number, where the bit’s value—in gauss (G) units—is dependent on its Configuration Register B’s Gain value. To convert the value to G based on Gain:

Gain – divide by

000 – 1370

001 – 1190

010 – 820

011– 660

100 – 440

101 – 390

110 – 330

111 – 230

The MMC compass stores conversions as a 16-bit unsigned number. To convert the value to G based, divide it by 4,096.

The QMC-L compass stores conversions as 16-bit bipolar number, where the bit’s value (in gauss) is dependent on the Range value of its Control Register 1. To convert the value to G based on Range:

Range – divide by

00 – 12000

— ADVERTISMENT—

Advertise Here

01 – 3000

The QMC-P compass stores conversions as 16-bit bipolar number, where the bit’s value (in gauss) is dependent on the Range value of its Control Register 2. To convert the value to G based on Range:

Range – divide by

00 – 1000

01 – 2500

10 – 3750

11 – 15000

Using these conversion factors, we can get a magnetic strength value for each of the three axes. So let’s add four more menu items to allow a user to do register reads and writes plus a multi-register grab of the X, Y, and Z axes.

MENU PLEASE

We’ve increased our Menu selections from 1 to 5. In Figure 5, I’ve chosen Item 1, and you can see that a device was found at address 0x0D. That happens to correspond to the QMC5883L device, which is plugged into my bus connector. The last address found is automatically assigned to addressI2C. However, you could use Menu Item 2 to assign any address to addressI2C. This will be used for Menu Items 3, 4, and 5. Let’s look at Item 5, because it uses the read and write register routines to request all three axis words as 6 registers.

FIGURE 5 
The RealTerm Serial Terminal Program is displaying the serial output from this application. When the user chooses Item "1, the application scans all available 7-bit I2C addresses looking for some device to acknowledge its address. The address is compared to a list of known devices. A match is displayed here for I2C address 0x0D as a QST QMC5883L.
FIGURE 5
The RealTerm Serial Terminal Program is displaying the serial output from this application. When the user chooses Item “1, the application scans all available 7-bit I2C addresses looking for some device to acknowledge its address. The address is compared to a list of known devices. A match is displayed here for I2C address 0x0D as a QST QMC5883L.

All compass modules can do both single conversions (to reduce current) and continuous conversions (which require less communications). You might be surprised to find that the defaults range from “disabled” to “enabled” in single-conversion mode, or “enabled” in continuous mode. If we look at the HMC5883, we find that it defaults to a single conversion, and once the three bridges have been sampled and conversion is complete, it sets the STATUS register RDY bit (bit0). The external ready output can be monitored, if available, or the STATUS register can be read.

If Menu Item 5 – “read X, Y, Z” is chosen, function readXYZ() is called. While each device follows a similar flow, each of these requires special code, so, based on part identification (addressI2C), different functions are called. In this case addressI2C = addressHMC, so we branch to readHMC(). We’re using the default condition, single conversion mode, so I will begin a new conversion by clearing bit1 and setting bit0 (writing 0x01) to the MODE register (0x02). Wait for the conversion to complete by checking the STATUS register for bit0 to be set, before reading all six Data output registers. These registers can be read at once, since the device automatically increments the register pointer after each read or write. Set the pointer to the first Data output register 0x03, by writing the 0x03 to addressI2C. Remember—a single write sets the pointer, and additional writes in the same transmission go into the registers pointed to.

We now have the six Data output registers in array myData[]. Pay attention to how the six registers are configured. For this device the registers are grouped as three pairs, X first, Z second, Y third. In addition, each pair is MSB first and LSB second.

countX = myData[0] * 256 + myData[1]; // X ADC counts

countZ = myData[2] * 256 + myData[3]; // Z ADC counts

countY = myData[4] * 256 + myData[5]; // Y ADC counts

What does “countX” mean here? It is 16-bits in a two’s complement format (it has polarity). There are 11-significant bits, 0x0000-07FF for positive values and 0xFFFF-0xF800 for negative values. If we divide countX by the full scale value 0x7FF or 2,047 we get a number for a decimal number between 0 and 1 that represents gauss units (G). Gauss refers to the intensity of a magnetic field (magnetic flux density). The device has a number of amplifier Gains that can be applied, based on the ms3bits of Configuration register B. These vary from 0.88 – 8.1. The default is 1.3. This means that the converted values are actually based on a little above 1G (1.3 gauss), so we need to reduce the conversion by 1/1.3.

valueX = countX / (2047 * 1.3); // X G

valueY = countY / (2047 * 1.3); // Y G

valueZ = countZ / (2047 * 1.3); // Z G

Since the gauss values are quite small fractions, we can convert to microTesla (µT) by multiplying G by 100. You can expect these measurements of the Earth’s magnetic field to be up to 65µT. Here we convert it just for the sake of viewing; its the relationship between measurements that we are really interested in.

To find the actual heading we need to use the function arc tangent on the X and Y axis values. This will give us the heading or arc in radians. A radian is the angle of an arc that is equal in the length to the radius. Since 180 degrees is equal to π x radians, that makes 360 degrees = 2 x π x radians. If we divide 360 degrees by 2 x π, we get 57.296 degrees/radian.

— ADVERTISMENT—

Advertise Here

float heading = atan2(valueY, valueX);// radians

float headingDegrees = heading * 180/PI; // degrees

FIGURE 6 
This map shows the movement of the geomagnetic pole since it was first investigated in 1650 [1].
FIGURE 6
This map shows the movement of the geomagnetic pole since it was first investigated in 1650 [1].

Now we have a heading to the north magnetic pole! Maps are drawn in relation to the North Pole. The North Pole and the north magnetic pole are not the same! Even worse, the magnetic or geomagnetic pole keeps moving [1] (Figure 6). Depending on where you are on Earth, there is an angle between the geomagnetic pole and the North Pole that you must take into account. For Connecticut, the angle is about -13 degrees or 13 degrees to the left of the North Pole. We’ll need to adjust to the right to point to true north. This is called the “declination angle,” and can be found on any good geologic map. Sometimes its shown pictorially, as in Figure 7.

FIGURE 7 
This portion of the Geological Survey map for Ellington CT shows the relationship between True North (TN, the star), magnetic north (MN), and grid north (GN). Our compass points to MN and must be adjusted for true north by offsetting the MN reading with the declination constant. For this map it's 14.5 degrees left of TN.
FIGURE 7
This portion of the Geological Survey map for Ellington CT shows the relationship between True North (TN, the star), magnetic north (MN), and grid north (GN). Our compass points to MN and must be adjusted for true north by offsetting the MN reading with the declination constant. For this map it’s 14.5 degrees left of TN.

headingDegrees = headingDegrees – declinationConstant;

if(headingDegrees <0) headingDegrees = headingDegrees + 360;

if(headingDegrees >359) headingDegrees = headingDegrees – 360;

Once the declination constant has been adjusted, there might be an overflow if the heading is close to 0 degrees. When negative, it could force the heading minus, so we add 360 degrees. If it is positive it could force the heading >360, so subtract 360 degrees.

I won’t go into how the other devices are handled. You can follow the rest of the routines for each device in this application. Be advised to have each datasheet on hand, so you can refer to its specifications and register usage. Datasheets for the compass modules discussed in this article can be downloaded from the Circuit Cellar Article Materials and Resources webpage.

ROBOT DIRECTION CONTROL?

As I said earlier in this column, my interest in 3-axis digital compass IC modules resulted from problems with the movements of robots with wheels. The biggest problem I’ve found with getting any robot to go straight is that each motor, when given the same voltage (PWM), will move at different speeds. This means the bot will veer off to the left or right. Adding optical encoders to keep track of wheel position can keep them in sync after the initial stiction, where each motor (and gear train) overcomes its internal friction. This may already have caused a pivot as one motor begins running first. While a sync of encoders can turn a robot back on course, it may already have chosen a new path.

When a bot turns, the turning radius is based on the wheel-to-wheel distance. For wide tires, this isn’t necessarily the tire’s center. That point could vary as the tire rotates, due to uneven treads or wheel wobble. If your bot has tank treads, it’s almost impossible to predict, because of the skid steer approach.

Now, if your bot is driving on an uneven or loose surface, there will be additional terrain issues for which optical encoders just can’t compensate. Enter the compass. Let’s say you want to turn 90 degrees. You can take a compass heading before you start turning, say 55 degrees. You add 90 to turn right and subtract 90 to turn left. Now you turn on the motors to turn left or right, and keep turning while you check the heading for the calculated value, say 55 + 90 = 145 degrees (left). The same goes for moving forward. Adjust the left and right motors based on the heading of the compass. The difference is the error and that can be used in a PID motor controller, in place of the optical encoder error to correct a heading.

GOTCHAS

As discussed earlier, we know that the magneto-resistive sensors can be “fooled” by large external magnetic fields or iron objects. Fortunately, we’re dealing with relative headings, and not necessarily true headings. While this could be a problem when using a compass to maneuver through a steel mill or around power station, it shouldn’t be a large problem around your house or yard. A bigger problem for our compass is the fact that once the module is no longer level, headings will be way off. Nautical compasses eliminate this by mounting the compass on a gimbal, as shown in Figure 8. A gimbal suspends the compass, so that it remains level when its support is tipped.

FIGURE 8 
On a boat (or a car), compasses can be gimbaled or suspended on X and Y-axis pivots to remain level independent of the vehicle's pitch or roll, thanks to gravity. They are, however, subject to acceleration changes.
FIGURE 8
On a boat (or a car), compasses can be gimbaled or suspended on X and Y-axis pivots to remain level independent of the vehicle’s pitch or roll, thanks to gravity. They are, however, subject to acceleration changes.

The gimbal’s function can be done in software, when you add an accelerometer. The accelerometer can be used to determine how far off level the module is, and then adjusts the X and Y magnetic values before using the atan2 function. For more information, you might read my article “Location Notification” (Circuit Cellar 227, June 2009) [2], or check out either of two other articles listed on Circuit Cellar’s Article Materials and Resources webpage [3] [4].

‘NUFF SAID

I think this phrase was first used by Stan Lee, who I’m sure you all know as a Marvel Universe superstar. Since his passing in 2018, we can no longer see a live cameo in every Marvel movie (can you say “AI”?). There is so much more I could discuss about this project, but what I really wanted to do was to clear up the confusion I’ve found over magneto-resistive ICs marketed as “5883” and “5983.”

Honeywell, “Why would you make different part numbers with the same I2C address and the same data in all three identification registers?” Memsic Inc. and QST Corporation, “Why would you use the “5883/5983” numbering on a competing product that is not library compatible?” You’ve made it difficult for a user familiar with the HMC part to substitute it. I guess we must be running out of part numbers!

— ADVERTISMENT—

Advertise Here

There may be other devices around using equally confusing part IDs, but I’ve not seen another like this. I hope you can wrap your head around this debacle. You may live by the adage that “real men don’t ask for directions,” but remember this, “real engineers read datasheets.” Too much to do, so little time. 

REFERENCES
[1] Shift in magnetic north pole (yearly position, 1590-2020). https://sascommunities.github.io/graphics-programming/robert/magnetic_north_pole.htm
[2] Jeff Bachiochi, “Location Notification: A Look at Anisotropic Magnetoresistance Sensors.” Circuit Cellar 227, June 2009.
[3] Tronics Bench, “3 Axis Magnetometer Tilt Compensation.”
https://www.best-microcontroller-projects.com/magnetometer-tilt-compensation.html
[4] Jim Remington, “Correcting the Balboa Magnetometer.”
https://forum.pololu.com/t/correcting-the-balboa-magnetometer/14315Captions

Code and Supporting Files

PUBLISHED IN CIRCUIT CELLAR MAGAZINE • MARCH 2024 #404 – Get a PDF of the issue

Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.


Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article
Website |  + posts

Jeff Bachiochi (pronounced BAH-key-AH-key) has been writing for Circuit Cellar since 1988. His background includes product design and manufacturing. You can reach him at: jeff.bachiochi@imaginethatnow.com or at: www.imaginethatnow.com.

Supporting Companies

Upcoming Events


Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2026 KCK Media Corp.

3-Axis Digital Compass IC Modules

by Jeff Bachiochi time to read: 19 min