Avançar para o conteúdo

Acelerômetro mostra a direção

Acelerômetro mostra a direção para a qual você aponta seu projeto ou sua placa. Hoje vamos estudar o BMI160, um chip acelerômetro e giroscópio. Vamos comunicar o BMI160 com um ESP32, mais especificamente o SeeedStudio Xiao ESP32-C6.

Já falamos de giroscópio antes, neste artigo. Porém acelerômetro vai ser a primeira vez aqui no blog, então vamos aprofundar um pouco. O projeto que vamos fazer usa três LEDs, um para cada eixo (X, Y e Z). Conforme você girar a protoboard, um dos três LEDs vai acender, indicando que estamos virados naquela direção. Cada orientação da protoboard gera acendimento do LED correspondente.

Comprei o BMI160 no Aliexpress neste link, um ótimo custo benefício. Isso visto pela quantidade de projetos legais que dá pra fazer. Por exemplo controle remoto por movimento (tipo Wii U) e robô equilibrista, ambos usam acelerômetros e giroscópios internamente.

Analisando o datasheet do BMI160, vemos que ele funciona alimentado entre 1,7 e 3,6V, portanto nada de alimenta-lo com 5V. Isto serve para o Arduino UNO, caso você vá usa-lo, coloque conversores de nível lógico nas linhas SDA e SCL.

Vemos também que o sensor lê acelerações até 16g. Isto significa 16 vezes a força da gravidade. Já seu giroscópio lê até 2000º por segundo, é um sensor bem completo!.

O projeto – acelerômetro mostra a direção

Vamos implementar um projeto simples porém muito legal didáticamente. Basicamente teremos três LEDs, um para cada eixo de orientação disponível. Usaremos a força da gravidade, que é 9,81m/s² a nosso favor. Quando qualquer um dos três eixos passar de 95% deste valor (0,95 * valor) nós ligaremos o LED correspondente.

Isto significa que necessariamente o sensor, e consequentemente a protoboard, precisa estar numa posição quase “reta”. Valores intermediários de aceleração, entre +0,95 e -0,95, não acionarão LED algum. Portanto apenas valores acima de +0,95 e abaixo de -0,95 ligarão algum LED.

Você já pode imaginar todo tipo de aplicação para o BMI160, conforme comentado acima. Qualquer projeto que necessite saber orientação ou aceleração em tempo real pode se beneficiar dele. No projeto de hoje, com LEDs o acelerômetro mostra a direção.

Hardware/montagem

Como de costume aqui no blog, as montagens são simples e com poucos componentes. Os projetos que trago para você não são grandes nem complexos. Isto tudo é para proporcionar fácil entendimento e rápida montagem/teste.

Hoje não vai ser diferente, precisaremos de um ESP32 ou qualquer outro microcontrolador com i2c. Como por exemplo Attiny85, Arduino UNO, Arduino Leonardo, etc. Usaremos a placa de giroscópio/acelerômetro BMI160 deste link. Precisaremos ainda de três LEDs de cores diferentes (ou da mesma cor, você escolhe) e três resistores de 680 Ohm. Mas pode ser qualquer valor entre 220 Ohm e 1500 Ohm.

O circuito está abaixo e foi montado em uma protoboard de 400 pontos. Usei esta pequena protoboard pois a placa que fiz para o ESP32-C6 é especial. Ela expões todos os seus 11 pinos em uma única linha, ocupando pouco espaço das protoboards.

BMI160 with ESP32 schematic diagram
BMI160 with ESP32 schematic diagram
BMI160 eixo z na protobboard
LED amarelo aceso, eixo Z

Independente de como você vai fazer a montagem, siga o diagrama esquemático acima e terá sucesso. O acelerômetro/giroscópio BMI160 tem duas fileiras de pinos para conexão. Apenas uma é necessária, aquela com o SDA e SCL além do 3V3 e GND.

Firmware/código

Eu usei o software IDE do Arduino para programar este exemplo. Precisei instalar uma biblioteca chamada “DFRobot_BMI160”. Ela está disponível para buscar e instalação dentro da própria IDE do Arduino.

Usei como base para meu código o exemplo “Calibrated_sensor_Output” em “File > Examples > FastIMU > Calibrated_sensor_Output”. Modifiquei o exemplo para inserir minha lógica de acendimentos dos LEDs.

O código completo está abaixo. Veja que o endereço do BMI160 é “0x69”, segundo seu próprio datasheet. Observe também que usamos a biblioteca “wire” para comunicação i2c, em 400kHz.

#include "FastIMU.h"
#include <Wire.h>

#define IMU_ADDRESS 0x69    //Change to the address of the IMU
#define PERFORM_CALIBRATION //Comment to disable startup calibration
BMI160 IMU;               //Change to the name of any supported IMU! 

// Currently supported IMUS: MPU9255 MPU9250 MPU6886 MPU6500 MPU6050 ICM20689 ICM20690 BMI055 BMX055 BMI160 LSM6DS3 LSM6DSL QMI8658

calData calib = { 0 };  //Calibration data
AccelData accelData;    //Sensor data
GyroData gyroData;
MagData magData;
long sensorTime= 0;

