IOT - enviando dados via MQTT

Este artigo mostra como fazer um projeto básico de IOT (Internet das Coisas) utilizando a placa MBZ.
Os dados provenientes das portas analógicas A0, A1, A2 e A3 do Arduino (ATMega328P) serão enviados via serial para o ESP8266, que através do protocolo MQTT, os publicará no serviço IOT da Adafruit.




1) Criar conta no serviço IOT 

Para poder publicar os dados na nuvem, será necessário criar uma conta e configurar conforme detalhado abaixo:

1.1) crie uma conta no site iot.adafruit.com
1.2) crie 4 feeds com os seguintes nomes: A0, A1, A2 e A3
1.3) crie um dashboard chamado "mbz"
1.4) no dashboard mbz, crie 4 blocks do tipo gauge, com os seguintes titles: A0, A1, A2 e A3
1.5) configure os valores mínimos do gauge para 0 e o máximo para 255
1.6) instale na IDE do Arduino a biblioteca Adafruit MQTT Library

2) Limpar programa no ATMega328P

Para evitar erros de upload de programa para o ESP8266, é aconselhável limpar o programa no ATMega328P.

2.1) Mude os jumpers perto do conector FDTI para a posição "atmega":
    



2.2) Selecione no menu "Ferramentas" a placa "Arduino/Genuino Uno":

2.3) Crie um "programa em branco", selecionando "Novo" no menu "Arquivo", compile e faça o upload para o ATMega328P:



3) Carregar o programa no ESP8266

O programa mbz_mqtt_esp8266.ino recebe os dados do Arduino e faz a publicação.

3.1) Conecte uma fonte de alimentação externa

3.2) Selecione no menu "Ferramentas" a placa "Generic ESP8266 Module":

3.3) Mude os jumpers perto do conector FDTI para a posição "esp". Mude os jumpers perto do ESP8266 conforme a imagem abaixo:

3.4) Pressione o botão "ESP_reset"

3.5) Compile e faça o upload do programa abaixo para o ESP8266:

Programa mbz_mqtt_esp8266.ino
/*******************************************************************
  MBZ MQTT example (ESP8266)
  -----------------------------
  Este programa recebe os dados das portas analógicas do ATMEGA328P 
  e os envia para o serviço de IOT da Adafruit via protocolo MQTT 
********************************************************************/
#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

#define SSID "WIFI_SSID"           // id do roteador WIFI
#define PWD  "WIFI_PASSWORD"       // senha do roteador WIFI

#define SERVER "io.adafruit.com"   // servidor MQTT
#define PORT   1883                // porta do servidor
#define USR    "AIO_USER"          // usuário do IO Adafruit 
#define KEY    "AIO_KEY"           // chave de acesso do IO Adafruit

WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, SERVER, PORT, USR, KEY);

Adafruit_MQTT_Publish PIN_A0 = Adafruit_MQTT_Publish(&mqtt, USR "/feeds/A0");
Adafruit_MQTT_Publish PIN_A1 = Adafruit_MQTT_Publish(&mqtt, USR "/feeds/A1");
Adafruit_MQTT_Publish PIN_A2 = Adafruit_MQTT_Publish(&mqtt, USR "/feeds/A2");
Adafruit_MQTT_Publish PIN_A3 = Adafruit_MQTT_Publish(&mqtt, USR "/feeds/A3");

void setup() {
  Serial.begin(19200);

  // Conecta no WIFI
  Serial.println();
  Serial.print("Conectando no WIFI: ");
  Serial.println(SSID);

  WiFi.begin(SSID, PWD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("WiFi conectado");
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());
  Serial.println();
}

// variaveis dos sensores
int val_A0 = 0;
int val_A1 = 0;
int val_A2 = 0;
int val_A3 = 0;

void loop() {
  // verifica se recebeu dados pela serial
  if (getSerialData()) {
    if (mqtt.connect() == 0) {
      // envia dados para o servidor MQTT
      PIN_A0.publish(val_A0);
      PIN_A1.publish(val_A1);
      PIN_A2.publish(val_A2);
      PIN_A3.publish(val_A3);
    }
  }

  delay(100);
}

bool getSerialData() {
  String serialData;
  bool ret = false;

  if (Serial.available()) {
    // lê porta serial
    serialData = Serial.readString();
    
    // retorna 'ok' para sinalizar que recebeu os dados
    Serial.print("ok");
  }

  if (!serialData.equals("")) {
    val_A0 = getValue(serialData, "A0");
    val_A1 = getValue(serialData, "A1");
    val_A2 = getValue(serialData, "A2");
    val_A3 = getValue(serialData, "A3");
    ret = true;
  }

  return ret;
}

