banner

Rastreamento por GPS




Pessoal, hoje volto a falar sobre geolocalização, mas, desta vez, controlando o módulo SIM808 via UART com comandos AT usando ESP32 LoRa. Vamos obter localização GPRS e enviar para o Ubidots IoT Platform via MQTT utilizando a rede GSM.





VALORES EXIBIDOS NO DISPLAY

Ts: Timestamp
NT: Network (GPS)
La: Latitude
Lo: Longitude
Last Refresh: Data/Hora do último envio
S: Status de envio Y/N




RECURSOS USADOS

  • ESP32 LoRa
  • SIM808 Module GSM GPRS GPS Development Board
  • SIM card (com crédito)
  • Regulador De Tensão (Conversor DC-DC)
  • Fonte/Bateria 5V
  • Jumpers
  • Protoboard





FUNCIONAMENTO DO PROJETO

Leitura De Localização Gps

Fluxo de dados



Envio de dados de localização para o Ubidots

Fluxo de dados
O envio de dados de localização para o Ubidots é feito via MQTT. Utilizaremos a função mqtt.publish(TOPICO, MENSAGEM); da biblioteca TinyGsmClient, que funciona junto com a PubSubClient.
Conforme listado no slide de Recursos Usados, é necessário um SIM card, pois é ele (junto ao módulo SIM) que disponibiliza a rede GSM/GPRS por onde os dados são transmitidos.





MONTAGEM






ESP32 LORA PINOUT





SIM808 DEV BOARD





Ligar SIM808 assim que conectado na fonte de energia

Para que o módulo funcione sem que seja necessário apertar o botão Power, solde um jumper conforme a imagem:


Inserindo SIM card

Insira o SIM card no soquete conforme as imagens





UBIDOTS IOT PLATFORM

Nova conta

Crie uma conta no ubidots, no vídeo Introdução ao protocolo MQTT com Ubidots – ESP32
é ensinado o passo a passo. Link: https://www.youtube.com/watch?v=D0CqWpeZ0yY


Obtendo dados MQTT - Token

Clique no nome de sua empresa e em API Credentials


Obtendo dados MQTT – Device ID e Topic

Selecione o dispositivo

Os valores de Device ID e o tópico são escritos conforme as imagens. Mais detalhes sobre o tópico no próximo slide.


Map Widget

Adicione um novo widget conforme as imagens

Adicione um novo widget conforme a imagem

Após criar o map widget, ele será adicionado ao dashboard. Assim que um novo registro é inserido na base de dados do ubidots (enviado pelo ESP32/SIM808)  o local referente aos dados é exibido conforme a imagem.


Dados recebidos





CÓDIGO

Bibliotecas necessárias

  • TinyGsmClient
  • PubSubClient
Download:

Obs: A biblioteca SSD1306.h referente ao OLED Display do ESP32 LoRa está anexada junto ao projeto.

Configurações

Importante
Altere o tamanho do buffer MQTT_MAX_PACKET_SIZE para 258 em PubSubClient.h conforme a imagem:


Fluxograma



Declarações e Variáveis

#include "GSMLocation.h" // Biblioteca com funções de localização utilizando a rede GSM
#include "displayESP32LoRa.h" // Biblioteca que contém funções relacionadas ao display LoRa (SPI)
#include "GSM_MQTTClient.h" // Biblioteca que contém funções relacionadas a comunicação MQTT com o Ubidots platform
#include <HardwareSerial.h> // Comunicação UART com o módulo SIM808

HardwareSerial mySerial(1); // Objeto referente a comunicação UART com o módulo SIM808

// Delay de envios mqtt e intervalo de reconexões mqtt
const int DELAY_MQTT_SEND = 2000, DELAY_MQTT_RECONNECT = 30000;

// Variáveis auxiliares usadas para calcular milissegundos 
long int previousMillis_Loop = 0, previousMillis_MQTTreconnect = 0;

// Flags que sinalizam a contagem de tempo de envio de dados por mqtt e reconexão mqtt
bool waitingDelay_Loop = true, waitingDelay_mqttReconnect = false;
// Tempo que é aguardado após o envio de um comando AT, para a função "sendAT"
// Ou até que uma resposta seja recebida
const int TIMEOUT_AT = 2000;
// Bits/segundo de transmissão serial entre SIM808 e ESP32 (e também para o monitor serial)
const int BAUD_RATE = 115200;

