Dans le produit final, l'écran LCD affichera une flèche qui pointera obstinément vers le nord.
Le champ magnétique terrestre
Notre planète est un gigantesque aimant: elle produit un champ magnétique. Ce champ magnétique comporte une composante verticale (orientée vers le haut si vous vous trouvez dans l'hémisphère sud, et vers le bas si vous êtes dans l'hémisphère nord) et une composante horizontale orientée vers le nord magnétique.
Pour l'utilisation comme boussole, il s'agit de placer le capteur à l'horizontale (bien à plat sur une table, par exemple). L'orientation horizontale du champ magnétique (donc l'orientation de la résultante des composantes x et y) représente l'angle que fait l'axe x du capteur par rapport au nord.
Branchement du capteur HMC5883L à l'Arduino Uno
Commençons par brancher le module HCM5883L à l'Arduino. Comme nous le verrons, il sera utile de tester et de calibrer ce capteur, et ce n'est qu'ensuite que j'ajouterai l'afficheur LCD.
Mon module HMC5833 comporte un régulateur de tension et des résistances de tirage, il se branche donc à l'Arduino de la façon suivante:
VCC du module HMC5883 ----- 5V de l'Arduino
GND du module HMC5883 ---- GND de l'Arduino
SCL du module HMC5883 ---- A5 de l'Arduino Uno
SDA du module HMC5883 ----- A4 de l'Arduino Uno
RDY du module HMC5883 ---- pas connectée
Si vous utilisez plutôt une carte STM32, ESP32 ou ESP8266, cet article indique comment y brancher le module HMC5833.
Calibration nécessaire!
Faisons un petit test pour vérifier la fiabilité des mesures prises par le capteur: on place le capteur à plat sur une table: on le fait lentement tourner sur lui-même sur au moins un tour complet tout en mesurant les 3 composantes du champ magnétique. J'ai utilisé le sketch ci-dessous (basé sur un sketch publié par l'équipe de Sparkfun) pour enregistrer les 3 composantes du champ magnétique, que j'ai ensuite copiées dans un tableur.
Si les mesures sont correctes, le graphique de la composante y en fonction de la composante x devrait former un cercle centré autour de la valeur 0. Quant à la composante z, elle devrait demeurer constante.
Mais voici ce que j'ai obtenu:
Ça ne va pas du tout! Les données forment une ellipse plutôt qu'un cercle, ce qui signifie qu'un même champ magnétique ne génère pas la même valeur numérique selon qu'il est mesuré par le capteur x ou par le capteur y. De plus, les mesures du capteur x ne sont pas réparties de façon symétrique autour de 0, ce qui signifie qu'un même champ magnétique ne génère pas la même mesure (en valeur absolue) selon qu'il est orienté dans le sens positif ou dans le sens négatif de l'axe.
Dans ce cas précis, x a varié entre -561 et -63, alors que y a varié entre -263 et +242. Quant à la composante z, qu'on ne voit pas sur le graphique, elle est demeurée presque constante, tel que prévu, fluctuant entre -613 et -587.
Pour faciliter la prise des mesures utiles à la calibration, j'ai légèrement modifié le sketch afin qu'il affiche la valeur minimale et la valeur maximale de x et y pendant une rotation complète du module:
En faisant à nouveau tourner le capteur à plat sur une table, ce deuxième sketch a affiché les informations suivantes:
xmin: -564 xmax: -62 ymin: -277 ymax: 254 zmin: -616 zmax: -606
(ce qui confirme approximativement les valeurs constatées dans le test précédent).
Nous pouvons maintenant utiliser la fonction "map" d'Arduino pour redistribuer nos valeurs entre une valeur minimale de -1000 et une valeur maximale de 1000, selon les deux axes (cette valeur de 1000 est arbitraire, mais nous ne nous intéressons qu'à l'orientation du champ magnétique; nous devons simplement nous assurer que l'échelle est la même dans les 2 directions):
xcalibree = map(xbrut, xmin, xmax, -1000, 1000);
ycalibree = map(ybrut, ymin, ymax, -1000, 1000);
En utilisant à nouveau le premier sketch de ce billet, nous réglons la constante "calibration" à 1, et nous assignons les valeurs qui conviennent à notre capteur pour les constantes xmin, xmax, ymin et ymax.
Nouvelle prise de mesure, et transfert des données dans un tableur, après calibration:
Voilà qui est beaucoup mieux: après calibration, les deux capteurs x et y mesurent un champ magnétique maximal de 1000. La résultante de x et y (le rayon du cercle) est la même peu importe l'orientation du module. Lorsque la valeur en x est maximale, celle en y est nulle, etc.
Ajout de l'afficheur LCD
Pour créer notre boussole, il ne reste plus qu'à ajouter l'afficheur LCD; le circuit est le même que lors de mes utilisations précédentes de cet afficheur; j'utilise un circuit intégré 4050 pour abaisser à 3,3 V la communication entre l'Arduino et l'afficheur.
Rien n'a changé en ce qui concerne le capteur magnétique:
Sketch de la boussole
Ce dernier sketch est assez similaire aux précédents, mais plutôt qu'afficher les composantes du champ magnétique dans le moniteur série, ces composantes servent à tracer sur l'écran LCD une flèche qui pointe vers le nord (le sketch est conçu en supposant que l'axe x du capteur pointe vers le côté droit du LCD).
Attention: pour que la boussole fonctionne correctement, il est important que vous écriviez les constantes appropriées pour la calibration de votre capteur HMC5883 (xmin, xmax, ymin, ymax). Vous devez également trouver l'angle de déclinaison de l'endroit où vous vous trouvez, et mettre la bonne valeur (en radians) dans la constante "declinaison".
Modifications envisageables:
Il est possible que le module HMC5883L nécessite de fréquentes recalibrations: on pourrait envisager d'insérer la procédure de calibration à l'intérieur du sketch de la boussole. En appuyant sur un bouton (où à chaque démarrage du sketch), on afficherait un message à l'écran demandant d'effectuer une rotation complète de la boussole afin de redéfinir les valeurs à utiliser lors du calcul de calibration. De plus, on pourrait surveiller la composante z du champ magnétique pour s'assurer que la boussole est bien tenue à l'horizontale (l'écran pourrait indiquer que la boussole est trop inclinée pour que la mesure soit fiable).
Yves Pelletier (Twitter, Facebook)
Calibration nécessaire!
Faisons un petit test pour vérifier la fiabilité des mesures prises par le capteur: on place le capteur à plat sur une table: on le fait lentement tourner sur lui-même sur au moins un tour complet tout en mesurant les 3 composantes du champ magnétique. J'ai utilisé le sketch ci-dessous (basé sur un sketch publié par l'équipe de Sparkfun) pour enregistrer les 3 composantes du champ magnétique, que j'ai ensuite copiées dans un tableur.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/***************************************************************** | |
Affichage dans le moniteur série des 3 composantes orthogonales | |
du champ magnétique, tel que mesurées par le capteur HMC5883L. | |
Basé sur l'exemple publié par Sparkfun: | |
https://www.sparkfun.com/tutorials/301 | |
Plus d'infos: | |
https://electroniqueamateur.blogspot.com/2019/06/fabrication-dune-boussole-avec-le.html | |
******************************************************************/ | |
#include <Wire.h> // Bibliothèque pour i2c | |
#define addresse 0x1E // Adresse i2c du capteur HMC5883 | |
// valeurs requises pour calibrer nos mesures (spécifique à votre module): | |
const int calibration = 0; // régler à "1" pour calibrer vos mesures, "0" pour utiliser les mesures non-calibrées | |
const int xmin = 0; | |
const int xmax = 0; | |
const int ymin = 0; | |
const int ymax = 0; | |
void setup() { | |
Serial.begin(9600); | |
Wire.begin(); | |
// Initialisation du HMC5883 | |
Wire.beginTransmission(addresse); | |
Wire.write(0x02); | |
Wire.write(0x00); // mode de mesure continue | |
Wire.endTransmission(); | |
} | |
void loop() { | |
int xbrut, ybrut, zbrut, xcalibree, ycalibree; | |
// On demande une prise de mesure | |
Wire.beginTransmission(addresse); | |
Wire.write(0x03); | |
Wire.endTransmission(); | |
// Lecture des mesures | |
Wire.requestFrom(addresse, 6); | |
if (6 <= Wire.available()) { | |
xbrut = Wire.read() << 8; //X msb | |
xbrut |= Wire.read(); //X lsb | |
zbrut = Wire.read() << 8; //Z msb | |
zbrut |= Wire.read(); //Z lsb | |
ybrut = Wire.read() << 8; //Y msb | |
ybrut |= Wire.read(); //Y lsb | |
} | |
// calibration, si désirée: | |
if (calibration) { | |
xcalibree = map(xbrut, xmin, xmax, -1000, 1000); | |
ycalibree = map(ybrut, ymin, ymax, -1000, 1000); | |
// affichage au moniteur série | |
Serial.print(xcalibree); Serial.print("\t"); | |
Serial.print(ycalibree); Serial.print("\t"); | |
Serial.println(zbrut); | |
} | |
else // on affiche les valeurs brutes | |
{ | |
Serial.print(xbrut); Serial.print("\t"); | |
Serial.print(ybrut); Serial.print("\t"); | |
Serial.println(zbrut); | |
} | |
delay(250); // court délai pour ralentir un peu le processus | |
} |
Si les mesures sont correctes, le graphique de la composante y en fonction de la composante x devrait former un cercle centré autour de la valeur 0. Quant à la composante z, elle devrait demeurer constante.
Mais voici ce que j'ai obtenu:
Ça ne va pas du tout! Les données forment une ellipse plutôt qu'un cercle, ce qui signifie qu'un même champ magnétique ne génère pas la même valeur numérique selon qu'il est mesuré par le capteur x ou par le capteur y. De plus, les mesures du capteur x ne sont pas réparties de façon symétrique autour de 0, ce qui signifie qu'un même champ magnétique ne génère pas la même mesure (en valeur absolue) selon qu'il est orienté dans le sens positif ou dans le sens négatif de l'axe.
Dans ce cas précis, x a varié entre -561 et -63, alors que y a varié entre -263 et +242. Quant à la composante z, qu'on ne voit pas sur le graphique, elle est demeurée presque constante, tel que prévu, fluctuant entre -613 et -587.
Pour faciliter la prise des mesures utiles à la calibration, j'ai légèrement modifié le sketch afin qu'il affiche la valeur minimale et la valeur maximale de x et y pendant une rotation complète du module:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/***************************************************************** | |
Affichage dans le moniteur série de la valeur maximale et de | |
la valeur minimale des 3 composantes orthogonales | |
du champ magnétique, tel que mesurées par le capteur HMC5883L. | |
Utile pour calibrer le capteur. | |
Basé sur l'exemple publié par Sparkfun: | |
https://www.sparkfun.com/tutorials/301 | |
Plus d'infos: | |
https://electroniqueamateur.blogspot.com/2019/06/fabrication-dune-boussole-avec-le.html | |
******************************************************************/ | |
#include <Wire.h> // Bibliothèque pour i2c | |
#define addresse 0x1E // Adresse i2c du capteur HMC5883 | |
int xmax, xmin, ymax, ymin, zmax, zmin; | |
void setup() { | |
Serial.begin(9600); | |
Wire.begin(); | |
// Initialisation du HMC5883 | |
Wire.beginTransmission(addresse); | |
Wire.write(0x02); | |
Wire.write(0x00); // mode de mesure continue | |
Wire.endTransmission(); | |
// On demande une prise de mesure | |
Wire.beginTransmission(addresse); | |
Wire.write(0x03); | |
Wire.endTransmission(); | |
// Première lecture | |
Wire.requestFrom(addresse, 6); | |
if (6 <= Wire.available()) { | |
xmin = Wire.read() << 8; //X msb | |
xmin |= Wire.read(); //X lsb | |
zmin = Wire.read() << 8; //Z msb | |
zmin |= Wire.read(); //Z lsb | |
ymin = Wire.read() << 8; //Y msb | |
ymin |= Wire.read(); //Y lsb | |
} | |
xmax = xmin; | |
ymax = ymin; | |
zmax = zmin; | |
} | |
void loop() { | |
int xbrut, ybrut, zbrut; | |
// On demande une prise de mesure | |
Wire.beginTransmission(addresse); | |
Wire.write(0x03); | |
Wire.endTransmission(); | |
// Lecture des mesures | |
Wire.requestFrom(addresse, 6); | |
if (6 <= Wire.available()) { | |
xbrut = Wire.read() << 8; //X msb | |
xbrut |= Wire.read(); //X lsb | |
zbrut = Wire.read() << 8; //Z msb | |
zbrut |= Wire.read(); //Z lsb | |
ybrut = Wire.read() << 8; //Y msb | |
ybrut |= Wire.read(); //Y lsb | |
} | |
// si une valeur qu'on vient de mesurer est plus petite ou plus grande que toutes | |
// les autres, on la conserve. | |
if (xbrut < xmin) { | |
xmin = xbrut; | |
} | |
if (xbrut > xmax) { | |
xmax = xbrut; | |
} | |
if (ybrut < ymin) { | |
ymin = ybrut; | |
} | |
if (ybrut > ymax) { | |
ymax = ybrut; | |
} | |
if (zbrut < zmin) { | |
zmin = zbrut; | |
} | |
if (zbrut > zmax) { | |
zmax = zbrut; | |
} | |
//Affichage des valeurs min et max: | |
Serial.print("xmin: "); | |
Serial.print(xmin); | |
Serial.print(" xmax: "); | |
Serial.print(xmax); | |
Serial.print(" ymin: "); | |
Serial.print(ymin); | |
Serial.print(" ymax: "); | |
Serial.print(ymax); | |
Serial.print(" zmin: "); | |
Serial.print(zmin); | |
Serial.print(" zmax: "); | |
Serial.println(zmax); | |
delay(250); | |
} |
xmin: -564 xmax: -62 ymin: -277 ymax: 254 zmin: -616 zmax: -606
(ce qui confirme approximativement les valeurs constatées dans le test précédent).
Nous pouvons maintenant utiliser la fonction "map" d'Arduino pour redistribuer nos valeurs entre une valeur minimale de -1000 et une valeur maximale de 1000, selon les deux axes (cette valeur de 1000 est arbitraire, mais nous ne nous intéressons qu'à l'orientation du champ magnétique; nous devons simplement nous assurer que l'échelle est la même dans les 2 directions):
xcalibree = map(xbrut, xmin, xmax, -1000, 1000);
ycalibree = map(ybrut, ymin, ymax, -1000, 1000);
En utilisant à nouveau le premier sketch de ce billet, nous réglons la constante "calibration" à 1, et nous assignons les valeurs qui conviennent à notre capteur pour les constantes xmin, xmax, ymin et ymax.
Nouvelle prise de mesure, et transfert des données dans un tableur, après calibration:
Voilà qui est beaucoup mieux: après calibration, les deux capteurs x et y mesurent un champ magnétique maximal de 1000. La résultante de x et y (le rayon du cercle) est la même peu importe l'orientation du module. Lorsque la valeur en x est maximale, celle en y est nulle, etc.
Ajout de l'afficheur LCD
Pour créer notre boussole, il ne reste plus qu'à ajouter l'afficheur LCD; le circuit est le même que lors de mes utilisations précédentes de cet afficheur; j'utilise un circuit intégré 4050 pour abaisser à 3,3 V la communication entre l'Arduino et l'afficheur.
- La broche VCC de l'afficheur et la broche 1 du 4050 sont branchés à la sortie 3.3 V de l'Arduino.
- La broche GND de l'afficheur et la broche 8 du 4050 sont branchés à la broche GND de l'Arduino.
- La broche 3 de l'Arduino est branchée à la broche 3 du 4050
- La broche 4 de l'Arduino est branchée à la broche 5 du 4050
- La broche 5 de l'Arduino est branchée à la broche 7 du 4050
- La broche 11 de l'Arduino est branchée à la broche 14 du 4050
- La broche 13 de l'Arduino est branchée à la broche 11 du 4050
- La broche SCE ou CS de l'afficheur est branchée à la broche 4 du 4050
- La broche RST de l'afficheur est branchée à la broche 2 du 4050
- La broche D/C de l'afficheur est branchée à la broche 6 du 4050
- La broche DN (MOSI) ou DIN de l'afficheur est branchée à la broche 15 du 4050
- La broche SCLK ou CLK de l'afficheur est branchée à la broche 12 du 4050
- La broche LED de l'afficheur n'est pas branchée (je n'avais pas besoin du rétroéclairage).
Rien n'a changé en ce qui concerne le capteur magnétique:
- VCC du module HMC5833 ----- 5V de l'Arduino
- GND du module HMC5833 ---- GND de l'Arduino
- SCL du module HMC5833 ---- A5 de l'Arduino Uno
- SDA du module HMC5833 ----- A4 de l'Arduino Uno
- RDY du module HMC5833 ---- pas connectée
Sketch de la boussole
Ce dernier sketch est assez similaire aux précédents, mais plutôt qu'afficher les composantes du champ magnétique dans le moniteur série, ces composantes servent à tracer sur l'écran LCD une flèche qui pointe vers le nord (le sketch est conçu en supposant que l'axe x du capteur pointe vers le côté droit du LCD).
Attention: pour que la boussole fonctionne correctement, il est important que vous écriviez les constantes appropriées pour la calibration de votre capteur HMC5883 (xmin, xmax, ymin, ymax). Vous devez également trouver l'angle de déclinaison de l'endroit où vous vous trouvez, et mettre la bonne valeur (en radians) dans la constante "declinaison".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************************** | |
Boussole constituée d'un Arduino, d'un magnétomètre | |
HMC5883 et d'un écran LCD Nokia. | |
Plus d'infos: | |
https://electroniqueamateur.blogspot.com/2019/06/fabrication-dune-boussole-avec-le.html | |
***********************************************************/ | |
#include <Wire.h> // i2c pour le capteur HMC5883 | |
#define adresse 0x1E // Adresse i2c du capteur HMC5883 | |
// bibliothèques pour l'écran LCD Nokia | |
#include <SPI.h> | |
#include <Adafruit_GFX.h> | |
#include <Adafruit_PCD8544.h> | |
// valeurs requises pour calibrer nos mesures (spécifique à votre module): | |
const int xmin = -564; | |
const int xmax = -62; | |
const int ymin = -277; | |
const int ymax = 254; | |
// Pour pointer vers le nord géographique plutôt que vers | |
// le nord magnétique, on additionne la déclinaison, qui dépend | |
// de l'endroit où vous êtes: | |
// http://www.magnetic-declination.com/ | |
float declinaison = 0.22; // en radians | |
Adafruit_PCD8544 display = Adafruit_PCD8544(5, 4, 3); | |
// D/C broche 5,CSE ou CS broche 4, RST broche 3 | |
const int rayon = 20; //rayon du cercle en pixels, sur l'afficheur LCD | |
void setup(void) | |
{ | |
// initialiation du capteur HMC5883 | |
Wire.begin(); | |
Wire.beginTransmission(adresse); | |
Wire.write(0x02); | |
Wire.write(0x00); | |
Wire.endTransmission(); | |
// initialisation de l'afficheur | |
display.begin(); | |
display.setContrast(60); // réglage du contraste (40-60) variable selon votre écran | |
display.clearDisplay(); | |
} | |
void loop(void) | |
{ | |
int xbrut, ybrut, zbrut, xcalibree, ycalibree; | |
// On demande les informations au capteur HMC5883 | |
Wire.beginTransmission(adresse); | |
Wire.write(0x03); //select register 3, X MSB register | |
Wire.endTransmission(); | |
//Lecture des composantes du champ magnétique | |
Wire.requestFrom(adresse, 6); | |
if (6 <= Wire.available()) { | |
xbrut = Wire.read() << 8; //X msb | |
xbrut |= Wire.read(); //X lsb | |
zbrut = Wire.read() << 8; //Z msb | |
zbrut |= Wire.read(); //Z lsb | |
ybrut = Wire.read() << 8; //Y msb | |
ybrut |= Wire.read(); //Y lsb | |
} | |
// valeurs calibrées: | |
xcalibree = map(xbrut, xmin, xmax, -1000, 1000); | |
ycalibree = map(ybrut, ymin, ymax, -1000, 1000); | |
// Calcul de l'angle entre le nord magnétique et l'axe x du capteur | |
float orientationNord = atan2(ycalibree, xcalibree); | |
// Pour pointer vers le nord géographique plutôt que vers | |
// le nord magnétique, on additionne la déclinaison | |
orientationNord += declinaison; | |
// on dessine la flèche à l'angle approprié sur l'afficheur LCD | |
display.clearDisplay(); | |
display.drawCircle(display.width() / 2, display.height() / 2, rayon, BLACK); // contour d'un cercle | |
display.drawLine(display.width() / 2, display.height() / 2, display.width() / 2 + rayon * cos(orientationNord) , display.height() / 2 - rayon * sin(orientationNord), BLACK); | |
display.display(); | |
delay(50); | |
} |
Modifications envisageables:
Il est possible que le module HMC5883L nécessite de fréquentes recalibrations: on pourrait envisager d'insérer la procédure de calibration à l'intérieur du sketch de la boussole. En appuyant sur un bouton (où à chaque démarrage du sketch), on afficherait un message à l'écran demandant d'effectuer une rotation complète de la boussole afin de redéfinir les valeurs à utiliser lors du calcul de calibration. De plus, on pourrait surveiller la composante z du champ magnétique pour s'assurer que la boussole est bien tenue à l'horizontale (l'écran pourrait indiquer que la boussole est trop inclinée pour que la mesure soit fiable).
Yves Pelletier (Twitter, Facebook)
Encore une fois BRAVO pour ce montage et les explications qui s 'en rapportent.
RépondreSupprimerbonne continuation.
Mes sincères félicitations pour la réalisation de ce montage qui ne paraît pas aussi simple qu'on le croit. J'ai passé 2 jours à son exécution et j'ai beaucoup appris !!! Contrairement aux autres montages qui ne parlent même pas de calibration et de déclinaison. Le vôtre c'est du top chef !!! Encore mille merci.
RépondreSupprimerBonjour, j'aimerais savoir si le module est compatible avec l'ESP8266 ?
RépondreSupprimerCordialement Léopold DESSARD
Oui, l'ESP8266 gère très bien la communication I2C.
Supprimerbonjour monsieur,
RépondreSupprimerJ'ai fais toute la procédure, les résultats sur Excel sont bon mais avec le moniteur et une vrai boussole j'ai un décalage sur l'axe nord sud ( le sud est bon, le nord 30° d'écart...), je ne comprend pas, le fait d'être à l'intérieur peu être ?
Le circuit 4050, est ce un cd4050?
RépondreSupprimer