int getValue(String str, String tk) {
  String buff = "";
  int p0, p1;

  p0 = str.indexOf(tk);

  if (p0 != -1) {
    p0 = str.indexOf(":", p0) + 1;
    p1 = str.indexOf(";", p0);
    buff = str.substring(p0, p1);
  }

  return buff.toInt();
}

Lembre-se de mudar as variáveis SSID, PWD, USR e KEY para os valores correspondentes à sua configuração.

Você pode testar se a publicação dos dados no IO Adafruit está funcionando, digitando uma string no Monitor serial, equivalente a que o Arduino enviará: A0:55;A1:62;A2:64;A3:61;

3.6) Mude os jumpers perto do ESP8266 conforme a imagem abaixo: 




3.7) Pressione o botão "ESP_reset"

4) Carregar o programa no ATMega328P

O programa mbz_mqtt_atmega.ino lê os dados das portas analógicas A0, A1, A2 e A3 e os envia um uma mensagem ESP8266.

4.1) Selecione no menu "Ferramentas" a placa "Generic ESP8266 Module":


4.2) Mude os jumpers perto do conector FDTI para a posição "atmega":


4.3) Compile e faça o upload do programa abaixo para o ATMega328P:

Programa mbz_mqtt_atmega.ino
/*******************************************************************
  MBZ MQTT example (ATMEGA328P)
  -----------------------------
  Este programa lê dados das portas analógicas (A0, A1, A2 e A3) 
  e envia para o módulo ESP8266.
  O ESP8266 recebe os dados e os envia o serviço de IOT da Adafruit 
  via protocolo MQTT (Message Queuing Telemetry Transport
********************************************************************/
#include <SoftwareSerial.h>

#define rxPin 2
#define txPin 3
SoftwareSerial EspSerial(rxPin, txPin); // RX, TX

#define PIN_ENABLE_ESP8266 4

// variaveis dos sensores
int val_A0 = 0;
int val_A1 = 0;
int val_A2 = 0;
int val_A3 = 0;

void setup() {
  // seta a velocidade das portas seriais
  Serial.begin(19200);
  EspSerial.begin(19200);

  // inicializa o ESP8266
  Serial.println("Inicializando o ESP8266");
  pinMode(PIN_ENABLE_ESP8266, OUTPUT);
  digitalWrite(PIN_ENABLE_ESP8266, LOW);
  delay(200);
  digitalWrite(PIN_ENABLE_ESP8266, HIGH);
  delay(200);

  // seta as portas A0, A2, A2 e A3 para leitura
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  pinMode(A3, INPUT);
}

void loop() {
  // lê os valores das portas analógicas
  updateSensors();

  // envia os dados para o ESP8266
  sendSensorsData();

  // aguarda 5 segs para fazer a próxima transmissão,
  // pois a conta gratuita do Adafruit IO limita 
  // a publicação de no máximo 60 posts por minuto
  delay(5000);
}

void updateSensors() {
  val_A0 = readSensor(A0);
  val_A1 = readSensor(A1);
  val_A2 = readSensor(A2);
  val_A3 = readSensor(A3);
}

int readSensor(int pinSensor) {
  int t = 0;
  int v = 0;
  int readings = 10;

  // faz 10 leituras e tira a média dos valores
  for (int i = 0; i < readings; i++) {
    t += analogRead(pinSensor);
    delay(5);
  }

  // limita os valores entre 0 e 255
  v = map(t / readings, 0, 1023, 0, 255);

  return v;
}

void sendSensorsData()
{
  int timeout = 2000;
  String strBuffer = "";

  // envia os dados pela serial
  EspSerial.print("A0:"); EspSerial.print(val_A0); EspSerial.print(";");
  EspSerial.print("A1:"); EspSerial.print(val_A1); EspSerial.print(";");
  EspSerial.print("A2:"); EspSerial.print(val_A2); EspSerial.print(";");
  EspSerial.print("A3:"); EspSerial.print(val_A3); EspSerial.print(";");

  // espera por uma resposta do ESP8266
  long int time = millis();

  while ( (time + timeout) > millis())
  {
    if (EspSerial.available() > 0)
    {
      strBuffer = EspSerial.readString();
      break;
    }
    delay(1);
  }

  // verifica se o ESP8266 mandou uma mensagem 'ok'
  // indicando que recebeu os dados com sucesso
  if (strBuffer.equals("ok")) {
    Serial.println("Dados transmitidos com sucesso.");
  } else {
    Serial.println("Problemas na comunicação.");
  }
}

5) Testar a comunicação

5.1) Selecione no menu "Ferramentas" a opção "Monitor Serial"

5.2) Selecione a velocidade "19200"



