Automation With SIM800L ESP32 M5Stack

About: Do you like technology? Follow my channel on Youtube and my Blog. In them I put videos every week of microcontrollers, arduinos, networks, among other subjects. New tutorials on Tuesdays and Fridays.

Today, we’re involved with SIM800L with M5Stack. This is an ESP32 that is already wrapped and encapsulated with a display. And if you don’t know the M5Stack, watch this video: ESP32 M5Stack with DHT22. And today’s example already has an SIM800L embedded in it. So with our assembly, it is possible to turn a lamp or fan on and off through an SMS message. And I can send this remotely, as there is no need for any application. This is a simple and reliable example of automation, and it does not require WiFi or LoRa.

Step 1: Demonstration

Step 2: Resources Used

  • ESP32 M5Stack Dev kit
  • SIM800L GSM module for M5Stack
  • 4-relay module
  • Smartphone
  • 2 Cell phone chips (with credit)
  • 1 Socket extension
  • 2 Lamps
  • Wires
  • Jumpers

Step 3: M5Stack Pinout

Step 4: M5Stack Pinout (External)

Step 5: SIM800L Chip

Insert a SIM Card into the SIM800L module, as shown.

Step 6: Assembly

Step 7: Code

Diagram

Step 8: Installing Libraries

M5Stack

https://github.com/m5stack/M5Stack

TinyGSMClient

https://github.com/vshymanskyy/TinyGSM

Arduino Core for ESP32

https://github.com/espressif/arduino-esp32

* The HardwareSerial library is part of the Arduino Core for the ESP32 library

Step 9: M5Stack Code - Declarations and Variables


#include <M5Stack.h> // Biblioteca do M5Stack
#include <HardwareSerial.h> //Biblioteca para comunicação Serial #define TINY_GSM_MODEM_SIM800 // Definição do tipo de modem usado (SIM800L) #include <TinyGsmClient.h> // Biblioteca GSM // Variáveis do display M5Stack #define WDT_SCR 320 // Tamanho #define HGT_SCR 240 // Altura #define WDT_SCR2 160 // Metade do tamanho #define HGT_SCR2 120 // Metade da altura // Celulares com permissões para enviar SMS const int sizeListNumbers = 2; const String numbers[sizeListNumbers] = {"+5518999999999", "+5518999999999"}; // Objeto de comunicação serial do SIM800L HardwareSerial SerialGSM(2); // Velocidade da serial do SIM800L (A velocidade do monitor serial é de 115200) const int BAUD_RATE = 9600; // Pinos dos relés const int relayPin1 = 21, relayPin2 = 22, relayPin3 = 19; // Objeto com as funções GSM TinyGsm modemGSM(SerialGSM); // Altura da fonte (FreeSerif24pt7b) 24 + 4 de espaçamento const int fontHeigth = 28; // Assinatura da função "M5StackButtonsRead" (para evitar erros de compilação) void M5StackButtonsRead(void *); // Rodaremos duas tasks, onde ambas utilizam os pinos dos relés // Essa flag indica se os pinos dos relés estão ocupados e evita que duas operações sejam feitas ao mesmo tempo bool pinsBusy = false; // Variáveis de referência contagem de tempo (millis), usados na nossa função de timeout long prevMillisStartScreen, prevMillisReadSerial; // Flags que indicam o início de contagem de tempo (millis), usados na nossa função de timeout bool flagStartScreen = false, flagReadSerial = false;

Step 10: M5Stack Code - Setup

