Como usar acelerômetro com ESP8266 NodeMCU

Como usar acelerômetro com ESP8266 NodeMCU

O assunto IoT (Internet das Coisas) está em alta e por isso vem surgindo inúmeras plataformas de desenvolvimento para esse tipo de solução. Uma delas, provavelmente a mais interessante e utilizada pelos makers, é o módulo Wifi ESP8266 NodeMCU. Além desse pequeno módulo conter I/O’s, regulador de tensão e conectividade USB que facilita programação via IDE do Arduino, contém um módulo WiFi que possibilita conexão à nuvem. E é essa conexão que vamos utilizar para ligar um acelerômetro com ESP8266 NodeMCU, enviando os dados para uma página web.

Acelerômetro com ESP8266 NodeMCU

Antes de mostrarmos o funcionamento do acelerômetro com ESP8266 NodeMCU, recomendamos a leitura do post Tutorial: Acelerômetro MPU6050 com Arduino.

Um Pouco de Teoria Sobre o MPU6050 e sua Comunicação I2C

O MPU6050 é uma IMU (Inertial Measurement Unit), que mede valores de aceleração em 3 eixos, X, Y e Z e velocidade angular também em X, Y e Z. Por isso esse sensor tem 6 graus de liberdade (6DOF). Além dessas medições o sensor também pode medir temperaturas de -40 a 80°C. O sensor ainda conta com um DMP(Digital Motion Processor) que pode realizar inúmeros cálculos e algoritmos complexos dentro do próprio MPU6050. Entre esses algoritmos temos detecção de passos, tap-detection, cálculos de ângulo e muito mais. Mais informações sobre o sensor podem ser encontradas no datasheet e no site da InveSense.

O protocolo I2C, que é um protocolo estilo mestre/escravo, consiste basicamente em leitura e escrita de valores em determinados registros. Isso é feito através do envio e recebimento de bits 0 e 1 de uma forma ordenada e em sequência. Para saber mais leia este pequeno resumo.

No caso deste projeto, temos o NodeMCU como mestre e o MPU6050 como escravo. De acordo com mapa de registro do MPU6050, o endereço I2C padrão do MPU6050 é 0x68 podendo ser alterado para 0x69 conectando o pino AD0 para 3,3V, ou seja, podemos utilizar até dois desses sensores em conjunto. Os valores enviados pelo MPU6050 são valores puros (raw data) que, se desejado, podem ser convertidos para ângulos utilizando-se de cálculos ou algoritmos.

Circuito acelerômetro com ESP8266 NodeMCU

Para montagem do circuito do acelerômetro com ESP8266 NodeMCU foram utilizados os seguintes materiais:

A ligação do circuito foi feita da seguinte maneira:

Pinos NodeMCU            Pinos MPU6050
     D5          ->          SDA
     D6          ->          SCL
     Vin         ->          VCC
     GND         ->          GND

Circuito acelerômetro com ESP8266 NodeMCU

O pino Vin do NodeMCU possui 5V, que é a mesma tensão ligada ao conector USB. Com este pino podemos alimentar o MPU6050. O acelerômetro em si opera a 3,3V mas como placa GY-251 possui um regulador de tensão, podemos utilizar 5V sem problemas para alimentação. As linhas de SDA e SCL de comunicação I2C possuem na placa resistores de pull-up para 3,3V de acordo com seu esquemático. Sendo assim, a comunicação I2C pode ser realizada diretamente no NodeMCU que também opera a 3,3V. Ou seja, tudo é energizado via USB 5V e ambas as placas regulam para suas tensões de operação 3,3V.

Configuração da IDE Arduino para o NodeMCU

O NodeMCU pode ser facilmente programado usando a IDE do Arduino como visto no post Como programar o NodeMCU com IDE Arduino. 

Entre na IDE do Arduino e clique em Arquivo -> Preferências:

Menu Preferências IDE Arduino

Na tela seguinte, digite o link abaixo no campo URLs adicionais de Gerenciadores de Placas:

http://arduino.esp8266.com/stable/package_esp8266com_index.json

A sua tela ficará assim:

Tela Preferências IDE Arduino