// Pino serial RX que deve ser conectado no TX do SIM808, usado para configurar a variável "mySerial"
const int RX_PIN = 21;
// Pino serial TX que deve ser conectado no RX do SIM808, usado para configurar a variável "mySerial"
const int TX_PIN = 22;

// Timestamp obtido pela rede GSM do módulo SIM808 e é usado para calcular data/hora
long int timestamp;

// Variável auxiliar usada para calcular o tempo percorrido, com base no "timestamp" obtido
int previousMillis_lastSent;

// Variável exibida no display que informa sucesso ou falha de envio mqtt
String mqttSendSuccess = "-";

String sendAT_WaitResponse(String);
String sendAT(String);



Setup


void setup()
{
  long prev_millis;
  int diff;  

  // Configura Display LoRa
  if(!loraDisplayConfig())
    ESP.restart();

  serialConfig();

  Serial.print("Waiting... ");
  waitSIM808();  
  Serial.println("ok");
  
  // Exibe no display, linha 0, mensagem entre aspas e true/false para limpar ou não o display
  showDisplay(0, "Setting baud rate...", true);

  // Configura baud rate serial do ESP32 e do SIM808
  if(!baudConfig())
    ESP.restart();




  // Exibe no console e no display
  Serial.println(String(BAUD_RATE)+" OK");

  showDisplay(0, String(BAUD_RATE)+" OK", true);    
  showDisplay(1,"Setting modem...", false);

  // Inicializa modem
  if(!modemConfig(mySerial))
  {
    // Exibe no console e no display
    Serial.println("Modem init fail");
    showDisplay(0, "Modem init fail", true);
    delay(5000);
    ESP.restart();
  }
  // Exibe no console e no display
  showDisplay(0, "Modem ok", true);
  Serial.println("Modem ok");  
  showDisplay(1, "Getting timestamp (GSM)", false);
  Serial.println("Getting timestamp (GSM)");




  // Obtém data/hora da rede GSM e converte para timestamp
  if(!gettimestamp())
  {
    // Exibe no display
    showDisplay(0, "Fail to obtain timestamp (GSM)", true);
    Serial.println("Fail to obtain timestamp (GSM)");
    ESP.restart();
  }

  prev_millis = millis();

  // Exibe no console e no display
  Serial.println("Timestamp ok:"+String(timestamp));
  showDisplay(0, "Timestamp ok:"+String(timestamp), true);

  Serial.println("Starting GSM MQTT...");
  showDisplay(1, "Starting GSM MQTT...", false);



  // Inicializa GSM MQTT
  if(!initGSM_MQTT())
  {
    // Exibe no console e no display
    Serial.println("GSM MQTT Client init fail");
    showDisplay(0, "GSM MQTT Client init fail", true);
    delay(5000);
    ESP.restart();
  }
  
  // Exibe no console e no display
  showDisplay(0, "GSM MQTT Client init ok", true);
  Serial.println("GSM MQTT Client init ok");  
  
  Serial.print("Setting GSM Location... ");  
  showDisplay(0, "Setting GSM Location...", true);  

  if(!GSMLocationInit(mySerial))
  {
    Serial.println("error");   
    showDisplay(0, "GSM Location error", true);     
    delay(5000);
    ESP.restart();
  }
  Serial.println("ok");



  showDisplay(0, "GSM Location OK", true);     
  showDisplay(1, "Loading Gps...", false);

  Serial.println("Loading Gps...");
  
  // Configura GPS com comandos AT
  if(!gpsConfig())
  {
    // Exibe no console e no display
    Serial.println("GPS init fail");
    showDisplay(0, "GPS init fail", true);
    delay(5000);
    ESP.restart();
  }

  // Exibe no console
  Serial.println("GPS OK");

  previousMillis_MQTTreconnect =  millis();  
  diff = (millis()-prev_millis)/1000;   // diferença em segundos
  timestamp += diff;
}



Loop


void loop()
{  
  if(timeout(DELAY_MQTT_SEND, &previousMillis_Loop, &waitingDelay_Loop))// Se atingiu timeout
  {    
    if(!mqtt.connected())
      reconnectMQTT();
    
    Serial.println("Actual data");
    sendValues();
  }
}


SendValues


