banner

Alarme e Automação com ESP32 e SIM800L



Hoje eu trago uma montagem que é uma espécie de alarme e automação juntos, utilizando o SIM800L. O modem é GPRS que precisa de um cartão SIM. Vamos dizer que esse cara é um “celularzinho” e é muito legal porque é extremamente barato e te possibilita desenvolver projetos surpreendentes. Enfim, vou te apresentar a montagem e código fonte com ESP32, SIM800L e um sensor de barreira, além de efetuar ligações e envio de alerta por SMS para smartphones.








Demonstração






Montagem





Recursos usados

  • ESP32 – WROOM
  • SIM800L
  • Display TFT 1.8’’
  • Sensor de barreira
  • Módulo de 2 Reles
  • Resistor 10k ohm
  • Fonte 4.1V e 5V
  • Jumpers
  • 1x Cartão SIM com plano SMS para o Smartphone
  • 1x Cartão SIM com plano SMS e ligação para o SIM800L
  • Smartphone





Pinout ESP32





Pinout SIM800L





Montagem


*Deixe os GND em comum



Montagem - Tabela





Código

Instalação de bibliotecas



Código ESP32

Declarações e variáveis

#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"};


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

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


sendAT e 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);
}

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

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

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

isItToCall e 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");






call (continuação) e 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\"");



SMSMessageRecv (continuação)

 // 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;
}

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

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<aux.length()-8; i++)
    *msg += aux.charAt(i);  
}


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



Faça o download dos arquivos






11 comentários:

  1. Muito bom professor! O detalhe das tensões de alimentação para mim foram muito importantes!

    ResponderExcluir
  2. Olá Boa noit muito bom esse projeto Ai to tentando monta com arduio uno e sensor pir e sim 800l como faço des de ja agradeco

    ResponderExcluir
  3. Parabéns professor!

    Será que consigo usar o esp-01 junto com o SIM800L ?

    Reduziria o custo do meu projeto, já que precisaria apenas enviar SMS e conectar a internet.

    ResponderExcluir
  4. Boa tarde Fernando,

    Seus projetos com o SIM800L ainda funcionam?

    Comprei um e não consigo usar.

    No site da anatel apresenta a mensagem:

    "O IMEI informado possui restrição de uso. Para maiores informações acesse aqui."

    Será que consigo contornar isso?

    ResponderExcluir
    Respostas
    1. Comprei um similar, também SIM800L mas não consigo usar.

      No hotsite da Anatel "consultaserialaparelho" e mostra que "Até o momento o IMEI informado não possui restrições de uso".

      veja meu comentário abaixo :-(

      Excluir
  5. Olá
    Eu tenho dois módulos: A6 e SIM800L ( https://www.filipeflop.com/produto/modulo-gsm-gprs-sim800l-antena-sma ) este que chegou na minha casa na última terça-feira mas eu não estou conseguindo conectividade com a rede, mesmo usando meu chip Claro que uso no meu celular atual.

    O led NET (que indica conexão com a rede) pisca a cada 1 segundo (ou menos).

    Estou usando a biblioteca TinyGsmClient.h (recentemente baiaxda) e ESP32.

    A conexão com microcontrolador está ok pois envio e recebo dados do módulo a 115200 e 9600 sem problemas:

    > modemGSM.getModemInfo() retorna SIM800 R14.18
    > !modemGSM.restart() retorna true (ok)

    só aqui que falha:
    > modemGSM.waitForNetwork()

    Veja no vídeo que estou alimentando o módulo com uma fonte exclusiva para ele e a antena está ligada, mas o led pisca na mesma frequência por horas.
    https://photos.app.goo.gl/dLcPgHxju7Bw5rR17

    - Alguma dica sobre outras formas de verificar isso?

    - Tem algum procedimento no SIM card que preciso fazer?

    - Será que foi bloqueado pela ANATEL?

    ResponderExcluir
    Respostas
    1. Olá. Comprei (com nota fiscal) e conectei o sim800l evb com um ard. uno e conforme esquemas que se acha na internet. Não usei nenhuma biblioteca para o sim800l, somente software serial e comandos AT. A maior dificuldade foi descobrir que precisa de um ";" apos o numero a discar no comando ATD+5511987654321; Curioso que num chip TIM pré pago funcionou normal e num chip VIVO o led net piscava a cada 1 seg (sem conexão com a rede, portanto), o led on acendia e depois de uns 4 seg. caia tudo, como se houvesse dado um reset. Continuo com o TIM e tá ok. Espero ter ajudado a vc e a outros.

      Excluir
  6. Estava com problemas mais consegui contornar não usando mais a protoboard.

    Antes piscava a cada 1 segundo o SIM800L (Indicação que não esta conectado a rede GSM) quando fiz a ligação de energia da fonte direto ao SIM800L o problema foi resolvido e ele passou a piscar a cada 3 segundos (Indicação que está conectado a rede GSM).

    É importante se atentar a tensão e corrente, 4.2V + 2A.

    Também checar a soldagem da antena.

    ResponderExcluir
    Respostas
    1. Resolvido.
      Coloquei outros chips da vivo e a tim e funcionou.

      Excluir
  7. Achei interessante o projeto e tentei reproduzi-lo, porém há um erro: "'getSMSNumber' was not declared in this scope", na linha do código "if(getSMSNumber(*msg, *&number))".
    alguém saberia o motivo do erro? Se sim, diga-me por favor.
    grato, Pedro.

    ResponderExcluir
  8. Amigo,
    Poste sua dúvida no www.forum.fernandok.com.
    Vamos formar uma comunidade e ajudar uns aos outros.

    ResponderExcluir

Tecnologia do Blogger.