Clique em OK para retornar à tela principal da IDE

Agora clique em Ferramentas -> Placa -> Gerenciador de Placas:

Menu Gerenciador de Placas IDE Arduino

Utilize a barra de rolagem para encontrar o esp8266 by ESP8266 Community e clique em Instalar.

Tela Gerenciador de Placas IDE Arduino

Assim teremos disponíveis as placas da linha ESP8266, suas bibliotecas e exemplos incluídos.

No menu Ferramentas -> Placas, selecione a placa NodeMCU 1.0 (ESP 12-E module)

Menu Placas IDE Arduino

Instalação da biblioteca ArduinoJSON

Este projeto usará a biblioteca ArduinoJson.h que fará a conversão dos dados do MPU6050 em formato JSON.

Para instalá-la clique em Sketch -> Incluir Biblioteca -> Gerenciar de Bibliotecas…

Menu Gerenciador de Bibliotecas IDE Arduino

No campo Refine sua busca… procure por json, selecione a biblioteca ArduinoJson by Benoit Blanchon e clique em instalar.

Tela Gerenciador de Bibliotecas IDE Arduino

Programação do NodeMCU

Para comunicação com o sensor temos basicamente duas funções usando Wire, uma de escrita e outra de leitura de registros do sensor. A partir dessas duas funções podemos realizar diversas tarefas com o sensor, como por exemplo, inicializá-lo, verificar a comunicação, escolher as escalas, ler os valores entre outras.

As funções de escrita e leitura de registros são writeRegMPU() e readRegMPU()  que encotram-se nas linhas de código 68 e 79 respectivamente.

Para que as leituras do sensor sejam enviadas ao servidor o programa formata esses dados em JSON usando a bibliotexa ArduinoJSON resultando no seguinte formato:

{
  "data" : {
    "accel" : {
      "accelX" : xxxx,
      "accelY" : xxxx,
      "accelZ" : xxxx
    },
    "gyro" : {
      "gyroX" : xxxx,
      "gyroY" : xxxx,
      "gyroZ" : xxxx
    },
    "temp" : xxxx
  },
  "nodeID" : "mcu1"
}

Com os dados convertidos para JSON eles estão prontos para serem enviados à internet.

Exibição dos Dados na Página Web

Para melhor visualização dos dados do acelerômetro com ESP8266 NodeMCU, foi desenvolvido um Web Server em NodeJS instalado em uma Rasberry Pi 3 que responde a requisições HTTP como GET e POST.

O NodeMCU envia uma requisição POST no link /accel com as leituras do sensor em formato JSON. A requisição segue o seguinte formato:

POST /accel HTTP/1.1 
Host: 192.168.0.24:3000
Content-Type: application/json
Content-Length: tamanho do JSON
conteúdo JSON

O servidor então recebe esses dados e exibe em tempo real para os clientes que acessam o servidor via browser.

Para que o NodeMCU saiba sua geolocalização, foi utilizada a API do site ip-api.com. O NodeMCU realiza um HTTP GET request no link /json e recebe de volta o seguinte JSON:

{ 
	"city":"Florianópolis",
	"country":"Brazil",
	"lat":-27.5833,
	"lon":-48.5667,
	"query":"186.xxx.xx.xx",
	"region":"SC"
}

Assim que o NodeMCU recebe sua geolocalização ele repassa para o servidor que exibe então, em um Google Maps.

O servidor exibe a seguinte página para os clientes que o acessam via browser:

Tela Página Web de Monitoramento Remoto

O código completo do servidor escrito em Node.js pode ser encontrado no GitHub.

Código acelerômetro com ESP8266 NodeMCU

//Programa: Acelerometro com ESP8266 NodeMCU
#include <ESP8266WiFi.h> // biblioteca para usar as funções de Wifi do módulo ESP8266
#include <Wire.h>         // biblioteca de comunicação I2C
#include <ArduinoJson.h>  // biblioteca JSON para sistemas embarcados

/*
 * Definições de alguns endereços mais comuns do MPU6050
 * os registros podem ser facilmente encontrados no mapa de registros do MPU6050
 */
