banner

Por que é importante entender Socket? Pt-1



Assuntos que considero muito importantes serão tratados neste artigo de hoje. O primeiro: Socket MultiClient, com dois ESP32 enviando dados para um terceiro, no caso, o Server. Segundo: controle remoto no qual implantei meu próprio Callback. Vou, portanto, mostrar o funcionamento de Multclients com Sockets, criar uma Lib que vai nos avisar através de callback sobre a mudança de status de pinos que acontecem em ESPs clientes conectados a um ESP servidor, além de mostrar o funcionamento dos Callbacks. Mas, especificamente sobre a LIB eu vou falar no próximo vídeo, ok!





Demonstração





Montagem Client





Montagem Server






Fluxograma Server





Fluxograma Client Loop






Implementando os arquivos .ino

Após a nossa lib estar finalizada e no local correto (pasta em C:\Users\<SEU_USUARIO>\Documents\Arduino\libraries) podemos partir para os arquivos Client.ino e Server.ino.



Client.ino

Declarações e variáveis

#include <WiFi.h>
#include <RemoteClient.h> //Faz parte da nossa lib

//Configurações da rede e porta do socket do outro ESP
//Devem ser iguais nos dois arquivos
#define SSID "ESP32Server"
#define PASSWORD "87654321"
#define SERVER_PORT 5000

//Quantidade de pinos cujo os estados serão verificados
#define PIN_COUNT 2
//Array com os pinos que iremos verificar
int pinNumbers[PIN_COUNT] = {23, 22};

//Objeto que verificará se os pinos mudaram de estado e enviará para o server
RemoteClient remoteClient(pinNumbers, PIN_COUNT);


Setup

void setup()
{
    Serial.begin(115200);
    //Conecta à rede criada pelo outro ESP
    connectWiFi();
    remoteClient.setServerAddressAndPort(WiFi.gatewayIP(), SERVER_PORT);
}


connectWiFi

void connectWiFi()
{
    Serial.print("Connecting to " + String(SSID));
    //Conectamos ao Access Point criado pelo outro ESP
    WiFi.begin(SSID, PASSWORD);

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

    Serial.println();
    Serial.println("Connected!");
}


Loop

void loop()
{ 
    //Se não está conectado à rede WiFi
    if(WiFi.status() != WL_CONNECTED)
    {
        //Mandamos conectar
        connectWiFi();
    }

    //Chama o método loop do remoteClient e este verificará se aconteceu mudança no estado dos pinos
    //e enviará para o server caso tenha acontecido
    remoteClient.loop();
}



Server.ino

Declarações e variáveis

#include <WiFi.h>
#include <RemoteServer.h> //Faz parte da nossa lib

//Configurações da rede e porta do socket criados por este ESP
//Devem ser iguais nos dois arquivos
#define SSID "ESP32Server"
#define PASSWORD "87654321"
#define SERVER_PORT 5000

//Objeto que irá receber por socket informações sobre mudança no estado dos pinos dos ESPs clients
RemoteServer remoteServer(SERVER_PORT);



Setup

void setup()
{
    Serial.begin(115200);

    //Cria uma rede WiFi para os outros ESPs se conectarem
    createWiFiAP();

    //Inicia o remoteServer para que ele possa receber conexões
    remoteServer.begin();

    //Passamos a função que será nosso callback quando um pino de um dos clients mudar de estado
    remoteServer.onStatusChange(statusChange);
}


createWiFiAP

void createWiFiAP()
{
    Serial.println("Creating AP " + String(SSID) + "...");
    //Coloca este ESP como Access Point
    WiFi.mode(WIFI_AP);
    //SSID e Senha para se conectarem a este ESP
    WiFi.softAP(SSID, PASSWORD);
    Serial.println("Created");
}


statusChange

//Função chamada toda vez que um pino em um dos clients mudar de estado
void statusChange(int pinNumber, int pinStatus)
{
    //Aqui vamos apenas replicar o estado no mesmo pino deste ESP
    //Colocamos o pino como saída
    pinMode(pinNumber, OUTPUT);
    //E mudamos o estado para o mesmo que recebemos do client
    digitalWrite(pinNumber, pinStatus);
}


Loop

void loop()
{
    //Chama a função do remoteServer que irá verificar novas conexões
    //e ler os dados dos pinos que os clients enviam quando há mudança de estado
    remoteServer.loop();
}




Biblioteca “Libraries”

Primeiramente vamos construir nossa lib que será constituída dos arquivos Pin.h, RemoteClient.h, RemoteClient.cpp, RemoteServer.h e RemoteServer.cpp
A pasta com estes arquivos deverá ser colocada em:
C:\Users\<SEU_USUARIO>\Documents\Arduino\libraries


Pin.h

#ifndef Remote_Pin_h
#define Remote_Pin_h

//Struct que será enviada entre o client e o server
typedef struct 
{
    int number;
    int status;
} Pin;

#endif



RemoteClient.h

#ifndef RemoteClient_h
#define RemoteClient_h

#include <Arduino.h>
#include <WiFi.h>
#include <vector>
#include "Pin.h"

class RemoteClient
{
    public:
        RemoteClient(int* pinNumbers, int pinCount);
        void loop();
        void setServerAddressAndPort(IPAddress serverIpAddress, int serverPort);
        int* pinNumbers;
        int pinCount;
        std::vector<Pin> pins;
        IPAddress serverIpAddress;
        int serverPort;
    private:
        boolean hasPinStatusChanged(int i);
        void sendPinStatus(int i);
};

