Módulo GSM SIM800L - Aplicações com Arduino

Módulo GSM SIM800L

Neste tutorial, iremos testar as principais aplicações do módulo GSM SIM800L diretamente com os comandos AT pela UART , assim, podemos fazer o mesmo código para diversos microcontroladores como PIC, ESP8266/32, ARM, AVR, etc, mudando apenas os métodos de utilização da UART de MCU para MCU, já que os comandos AT serão sempre iguais. Para esse tutorial usaremos o Arduino Mega como controlador.

Módulo GSM SIM800L
Figura 1 - Módulo GSM SIM800L

[toc]

kit robotica educacional com Arduino ESP ou Microbit

O que vamos aprender?

As principais aplicações deste módulo são:

  • SMS (receber e enviar).
  • Obter horário da rede, similar ao NTP ou RTC.
  • Obter localização por triangulação da rede.
  • Conexões TCP/UDP (inclusive SSL/TLS).
  • Ligações com entrada e saída de áudio.

Neste tutorial, Aprenderemos os 3 primeiros itens separadamente encontrados na lista apresentada.


Mãos a obra - Testando algumas funções do SIM800L

Componentes necessários

Montando o projeto

Atenção: esse módulo pode utilizar muita corrente de sua fonte em picos de transmissão (até 2A), então não alimente-o diretamente pelo Arduino. Recomenda-se utilizar uma fonte externa de 3,4V a 4,3V de no mínimo 2A. Caso não tenha uma fonte com tanta corrente, você pode adicionar alguns capacitores de alto valor capacitivo para ajudar nos picos de transmissão. Se a fonte não for suficiente para alimentá-lo, ele irá reiniciar aleatoriamente durante as transmissões.

 

Esquematico no Frittzing Arudino Mega e Módulo GSM SIM800L
Figura 2 - Esquematico no Frittzing

Projeto 1 - Enviando e recebendo SMS

Programando o Arduino

void setup()
{
  Serial2.begin(115200);
  pinMode(13, OUTPUT);//LED
  pinMode(8, OUTPUT);//Pino de reset do GSM

  //reinicia o GSM
  digitalWrite(8, 0);
  delay(2000);
  digitalWrite(8, 1);
  delay(7000);

  if (gsmWrite("AT", "OK") == "FAIL")
  {
    return;//Se o GSM nao responder, retorna a funcao (trava o mcu)
  }

  delay(5000);//Espera o termino da inicializacao do GSM
  SMSsnd("014998202683", "Arduino ON, lendo mensagens...");//Envia a mensagem pro numero. Atencao: precisa do DDD!

  
}

void loop()
{
  SMSrd();//Verifica se há novas mensagens para serem lidas a cada 10seg
  delay(10000);
}


String gsmWrite(String snd, String rcv)//Funcao que envia dados pro GSM e espera a resposta de ate 30seg
{
  Serial2.println(snd);

  if (rcv.indexOf("+CMGS") > -1)
  {
    delay(150);
    Serial2.write(0x1A);
  }

  for (uint16_t i = 0; i < 1200; i++)
  {
    delay(25);
    if (Serial2.available())
    {
      delay(50);
      String a = Serial2.readString();


      if (a.indexOf(rcv) > -1 || rcv.length() == 0)
      {
        return a;
      }
    }
  }

  return "FAIL";
}

void SMSsnd(String nm, String msg)//Funcao que envia o SMS
{
  String snd = "AT+CMGS=\"";  snd += nm;  snd += "\"";

  gsmWrite("AT+CMGF=1", "OK");
  gsmWrite("AT+CSMP=17,255,0,0", "OK");
  gsmWrite(snd, ">");
  gsmWrite(msg, "+CMGS:");
}

void SMSrd()//Funcao que le se ha SMS nao lido
{
  String c = "";
  gsmWrite("AT+CMGF=1", "OK");
  c = gsmWrite("AT+CMGL=\"REC UNREAD\"", "");

  if (c.indexOf("+CMGL:") > -1)
  {
    if    (c.indexOf("ON") > -1)//ON
    {
      digitalWrite(13, 1);//se o SMS conter o texto ON, liga o LED
    }
    else if (c.indexOf("OFF") > -1)//OFF
    {
      digitalWrite(13, 0);//se o SMS conter o texto OFF, desliga o LED
    }

    gsmWrite("AT+CMGD=1,1", "OK");//apaga todas mensagens (SIM card ha pouca memoria)
  }
}

