Introdução
Neste artigo, vamos criar um servidor web para visualizar os dados coletados pela estação meteorológica montada com a Franzininho WiFi Lab01, permitindo assim um monitoramento remoto da estação. A estação será composta por:
- O DHT11 para medir temperatura e umidade,
- O BMP180 para medir pressão atmosférica e altitude,
- O MQ135 para avaliar a qualidade do ar,
- O sensor de chuva para monitorar as condições climáticas.
Recursos necessários
As instruções de como montar a Franzininho WiFi Lab01 para criar uma estação meteorológica, estão no artigo Estação meteorológica com a Franzininho WiFi Lab01.
Código
Para criar um servidor web é preciso criar dois arquivos: boot e main.
Importante: Os dois arquivos precisam ser salvos na placa.
- Acesse “Arquivo” > “Salvar como…” e selecione “dispositivo MicroPython”.

- Nomeie o arquivo e clique em OK para salvá-lo na placa.
Boot
Com a Franzininho WiFi Lab01 conectada ao seu computador, abra o Thonny e crie um novo arquivo contendo o código do boot. O nome do arquivo precisa ser boot.py para a conexão Wi-Fi ocorrer.
import socket, network
from machine import Pin, SoftI2C
import esp, gc, dht
from bmp180 import BMP180
# 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)
# caso já esteja conectado
if wlan.isconnected() == False:
wlan.connect(ssid, password)
# espere conectar
while wlan.isconnected() == False:
pass
print('connected!')
print(wlan.ifconfig())
# atribuição de pinos da Franzininho
i2c = SoftI2C(scl=Pin(9), sda=Pin(8))
# configurando DHT
d = dht.DHT11(Pin(15))
# configurando BMP180
bmp180 = BMP180(i2c)
bmp180.oversample_sett = 2
bmp180.baseline = 101325
# configurando pino do MQ135
mq135= Pin(5,Pin.IN)
#configurando sensor de chuva
sensor_chuva= Pin(2, Pin.IN)Para entender como ocorre a conexão Wi-Fi no arquivo boot, consulte o artigo Web Server com a Franzininho WiFi Lab01 e MicroPython.
Além de estabelecer a conexão de rede foi necessário inserir as configurações dos sensores utilizados para compor a estação meteorológica. Para isso, incluímos as bibliotecas necessárias para o funcionamento dos sensores, como: as bibliotecas para o sensor de temperatura e umidade (DHT), para comunicação I2C e para o sensor de pressão e altitude BMP180.
Além disso, foi preciso estabelecer a relação entre cada sensor e o respectivo pino ao qual está conectado no dispositivo.
# atribuição de pinos da Franzininho
i2c = SoftI2C(scl=Pin(9), sda=Pin(8))
# configurando DHT
d = dht.DHT11(Pin(15))
# configurando BMP180
bmp180 = BMP180(i2c)
bmp180.oversample_sett = 2
bmp180.baseline = 101325
# configurando pino do MQ135
mq135= Pin(5,Pin.IN)
#configurando sensor de chuva
sensor_chuva= Pin(2, Pin.IN)Por último, é preciso lembrar que o endereço IP para acessar o servidor é disponibilizado pela primeira informação que é exibida quando executamos o comando ‘ifconfig()’. Esse endereço pode variar por múltiplas razões, então é importante ter atenção ao endereço que aparecerá ao executar o comando na sua máquina.

