Construindo uma bússola eletrônica com o módulo HMC5883L, anel de LEDs RGB e Arduino 16

O módulo Bússola Eletrônica HMC5883L permite determinar a direção do Norte através da medição do campo magnético da Terra. Neste post vamos ver alguns detalhes do seu funcionamento e sua interface com o Arduino e construir uma bússola eletrônica usando o módulo e um anel de LEDs RGB.

Bússola Eletrônica com Arduino (Figura 01)

Material Necessário

Conhecendo o módulo Bússola Eletrônica HMC5883L

Apesar do nome, este módulo não usa o chip HMC5883L, pois ele já foi descontinuado. No seu lugar é usado o QMC5833L, que foi desenvolvido a partir do licenciamento do HMC5883L, mas tem algumas diferenças. O princípio de funcionamento é igual nos dois chips: três sensores magnéticos medem a intensidade em três eixos (X, Y e Z). Estas medidas são convertidas em três números de 16 bits com sinal, através de um conversor analógico digital, e armazenadas em registradores internos.

A comunicação do módulo com um microcontrolador é feita através do protocolo I2C. Lembrando, esta é uma ligação a dois fios “em varal” onde cada dispositivo tem um endereço de 7 bits. No caso do QMC5833L, este endereço é 0x0D. As leituras e escritas nos registradores começam com uma operação de escrita I2C na qual é passado o número do registrador que será lido/escrito. Para simplificar, após cada leitura ou escrita o número do registrador a acessar é automaticamente incrementado, facilitando a leitura ou escrita de registradores consecutivos.

A figura abaixo mostra os registradores disponíveis e seus números. Reparar que o resultado da medição ocupa 6 registradores – um par para cada direção, com o byte menos significativo primeiro – na ordem X, Y e Z.

Módulo HMC5883L (Figura 02)

A correspondência dos eixos X, Y e Z com a posição física da placa pode ser observada através da indicação impressa no módulo HMC5883L:

Módulo HMC5883L (Figura 03)

Um problema com os módulos de bússola é que as leituras tem um valor (offset) somado a elas. Por este motivo é fundamental fazer um processo de calibração, realizando leituras à medida que o módulo é girado e calculando o offset de cada eixo a partir delas.

Obtida a intensidade do campo magnético nos três eixos, a trigonometria nos permite obter o ângulo para o Norte magnético da Terra (no nosso exemplo vamos supor que o módulo está horizontal e portanto o eixo Z pode ser ignorado). O Norte magnético está ligeiramente afastado do Norte geográfico. Isto pode ser compensado através da declinação magnética, que pode ser consultada para a sua localidade neste site.

Para economizar energia, o chip inicialmente está num modo ocioso (sem realizar medições); ele pode ser configurado para diversas taxas de leitura do sensores. No nosso exemplo vamos usar o modo contínuo.

Montagem da bússola eletrônica com o módulo HMC5883L, anel de LEDs RGB e Arduino

A figura abaixo mostra a montagem que vamos usar. Para facilitar a montagem foi soldada uma barra de pinos 90 graus ao anel de LEDs. É importante o módulo bússola HMC5883L e o anel de LEDs RGB estarem alinhados como na figura.

Bússola eletrônica com Arduino (Figura 04)

Código

A maioria das bibliotecas disponíveis trata somente o HMC5883L; por este motivo escrevi rotinas específicas para o QMC5883L.

#include <Wire.h>

// Classe simples para tratar a bússola
class Bussola {
  public:
    Bussola(void);
    bool inicia(void);
    void setDeclination (int graus , int mins, char dir);
    int leDirecao(void);
    void iniciaCal();
    void encerraCal();

  private:
    static const int ender_QMC = 0x0D; // endereço I2C do QMC5883
    static const int regCR1_QMC = 9;   // registrador de configuração
    static const int regSR_QMC = 11;   // registador set/reset
    static const int regXL_QMC = 0;    // primeiro registrador de dados
    static const int regST_QMC = 6;    // registrador de status

