Como usar ESP8266/ESP32 como servidor TCP e cliente com soquetes

Cómo utilizar ESP8266/ESP32 como servidor TCP y cliente con sockets

ESP8266 y ESP32 son placas de desarrollo WiFi populares. Estas placas de microcontrolador son ideales para construir el Internet de las cosas (IoT). Aquí cubrimos cómo conectar ESP8266 y ESP32 (o cualquier puerto MicroPython) con una conexión de red para usar como estación WiFi. Usamos el módulo de red MicroPython.

Este módulo de red proporciona un controlador para crear una interfaz de red. La interfaz depende del medio de red (WiFi o Ethernet) y del hardware del puerto específico para ese medio. Utilizando una interfaz de red como WLAN (WiFi) o Ethernet, ESP8266/ESP32 o cualquier puerto MicroPython se puede conectar a Internet. El mismo módulo también es útil para gestionar determinadas configuraciones del enrutador.

Se requiere un socket MicroPython o un módulo Usocket para acceder a diferentes servicios de red. Ejemplos de servicios de red incluyen World Wide Web (WWW), búsqueda de nombres de dominio (DNS), correo electrónico, servidor de archivos, servicios horarios, VoIP, red de sensores inalámbricos, servicios de directorio, intercambio de archivos, servidor de archivos, mensajería instantánea, administración de redes. protocolos, y hay otros.

Una vez que el ESP8266/ESP32 está conectado a Internet a través de WiFi o Ethernet (usando un módulo de red), se pueden usar para varias aplicaciones basadas en IoT. Para IoT, ESP8266 y ESP32 se conectan a servicios de red/Internet a través de acceso de bajo nivel, que se proporciona a través del soporte de socket básico de los sistemas operativos/firmware subyacentes.

Por ejemplo, MicroPython proporciona módulos de socket y usocket para la programación de red a nivel de socket de puertos y tarjetas compatibles. El módulo socket es una reimplementación de un subconjunto del módulo CPython con el mismo nombre. El módulo Usocket es una implementación extendida del módulo de socket integrado de MicroPython.

Con la ayuda de sockets, ESP8266/ESP32 se puede configurar como servidor o cliente TCP/IP. Las tarjetas también se pueden configurar como servidor o cliente UDP . ESP8266/ESP32 o cualquier puerto MicroPython se puede utilizar para cualquier función de red, como servidor web o cliente.

En este artículo, cubriremos los conceptos básicos del Protocolo de control de transmisión/Protocolo de Internet (TCP/IP) y las redes UDP. También discutiremos cómo se puede configurar el ESP8266/ESP32 como servidor web o cliente dentro de una red de área amplia utilizando el módulo de socket. Finalmente, probaremos una aplicación IoT ESP8266/ESP32 simple como servidor web.

¿Qué es un socket de Python?
Los sockets se utilizan para la programación de redes de bajo nivel. En programación, un socket es el punto final de comunicación entre dos programas que se ejecutan en una red. Facilitan la comunicación bidireccional entre dos dispositivos en red. Una aplicación de sockets típica es una aplicación cliente-servidor donde uno de los dispositivos en red es un servidor y el otro es un cliente.

Los sockets proporcionan comunicación entre procesos (IPC), lo que significa que los sockets (o puntos finales) se comunican dentro de un proceso, entre procesos en una o más máquinas. Los sockets de dominio Unix se comunican entre procesos en la misma máquina. Los sockets de Internet, los sockets Berkley o los sockets BSD se comunican entre procesos en diferentes máquinas. La red en la que dos dispositivos participan en la comunicación a nivel de socket puede ser una red de área local (LAN), una red de área amplia (WAN) o Internet.

El módulo de socket CPython facilita una interfaz para la API de socket de Berkeley que se asigna directamente a las llamadas del sistema a nivel de sistema operativo/firmware. El módulo de socket MicroPython es un subconjunto reimplementado del mismo módulo.

Es importante tener en cuenta que en redes, el término "socket" se refiere al puerto de red y la dirección IP de un dispositivo. El puerto es el punto final que especifica el tipo de servicio de red o proceso específico. Es un identificador de 16 bits para el protocolo de red y el servicio de red combinados. El protocolo de red puede ser TCP/IP o UDP.