5.3) Abra no seu browser o dashboard "mbz":


Agora você pode personalizar seu dashboard, configurando de acordo com as necessidades do seu projeto IOT.





4 comentários :

  1. Excelente post Marcelo Maximiano, sou do hardware mas muitas vezes tenho que programar e sofro bastante, essa tua publicação me salvou algumas coisas, modifiquei o código para utilizar em uma aplicação minha que comunica via serial, deu certo, mas eu gostaria de alimentar cada variável separadamente, porém com o teu código sempre que tento alimentar somente uma as outras zeram :/ , tem como resolver isso?
    Abraço e obrigado pelo post. :D

    ResponderExcluir
    Respostas
    1. Neste exemplo, o Arduino transmite simultaneamente os valores de 4 portas analógicas.
      E sendo assim, o ESP8266 espera receber esses valores em uma única transmissão.
      Para passar valores individualmente, você deve alterar do lado do Arduino, o método sendSensorData(), para receber como parâmetro o nome da variável (A0,A1,A2,A3) e o seu respectivo valor, e depois enviar somente esses dados:
      Exemplo:
      void sendSensorData(String nomeVar, String valorVar)
      {
      int timeout = 2000;
      String strBuffer = "";

      // envia os dados pela serial
      EspSerial.print(nomeVar + ":"); EspSerial.print(valorVar); EspSerial.print(";");

      // espera por uma resposta do ESP8266
      long int time = millis();
      ...

      E no lado do ESP8266, você deve alterar a função getSerialData() para procurar na string serialData pelo nome de cada variável que pode ser recebida (A0,A1,A2,A3), ler o valor dela e executar o publish só do que foi recebido.

      Obrigado pelo feedback!

      Excluir
  2. Obrigado pelo retorno Marcelo, minha falta de habilidade em programação me faz ter um trabalhão rsrsrs, fico só remendando códigos, estou usando assim no arduino para ele receber dados e envio os dados via pc(futuro tablet) para o arduino:

    void setup() {
    Serial.begin(57600);
    }
    void loop() {
    // variaveis dos sensores

    // verifica se recebeu dados pela serial
    if (getSerialData()) {

    if (val_A0 != 0){
    b0 = val_A0;}

    if (val_A1 != 0){
    b1 = val_A1;}

    if (val_A2 != 0){
    b2 = val_A2;}

    if (val_A3 != 0){
    b3 = val_A3;}

    if (val_A4 != 0){
    b4 = val_A4;}

    if (val_A5 != 0){
    b5 = val_A5;}

    if (val_A6 != 0){
    b6 = val_A6;}

    if (val_A7 != 0){
    b7 = val_A7;}

    Serial.println("===========");
    Serial.println(b0);
    Serial.println(b1);
    Serial.println(b2);
    Serial.println(b3);
    Serial.println(b4);
    Serial.println(b5);
    Serial.println(b6);
    Serial.println(b7);
    Serial.println("===========");




    }
    delay(10);
    }

    bool getSerialData() {
    String serialData;
    bool ret = false;
    if (Serial.available()) {
    // lê porta serial
    serialData = Serial.readString();
    // retorna 'ok' para sinalizar que recebeu os dados
    Serial.println("ok");
    }

    if (!serialData.equals("")) {
    val_A0 = getValue(serialData, "A0:");
    val_A1 = getValue(serialData, "A1:");
    val_A2 = getValue(serialData, "A2:");
    val_A3 = getValue(serialData, "A3:");
    val_A4 = getValue(serialData, "A4:");
    val_A5 = getValue(serialData, "A5:");
    val_A6 = getValue(serialData, "A6:");
    val_A7 = getValue(serialData, "A7:");

    ret = true;
    }
    return ret;
    }

    int getValue(String str, String tk) {
    String buff = "";
    int p0, p1;
    p0 = str.indexOf(tk);
    if (p0 != -1) {
    p0 = str.indexOf(":", p0) + 1;
    p1 = str.indexOf(";", p0);
    buff = str.substring(p0, p1);
    }
    return buff.toInt();
    }

    A ideia é controlar um equipamento conectado ao arduino via tablet, o software do arduino está pronto e só quero alimentar suas variáveis e verificar alguns parâmetros. Teu exemplo foi o único que encontrei que lê mais de um valor pela serial, conforme acima consegui o problema de alimentar separadas, agora não sei porque se desconecto a serial e conecto denovo os valores zeram :/... Mas não vou desistir não :D

    ResponderExcluir
  3. Olá,
    Muito bom o seu passo a passo. Já tentou fazer o mesmo mas sem o Arduino? Digo utilizando o próprio ESP como microcontrolador?

    ResponderExcluir