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