const int MPU_ADDR =      0x68; // definição do endereço do sensor MPU6050 (0x68)
const int WHO_AM_I =      0x75; // registro de identificação do dispositivo
const int PWR_MGMT_1 =    0x6B; // registro de configuração do gerenciamento de energia
const int GYRO_CONFIG =   0x1B; // registro de configuração do giroscópio
const int ACCEL_CONFIG =  0x1C; // registro de configuração do acelerômetro
const int ACCEL_XOUT =    0x3B; // registro de leitura do eixo X do acelerômetro

const int sda_pin = D5; // definição do pino I2C SDA
const int scl_pin = D6; // definição do pino I2C SCL

bool led_state = false;

// variáveis para armazenar os dados "crus" do acelerômetro
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ; 

// Definições da rede Wifi
const char* SSID = "Nome da Rede WiFi";
const char* PASSWORD = "Senha da Rede Wifi";

// endereço IP local do Servidor Web instalado na Raspberry Pi 3
// onde será exibida a página web
const char* rpiHost = "192.168.0.24";  

// servidor que disponibiliza serviço de geolocalização via IP    
const char* IpApiHost = "ip-api.com";   

WiFiClient client;

// construindo o objeto JSON que irá armazenar os dados do acelerômetro na função populateJSON()
StaticJsonBuffer<300> jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
JsonObject& data = object.createNestedObject("data");
  
JsonObject& accel = data.createNestedObject("accel");
JsonObject& temp = data.createNestedObject("temp");
JsonObject& gyro = data.createNestedObject("gyro");
  
JsonObject& accelX = accel.createNestedObject("accelX");
JsonObject& accelY = accel.createNestedObject("accelY");
JsonObject& accelZ = accel.createNestedObject("accelZ");

JsonObject& gyroX = gyro.createNestedObject("gyroX");
JsonObject& gyroY = gyro.createNestedObject("gyroY");
JsonObject& gyroZ = gyro.createNestedObject("gyroZ");

/*
 * função que configura a I2C com os pinos desejados 
 * sda_pin -> D5
 * scl_pin -> D6
 */
void initI2C() 
{
  //Serial.println("---inside initI2C");
  Wire.begin(sda_pin, scl_pin);
}

/*
 * função que escreve um dado valor em um dado registro
 */
void writeRegMPU(int reg, int val)      //aceita um registro e um valor como parâmetro
{
  Wire.beginTransmission(MPU_ADDR);     // inicia comunicação com endereço do MPU6050
  Wire.write(reg);                      // envia o registro com o qual se deseja trabalhar
  Wire.write(val);                      // escreve o valor no registro
  Wire.endTransmission(true);           // termina a transmissão
}

/*
 * função que lê de um dado registro
 */
uint8_t readRegMPU(uint8_t reg)        // aceita um registro como parâmetro
{
  uint8_t data;
  Wire.beginTransmission(MPU_ADDR);     // inicia comunicação com endereço do MPU6050
  Wire.write(reg);                      // envia o registro com o qual se deseja trabalhar
  Wire.endTransmission(false);          // termina transmissão mas continua com I2C aberto (envia STOP e START)
  Wire.requestFrom(MPU_ADDR, 1);        // configura para receber 1 byte do registro escolhido acima
  data = Wire.read();                   // lê o byte e guarda em 'data'
  return data;                          //retorna 'data'
}

/*
 * função que procura pelo sensor no endereço 0x68
 */
void findMPU(int mpu_addr)
{
  Wire.beginTransmission(MPU_ADDR);
  int data = Wire.endTransmission(true);

  if(data == 0)
  {
    Serial.print("Dispositivo encontrado no endereço: 0x");
    Serial.println(MPU_ADDR, HEX);
  }
  else 
  {
    Serial.println("Dispositivo não encontrado!");
  }
}

/*
 * função que verifica se o sensor responde e se está ativo
 */
