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();
}









































1 Comentários
Esse arquivo ino já é o suficiente pro rastreador funcionar?
ResponderExcluir