#endif



RemoteClient.cpp

Include

#include "RemoteClient.h"

//pinNumbers - Ponteiro para um array com os números dos pinos que serão verificados
//pinCount - Quantidade de pinos no array pinNumbers
RemoteClient::RemoteClient(int* pN, int pC) : pinNumbers(pN), pinCount(pC)
{
    //Para cada pino
    for(int i=0; i<pinCount; i++)
    {
        Pin pin;
        //Vinculamos o pino da struct com o recebido por parâmetro
        pin.number = pinNumbers[i];
        //Colocamos como INPUT
        pinMode(pin.number, INPUT);
        //Dizemos que o estado está em LOW
        pin.status = LOW;
        //adicionamos ao vector
        pins.push_back(pin);
    }
}

RemoteClient::setServerAddressAndPort

//Endereço ip do server e a porta
void RemoteClient::setServerAddressAndPort(IPAddress serverIpAddress, int serverPort)
{
    this->serverIpAddress = serverIpAddress;
    this->serverPort = serverPort;
}

RemoteClient::loop

void RemoteClient::loop()
{
    //Para cada pino
    for(int i=0; i<pinCount; i++)
    {
        //Se o estado foi alterado
        if(hasPinStatusChanged(i))
        {
            //Enviaremos para o server os dados do pino cujo estado foi alterado
            sendPinStatus(i);
        }
    }
}

RemoteClient::hasPinStatusChanged

bool RemoteClient::hasPinStatusChanged(int i)
{
    //Faz a leitura do estado do pino na posição 'i' do array de pinos
    int status = digitalRead(pins[i].number);

    //Se está diferente
    if(pins[i].status != status)
    {
        //Atualizamos os dados do pino e retornamos true
        pins[i].status = status;
        return true;
    }

    //Se chegou aqui então o pino não teve seu estado alterado, então retornamos falso
    return false;
}

RemoteClient::sendPinStatus

void RemoteClient::sendPinStatus(int i)
{
    //Conectamos com o server
    WiFiClient client;
    client.connect(serverIpAddress, serverPort);
    
    //Enviamos os dados do pino na posição 'i'do array para o server
    client.write((uint8_t*) &pins[i], sizeof(Pin));
    client.flush();
    client.stop();
}


RemoteServer.h

Include

#ifndef RemoteServer_h
#define RemoteServer_h

#include <Arduino.h>
#include <WiFi.h>
#include <vector>
#include "Pin.h"

typedef void (*OnStatusChangeCallback)(int pinNumber, int pinStatus);

class RemoteServer
{
    public:
        RemoteServer(int serverPort);
        std::vector<WiFiClient> clients;
        void begin();
        void loop();
        void stop();
        void onStatusChange(OnStatusChangeCallback callback);
        //ou
        // void onStatusChange(void (*callback)(int pinNumber, int pinStatus));
        //ou
        // void onStatusChange(void (*callback)(int, int));
        //ou
        // void onStatusChange(void (*)(int, int));
    

readAndNotify

private:
        void readAndNotify(WiFiClient client);
        WiFiServer server;
        void (*callback)(int, int); //Variável que irá apontar para função callback
        //ou
        // void (*callback)(int pinNumber, int pinStatus);
        //ou
        // OnStatusChangeCallback callback;
};

#endif

RemoteServer::RemoteServer

#include "RemoteServer.h"

//Construimos o server com o valor de serverPort
RemoteServer::RemoteServer(int serverPort) : server(serverPort)
{
}

RemoteServer::begin

void RemoteServer::begin()
{
    //Iniciamos o server para que este possa receber conexões
    server.begin();
}

RemoteServer::onStatusChange

void RemoteServer::onStatusChange(OnStatusChangeCallback callback)
{
    this->callback = callback;
}
//ou
// void RemoteServer::onStatusChange(void (*callback)(int pinNumber, int pinStatus))
// {
//     this->callback = callback;
// }
//ou
// void RemoteServer::onStatusChange(void (*callback)(int, int))
// {
//     this->callback = callback;
// }

RemoteServer::loop

void RemoteServer::loop()
{
    //Verificamos se temos um client tentando se conectar
    WiFiClient client = server.available();

    //Se temos
    if (client)
    {
        //Colocamos no final do vector que armazena os clients
        clients.push_back(client);
    }

    for (std::vector::iterator it = clients.begin(); it != clients.end();)
    {
        //O conteúdo apontado pelo iterator é o client
        WiFiClient client = *it;

        if(client.connected())
        {
            //Se o client possui algo para nos enviar
            if (client.available())
            {
                readAndNotify(client);
            }

            //Vamos para o próximo client
            it++;
        }
        //Se não estiver mais conectado
        else
        {
            //Apagamos do vector
            clients.erase(it);
        }
    }
}

readAndNotify

void RemoteServer::readAndNotify(WiFiClient client)
{
    //Fazemos a leitura dos dados do client para a variável pin
    Pin pin;
    client.read((uint8_t*)&pin, sizeof(pin));

    //Se possuímos um callback adicionado
    if(callback)
    {
        //Chamamos o callback informando o número do pino que foi alterado e o novo estado
        callback(pin.number, pin.status);
    }
}


RemoteServer::stop

void RemoteServer::stop()
{
    //Paramos o servidor
    server.stop();
}




Faça o download dos arquivos:




Nenhum comentário:

Tecnologia do Blogger.