void checkMPU(int mpu_addr)
{
  findMPU(MPU_ADDR);
    
  int data = readRegMPU(WHO_AM_I); // Register 117 – Who Am I - 0x75
  
  if(data == 104) 
  {
    Serial.println("MPU6050 Dispositivo respondeu OK! (104)");

    data = readRegMPU(PWR_MGMT_1); // Register 107 – Power Management 1-0x6B

    if(data == 64) Serial.println("MPU6050 em modo SLEEP! (64)");
    else Serial.println("MPU6050 em modo ACTIVE!"); 
  }
  else Serial.println("Verifique dispositivo - MPU6050 NÃO disponível!");
}

/*
 * função de inicialização do sensor
 */
void initMPU()
{
  setSleepOff();
  setGyroScale();
  setAccelScale();
}

/* 
 *  função para configurar o sleep bit  
 */
void setSleepOff()
{
  writeRegMPU(PWR_MGMT_1, 0); // escreve 0 no registro de gerenciamento de energia(0x68), colocando o sensor em o modo ACTIVE
}

/* função para configurar as escalas do giroscópio
   registro da escala do giroscópio: 0x1B[4:3]
   0 é 250°/s

    FS_SEL  Full Scale Range
      0        ± 250 °/s      0b00000000
      1        ± 500 °/s      0b00001000
      2        ± 1000 °/s     0b00010000
      3        ± 2000 °/s     0b00011000
*/
void setGyroScale()
{
  writeRegMPU(GYRO_CONFIG, 0);
}

/* função para configurar as escalas do acelerômetro
   registro da escala do acelerômetro: 0x1C[4:3]
   0 é 250°/s

    AFS_SEL   Full Scale Range
      0           ± 2g            0b00000000
      1           ± 4g            0b00001000
      2           ± 8g            0b00010000
      3           ± 16g           0b00011000
*/
void setAccelScale()
{
  writeRegMPU(ACCEL_CONFIG, 0);
}

/* função que lê os dados 'crus'(raw data) do sensor
   são 14 bytes no total sendo eles 2 bytes para cada eixo e 2 bytes para temperatura:

  0x3B 59 ACCEL_XOUT[15:8]
  0x3C 60 ACCEL_XOUT[7:0]
  0x3D 61 ACCEL_YOUT[15:8]
  0x3E 62 ACCEL_YOUT[7:0]
  0x3F 63 ACCEL_ZOUT[15:8]
  0x40 64 ACCEL_ZOUT[7:0]

  0x41 65 TEMP_OUT[15:8]
  0x42 66 TEMP_OUT[7:0]

  0x43 67 GYRO_XOUT[15:8]
  0x44 68 GYRO_XOUT[7:0]
  0x45 69 GYRO_YOUT[15:8]
  0x46 70 GYRO_YOUT[7:0]
  0x47 71 GYRO_ZOUT[15:8]
  0x48 72 GYRO_ZOUT[7:0]
   
*/
void readRawMPU()
{  
  Wire.beginTransmission(MPU_ADDR);       // inicia comunicação com endereço do MPU6050
  Wire.write(ACCEL_XOUT);                       // envia o registro com o qual se deseja trabalhar, começando com registro 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);            // termina transmissão mas continua com I2C aberto (envia STOP e START)
  Wire.requestFrom(MPU_ADDR, 14);         // configura para receber 14 bytes começando do registro escolhido acima (0x3B)

  AcX = Wire.read() << 8;                 // lê primeiro o byte mais significativo
  AcX |= Wire.read();                     // depois lê o bit menos significativo
  AcY = Wire.read() << 8;
  AcY |= Wire.read();
  AcZ = Wire.read() << 8;
  AcZ |= Wire.read();

  Tmp = Wire.read() << 8;
  Tmp |= Wire.read();

  GyX = Wire.read() << 8;
  GyX |= Wire.read();
  GyY = Wire.read() << 8;
  GyY |= Wire.read();
  GyZ = Wire.read() << 8;
  GyZ |= Wire.read(); 

  Serial.print("AcX = "); Serial.print(AcX);
  Serial.print(" | AcY = "); Serial.print(AcY);
  Serial.print(" | AcZ = "); Serial.print(AcZ);
  Serial.print(" | Tmp = "); Serial.print(Tmp/340.00+36.53);
  Serial.print(" | GyX = "); Serial.print(GyX);
  Serial.print(" | GyY = "); Serial.print(GyY);
  Serial.print(" | GyZ = "); Serial.println(GyZ);

  led_state = !led_state;
  digitalWrite(LED_BUILTIN, led_state);         // pisca LED do NodeMCU a cada leitura do sensor
  delay(50);                                        
}

