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


Comunicação Wireless TCP com ESP (Parte 2)

Comunicação Wireless TCP com ESP

Daremos inicio a segunda parte do nosso material sobre comunicações Wireless. No tutorial anterior, foi abordado sobre o protocolo UDP, que não é orientado à conexão. Desta vez, vamos aprender sobre o protocolo TCP, este por sua vez, é orientado à conexão, que nos garante a entrega de mensagens, ordenação do recebimento/envio, etc. O código aqui apresentado irá funcionar tanto para ESP8266 quanto para o ESP32, já que a diferença é mínima e funciona da mesma forma.

 

[toc]

Protocolo TCP

TCP (Transmission Control Protocol) é um protocolo para transmissão de dados voltado à conexão, ou seja, permite que as maquinas se comuniquem e gerenciem o estado atual da conexão com seus clientes e servidores. Uma característica importante do TCP é seu gerenciamento do status da rede e os dados, com isto, podemos saber se o dado enviado foi realmente entregue ao destinatário, também garante a entrega ordenada das informações sem que haja colisões e perdas inperceptíveis. Muitas aplicações usam o TCP para efetuar suas comunicações, sendo amplamente usado em todo tipo de situação, ao contrario do UDP que por não ser orientado à conexão, pode gerar erros em nossos sistemas.


Mãos à obra

Componentes necessários

Obs: Você pode usar qualquer aplicativo ou programa para transmitir os pacotes TCP. Nós usaremos o primeiro encontrado na Play Store para ser fácil de vocês encontrarem caso queiram usar também. Link do TCP Client na Play Store

Código do projeto

#ifdef ESP8266//Se estiver usando ESP8266, automáticamente irá adicionar as bibliotecas do ESP8266.
#include <ESP8266WiFi.h>
#include <WiFiServer.h>
#elif defined ESP32//Se estiver usando ESP32, fara a mesma operaçao.
#include <WiFi.h>
#endif

WiFiServer sv(555);//Cria o objeto servidor na porta 555
WiFiClient cl;//Cria o objeto cliente.


void setup()
{
    Serial.begin(115200);//Habilita a comm serial.

    WiFi.mode(WIFI_AP);//Define o WiFi como Acess_Point.
    WiFi.softAP("NodeMCU", "");//Cria a rede de Acess_Point.

    sv.begin();//Inicia o servidor TCP na porta declarada no começo.
}

void loop()
{
    tcp();//Funçao que gerencia os pacotes e clientes TCP.
}

void tcp()
{
    if (cl.connected())//Detecta se há clientes conectados no servidor.
    {
        if (cl.available() > 0)//Verifica se o cliente conectado tem dados para serem lidos.
        {
            String req = "";
            while (cl.available() > 0)//Armazena cada Byte (letra/char) na String para formar a mensagem recebida.
            {
                char z = cl.read();
                req += z;
            }

            //Mostra a mensagem recebida do cliente no Serial Monitor.
            Serial.print("\nUm cliente enviou uma mensagem");
            Serial.print("\n...IP do cliente: ");
            Serial.print(cl.remoteIP());
            Serial.print("\n...IP do servidor: ");
            Serial.print(WiFi.softAPIP());
            Serial.print("\n...Mensagem do cliente: " + req + "\n");

	    //Envia uma resposta para o cliente
            cl.print("\nO servidor recebeu sua mensagem");
            cl.print("\n...Seu IP: ");
            cl.print(cl.remoteIP());
            cl.print("\n...IP do Servidor: ");
            cl.print(WiFi.softAPIP());
            cl.print("\n...Sua mensagem: " + req + "\n");
			
        }
    }
    else//Se nao houver cliente conectado,
    {
        cl = sv.available();//Disponabiliza o servidor para o cliente se conectar.
        delay(1);
    }
}

Colando para funcionar

Devo lembrar que o IP padrão do ESP em modo Acess_Point é 192.168.4.1

1-) Precisamos nos conectar na rede WiFi criada pelo micro controlador.

