Servidor Web de video con Esp32-Cam y MicroPython
Presentación
Este proyecto es una versión simplificada del Servidor Web de video con Esp32-Cam para capturar video que puede verse online en el servidor instalado en la misma placa, sin utilizar la librería uasyncio .
Una vez arrancada la placa nos muestra en la consola el IP asignado y al ingresar a traves del navegador se ve la pantalla con el video que se obtiene a traves de la camara. La imagen de la pantalla es la siguiente:
Materiales
Placa Esp32-Cam
Programador Esp32-Cam
Si bien por ser mas cómodo y práctico se ha usado el adaptador el mismo puede ser remplazado por una placa FTDI y el cableado pertinente.
Montaje del proyecto en la placa Esp8266
Para utilizar la placa Esp32-Cam con MicroPython es necesario flashear primero la misma con el firmware correspondiente. En el caso de este proyecto se utilizo el provisto por la pagina de https://github.com/shariltumin/esp32-cam-micropython-2022/blob/main/firmware.bin
Una vez instalado el firmware en la placa utilizando la utilidad que viene provista por el IDE Thonny, o por aquella que ustedes utilicen ademas del firmware quedara grabado en la placa un archivo boot.py que debe ser borrado y en su reemplazo copiar los archivos que se incluyen en el codigo desarrollado a continuacion.
Codigo
El codigo se ha escrito en dos archivos conec_wifi.py, main.py, site.py y html.py.
El archivo conec_wifi.py contiene los datos necesarios para la conexión a la red wifi a la que se va a conectar los cuales deben ser agregados en el lugar indicado.
Los archivos main.py y site.py son el nucleo del servidor, el nombre elegido para el primero es a efectos que el programa se inicie automaticamente.
Finalmente el archivo html.py contiene la página html que nos permite visualizar el video de la camara.
conec_wifi.py
#Configuracion de conexion WIFI:
from time import sleep
import network
class conexion:
red = "XXXXXXXXX"
clave = "xxxxxxxxx"
def __init__(mi, red='', clave=''):
network.WLAN(network.AP_IF).active(False)# Scan for available access points
mi.wlan = network.WLAN(network.STA_IF)
mi.wlan.active(True)
if red == '':
mi.red = conexion.red
mi.clave = conexion.clave
else:
mi.red = red
mi.clave = clave
def conectar(mi, red='', clave=''):
if red != '':
mi.red = red
mi.clave = clave
if not mi.wlan.isconnected():
mi.wlan.connect(mi.red, mi.clave)
def status(mi):
if mi.wlan.isconnected():
return mi.wlan.ifconfig()
else:
return ()
def esperar(mi):
cnt = 30
while cnt > 0:
print("Esperando ..." )
# con(mi.red, mi.clave) # Connect to an red
if mi.wlan.isconnected():
print('Se conecto a: %s' % mi.red)
print('IP: %s\nSUBNET: %s\nGATEWAY: %s\nDNS: %s' % mi.wlan.ifconfig()[0:4])
cnt = 0
else:
sleep(5)
cnt -= 5
return
def scan(mi):
return mi.wlan.scan()
main.py
from machine import reset
from time import sleep
import usocket as soc
import gc
import camera
from conec_wifi import conexion
import site
def clean_up(cs):
cs.close()
del cs
gc.collect()
def server(pm):
p=pm[0]
ss=soc.socket(soc.AF_INET, soc.SOCK_STREAM)
ss.setsockopt(soc.SOL_SOCKET, soc.SO_REUSEADDR, 1)
sa = ('0.0.0.0', p)
ss.bind(sa)
ss.listen(1) # cantidad de clientes simultaneos
print("Iniciar servidor", p)
while True:
ms='';rq=[]
try:
cs, ca = ss.accept()
except:
pass
else:
r=b'';e=''
try:
r = cs.recv(1024)
#print("r")
#print(r)
except Exception as e:
print(f"EX:{e}")
clean_up(cs)
try:
ms = r.decode()
rq = ms.split(' ')
except Exception as e:
print(f"RQ:{ms} EX:{e}")
clean_up(cs)
else:
if len(rq)>=2:
print(ca, rq[:2])
rv,ph=rq[:2]
rqp = ph.split('/')
pth=f'/{rqp[1]}'
site.app[pth](cs)
clean_up(cs)
continue
for i in range(5):
cam = camera.init()
print("Cámara esta lista?: ", cam)
if cam: break
else: sleep(2)
else:
print('Se acabó el tiempo')
reset()
if cam:
print("Cámara lista")
conec = conexion()
conec.conectar()
conec.esperar()
for i in range(5):
if conec.wlan.isconnected(): break
else: print("WIFI no listo. Esperar...");sleep(2)
else:
print("WIFI no listo. ¡No puedo continuar!")
reset()
#**********************************************************************
# configuración de la cámara
#pixformat': 0:JPEG, 1:Escala de grises (2bytes/pixel), 2:RGB
#framesize': 1:96x96, 2:160x120, 3:176x144, 4:240x176, 5:240x240
# 6:320x240, 7:400x296, 8:480x320, 9:640x480, 10:800x600
# 11:1024x768, 12:1280x720, 13:1280x1024, 14:1600x1200
# 15:1920x1080, 16:720x1280, 17:864x1536, 18:2048x1536
#quality: [0,63] un número más bajo significa una calidad más alta
#contrast: [-2,2] mayor número mayor contraste
#saturation: [-2,2] mayor número mayor saturación. -2 escala de grises
#brightness: [-2,2] mayor número mayor brillo. 2 más brillantes
#speffect: 0: sin efecto, 1:negativo, 2:blanco y negro, 3:rojizo,
# 4: verdoso, 5: azul, 6: retro
#whitebalance: 0:predeterminado, 1:soleado, 2:nublado, 3:oficina, 4:casa
#aelevels: [-2,2] Nivel AE: exposición automática
#aecvalue: [0,1200] Valor AEC: Control automático de exposición
#agcgain: [0,30] Ganancia AGC: Control automático de ganancia
#Los valores numericos son siempre numeros enteros
#****************************************************************************
# establecer la configuración de la cámara
camera.framesize(8) # frame size 480x320
camera.contrast(2) # maximo contraste
camera.speffect(0) # por defecto
site.ip=conec.wlan.ifconfig()[0]
site.camera=camera
server((80,)) # puerto 80
reset()
site.py
from html import pagina, emision
app={}
def route(p):
def w(g):
app[p]=g
return w
@route('/')
def root(cs):
p=pagina['imagen']%(f'http://{ip}/live',0)
ln=len(p)+2
cs.write(b'%s\r\n\r\n%s\r\n' % (emision['imagen']%ln, p))
@route('/live')
def live(cs):
cs.write(b'%s\r\n\r\n' % emision['stream'])
cs.setblocking(True)
pic=camera.capture
put=cs.write
hr=emision['cuadro']
while True:
try:
put(b'%s\r\n\r\n' % hr)
put(pic())
put(b'\r\n')
except Exception as e:
print(e)
break
html.py
pagina = {
'imagen':'''<!DOCTYPE html>
<html>
<head>
<title>ESP32 Camera</title>
<link rel="icon" href="data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=">
</head>
<body>
<h1 align:center>Cámara Esp32-Cam</h1>
<div style="display:flex; margin-top: 15%%; justify-content:center; align-items:center; height:600px;">
<img src="%s" style="height:100%%; transform:rotate(%sdeg);"/>
</div>
</body>
</html>
''',
}
emision = {
'imagen': """HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Connection: Closed
Content-Length: %d""",
'stream': """HTTP/1.1 200 OK
Content-Type: multipart/x-mixed-replace; boundary=frame
Connection: keep-alive
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Expires: Thu, Jan 01 1970 00:00:00 GMT
Pragma: no-cache""",
'cuadro': """--frame
Content-Type: image/jpeg""",
}