/*
 * função que conecta o NodeMCU na rede Wifi
 * SSID e PASSWORD devem ser indicados nas variáveis
 */
void reconnectWiFi() 
{
  if(WiFi.status() == WL_CONNECTED)
    return;

  WiFi.begin(SSID, PASSWORD);

  while(WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }

  Serial.println();
  Serial.print("Conectado com sucesso na rede: ");
  Serial.println(SSID);
  Serial.print("IP obtido: ");
  Serial.println(WiFi.localIP());  
}

void initWiFi()
{
  delay(10);
  Serial.print("Conectando-se na rede: ");
  Serial.println(SSID);
  Serial.println("Aguarde");

  reconnectWiFi();
}

/*
 * função que armazena cada dado do sensor em um objeto JSON
 * utiliza a biblioteca ArduinoJson
 */
void populateJSON()
{
  object["nodeID"] = "mcu1";

  accel["accelX"] = AcX;
  accel["accelY"] = AcY;
  accel["accelZ"] = AcZ;

  data["temp"] = Tmp/340.00+36.53;

  gyro["gyroX"] = GyX;
  gyro["gyroY"] = GyY;
  gyro["gyroZ"] = GyZ;
}

/*
 * função que envia os dados do sensor para o servidor em formato JSON
 * faz um POST request ao servidor 
 */
void makePOST()
{
  if(!client.connect(rpiHost, 3000))     // aqui conectamos ao servidor
  {
    Serial.println("Não foi possível conectar ao servidor!\n");
  }
  else
  {    
    Serial.println("Conectado ao servidor");
    // Make HTTP POST request    
    client.println("POST /accel HTTP/1.1");
    client.println("Host: 192.168.0.24");
    client.println("Content-Type: application/json");
    client.print("Content-Length: ");
    client.println(object.measureLength());
    client.println();
    object.printTo(client);    // enviamos os dados em JSON    
  }
}

/*
 * Função que realiza GET request no site ip-api.com
 * Esse site disponibiliza uma API de geolocalização via IP
 * A função retorna um JSON com dados de geolocalização
 * Os dados de geolocalização são exibidos na página web em um Google Maps
 */
String makeGETlocation()
{
  if ( !client.connect(IpApiHost, 80) ) {
    Serial.println("connection ao ip-api.com falhou");
    return "connection failed";
  }
  
  // Realiza HTTP GET request
  client.println("GET /json/?fields=8405 HTTP/1.1");
  client.print("Host: ");
  client.println(IpApiHost);
  client.println("Connection: close");
  client.println();

  // recebe o Header de resposta
  while (client.connected()) {
    String data = client.readStringUntil('\n');
    if (data == "\r") {
      break;
    }
  }
  // recebe os dados de geolocalização em formato JSON e guarda na variável data
  String data = client.readStringUntil('\n');
  Serial.println("Dados de geolocalização recebidos\n");
  Serial.println(data);  
  return data; 
}

/*
 * Função que envia ao servidor a localização do NodeMCU
 * função realiza um POST request ao servidor no link /location
 * o servidor por sua vez exibe a localização do NodeMCU no Google Maps
 */
void makePOSTlocation()
{
  String location = makeGETlocation(); // guarda o JSON de geolocalização na variável location
  if(!client.connect(rpiHost, 3000))     // aqui conectamos ao servidor
  {
    Serial.print("Could not connect to host: \n");
    Serial.print(rpiHost);
  }
  else
  {
    // realiza HTTP POST request    
    client.println("POST /location HTTP/1.1");
    client.println("Host: 192.168.0.24");
    client.println("Content-Type: application/json");
    client.print("Content-Length: ");
    client.println(location.length());
    client.println();
    client.println(location);    // enviamos o JSON ao servidor
  }
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);

  Serial.println("\nIniciando configuração WiFi\n");
  initWiFi();

  Serial.println("\nIniciando configuração do MPU6050\n");
  initI2C();
  initMPU();
  checkMPU(MPU_ADDR);

  Serial.println("Enviando localização ao servidor\n");
  makePOSTlocation();

  Serial.println("\nConfiguração finalizada, iniciando loop\n");  
}