Esta tabla enumera algunos de los números de puerto comúnmente utilizados para diferentes servicios de red TCP/IP y UDP.

Números de puerto para diferentes servicios de red TCP/IP y UDP.

La dirección de Protocolo de Internet (IP) es un identificador numérico de un dispositivo conectado a la red informática que utiliza IP para la comunicación. La combinación de una dirección IP y un número de puerto (es decir, socket) es un identificador único de:

  • El dispositivo de origen/destino en la red informática.
  • El protocolo de red (protocolo IP) utilizado para la comunicación.
  • El servicio/proceso de red involucrado en la comunicación.

Socket = Dirección IP + Número de puerto de red

Los sockets MicroPython se refieren a sockets de programación, no a sockets de red (dirección IP y número de puerto). Sin embargo, algunos métodos API de socket toman el socket de red (dirección IP y número de puerto) como parámetros, como el método de conexión. El parámetro se acepta como un nombre de host y un número de puerto separados.

Acceso de bajo nivel versus acceso de alto nivel acceso de alto nivel
Python estándar proporciona dos tipos de acceso a redes informáticas: de bajo nivel y de alto nivel.

  • El acceso de bajo nivel lo proporcionan los sockets. Los sockets utilizan llamadas al sistema desde el sistema operativo/firmware subyacente para comunicarse con el dispositivo en el otro extremo de la red. Los sockets pueden implementar protocolos orientados a conexión y sin conexión.
  • El acceso de alto nivel se proporciona mediante la implementación de protocolos de Internet en la capa de aplicación, como HTTP, FTP, DNS, etc.

MicroPython proporciona sólo acceso de bajo nivel a la programación de red porque sus dispositivos de destino son microcontroladores y controladores integrados. La implementación directa del acceso de alto nivel requiere muchos recursos y no es factible con plataformas como los microcontroladores.

MicroPython configura fácilmente puertos con soporte de acceso de bajo nivel como servidores o clientes. La programación de sockets incluso permite que un servidor se comunique con múltiples clientes simultáneamente.

Protocolos de red TCP/IP vs. UDP
Dos protocolos de red ampliamente utilizados son TCP y UDP. Ambos protocolos son parte del conjunto TCP/IP, un conjunto de protocolos en capas utilizados para la comunicación a través de Internet. Esta suite se basa en un modelo cliente-servidor donde los clientes envían solicitudes al servidor. El servidor puede atender solicitudes de varios clientes.

Hay cuatro capas de TCP/IP:

  • Capa de aplicación: se implementan protocolos como HTTP, HTTPS y FTP.
  • Capa de transporte: los datos se comunican como un datagrama mediante TCP.
  • Capa de red: la conexión a Internet se establece a través de IP, donde los dispositivos de origen y destino se identifican mediante direcciones IP.
  • Capa de enlace de datos: los datos se transmiten a través de Internet como bits al dispositivo de destino.

Un análisis de la arquitectura de red TCP/IP.

TCP y UDP son los dos protocolos de capa de transporte. Los sockets pueden usar TCP y UDP para configurar cliente-servidor. TCP es el protocolo más fiable ya que comprueba exhaustivamente si hay errores. Por este motivo, es utilizado por varios protocolos de capa de aplicación, como HTTP, HTTPS, FTP, SMTP, etc.

Un beneficio de UDP es que utiliza menos recursos. Pero en general es menos confiable y tiene una verificación de errores limitada. Servicios como VoIP suelen utilizar UDP.

La mayoría de los dispositivos IoT dependen del protocolo TCP para comunicar datos entre un servidor TCP y clientes TCP, particularmente cuando los protocolos HTTP, HTTPS o SMTP funcionan en la capa de aplicación. Las tarjetas IoT suelen configurarse como un servidor TCP o un cliente TCP.

Dirigiéndose a las familias
Los dispositivos de origen y destino se identifican mediante direcciones IP en la capa de red. Sin embargo, existen diferentes tipos de direcciones IP. Cada tipo se reconoce por su familia de direcciones.

Direcciones de familias y sus identificadores en la programación de sockets.

