ESP32: Controle Remoto usando Sockets


Hoje vamos mostrar a comunicação entre dois ESP’s, neste caso, o ESP32. Na nossa montagem, um dos microcontroladores funciona como server, com um led no protoboard, no modo AP. O segundo será o Client, ou seja, controle. Um botão ligado a este segundo ESP que vai controlar o led que estará ligado ao server.


Neste projeto nós utilizamos o Socket, um mecanismo de comunicação utilizado para implementar um modelo cliente/servidor, que permite a troca de mensagens entre os processos de uma máquina/aplicação servidor e de uma máquina/aplicação cliente.
Neste caso, o primeiro ESP, que funciona como server, é o AP (Access Point), que terá conexão estabelecida com o Client. Não é preciso ter um roteador! Se você quer controlar um drone, um carrinho de controle remoto ou um aeromodelo, por exemplo, você pode usar o ESP32 pra isso e esse dispositivo vai longe, principalmente se tiver algum tipo de antena. Se for a rede LORA, então, alcança quilômetros, podendo chegar a 3,6 km de ESP para ESP.

Dando sequência, eu tenho um led no server e um botão no Client. Eu fiz com um botão, mas a montagem serve para quantos botões você quiser. 


Pinagem


Aqui eu deixo a pinagem do componente.



Montagem Server


Ligo um led no server, o nosso receptor, e o inicio como Access Point. O Client, então, conecta nele. Lembrando que neste caso estamos usando a porta D23.





Montagem Client


Na mesma porta D23, agora no dispositivo Client, ligo a chave que equivale ao PushButton, e também um resistor de Pull Down.




Código Fonte

Para este projeto temos três arquivos: um do server, um do Client e um terceiro que usamos nestes dois casos, que é o ESP32Socket.ino.

ESP32Socket.ino

//Arquivo principal. Neste arquivo vão os 'includes' e as configurações principais
//que são compartilhadas entre os outros arquivos .ino
#include <WiFi.h>

#define SSID "ESP32Server"
#define PASSWORD "87654321"
#define SERVER_PORT 5000

//Protocolo que o Server e o Client utilizarão para se comunicar
enum Protocol{
    PIN, //Pino que se deseja alterar o estado
    VALUE, //Estado para qual o pino deve ir (HIGH = 1 ou LOW = 0)
    BUFFER_SIZE //O tamanho do nosso protocolo. IMPORTANTE: deixar sempre como último do enum
};

//Diretiva de compilação que informará qual arquivo que queremos que seja compilado
//Caso queira que o arquivo Client.ino seja compilado, remova ou comente a linha do '#define'
//abaixo
//Caso queira que o arquivo Server.ino seja compilado, deixe o '#define IS_SERVER' abaixo descomentado
#define IS_SERVER

Iniciamos, então, pelo nome da nossa rede: ESP32Server. Defini um número qualquer para o Password e defini o porte que será utilizado pelo Socket, neste caso, o 5000. EM seguida, na linguagem C você tem um recurso que chama enum = enumeration, que aqui vai definir qual pino será apertado com o botão, qual o valor desse pino e o tamanho do buffer. Portanto, nós temos um buffer, que na verdade é um array. Então, ele define uma sequência de protocolo. Primeiro, então ele define o pino, em segundo vem o valor do pino, se esta está em 0 ou em 1, e, por fim, o tamanho do buffer.
Esse enumeration vai servir de índice do array do buffer que vai ser mandado para o server. Ele vai indicar qual o botão, qual o valor botão, e, então, podemos ter diversos botões.

Outra coisa importante aqui é a diretiva de compilação. Se eu defino uma constante (IS_SERVER), eu estou dizendo que esse dispositivo é um servidor, que vai ter um IF para checagem e compilação, caso proceda.


Server.ino

//Apenas vai compilar o código contido neste arquivo
//caso IS_SERVER esteja definido
#ifdef IS_SERVER

//Cria o server na porta definida por 'SERVER_PORT'
WiFiServer server(SERVER_PORT);

void setup()
{
    //Coloca este ESP como Access Point
    WiFi.mode(WIFI_AP);
    //SSID e Senha para se conectarem a este ESP
    WiFi.softAP(SSID, PASSWORD);
    //Inicia o server
    server.begin();
}

void loop()
{
    //Verifica se tem algum cliente se conectando
    WiFiClient client = server.available();
    if (client)
    {     
        //Se o cliente tem dados que deseja nos enviar
        if (client.available())
        {//Criamos um buffer para colocar os dados
           uint8_t buffer[Protocol::BUFFER_SIZE];
            //Colocamos os dados enviados pelo cliente no buffer
            int len = client.read(buffer, Protocol::BUFFER_SIZE);
            //Verificamos qual o pino que o cliente enviou
            int pinNumber = buffer[Protocol::PIN];
            //Verificamos qual o valor deste pino
            int value = buffer[Protocol::VALUE];
            //Colocamos o pino em modo de saída
            pinMode(pinNumber, OUTPUT);
            //Alteramos o estado do pino para o valor passado
            digitalWrite(pinNumber, value);
        }

        //Fecha a conexão com o cliente
        client.stop();
    }
}
//Encerra o #ifdef do começo do arquivo
#endif


Aqui no Server.ino começamos com o #ifdef. Então, no caso de ser um servidor, o código todo que estiver abaixo será compilado.
Nesta parte, então, criamos o objeto, aqui o WiFiServer e passo o SERVER-PORT para ele.
Já no Setup, inicio esse ESP como Access Point, seguindo os próximos passos do código, definimos o nome da rede, entre outros detalhes, para permitir a entrada do Client.
Não é necessário definir nenhum IP, pois o dispositivo já conta com um IP Default de sua própria Lib.
Ainda, no Loop, verificamos se há um Client conectado e se este está disponível. Cria, então, um buffer size, que vai receber o buffer que virá do Client. Define agora uma variável len, que é o tamanho e faz o Cliente read, pois quando você está lidando com o Socket não é Post and Get, que é HTTP. No caso do Socket, usamos READ and WRITE.
Prosseguindo, o server aponta o pinNumber pela posição do buffer, define o value, que envolve a suposição se o botão está ou não apertado, e delibera ainda, no pinMode, a saída, que no caso é a porta D23.

Aqui fechamos o Client porque traz uma certa segurança e damos um #endif, encerrando nossa diretiva de compilação.



Client.ino

//Apenas vai compilar o código contido neste arquivo
//caso IS_SERVER NÃO esteja definido
//(if n def, atenção para o 'n')
#ifndef IS_SERVER

//Pino que vamos fazer a leitura
#define IN_PIN 23

void setup(){
    //Colocamos o pino em modo de leitura
    pinMode(IN_PIN, INPUT);
    //Conectamos Access Point criado
    //pelo outro ESP
    WiFi.begin(SSID, PASSWORD);

    //Esperamos conectar
    while (WiFi.status() != WL_CONNECTED){
        delay(500);
    }
}


void loop(){
    //Variável que utlizaremos para conectar ao servidor
    WiFiClient client;
    //Se não conseguiu se conectar então retornamos
    if (!client.connect(WiFi.gatewayIP(), SERVER_PORT)){
        return;
    }

    //Criamos um buffer para colocar os dados
    uint8_t buffer[Protocol::BUFFER_SIZE];
    //Fazemos a leitura do pino
    int value = digitalRead(IN_PIN);
    //Colocamos no buffer o número do pino
    //cujo estado queremos enviar
    buffer[Protocol::PIN] = IN_PIN;
    //Colocamos no buffer o estado atual do pino
    buffer[Protocol::VALUE] = value;
    //Enviamos e finalizamos a conexão
    client.write(buffer, Protocol::BUFFER_SIZE);
    client.flush();
    client.stop();
}
//Encerra o #ifndef do começo do arquivo
#endif



No Client.ino, reparem na diretiva de compilação: #ifndef (se não está definido que ele é o server), então, eu compilo todo o código até o ifend, ou seja, o final. Ele, então, define onde está ligado o botão controlado pelo cliente.
No Setup, fazemos um pinMode, conectamos Access Point criado pelo outro ESP pelo WiFi.begin e esperamos a conexão.
No Loop, o WiFiCliente cria o objeto. Caso este não esteja conecta a ponta de ligação, ele retorna. Caso contrário, prossegue com os passos.
Segue criando o array a ser enviado ao server, faz a leitura desse pino e coloca no buffer o número desse pino. Informa o valor atual do pino, envia e encerra a conexão.
Fazendo um comentário a parte: caso você queira montar um carrinho de controle remoto, então, você vai precisar de vários botões. Portanto, você tem que focar neste código, na parte de trata dos valores, números de pinos, para montar o array e escrever no buffer size.

Retomando, o client.write preenche o buffer e o client.flush que envia as informações para o server. Por fim, o cliente.stop fecha a conexão com o socket.



Faça o download dos arquivos:






8 comentários:

  1. Bacana o tutorial, estou brincando recentemente com esp32. Mas tive uma dúvida, nesse processo vc ensina como se fosse rede local. Caso eu queira fazer essa comunicação das duas placas atraves da internet para por exemplo, ter um esp em casa e outro no servico, para de casa acionar algo lá. Como faria? Seria comunicação TCP? Ou teria que ter um servidor intermediando as duas placas?

    ResponderExcluir
    Respostas
    1. É necessário ter um gateway. Já vou soltar a primeira vídeo aula de como podemos implementar uma conexão desse tipo. Aguarde!

      Excluir
    2. Estarei aguardando e novamente parabéns pelo trabalho :)

      Excluir
  2. Bacana o tutorial, estou brincando recentemente com esp32. Mas tive uma dúvida, nesse processo vc ensina como se fosse rede local. Caso eu queira fazer essa comunicação das duas placas atraves da internet para por exemplo, ter um esp em casa e outro no servico, para de casa acionar algo lá. Como faria? Seria comunicação TCP? Ou teria que ter um servidor intermediando as duas placas?

    ResponderExcluir
  3. Muito bom !!!!
    Só uma pergunta , no caso de criar 10 botões ...e apertar todos ao mesmo tempo , essa rotina funciona , ou teria que criar uma outra .
    Parabéns pela sua dedicação em ajudar muita gente , a entender melhor essa nova tecnologia !!
    abraços !

    ResponderExcluir
  4. Olá Fernando!! Excelente trabalho e parabéns pela qualidade das apresentações e dos projetos. É possível utilizar o ESP32 como socket server com múltiplos clients , por exemplo, 4 ESP8622 como socket client? Obrigado. Parabéns !!!

    ResponderExcluir
  5. Fernando, boa noite!
    Parabéns pelo seu trabalho estou gostando muito dos materiais disponíveis.
    Montei o circuito exatamente como foi orientado. Funcionou conforme apresentado, porem eles travam apos alguns acionamentos sequenciais, poderia me ajudar a solucionar este problema.
    Obrigado.

    ResponderExcluir
  6. Muito interessante essa ideia do ESP32 funcionar com AP, porém tenho algumas dúvidas, seguem abaixo:
    Com relação a faixa de IP's fornecido pelo ESP32 nessa configuração, existe a possibilidade de alterarmos? Com relação a quantidade de conexões no ESP32 como AP existe algum limite de conexões, esse limite pode ser configurado?

    ResponderExcluir

Tecnologia do Blogger.