void sendValues()
{
  String response = "";
  String latitude, longitude;

  // Envia comando AT referente a localização GPS e aguarda resposta
  response = sendAT_WaitResponse("AT+CGNSINF");
  
  // Verificação se a resposta está relacionada com GPS 
  if(response.indexOf("+CGNSINF") >= 0)
  {
    // Obtém substrings com os dados de localização, exibe no display 
    getLatitudeLongitude(response, &latitude, &longitude);

    // Se os dados forem válidos
    if(latitudeLongitudeValid(latitude, longitude))
    {
      // Envia para o ubidots via mqtt      
      Serial.println("[GPS] lat:"+latitude+", lgn:"+longitude);
      showValuesToDisplay(latitude, longitude, "GPS");    
      sendToCloud(latitude, longitude, String(timestamp), "GPS");
    }
    else // Se os dados forem inválidos
    {
      // Envia vazio para o ubidots via mqtt
      sendToCloud("", "", String(timestamp), "-");
    }
  }
}


sendToCloud


void sendToCloud(String latitude, String longitude, String _timestamp, String networkData)
{  
  String json;
  long int diff;
  
  // Se a latitude estiver vazia, envia sem os valores, isso indica que o json foi enviado com sucesso mas não foi possível obter os valores de GPS
  // Caso sejam enviados valores vazios, exemplo:
  // {"location": {"value":1, "context":{"lat": , "lng": ,"time":"1533672721"}}}
  // A localização será definida como 0,0 e no Dashboard o dispositivo aparecerá nesta localização, que não é o que queremos...
  if(latitude.equals(""))
    json = "{\"location\": {\"value\":1, \"context\":{ \"time\":\""+_timestamp+"\"}}}";
  else // Se a latitude não estiver vazia, envia os valores normalmente
    json = "{\"location\": {\"value\":1, \"context\":{\"lat\": "+ latitude +",\"lng\": "+ longitude +",\"time\":\""+_timestamp+"\", \"nt\":\""+networkData+"\"}}}"; 

  Serial.println(json);
    
  // Calcula o tempo que passou e acrescenta no timestamp
  diff = (millis()-previousMillis_lastSent)/1000;  
  timestamp += diff;




  // Se foi enviado com sucesso
  if(mqttSend(json))
  {
    // Obtém tempo após o envio, para efetuar o cálculo acima novamente
    previousMillis_lastSent = millis();
    Serial.println("MQTT sent");
    mqttSendSuccess = "Y";
  }
  else
  {    
    // Obtém tempo após o envio, para efetuar o cálculo acima novamente
    previousMillis_lastSent = millis();

    Serial.println("MQTT send error");  
    
    // Define caracter como N (no), que é exibido no display (em "showLastRefresh()") informando falha de envio mqtt
    if(mqttSendSuccess == "Y" || mqttSendSuccess == "-")
      previousMillis_MQTTreconnect = millis();

    mqttSendSuccess = "N";
    showValuesToDisplay(latitude, longitude, networkData);
  } 
}


Exibição no display

// Exibe no display a data e hora em que os últimos valores lat/lng válidos foram obtidos 
void showLastRefresh()
{
  showDisplay(4, "Last refresh:", false);
  // Exibe junto com a data/hora (separado por espaço) um caracter que informa se o mqtt foi enviado com sucesso ou não (Y/N)
  showDisplay(5, timeStamp_to_DateTime(timestamp)+" S:"+mqttSendSuccess, false);  
}

// Exibe no display os valores: Timestamp, latitude, longitude e Data/hora em que os últimos valores lat/lng válidos foram obtidos 
void showValuesToDisplay(String latitude, String longitude, String networkData)
{      
    Serial.println("Ts: " + String(timestamp));
    Serial.println("La: " + latitude);
    Serial.println("Lo: " + longitude);

    showDisplay(0, "Ts: " + String(timestamp)+" NT: "+networkData, true);
    showDisplay(1, "La: " + latitude, false);
    showDisplay(2, "Lo: " + longitude, false);        

    showLastRefresh();
}


Comunicação serial com o módulo SIM808

// Envia comando AT e aguarda resposta com timeout
String sendAT(String command)
{
  String response = "";    
  mySerial.println(command); 

  delay(5);
  long int time = millis();   
  while((time+TIMEOUT_AT) > millis())
  {
    while(mySerial.available())
      response += char(mySerial.read());
  }        
  
  return response;
}

