My last post was about the usage of a digital compass. The topic of that post was on the usage of the compass together with an Arduino to obtain the values of the components of the magnetic field vector measured by the sensor.
The values obtained with the technique illustrated there are, in fact, just a digitalisation of the "real" values. In other words, what you read with Arduino is just an 11-bits binary number whose value is proportional to the magnetic field sensed in each direction. The sensor used on the PmodCMPS digital compass can measure fields up to ± 8 G (Gauss, a unit of measure for magnetic fields). According to the datasheet the value provided on each channel is a 16-bit number, whose 5 most significant bits are used as the sign: the reason for using 5 bits and not just 1 is that the ADC on the sensor has only 12-bits. With 11 bits we can get numbers up to 211=2048 (in fact from 0 to 2047). That means that a reading of +2048 corresponds to +8 G, at maximum gain, while reading of -2048 corresponds to -8 G. Playing with the sensor you can see that sometimes you also get +4096 or -4096. That is an artefact of the ADC that, if saturates, return zero and the reading appears as a 12-bit integer number.
We are then ready to make a map of the magnetic field provided by a small magnet. The setup can be made as follows: just use a piece of scotch to firmly attach the sensor on a piece of graph paper, as seen in the picture below.
With the sensor in this position, the X-axis is perpendicular to the paper, the Y-axis points toward left and the Z-axis points to the top of the figure.
The readings made on each channel must be converted to the proper units (G) before being used and to do that we need to know the calibration constants, i.e. the numbers C such that the value V of the field in a given direction can be obtained as C*R where R is the reading. Those constants depend on the gain of the device that can be adjusted using the sensor's configuration register. The default value of the gain is such that the maximum value of the reading (2048) is attained when the device senses a magnetic field of 1.3 G (see pag. 13 of the datasheet).
In order to get the value of the magnetic field in a given direction returning R as value, we then must multiply R by 1.3/2048.
Our purpose is to measure the magnetic field of the earth at the beginning of the run, keeping the magnets far from the sensor. Then, we move the magnet for which we want to map the field closer to the sensor and write down the values read for each position w.r.t. the sensor. From each value we subtract the corresponding value when the magnet is far from the device, to remove the contribution of the earth's magnetic field. Let's then look at the following sketch.
void loop() { if (!(avgDone)) { for (int i = 0; i < 100; i++) { for (int j = 0; j < 3; j++) { b[j] += B(j); } } for (int j = 0; j < 3; j++) { b[j] /= 100.; Serial.print("B["); Serial.print(j); Serial.print("] = "); Serial.println(b[j]); } Serial.println("===================================="); avgDone = true; } double Bx[3]; for (int i = 0; i < 3; i++) { Bx[i] = B(i) - b[i]; } for (int j = 0; j < 3; j++) { Serial.print("B["); Serial.print(j); Serial.print("] = "); Serial.println(Bx[j]); } delay(5000); }
avgDone is a boolean variable set as false in the setup() method, such that, as soon as the system is powered on, it takes 100 measurements of the three components of the field by means of the B(i)method that returns the i-th component of the field. To take the average, we store the sum of the readings in a three components array b[]. At the end of the loop the components of such an array are divided by 100. Each of them, then, contains the reading corresponding to the average magnetic field of the earth.
The B(i)method is defined as
int B(int i) { int b[3]; Wire.beginTransmission(ADDRESS); Wire.write(0x03); //select register 3, X MSB register Wire.endTransmission(); //Read data from each axis, 2 registers per axis Wire.requestFrom(ADDRESS, 6); if(Wire.available()) { b[0] = Wire.read() * 256; b[0] += Wire.read(); b[2] = Wire.read() * 256; b[2] += Wire.read(); b[1] = Wire.read() * 256; b[1] += Wire.read(); } return b[i]; }
In fact it can be made more efficient: this way it always reads both the three components, then returns one of them. Since performance is not an issue we can keep the code as it is: keep it simple is always a good rule.
The average values measured by us were (-284, -543, -17). In order to check that these values are correct values we compute their values in G using calibration constants. Multiplying each of them by 1.3/2048 we obtain (-0.180, -0.345, -0.011). The magnitude of such a vector is given by the Pythagoras' theorem as the square root of the sum of the squares of the components, i.e. the square root of 0.1802+0.3452+0.0112= 0.151546 (we can just ignore the signs since the squares are always positive), whose root is 0.39 G, not far from the expected value of about 0.5 G (in fact the earth's magnetic field ranges from about 0.3 to 0.6 G, depending on the location).
Once the average has been taken and shown on the Serial Monitor, we start taking measurements of the magnetic field every 5 s, so we have the time to move the magnet and write down the values.
The picture shows how we proceeded: the metal ring you can see close to the sensor is a small neodymium magnet. The position of the magnet w.r.t. sensor can be obtained from the graph paper. For each position we read the three values of the field as provided by the sensor and write them down on the paper in the position occupied by the magnet.
In this way we can obtain a complete map of the field, measuring the values at different positions. Remember that the values provided by our sketch are now the readings in excess w.r.t. the earth's magnetic field, so we can get the magnetic field of the magnet directly from them
The picture on the right shows such a map. We reported the values read for the earth's magnetic field and the reference frame (that in fact contains a mistake: the axis pointing up is in fact the z axis).
Taking the values on the top right (57, 1, -19) we can see that the magnetic field provided by that small magnet has almost no component along y, so it points below and toward the reader.
The real values of the magnetic field are (0.036, 0.000, -0.012) G (just multiply the readings by 1.3/2048). The magnitude of the vector is then 0.038 G.
Moving the magnet closer its magnetic field increases. For example, when the magnet is in the bottom left position the readings are (833, 703, -1254) corresponding to (0.529, 0.446, 0.796) G, and the strength of the field is 1.05 G.
This kind of magnets provides a field that decreases strongly with the distance, as you can see from the values measured by our sensor.
Using a similar technique you can measure the field generated by any magnet, either by moving the magnet or the sensor in space. You can then compute a complete map of the field or of the field strength.