    // fatores de correção determinados na calibração
    int16_t xMin, yMin, xMax, yMax;
    float escX = 1.0;
    float escY = 1.0;
    int16_t offX = 0;
    int16_t offY = 0;

    // Diferença entre o Polo Magnético e o Geográfico
    float declination = 0.0;

    // Rotina para disparar leitura dos dados
    void pedeDados(int regStatus, int regDados);
};

Bussola bussola;

// Construtor
Bussola::Bussola(void) {
}

// Inicia comunicação com a bússola
bool Bussola::inicia() {
  // Confere o endereço
  Wire.beginTransmission(ender_QMC);
  if (Wire.endTransmission() != 0) {
    return false;
  }

  // Inicia o chip para modo contínuo
  Wire.beginTransmission(ender_QMC);
  Wire.write(regSR_QMC);
  Wire.write(0x01);
  Wire.endTransmission();
  Wire.beginTransmission(ender_QMC);
  Wire.write(regCR1_QMC);
  Wire.write(0x0D);
  Wire.endTransmission();

  return true;
}

// Define a declinação (correção entre o Norte magnético e o Norte geofráfico)
// ver http://www.magnetic-declination.com/
void Bussola::setDeclination (int graus , int mins, char dir) {
  declination = (graus + mins / 60.0) * PI / 180.0;
  if (dir == 'W') {
    declination = - declination;
  }
  Serial.println (declination);
}

// Le a direção da bússola em graus (0 a 360) em relação à marcação do eixo X
// Assume que a bússola esta na horizontal
int Bussola::leDirecao(void) {
  int16_t x, y, z;

  // Le a intesidade do campo magnético
  pedeDados(regST_QMC, regXL_QMC);
  x = Wire.read();          //LSB  x
  x |= Wire.read() << 8;    //MSB  x
  y = Wire.read();          //LSB y
  y |= Wire.read() << 8;    //MSB y
  z = Wire.read();          //LSB  z
  z |= Wire.read() << 8;    //MSB z

  // Registra mínimo e máximo para a calibração
  if (x < xMin) {
    xMin = x;
  }
  if (xMax < x) {
    xMax = x;
  }
  if (y < yMin) {
    yMin = y;
  }
  if (yMax < y) {
    yMax = y;
  }

  // corrige e calcula o angulo em radianos
  float xC = (x - offX) * escX;
  float yC = (y - offY) * escY;
  float angulo = atan2 (yC, xC) + declination;

  // Garante que está entre 0 e 2*PI
  if (angulo < 0) {
    angulo += 2.0 * PI;
  } else if (angulo >= 2 * PI) {
    angulo -= 2.0 * PI;
  }

  // Converte para graus
  return round ((angulo * 180.0) / PI);
}

void Bussola::pedeDados(int regStatus, int regDados) {
  // Espera ter um dado a ler
  do {
    Wire.beginTransmission(ender_QMC);
    Wire.write(regStatus);
    Wire.endTransmission();
    Wire.requestFrom(ender_QMC, 1);
  } while ((Wire.read() & 1) == 0);

  Wire.beginTransmission(ender_QMC);
  Wire.write(regDados);
  Wire.endTransmission();
  Wire.requestFrom(ender_QMC, 6);
}

// Inicia processo de calibração
void Bussola::iniciaCal() {
  xMax = yMax = -32768;
  xMin = yMin = 32767;
}

// Encerra a calibração
void Bussola::encerraCal() {
  Serial.print ("X: ");
  Serial.print (xMin);
  Serial.print (" - ");
  Serial.println (xMax);
  Serial.print ("Y: ");
  Serial.print (yMin);
  Serial.print (" - ");
  Serial.println (yMax);

  // Offset para centralizar leituras em zero
  offX = (xMax + xMin) / 2;
  offY = (yMax + yMin) / 2;

  // Escala para ter a mesma variação nos dois eixos
  int16_t varX = xMax - xMin;
  int16_t varY = yMax - yMin;
  if (varY > varX) {
    escY = 1.0;
    escX = (float) varY / varX;
  } else {
    escX = 1.0;
    escY = (float) varX / varY;
  }
}

Para controle do anel de LED vamos usar a biblioteca Adafruit NeoPixel, que você pode instalar direto do Gerenciador de Bibliotecas da IDE do Arduino:

Biblioteca Adafruit Neopixel (Figura 05)

Vamos usar o anel de LEDs para indicar a direção do Norte. Por exemplo, se o ângulo for exatamente 90 graus, vamos  acender apenas o LED 3. Se o ângulo for 115 graus, vamos acender os LEDs 3 e 4 com igual intensidade. Se o ângulo for 100 graus, os LED 3 e 4 serão acesos, porém o LED 3 será aceso mais forte.  A figura abaixo mostra a correspondência entre o índice do LED e o ângulo:

Anel de LED RGB (Figura 06)

A rotina abaixo acende os LEDs apropriados:

#include <Adafruit_NeoPixel.h>

const int PINO_ANEL = 3;
const int IMAX = 63; // Intensidade máxima, 0 a 255

Adafruit_NeoPixel anel(12, PINO_ANEL, NEO_GRB + NEO_KHZ800);

// Acende um ou dois LED do anel para indicar um ângulo
// em relação ao eixo X do sensor
// 0 <= angulo < 360
void aponta (int angulo) {
  // corrige o sentido
  angulo = 360 - angulo;
  if (angulo == 360) {
    angulo = 0;
  }
  // determina o primeiro LED
  int a = angulo / 30;
  a = (a + 10) % 12;

  // determina a intensidade do segundo LED
  int b = ((angulo % 30) * IMAX) / 30;

  // Acende os LEDs
  anel.clear();
  anel.setPixelColor (a, anel.Color(0, IMAX - b, IMAX - b));
  anel.setPixelColor ((a + 1) % 12, anel.Color(0, b, b));
  anel.show();
}

Após iniciarmos bússola e LED, vamos realizar uma calibração por 20 segundos.

// Iniciação
void setup() {
  // Inicia os LEDs
  anel.begin();
  anel.clear();

  // Inica a bussola
  Wire.begin();
  Serial.begin(115200);
  if (!bussola.inicia()) {
    Serial.println ("Nao encontrou a bussola!");
    for (;;) {
      delay(100);
    }
  }

  // Calibração da bussola
  Serial.println ("Calibrando... rode o sensor em um círculo");
  int pos = 0;
  bussola.iniciaCal();
  long tmpFim = millis() + 20000L;
  while (millis() < tmpFim) {
    bussola.leDirecao();
    aponta (pos);
    pos += 5;
    if (pos == 360) {
      pos = 0;
    }
    delay (10);
  }
  bussola.encerraCal();
  Serial.println ("Calibrado");
}

O programa principal lê a bússola e apresenta a direção do Norte nos LEDs.

// Laço principal
void loop() {
  aponta(bussola.leDirecao());
  delay (200);
}

Colocando a bússola eletrônica com Arduino para funcionar

  1. Faça a montagem do circuito conforme a figura.
  2. Execute a IDE do Arduino no seu computador.
  3. Certifique-se que a placa selecionada no menu Ferramentas é o Arduino Nano.
  4. Conecte o Nano ao computador.
  5.  Selecione no menu Ferramentas a porta onde o Nano está conectado.
  6. Copie (ou digite) o código acima (as quatro partes) em um novo sketch e salve com o nome “Bussola” (ou outro de sua preferência).
  7. Carregue o código no Arduino Nano.
  8. Após a carga (ou um reset do Arduino) os LEDs serão acesos sequencialmente de forma rápida, indicando que está sendo realizada a calibração. Gire a montagem para que o programa consiga registrar a leitura em todos os ângulos, procurando manter a protoboard paralela ao chão.
  9. Após 20 segundos a calibração será encerrada e os LEDs acesos passarão a indicar a direção do Norte magnético.

Resultado

Após a calibração, os LEDs acessos indicarão a direção do Norte magnético:

Bússola eletrônica com Arduino (Figura 7)

Conclusão

Usando o módulo Bússola Eletrônica HMC5883L podemos medir a direção para o Norte. Isto pode ser usado para fazer uma bússola (como neste projeto), descobrir a orientação do sensor ou até navegar um veículo robótico.

