Esse vídeo de hoje, posso
dizer que ele é “Multicoisas”! Ou seja, tratamos de Multitarefas, Multicore e Multiclients.
Há um tempo atrás eu fiz um controle remoto com dois ESPs: um client e um access
point. E, baseado nisto, vamos, neste projeto de hoje, montar um servidor multiclient,
o que quer dizer que teremos vários clientes conectados em um único ESP.
Nesta aula, portanto, nós
iremos criar um server no ESP32 e adicionar novos clientes no loop, além de
tratar as requisições em outro core. Os clientes irão enviar informação sobre a
mudança de estado dos seus pinos e o server irá reproduzir estas mudanças de
estado.
Demonstração
Montagem Server
Montagem Client
Fluxo
Server
Client
Client.ino
Declarações e variáveis
#include <WiFi.h>
//Dados da rede
//Deve ser giual no Server
#define SSID "ESP32Server"
#define PASSWORD "87654321"
#define SERVER_PORT 5000
//Objeto que vai fazer a conexão com o server
WiFiClient client;
//Struct que define os dados que vamos enviar (deve ser igual no server)
typedef struct{
int number;
int status;
}Pin;
//Quantidade de pinos que iremos ler e enviar o status
#define PIN_COUNT 2
//Array com os pinos definidos
//No caso vamos trabalhar com os 21 e 19 mas você pode alterar para os pinos que desejar
Pin pins[PIN_COUNT] = {
{.number = 21},
{.number = 19}
};
Setup
void setup(){
Serial.begin(115200);
//Tempo para considerar a conexão como perdida
client.setTimeout(5000);
//Conectamos à rede WiFi e conectamos ao server
setupWiFi();
connectClient();
for(int i=0; i<PIN_COUNT; i++){
pinMode(pins[i].number, INPUT);
sendPinStatus(i); //Enviamos para o server o estado atual dos pinos
}
//Geramos uma nova tarefa
xTaskCreatePinnedToCore(
handleConnection, //Função que será executada
"handleConnection", //Nome da tarefa
10000, //Tamanho da pilha
NULL, //Parâmetro da tarefa (no caso não usamos)
2, //Prioridade da tarefa
NULL, //Caso queria manter uma referência para a tarefa que vai ser criada (no caso não precisamos)
0); //Número do core que será executada a tarefa (usamos o core 0 para o loop ficar livre com o core 1)
}
Setup WiFi
void setupWiFi()
{
Serial.print("Connecting to " + String(SSID));
//Conectamos à rede WiFi criado pelo outro ESP
WiFi.begin(SSID, PASSWORD);
//Esperamos conectar
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(500);
}
//Se chegou aqui está conectado à rede WiFi
Serial.println();
Serial.println("Connected!");
}
ConnectClient
void connectClient()
{
Serial.println("Connecting client");
//Esperamos conectar com o server
while (!client.connect(WiFi.gatewayIP(), SERVER_PORT))
{
Serial.print(".");
delay(500);
}
//Se chegou aqui está conectado com o server
Serial.println();
Serial.println("Client connected!");
}
Loop
void loop()
{
//Se não estiver conectado à rede WiFi, mandamos conectar
if(WiFi.status() != WL_CONNECTED)
{
setupWiFi();
}
}
HandleConnection
void handleConnection(void* pvParameters)
{
//IMPORTANTE: A tarefa não pode terminar, deve ficar presa em um loop infinito
while(true)
{
//Se não estiver conectado com o server, mandamos conectar
if(!client.connected())
{
connectClient();
}
//Para cada pino, verificamos se mudou o estado. Se mudou enviamos para o server o novo estado
for(int i=0; i<PIN_COUNT; i++)
{
if(hasPinStatusChanged(i))
{
sendPinStatus(i);
}
}
//Delay de 5ms da tarefa. É feita em ticks. Para executar em millis dividimos pela constante portTICK_PERIOD_MS
TickType_t taskDelay = 5 / portTICK_PERIOD_MS;
vTaskDelay(taskDelay);
}
}
hasPinStatusChanged
//Verifica se o estado do pino na posição 'i' do array mudou
//Retorna 'true' se mudou ou 'false' caso contrário
boolean hasPinStatusChanged(int i)
{
//Faz a leitura do pino
int pinStatus = digitalRead(pins[i].number);
//Se o estado do pino for diferente
if(pins[i].status != pinStatus)
{
//Guardamos o novo estado e retornamos true
pins[i].status = pinStatus;
return true;
}
//Só chegará aqui se o estado não foi alterado
//Então retornamos falso
return false;
}
sendPinStatus
//Envia para o server os dados do pino na posição 'i' do array
void sendPinStatus(int i)
{
client.write((uint8_t*)&pins[i], sizeof(Pin));
client.flush();
}
Server.ino
Declarações e variáveis
#include <WiFi.h>
#include <vector>
//Dados da rede
//Deve ser igual no Client
#define SSID "ESP32Server"
#define PASSWORD "87654321"
#define SERVER_PORT 5000
//Criamos um server na porta definida por 'SERVER_PORT'
WiFiServer server(SERVER_PORT);
//Vector onde vamos adicionar os clients conforme eles forem conectando
std::vector<WiFiClient> clients;
//Struct que define os dados que vamos enviar (deve ser igual no client)
typedef struct{
int number;
int status;
}Pin;
Setup
void setup()
{
Serial.begin(115200);
//Criamos a rede WiFi e iniciamos o server
setupWiFi();
server.begin();
xTaskCreatePinnedToCore(
handleClients, //Função que será executada
"handleClients", //Nome da tarefa
10000, //Tamanho da pilha
NULL, //Parâmetro da tarefa (no caso não usamos)
2, //Prioridade da tarefa
NULL, //Caso queria manter uma referência para a tarefa que vai ser criada (no caso não precisamos)
0); //Número do core que será executada a tarefa (usamos o core 0 para o loop ficar livre com o core 1)
}
SetupWiFi
void setupWiFi()
{
//Coloca este ESP como Access Point
WiFi.mode(WIFI_AP);
//SSID e Senha para se conectarem a este ESP
WiFi.softAP(SSID, PASSWORD);
}
Loop
void loop()
{
//Verifica se um novo client está tentando se conectar
WiFiClient client = server.available();
//Se sim colocamos ele no vector
if(client)
{
clients.push_back(client);
}
}
HandleClients
void handleClients(void* pvParameters)
{
//IMPORTANTE: A tarefa não pode terminar, deve ficar presa em um loop infinito
while(true)
{
//Para cada client que temos no vector
for(int i=0; i<clients.size(); i++)
{
//Guardamos a referência ao client
WiFiClient client = clients[i];
//Se o client está tentando enviar algo
if(client.available())
{
//Fazemos a leitura dos dados e colocamos na saída do pino o estado que recebemos
Pin pin;
client.read((uint8_t*)&pin, sizeof(pin));
pinMode(pin.number, OUTPUT);
digitalWrite(pin.number, pin.status);
}
}
//Delay de 5ms da tarefa. É feita em ticks. Para executar em millis dividimos pela constante portTICK_PERIOD_MS
TickType_t taskDelay = 5 / portTICK_PERIOD_MS;
vTaskDelay(taskDelay);
}
}
Faça o download dos arquivos







2 Comentários
Bom dia.
ResponderExcluirSigo o canal e vi ontem o vídeo deste tema, mas fiquei com uma dúvida, de como é que o server indexa os pinos de saída? Alguém me pode indicar?
Continuação de bom trabalho e obrigado. (de Portugal)
Olá Fernando, e fazer o inverso? o server mandar comandos aos clientes, chaves 1-2 cliente 1, chaves 3-4 cliente 2.
ResponderExcluirParabéns por seu belo trabalho. Abraços.