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:
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¶metro2=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¶metro2=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:
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:
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:
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):
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
Muito bom seu post. Me ajudou muito.
Muito obrigado, fico feliz por ter ajudado. Achar conteúdo detalhado assim sobre o NodeMcu é um pouco complicado.
oi boa tarde, eu tenho um led e gostaria de fazer ele piscar com um tempo desejado, criei uma pagina e estou passando o valor digitado para uma variavel dentro do html, como posso pegar este valor e fazer o led piscar de acordo com ele ?
Olá. Para enviar o valor digitado para o o NodeMcu, você deve usar um form na sua página. Na parte que explico sobre o form, mostrei um com um dado do tipo “text” e dois do tipo “radio”. Para o seu caso, você deve usar um form do tipo “text”. Exemplo:
<form action=”” method=”GET”>
Delay LED:
<input name=”delay” type=”text” value=”1000″ />
<input type=”submit” value=”Enviar” /></form>
Assim que você clicar no botão “Enviar”, a página irá enviar o valor inserido para o NodeMcu. Ai basta processar o dado recebido para gerar o tempo do LED piscar. E não se esqueça de mudar o parâmetro “action” de acordo com sua necessidade. Se você ficar na dúvida, dê uma olhada neste outro post que tem um exemplo interessante. E qualquer coisa volte a perguntar.
Fiquei com uma duvida, eu preciso escrever o código do form no nodemcu também? pq se eu fizer isso acho que vai desconfigurar o layout da minha página. No meu caso eu vou ativar um rele por 5 segundos
Depende do que você quer fazer exatamente. Se você vai ativar o relé pela página, você vai precisar de um form na página para receber o comando e enviar para o NodeMcu. E, como a criação da página é feita dentro do NodeMcu, o código do form, no fim das contas, está no NodeMcu. Pegando o próprio exemplo do post, há uma parte do código do NodeMcu onde criamos a página em html e outra onde processamos os comandos enviados pelo form. A não ser que sua página seja criada externamente (um servidor) e você esteja acessando ela pelo NodeMcu, aí só precisaria do form na página e o NodeMcu conteria apenas o código para processar os dados enviados pelo form.
Sendo sincero, acho que não entendi muito bem a sua duvida.
Vou tentar explicar melhor, eu tenho um sistema feito em php externamente, todo prontinho e funcionando, mas eu quero um botão nesse sistema pra eu controlar um rele no nodemcu por exemplo. Como eu faço isso?
Entendi. Neste caso, seu sistema teria que enviar uma requisição para o NodeMcu. Isso porque, no caso do post, o web server é do próprio NodeMcu, então ele consegue reconhecer as requisições que são feitas na página de forma direta. No seu caso, qualquer requisição feita no seu sistema seria reconhecida apenas pelo próprio sistema. Portanto, dentro do seu sistema, você tem que adicionar algum botão ou algo assim que envie uma requisição para o servidor do NodeMcu (utilizando o mesmo IP do webserver). Com isto, o NodeMcu precisará apenas detectar a requisição feita pelo seu sistema externo e poderá executar a ação que você determinar, por exemplo, controlar um relé. E não será necessário criar uma página para o NodeMcu. A ideia é parecida com a do post de controlar o NodeMcu por aplicativo (o aplicativo faz requisições para o web server do NodeMcu).
Entendi, não sei como fazer isso, se puder fazer uma aula com esse exemplo em específico, eu agradeço muito, não achei na internet
É uma boa ideia, mas eu não sei absolutamente nada de php. O que posso fazer é ensinar a fazer isso por meio de um servidor externo em HTML, que tal? Inclusive, para fazer isso em HTML, basta criar um form no seu servidor externo desta forma:
2
3
4
<input type="submit" value="Ligar">
<input type="submit" value="Desligar">
</form>
Sendo que, no parâmetro “IP-Do-NodeMcu”, você troca pelo IP do NodeMcu. E, lembrando, que o servidor externo e o NodeMcu devem estar na mesma rede.
Se você só puder fazer isto por php, sugiro dar uma olhada neste link que ensina como fazer isto (olhe a primeira resposta do link). Você vai entender muito melhor que eu o código de exemplo de requisição do link. Ai bastaria trocar a “url” pelo IP do NodeMcu e os dados (“data”) pelos dados que você quer enviar.
Se você puder fazer em html mesmo eu agradeço muito, não tenho a necessidade de ser em php não. Até pq eu só quero ativar o rele, esperar 5 segundos e ele desativar sozinho, por exemplo.
Beleza então! Vou tentar fazer esse fim de semana pra postar no início da semana que vem. E uma última dúvida, a forma como você pretende acionar o relé é por meio de um botão “Ligar” (por exemplo) ou é de alguma outra forma específica?
Opa, muito obrigado. Sim, é um botão ligar, quando eu acionar o botão, o relé vai ficar ativo por um determinado tempo em segundos e desligar sozinho
Por nada, Lucas! Vou fazer o post com este exemplo então. E acompanha o site, que segunda eu devo postar se tudo der certo.
Beleza, vou acompanhar aqui. Valeu
Com certeza, um tutorial muito interessante!
Porém, pela complexidade do assunto, não o entendi corretamente e tenho uma questão: tenho um sketch Arduino com ESP8266 para um Relógio + Termômetro usando um sensor DHT22. Os dados de Data e Hora são obtidos de um servidor de intranet (HTTP_SERVER) mas, está localizado em outro país e, eu gostaria de aproveitar tal sketch, portanto, é possível, colocando um servidor brasileiro no sketch? Se sim, qual e como fazer?
Deixo aqui parte do sketch como referência:
// — HTTP CLIENT ————————————————————————
// Este é o meu servidor de intranet, que está executando uma API REST. Você precisará de um desses. (texto autor)
#define HTTP_SERVER “http://10.0.0.159/iotServer.php”
typedef enum {TIME_INFO, DATE_INFO} DatetimeInfo;
HTTPClient http;
Meus antecipados agradecimentos
Obrigado pela mensagem, Daniel! Um possível motivo da dificuldade do entendimento é que as aulas sobre o NodeMcu são baseadas na programação utilizando a IDE Esplora que utiliza a linguagem de programação Lua. Portanto, é um pouquinho mais difícil do que utilizar a IDE do Arduino.
Enfim, respondendo sua pergunta, imagino que a solução seja apenas trocar o IP do servidor estrangeiro para um IP de um servidor local. Analisando o pedaço de código que você disponibilizou, suponho que esta mudança seja feita na linha: #define HTTP_SERVER “http://10.0.0.159/iotServer.php”
No lugar do “http://10.0.0.159/iotServer.php”, tente colocar um IP de um servidor brasileiro.
Fazendo uma rápida pesquisa, encontrei este site brasileiro, que fornece servidores de horário. Entretanto, não sei se os IPs disponíveis são compatíveis com o NodeMcu. Faça o teste com um destes IPs e me conte se deu certo! Fico no aguardo…
Boa tarde, gostaria de fazer um sistema de alarme, para simular um desabamento de barragem, utilizando sensores de vibração e inclinação. Monitorando os dados dos sensores via web.
Como ficaria, para montar os bando de dados e mostrar na web?
Desde já, agradeço!
Boa noite, Renato. Acredito que o NodeMcu não tenha nenhuma biblioteca “oficial” sobre banco de dados. Portanto, você teria que criar uma estrutura própria para gravar os dados na memoria interna com a ajuda da biblioteca file. E a parte de mostrar na web é do jeito que está explicitado no post mesmo. Enfim, espero ter ajudado.
Opa Fábio!
Muito bom seu tópico! Muito esclarecedor.
Mas você falou de dois métodos GET e POST, no qual POST, em sua maioria das vezes é melhor. Mas não exemplificou como pegar os dados que chegam via POST. Explicou apenas via GET. Poderia exemplificar, por favor?
Abraço.
Eai, Judenilson. Eu é que agradeço pelo seu retorno!
Você tem razão, esta parte do método POST poderia ter sido melhor mostrada. Assim que possível, irei adicionar um exemplo nesta página.
Mas, enquanto isto, aqui no site, tem outra página que mostra um sistema de reconhecimento de voz utilizando o NodeMcu. Nele, além de utilizar o método POST, utilizei outras “técnicas” interessantes para criar o sistema, então sugiro dar uma olhadinha. A parte do “form” está explicada no tópico “Criação do web server”.
De todo modo, o importante é você saber que, quando utilizamos o método POST, a resposta enviada pelo servidor fica localizada no corpo da mensagem. Portanto, não é possível identificar um comando da forma como fizemos no GET:
if string.match(payload, “POST /algumacoisa”) then
Teríamos, na verdade, que verificar pelo comando diretamente:
if string.match(payload, “algumacoisa”) then
Enfim, espero ter ajudado.
Abraço!
Parabéns pelo post. Eu estava há vários dias tentando colocar o meu esp32 pra se conectar com a web e nada. Agora depois que li seu post deu tudo certo.
Obrigado.
Muito obrigado, Edmar. Que bom que funcionou certinho!
post muito show.
gostaria de uma ajuda, como eu enviaria dados para um servidor python (flask) do nodemcu, e salvaria os dados do ldr, por exemplo, em um arquivo css. no caso eu preciso usar post e get? pra onde eu mando?
Obrigado! Infelizmente, não vou conseguir te ajudar, pois não tenho conhecimento sobre o flask. Mas acredito que o caminho para fazer isso seja semelhante ao que pode ser visto na aula 8 (NodeMcu enviando requisições para servidores externos). Talvez a aula 12 ajude também.
como poderia fazer um botão dentro do doctype e incrementar uma variável no esp32? para ajuste analógico por exemplo?
gostaria de saber como uso o server.on nesse caso com a biblioteca ESPAsyncWebServer,
Olá, Thiago. Infelizmente não vou conseguir te ajudar amigo. Sei que pode ser parecido, mas nunca mexi com o esp32 e nem com a biblioteca ESPAsyncWebServer.