2-) Logo, iremos adicionar o servidor com seu IP e PORTA no aplicativo. Lembre-se que você pode usar qualquer aplicativo ou programa de computador para isso.

 

3-) Após incluir os dados do servidor no aplicativo, podemos conectar-se e enviar os dados.

 

 

Obs: Também há dados disponíveis no Serial monitor, que o micro controlador escreveu, olhe como ficou.


Entendendo a fundo

Software

-Função WiFiClient::connected()

if (cl.connected())//Detecta se há clientes conectados no servidor.
{

}

Esta função retorna se há cliente conectado em nosso servidor. Também irá retornar que há clientes até que todos dados sejam lidos do buffer, se não, continuará aparecendo clientes conectados até que todos dados sejam lidos.

-Função WiFiClient::available()

if (cl.available() > 0)//Verifica se o cliente conectado tem dados para serem lidos.
{

}

Esta função, retorna a quantidade de bytes disponiveis para a leitura no buffer do cliente. Pode ser usada para detectar se há dados, e também para efetuar a leitura byte a byte do buffer, afim de montar a String final.

-Função WiFiClient::print()

cl.print();

Usada para enviar a String para o cliente. Há várias maneiras de mandar uma informação para o cliente, desde write(), printf() até print() e println().

-Função WiFiServer::available()

cl = sv.available();//Disponabiliza o servidor para o cliente se conectar.

Esta função, obtem um cliente que esta conectado ao servidor e tem dados disponiveis para a leitura. Pode ser entendida a grosso modo que diz ao cliente que há um servidor disponivel para enviar dados.


Desafio

O desafio desta vez, é tentar conectar multiplos clientes ao mesmo tempo em seu servidor. Este código não permite isto, então faça alterações e estude sobre as bibliotecas usadas para permitir multiplas conexões!

Fechamento

Neste tutorial, você aprendeu a efetuar uma troca simples de mensagens entre o Cliente (nosso celular) e o Servidor (ESP), pode ser usada de mesma forma para comunicação entre dois micro controladores. Na parte 3 deste material, aplicaremos as aulas passadas em um aplicativo em Android ou PC para se comunicar com nosso ESP8266/32. Não se esqueça de comentar sobre duvidas, elogios e criticas! Também aceitamos sugestões sobre a terceira parte deste material.

 


Comunicação Wireless UDP com ESP (Parte 1)

Comunicação Wireless UDP com ESP

Neste material, iremos aprender a fazer uma comunicação Wireless UDP entre dois ESP8266 para transmissão de informações. O grande diferencial do ESP é sua comunicação Wireless com suporte a WiFi, então nada mais justo que aprender a usar isto. Lembrando que este código também funciona para o ESP32.

 

[toc]

Protocolo UDP

O protocolo UDP, da camada de transporte, nos permite de forma rápida e descomplicada, o envio de informações através da rede. A simplicidade de usar o UDP é compensada ao fato de não ser uma comunicação orientada à conexão, como por exemplo o TCP, logo, não há garantia que os pacotes sejam recebidos de forma ordenada ou se quer sejam recebidos. Usa-lo para aplicações simples é recomendado, já que não são dados críticos; a perda de pacotes (improvável, porém possa acontecer) não é tão prejudicial ao sistema. Iremos transmitir de um Cliente para um Host.

Esta comunicação simples pode ser feita para transmitir dados até um banco de dados em um computador ou informações para outros Micro controladores. As aplicações são as mais diversas e variadas possíveis, novamente, o limite é sua imaginação.

Lembre-se, serão dois ESP8266, um cliente e outro host. Os dois códigos estarão logo abaixo!

Veja como é rápido o processo de envio e recebimento dos dados. O cliente pisca quando envia o pacote e o host  pisca após receber o pacote, é tão rápido que os dois parecem piscar juntos:

 


Mãos à obra

Componentes necessários

 

Hardware

Acenderemos o LED_BUILTIN do NodeMCU para indicar a transmissão de dados. Este LED OnBoard, está conectado ao pino D4 do NodeMCU.

