Introduction: ESP32: SIM800L and Barrier Sensor
Today I’ll discuss an assembly that functions as an alarm and automation together, using the SIM800L. The modem is GPRS and needs a SIM card. We can say this is like a "cell phone," but it’s very cool. It’s extremely cheap and allows you to develop amazing projects. I'll introduce you to the assembly and source code with ESP32, SIM800L, and a barrier sensor, in addition to making calls and sending alerts via SMS to smartphones.
Step 1: Demonstration
Step 2: Assembly
Step 3: Resources Used
· ESP32 - WROOM
· SIM800L
· 1.8 '' TFT display
· Barrier sensor
· 2 Relay Module
· 10k ohm resistor
· 4.1V and 5V power supply
· Jumpers
· 1x SIM card with SMS plan for Smartphone
· 1x SIM card with SMS plan and connection to SIM800L
· Smartphone
Step 4: Pinout ESP32
Step 5: Pinout SIM800L
Step 6: Assembly
* Leave the GND in common
Step 7: Assembly - Table
Step 8: Code
Step 9: ESP32 Code - Declarations and Variables
#include <Arduino.h> //biblioteca arduino (opcional)
#include <Adafruit_GFX.h> //biblioteca do display grafico #include <Fonts/FreeSans9pt7b.h> //fonte usada no display #include <Adafruit_ST7735.h> // biblioteca de hardware do display #include <SPI.h> // biblioteca de comunicação SPI #define TINY_GSM_MODEM_SIM800 // definição do modem usado (SIM800L) #include <TinyGsmClient.h> // biblioteca com comandos GSM // pinos display #define TFT_CS 22 // CS #define TFT_RST 21 // RESET #define TFT_DC 5 // A0 #define TFT_MOSI 23 // SDA #define TFT_CLK 18 // SCK // objeto do display Adafruit_ST7735 display = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST); // tamanho da fonte do display int fontHeight = 12; // objeto de comunicação serial do SIM800L HardwareSerial SerialGSM(1); // objeto da bibliteca com as funções GSM TinyGsm modemGSM(SerialGSM); // velocidade da serial tanto do SIM800L quanto do monitor serial const int BAUD_RATE = 9600; // variáveis usadas para contar o tempo sem travar a função loop // millis de referencia long int millisRefCon, millisUserResp; // flag que indica a contagem de tempo (usadas pela função 'timeout') bool flagCon = false, flagUserResp = false; // pinos aonde os reles serão ligados e RX / TX aonde o SIM800L será ligado const int relayPin1 = 17, relayPin2 = 15, sensorPin = 16, RX_PIN = 4, TX_PIN = 2; //Access point name da vivo const char *APN = "zap.vivo.com.br"; //Usuario, se não existir deixe em vazio const char *USER = ""; //Password, se não existir deixe em vazio const char *PASSWORD = ""; // as variáveis abaixo usadas pela função loop // flag que indica se, após a ligação feita pelo SIM800L, um usuario respondeu com um SMS em até 1min bool userResponseSMS = false; // flag que indica se o sensor está ativo bool sensorActivated = false; // index do vetor de numeros de celular, usado para percorrer o vetor int i = 0; // quantidade de celulares que receberão mensagens e ligações e poderão enviar comandos SMS const int numbersTL = 2; // numero de celulares, a ordem de chamada pelo programa é da esquerda para a direita const String numbers[numbersTL] = {"+5518999999999", "+5518999999999"};
Step 10: ESP32 Code - Setup
void setup()
{ Serial.begin(BAUD_RATE); Serial.println("Starting..."); // seta pinos do sensor como entrada pinMode(sensorPin, INPUT); // seta pinos dos reles como saída pinMode(relayPin1, OUTPUT); pinMode(relayPin2, OUTPUT); // os reles trabalham com lógica inversa, setamos como HIGH para desligá-los de início digitalWrite(relayPin1, HIGH); digitalWrite(relayPin2, HIGH); // atribui para as variáveis de contagem de tempo o tempo atual antes de entrar no loop millisRefCon = millisUserResp = millis(); display.initR(INITR_BLACKTAB); resetDisplay(); // inicia e configura o SIM800L setupGSM(); resetDisplay(); display.println("GPRS: Connected"); }
Step 11: ESP32 Code - SetupGSM
// inicializa GSM
void setupGSM() { display.println("Setup GSM..."); display.setTextColor(ST7735_GREEN); // inicia serial SIM800L SerialGSM.begin(BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN, false); delay(3000); // exibe info do modem no monitor serial Serial.println(modemGSM.getModemInfo()); // inicia o modem if (!modemGSM.restart()) { display.setTextColor(ST7735_RED); display.println("Restarting GSM\nModem failed"); delay(10000); ESP.restart(); return; } display.println("Modem restart OK"); // aguarda network if (!modemGSM.waitForNetwork()) { display.setTextColor(ST7735_RED); display.println("Failed to connect\nto network"); delay(10000); ESP.restart(); return; } display.println("Modem network OK"); // conecta na rede (tecnologia GPRS) if(!modemGSM.gprsConnect(APN,USER,PASSWORD)) { display.setTextColor(ST7735_RED); display.println("GPRS Connection\nFailed"); delay(10000); ESP.restart(); return; } display.println("GPRS Connect OK"); //Define modo SMS para texto (0 = PDU mode, 1 = Text mode) if(sendAT("AT+CMGF=1").indexOf("OK") < 0) { display.setTextColor(ST7735_RED); display.println("SMS Txt mode Error"); delay(10000); ESP.restart(); return; } display.println("SMS Txt mode OK"); //Exclui todos SMS armazenados sendAT("AT + CMGD=1,4"); resetDisplay(); display.setTextColor(ST7735_WHITE); }
Step 12: ESP32 Code - SendAT and ResetDisplay
//Envia comando AT e aguarda até que uma resposta seja obtida
String sendAT(String command) { String response = ""; SerialGSM.println(command); // aguardamos até que haja resposta do SIM800L while(!SerialGSM.available()); response = SerialGSM.readString(); return response; } // limpa e configura display void resetDisplay() { display.setRotation(1); display.setFont(&FreeSans9pt7b); display.fillScreen(ST77XX_BLACK); display.setTextColor(ST7735_WHITE); display.setCursor(0,fontHeight); }
Step 13: ESP32 Code - Loop
void loop()
{ String msg, number; // de 5 em 5 segundos, verifica se o SIM800L está desconectado, se sim, tenta reconectar if(timeout(5000, &millisRefCon, &flagCon)) verifyGPRSConnection(); // se o SIM800L está conectado if(modemGSM.isGprsConnected()) { // função que verifica se deve-se efetuar a chamada ou não if(isItToCall()) { // sinaliza que o sensor foi ativado sensorActivated = true; userResponseSMS = false; // atribui à variavel de referencia para contar o tempo, o valor atual do millis millisUserResp = millis(); Serial.println("Sensor activated!"); Serial.println("Calling to number "+String(i+1)); display.println("Sensor activated!"); display.println("Calling to number "+String(i+1)); // efetua a ligação para um dos nºs do vetor, iniciando com 0 call(numbers[i++]); // após a chamada soma-se 1 ao i // se chegou ao fim do vetor, retorna ao início (0) if(i>=numbersTL) i = 0; } // verifica se foi recebido um SMS if(SMSMessageRecv(&msg, &number)) { // exibe mensagem no display e monitor serial resetDisplay(); display.println("SMS Msg Received"); Serial.println("SMS Msg Received"); delay(2500); // validamos o SMS e executamos uma ação executeCommand(number, msg); } } else // exibe na serial que o modem está desconectado Serial.println("Disconnected"); // único delay no loop de 10ms (desconsiderando a função de reconexão, que possui delay para exibição do display) delay(10); }
Step 14: ESP32 Code - Timeout
// Função que compara se o tempo foi atingido, sem que 'congele' a execução do loop
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; }
Step 15: ESP32 Code - VerifyGPRSConnection
// verifica se o SIM800L se desconectou, se sim tenta reconectar
void verifyGPRSConnection() { resetDisplay(); display.print("GPRS: "); if(modemGSM.isGprsConnected()) display.println("Connected"); else { display.println("Disconnect"); display.println("Reconnecting..."); if(!modemGSM.waitForNetwork()) { display.setTextColor(ST7735_RED); display.println("GPRS Con. Failed"); delay(5000); display.setTextColor(ST7735_WHITE); } else { if(!modemGSM.gprsConnect(APN,USER,PASSWORD)) { display.setTextColor(ST7735_RED); display.println("GPRS Con. Failed"); delay(5000); display.setTextColor(ST7735_WHITE); } else { display.setTextColor(ST7735_GREEN); display.println("GPRS Con. OK"); } } } }
Step 16: ESP32 Code - IsItToCall and Call
bool isItToCall()
{ // se o sensor de barreira está ativo ou foi uma vez ativado e não foi recebido um SMS em 1 min, retornamos true indicando que deve-se ligar parao próx número return digitalRead(sensorPin) == HIGH || (sensorActivated && !userResponseSMS && timeout(60000, &millisUserResp, &flagUserResp)); } // executa chamada void call(String number) { display.print("Calling..."); Serial.println(number); // tenta executar chamada bool res = modemGSM.callNumber(number); // se obteve sucesso exibe OK, se não, fail if(res) display.println(" OK"); else display.println(" fail");
Step 17: ESP32 Code - Call (continued) and SMSMessageRecv
if (res)
{ // assim que a chamada for feita é finalizada res = modemGSM.callHangup(); // exibe se foi possível finalizar ou não display.print("Hang up: "); if(res) display.println("OK"); else display.println("fail"); } } // verifica se um sms é recebido e obtem o número de quem o enviou bool SMSMessageRecv(String *msg, String *number) { // comando AT que lista todos os SMS armazenados *msg = sendAT("AT+CMGL=\"ALL\""); // se o SIM800L responder com SM, significa que um novo SMS acaba de chegar // então pedimos novamente que o SIM nos liste os SMS armazenados if((*msg).indexOf("+CMTI: \"SM\"")>=0) *msg = sendAT("AT+CMGL=\"ALL\"");
Step 18: ESP32 Code - SMSMessageRecv (continued)
// se a mensagem possui um OK e possui mais que 10 caracteres (existe pelo menos um SMS)
if((*msg).indexOf("OK")>=0 && (*msg).length()>10) { // exibe a mensagem na serial Serial.println(*msg); // obtém o número que nos enviou o SMS e exibe se obteve sucesso if(getSMSNumber(*msg, *&number)) { Serial.println("numero obtido: "+*number); return true; } else { Serial.println("Erro ao obter numero"); return false; } } // exibe na serial um ponto (debug) Serial.print("."); return false; }
Step 19: ESP32 Code - ExecuteCommand
void executeCommand(String number, String msg)
{ // se o número não é válido, exibe mensagem e não faz nada if(!numberIsValid(number)) { display.println("Number is not valid"); Serial.println("Number is not valid"); Serial.println(number); delay(2500); } else { // se o número é valido // obtem o texto do SMS recebido Serial.println("Msg: '"+msg+"'"); getTextSMS(&msg); Serial.println("Cmd: '"+msg+"'"); // executa comando de acordo com o texto recebido if(commandOK(number, msg)) { // sinaliza que o usuario respondeu com um SMS válido userResponseSMS = true; // retorna para false a flag que indica se sensor está ativo sensorActivated = false; display.println("Sending status"); Serial.println("Sending status"); // Envia o estado dos dois relés em um SMS sendResponse(number); } else { // se o comando é inválido, exibe no display // sinaliza que o usuario respondeu com um SMS inválido userResponseSMS = false; display.println("Cmd is not valid"); Serial.println("Cmd is not valid"); } } // exclui todos os SMS armazenados sendAT("AT + CMGD=1,4"); }
Step 20: ESP32 Code - GetTextSMS
// obtem texto da mensagem e o retorna por parâmetro
void getTextSMS(String *msg) { String aux; /* Exemplo de mensagmem: [ +CMGL: 1,"REC UNREAD","+5518999999999","","18/11/30,11:36:14-08" Hello OK ] */ //pula primeiro \n *msg = (*msg).substring((*msg).indexOf( "\n" )+1); //pula primeira linha: 'Cmd: +CMGL: 1,"REC UNREAD","+5518999999999","","18/11/30,11:04:30-08"' *msg = (*msg).substring((*msg).indexOf( "\n" )+1); aux = *msg; if(aux.length() <= 8) return; *msg = ""; // retira a substring "\r\n\r\nOK\r\n" (8 caracteres) for(int i=0; i
Step 21: ESP32 Code - CommandOK
// executa comando de acordo com a variavel 'msg'
bool commandOK(String number, String msg) { // flag que indica se entrou em algum if (indicando comando válido) bool smsOK = false; // verifica se a msg corresponde a algum dos comandos existentes e seta os pinos correspondentes // os reles possuem lógica inversa if(msg.equalsIgnoreCase("relay 1 on")) { digitalWrite(relayPin1, LOW); smsOK = true; } else if(msg.equalsIgnoreCase("relay 1 off")) { digitalWrite(relayPin1, HIGH); smsOK = true; } else if(msg.equalsIgnoreCase("relay 2 on")) { digitalWrite(relayPin2, LOW); smsOK = true; } else if(msg.equalsIgnoreCase("relay 2 off")) { digitalWrite(relayPin2, HIGH); smsOK = true; } else if(msg.equalsIgnoreCase("relays off")) { digitalWrite(relayPin1, HIGH); digitalWrite(relayPin2, HIGH); smsOK = true; } else if(msg.equalsIgnoreCase("relays on")) { digitalWrite(relayPin1, LOW); digitalWrite(relayPin2, LOW); smsOK = true; } else if(msg.equalsIgnoreCase("ok")) // comando OK, usado para sinalizar alarme falso smsOK = true; else if(msg.equalsIgnoreCase("hello")) // comando hello, usado para verificar o funcionamento { modemGSM.sendSMS(number, "Hello!"); smsOK = true; } else if(msg.equalsIgnoreCase("status")) // comando status que obtém os estados dos pinos smsOK = true; return smsOK; }