Main
O main será composto pelo script que permitirá a construção da página web, juntamente com as funções que realizam as leituras dos sensores da estação.
def read_sensor():
global temp, hum, p, alt
temp = p = alt = hum = 0
try:
# coletando umidade e temperatura
d.measure()
temp = d.temperature()
hum = d.humidity()
#coletando pressão e altitude
p = round(bmp180.pressure, 2)
alt = round(bmp180.altitude, 2)
msg = (b'{0:.0f},{1:.0f},{2:3.1f},{3:3.1f}'.format(temp, hum, p, alt))
return(msg)
except OSError as e:
return('Failed to read sensor.')
def odor_sensor():
if mq135.value():
return ("Gás tóxico: não detectado")
else:
return ("Gás tóxico: detectado")
def rain_sensor():
if sensor_chuva.value():
return("Chuva: não detectada")
else:
return("Chuva: detectada")
def web_page():
odor = odor_sensor() # obter o valor do odor
rain = rain_sensor() # obter o valor do sensor de chuva
html = """<!DOCTYPE HTML><html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="refresh" content="3">
<meta charset="UTF-8">
<script src="https://kit.fontawesome.com/ae775d017c.js" crossorigin="anonymous"></script>
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h2 { font-size: 3.0rem; }
p { font-size: 3.0rem; }
.units { font-size: 2.2rem; }
.dht-labels{
font-size: 1.5rem;
vertical-align:middle;
padding-bottom: 15px;
}
.left-column {
float: left;
width: 50%; /* Define a largura da coluna esquerda */
}
.right-column {
float: right;
width: 50%; /* Define a largura da coluna direita */
}
</style>
</head>
<body>
<h2>Estação Meteorológica Franzininho</h2>
<div class="left-column">
<p>
<i class="fa-solid fa-temperature-three-quarters" style="color: #f46315;"></i>
<span class="dht-labels">Temperatura</span>
<span>"""+str(temp)+"""</span>
<sup class="units">°C</sup>
</p>
<p>
<i class="fas fa-tint" style="color:#00add6;"></i>
<span class="dht-labels">Umidade</span>
<span>"""+str(hum)+"""</span>
<sup class="units">%</sup>
</p>
<p>
<i class="fa-solid fa-cloud-showers-heavy" style="color: #36a3f7;"></i>
<span class="dht-labels"><b>"""+rain+"""</b></span>
</p>
</div>
<div class="right-column">
<p>
<i class="fa-solid fa-gauge-high" style="color: #08ba46;"></i>
<span class="dht-labels">Pressão</span>
<span>"""+str(p)+"""</span>
<sup class="units">Pa</sup>
</p>
<p>
<i class="fa-solid fa-mountain-sun" style="color: #296ea3;"></i>
<span class="dht-labels">Altitude</span>
<span>"""+str(alt)+"""</span>
<sup class="units">m</sup>
</p>
<p>
<i class="fa-solid fa-skull-crossbones" style="color: #e60000;"></i>
<span class="dht-labels"><b>"""+odor+"""</span>
</p>
</div>
</body>
</html>"""
return html
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', 80))
s.listen(5)
while True:
conn, addr = s.accept()
print("Got a connection from %s" % str(addr))
request = conn.recv(1024)
print("Content = %s" % str(request))
sensor_readings = read_sensor()
print(sensor_readings)
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 os dados coletados pelos sensores, indicando a temperatura, pressão, umidade, altitude e se há ou não a detecção de chuva e gases tóxicos. Então, antes de gerar o texto HTML, precisamos coletar os dados dos sensores.
Começando pela leitura dos sensores DHT11 e BMP180, foi criada a função ‘read_sensor()’. Essa função define quatro variáveis globais: ‘temp’, ‘hum’, ‘p’ e ‘alt’, que armazenarão os dados lidos pelos sensores DHT e BMP180, respectivamente.
def read_sensor():
global temp, hum, p, alt
temp = p = alt = hum = 0
try:
# coletando umidade e temperatura
d.measure()
temp = d.temperature()
hum = d.humidity()
#coletando pressão e altitude
p = round(bmp180.pressure, 2)
alt = round(bmp180.altitude, 2)
msg = (b'{0:.0f},{1:.0f},{2:3.1f},{3:3.1f}'.format(temp, hum, p, alt))
return(msg)
except OSError as e:
return('Leitura do sensor falhou.')Nessa função utilizamos o recurso ‘try’ e ‘except’ que é feito no intuito de lidar com possíveis erros durante a execução do código. Nesse caso, o erro ocorre quando há uma falha na leitura do sensor.
Se a leitura for bem sucedida, o retorno da função é ‘msg’: uma string que contém os valores de temperatura, umidade, pressão e altitude formatados. Os dois primeiros valores são ‘{0:.0f},{1:.0f}’ que definem temperatura e umidade, respectivamente, como pontos flutuantes sem casas decimais. Os dois últimos valores ‘{2:3.1f},{3:3.1f}’ definem pressão e altitude, respectivamente, como pontos flutuantes com casas decimais.
Se houver um erro na leitura o bloco ‘except’ irá retornar uma mensagem informando que houve um erro na leitura.
Em seguida, são definidas as funções para a leitura do sensor MQ135 e do sensor de chuva. Ambos são sensores que utilizam entradas digitais para determinar a detecção de determinada substância ou condição. Para isso, foram criadas funções que utilizam uma estrutura condicional ‘if’ e ‘else’ para verificar se a detecção foi realizada. Essas funções retornam uma mensagem que será exibida no servidor web, informando se a detecção ocorreu ou não.
def odor_sensor():
if mq135.value():
return ("Gás tóxico: não detectado")
else:
return ("Gás tóxico: detectado")
def rain_sensor():
if sensor_chuva.value():
return("Chuva: não detectada")
else:
return("Chuva: detectada")
Capturados os dados dos sensores, a página web pode ser criada. Iniciamos a função do servidor web com a seguinte tag meta:
<meta name="viewport" content="width=device-width, initial-scale=1">Ela permite que sua página web seja visualizada em qualquer navegador.
A tag <meta> instrui o navegador a atualizar automaticamente a página após um intervalo de tempo específico, neste caso, a cada 3 segundos:
<meta http-equiv="refresh" content="3">
A tag <script> é necessária para carregar os ícones usados na página da web a partir do site do Font Awesome.
<script src="https://kit.fontawesome.com/ae775d017c.js" crossorigin="anonymous"></script>
O site Font Awesome Icons contém os ícones que serão utilizados para representar os dados coletados pelos sensores. Para utilizar os ícones é simples, você precisará ir em ‘Start’ e seguir o tutorial para poder incorporar os ícones no seu html.
Entre as tags <style></style>, adicionamos código CSS para personalizar a página da web. Assim, basicamente, estamos definindo a página para exibir o texto com a fonte Arial em um bloco sem margem e alinhado ao centro. Depois, definimos o tamanho da fonte para o título (h2), parágrafo (p) e unidades (.units) das leituras. O tamanho, alinhamento e espaço dos dados lidos são definidos em ‘.dht-labels’.
Por último, para dividir a tela em duas partes e melhorar a exibição da estação meteorológica, criamos ‘.left-column’ e ‘.right-column’. No caso da ‘.left-column’, os elementos serão alinhados à esquerda, na ‘.right-column’, os elementos serão alinhados à direita, ambas as colunas terão 50% de largura.
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h2 { font-size: 3.0rem; }
p { font-size: 3.0rem; }
.units { font-size: 2.2rem; }
.dht-labels{
font-size: 1.5rem;
vertical-align:middle;
padding-bottom: 15px;
}
.left-column {
float: left;
width: 50%; /* Define a largura da coluna esquerda */
}
.right-column {
float: right;
width: 50%; /* Define a largura da coluna direita */
}
</style>
Nas tags <body></body> é onde adicionamos o conteúdo da página da web. Já as tags <h2></h2> adicionam um título à página da web. Neste caso, o texto “Estação Meteorológica Franzininho”.
Em seguida, dividimos o lado esquerdo da página e utilizamos parágrafos para exibir os dados de temperatura, umidade e detecção de chuva. Os parágrafos são delimitados pelas tags <p> e </p>. O parágrafo para a temperatura é o seguinte:
<p>
<i class="fa-solid fa-temperature-three-quarters" style="color: #f46315;"></i>
<span class="dht-labels">Temperatura</span>
<span>"""+str(temp)+"""</span>
<sup class="units">°C</sup>
</p>
As tags <i> são responsáveis por exibir os ícones do Font Awesome. Para obter o ícone de sua escolha pesquise pelo nome em inglês, por exemplo ‘temp’ e selecione a opção ‘Free’ para acessar os ícones gratuitos.
Clique no ícone desejado e, se necessário, altere a cor. Depois, basta copiar o texto HTML fornecido.
O html obtido, após alterar a cor do ícone, foi esse:
<i class="fa-solid fa-temperature-three-quarters" style="color: #f46315;"></i>
A tag <span> irá exibir ‘Temperatura’ com a fonte predefinida em ‘dht-labels’.
<span class="dht-labels">Temperatura</span>
A variável ‘temp’ é incorporada ao texto HTML usando os sinais de “+” para concatenar strings. Por último, as tags <sup></sup> fazem com que o texto seja sobrescrito e utilizamos esse recurso para mostrar as unidades dos dados.
Os demais parágrafos seguem a mesma lógica. Assim, a página web ficará conforme mostrado na figura abaixo.
Definida a página web, é criado um servidor básico usando sockets no Python para responder às solicitações HTTP. Para entender cada detalhe do servidor, consulte o artigo Web Server com a Franzininho WiFi Lab01 e MicroPython.
Abaixo está o resultado da visualização do servidor web. Optamos por mostrar a tela na horizontal para melhor visualização no celular, no entanto, isso resultou em uma dificuldade para exibir a página inteira. Logo, na primeira imagem, você pode ver o título da página e os dados coletados pelos sensores DHT11 e BMP180. Para visualizar os outros dados, é necessário rolar a tela para baixo.
Na segunda imagem, todas as informações estão visíveis, incluindo a detecção de chuva, indicada pela presença de uma gota de água sobre o sensor de chuva. Na terceira imagem, um recipiente de acetona foi aberto e o sensor detectou a presença da substância. Na quarta imagem, o sensor de chuva foi limpo e não há mais detecção de chuva.
Na última imagem, a acetona foi retirada do alcance do sensor, então não há detecção de gases ou chuva.
Conclusão
Neste artigo, construímos um servidor web básico para o monitoramento remoto de uma estação meteorológica. Exploramos o processo de conexão da placa ao WiFi, estabelecimento da conexão cliente-servidor e criação de uma página web simples.
É importante destacar que este projeto é apenas o ponto de partida para uma infinidade de possibilidades. Como sugestão para implementações futuras, recomenda-se a personalização da interface do servidor web e a exploração de possibilidades para adicionar mais funcionalidades ao servidor, permitindo um maior controle dos recursos na placa.




