Web Server com a Franzininho WiFi Lab01 e MicroPython

Este post faz parte da série Franzininho WiFi com MicroPython

Introdução

Neste artigo, vamos explorar a conexão WiFi da Franzininho WiFi Lab01 para criar o nosso próprio Web Server. O objetivo é entender como funciona a comunicação entre cliente e servidor, para possibilitar o desenvolvimento de um servidor web capaz de controlar o estado do LED na placa, permitindo ativar e desativar.

O que é a Comunicação Cliente-servidor?

A estrutura cliente-servidor implica na comunicação entre dois programas distintos: o cliente, responsável por enviar solicitações, e o servidor, encarregado de responder a essas solicitações. Abaixo, temos um esquema que ilustra essa organização.

Web Server Franzininho WiFi
Figura 1 – Comunicação cliente-servidor.

Primeiramente, é essencial compreender o papel crucial dos sockets na implementação desse sistema. Os sockets são mecanismos de comunicação que permitem a troca de dados entre processos distintos.

Assim, o servidor cria um socket para estabelecer a conexão e aguarda conexões de clientes. Enquanto o cliente cria um socket e se conecta ao socket do servidor utilizando o endereço IP e a porta específicos do servidor. Como tanto o cliente quanto o servidor têm seus próprios sockets, a comunicação entre eles é bidirecional.

Nesse contexto, temos o método bind, utilizado para vincular o socket do servidor a uma interface de rede específica e a uma porta designada para aguardar conexões. Já no caso do cliente, esse procedimento geralmente não é necessário.

O listen é utilizado para colocar o socket do servidor em um estado de escuta, permitindo que ele aguarde por conexões de clientes. 

Do lado do servidor temos o accept utilizado para aceitar uma conexão de um cliente quando ela ocorre. Já no lado do cliente temos o método connect utilizado em um socket para iniciar uma conexão com um servidor. Quando uma conexão é estabelecida, o accept retorna um novo socket que representa a conexão com o cliente e o endereço do cliente.

Por fim, os métodos send e recv são utilizados para enviar e receber dados através de sockets.

Recursos necessários

Para iniciar o trabalho com os GPIOs, é essencial possuir o diagrama de pinos da placa à disposição, pois isso vai permitir que você identifique tanto os nomes quanto as funções associadas a cada um deles.

PinoRecurso
IO1LDR
IO2BT6
IO3BT5
IO4BT4
IO5BT3
IO6BT2
IO7BT1
IO8OLED_SDA
IO9OLED_SCL
IO10TFT_DC
IO11TFT_RES
IO12LED AZUL
IO13LED VERDE
IO14LED VERMELHO
IO15DHT11
IO17BUZZER
IO35TFT_SDA
IO36TFT_SCL
Tabela 1 – Franzininho WiFi Lab01 pinout

Código Web Server em MicroPython

Para criar um Web Server é preciso criar dois arquivos: boot e main. 

Boot

Com a Franzininho WiFi Lab01 conectada ao seu computador, abra o Thonny e crie um novo arquivo contendo o código do boot:

import socket, network
from machine import Pin
import esp, gc

# gerenciador de memória
esp.osdebug(None)
gc.collect()
# dados da rede
ssid = 'SUBSTITUA_PELO_SEU_SSID'
password = 'SUBSTITUA_PELA_SUA_SENHA'

# configurando como cliente
wlan = network.WLAN(network.STA_IF)
wlan.active(True)

# verifica conexão
if wlan.isconnected() == False:
    wlan.connect(ssid, password)

# espere conectar
while wlan.isconnected() == False:
    pass

print('connected!')
print(wlan.ifconfig())

led = Pin(13, Pin.OUT)

Como mencionado anteriormente, criamos um servidor web usando sockets e a API de sockets do Micropython. Além de importar a biblioteca de sockets, é necessário importar a biblioteca de rede – network. Ela nos permite conectar o ESP32, microcontrolador da Franzininho Wifi Lab01, a uma rede Wi-Fi.

É preciso, também, importar a classe Pin de ‘machine’ para interagir com os GPIOs da Franzininho:

import socket, network
from machine import Pin
import esp, gc

As duas últimas bibliotecas são importadas no intuito de realizar uma economia de recursos. A biblioteca ‘esp’ é importada para desativar as mensagens de depuração do sistema operacional, utilizando a função ‘osdebug’. Já a biblioteca ‘gc’, de garbage collector ou coletor de lixo, é uma maneira de recuperar a memória ocupada por objetos que não estão mais em uso pelo programa. Isso será útil para economizar espaço na memória flash:

esp.osdebug(None)
gc.collect()

Em seguida, são definidas duas variáveis: ssid e password que são responsáveis por guardar o SSID e a senha da sua rede, respectivamente, para que o ESP possa se conectar ao seu roteador:

ssid = 'SUBSTITUA_PELO_SEU_SSID'
password = 'substitua_pela_senha'

Lembre-se de alterar essas variáveis para conexão ocorrer corretamente.

Em seguida, vamos configurar o ESP32 para se conectar a uma rede Wi-Fi como cliente. Primeiro um objeto ‘WLAN’ é criado usando a classe ‘STA_IF’ de ‘network’. O ‘WLAN’ representa a interface sem fio do microcontrolador e ‘STA_IF’ indica que estamos configurando a interface como uma estação Wi-Fi – conhecido como cliente:

wlan = network.WLAN(network.STA_IF)

A configuração seguinte ativa a estação Wi-Fi no microcontrolador, permitindo que ele procure e se conecte a redes Wi-Fi disponíveis. ‘True’ indica que a estação Wi-Fi deve ser ativada:

wlan.active(True)

Após executar esses comandos, o microcontrolador ESP32 está pronto para se conectar a uma rede Wi-Fi como cliente. 

Para realizar a conexão, iniciamos verificando se o dispositivo já está conectado à rede Wi-Fi com ‘wlan.isconnected()’. Se o dispositivo não estiver conectado, ele tenta se conectar usando o nome da rede (SSID) e a senha fornecidos anteriormente.

Há um loop que espera até que o dispositivo esteja totalmente conectado à rede. O loop é mantido enquanto ‘wlan.isconnected()’ retornar falso, indicando que o dispositivo ainda não está conectado. O ‘pass’ significa que o loop não fará nada enquanto aguarda a conexão:

if wlan.isconnected() == False:
    wlan.connect(ssid, password)

while wlan.isconnected() == False:
    pass

Se a conexão ocorrer será exibido no terminal “connected!”. Em seguida, utilizando ‘ifconfig()’ de ‘WLAN’, serão impressas informações da configuração da interface de rede, como endereço IP, máscara de sub-rede, gateway, etc.

O servidor web será acessado em um navegador web utilizando o endereço IP fornecido pelo ‘ifconfig()’.

Por último, selecionamos o GPIO 13, que está associado ao LED verde e será o LED aceso na interface do servidor web.

Main

O main conterá o script que permitirá a construção da página web. Assim, começamos criando uma função chamada ‘web_page()’. Esta função retorna uma variável chamada ‘html‘, que contém o código HTML necessário para criar a página da web:

def web_page():
  if led.value() == 1:
    gpio_state="ON"
  else:
    gpio_state="OFF"
 
  html = """<html><head> <title>My Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,"> <style>html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
  h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}.button{display: inline-block; background-color: #e7bd3b; border: none;
  border-radius: 4px; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
  .button2{background-color: #4286f4;}</style></head><body> <h1>Franzininho Web Server</h1>
  <p>LED status: <strong>""" + gpio_state + """</strong></p><p><a href="/?led=on"><button class="button">ON</button></a></p>
  <p><a href="/?led=off"><button class="button button2">OFF</button></a></p></body></html>"""
  return html

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)

while True:
  conn, addr = s.accept()
  print('Got a connection from %s' % str(addr))
  request = conn.recv(1024)
  request = str(request)
  print('Content = %s' % request)
  led_on = request.find('/?led=on')
  led_off = request.find('/?led=off')
  if led_on == 6:
    print('LED ON')
    led.value(1)
  if led_off == 6:
    print('LED OFF')
    led.value(0)
  response = web_page()
  conn.send('HTTP/1.1 200 OK\n')
  conn.send('Content-Type: text/html\n')
  conn.send('Connection: close\n\n')
  conn.sendall(response)
  conn.close()

A página da web irá mostrar o estado atual do GPIO, indicando se o LED está ou não ativado. Então, antes de gerar o texto HTML, precisamos verificar o estado do LED. O estado é salvo na variável ‘gpio_state’:

if led.value() == 1:
  gpio_state="ON"
else:
  gpio_state="OFF"

Essa variável é incorporada ao texto HTML usando os sinais de “+” para concatenar strings. O texto em HTML cria uma página simples que exibe o texto “Franzininho Web Server”, o estado atual do LED e fornece dois botões para ligar e desligar o LED:

