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

ESP8266 e ESP32 são placas de desenvolvimento WiFi populares. Essas placas microcontroladoras são ideais para construir a Internet das Coisas (IoT). Abordamos como conectar ESP8266 e ESP32 (ou qualquer porta MicroPython) com uma conexão de rede para uso como estação WiFi aqui. Usamos o módulo de rede MicroPython.

Este módulo de rede fornece um driver para criar uma interface de rede. A interface depende do meio de rede (WiFi ou Ethernet) e do hardware específico da porta para esse meio. Usando uma interface de rede como WLAN (WiFi) ou Ethernet, ESP8266/ESP32 ou qualquer porta MicroPython pode se conectar à Internet. O mesmo módulo também é útil para gerenciar certas configurações de roteador.

O soquete do MicroPython ou o módulo usocket é necessário para acessar diferentes serviços de rede. Exemplos de serviços de rede incluem World Wide Web (WWW), Pesquisa de Nome de Domínio (DNS), e-mail, servidor de arquivos, serviços de horário, VoIP, rede de sensores sem fio, serviços de diretório, compartilhamento de arquivos, servidor de arquivos, mensagens instantâneas, gerenciamento de rede. protocolos, e há outros.

Uma vez que o ESP8266/ESP32 esteja conectado à internet via WiFi ou Ethernet (usando um módulo de rede), eles podem ser usados ​​para diversas aplicações baseadas em IoT. Para a IoT, ESP8266 e ESP32 conectam-se aos serviços de Internet/rede por meio de acesso de baixo nível, que é fornecido por meio de suporte básico de soquete dos sistemas operacionais/firmware subjacentes.

Por exemplo, MicroPython fornece módulos soquete e usocket para programação de rede em nível de soquete de portas e placas suportadas. O módulo soquete é uma reimplementação de um subconjunto do módulo CPython com o mesmo nome. O módulo usocket é uma implementação estendida do módulo soquete integrado do MicroPython.

Com a ajuda de soquetes, ESP8266/ESP32 pode ser configurado como servidor ou cliente TCP/IP. As placas também podem ser configuradas como UDP servidor ou cliente. ESP8266/ESP32 ou qualquer porta MicroPython pode ser usada para qualquer função de rede como servidor web ou cliente.

Neste artigo, abordaremos os conceitos básicos do Protocolo de Controle de Transmissão/Protocolo de Internet (TCP/IP) e redes UDP. Também discutiremos como o ESP8266/ESP32 pode ser configurado como um servidor web ou cliente dentro de uma rede de longa distância usando o módulo soquete. Por fim, testaremos uma aplicação IoT simples do ESP8266/ESP32 como servidor web.

O que é um soquete Python?
Sockets são usados ​​para programação de rede de baixo nível. Na programação, um soquete é o ponto final de uma comunicação entre dois programas executados em uma rede. Eles facilitam a comunicação bidirecional entre dois dispositivos em rede. Uma aplicação típica de soquetes é uma aplicação cliente-servidor onde um dos dispositivos em rede é um servidor e o outro é um cliente.

Os soquetes oferecem comunicação entre processos (IPC), o que significa que os soquetes (ou endpoints) se comunicam dentro de um processo – entre processos em uma ou mais máquinas. Os soquetes de domínio Unix comunicam-se entre processos na mesma máquina. Soquetes de Internet, soquetes Berkley ou soquetes BSD comunicam-se entre processos em máquinas diferentes. A rede em que dois dispositivos estão envolvidos na comunicação em nível de soquete pode ser uma rede local (LAN), uma rede de área ampla (WAN) ou a Internet.

O módulo de soquete do CPython facilita uma interface para a API de soquete Berkeley que mapeia diretamente para as chamadas de sistema no nível do sistema operacional/firmware. O módulo de soquete do MicroPython é um subconjunto reimplementado do mesmo módulo.

É importante notar que, em redes, o termo “soquete” se refere à porta de rede e ao endereço IP de um dispositivo. A porta é o terminal que especifica o tipo de serviço de rede ou o processo específico. É um identificador de 16 bits para o protocolo de rede e serviço de rede combinados. O protocolo de rede pode ser TCP/IP ou UDP.

Esta tabela lista alguns dos números de porta comumente usados ​​para diferentes serviços de rede TCP/IP e UDP.

Números de porta para diferentes serviços de rede TCP/IP e UDP.

O endereço de protocolo da Internet (IP) é um identificador numérico de um dispositivo conectado à rede de computadores que usa IP para comunicação. A combinação de um endereço IP e um número de porta (ou seja, soquete) é um identificador exclusivo de:

  • O dispositivo de origem/destino na rede de computadores
  • O protocolo de rede (protocolo IP) usado para a comunicação
  • O serviço/processo de rede envolvido na comunicação.

Soquete = Endereço IP + Número da porta de rede

Os soquetes MicroPython referem-se aos soquetes de programação – não aos soquetes de rede (endereço IP e número da porta). No entanto, alguns métodos de API de soquete usam soquete de rede (endereço IP e número da porta) como parâmetros, como o método connect . O parâmetro é aceito como um nome de host e um número de porta separados.

Acesso de baixo nível vs. acesso de alto nível
O Python padrão fornece dois tipos de acesso a redes de computadores: baixo nível e alto nível.

  • Acesso de baixo nível é fornecido pelos soquetes. Os soquetes usam as chamadas de sistema do sistema operacional/firmware subjacente para se comunicar com o dispositivo na outra extremidade da rede. Os soquetes podem implementar protocolos orientados e sem conexão.
  • Acesso de alto nível é fornecido pela implementação de protocolos de Internet na camada de aplicação, como HTTP, FTP, DNS, etc.

MicroPython oferece apenas acesso de baixo nível à programação da rede porque seus dispositivos alvo são microcontroladores e controladores embarcados. A implementação direta de acesso de alto nível consome muitos recursos e não é viável com plataformas como microcontroladores.

O MicroPython configura facilmente portas com suporte de acesso de baixo nível como servidores ou clientes. A programação de soquete permite até que um servidor se comunique com vários clientes simultaneamente.

Protocolos de rede TCP/IP vs. UDP
Dois protocolos de rede amplamente utilizados são TCP e UDP. Ambos os protocolos fazem parte do conjunto TCP/IP, um conjunto de protocolos em camadas usados ​​para comunicação pela Internet. Este conjunto é baseado em um modelo cliente-servidor onde os clientes enviam solicitações ao servidor. O servidor pode atender às solicitações de vários clientes.

Existem quatro camadas de TCP/IP:

  • Camada de aplicação – protocolos como HTTP, HTTPS e FTP são implementados.
  • Camada de transporte – os dados são comunicados como um datagrama usando o TCP.
  • Camada de rede – a conexão com a internet é estabelecida via IP, onde os dispositivos de origem e destino são identificados por endereços IP.
  • Camada de link de dados – os dados são transmitidos pela Internet como bits para o dispositivo de destino.

Uma análise da arquitetura de rede TCP/IP.

TCP e UDP são os dois protocolos da camada de transporte. Os soquetes podem usar TCP e UDP para configurar o cliente-servidor. TCP é o protocolo mais confiável, pois verifica extensivamente os erros. Por esse motivo, é utilizado por diversos protocolos da camada de aplicação, como HTTP, HTTPS, FTP, SMTP, etc.

Um benefício do UDP é que ele usa menos recursos. Mas, no geral, é menos confiável com verificação de erros limitada. Serviços como VoIP costumam usar UDP.

A maioria dos dispositivos IoT depende do protocolo TCP para comunicar dados entre um servidor TCP e clientes TCP, principalmente onde os protocolos HTTP, HTTPS ou SMTP funcionam na camada de aplicação. As placas IoT são frequentemente configuradas como um servidor TCP ou cliente TCP.

Dirigir-se às famílias
Os dispositivos de origem e destino são identificados por endereços IP na camada de rede. No entanto, existem diferentes tipos de endereços IP. Cada tipo é reconhecido por sua família de endereços.

As famílias de endereços e seus identificadores na programação de soquetes.

Definindo um soquete
Na programação de rede, um soquete é definido por um método construtor, socket.socket , que recebe dois parâmetros. Um dos parâmetros é uma família de endereços, especificada pelos identificadores mencionados na tabela acima. O segundo parâmetro é o protocolo de transporte, TCP ou UDP.

A rede TCP é especificada por socket.SOCK_STREAM. A rede UDP é especificada por socket.SOCK_DGRAM. Uma definição típica de soquete em Python é:

soquete de importação
s = soquete.socket(socket.AF_INET, soquete.SOCK_STREAM)

Se a família de endereços AF_INET for usada, o endereço IPv4 será especificado com um nome de host e um número de porta (ou seja, por um soquete de rede). O nome do host pode ser uma string que representa um site/local da Internet ou o endereço IP numérico de um site/dispositivo. O número da porta é um número inteiro que especifica o serviço de rede. Por exemplo, o número da porta especifica HTTP, 443 especifica HTTPS, etc.

Servidor ou cliente TCP?
Ao projetar uma aplicação IoT, uma das primeiras questões é se a porta/placa IoT MicroPython deve ser configurada como um servidor ou cliente TCP. No modelo cliente-servidor, um cliente faz uma solicitação ao servidor e o servidor atende a essa solicitação. O servidor não pode iniciar a comunicação.

Esta é uma consideração importante ao configurar um dispositivo IoT em uma rede. O dispositivo deve ser configurado como um servidor TCP se for projetado para operações de controle operacional ou outros componentes. Isso garante que ele possa receber comandos de controle de dispositivos clientes, como um computador, celular ou controle remoto.

O dispositivo IoT deverá ser configurado como um cliente TCP se as operações de controle dependerem do feedback de um servidor IoT ou plataforma de nuvem. O mesmo se aplica se o dispositivo for projetado para exibir ou manipular dados de um servidor/nuvem IoT remoto.

Por exemplo, a placa deve ser configurada como um servidor TCP se o dispositivo operar um relé da porta MicroPython/placa IoT por meio de uma página da web. Ao contrário, ele deve ser configurado como um cliente TCP se o horário de exibição da porta MicroPython/placa IoT for recebido de um servidor de horário.

O módulo de soquete
O módulo de soquete do MicroPython fornece acesso à Internet ou à interface de soquete Berkeley/BSD. O módulo de soquete do firmware MicroPython implementa diretamente uma interface semelhante a um arquivo (stream). Isso é diferente do módulo de soquete do CPython e é por isso que os objetos de soquete não precisam ser convertidos em objetos semelhantes a arquivos usando a função makefile .

O módulo socket é importado em um script MicroPython usando esta instrução:

soquete de importação

O módulo contém uma classe socket que inclui todos os métodos para criar e configurar uma interface de soquete de internet. O método construtor é chamado para fazer isso. Tem este protótipo:

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

O método construtor leva três parâmetros:

  • A família de endereços determina o tipo de endereço IP usado na camada de rede.
  • O tipo de soquete determina a seleção da rede TCP ou UDP.
  • O número do protocolo determina o tipo de protocolo TCP ou UDP a ser usado.

O número do protocolo é opcional e deve ser evitado. Normalmente é omitido pelas portas do MicroPython e é automaticamente selecionado pelo tipo de soquete.

O seguinte método de módulo de soquete configura uma porta MicroPython como um servidor TCP…

soquete.bind(endereço): vincula o soquete a um endereço IP. O soquete ainda não deve estar vinculado.

soquete.ouvir : permite que o servidor TCP aceite conexões. Um parâmetro backlog pode ser passado para este método, que especifica o número de conexões não aceitas antes de recusar novas conexões. Se o parâmetro não for fornecido, qualquer valor razoável será escolhido pelo servidor.

soquete.accept : permite que o servidor aceite uma nova conexão. O soquete já deve estar vinculado a um endereço IP e estar escutando conexões. Este método retorna um par de valores: conn e endereço. O conn é um novo objeto de soquete usado para transmitir e receber dados na nova conexão. O endereço é o endereço IP do dispositivo na outra extremidade da rede com o qual os dados são comunicados.

O seguinte método de módulo de soquete configura uma porta MicroPython como um cliente TCP…

soquete.connect(endereço): conecta o soquete do cliente a um soquete do servidor remoto. O endereço IP do soquete do servidor remoto é passado como parâmetro.

Os seguintes métodos de módulo de soquete gerenciam a comunicação de dados no servidor e cliente TCP.

soquete.send(bytes): usado para enviar dados para o outro soquete e deve estar conectado a um soquete remoto. O método retorna o número de bytes enviados ao soquete remoto. Os soquetes do servidor e do cliente usam esse método.

socket.sendto(bytes, endereço): usado para enviar dados para um soquete remoto, que é determinado pelo endereço IP passado como parâmetro. O soquete ainda não deve estar conectado ao soquete remoto. Este método é mais útil em um servidor TCP, que pode interagir com vários clientes.

soquete.sendall(bytes): usado para enviar todos os dados para o soquete remoto, transmitindo-os em pedaços consecutivos. O soquete deve estar conectado ao soquete remoto. O comportamento deste método para soquetes sem bloqueio é indefinido.

soquete.write(buf): envia um buffer de bytes para o soquete remoto. O soquete deve estar conectado a um soquete remoto. Este método grava todos os dados no soquete remoto seguindo uma política de “não gravação curta”. Isto não representa nenhum problema para os soquetes de bloqueio para os quais os dados são sempre enviados com sucesso. Para soquetes sem bloqueio, este método retorna o número de bytes enviados — que pode ser menor que o tamanho real do buffer. Sabe-se quantos bytes são enviados com sucesso para o soquete sem bloqueio.

soquete.recv(bufsize): recebe dados do soquete remoto. Ele retorna um objeto byte que representa os dados recebidos. O tamanho máximo dos dados recebidos é determinado pelo tamanho do objeto buffer que é passado como parâmetro.

soquete.recvfrom(bufsize): recebe dados do soquete remoto. O soquete deve estar conectado a um soquete remoto. O método retorna o objeto byte — representando os dados recebidos e o endereço IP — do soquete remoto. Este método é mais útil em um servidor TCP, que pode se conectar a vários clientes.

socket.read((tamanho)): lê do soquete remoto até o número de bytes, especificado como parâmetro de tamanho. Se nenhum parâmetro de tamanho for especificado, ele lê todos os dados do soquete remoto até receber EOF (ou seja, até que o soquete remoto seja fechado). Este método segue uma política de “sem leituras curtas” que permite ler todos os dados solicitados. Para soquetes sem bloqueio, este método ainda não consegue ler todos os dados.

soquete.readinto(buf(,nbytes)): lê dados do soquete remoto em um objeto buffer. Se o parâmetro bytes for especificado, ele lê apenas o número especificado de bytes no buffer. Este método retorna o número de bytes lidos e armazenados no objeto buffer e segue uma política de “nenhuma leitura curta” como o método socket.read .

soquete.readline : lê uma linha terminada por um caractere de nova linha do soquete remoto. Ele retorna a linha lida do soquete remoto.

soquete.fechar : fecha um soquete e libera os recursos mantidos por ele. Depois que um soquete é fechado, qualquer operação futura falhará. Quando um soquete é fechado, o soquete remoto pode receber uma indicação EOF, desde que seja suportado pelo protocolo. Mas os soquetes são fechados automaticamente quando o lixo é coletado e devem ser fechados explicitamente para melhorar a eficiência.

Os seguintes métodos do módulo de soquete modificam ou alteram o comportamento do soquete…

socket.setsockopt(nível, optname, valor): Observe que os soquetes têm como padrão um comportamento padrão. O comportamento de um soquete pode ser modificado alterando várias opções de soquete. Este método define o valor de uma determinada opção de soquete. As opções de soquete que podem ser definidas com este método são fornecidas como constantes começando em SO_ ou SOL_ no módulo de soquete. O valor que pode ser passado para as opções de soquete é um número inteiro ou um objeto byte.

socket.setblocking(sinalizador): define um soquete para modo bloqueador ou não bloqueador. Se o sinalizador estiver definido como True, o soquete será definido para o modo de bloqueio. Se o sinalizador for definido como False, o soquete será definido para o modo sem bloqueio. Este método serve como uma abreviação para o método socket.settimeout . O modo padrão do soquete é o bloqueio. Um soquete de bloqueio não retorna ao programa até que o evento solicitado seja concluído. Os soquetes sem bloqueio respondem imediatamente ao programa, revelando se a ação solicitada foi concluída ou não. Um soquete de bloqueio retorna um número de erro se o evento/ação solicitado estiver em andamento.

soquete.settimeout(valor): define um tempo limite para os soquetes de bloqueio. O tempo limite é especificado como um valor de ponto flutuante não negativo. Se o valor do tempo limite for definido como '0', o soquete será colocado no modo sem bloqueio. Se o valor do tempo limite for definido como nenhum, o soquete será colocado no modo de bloqueio. Este método não está disponível em todas as portas/placas MicroPython.

soquete.makefile(modo='rb'): retorna um objeto de arquivo associado ao soquete. O método makefile do MicroPython não suporta erros, codificação e parâmetros de nova linha como o método makefile do CPython. Ele também não suporta buffer de fluxos. Se o parâmetro buffer estiver definido, ele será ignorado e considerado '0' (ou seja, sem buffer). O único parâmetro necessário é 'modo'. Somente os modos binários rb, wb e rwb são suportados. Se um objeto de arquivo retornado por este método for fechado, o soquete original também será fechado.

As funções fornecidas pelo módulo soquete são as seguintes…

soquete.getaddrinfo : converte um soquete de rede (ou seja, host/porta) em uma sequência de 5 tuplas que pode ser usada para criar um soquete. O formato da tupla 5 é (família, tipo, proto, canonname, sockaddr). Este método é frequentemente usado para resolver nomes de host textuais. O exemplo a seguir imprime o endereço IP resolvido de engenheirosgarage.com.

soquete de importação
sockaddr = socket.getaddrinfo('www.engineersgarage.com', 80)(0)(-1)
imprimir(sockaddr)

Ao executar o código acima, o seguinte endereço IP é retornado após resolver o nome do host…

soquete.inet_ntop(af,bin_addr): converte um endereço de rede binário em uma representação textual de acordo com a família de endereços especificada. Aqui está um exemplo válido desta função:

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

O resultado desta chamada de função é: '127.0.0.1'

soquete.inet_pton(af,txt_addr): converte um endereço de rede textual da família de endereços fornecida em sua representação binária. Aqui está um exemplo válido desta função:

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

O resultado desta chamada de função é: b'\x7f\0\0\1′

O módulo de soquete do MicroPython não suporta exceções socket.error.

O módulo usocket
O módulo usocket do MicroPython é semelhante ao módulo socket, que pode ser integrado na porta/placa MicroPython ou instalado a partir do micropython-lib. Se o script do módulo soquete não for encontrado, use o script do módulo usocket. Ambos funcionam.

Configurando ESP8266/ESP32 como cliente TCP usando soquetes
O código MicroPython a seguir configura ESP8266/ESP32 como um cliente TCP que lê o código-fonte HTML de uma página da web. A página da web deve ter protocolo HTTP e não protocolo HTTPS.

rede de importação

estação = rede.WLAN(rede.STA_IF)
se station.isconnected == Verdadeiro:
print(“Já conectado”)
estação.active(Verdadeiro)
estação.connect(“SSID”, “SENHA”)
enquanto estação.isconnected == Falso:
passar
print(“Conexão bem sucedida”)
imprimir(estação.ifconfig )

def http_get(url):
soquete de importação
_, _, host, caminho = url.split('/', 3)
addr = socket.getaddrinfo(host, 80)(0)(-1)
s = soquete.socket
s.conectar(endereço)
s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (caminho, host), 'utf8'))
enquanto Verdadeiro:
dados = s.recv(200)
se dados:
imprimir(str(dados, 'utf8'), fim=”)
outro:
quebrar
s.fechar

http_get('

Você deve substituir o SSID e a SENHA na linha 7 pelo SSID e a chave de rede da sua própria conexão WiFi.

O resultado do script MicroPython acima é mostrado abaixo.

Lendo páginas da web criptografadas por SSL
O ESP8266 configurado acima não consegue ler o conteúdo HTML das páginas criptografadas por SSL. MicroPython tem pouco suporte para o protocolo SSL. No entanto, é possível ler o conteúdo das páginas criptografadas SSL usando Python padrão em um microcomputador.

Experimente o seguinte script Python em um Raspberry Pi ou em seu computador.

def http_get(url):
soquete de importação
importar SSL
_, _, host, caminho = url.split('/', 3)
real_host = host.replace(“ “”).replace(“ “”).split(“/”)(0).split(“:”)(0)
s = soquete.socket(socket.AF_INET, soquete.SOCK_STREAM)
solicitação = bytes('GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n' % (caminho, host), 'utf8')
contexto = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)

s = context.wrap_socket(s, server_hostname = real_host)
s.connect((host_real, 443))
s.sendall(solicitação)
concurso = s.recv(5000).decode
imprimir (concurso)
enquanto contexto! = b”:
concurso = s.recv(5000).decode
imprimir (concurso)
s.fechar

http_get('

O script Python acima lê o código-fonte HTML de uma página da web criptografada por SSL de Engineergarage.com. Observe o URL do site e engenheirosgarage.com usam protocolos HTTPS. O script Python executa um cliente TCP e retorna o conteúdo HTML do URL fornecido, conforme mostrado nas imagens abaixo.

Configurando ESP8266/ESP32 como servidor TCP usando soquetes
O script MicroPython a seguir configura o ESP8266 como um servidor TCP. O servidor ESP8266 usa o endereço IP do roteador conectado. Quando o servidor é solicitado por um cliente, como um computador conectado à mesma conexão WiFi, ele retorna uma página HTML.

rede de importação
máquina de importação

estação = rede.WLAN(rede.STA_IF)
se station.isconnected == Verdadeiro:
print(“Já conectado”)
estação.active(Verdadeiro)
estação.connect(“SSID”, “SENHA”)
enquanto estação.isconnected == Falso:
passar
print(“Conexão bem sucedida”)
imprimir(estação.ifconfig )

html = “””

Servidor ESP8266

Bem-vindo ao servidor ESP8266




“””

soquete de importação
addr = soquete.getaddrinfo('0.0.0.0', 80)(0)(-1)

s = soquete.socket
s.bind(endereço)
s.ouvir(1)

print('ouvindo', endereço)

enquanto Verdadeiro:
cl, endereço = s.accept
print('cliente conectado de', endereco)
cl_file = cl.makefile('rwb', 0)
enquanto Verdadeiro:
linha = cl_file.readline
se não for linha ou linha == b'\r\n':
quebrar
resposta=html
cl.send('HTTP/1.0 200 OK\r\nTipo de conteúdo: texto/html\r\n\r\n')
cl.send(resposta)
cl.fechar

Você deve substituir o SSID e a SENHA na linha 8 do código acima pelo SSID e chave de rede da sua própria conexão WiFi. Para executar o servidor, carregue e execute o script MicroPython acima no ESP8266 usando uPyCraft IDE ou Thonny IDE.

A captura de tela abaixo mostra o servidor ESP8266 rodando e respondendo a um cliente.

Para fazer uma solicitação ao servidor ESP8266, digite o endereço IP retornado pelo método station.ifconfig na barra de endereços do seu navegador. O servidor ESP8266 retornará uma página HTML como resposta ao navegador do seu computador.

A captura de tela abaixo é um exemplo de página da web retornada pelo servidor ESP8266.

Conteúdo Relacionado

Voltar para o blog

Deixe um comentário

Os comentários precisam ser aprovados antes da publicação.