Código do projeto

Cliente

#include <ESP8266WiFi.h>//Biblioteca do WiFi.
#include <WiFiUdp.h>//Biblioteca do UDP.

WiFiUDP udp;//Cria um objeto da classe UDP.
long x;//Variavel para ser enviada.

void setup()
{
   pinMode(D4, OUTPUT);//Habilita o LED onboard como saida.
   digitalWrite(D4, 1);//Desliga o LED.

   WiFi.mode(WIFI_STA);//Define o ESP8266 como Station.
}

void loop()
{
   connect();//Sub-rotina para conectar-se ao host.

   send();//Sub-rotina para enviar os dados ao host.

   delay(500);//Aguarda meio segundo.
}

void connect()//Sub-rotina para verificar a conexao com o host.
{
   if (WiFi.status() != WL_CONNECTED)//Caso nao esteja conectado ao host, ira se conectar.
   {
      WiFi.begin("NodeMCU", "");//Conecta à rede do host.
      delay(2000);//Espera ate que a conexao seja feita.
   }
}

void send()//Sub-rotina para enviar dados ao host.
{
   if (WiFi.status() == WL_CONNECTED)//Só ira enviar dados se estiver conectado.
   {
      x = random(0, 1000);//Gera um numero aleatorio entre 0 e 1000.
      udp.beginPacket("192.168.4.1", 555);//Inicializa o pacote de transmissao ao IP e PORTA.
      udp.println(x);//Adiciona-se o valor ao pacote.
      udp.endPacket();//Finaliza o pacote e envia.

      digitalWrite(D4, 0);//-
      delay(5);//-
      digitalWrite(D4, 1);//Pisca o led rapidamente apos enviar.
   }
   else//Caso nao esteja com uma conexao estabelicida ao host, piscara lentamente.
   {
      digitalWrite(D4, 0);
      delay(250);
      digitalWrite(D4, 1);
   }
}

 

Host

#include <ESP8266WiFi.h>//Biblioteca do WiFi.
#include <WiFiUdp.h>//Biblioteca do UDP.

WiFiUDP udp;//Cria um objeto da classe UDP.

String req;//String que armazena os dados recebidos pela rede.

void setup()
{
   pinMode(D4, OUTPUT);//Habilita o LED onboard como saida.
   digitalWrite(D4, 1);//Desliga o LED.

   Serial.begin(115200);//Habilita a comunicaçao serial para a string recebida ser lida no Serial monitor.

   WiFi.mode(WIFI_AP);//Define o ESP8266 como Acess Point.
   WiFi.softAP("NodeMCU", "");//Cria um WiFi de nome "NodeMCU" e sem senha.
   delay(2000);//Aguarda 2 segundos para completar a criaçao do wifi.

   udp.begin(555);//Inicializa a recepçao de dados UDP na porta 555
}

void loop()
{
   listen();//Sub-rotina para verificar a existencia de pacotes UDP.
}

void listen()//Sub-rotina que verifica se há pacotes UDP's para serem lidos.
{
   if (udp.parsePacket() > 0)//Se houver pacotes para serem lidos
   {
       req = "";//Reseta a string para receber uma nova informaçao
       while (udp.available() > 0)//Enquanto houver dados para serem lidos
       {
           char z = udp.read();//Adiciona o byte lido em uma char
           req += z;//Adiciona o char à string
       }

       //Após todos os dados serem lidos, a String estara pronta.

       Serial.println(req);//Printa a string recebida no Serial monitor.

       digitalWrite(D4, 0);//-
       delay(5);//-
       digitalWrite(D4, 1);//Pisca o LED rapidamente apos receber a string.
    }
}

 

Entendendo a fundo

Software

Recomendamos que vocês a leiam esta referência sobre WiFi, praticamente todos comandos disponíveis estão explicados AQUI.

Cliente

 

-Enviando dados para o destino

