Para criar um servidor de páginas Web no ESP32 será conveniente usar uma biblioteca para o efeito. A biblioteca micropython-aioweb que usaremos neste exemplo recorre ao paradigma da programação assíncrona já abordada anteriormente, bastando incluir o ficheiro web.py no nosso projeto.
Tal como consta do exemplo incluído na descrição da biblioteca, uma página web simples poderia ser servida pelo seguinte programa main.py:
import web import uasyncio as asyncio app = web.App(host='0.0.0.0', port=80) # root route handler @app.route('/') async def handler(r, w): # write http headers w.write(b'HTTP/1.0 200 OK\r\n') w.write(b'Content-Type: text/html; charset=utf-8\r\n') w.write(b'\r\n') # write page body w.write(b'Hello world!') # drain stream buffer await w.drain() # Start event loop and create server task loop = asyncio.get_event_loop() loop.create_task(app.serve()) loop.run_forever()
Quando o programa é executado é possível constatar em que IP o servidor se encontra (neste exemplo em 10.0.5.117):
Connecting to network... ................ Connected! network config: ('10.0.5.117', '255.255.255.0', '10.0.5.1', '10.0.5.1')
Pelo que se em seguida acedermos a partir de um browser a esse IP obteremos a página Web que apresenta a mensagem “Hello world!”:
Contudo no nosso caso pretendemos uma página que interaja com determinados componentes ligados ao ESP32 (concretamente um LED e um botão), pelo que teremos que usar websockets para o efeito (funcionalidade incluída na biblioteca escolhida).
Para além do ficheiro web.py, o programa que apresentamos de seguida é basicamente formado por dois ficheiros:
- main.py – script Python que cria o servidor e interage com o hardware
- index.html – ficheiro HTML que descreve o conteúdo da página web
Os respetivos códigos são mostrados de seguida:
import network import web import uasyncio as asyncio from machine import Pin led = Pin(21, Pin.OUT) led.value(False) but = Pin(23, Pin.IN, Pin.PULL_UP) async def checkbut(): global WS_CLIENTS state = True while True: if but.value() != state: state = not state msg = 'Button ' + ('OFF' if state else 'ON') print(msg) for ws_client in WS_CLIENTS: try: await ws_client.send(msg) except: continue await asyncio.sleep_ms(5) app = web.App(host='0.0.0.0', port=80) # Store current WebSocket clients WS_CLIENTS = set() # root route handler @app.route('/') async def index_handler(r, w): f = open('index.html') w.write(f.read()) f.close() await w.drain() # /ws WebSocket route handler @app.route('/ws') async def ws_handler(r, w): global WS_CLIENTS # upgrade connection to WebSocket ws = await web.WebSocket.upgrade(r, w) r.closed = False # add current client to set WS_CLIENTS.add(ws) while ws.open: # handle ws events evt = await ws.recv() if evt is None or evt['type'] == 'close': ws.open = False elif evt['type'] == 'text': msg = evt['data'] led.value(True if msg == "LED ON" else False) # remove current client from set WS_CLIENTS.discard(ws) # Start event loop and create server task loop = asyncio.get_event_loop() loop.create_task(app.serve()) loop.create_task(checkbut()) loop.run_forever()
Não é intenção desta unidade curricular abordar em profundidade a temática da construção de páginas web nem muito menos abordar o conceito dos Websockets aqui usados. O código apresentado pode contudo constituir um ponto de partida para a análise destas temáticas.
A página Web servida pelo ESP32 pode ser acedida através dum Browser indicando o IP atribuído ao ESP32 (neste caso particular o 10.0.5.137). Nesta página não só é possível controlar o estado dum LED na placa como também receber o estado dum dos seus botões assim que ele muda de estado.
Neste último exemplo o ESP32 está a aceder a uma rede WiFi existente nas suas imediações mas como já foi referido na ausência dessa rede, ele próprio pode criar um Access Point. Para tal o script boot.py tem que ser alterado em conformidade:
import network wlan = network.WLAN(network.AP_IF) wlan.config(essid='ESP32-AP', password='12345678', authmode=network.AUTH_WPA_WPA2_PSK) wlan.config(max_clients=5) wlan.active(True) print('network config:', wlan.ifconfig())
Neste caso é criada uma rede WiFi com o nome ESP32-AP cuja password de acesso é 12345678. Nessa rede, o ESP32-AP fica com o IP 192.168.4.1, pelo que deverá agora ser a esse IP que o browser deverá se ligar (desde que esteja a ser executado num dispositivo ligado a essa mesma rede).
De notar, que caso este servidor seja executado no simulador Wokwi, o nosso browser não consegue aceder nem a essa rede ESP32-AP nem à rede gerada pelo simulador (Wokwi-GUEST), pelo que nesta situação não nos é possível abrir a página Web.