Colocando para funcionar

Depois de ligar nosso projeto na alimentação, vamos logo receber uma mensagem indicando que o sistema está ativo, mostrando que o envio de SMS funcionou perfeitamente. Também foi adicionada a leitura de SMS, de modo que, quando um SMS é enviado com a mensagem "ON" ou "OFF",  o LED no pino 13 seja ligado ou desligado.

Mensagem recebida do Módulo GSM SIM800L
Figura 3 - Mensagem recebida do Módulo GSM SIM800L

Projeto 2 - Obtendo horário através da rede

Programando o Arduino

void setup()
{
  Serial.begin(115200);
  Serial2.begin(115200);//Inicia a Serial do GSM
  pinMode(8, OUTPUT);//Pino de reset do GSM

  //Reinicia o GSM
  digitalWrite(8, 0);
  delay(2000);
  digitalWrite(8, 1);
  delay(7000);

  String rcv = gsmWrite("AT", "*PSUTTZ:");//Espera o GSM obter o horario da rede
  if (rcv.indexOf("FAIL") > -1)
  {
    gsmWrite("AT+CLTS=1;&W", "OK");//Se nao responder, pode ser configuracao do seu GSM, entao vamos ativar a obtencao automatica de horario pela rede
    setup();//retorna ao inicio
  }

  delay(5000);
  

  
}

void loop()
{
  Serial.println(gsmTIME());//Mostra a data e horario no Serial Monitor
  delay(5000);
}


String gsmWrite(String snd, String rcv)//Funcao que envia dados ao GSM e espera a resposta
{
  Serial2.println(snd);

  if (rcv.indexOf("+CMGS") > -1)
  {
    delay(150);
    Serial2.write(0x1A);
  }

  for (uint16_t i = 0; i < 1200; i++)
  {
    delay(25);
    if (Serial2.available())
    {
      delay(50);
      String a = Serial2.readString();


      if (a.indexOf(rcv) > -1 || rcv.length() == 0)
      {
        return a;
      }
    }
  }

  return "FAIL";
}

String gsmTIME()//Funcao que retorna a data e horario
{
  String c = gsmWrite("AT+CCLK?", "+CCLK:");

  int16_t a = c.indexOf("\"") + 1;
  int16_t b = c.indexOf("\"", a);
  return c.substring(a, b);
}

Colocando para funcionar

Abrindo o Serial Monitor, podemos observar a data e o horário retornado pelo módulo GSM, lembrando que a data está no padrão (ano/mês/dia). O ultimo valor do horário corresponde ao fuso horário*4, logo, para obter nosso fuso horário, basta dividir "-12" por 4  e chegamos em -3 (fuso horário de brasília).

Data e hora do módulo GSM SIM800L
Figura 4 - Data e hora do módulo GSM SIM800L

Projeto 3 - Obtendo localização através da rede

Programando o Arduino

void setup()
{
  Serial.begin(115200);
  Serial2.begin(115200);
  pinMode(8, OUTPUT);//Pino de reset do GSM

  //Reseta o GSM
  digitalWrite(8, 0);
  delay(2000);
  digitalWrite(8, 1);
  delay(7000);

  if (gsmWrite("AT", "OK") == "FAIL")
  {
    return;
  }

  delay(5000);
  
  if (gsmWrite("AT+SAPBR=1,1", "OK") == "FAIL")//Ativa a rede pra localizacao
  {
    return;
  }
  else
  {
    String rcv = gsmWrite("AT+CIPGSMLOC=1,1", "+CIPGSMLOC:");//Pergunta a localizacao ao GSM
    int a = rcv.indexOf(":") + 2;
    int b = rcv.indexOf(",", a);
    if (rcv.substring(a, b) == "0")
    {
      a = rcv.indexOf(",", b) + 1;
      b = rcv.indexOf(",", a) + 1;
      b = rcv.indexOf(",", b);
      Serial.println(rcv.substring(a, b));//Mostra a localizacao no Serial monitor
    }
  }
}

void loop()
{
  
}


String gsmWrite(String snd, String rcv)//Funcao que envia dados ao GSM e espera a resposta
{
  Serial2.println(snd);

  if (rcv.indexOf("+CMGS") > -1)
  {
    delay(150);
    Serial2.write(0x1A);
  }

  for (uint16_t i = 0; i < 1200; i++)
  {
    delay(25);
    if (Serial2.available())
    {
      delay(50);
      String a = Serial2.readString();


      if (a.indexOf(rcv) > -1 || rcv.length() == 0)
      {
        return a;
      }
    }
  }

  return "FAIL";
}