void setup() {
  Wire.begin();
  Wire.setClock(400000); //400khz clock
  Serial.begin(115200);
  
  pinMode(D0, OUTPUT);
  pinMode(D1, OUTPUT);
  pinMode(D2, OUTPUT);
  int err = IMU.init(calib, IMU_ADDRESS);
  if (err != 0) {
    Serial.print("Error initializing IMU: ");
    Serial.println(err);
    while (true) {
      ;
    }
  }
  
#ifdef PERFORM_CALIBRATION
  Serial.println("FastIMU calibration & data example");
  if (IMU.hasMagnetometer()) {
    delay(1000);
    Serial.println("Move IMU in figure 8 pattern until done.");
    delay(3000);
    IMU.calibrateMag(&calib);
    Serial.println("Magnetic calibration done!");
  }
  else {
    delay(5000);
  }

  delay(5000);
  Serial.println("Keep IMU level.");
  delay(5000);
  IMU.calibrateAccelGyro(&calib);
  Serial.println("Calibration done!");
  Serial.println("Accel biases X/Y/Z: ");
  Serial.print(calib.accelBias[0]);
  Serial.print(", ");
  Serial.print(calib.accelBias[1]);
  Serial.print(", ");
  Serial.println(calib.accelBias[2]);
  Serial.println("Gyro biases X/Y/Z: ");
  Serial.print(calib.gyroBias[0]);
  Serial.print(", ");
  Serial.print(calib.gyroBias[1]);
  Serial.print(", ");
  Serial.println(calib.gyroBias[2]);
  if (IMU.hasMagnetometer()) {
    Serial.println("Mag biases X/Y/Z: ");
    Serial.print(calib.magBias[0]);
    Serial.print(", ");
    Serial.print(calib.magBias[1]);
    Serial.print(", ");
    Serial.println(calib.magBias[2]);
    Serial.println("Mag Scale X/Y/Z: ");
    Serial.print(calib.magScale[0]);
    Serial.print(", ");
    Serial.print(calib.magScale[1]);
    Serial.print(", ");
    Serial.println(calib.magScale[2]);
  }
  delay(5000);
  IMU.init(calib, IMU_ADDRESS);
#endif

  //err = IMU.setGyroRange(500);      //USE THESE TO SET THE RANGE, IF AN INVALID RANGE IS SET IT WILL RETURN -1
  //err = IMU.setAccelRange(2);       //THESE TWO SET THE GYRO RANGE TO ±500 DPS AND THE ACCELEROMETER RANGE TO ±2g
  
  if (err != 0) {
    Serial.print("Error Setting range: ");
    Serial.println(err);
    while (true) {
      ;
    }
  }
}

void loop() {
  if(millis() - sensorTime > 50){
    sensorTime= millis();
    IMU.update();
    IMU.getAccel(&accelData);
    float x= accelData.accelX;
    float y= accelData.accelY;
    float z= accelData.accelZ;

    Serial.print(x);
    Serial.print("\t");
    Serial.print(y);
    Serial.print("\t");
    Serial.print(z);
    Serial.print("\t");
    IMU.getGyro(&gyroData);
    Serial.print(gyroData.gyroX);
    Serial.print("\t");
    Serial.print(gyroData.gyroY);
    Serial.print("\t");
    Serial.print(gyroData.gyroZ);
    if (IMU.hasMagnetometer()) {
      IMU.getMag(&magData);
      Serial.print("\t");
      Serial.print(magData.magX);
      Serial.print("\t");
      Serial.print(magData.magY);
      Serial.print("\t");
      Serial.print(magData.magZ);
    }
    if (IMU.hasTemperature()) {
      Serial.print("\t");
      Serial.println(IMU.getTemp());
    }
    else {
      Serial.println();
    }
    
    if(x > 0.95 || x < -0.95){
      digitalWrite(D0, HIGH);
      digitalWrite(D1, LOW);
      digitalWrite(D2, LOW);
    }else if(y > 0.95 || y < -0.95){
      digitalWrite(D0, LOW);
      digitalWrite(D1, HIGH);
      digitalWrite(D2, LOW);
    }else if(z > 0.95 || z < -0.95){
      digitalWrite(D0, LOW);
      digitalWrite(D1, LOW);
      digitalWrite(D2, HIGH);
    }else{
      digitalWrite(D0, LOW);
      digitalWrite(D1, LOW);
      digitalWrite(D2, LOW);
    }
  }
  
  
}

A parte que eu adicionei ao código está abaixo. Veja que eu usei condicionais IF() comparando os eixos X, Y e Z com valores “> 0.95” e “< -0.95”. Isso garante uma faixa de 5% de tolerância/inclinação na detecção da orientação da placa.

if(x > 0.95 || x < -0.95){
      digitalWrite(D0, HIGH);
      digitalWrite(D1, LOW);
      digitalWrite(D2, LOW);
    }else if(y > 0.95 || y < -0.95){
      digitalWrite(D0, LOW);
      digitalWrite(D1, HIGH);
      digitalWrite(D2, LOW);
    }else if(z > 0.95 || z < -0.95){
      digitalWrite(D0, LOW);
      digitalWrite(D1, LOW);
      digitalWrite(D2, HIGH);
    }else{
      digitalWrite(D0, LOW);
      digitalWrite(D1, LOW);
      digitalWrite(D2, LOW);
    }

Colocando para funcionar

Após copiar o código acima e colar na sua IDE do Arduino, clique no botão “>” upload que está no canto superior esquerdo. Após alguns segundos o código será gravado no seu ESP32 ou Arduino. Aguarde então mais alguns segundos e um dos LEDs deve acender.

Movimente a placa nos três eixos e observer os LEDs acendendo um após o outro. Eu fiz um vídeo ilustrando o funcionamento do código no circuito, veja bem no início do artigo.

LED vermelho, eixo Y
LED vermelho, eixo Y
LED verde, eixo X
LED verde, eixo X
led amarelo com esp32
LED amarelo, eixo Z

Caso tenha comentários ou informações a acrescentar, comente no campo abaixo ou lá no Youtube do blog FritzenLab. Até a próxima.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *