Exibir dados e comandar dispositivos por um web server é uma tarefa bem interessante. E, com o NodeMcu, é possível fazer isso sem muita dificuldade. Portanto, vamos aprender como receber e enviar dados entre o NodeMcu e seu próprio web server.

Na aula anterior, aprendemos a criar um web server que exibe algumas informações básicas.


Informações importantes

Antes de saber como enviar ou receber dados, é importante entender sobre a comunicação por trás dos métodos. Como disse na aula anterior, um web server utiliza o protocolo HTTP para se comunicar. Veja abaixo o mesmo exemplo que mostrei antes:

Exemplo de uma mensagem com protocolo HTTP

E, o HTTP possui uma série de métodos de solicitação de dados, dentre eles temos, resumidamente:

GET

Utilizado para solicitar dados do servidor. Os parâmetros são enviados junto ao URL da solicitação. Exemplo:

GET /arquivo.php?parametro1=valor1&parametro2=valor2 HTTP/1.1

Host: siteexemplo.com

O GET possui limitação no tamanho da requisição e é menos seguro, pois pode ficar armazenado no histórico (um dos motivos).

POST

Utilizado para enviar enviar dados para criar/atualizar um recurso do servidor. Os parâmetros são enviados no corpo da solicitação. Exemplo:

POST /arquivo.php HTTP/1.1

Host: siteexemplo.com

parametro1=valor1&parametro2=valor2

O POST não possui limitação no tamanho da requisição e é mais seguro por não armazenar nada no histórico.

PUT

Mesma coisa do método POST. A diferença é: no caso do PUT, se um parâmetro igual for enviado várias vezes, ele sobrescreve o dado anterior. Já o POST, envia um novo registro do dado informado.

HEAD

Mesma coisa do método GET. A diferença é: no caso do HEAD, o servidor não retorna todo o conteúdo (não retorna o corpo). O HEAD retorna apenas informações “resumidas” do conteúdo, no caso o cabeçalho (por isso a palavra HEAD).

Exemplo: descobrir o tamanho de um arquivo antes de efetivamente baixá-lo.

DELETE

Utilizado para deletar um determinado recurso do servidor. Exemplo:

DELETE /arquivo.html HTTP/1.1

OPTIONS

Utilizado para saber quais métodos são aceitos pelo servidor.

Existem ainda outros métodos, como TRACE, CONNECT e PATCH. Entretanto, não entrarei em detalhes, pois meu objetivo é de apenas dar um breve panorama dos principais métodos existentes para entender o funcionamento por trás dos recursos que mostrarei nesta aula e na aula seguinte.


Exibindo dados no web server do NodeMcu

Exibir os dados de, por exemplo, um sensor no web server, é relativamente simples. Tendo em vista o que aprendemos na aula anterior, basta enviar os dados junto com a página em HTML.

Vamos considerar que desejo mostrar na página qual valor a porta analógica está lendo. Portanto, na hora de enviar os dados da página, envio também a leitura – adc.read(0) – em uma linha separada. E, toda hora que quisermos saber o valor atual do sensor, teremos que atualizar a página.

Porém, isso não é nada prático. Então, temos que criar uma forma de atualizar a página automaticamente a cada x segundos. Para isso, temos o elemento ‘meta’ e o método de refresh:

<meta http-equiv=”refresh” content=”2″>

O código acima manda a página ser atualizada 2 segundos a partir de sua exibição. Ou seja, a página aparece, 2 seg depois é atualizada e o processo se repete.

Sendo assim, basta inserir esse código em nossa página e mudar o tempo para que o valor da porta analógica seja atualizado automaticamente. Além disso, precisamos colocar o meta entre os elementos <head> e </head> (cabeçalho). Caso contrário, de acordo com testes que fiz, o navegador não interpreta corretamente o código enviado e exibe a página de forma errada.

Código de exemplo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
wifi.setmode(wifi.STATION)
wifi.sta.config {ssid="NOME_DO_SEU_WI-FI", pwd="SENHA_DO_WI-FI"}

tmr.alarm(0, 1000, 1, function() --Aguarda o esp conectar na rede
    if wifi.sta.getip() == nil then
        print("Conectando à rede...\n")
    else
        ip=wifi.sta.getip()
        print("IP: ",ip)
        tmr.stop(0)
    end
end)

srv=net.createServer(net.TCP)
srv:listen(80, function(conn)
    conn:on("receive",function(client,payload)
        print(payload)

        client:send("<head><meta http-equiv='refresh' content='1'></head>") --Atualiza a página a cada 1s
        client:send("<h1>Recebendo dados da porta analógica</h1>")
        client:send("<p>Valor da porta analógica:</p>")
        client:send(adc.read(0)) --Envia o valor do sensor
        client:send("<p>A página é atualizada sozinha</p>")

        client:on("sent", function(conn) conn:close() end) --Finaliza a conexão
    end)
end)

Observe que, logo depois de criar o parágrafo <p>Valor da porta analógica:</p>, enviei os dados do sensor. E em seguida enviei o restante da página. O resultado do código acima pode ser visto abaixo:

recebendo dados pela página nodemcu

Também é possível envolver os dados do sensor em um paragrafo:

client:send(“<p>” .. adc.read(0) .. “</p>”)

Usando arquivo HTML

É possível também utilizar a técnica de enviar o código HTML para dentro da placa (como vimos na aula anterior). Porém, não dá para simplesmente escrever ‘adc.read(0)’ juntamente com o restante do código HTML, pois isso será interpretado como um texto e não um comando.

Então, temos que fazer uma quebra de linha entre essas partes. Dessa forma, na hora do código ler linha por linha do arquivo HTML, nós identificamos essa parte do código e enviamos o comando certo.

Ou seja, você deverá fazer o seguinte: no código HTML, vá na linha onde deseja exibir os dados do sensor; pule uma linha do elemento anterior; escreva os dados (ex: adc.read(0)); pule mais uma linha; continue o resto do código.

Para o código anterior, considerando que a página está em um arquivo HTML, temos:

<p>Valor da porta analógica:</p>
adc.read(0)
<p>A página é atualizada sozinha<\p>

Então, os três elementos estão separados, cada um em uma linha. Agora, basta criar um teste no código para, quando chegar na linha adc.read(0), o programa escrever o comando separadamente. Se deixar o código escrever apenas a leitura da linha, ele interpreta o ‘adc.read(0)’ como texto e não comando. Por isso, devemos colocar o seguinte if no envio da linha lida:

1
2
3
4
5
if string.match(_line, "adc.read%(0%)") then --se ele identificou a palavra adc.read(0) na linha lida, então
    client:send(adc.read(0))
else --senão, ele apenas escreve a linha
    client:send(_line)
end

Obs: o comando string.match exige a utilização do símbolo % antes de alguns caracteres, como ponto e parêntesis.

Você poderia escrever também, no código HTML, algum texto específico, como “VALOR AQUI”. Para que, na hora de testar no if, você verifica se chegou na linha escrito “VALOR AQUI”. Qualquer uma das formas são válidas.

O código completo pode ser visto abaixo:

Código completo por HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
wifi.setmode(wifi.STATION)
wifi.sta.config {ssid="NOME_DO_SEU_WI-FI", pwd="SENHA_DO_WI-FI"}

tmr.alarm(0, 1000, 1, function() --Aguarda o esp conectar na rede
    if wifi.sta.getip() == nil then
        print("Conectando à rede...\n")
    else
        ip=wifi.sta.getip()
        print("IP: ",ip)
        tmr.stop(0)
    end
end)


srv = net.createServer(net.TCP)

srv:listen(80,function(conn)
    conn:on("receive", function(client, payload)
        local _line       --Variavel para leitura das linhas
        if file.open("pagina1.html","r") then --Se abrir o arquivo corretamente
            repeat
                _line = file.readline() --Lê a linha
                if (_line~=nil) then  --Se não for nula, envia os dados
                    if string.match(_line, "adc.read%(0%)") then --se ele identificou a palavra adc.read(0) na linha lida, então
                        client:send(adc.read(0))
                    else --senão, ele apenas escreve a linha
                        client:send(string.sub(_line,1,-2))  --ignora o pulador de linha
                    end
                end
            until _line==nil --Repete até a ultima linha do arquivo
            file.close() --Termina a leitura do arquivo
        end

        client:on("sent", function(conn) conn:close() end) --finaliza a conexão
    end)
end)

Comandando dispositivos pelo web server

Para criarmos um elemento interativo na página da internet gerada pelo NodeMcu, precisamos utilizar o elemento ‘form’.

Elemento Form

O ‘form’ é um elemento utilizado para coletar informações fornecidas pelo usuário. Ele é um elemento que possui componentes para seleção ou caixas de texto para escrita. E, ao fim disso tudo, um botão para enviar os dados que foram preenchidos/selecionados. Por exemplo:

 Nome:

Avaliação:
Gostei
Não gostei

 

1
2
3
4
5
6
7
<form action="/vazio" method="GET"> 
Nome:
<input name="nome" type="text" value="Mundo" />
Avaliação:
<input name="avaliacao" type="radio" value="positivo" /> Gostei <br>
<input name="avaliacao" type="radio" value="negativo" /> Não gostei <br>
<input type="submit" value="Enviar" /></form>

Dentro do ‘form’, é preciso ter os elementos de entrada de dados. E existem vários tipos diferentes: lista de seleção, botão radial, caixa de texto etc. Se deseja saber melhor, recomendo este site.

Os elementos de entrada precisam de um nome e um valor. Esse dois são enviados como parâmetro na hora que clicamos no botão ‘Submit’. Então, o nome e o valor informarão o que o usuário enviou. Por exemplo, no caso acima, se você clicar no botão, será enviado: nome=Fábio&avaliacao=positivo (ou avalicao=negativo dependendo da escolha).

O botão submit, obviamente, também é essencial, pois é ele que garante o envio das informações. A única modificação dele é o ‘value’ que é o texto dentro do botão.

Outro parâmetro é o ‘action’, que define o endereço o qual os dados serão enviados. No caso acima coloquei ‘/vazio’ que se refere a página www.mundoprojetado.com/vazio. Normalmente, é colocado um endereço do servidor que irá receber os dados e processá-los.

Ao lado do ‘action’ existe o parâmetro ‘method’ que define o método de envio de dados a ser utilizado. É possível utilizar o GET e o POST. A princípio, a diferença é que um mostra os dados enviados no endereço do navegador e o outro não.

Tem um aspecto interessante nos botões que mostrarei abaixo. Mas, não vou entrar nos outros detalhes, então recomendo este link para conhecer melhor os parâmetros do form.

Comandando um LED

Para exemplificar, vou criar dois botões para comandar um LED (um para ligar e outro para desligar). Para não ter que escolher ligar ou desligar e ter que clicar em um botão submit, vou utilizar a função ‘onclick’ dos botões. Esta função permite assimilar algum comando quando o botão for clicado.

Então, farei uso do comando window.location.href=’/endereço’ que redireciona a página para o link atual com /endereço. Se a pessoa clicar no botão ligar, redireciono para a página ‘/ligar’ e ‘/desligar’ para o botão de desligar o LED. Portanto, não precisarei do botão submit. E, o código completo da página está mostrado abaixo:

1
2
3
4
5
6
<h1>Central de controle dos LEDs</h1>
<p> Apague e acione o LED apertando os botões abaixo:</p>
<form method="GET">
    <input type="button" value="Ligar" onclick="window.location.href='/ligar'">
    <input type="button" value="Desligar" onclick="window.location.href='/desligar'">
</form>

O resultado pode ser visto abaixo:

nodemcu comandando led pelo web server

Repare que, na imagem, eu tinha acabado de clicar no botão ligar e no endereço ficou ‘192.171.1.100/ligar’.

Identificando dados

Depois que os dados são recebidos pelo NodeMcu, nós ainda precisamos processá-los para garantir que ele execute a ação desejada. Aplicando o código html que criamos acima e clicando nos botões, podemos dar uma olhada em como os dados são recebidos (no payload):

mensagem recebida do web server do nodemcu

Ou seja, a parte que nos interessa está apenas no começo. O formato é “GET” e “/ligar” ou “/desligar”. Portanto, é só verificar se no payload (2º parâmetro da função ‘receive’) existe GET /ligar ou GET /desligar. Bastou adicionar este condicional depois do envio dos dados, juntamente com o acionamento/desligamento do LED:

1
2
3
4
5
6
7
if string.match(payload, "GET /ligar") then --Se encontrou o termo 'GET /ligar' no payload, então
    gpio.write(led, gpio.HIGH)
    print("Ligou o LED")
elseif string.match(payload, "GET /desligar") then --ou se encontrou o termo 'GET /desligar' no payload, então
    gpio.write(led, gpio.LOW)
    print("Desligou o LED")
end

Com isso, nosso código já é capaz de comandar o LED pelo web server sem problemas. No tópico mais abaixo farei algumas observações a respeito desse controle em geral.

Código completo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
led = 1 --Declarando pino do LED

wifi.setmode(wifi.STATION)
wifi.sta.config {ssid="NOME_DO_SEU_WI-FI", pwd="SENHA_DO_WI-FI"}

tmr.alarm(0, 1000, 1, function() --Aguarda o esp conectar na rede
    if wifi.sta.getip() == nil then
        print("Conectando à rede...\n")
    else
        ip=wifi.sta.getip()
        print("IP: ",ip)
        tmr.stop(0)
    end
end)

srv=net.createServer(net.TCP)
srv:listen(80, function(conn)
    conn:on("receive",function(client,payload)
        print(payload)

        client:send("<h1>Central de controle dos LEDs</h1>")
        client:send("<p> Apague e acione o LED apertando os botões abaixo:</p>")

        client:send("<form>") --Cria o form
        client:send("<input type="button" value="Ligar" onclick="window.location.href='/ligar'">")
        client:send("<input type="button" value="Desligar" onclick="window.location.href='/desligar'">")
        client:send("</form>")

        if string.match(payload, "GET /ligar") then
            gpio.write(led, gpio.HIGH)
            print("Ligou o LED")
        elseif string.match(payload, "GET /desligar") then
            gpio.write(led, gpio.LOW)
            print("Desligou o LED")
        end

        client:on("sent", function(conn) conn:close() end)
    end)
end)

Observações

Essa parte do controle do NodeMcu pelo navegador pode ser um pouco confusa a princípio por envolver muitos conceitos novos. Entretanto, gostaria de dizer que não existe apenas uma forma de fazer o controle de LEDs pelo Web Server. Poderia ter criado um form assim como mostrei mais acima do post, mas mostrei uma forma mais intuitiva de comandar um LED.

Tanto a parte de criar o código HTML, quanto a de criar o código para o NodeMcu, podem ser feitas de maneira diferente. Meu objetivo foi mostrar o direcionamento geral de como fazer as duas partes. É possível criar o form de muitas maneiras diferentes e é possível organizar melhor o código do NodeMcu criando funções para processar os dados.

Enfim, agora você já deve ser capaz de criar um web server no NodeMcu que pode comandar qualquer dispositivo que desejar. Ou então, um web server que mostre informações de um sensor.

Neste post aqui, mostro um código de reconhecimento de voz que utiliza um web server do NodeMcu. O único problema é que ele não funciona mais, mas ajuda muito a entender como fazer algumas coisas com o NodeMcu.

Acessando páginas com NodeMcu – Aula 8 – NB