void loop() {
  readRawMPU();    // lê os dados do sensor
  populateJSON();  // transforma os dados em formato JSON
  makePOST();      // envia os dados ao servidor  
  delay(100);  
}

 

Conclusão

Neste artigo foi apresentado um sistema de monitoramento remoto de um acelerômetro com ESP8266 NodeMCU usando um sensor MPU6050, cobrindo temas teóricos como comunicação I2C e o modo de comunicação do MPU6050. Fizemos com que a placa Wifi NodeMCU se portasse como um Web Client, enviando as leituras do MPU6050 a um servidor através de HTTP requests como também seus dados de geolocalização.

Gostou do post do acelerômetro com ESP8266 NodeMCU? Ajude-nos a melhorar o blog atribuindo uma nota a este tutorial (estrelas no final do artigo), comente e visite nossa loja FILIPEFLOP !

3
Como usar acelerômetro com ESP8266 NodeMCU
6 votos, 5.00 classificação média (100% pontuação)

Técnico em Mecatrônica pelo SENAI, onde teve os primeiros contatos com microcontroladores, eletrônica e programação. Graduação em Engenharia de Controle e Automação. Integrante do Depto. técnico da FILIPEFLOP.

Compartilhe este Post

11 Comentários

  1. Armando - 20 de abril de 2017

    Giovanni a parte do Raspberry não esta incluída neste seu artigo ? Teria como enviar pelo email? Eu achei muito bom este seu artigo parabéns.

    Abraços.

  2. Roberto Carlos - 21 de fevereiro de 2017

    Bom dia.
    Gostaria de uma dica de como criar um código entre dois ESP8266, sendo assim:
    ESP8266_(número-1), com dois botôes envia comando para o ESP8266_9número-2) ligar dois leds ou relay.
    Os dois se conectando e responde automaticamente, quando estiverem no raio de alcance.
    Esta uma ideia para fazer um controle remoto com dois ESP8266.

    Desde já obrigado

  3. Roberto Carlos - 20 de fevereiro de 2017

    TUNIOT é um gerador de código de bloco para NODEMCU.
    http://easycoding.tn/

  4. JAGoris - 7 de fevereiro de 2017

    Caro Bauermeister, Muito bom o seu tutorial! Enfim todas as informações para configurar a IDE do arduino reunidas num só artigo bem claro.
    Tenho uma dúvida que gostaria que esclarecesse: Pelo que entendi, o servidor deverá ser instalado em um Raspberry Pi-3. Se assim for, me parece que isto é um complicador, pois a grande maioria dos usuários de arduino, assim como eu, não tem esta plataforma por ser de alto custo. Não seria melhor optar por instalar o Web Server em um PC ou em Note book?
    Fica a sugestão para próximo tutorial!

    • Giovanni Bauermeister - 8 de fevereiro de 2017

      Olá JAGoris, obrigado pela leitura e sugestões!
      Foi usada uma Raspberry Pi 3 apenas por conveniência e por se tratar de uma solução embarcada onde poderia colocar a Raspberry Pi em uma caixinha ao invés de um computador ligado 24/7 apenas para tarefa de servidor. Mas o servidor pode ser executado em computadores com Linux ou Windows bastando apenas instalar o Node.js e os módulos corretamente para cada plataforma. Existem outros métodos de comunicação com servidores como MQTT que também poderia ser usado em uma solução como essa.

    • Angelo Roncali - 8 de fevereiro de 2017

      Gostei de sua sugestão sobre o WebServer.

  5. Gabriel Martins - 7 de fevereiro de 2017

    Muito bom!!!
    A biblioteca de Kalman pra arduino(https://github.com/TKJElectronics/KalmanFilter), que eh usada para achar o angulo com o mpu6050, ja esta funcionando com o esp8266 e outros arduinos de 32bits.
    Seria bacana se vc pudesse fazer um post usando-a para mostrar o angulo via web.

Deixe uma resposta