Shield Ethernet W5100 – Controlando saídas de um Arduino via navegador – Servidor Web

Neste tutorial iremos dar prosseguimento ao conteúdo relacionado à criação de Servidores Web utilizando o Arduino UNO juntamente com o Ethernet Shield W5100. Anteriormente, vimos quais eram os procedimentos básicos para criar um Servidor Web e também para efetuar a leitura de valores provenientes de elementos ligados tanto às entradas analógicas quanto às digitais. Neste tutorial você aprenderá como controlar as saídas digitais do Arduino UNO por meio do navegador acessando um Servidor Web implementado com seu Shield Ethernet W5100

É importante, para o pleno entendimento desse tutorial, que o leitor tenha lido os dois tutoriais anteriores:

[toc]


Mãos à obra – Acionando saídas digitais do Arduino UNO através de um Servidor Web

Componentes necessários

Para reproduzir este projeto, você irá precisar dos seguintes componentes:

Montando o projeto

Na figura abaixo você pode conferir o hardware pronto para ser utilizado.

Hardware para servidor web com o Ethernet Shield w5100 e leds
Hardware utilizado com o Ethernet Shield w5100 e leds

Gostaríamos de sugerir que você tenha bastante cuidado no momento em que for encaixar o Ethernet Shield W5100 no Arduino UNO, pois, além da possibilidade de entortar os pinos do shield, você também pode se machucar.

Lembre-se que o shield em questão possui uma série de pinos em sua parte inferior, tanto nas laterais como em sua parte dianteira, portanto, antes de pressionar o mesmo sobre o Arduino UNO, certifique-se de que os pinos estejam levemente posicionados em suas respectivas entradas para que então você possa ir realizando o encaixe lentamente.

Realizando as conexões

Para reproduzir este projeto, garanta que o seu Shield Ethernet W5100 esteja ligado corretamente a uma das portas LAN presentes no seu modem ou roteador.

Programando

Este programa foi elaborado a partir do código-exemplo da biblioteca Ethernet.h denominado WebServer.

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };   
IPAddress ip(192, 168, 25, 26);                       
EthernetServer server(80);                             

String requisicao_do_navegador;
String parametro_da_requisicao;

void setup()
{
    Ethernet.begin(mac, ip); 
    server.begin();                 
    pinMode(2,OUTPUT);
    pinMode(7,OUTPUT);
}

void loop()
{
    EthernetClient client = server.available();

    if (client) { 
        boolean currentLineIsBlank = true;
        while (client.connected()) {
            if (client.available()) { 
                char c = client.read(); 
                requisicao_do_navegador += c;  
                
                if (c == '\n' && currentLineIsBlank ) 
                {     
                        if (definindo_a_requisicao(&requisicao_do_navegador)) {
                        parametro_da_requisicao = pegar_parametro_da_requisicao(&requisicao_do_navegador);
                        Serial.println(parametro_da_requisicao);
                                                 
                        client.println("HTTP/1.1 200 OK");
                        client.println("Content-Type: text/html");
                        client.println("Connection: close");
                        client.println();
                        
                        //Conteudo da Página HTML
                        client.println("<!DOCTYPE html>");
                        client.println("<html>");
                        client.println("<head>");
                        client.println("<title>Servidor Web VDS</title>");
                        client.println("</head>");
                        client.println("<body>");
                        client.println("<h1><font color=#4279c7>Servidor Web do Vida de Sil&iacute;cio</font></h1>");
                        client.println("<hr/>");                         
                        client.println("<h1>Sa&iacute;das digitais</h1>");
                        
                        client.println("<form method=\"get\">");

                        saida(client);
                        
                        client.println("<br/>");
                        saida2(client);
                        client.println("</form>");
                                                
                        
                        client.println("</body>");
                        client.println("</html>");
                        } else {
                        client.println("HTTP/1.1 200 OK");
                    }
                   
                    requisicao_do_navegador = "";    
                    break;
                }
                
                if (c == '\n') {
                    currentLineIsBlank = true;
                } 
                else if (c != '\r') {
                    currentLineIsBlank = false;
                }
            }
        } 
        delay(1);     
        client.stop(); 
    } 
}

void saida(EthernetClient cl)
{
        
        if (parametro_da_requisicao.indexOf("P2=1") > -1) 
        { 
           digitalWrite(2, HIGH);
           cl.println("<input type=\"checkbox\" name=\"P2\" value=\"1\" onclick=\"submit();\" checked > Pino digital 2");
        } 
        else 
        {
           digitalWrite(2, LOW);
           cl.println("<input type=\"checkbox\" name=\"P2\" value=\"1\" onclick=\"submit();\" > Pino digital 2");
        }
        
}

void saida2(EthernetClient cl)
{
        
        if (parametro_da_requisicao.indexOf("P7=1") > -1) 
        { 
           digitalWrite(7, HIGH);
           cl.println("<input type=\"checkbox\" name=\"P7\" value=\"1\" onclick=\"submit();\" checked > Pino digital 7");
        } 
        else 
        {
           digitalWrite(7, LOW);
           cl.println("<input type=\"checkbox\" name=\"P7\" value=\"1\" onclick=\"submit();\" > Pino digital 7");
        }
        
}

String pegar_parametro_da_requisicao(String *requisicao) {
int pos_inicial, pos_final;
String parametro;

  
  pos_inicial = (*requisicao).indexOf("GET") + 3;
  pos_final = (*requisicao).indexOf("HTTP/") - 1;
  parametro = (*requisicao).substring(pos_inicial,pos_final);
  parametro.trim();
 
  return parametro;
}

bool definindo_a_requisicao(String *requisicao) {
String parametro;
bool requisicao_desejada = false;

  parametro = pegar_parametro_da_requisicao(requisicao);

  if (parametro == "/") {
     requisicao_desejada = true;
  }

  if (parametro.substring(0,2) == "/?") {
     requisicao_desejada = true;
  }  

  return requisicao_desejada;
}

Colocando para funcionar

Veja como ficou nosso segundo Servidor Web.

Servidor Web com Ethernet Shield W5100
Servidor Web pronto.

Entendendo a fundo

Antes de começarmos com a explicação do código, gostaríamos de ressaltar que iremos apresentar apenas os detalhes dos tópicos não contemplados nos tutoriais anteriores, pois, desta maneira, será mais fácil focar nas partes referentes às atualizações feitas sobre o programa já existente, portanto, caso você tenha alguma dúvida sobre alguma parte do programa que não seja explicada neste momento, sugerimos que acesse o nossos materiais citados anteriormente.

Software

– Definindo os pré-requisitos para o funcionamento do código

Inicialmente, devemos incluir duas bibliotecas no código para que o mesmo pudesse funcionar corretamente. A biblioteca SPI.h, responsável pela comunicação dos módulos do shield com o Arduino UNO utilizando o protocolo SPI e a biblioteca Ethernet.h que atua possibilitando a conexão do conjunto, em um primeiro momento, com uma rede local.

Após a inclusão das bibliotecas citadas, devemos definir um endereço MAC (lembre-se que este pode ser qualquer um, desde que seja único em sua rede local) e um endereço IP (este deve ser um endereço válido  e disponível dentro da sua rede local). Além disso, devemos criar o objeto que será responsável por representar o Servidor Web no código (aqui, chamamos este de server) e relacioná-lo com a porta 80.

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };   
IPAddress ip(192, 168, 25, 16);                       
EthernetServer server(80);

– Declarando as variáveis responsáveis pela manipulação da requisição do navegador

Em seguida, declaramos duas variáveis do tipo String , as quais, conforme veremos posteriormente, serão responsáveis por armazenar informações referentes à requisição feita pelo navegador.

String requisicao_do_navegador;
String parametro_da_requisicao;

– Definindo as configurações iniciais

Dentro da função setup(), devemos determinar o modo de operação dos pinos digitais que serão utilizados (estes terão o papel de saídas digitais) . Além disso, iniciamos a conexão com a rede local através da função Ethernet.begin() (passando como parâmetro os endereços MAC e IP definidos anteriormente) e também iniciamos o Servidor Web por meio da sentença server.begin() (lembre-se que server é o objeto criado para representar o Servidor Web no código).

void setup()
{
    Ethernet.begin(mac, ip); 
    server.begin();                 
    pinMode(2,OUTPUT);
    pinMode(7,OUTPUT);
}

– Entendendo as requisições do navegador