Agora que você já conhece o módulo Bússola Eletrônica HMC5883L e sabe como usá-lo, que tal testar algum projeto com ele?

Gostou do artigo “Construindo uma bússola eletrônica com o módulo HMC5883L, uma anel de LEDs RGB e um Arduino”? Deixe seu comentário logo abaixo dizendo o que achou. Para mais artigos e tutorias de projetos acesse nosso blog.

Faça seu comentário

Acesse sua conta e participe

16 Comentários

  1. Prezado, qual a maior distância que o sensor pode ser montado ? Preciso saber a posição de um disco a mais de 10m. Obrigado

    1. A comunicação I2C foi projetada pensando em pequenas distâncias (na maioria dos casos em uma mesma placa). A Wikipedia fala em 30cm e menciona que usando um “driver diferencial” distâncias até 20 metros seriam possíveis. Não tenho experiência com isso, mas você pode ver mais detalhes na Wikipeida (https://en.wikipedia.org/wiki/I%C2%B2C#Circuit_interconnections) e nas referência que ela menciona (como https://hackaday.com/2017/02/08/taking-the-leap-off-board-an-introduction-to-i2c-over-long-wires/).

  2. Funcionou sim, desculpa ai, o ESP32 ficou perfeito;.

  3. Oi! Ótima explicação, porem não funcionou no ESP32, o que pode ser?

    1. É difícil dizer sem mais detalhes. Minha sugestão é que primeiro você tente investigar se o problema está com a leitura da bússola ou na apresentação nos LEDs. O GPIO3 no ESP32 é usado pela serial, portanto você deve usar um outro pino para ligar o anel de LEDs. Confira também que você está usando os pinos coretos no ESP32 para o I2C.

  4. Muito interessante e pretendo fazer a montagem, eu pretendo colocar para verificar e direcionar minha antena de radioamador, mas gostaria de saber se tem alguma modificação para colocar de 10° em 10°

    OSVALDO GOMES TERRA JUNIOR
    1. Você poderia trocar o anel de LEDs por 36 LEDs individuais, mas talvez seja mais prático usar um display. A maior dificuldade vai ser obter uma precisão de 10 graus do sensor, eu tentaria fazer uma calibração com uma bússola.

  5. Bom dia Prof. gostaria de montar um modulo que mostrasse os dados em Graus em um display alfanumérico, 16×2 linhas com maior precisão, grau em grau se possível.
    Se tiver algum projeto que possa me atender fico grato.

    1. O método leDirecao() devolve a direção como um valor inteiro de 0 a 360, você pode apresentar este valor no display. Entretanto, o sensor não é totalmente linear e pode sofre interferência de campos eletromagnéticos (da mesma forma que uma bússola tradicional). Mesmo com o processo de calibração não acho que o resultado seja confiável grau a grau.

      1. Boa tarde Prof. Obrigado pela resposta.
        Contudo gostaria de saber se você teria uma solução em que pudesse atender minha necessidade, preciso medir direção, Grau a Grau e mandar essa informação para um display e também armazenar estes dados em um arquivo de bloco de notas para serem lidos posteriormente.

  6. Caro Professor,
    Eu gostaria de usar o módulo externo à placa para verificar a direção de uma antena direcional. Outro pensamento é se poderia usar um display com dígitos invés da roda de LED.
    Desde já gato pela atenção
    Daniel Xavier de Melo
    55 99126.9691

    1. Você pode usar o módulo externo à placa, mas não recomendo usar um cabo muito longo (meu chute é não passar de uns 20 a 30 cm). O método leDirecao() retorna um inteiro de 0 a 360, você pode apresentar esse valor em um display como esse aqui: https://www.makerhero.com/produto/modulo-tm1637-com-display-7-segmentos-4-digitos/

  7. Não, você pode usar outros modelos de Arduino, tomando o cuidado de usar os pinos corretos para as ligações.

  8. Tem necessidade do arduíno ser o nano ?

  9. Muito bom!
    Parabens!