banner

Por que é importante entender Socket? Pt-2 e Pt-3


Esta é a continuação do vídeo: Porque você deve entender Socket! Nele, já falei sobre assuntos que considero muito importantes, como Socket MultiClient, com dois ESP32 enviando dados para um terceiro, bem como um controle remoto que criei com meu próprio Callback. Agora, neste vídeo, eu te mostro como fazer a LIB orientada a objeto. Tanto que usei aqui de referência o Lego porque ele é sempre usado para exemplificar a orientação a objeto, e a ideia é justamente essa, você reaproveitar objetos que já foram feitos. Resumindo: vou te mostrar como criar uma lib Orientada a Objeto 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.





Montagem Client





Montagem Server





Fluxograma Server





Fluxograma Client Loop






Começando pela Lib

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




Modificadores de acesso

private:  apenas a classe pode acessar ou modificar as variáveis ou métodos deste tipo.
protected: além da própria classe, as classes que estendem esta classe também possuem acesso.
public: todos possuem acesso



Esse é um exemplo com código fonte:





Lista de inicialização

Podemos usar uma lista na frente do construtor para inicializar nossas variáveis.
Exemplo:





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

Includes

#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

Includes

#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));
    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.cpp

Includes

#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<WiFiClient>::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();
}




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

Includes

#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

Includes

#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();
}




Faça o download dos arquivos








Um comentário:

  1. Boa Tarde Fernando estou tentando compilar o seu código mas ele está dando o seguinte erro: C:\Users\José Tavares\Desktop\ESP8266\Capacete\LibRemote\Server\Server.ino: In function 'void createWiFiAP()':

    Server:31:14: error: 'class WiFiClass' has no member named 'mode'

    Server:31:19: error: 'WIFI_AP_STA' was not declared in this scope

    Server:33:14: error: 'class WiFiClass' has no member named 'softAP'

    exit status 1
    stray '\302' in program

    ResponderExcluir

Tecnologia do Blogger.