Nos tutoriais anteriores, ressaltamos o mecanismo básico de funcionamento dos códigos voltados para a criação de Servidores Web, o qual, começa através de um procedimento padrão, que por sua vez, consiste em utilizar uma variável para obter caractere por caractere da requisição feita pelo navegador até que esta seja completamente recebida pelo Arduino UNO, de modo que, apenas a partir deste ponto inicia-se o processo de elaboração da página.

Aqui, teremos uma nova condição que deve ser cumprida para que o Servidor Web entregue uma página como resposta para o navegador. Esta é função definindo_a_requisição(), cujo papel consiste em determinar se a requisição feita pelo navegador cumpre alguns requisitos para que então o Servidor Web possa entregar a página para o navegador. Repare que o parâmetro da função citada é o texto completo de requisição feita pelo navegador (armazenado na variável requisição_do_navegador) e que a mesma se encontra como parâmetro de uma função if(), ou seja, caso a função definindo_a_requisição() retorne um valor verdadeiro, o conteúdo da função if() será executado.

if(definindo_a_requisicao(&requisicao_do_navegador))
{
      ****** Conteúdo da função if() ******
}

Veja como é a função definindo_a_requisição():

bool definindo_a_requisicao(String *requisicao) {
String parametro;
bool requisicao_desejada = false;

  parametrofinal = pegar_parametro_da_requisicao(requisicao);

  if (parametrofinal == "/") {
     requisicao_desejada = true;
  }

  if (parametrofinal.substring(0,2) == "/?") {
     requisicao_desejada = true;
  }  

  return requisicao_desejada;
}

Primeiramente, como já era previsto, esta função retorna um valor booleano, ou seja, verdadeiro ou falso. O que esta função faz é basicamente chamar outra função, denominada pegar_parametro_da_requisicao(), que por sua vez, será responsável por pegar a requisição feita pelo navegador, manipulá-la e gerar um resultado a partir da mesma, de modo que, este resultado ficará armazenado na variável parametrofinal. Vamos observar o comportamento da função pegar_parametro_da_requisicao() para depois analisarmos o que ocorre com a variável citada anteriormente.

String pegar_parametro_da_requisicao(String *requisicao) {
int pos_inicial, pos_final;
String parametro;

  
  pos_inicial = (*requisicao).indexOf("GET") + 3;
  pos_final = (*requisicao).indexOf("HTTP/") - 1;
  parametro = (*requisicao).substring(pos_inicial,pos_final);
  parametro.trim();
 
  return parametro;
}

– Exemplificando com a primeira requisição feita pelo navegador

Por hora, vamos apenas analisar as requisições que o navegador pode fazer, sem nos aprofundarmos muito, para então ver como isso reflete no código. Primeiramente, imagine que você gravou o programa que disponibilizamos aqui, no seu Arduino UNO e posteriormente foi no seu navegador e digitou o endereço IP predefinido no código (no nosso caso 192.168.25.16).

Ao realizar este procedimento, o navegador enviará a seguinte requisição para o Servidor Web:

Veja o que irá acontecer com esta informação:

  1. O programa irá receber todo conteúdo da requisição e armazenar o mesmo na variável requisicao_do_navegador. 
  2. A função definindo_a_requisicao() será executada e a requisição armazenada na variável requisicao_do_navegador será o parâmetro desta função.
  3. Dentro da função definindo_a_requisicao() haverá  a execução de uma outra função chamada pegar_parametro_da_requisicao(), que por sua vez, também terá como parâmetro a requisição armazenada na variável requisicao_do_navegador.
  4. A função  pegar_parametro_da_requisicao() é responsável por retirar e armazenar (na variável parametro) o pedaço da primeira linha da requisição, que está entre as palavras GET e HTTPneste exemplo, será ” / “. Entretanto, utilizando a função trim(), removemos os espaços antes e depois dos caracteres de fato, portanto, o conteúdo guardado na variável parametro será somente “/”.
  5. O conteúdo da variável parametro da função pegar_parametro_da_requisicao() será levado para a variável parametrofinal da função definindo_a_requisicao(), de modo que, quando esta for testada na primeira função if() dentro desta (a que compara a variável parametrofinal com o caractere “/“), haverá uma resposta positiva, portanto, esta resposta, true, será utilizada na função if() cujo argumento é a função definindo_a_requisicao() e o servidor poderá entregar uma página como resposta à requisição feita.

– Exemplificando com as outras requisições feitas pelo navegador

Como veremos posteriormente, quando clicamos em um checkbox para acionar umas das nossa saídas digitais, o navegador irá fazer a seguinte requisição (suponha que o clique aconteceu no checkbox referente ao pino 2):

Neste caso, a variável parametrofinal da função definindo_a_requisicao() será preenchida com “/?P2=1“. Repare que no primeiro if() existente nessa função terá como resultado, falso, já que o conteúdo da variável parametrofinal é diferente de “/”, no entanto, no segundo if(), temos a função substring(), que por sua vez, irá pegar somente os dois primeiros caracteres da string “/?p2=1“, ou seja, “/?” e irá comparar com “/?, gerando portanto, true, que será utilizado na função if() cujo parâmetro é a função definindo_a_requisicao(), permitindo assim, que o nosso Servidor Web responda à requisição com uma página.

O próximo tipo de requisição ocorre quando desmarcamos um checkbox, observe:

Nesta requisição ocorre algo semelhante ao que aconteceu anteriormente, de modo que, a variável parametrofinal da função definindo_a_requisicao() é justamente a string “/?“, validando portanto, o segundo if() desta função e também o if() cujo parâmetro é a função definindo_a_requisicao(). Portanto, perceba que neste tipo de requisição, o Servidor Web tem permissão de responder à requisição com uma página.

– Exemplo de hipótese em que o servidor não deverá responder

Como vimos anteriormente, existem 3 requisições que o Servidor Web deverá responder através da criação de uma pagina, estas são: Primeiro acesso ao endereço IP do Servidor Web, quando um checkbox é marcado e quando um checkbox é desmarcado.

À título de curiosidade, existe uma requisição, que é comumente feita pelo google chrome (navegador que estou utilizando) a um servidor, relacionada ao ícone que deve aparecer na aba do navegador ao conectar-se com o servidor em questão. Esta requisição é da seguinte forma:

Note que quando este tipo de requisição for tratado, o conteúdo da variável parametrofinal será a string “/favicon.ico“, que por sua vez, não se enquadra nas condições necessárias para que o programa responda através de uma pagina criada, portanto, a condição da função if() cujo parâmetro é a função definindo_a_requisicao(), não será satisfeita. Sendo assim, o que irá ocorrer é a execução do complemento else desta função if(), o qual, simplesmente envia uma mensagem de cabeçalho sem alterar o conteúdo da página enviada anteriormente.

if(definindo_a_requisicao(&requisicao_do_navegador))
{
      ****** Conteúdo da função if() ******
}
else 
{ 
      client.println("HTTP/1.1 200 OK"); 
}

– Construção da página

Após a permissão para o Servidor Web responder com uma página, utilizamos a variável parametro_da_requisicao para conter um dos 3 tipos parâmetros permitidos anteriormente (“/”, “/?”,”/?P2=1″). Observe que, neste momento, estamos fazendo o uso apenas das strings entre os elementos GET e HTTP, sem os espaços antes e depois.

parametro_da_requisicao = pegar_parametro_da_requisicao(&requisicao_do_navegador);

Posteriormente, enviamos o cabeçalho padrão de uma pagina da Web em HTML.

client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();

Em seguida, começamos a construir, de fato, a página. Neste momento, utilizamos apenas os conceitos já apresentados nos tutoriais anteriores.

client.println("<!DOCTYPE html>");
client.println("<html>");
client.println("<head>");
client.println("<title>Servidor Web VDS</title>");
client.println("</head>");
client.println("<body>");
client.println("<h1><font color=#4279c7>Servidor Web do Vida de Sil&iacute;cio</font></h1>");
client.println("<hr/>");                         
client.println("<h1>Sa&iacute;das digitais</h1>");

Com esta porção de código, nossa página estará assim:

Servidor web com ethernet shield W5100
Adição de títulos na página

– Criação de formulários

O próximo passo que iremos fazer é criar um formulário através do par <form></form>.

Os formulários são seções de um documento que contém controles interativos, que por sua vez, permitem ao usuário submeter informações a um determinado Servidor Web.

Dentro da tag de abertura do formulário devemos declarar qual método que será utilizado para submeter o mesmo, de modo que, o método que será utilizado por nós será o método GET. Sendo assim, declaramos nosso formulário da seguinte maneira:

client.println("<form method=\"get\">");

      ***** Conteúdo do formulário *****

client.println("</form>");

Note que, no código acima, temos uma string sendo passada como parâmetro para a função println(), portanto, entre aspas duplas, no entanto, a palavra get também deve vir entre aspas duplas , por isso, utilizamos o conjunto \” para simbolizar aspas duplas dentro da string.

– Recebendo dados do formulário

Em seguida, chamamos uma função denominada saida(), que por sua vez, será responsável por criar e controlar o checkbox referente à saída digital do pino 2.

saida(client);

O que a função saida() faz é basicamente pegar o conteúdo da variável parametro_da_requisicao e através da função indexOf(), ver se nele existe a porção de texto “P2=1”.

A função indexOf() retorna um número inteiro referente à posição do primeiro caractere do conjunto de caracteres buscado em uma determinada string, portanto, caso o primeiro caractere do conjunto citado esteja no começo da string, a função retornará o número 0, logo, se compararmos o retorno desta função com -1 , estaremos apenas verificando se existe o tal conjunto de caracteres na string (isso é o que importa para nós).

– Acionando as saídas

Caso exista o conjunto de caracteres “P2=1”, o led presente na saída digital 2 é acionado e o servidor incluirá na programação da página, a linha referente à criação de um objeto de entrada de dados, do tipo checkbox. O que você deve entender neste momento é o seguinte:

  1. Na declaração do checkbox, existe dois parâmetros, name e value.
  2. Quando o checkbox é marcado, o navegador irá associar estes dois parâmetros e enviar esta associação na requisição, por exemplo, quando o checkbox referente ao pino digital 2 for marcado, o navegador associará o name P2 ao value 1 e irá anexar o conjunto sob a forma /?P2=1 na requisição, conforme vimos anteriormente.
  3. Esta requisição chega ao servidor e ele analisa, se existe algum elemento que comece, por exemplo, com /?.
  4. Na presença do elemento citado anteriormente, o servidor é autorizado a responder com uma página da web, porém ele deve identificar por inteiro como é o elemento que começa com /?, de modo que, se este for /?P2=1, o servidor irá perceber que o checkbox referente ao pino de saída digital 2 que foi marcado.
  5. Quando o servidor constata que o checkbox citado que foi marcado, o led é acionado e permanecerá assim até que o checkbox em questão seja desmarcado.
void saida(EthernetClient cl)
{
        
        if (parametro_da_requisicao.indexOf("P2=1") > -1) 
        { 
           digitalWrite(2, HIGH);
           cl.println("<input type=\"checkbox\" name=\"P2\" value=\"1\" onclick=\"submit();\" checked > Pino digital 2");
        } 
        else 
        {
           digitalWrite(2, LOW);
           cl.println("<input type=\"checkbox\" name=\"P2\" value=\"1\" onclick=\"submit();\" > Pino digital 2");
        }
        
}

O mesmo procedimento é realizado para o outro pino de saída digital utilizado, no entanto, este é identificado nas requisições por /?P7 = 1.

Além disso, vamos supor que você marque o checkbox referente ao pino 2, de modo que, a requisição do navegador para o servidor traga /?P2 = 1. Em um determinado momento, você também marcou o checkbox referente ao pino 7, logo, é válido pensar que, na nova requisição haverá somente o conjunto /?P7 = 1no entanto, como o checkbox referente ao pino 2 ainda está marcado, o elemento que virá na requisição será /?P2=1&P7=1

Servidor Web com Ethernet Shield W5100
Resultado final

Considerações finais

Neste tutorial, demonstramos como você pode fazer para controlar as saídas digitais do seu Arduino UNO através de um  navegador. Para isto, utilizamos um Arduino UNO em conjunto com um Ethernet Shield W5100. Esta foi a terceira parte de uma série de artigos sobre a criação de Servidores Web, portanto, esperamos que continue nos acompanhando e sinta-se à vontade para nos dar sugestões, críticas ou elogios. Lembre-se de deixar suas dúvidas nos comentários abaixo.

Privacy Preference Center