void setup()
{ // Iniciamos o Objeto, desabilitando o SD, que não será usado M5.begin(true,false,true); //LCD, SD, Serial // Exibimos os textos "Relay 1", "Relay 2" e "Relay 3" // Referentes aos botões do M5Stack showLabelButtons(); // Setamos os relés (output) e criamos uma task de leitura dos botões inputOutputInit(); // Iniciamos o monitor serial e modemGSM com suas respectivas velocidades serialInit(); // Iniciamos o display displayInit(); // Reiniciamos o modem, informando que se for encontrado algum problema deve-se reiniciar o ESP modemRestart(true); // Exibe a tela inicial com a mensagem "Waiting for messages" showStartScreen(); // Setamos as flags de contagem de tempo como true flagStartScreen = flagReadSerial = true; }

Step 11: M5Stack Code - Loop

// OBS: Para atualizar os componentes do M5Stack é necessário chamar a função M5.update();
// Essa função já é chamada na task (core 0) 'M5StackButtonsRead', então não é necessário chamá-la novamente no loop void loop() { // A cada 300ms verificamos se existem novos SMS if(modemGSM.getSimStatus() == SIM_READY && timeout(300, &prevMillisReadSerial, &flagReadSerial)) { // Printamos um ponto (debug) indicando a frequencia em que a função "verifySMSMessages" é chamada Serial.print("."); verifySMSMessages(); } // A cada 5 segundos atualizamos o display com a tela inicial if(timeout(5000, &prevMillisStartScreen, &flagStartScreen)) { showStartScreen(); // Se o status do SIM não for "Ready" (pronto), reiniciamos o modem if(modemGSM.getSimStatus() != SIM_READY) modemRestart(false); } }

Step 12: M5Stack Code - InputOutputInit

// Iniciamos os pinos e a task que ficará varrendo os botões do M5Stack
// Os botões do M5Stack não precisam ser setados no pinMode void inputOutputInit() { pinMode(relayPin1, OUTPUT); pinMode(relayPin2, OUTPUT); pinMode(relayPin3, OUTPUT); // Digital write com flag (Os relés possuem lógica inversa) pinsControl(relayPin1, HIGH); pinsControl(relayPin2, HIGH); pinsControl(relayPin3, HIGH); // Iniciamos uma task que irá ler os botões xTaskCreatePinnedToCore( M5StackButtonsRead, //Função que será executada "M5StackButtonsRead", //Nome da tarefa 10000, //Tamanho da pilha NULL, //Parâmetro da tarefa (no caso não usamos) 2, //Prioridade da tarefa NULL, //Caso queira manter uma referência para a tarefa que vai ser criada (no caso não precisamos) 0); //Número do core que será executada a tarefa (usamos o core 0 para o loop ficar livre com o core 1) }

Step 13: M5Stack Code - M5StackButtonsRead

// Task que lê o botão a cada 10ms e controla os relés
void M5StackButtonsRead(void *p) { TickType_t taskDelay = 10 / portTICK_PERIOD_MS; //IMPORTANTE: SEMPRE DEIXAR UM LOOP COM UM DELAY PARA ALIMENTAR WATCHDOG while(true) { // Se o primeiro botão "Relay 1" foi pressionado, modamos o estado do relé 1 if(M5.BtnA.wasPressed()) pinsControl(relayPin1, !digitalRead(relayPin1)); // Se o segundo botão "Relay 2" foi pressionado, modamos o estado do relé 2 if(M5.BtnB.wasPressed()) pinsControl(relayPin2, !digitalRead(relayPin2)); // Se o terceiro botão "Relay 3" foi pressionado, modamos o estado do relé 3 if(M5.BtnC.wasPressed()) pinsControl(relayPin3, !digitalRead(relayPin3)); // Executamos um delay de 10ms, os delays executado nas xTasks são diferentes vTaskDelay(taskDelay); // Atualizamos o objeto M5 referente ao M5Stack M5.update(); //IMPORTANTE: SEMPRE DEIXAR UM DELAY PARA ALIMENTAR WATCHDOG } }

Step 14: M5Stack Code - PinsControl

// Função que verifica se o pino do relé passado por parâmetro está ocupado
// E evita que duas operações (uma de cada task) sejam feitas ao mesmo tempo void pinsControl(int pin, int val) { // Enquanto a flag estiver "true", não faz nada while(pinsBusy); // Seta a flag como true pinsBusy = true; // Executa o digitalWrite digitalWrite(pin, val); // Logo em seguida seta a flag como false pinsBusy = false; }

Step 15: M5Stack Code - SerialInit and DisplayInit

// Inicia o monitor serial e a serial do modem GSM
void serialInit() { Serial.begin(115200); SerialGSM.begin(BAUD_RATE); TinyGsmAutoBaud(SerialGSM); } // Configura o display com as cores de fundo, de texto e a fonte do texto void displayInit() { // Seta a tela de fundo como preta M5.Lcd.fillScreen(BLACK); // Seta a cor do texto como branca M5.Lcd.setTextColor(WHITE); // Seta a fonte do texto M5.Lcd.setFreeFont(&FreeSerif24pt7b); }

Step 16: M5Stack Code - ModemRestart

// Reiniciamos o modem
void modemRestart(bool restartESP) { // Reiniciamos o modem if(!gsmRestart(restartESP)) return; // Aguardamos a mensagem SMS Ready e Call Ready enviada pelo SIM800L if(!waitSMSCallReady(restartESP)) return; // Conectamos na rede gsm if(!waitNetwork(restartESP)) return;

Step 17: M5Stack Code - VerifySMSMessages

// Verificamos se existem mensagens não lidas
void verifySMSMessages() { // Recebemos todas as mensagens não lidas no buffer "msg" String msg = listSMSUnRead(); // Se existe pelo menos uma mensagem if(!msg.equals("")) { // Chamamos a função que separa as mensagems e executa uma ação de acordo com o texto de cada mensagem splitMsg(msg); // Setamos a variável de referencia de contagem de tempo com o tempo atual (millis) para que ela seja mostrada só após o timeout (5 segundos) prevMillisStartScreen = millis(); } }

Step 18: M5Stack Code - ListSMSUnread

// Enviamos um comando AT pedindo que o modem nos retorne uma lista de SMS ainda não lidos
String listSMSUnRead() { String msg = ""; // Enviamos um comando AT modemGSM.sendAT("+CMGL=\"REC UNREAD\"\r\n"); // Aguardamos uma resposta em até 5 segundos modemGSM.waitResponse(5000, msg); // Se não recebemos a resposta, aborta função if(msg.equals("")) return ""; //Serial.println("Response:["+msg+"]"); // Se a resposta recebida for uma mensagem de Erro //Exemplo de mensagem: /* AT+CMGL="REC UNREAD" ERROR */ if(msg.indexOf("AT+CMGL=\"REC UNREAD\"")>=0) { // Exibimos na serial Serial.println(msg); // Reiniciamos o modem // false = não reiniciará o ESP caso ocorra uma falha modemRestart(false); // Retornamos vazio return ""; } // Se a mensagem possuir "OK", "SMS Ready" ou "Call Ready", abortamos a função pois não é o SMS em si if((msg.indexOf("OK")<=4 && msg.indexOf("OK")>=0) || (msg.indexOf("Call Ready")<=4 && msg.indexOf("Call Ready")>=0) || (msg.indexOf("SMS Ready")<=4 && msg.indexOf("SMS Ready")>=0)) return ""; // Exibimos a mensagem na serial Serial.println(msg); // Retornamos a String msg return msg; }

Step 19: M5Stack Code - SplitMsg

// A função "splitMsg" percorre a String "fullMsg" com as mensagens não lidas (Exemplo abaixo) e obtém os respectivos nºs e texto das mensagens
/*+CMGL: 1,"REC READ","+5518999999999","","18/12/07,08:57:56-08" Relay 1 on +CMGL: 2,"REC UNREAD","+5518999999999","","18/12/07,08:57:59-08" Relay 2 on OK*/ void splitMsg(String fullMsg) { String msg, number, aux; // Exibe na serial (debug) Serial.println("Full msg:["+fullMsg+"]"); // Para retirarmos os \n do início do buffer, executamos este primeiro while // Enquanto o início da mensagem não for "+CMGL", pulamos uma linha while(!fullMsg.startsWith("+CMGL") && fullMsg.indexOf("\n")>=0) fullMsg = fullMsg.substring(fullMsg.indexOf("\n")+1); // Enquanto o início da mensagem não for "+CMGL", significa que é uma mensagem válida while(fullMsg.startsWith("+CMGL")) { // Obtém a pos da segunda vírgula int pos2ndComma = fullMsg.indexOf(",",fullMsg.indexOf(",")+1); // Obtém substring após a segunda vírgula aux = fullMsg.substring(pos2ndComma+1); // Obtém número que inicia após a primeira aspas e termina antes da segunda aspas number = aux.substring(1, aux.indexOf(",")-1); // Exibe o número de celular que nos enviou o SMS no monitor serial entre colchetes Serial.println("Number:["+number+"]"); // Pula a primeira linha fullMsg = aux.substring(aux.indexOf("\n")+1); // Obtém substring até o primeiro \n msg = fullMsg.substring(0, fullMsg.indexOf("\n")-1); // Exibe o texto da mensagem no monitor serial entre colchetes Serial.println("Msg:["+msg+"]"); // Se o número for válido if(numberIsValid(number)) { // Exibimos no display que um novo SMS chegou showDisplay("A new SMS Arrived!\n", true); showDisplay("\""+msg+"\"", false); // Chamamos a função "newSMS" que executará o comando de acordo com o texto guardado em "msg" newSMS(number, msg); } else { // Exibimos no display que o nº é inválido showDisplay("New SMS...\nInvalid number\n", true); Serial.println("New SMS...\nInvalid number"); } // Pulamos duas linhas para obter a próxima mensagem fullMsg = fullMsg.substring(fullMsg.indexOf("\n", fullMsg.indexOf("\n")+1)+1); } }

Step 20: M5Stack Code - NumberIsValid

// Verifica se o número é algum dos declarados pelo programa
bool numberIsValid(String number) { // Percorre vetor com os números e verifica se é a String é igual for(int i = 0; i < sizeListNumbers; i++) if(numbers[i].equals(number)) return true; return false; }

Step 21: M5Stack Code - NewSMS

// Executa uma ação de acordo com a variavel 'msg'
void newSMS(String number, String msg) { // "pinsControl" é uma função que é usada pela task core 0 (botão) e também pela task core 1 (loop) e evita que as duas acessem os pinos ao mesmo tempo if(msg.equalsIgnoreCase("relay 1 on")) { // Os reles possuem lógica inversa pinsControl(relayPin1, LOW); } else if(msg.equalsIgnoreCase("relay 1 off")) { pinsControl(relayPin1, HIGH); } else if(msg.equalsIgnoreCase("relay 2 on")) { pinsControl(relayPin2, LOW); } else if(msg.equalsIgnoreCase("relay 2 off")) { pinsControl(relayPin2, HIGH); } else if(msg.equalsIgnoreCase("relay 3 on")) { pinsControl(relayPin3, LOW); } else if(msg.equalsIgnoreCase("relay 3 off")) { pinsControl(relayPin3, HIGH); } else if(msg.equalsIgnoreCase("relays off")) { pinsControl(relayPin1, HIGH); pinsControl(relayPin2, HIGH); pinsControl(relayPin3, HIGH); } else if(msg.equalsIgnoreCase("relays on")) { pinsControl(relayPin1, LOW); pinsControl(relayPin2, LOW); pinsControl(relayPin3, LOW); } else // Faz com que o modem efetue uma ligação para o número que enviou o SMS if(msg.equalsIgnoreCase("call me")) { showDisplay("Calling...\n", true); if(modemGSM.callNumber(number)) { showDisplay("OK\n", false); Serial.println("OK\n"); } else { showDisplay("Failed\n", false); Serial.println("Failed\n"); } modemGSM.callHangup(); } else // Se recebermos um SMS "status", o modem enviará uma resposta SMS com os status dos relés if(msg.equalsIgnoreCase("status")) { String status = "Relay 1: "; // Obtém os status do relé 1 if(digitalRead(relayPin1) == LOW) status+="ON\n"; else status+="OFF\n"; status += "Relay 2: "; // Obtém os status do relé 2 if(digitalRead(relayPin2) == LOW) status+="ON\n"; else status+="OFF\n"; status += "Relay 3: "; // Obtém os status do relé 3 if(digitalRead(relayPin3) == LOW) status+="ON"; else status+="OFF"; // Exibições display e monitor serial showDisplay("Sending SMS...\n",true); Serial.println("Sending SMS..."); // Envia SMS if(modemGSM.sendSMS(number,status)) { showDisplay("OK", false); Serial.println("OK"); } else { showDisplay("Failed", false); Serial.println("Failed"); } } else // Se recebermos um SMS "delete all", o modem excluirá todos os SMS (lidos e não lidos) e enviará uma resposta SMS se a exclusão foi ou não possível if(msg.equalsIgnoreCase("delete all")) { String respMsg; // Deleta todos os SMS if(deleteALLSMS()) respMsg = "The messages have been deleted"; else respMsg = "Failed to delete messages"; // Exibições display e monitor serial showDisplay("Sending SMS...\n",true); Serial.println("Sending SMS..."); // Envia SMS if(modemGSM.sendSMS(number, respMsg)) { showDisplay("OK", false); Serial.println("OK"); } else { showDisplay("Failed", false); Serial.println("Failed"); } } }

Step 22: M5Stack Code - DeleteALLSMS

// Função que deleta todos os SMS
bool deleteALLSMS() { String resp = ""; // Enviamos um comando AT modemGSM.sendAT("+CMGD=1,4\r\n"); // Aguardamos uma resposta em até 3 segundos modemGSM.waitResponse(3000, resp); // Se a resposta conter um "OK" retornamos "true", se não retornamos "false" return resp.indexOf("OK")>=0; }

Step 23: M5Stack Code - GsmRestart

// Reinicia o modem, exibindo no monitor serial e no display o sucesso ou falha
bool gsmRestart(bool restart) { // Exibições display e monitor serial showDisplay("Restarting modem...\n", true); Serial.println("Restarting modem..."); // Se foi possível reiniciar o modem if(modemGSM.restart()) { // Exibições display e monitor serial (sucesso) M5.Lcd.setCursor(0,0); showDisplay("Modem ", true); M5.Lcd.setTextColor(GREEN); showDisplay("OK\n",false); M5.Lcd.setTextColor(WHITE); Serial.println("Modem ok"); return true; }

Step 24: M5Stack Code - WaitSMSCallReady

// Aguarda que a mensagem "SMS Ready" seja recebida do modem em até 10 segundos, exibindo no monitor serial e no display o sucesso ou falha
bool waitSMSCallReady(bool setup) { // Exibições display e monitor serial Serial.println("Waiting SMS/Call"); showDisplay("Waiting SMS/Call ", false); String msg = ""; long prevMillis = millis(); // Em até 10 segundos aguardamos a mensagem "SMS Ready" while(prevMillis + 10000 > millis() && msg.indexOf("SMS Ready")<0 a="" erialgsm.available="" foi="" if="" mensagem="" msg.indexof="" msg="SerialGSM.readString();" ready="" recebida="" se="">=0) { // Exibições display e monitor serial (sucesso) Serial.println(msg); M5.Lcd.setTextColor(GREEN); showDisplay("OK\n", false); M5.Lcd.setTextColor(WHITE); return true; } // Se a mensagem não foi recebida // Exibições display e monitor serial (falha) Serial.println("SMS/Call init failed"); M5.Lcd.setTextColor(RED); showDisplay("Failed\n", false); M5.Lcd.setTextColor(WHITE); // Se estamos ainda no setup, aguardamos 5 segundos e reiniciamos o ESP if(setup) { delay(3000); ESP.restart(); } return false; }

Step 25:

// Configuramos a mensagem SMS para modo texto no modem GSM
bool setSMSTextMode(bool restart) { String resp = ""; // Enviamos um comando AT modemGSM.sendAT("+CMGF=1\r\n"); // Aguardamos uma responsta em até 3 segundos modemGSM.waitResponse(3000, resp); // Exibição display showDisplay("SMS Text mode ", false); // Se a resposta conter um "OK" if(resp.indexOf("OK")>=0) { // Exibimos no display (sucesso) M5.Lcd.setTextColor(GREEN); showDisplay("OK\n", false); M5.Lcd.setTextColor(WHITE); Serial.println("SMS Text mode ok"); return true; } // Se a resposta não conter um "OK" // Exibimos no display (falha) M5.Lcd.setTextColor(RED); showDisplay("Fail\n", false); M5.Lcd.setTextColor(WHITE); Serial.println("SMS Text mode failed"); // Se a flag "restart" estiver como "true", aguardamos 5 segundos e reiniciamos o ESP if(restart) { delay(5000); ESP.restart(); } return false; }

Step 26: M5Stack Code - ShowStartScreen

// Exibimos a mensagem "Waiting for messages" com uma linha grifando o texto
void showStartScreen() { showDisplay("", true); M5.Lcd.fillRect(10, HGT_SCR2, WDT_SCR-20, 1, TFT_WHITE); M5.Lcd.setCursor(5, HGT_SCR2-10); showDisplay("Waiting for messages", false); }

Step 27: M5Stack Code - ShowLabelButtons

// Exibe as labels "Relay 1", "Relay 2" e "Relay 3"
void showLabelButtons() { String msg = "Relay 1"; int spacing = 5; M5.Lcd.setFreeFont(&FreeSerif12pt7b); M5.Lcd.setCursor((WDT_SCR2/2 ) - (msg.length()*12 / 2) - spacing,HGT_SCR-20); M5.Lcd.print(msg); msg = "Relay 2"; M5.Lcd.setCursor(WDT_SCR2 - (msg.length()*12 / 2) + spacing*2, HGT_SCR-20); M5.Lcd.print(msg); msg = "Relay 3"; M5.Lcd.setCursor(WDT_SCR2 + (WDT_SCR2/3) + spacing, HGT_SCR-20); M5.Lcd.print(msg); }

Step 28: M5Stack 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 29: Files

Download the files

PDF

INO

Share

    Recommendations

    • Paper Contest

      Paper Contest
    • Trash to Treasure

      Trash to Treasure
    • Weaving Challenge

      Weaving Challenge

    Discussions