Definiendo un enchufe
En la programación de redes, un socket se define mediante un método constructor, socket.socket, que toma dos parámetros. Uno de los parámetros es una familia de direcciones, especificada por los identificadores mencionados en la tabla anterior. El segundo parámetro es el protocolo de transporte, TCP o UDP.

La red TCP está especificada por socket.SOCK_STREAM. La red UDP está especificada por socket.SOCK_DGRAM. Una definición de socket típica en Python es:

conector de importación
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Si se utiliza la familia de direcciones AF_INET, la dirección IPv4 se especifica con un nombre de host y un número de puerto (es decir, mediante un socket de red). El nombre de host puede ser una cadena que represente un sitio/ubicación de Internet o la dirección IP numérica de un sitio/dispositivo. El número de puerto es un número entero que especifica el servicio de red. Por ejemplo, el número de puerto especifica HTTP, 443 especifica HTTPS, etc.

¿Servidor o cliente TCP?
Al diseñar una aplicación IoT, una de las primeras preguntas es si el puerto/placa IoT de MicroPython debe configurarse como servidor o cliente TCP. En el modelo cliente-servidor, un cliente realiza una solicitud al servidor y el servidor cumple esa solicitud. El servidor no puede iniciar la comunicación.

Esta es una consideración importante al configurar un dispositivo IoT en una red. El dispositivo debe configurarse como un servidor TCP si está diseñado para operaciones de control operativo u otros componentes. Esto asegura que pueda recibir comandos de control desde dispositivos cliente como una computadora, un teléfono celular o un control remoto.

El dispositivo IoT debe configurarse como un cliente TCP si las operaciones de control dependen de la retroalimentación de un servidor IoT o una plataforma en la nube. Lo mismo se aplica si el dispositivo está diseñado para mostrar o manipular datos desde un servidor/nube de IoT remoto.

Por ejemplo, la placa debe configurarse como un servidor TCP si el dispositivo opera una placa IoT/retransmisión de puerto MicroPython a través de una página web. En su lugar, debe configurarse como un cliente TCP si la hora de visualización del puerto MicroPython/tarjeta IoT se recibe de un servidor de hora.

El módulo de enchufe
El módulo de socket MicroPython proporciona acceso a Internet o a la interfaz de socket Berkeley/BSD. El módulo de socket de firmware MicroPython implementa directamente una interfaz similar a un archivo (flujo). Esto es diferente del módulo de socket de CPython y es por eso que los objetos de socket no necesitan convertirse en objetos similares a archivos usando la función makefile.

El módulo de socket se importa a un script MicroPython usando esta instrucción:

conector de importación

El módulo contiene una clase de socket que incluye todos los métodos para crear y configurar una interfaz de socket de Internet. Para hacer esto se llama al método constructor. Tiene este prototipo:

clase socket.socket(af=AF_INET, tipo=SOCK_STREAM, proto=IPPROTO_TCP, /)

El método constructor toma tres parámetros:

  • La familia de direcciones determina el tipo de dirección IP utilizada en la capa de red.
  • El tipo de socket determina la selección de red TCP o UDP.
  • El número de protocolo determina el tipo de protocolo TCP o UDP que se utilizará.

El número de protocolo es opcional y debe evitarse. Normalmente se omite en los puertos MicroPython y se selecciona automáticamente según el tipo de socket.

El siguiente método del módulo de socket configura un puerto MicroPython como servidor TCP...

socket.bind(dirección) : vincula el socket a una dirección IP. El encaje aún no debe estar atado.

socket.listen : permite que el servidor TCP acepte conexiones. Se puede pasar un parámetro de trabajo pendiente a este método, que especifica el número de conexiones no aceptadas antes de rechazar nuevas conexiones. Si no se proporciona el parámetro, el servidor elegirá cualquier valor razonable.

socket.accept : permite al servidor aceptar una nueva conexión. El socket ya debe estar vinculado a una dirección IP y estar escuchando conexiones. Este método devuelve un par de valores: conexión y dirección. La conexión es un nuevo objeto de socket que se utiliza para transmitir y recibir datos a través de la nueva conexión. La dirección es la dirección IP del dispositivo en el otro extremo de la red con el que se comunican los datos.