Colocando para funcionar

As coordenadas que o módulo GSM retorna é longitude e latitude respectivamente.

Coordenadas lidas pelo Módulo GSM SIM800L
Figura 5 - Coordenadas lidas pelo Módulo GSM SIM800L

Entendendo a fundo

Software

Para utilização do módulo GSM foram usados os comandos "AT" do Datasheet, este pode ser encontrado logo abaixo em nossa seção de referências. Existem incontáveis comandos para as mais diversas aplicações e se você pretende utilizar o módulo GSM, aconselhamos que estude o Datasheet e conheça os comandos necessários para seu projeto.

A comunicação entre o Microcontrolador e o módulo GSM é feita através da porta Serial (UART) à 115200b/s neste caso, mas pode ser alterada. A cada comando enviado, normalmente, retorna-se uma resposta, então sempre que enviamos um comando, precisamos esperar a resposta para saber se os dados enviados foram realmente enviados. Para isso, foi criada uma função básica cuja função é escrever o dado no módulo GSM e esperar o retorno da resposta em um tempo limite de 30 segundos. Tome cuidado que alguns comandos podem demorar até 85 segundos para retornar resposta e caso isso aconteça, o código irá falhar por timeout. Para arrumar isso, aumente o timeout da função de leitura.

-Função gsmWrite(String snd, String rcv)

gsmWrite("AT", "OK");

A função criada para envio de dados do módulo GSM é uma boa maneira de utilizar o mesmo em diversos tipos de códigos, entretanto, como normalmente após a escrita de algum comando nós esperamos uma resposta, isto pode deixar nosso código lento e travando, já que alguns comandos demoram até 85 segundos!

Essa função basicamente escreve o comando (primeiro argumento da função, "snd") AT na porta Serial e posteriormente espera a resposta do mesmo (segundo argumento da função, "rcv"). Caso o módulo GSM não retorne a reposta esperada em 30 segundos que definimos no loop FOR() da função, retorna-se a mensagem "FAIL".

 -Comandos AT

O módulo GSM SIM800L funciona com os comandos AT e a maioria deles retorna alguma mensagem de confirmação ou erro, vamos ver como alguns deles funcionam!

AT+CCLK

Figura 6 - AT+CCLK

Quando requisitado "AT+CCLK?" do SIM800L, retorna-se "+CCLK: <time>" e ao fim, "OK". Também pode-se retornar erros como mostrado na imagem.

AT+CMGL

Figura 7 - AT+CMGL

A leitura de SMS do nosso projeto foi dedicada apenas às mensagens não lidas, por isso utilizamos "AT+CMGL="REC UNREAD"", mas você pode trocar para qualquer uma das opções acima que se encaixar melhor ao seu projeto.


Conclusões finais

As utilidades para o módulo GSM são inúmeras e isto nos permite ter uma gama de aplicações muito grande em um mesmo projeto, já que conta com diversas features, desde SMS até conexões TCP e etc. Apesar de alguns comandos demorarem bastante por causa da latência da rede telefônica, ainda podemos usufruir bem deste incrível módulo. O SIM800L faz muito mais do que mostrado aqui, entretanto estamos mostrando apenas o básico para norteá-los.

Referencias


Protocolo de tempo NTP com ESP

Protocolo de tempo NTP com ESP

É comum que em projetos de automação e robótica seja necessário saber a hora correta, para marcar a hora em que uma ação ocorreu ou até para ativar sensores em certo horário. Vamos aprender a adicionar um RTC (Real Time Clock - Relógio de tempo Real) ao ESP sem precisar de um hardware externo, como por exemplo um DS1307 ou do DS3231. Para isso, usaremos o NTP que só precisa de uma conexão com a internet o que é facilmente resolvido quando o assunto é projetos com ESP e IoT.

 

Sobre o NTP

Do Wikipédia:

O NTP é um protocolo para sincronização dos relógios dos computadores baseado no protocolo UDP sob a porta 123, para sincronização do relógio de um conjunto de computadores em redes de dados com latência variável. O NTP permite manter o relógio de um computador com a hora sempre certa e com grande exatidão.

O NTP serve tanto para atualizar e também manter os horários e datas sincronizadas entre dispositivos. Não há muitas explicações sobre isto, porém caso queira aprender mais sobre o NTP, clique AQUI ou AQUI.

 

 

Digamos que você tem uma rede de sensores, e precisa que eles liguem as 20h da noite, as melhores alternativas para isto, são um RTC externo/interno ou até o NTP caso haja conexão com a internet. Para o exemplo deste tutorial, acenderemos o LED Onboard de acordo com o horário que iremos definir. Com o NTP, também é possível arrumar a data e hora de um RTC externo que esteja errado!

Download da biblioteca NTPClient

Neste GIF, é mostrado o funcionamento do exemplo deste tutorial, no qual o LED foi acionado as 19:23:30.

ESP8266
FIG 1 - Projeto do tutorial - ESP8266

Mãos à obra

Componentes necessários

Montando o projeto

Nesse projeto usaremos apenas a placa NodeMCU. Usaremos o LED que já vem na placa ligado ao Pino D4 para fazer nossa experiência. Também funciona com ESP32.

Código do projeto

Não se esqueça de alterar as credenciais do WiFi e usar o fuso horário correto. Também altere para o horário que desejar, como o tutorial foi feito as 19:23, usamos este valor.

#include <NTPClient.h>//Biblioteca do NTP.
#include <WiFiUDP.h>//Biblioteca do UDP.
#include <ESP8266WiFi.h>//Biblioteca do WiFi.

WiFiUDP udp;//Cria um objeto "UDP".
NTPClient ntp(udp, "a.st1.ntp.br", -3 * 3600, 60000);//Cria um objeto "NTP" com as configurações.

#define led D4//Define o LED ao pino D4.

String hora;//Váriavel que armazenara o horario do NTP.

void setup()
{
   Serial.begin(9600);//Inicia a comunicação serial.

   pinMode(led, OUTPUT);//Define o pino como saida.
   digitalWrite(led, 1);//Apaga o LED.

   WiFi.mode(WIFI_STA);
   WiFi.begin("SUA REDE", "SUA SENHA");//Conecta ao WiFi.
   delay(2000);//Espera a conexão.

   ntp.begin();//Inicia o NTP.
   ntp.forceUpdate();//Força o Update.
}

void loop()
{
   hora = ntp.getFormattedTime();//Armazena na váriavel HORA, o horario atual.
   Serial.println(hora);//Printa a hora já formatada no monitor.

   if (hora == "19:23:30")//Se a hora atual for igual à que definimos, irá acender o led.
   {
      digitalWrite(led, 0);//Acende
   }

   delay(1000);//Espera 1 segundo.
}

Entendendo a fundo

Software

-Declarando o objeto

NTPClient ntp(udp, "a.st1.ntp.br", -3 * 3600, 60000);//Cria um objeto "NTP" com as configurações.

Aqui é criado o objeto NTP com os seguintes parâmetros:

  1. Objeto UDP.
  2. Servidor do NTP, Acesse ESTE link para ver os servidores brasileiros disponíveis.
  3. Fuso horário multiplicado por segundos, apenas altere o fuso horário se for necessário.
  4. intervalo de updates para atualização da hora, é necessário para manter a hora correta. O padrão é 1 Minuto.

 

-Função NTPClient::begin() e forceUpdate()

ntp.begin();//Inicia o NTP.

ntp.forceUpdate();//Força o Update.

Iniciamos o NTP e também forçamos o update para certificar de que a hora esteja certa.

-Salvando a hora atual

hora = ntp.getFormattedTime();//Armazena na váriavel HORA, o horario atual.

Esta função ntp.getFormattedTime() retorna uma string com a hora atual, contendo HH:MM:SS. Salvamos isto na nossa String hora

Também é possível pegar os dados separados ou até o dia. Veja as funções da biblioteca!

-Acionando o LED com a hora

if (hora == "19:23:30")//Se a hora atual for igual à que definimos, irá acender o led.
{
   digitalWrite(led, 0);//Acende
}

Caso a variável que contem a hora atual seja igual à definida, neste caso é 19:23:30, acenderá o LED.


Desafio

O desafio desta vez, é manter o LED aceso entre intervalos de tempo, por exemplo das 13h até as 16h. Para isto, pode-se usar a boa e velha lógica booleana ou a função constrain() do arduino. Boa sorte!

Considerações finais

A implementação deste simples protocolo pode ser de grande ajuda e utilidade em seus projetos que tenham conexão com a Internet. Dúvidas? Sugestões? Críticas? Comente abaixo!