udp.beginPacket("192.168.4.1", 555);//Inicializa o pacote de transmissao ao IP e PORTA.
udp.println(x);//Adiciona-se o valor ao pacote.
udp.endPacket();//Finaliza o pacote e envia.

Nesta parte, usamos 3 funções do UDP, entre elas estão:

-Função WiFiUDP::beginPacket()

udp.beginPacket("192.168.4.1", 555);

Estamos enviando o pacote para o IP "192.168.4.1" que é o IP padrão do host no ESP8266, na porta 555.

 

-Função WiFiUDP::println()

udp.println(x);

Adicionamos o valor da variável X ao pacote.

 

-Função WiFiUDP::endPacket()

udp.endPacket();

Por ultimo, fechamos o pacote para finalmente ser enviado ao destino.

 

Para melhor entendimento, veja esta figura:

 

  • Vermelho = beginPacket();
  • Verde = print();
  • Azul = endPacket();

 


Host

 

-Função WiFiUDP::parsePacket()

udp.parsePacket();

Esta função faz a verificação de novos dados no buffer. Se houver dados novos para serem lidos, a função irá retornar um numero maior que zero.


Desafio

O desafio agora é você fazer uma mensagem de retorno para quem enviou uma mensagem ao host, por exemplo, quando o cliente 1 enviar dados ao host, o host responda "OK". Também faça um Print do IP de quem foi recebido o dado. Na referencia do WiFi (citado acima), há os comandos necessários!

 

Finalizando

Com a possibilidade de comunicação entre dispositivos pelo WiFi ou Wireless Ponto-a-Ponto, podemos fazer projetos muito legais e redes de dispositivos para se comunicarem. Não se esqueça de ver a parte 2 do material, sobre TCP! Dúvidas? Sugestões? Críticas? Comente abaixo!


Servidor WEB com ESP - NodeMCU

Servidor WEB com NodeMcu - ESP

Vamos aprender à criar um Servidor WEB usando HTML e TCP/IP em nosso pequeno ESP8266 ou ESP32. Para essa experiência usaremos o NodeMcu, mas daremos as instruções para você fazer o mesmo projeto com o ESP32. Um servidor web é, como o nome sugere, um servidor de informações que serão fornecidas pelo MCU através da internet ou até LAN. Podemos acessar estes dados em computadores, celulares ou outros MCU's, e também enviar informações e instruções para o MCU.

Http servidor web

Aplicação prática

Acionar LEDs, motores, luzes, ventiladores, etc. Você terá uma interface HMI (Interface Homem-Máquina) e com isso, conseguirá manipular seu sistema das mais diversas formas. Ver status de funcionamento de sensores ou atuadores, as possibilidades são muitas, depende de você. Mostraremos apenas o básico, o controle de um LED por botão, porém, após você entender o funcionamento deste sistema, conseguira fazer muitas coisas!

Para este projeto, você verá o básico necessário para implementar com sucesso um acionamento (transmissão de dados SITE->MCU).

TCP

O protocolo TCP/IP (Transmission Control Protocol) é um protocolo para transmissão orientado à conexão, ou seja, permite que as maquinas se comuniquem e gerenciem o estado atual da transmissão.

Uma característica muito importante sobre o TCP é seu gerenciamento do status da rede e os dados, com isto, podemos saber se o dado enviado foi realmente entregue ao destinatário, também garante a entrega ordenada das informações sem que haja colisões e perdas imperceptíveis.

De um jeito mais fácil, TCP será nosso “Caminhão”, que irá levar as informações até o navegador de quem acessar nosso IP.


HTML

HTML é uma linguagem de marcação utilizada em sites da WEB e são interpretadas pelos navegadores (http request). Quando o usuário se conectar ao IP do ESP pelo navegador, o mesmo irá enviar os dados no formato do HTML via TCP e com isso, após o navegador receber os dados, irá mostrar a página web!

Clique aqui para ver mais Informações sobre HTML


Mãos à obra

Componentes necessários

Hardware

Vamos acender o LED_BUILTIN do NodeMCU, este LED OnBoard, está conectado ao pino D4 do NodeMCU.