// Envia comando AT e aguarda até que uma resposta seja obtida
String sendAT_WaitResponse(String command)
{
  String response = "";    
  mySerial.println(command); 

  delay(1000);
  while(response == "" || mySerial.available())
    response += char(mySerial.read()); 
  
  return response;
}


GetLatitudeLongitude

// Obtém substring latitude e longitude da resposta obtida pelo comando AT
void getLatitudeLongitude(String response, String *latitude, String *longitude)
{
  int i, count;
  *latitude = "";
  *longitude = "";

  for(i = 0, count = 0; i<response.length() && count<3; i++)
    if(response.charAt(i) == ',')
      count++;
 
  while(i<response.length()  && response.charAt(i)!=',')
  {
    *latitude+=String(response.charAt(i));
    i++;
  }

  for(i++; i<response.length()  && response.charAt(i)!=','; i++)
    *longitude+=String(response.charAt(i));

  if((*latitude).equals(""))
    *latitude = "-";

  if((*longitude).equals(""))
    *longitude = "-";
}


LatitudeLongitudeValid

// Verifica se a latitude e longitude são válidas
bool latitudeLongitudeValid(String latitude, String longitude)
{
  if(latitude.equals("") || latitude.equals("-") || longitude.equals("") || longitude.equals("-"))
    return false;

  return true;
}


ReconnectMQTT

// Re-estabelece conexão com ubidots por mqtt, de tempo em tempo
void reconnectMQTT()
{
  // Se o tempo timeout foi atingido
  if(timeout(DELAY_MQTT_RECONNECT, &previousMillis_MQTTreconnect, &waitingDelay_mqttReconnect))
  {
    // Exibe no display e no console
    Serial.println("Trying to reconnect MQTT");
    showDisplay(0, "Trying to reconnect MQTT", true);   
    showDisplay(1, "Waiting for network...", false);   

    if(!networkConnect())
    {
      Serial.println("Network connect failed!");
      showDisplay(0, "Network connect failed!", true);
      previousMillis_MQTTreconnect = millis();
      return;
    }

    showDisplay(0, "Trying to reconnect MQTT", true);
    showDisplay(1, "Reconnecting...", false);

    // Inicializa GSM mqtt client
    if(!initGSM_MQTT())
    {
      Serial.println("Reconnect failed!");
      showDisplay(0, "Reconnect failed!", true);
      previousMillis_MQTTreconnect = millis();
      return;
    }
    
    // Exibe no display
    showDisplay(0, "Trying to reconnect MQTT", true);
    showDisplay(1, "Reconnecting MQTT...", false);

    // Reconecta mqtt
    if(!mqttReconnect())
    {      
      Serial.println("Reconnect failed!");
      showDisplay(0, "Reconnect failed!", true);
      previousMillis_MQTTreconnect = millis();
      return;
    }    

    // Exibe no display e no console
    Serial.println("OK! Getting location data...");
    showDisplay(0, "OK! Getting location data...", true);

    previousMillis_MQTTreconnect = millis();
    mqttSendSuccess = "-";
  } 
}


Timeout

// Função que compara se o tempo foi atingido, de acordo com os parâmetros recebidos
bool timeout(const int DELAY, long *previousMillis, bool *flag)
{
  if(*flag)
  {
    *previousMillis = millis();
    *flag = false;
  }  

  if((*previousMillis + DELAY) < millis())
  {
    *flag = true;
    return true;
  }

  return false;
}


gettimestamp e waitSIM808

// Obtém timestamp a partir da data obtida pela rede GSM
bool gettimestamp()
{
  timestamp = getDateTimeStampGSM_modem();
  // Assim que obteve o timestamp guarda o tempo para depois contar quanto tempo passou
  previousMillis_lastSent = millis();

  // Se não obteve o timestamp retorna falso
  if(timestamp == -1)
    return false;

  return true;
}

void waitSIM808()
{
  for(int n = 3; n>0; n--)
  {
    showDisplay(0, "Waiting "+String(n), true);
    delay(1000);
  }
}


baudConfig, serialConfig e gpsConfig

// Configura baud rate do SIM808
bool baudConfig()
{
  // Se obteve sucesso retorna true
  if(sendAT("AT+IPR="+String(BAUD_RATE)).indexOf("OK") >= 0)
      return true;

  return false;
}