El siguiente método del módulo de socket configura un puerto MicroPython como cliente TCP...

socket.connect(dirección) : conecta el socket del cliente a un socket del servidor remoto. La dirección IP del socket del servidor remoto se pasa como parámetro.

Los siguientes métodos del módulo de socket administran la comunicación de datos en el servidor y el cliente TCP.

socket.send(bytes) : se utiliza para enviar datos al otro socket y debe estar conectado a un socket remoto. El método devuelve el número de bytes enviados al socket remoto. Tanto los sockets del servidor como del cliente utilizan este método.

socket.sendto(bytes, dirección) : se utiliza para enviar datos a un socket remoto, que está determinado por la dirección IP pasada como parámetro. El enchufe aún no debe estar conectado al enchufe remoto. Este método es más útil en un servidor TCP, que puede interactuar con varios clientes.

socket.sendall(bytes) : se utiliza para enviar todos los datos al socket remoto, transmitiéndolos en fragmentos consecutivos. El enchufe debe estar conectado al enchufe remoto. El comportamiento de este método para sockets sin bloqueo no está definido.

socket.write(buf) : envía un búfer de bytes al socket remoto. El enchufe debe estar conectado a un enchufe remoto. Este método escribe todos los datos en el socket remoto siguiendo una política de "no escritura corta". Esto no supone ningún problema para bloquear sockets a los que los datos siempre se envían correctamente. Para sockets sin bloqueo, este método devuelve la cantidad de bytes enviados, que puede ser menor que el tamaño real del búfer. Se sabe cuántos bytes se envían correctamente al socket sin bloquearse.

socket.recv(bufsize) : recibe datos del socket remoto. Devuelve un objeto de bytes que representa los datos recibidos. El tamaño máximo de los datos recibidos está determinado por el tamaño del objeto del búfer que se pasa como parámetro.

socket.recvfrom(bufsize) : recibe datos del socket remoto. El enchufe debe estar conectado a un enchufe remoto. El método devuelve el objeto de bytes (que representa los datos recibidos y la dirección IP) del socket remoto. Este método es más útil en un servidor TCP, que puede conectarse a varios clientes.

socket.read((size)) : lee desde el socket remoto hasta el número de bytes, especificado como parámetro de tamaño. Si no se especifica ningún parámetro de tamaño, lee todos los datos del socket remoto hasta que recibe EOF (es decir, hasta que se cierra el socket remoto). Este método sigue una política de "no lecturas breves" que le permite leer todos los datos solicitados. Para sockets sin bloqueo, este método aún no puede leer todos los datos.

socket.readinto(buf(,nbytes)) : lee datos del socket remoto en un objeto de búfer. Si se especifica el parámetro bytes, solo lee el número especificado de bytes en el búfer. Este método devuelve el número de bytes leídos y almacenados en el objeto de búfer y sigue una política de "no lecturas cortas" como el método socket.read.

socket.readline : Lee una línea terminada por un carácter de nueva línea desde el socket remoto. Devuelve la línea leída desde el socket remoto.

socket.close : cierra un socket y libera los recursos que contiene. Una vez que se cierra un socket, cualquier operación futura fallará. Cuando un socket está cerrado, el socket remoto puede recibir una indicación EOF siempre que sea compatible con el protocolo. Pero los sockets se cierran automáticamente cuando se recolecta basura y deben cerrarse explícitamente para mejorar la eficiencia.

Los siguientes métodos del módulo de socket modifican o cambian el comportamiento del socket...

socket.setsockopt(level, optname, value) : tenga en cuenta que los sockets tienen un comportamiento estándar predeterminado. El comportamiento de un socket se puede modificar cambiando varias opciones de socket. Este método establece el valor de una opción de socket determinada. Las opciones de socket que se pueden configurar con este método se proporcionan como constantes a partir de SO_ o SOL_ en el módulo de socket. El valor que se puede pasar a las opciones del socket es un número entero o un objeto de bytes.

socket.setblocking(flag) : establece un socket en modo de bloqueo o no bloqueo. Si el indicador se establece en Verdadero, el socket se establece en modo de bloqueo. Si el indicador se establece en Falso, el socket se establece en modo sin bloqueo. Este método sirve como abreviatura del método socket.settimeout. El modo predeterminado del socket es el bloqueo. Un socket de bloqueo no regresa al programa hasta que se completa el evento solicitado. Los sockets sin bloqueo responden inmediatamente al programa, revelando si la acción solicitada se ha completado o no. Un socket de bloqueo devuelve un número de error si el evento/acción solicitado está en curso.

socket.settimeout(value) : establece un tiempo de espera para bloquear sockets. El tiempo de espera se especifica como un valor de coma flotante no negativo. Si el valor del tiempo de espera se establece en '0', el socket se colocará en modo sin bloqueo. Si el valor del tiempo de espera se establece en ninguno, el socket se coloca en modo de bloqueo. Este método no está disponible en todos los puertos/placas MicroPython.

socket.makefile(mode='rb') : devuelve un objeto de archivo asociado con el socket. El método makefile de MicroPython no admite errores, codificación ni parámetros de nueva línea como el método makefile de CPython. Tampoco admite el almacenamiento en búfer de transmisión. Si se establece el parámetro de búfer, se ignorará y se considerará '0' (es decir, sin búfer). El único parámetro requerido es "modo". Sólo se admiten los modos binarios rb, wb y rwb. Si se cierra un objeto de archivo devuelto por este método, el socket original también se cierra.

Las funciones proporcionadas por el módulo de enchufe son las siguientes...

socket.getaddrinfo : convierte un socket de red (es decir, host/puerto) en una secuencia de 5 tuplas que se puede usar para crear un socket. El formato de la tupla de 5 es (familia, tipo, proto, nombre de canon, sockaddr). Este método se utiliza a menudo para resolver nombres de host textuales. El siguiente ejemplo imprime la dirección IP resuelta de ingenierosgarage.com.

conector de importación
sockaddr = socket.getaddrinfo('www.engineersgarage.com', 80)(0)(-1)
imprimir (direccióncalcetín)

Al ejecutar el código anterior, se devuelve la siguiente dirección IP después de resolver el nombre de host...

socket.inet_ntop(af,bin_addr) : convierte una dirección de red binaria en una representación textual según la familia de direcciones especificada. Aquí hay un ejemplo válido de esta función:

socket.inet_ntop(socket.AF_INET, b”\x7f\0\0\1″)

El resultado de esta llamada a función es: '127.0.0.1'

socket.inet_pton(af,txt_addr) : convierte una dirección de red textual de la familia de direcciones dada en su representación binaria. Aquí hay un ejemplo válido de esta función:

socket.inet_pton(socket.AF_INET, “127.0.0.1”)

El resultado de esta llamada a función es: b'\x7f\0\0\1′

El módulo de socket MicroPython no admite excepciones socket.error.

El módulo Usocket
El módulo Usocket de MicroPython es similar al módulo socket, que puede integrarse en el puerto/placa MicroPython o instalarse desde micropython-lib. Si no se encuentra el script del módulo de socket, utilice el script del módulo de socket. Ambos trabajan.

Configurar ESP8266/ESP32 como cliente TCP usando sockets
El siguiente código MicroPython configura ESP8266/ESP32 como un cliente TCP que lee el código fuente HTML de una página web. La página web debe tener protocolo HTTP y no protocolo HTTPS.

red de importación

estación = red.WLAN(red.STA_IF)
si station.isconnected == Verdadero:
imprimir(“Ya conectado”)
estación.activa (Verdadero)
station.connect(“SSID”, “CONTRASEÑA”)
mientras que station.isconnected == Falso:
gastar
imprimir(“Conexión exitosa”)
imprimir(estación.ifconfig)

def http_get(url):
conector de importación
_, _, host, ruta = url.split('/', 3)
dirección = socket.getaddrinfo(host, 80)(0)(-1)
s = enchufe.socket
s.connect(dirección)
s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (ruta, host), 'utf8'))
mientras que Verdadero:
datos = s.recv(200)
si datos:
imprimir(cadena(datos, 'utf8'), fin=”)
otro:
romper
s.cerrar

http_get('

Debes reemplazar el SSID y CONTRASEÑA en la línea 7 con el SSID y clave de red de tu propia conexión WiFi.

El resultado del script MicroPython anterior se muestra a continuación.

Leer páginas web cifradas con SSL
El ESP8266 configurado anteriormente no puede leer el contenido HTML de páginas cifradas con SSL. MicroPython tiene poco soporte para el protocolo SSL. Sin embargo, es posible leer el contenido de páginas cifradas con SSL utilizando Python estándar en una computadora personal.

Pruebe el siguiente script de Python en una Raspberry Pi o en su computadora.

def http_get(url):
conector de importación
importar SSL
_, _, host, ruta = url.split('/', 3)
real_host = host.replace(“ “”).replace(“ “”).split(“/”)(0).split(“:”)(0)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
solicitud = bytes('GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n' % (ruta, host), 'utf8')
contexto = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)

s = contexto.wrap_socket(s, nombre_host_servidor = host_real)
s.connect((host_real, 443))
s.sendall(solicitud)
concurso = s.recv(5000).decode
imprimir (concurso)
como contexto! =b”:
concurso = s.recv(5000).decode
imprimir (concurso)
s.cerrar

http_get('

El script Python anterior lee el código fuente HTML de una página web cifrada con SSL de Engineergarage.com. Tenga en cuenta que la URL del sitio web y Engineersgarage.com utilizan protocolos HTTPS. El script Python ejecuta un cliente TCP y devuelve el contenido HTML de la URL proporcionada, como se muestra en las imágenes a continuación.

Configurar ESP8266/ESP32 como servidor TCP usando sockets
El siguiente script de MicroPython configura el ESP8266 como un servidor TCP. El servidor ESP8266 utiliza la dirección IP del enrutador conectado. Cuando un cliente solicita el servidor, como una computadora conectada a la misma conexión WiFi, devuelve una página HTML.

red de importación
máquina de importación

estación = red.WLAN(red.STA_IF)
si station.isconnected == Verdadero:
imprimir(“Ya conectado”)
estación.activa (Verdadero)
station.connect(“SSID”, “CONTRASEÑA”)
mientras que station.isconnected == Falso:
gastar
imprimir(“Conexión exitosa”)
imprimir(estación.ifconfig)

html = “””

Servidor ESP8266

Bienvenido al servidor ESP8266




“””

conector de importación
dirección = socket.getaddrinfo('0.0.0.0', 80)(0)(-1)

s = enchufe.socket
s.bind(dirección)
s.escuchar(1)

print('escuchando', dirección)

mientras que Verdadero:
cl, dirección = s.aceptar
print('cliente conectado desde', dirección)
cl_file = cl.makefile('rwb', 0)
mientras que Verdadero:
línea = cl_file.readline
si no linea o linea == b'\r\n':
romper
respuesta=html
cl.send('HTTP/1.0 200 OK\r\nTipo de contenido: texto/html\r\n\r\n')
cl.enviar(respuesta)
cl.cerrar

Debe reemplazar el SSID y la CONTRASEÑA en la línea 8 del código anterior con el SSID y la clave de red de su propia conexión WiFi. Para ejecutar el servidor, cargue y ejecute el script MicroPython anterior en el ESP8266 usando uPyCraft IDE o Thonny IDE.

La siguiente captura de pantalla muestra el servidor ESP8266 ejecutándose y respondiendo a un cliente.

Para realizar una solicitud al servidor ESP8266, escriba la dirección IP devuelta por el método station.ifconfig en la barra de direcciones de su navegador. El servidor ESP8266 devolverá una página HTML en respuesta al navegador de su computadora.

La siguiente captura de pantalla es un ejemplo de una página web devuelta por el servidor ESP8266.

Regresar al blog

2 comentarios

Olá bom dia, como implementar uma comunicação entre o Labview como servidor e esp32 como servidor? Estou com dificuldade nisso,e qualquer é bem vindo.

Miguel

Olá bom dia, como implementar uma comunicação entre o Labview como servidor e esp32 como servidor? Estou com dificuldade nisso,e qualquer é bem vindo.

Miguel

Deja un comentario

Ten en cuenta que los comentarios deben aprobarse antes de que se publiquen.