NodeMcu trabalhando como servidor web

Código do projeto

Não se esqueça de colocar suas credenciais do WiFi na linha 14 para o funcionamento do código!

#include <ESP8266WiFi.h>//Biblioteca que gerencia o WiFi.
#include <WiFiServer.h>//Biblioteca que gerencia o uso do TCP.

WiFiServer servidor(80);//Cria um objeto "servidor" na porta 80 (http).
WiFiClient cliente;//Cria um objeto "cliente".

String html;//String que armazena o corpo do site.

void setup()
{
   Serial.begin(9600);//Inicia comunicaçao Serial.

   WiFi.mode(WIFI_STA);//Habilita o modo STATION.
   WiFi.begin("SUA REDE", "SUA SENHA");//Conecta no WiFi (COLOQUE O NOME E SENHA DA SUA REDE!).

   Serial.println(WiFi.localIP());//Printa o IP que foi consebido ao ESP8266 (este ip que voce ira acessar).
   servidor.begin();//Inicia o Servidor.

   pinMode(D4, OUTPUT);//Define o LED_BUILTIN como Saida.
}

void loop()
{
   http();//Sub rotina para verificaçao de clientes conectados.
}

void http()//Sub rotina que verifica novos clientes e se sim, envia o HTML.
{
   cliente = servidor.available();//Diz ao cliente que há um servidor disponivel.

   if (cliente == true)//Se houver clientes conectados, ira enviar o HTML.
   {
      String req = cliente.readStringUntil('\r');//Faz a leitura do Cliente.
      Serial.println(req);//Printa o pedido no Serial monitor.

      if (req.indexOf("/LED") > -1)//Caso o pedido houver led, inverter o seu estado.
      {
         digitalWrite(D4, !digitalRead(D4));//Inverte o estado do led.
      }

      html = "";//Reseta a string.
      html += "HTTP/1.1 Content-Type: text/html\n\n";//Identificaçao do HTML.
      html += "<!DOCTYPE html><html><head><title>ESP8266 WEB</title>";//Identificaçao e Titulo.
      html += "<meta name='viewport' content='user-scalable=no'>";//Desabilita o Zoom.
      html += "<style>h1{font-size:2vw;color:black;}</style></head>";//Cria uma nova fonte de tamanho e cor X.
      html += "<body bgcolor='ffffff'><center><h1>";//Cor do Background

      //Estas linhas acima sao parte essencial do codigo, só altere se souber o que esta fazendo!

      html += "<form action='/LED' method='get'>";//Cria um botao GET para o link /LED
      html += "<input type='submit' value='LED' id='frm1_submit'/></form>";

      html += "</h1></center></body></html>";//Termino e fechamento de TAG`s do HTML. Nao altere nada sem saber!
      cliente.print(html);//Finalmente, enviamos o HTML para o cliente.
      cliente.stop();//Encerra a conexao.
   }
}

Entendendo a fundo

Software

-Função WiFiServer::available()

cliente = servidor.available();

Aqui, estamos dizendo ao cliente que há um servidor disponível para conexão.

-Detectando clientes conectados

if (cliente == true){}

Com esta condicional, é verificado a existência de clientes conectados no ESP, e se sim, enviará o HTML para o cliente.

-Enviando a estrutura do HTML

cliente.print(html);

Após a criação da estrutura do HTML, precisamos enviar para o cliente. É feito com um simples print().

-Fechando conexão

cliente.stop();

Após o envio do HTML, encerramos a conexão pois não é necessário se manter conectado.


Foi usado um botão do tipo SUBMIT para fazer uma requisição GET ao Host, para entender melhor como funcionam os botoes no HTML, veja ESTE tutorial.

Fechamento

Com a possibilidade de receber e enviar dados pela internet, nossos horizontes ficam bem amplos. Podemos tanto controlar o MCU para por exemplo acionar o portão da garagem, ou descobrir se esta chovendo com sensores e etc. Dúvidas? Sugestões? Críticas? Comente abaixo!