void serialConfig()
{
  // Set console baud rate
  Serial.begin(BAUD_RATE);

  // Configura baud rate do ESP32 UART
  mySerial.begin(BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN); // esp32 lora 0 = RX, 22 = TX
}

// Configuração referente ao gps
bool gpsConfig()
{
  if(sendAT("AT+CGNSPWR=1").indexOf("OK") >= 0) // Liga fonte de energia do GNSS (1 = ligar, 0 = desligar)
    if(sendAT("AT+CGPSINF=0").indexOf("OK") >= 0) // Tenta obter pela primeira vez a localização GPS
      return true;
  
  return false;
}



CÓDIGO

Declarações e Variáveis

//#define TINY_GSM_MODEM_SIM800
#define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_XBEE

#include "dateTimeFuncions.h" //Biblioteca com as funções de data (parsing)
#include <PubSubClient.h> //Biblioteca para as publicações via mqtt
#include <TinyGsmClient.h> //Biblioteca que configura o modem GSM

//Dados utilizados para a conexão GPRS referentes ao access point, utilizando o modem do SIM808 basta deixá-los como vazio 
const char apn[]  = "claro.com.br"; //YourAPN
const char user[] = "claro";
const char pass[] = "claro";

//Detalhes do MQTT

//ID do dispositivo, obtido pelo Ubidots
const String DEVICE_ID = "----------------";

//Token, obtido pelo Ubidots
const String TOKEN = "-------------------";

//Servidor do Ubidots
const String SERVER = "things.ubidots.com";

//Porta padrão
const int PORT = 1883;

//Tópico referente ao dispositivo
//Deve-se respeitar o seguinte exemplo: v1.6/devices/{LABEL_OF_DEVICE}/{LABEL_OF_VARIABLE}
//Porém, para latitude e longitude não é necessário criar uma variável, e o valor de LABEL_OF_VARIABLE é omitido
const char* TOPIC = "/v1.6/devices/esp32-gps"; 

//Serial UART, conectado ao módulo SIM808
HardwareSerial Serial_SIM_Module(1);

//Objeto referente ao GSM
TinyGsm modem(Serial_SIM_Module);
TinyGsmClient client(modem);
PubSubClient mqtt(client);


initGSM_MQTT e mqttSend

//Inicia comunicação MQTT
bool initGSM_MQTT()
{
  //MQTT server setup
  mqtt.setServer(SERVER.c_str(), PORT);

  if(mqttConnect())
  {
    Serial.println("MQTT Connected");
    return true;
  }

  Serial.println("MQTT Connect fail");
  return false;
}

//Envia mensagem via mqtt
//Exemplo json: {"location": {"value":1, "context":{"lat":-22.999999, "lgn": -51.999999, time:2222222}}}
bool mqttSend(String msg)
{
  return mqtt.publish(TOPIC, msg.c_str());
}


mqttConnect e mqttReconnect

//Conecta no servidor mqtt
bool mqttConnect() 
{
  Serial.print("Connecting to ");
  Serial.print(SERVER);

  //Connect to MQTT server
  bool status = mqtt.connect(DEVICE_ID.c_str(), TOKEN.c_str(), "");
  
  if(status == false) 
  {
    Serial.println(" fail");
    return false;
  }

  Serial.println(" OK");
  return true;
}

//Reconecta mqtt
bool mqttReconnect()
{
  if(mqtt.connected())
    return true;
  
  if(mqttConnect())
    return true;
      
  return false;    
}


networkConnect e modemConfig

bool networkConnect()
{
  Serial.print("Waiting for network...");

  if (!modem.waitForNetwork()) 
  {
    Serial.println(" fail");
    return false;
  }

  Serial.println(" OK");

  //Conecta GPRS
  Serial.print("Connecting to ");
  Serial.print(apn);

  if (!modem.gprsConnect(apn, user, pass)) 
  {
    Serial.println(" fail");
    return false;
  }

  Serial.println(" OK");
  return true;
}

//Configura o modem GPRS
bool modemConfig(HardwareSerial mySerial)
{
  Serial_SIM_Module = mySerial;
  //Inicia modem
  Serial.println("Initializing modem...");  
  
  if(!modem.restart()) 
    return false;
    
  //Conecta na rede
  return networkConnect();
}


FAÇA O DOWNLOAD DOS ARQUIVOS:

PDF

INO

Nenhum comentário:

Tecnologia do Blogger.