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.