html = """<html><head> <title>My Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,"> <style>html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
  h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}.button{display: inline-block; background-color: #e7bd3b; border: none;
  border-radius: 4px; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
  .button2{background-color: #4286f4;}</style></head><body> <h1>Franzininho Web Server</h1>
  <p>LED status: <strong>""" + gpio_state + """</strong></p><p><a href="/?led=on"><button class="button">ON</button></a></p>
  <p><a href="/?led=off"><button class="button button2">OFF</button></a></p></body></html>"""
  return html

A página web será conforme mostrado na figura abaixo:

Web Server Franzininho WiFi
Figura 2 – Página web criada.

Definida a página web, é criado um servidor básico usando sockets no Python para responder às solicitações HTTP. Vamos entender o que está acontecendo em cada parte.

Primeiro, um socket é criado usando a família de endereços ‘AF_INET’, o qual indica que o protocolo de comunicação será o IPv4 (Internet Protocol version 4). Já o tipo de socket é definido como ‘SOCK_STREAM’ que é utilizado para conexões TCP. O socket é vinculado ao endereço IP vazio (”) e à porta 80. O método ‘listen(5)’ especifica o número máximo de conexões pendentes que o socket poderá ter:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)

No loop, ao receber uma conexão, a função ‘accept()‘ retorna um novo socket denominado ‘conn‘ e o endereço do cliente é armazenado na variável ‘addr‘. Feito isso, o ‘request’ recebe a solicitação HTTP do cliente e a solicitação é convertida para uma string:

conn, addr = s.accept()
request = conn.recv(1024)
request = str(request)

Após isso, é verificado se a solicitação contém ‘/?led=on‘ ou ‘/?led=off‘, que são os comandos para ligar ou desligar o LED. Se uma correspondência for encontrada, o estado do LED é alterado usando ‘led.value()‘. Em seguida, a função ‘web_page()‘ é chamada para gerar o conteúdo HTML da resposta:

led_on = request.find('/?led=on')
led_off = request.find('/?led=off')
if led_on == 6:
  led.value(1)
if led_off == 6:
  led.value(0)response = web_page()

Por último, enviamos a resposta do servidor de volta ao cliente após receber a solicitação HTTP. O comando ‘conn.send('HTTP/1.1 200 OK\n')’ envia a linha de status HTTP ao cliente, indicando que a solicitação foi bem-sucedida. Já ‘conn.send('Content-Type: text/html\n')’ é para informar que o tipo de conteúdo enviado é HTML.

O comando ‘conn.send('Connection: close\n\n')’ informa que a conexão TCP será fechada após a conclusão da resposta. E o comando ‘conn.sendall(response)’ envia o conteúdo gerado pela função web_page() (a página HTML criada anteriormente) para o cliente.

Finalmente, ‘conn.close()’ indica que a conexão com o cliente é fechada, liberando os recursos associados à conexão:

conn.send('HTTP/1.1 200 OK\n')
conn.send('Content-Type: text/html\n')
conn.send('Connection: close\n\n')
conn.sendall(response)conn.close()

Abaixo temos o resultado do servidor web, lembrando que para alterar o estado do led basta pressionar os botões do servidor:

Web Server Franzininho WiFi
Figura 3 – Resultados
Web Server Franzininho WiFi
Figura 4 – Resultados

Conclusão

Neste artigo, exploramos de maneira abrangente o desenvolvimento de um servidor web utilizando a placa Franzininho Wifi Lab01. Desde os estágios iniciais de estabelecimento da conexão até a criação da página HTML.

Além disso, discutimos o papel significativo dos métodos ‘bind’, ‘listen’, ‘accept’, ‘connect’,  ‘send’ e ‘recv’ na estrutura cliente-servidor, ressaltando suas respectivas funções na criação e gerenciamento de conexões.

Em resumo, tivemos uma introdução prática sobre a criação de um servidor web utilizando a Franzininho Wifi Lab01. Como sugestão para explorações futuras, recomenda-se a personalização da interface do servidor web, como também a exploração de possibilidades que permitam ao servidor ter mais funções para o controle dos recursos na placa.

Franzininho WiFi com MicroPython

Monitoramento ambiental usando um Sensor de Detecção de Chuva com a Franzininho WiFi Lab01 Estação meteorológica com a Franzininho WiFi Lab01
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
0 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Home » Software » Web Server com a Franzininho WiFi Lab01 e MicroPython

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: