Projetos que necessitem que ESP32s comuniquem entre si são recorrentes. Por exemplo: um ESP32 de uma central de automação que recebe dados de outros ESP32s espalhados pela casa. Para essa comunicação, pode se utilizar o protocolo ESP-NOW, que será abordado neste post.
Para este post, serão utilizados os seguintes materiais:
Adicionalmente, também será necessária uma rede sem fio provida por um roteador com Internet para conexão com o ESP32.
O ESP-NOW é um protocolo rápido desenvolvido pela Espressif (fabricante do ESP32), que permite a vários dispositivos ESP32 e ESP8266 se comunicarem sem usar WiFi. O protocolo é semelhante à conectividade sem fio de 2,4 GHz de baixa potência, geralmente implantada em mouses sem fio. Assim, o emparelhamento entre os dispositivos é necessário antes de sua comunicação. Após o pareamento, a conexão é segura e ponto a ponto, sem necessidade de os ESP32s afirmarem que reconheceram uns aos outros e estão prontos para iniciarem a comunicação (handshake).
O ESP-NOW aplica a tecnologia IEEE802.11 (tecnologia de redes locais sem-fio, mais conhecida como WiFi), juntamente com a tecnologia de criptografia CCMP (mecanismo aprimorado de encapsulamento criptográfico de dados projetado para confidencialidade de dados), realizando assim uma solução de comunicação.
Diferentemente dos protocolos WiFi tradicionais, no protocolo ESP-NOW, as cinco primeiras camadas superiores no OSI (modelo de rede de computador referência da ISO dividido em camadas de funções) são simplificadas para uma camada no ESP-NOW, portanto, os dados não precisam passar pela camada física, camada de enlace de dados, camada de rede, camada de transporte. Isso reduz o atraso causado pela perda de pacotes em rede congestionada e leva a um tempo de resposta rápido.
O protocolo ESP-NOW ocupa menos recursos de CPU e flash em comparação com o WiFi. O modo de transmissão de dados do ESP-NOW é flexível, incluindo unicast e broadcast, e suporta conexão unidirecional, bidirecional e multidirecional entre dispositivos.
Após ligar, os dispositivos podem transmitir dados e controlar outros dispositivos emparelhados diretamente “sem qualquer conexão sem fio”, e a velocidade de resposta é em milissegundos. Quando o dispositivo se conecta a um roteador ou funciona como um Ponto de Acesso, ele também pode realizar uma comunicação rápida e estável pelo ESP-NOW, além de manter uma conexão estável através do ESP-NOW mesmo se o roteador estiver com defeito ou a rede estiver instável.
Além disso, o protocolo ESP-NOW permite que seja realizada comunicação de longa distância estável, mesmo que os dispositivos estejam separados por paredes e até pisos.
O ESP-NOW suporta os seguintes recursos:
A tecnologia ESP-NOW também tem as seguintes limitações:
O uso do protocolo ESP-NOW é compatível com o WiFi e também com o Bluetooth Low Energy e suporta as séries de microcontroladores ESP8266, ESP32, ESP32-S e ESP32-C.
Para qualquer comunicação ESP-NOW entre ESP32s, é necessário saber o endereço MAC do ESP32 ao qual será feito o envio de dados, deste modo é que se sabe para qual dispositivo enviará os dados. Como cada ESP32 possui um endereço MAC único, este é o identificador de cada placa para enviar dados para ela usando o ESP-NOW.
Para efetuar a comunicação ESP-NOW, é feito o seguinte processo:
struct esp_now_peer_info
: Estrutura que armazena parâmetros de informações de dispositivos destinatários ESP-NOW.
uint8_t peer_addr[ESP_NOW_ETH_ALEN]
: um array de 6 bytes que representa o endereço MAC do dispositivo destinatário ESP-NOW;uint8_t channel
: Canal WiFi que o dispositivo destinatário usa para enviar e receber dados do ESP-NOW. O valor padrão é 0, que significa que o ESP-NOW irá escolher o canal automaticamente;bool encrypt
: Informa se os dados do ESP-NOW que o dispositivo destinatário envia e recebe devem ser criptografados ou não.
typedef struct esp_now_peer_info esp_now_peer_info_t
: Definição do tipo esp_now_peer_info
que armazena parâmetros de informações de dispositivos destinatários ESP-NOW.
typedef void (*esp_now_send_cb_t)(const uint8_t *mac_addr, esp_now_send_status_t status)
: Definição de tipo para função de retorno de chamada (callback) de envio de dados ESP-NOW, que é chamada quando uma mensagem é enviada com sucesso ou falha na transmissão via ESP-NOW.
mac_addr
: um ponteiro para o endereço MAC do dispositivo receptor da mensagem;status
: status do envio de dados ESP-NOW (sucesso ou falha), sendo retornado uma resposta do tipo Enumeração esp_now_send_status_t
.
typedef void (*esp_now_recv_cb_t)(const uint8_t * mac, const uint8_t *data, int data_len)
: Definição de tipo para função de retorno de chamada (callback) que é chamada sempre que um pacote é recebido por meio da comunicação ESP-NOW.
const uint8_t * mac
: um ponteiro para um array de 6 bytes que contém o endereço MAC do dispositivo remetente do pacote recebido;data
: um ponteiro para um array de bytes que contém o buffer de dados do pacote recebido;data_len
: um inteiro que representa o tamanho dos dados recebidos em bytes.
enum esp_now_send_status_t
: Essa enumeração define os possíveis valores de status de envio de dados do ESPNOW após uma tentativa de envio de mensagem.
enumerator ESP_NOW_SEND_SUCCESS
: indica que o envio da mensagem foi concluído com sucesso, ou seja, a mensagem foi enviada com êxito para o destinatário especificado;enumerator ESP_NOW_SEND_FAIL
: indica que o envio da mensagem falhou por algum motivo. Isso pode acontecer, por exemplo, se o destinatário estiver inativo ou fora do alcance do sinal.esp_err_t esp_now_init(void)
: função utilizada para inicializar a função ESP-NOW. Ela retorna um valor do tipo esp_err_t, que indica se a inicialização foi concluída com sucesso ou se houve algum erro.
ESP_OK
: indica que a inicialização da função ESP-NOW foi concluída com sucesso, ou seja, o módulo está pronto para ser usado;ESP_ERR_ESPNOW_INTERNAL
: indica um erro interno da função ESP-NOW, que pode ser causado por diversos motivos, como falha na comunicação com o hardware ou configuração incorreta do módulo. É recomendado tentar reiniciar o dispositivo e executar a inicialização novamente. Se o problema persistir, é possível verificar se a biblioteca ESP-NOW está atualizada e se a configuração do módulo está correta. Além disso, é possível consultar a documentação da biblioteca ou buscar ajuda na comunidade para encontrar soluções para problemas específicos.
esp_err_t esp_now_deinit(void)
: função da biblioteca ESP-NOW utilizada para finalizar a função ESP-NOW. Ela retorna um valor do tipo esp_err_t, que indica se a finalização foi concluída com sucesso ou se houve algum erro.
ESP_OK
: indica que a finalização do módulo ESP-NOW foi concluída com sucesso, ou seja, o módulo foi finalizado sem problemas. Isso significa que o módulo não está mais em uso e que a memória associada a ele foi liberada. Caso seja necessário, é possível chamar a função esp_now_init(void)
novamente para reinicializar o módulo ESP-NOW.
esp_err_t esp_now_register_recv_cb(esp_now_recv_cb_t cb)
: esta função é utilizada para registrar uma função de callback a ser chamada quando o dispositivo receber um pacote ESP-NOW. Ela retorna um valor que indica se a operação foi concluída com sucesso ou se houve algum erro.
cb
: ponteiro para a função de retorno de chamada de recebimento de dados ESPNOW.ESP_OK
: indica que a operação foi concluída com sucesso e que a função de callback foi registrada com êxito;ESP_ERR_ESPNOW_NOT_INIT
: indica que o módulo ESP-NOW ainda não foi inicializado. Nesse caso, é necessário chamar a função esp_now_init(void)
antes de registrar a função de callback;ESP_ERR_ESPNOW_INTERNAL
: indica que ocorreu um erro interno na biblioteca ESP-NOW, que pode ser causado por diversos motivos, como falha na comunicação com o hardware ou configuração incorreta do módulo. Nesse caso, é recomendado tentar reiniciar o dispositivo e executar a operação novamente.
esp_err_t esp_now_register_send_cb(esp_now_send_cb_t cb)
: registra um callback (função de retorno de chamada) para ser chamado quando um pacote de dados é enviado usando o ESP-NOW. O retorno da função é uma variável do tipo “esp_err_t”, que indica o status da operação.
cb
: ponteiro para a função de retorno de chamada que deve ser registrada.ESP_OK
: a operação foi concluída com êxito;ESP_ERR_ESPNOW_NOT_INIT
: a biblioteca ESP-NOW ainda não foi inicializada;ESP_ERR_ESPNOW_INTERNAL
: um erro interno ocorreu na biblioteca ESP-NOW.
esp_err_t esp_now_add_peer(const esp_now_peer_info_t *peer)
: adiciona um novo dispositivo (peer) à lista de dispositivos conhecidos. O retorno da função é uma variável do tipo “esp_err_t”, que indica o status da operação.
peer
: ponteiro para uma estrutura do tipo esp_now_peer_info_t
que contém as informações do novo dispositivo.ESP_OK
: o novo dispositivo foi adicionado com sucesso à lista de dispositivos conhecidos;ESP_ERR_ESPNOW_NOT_INIT
: a biblioteca ESP-NOW ainda não foi inicializada;ESP_ERR_ESPNOW_ARG
: algum dos argumentos passados para a função é inválido;ESP_ERR_ESPNOW_FULL
: a lista de dispositivos conhecidos já está cheia e não é possível adicionar um novo dispositivo;ESP_ERR_ESPNOW_NO_MEM
: a biblioteca ESP-NOW não tem memória suficiente para adicionar um novo dispositivo;ESP_ERR_ESPNOW_EXIST
: o dispositivo já está na lista de dispositivos conhecidos.
esp_err_t esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len)
: envia um pacote de dados para um dispositivo específico na rede ESP-NOW. O retorno da função é uma variável do tipo “esp_err_t”, que indica o status da operação.
peer_addr
: um ponteiro para um array de 6 bytes que contém o endereço MAC do dispositivo de destino (Se peer_addr
for NULL, os dados serão enviados para todos os pares adicionados à lista de pares);data
: um ponteiro para um array de bytes que contém os dados a serem enviados;len
: um valor inteiro que indica o tamanho dos dados a serem enviados (O comprimento máximo dos dados deve ser menor que 250 bytes).ESP_OK
: o pacote de dados foi enviado com sucesso;ESP_ERR_ESPNOW_NOT_INIT
: a biblioteca ESP-NOW ainda não foi inicializada;ESP_ERR_ESPNOW_INTERNAL
: ocorreu um erro interno na biblioteca ESP-NOW;ESP_ERR_ESPNOW_ARG
: algum dos argumentos passados para a função é inválido;ESP_ERR_ESPNOW_NO_MEM
: a biblioteca ESP-NOW não tem memória suficiente para enviar o pacote de dados, quando isso acontecer, você pode demorar um pouco antes de enviar os próximos dados;ESP_ERR_ESPNOW_NOT_FOUND
: esse erro ocorre quando o endereço MAC apontado por peer_addr
não corresponde a nenhum dispositivo conhecido na rede ESP-NOW. Isso pode acontecer se o dispositivo de destino não estiver dentro do alcance ou se não estiver executando o código ESP-NOW apropriado;ESP_ERR_ESPNOW_IF
: esse erro ocorre quando há um problema com a interface Wi-Fi ou ESP-NOW. Por exemplo, pode ocorrer se a interface Wi-Fi não estiver configurada corretamente ou se a inicialização do ESP-NOW falhar.
Todas as funções, estruturas, definições e enumerações podem ser consultadas na documentação, acessada em docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html
Nota de Aviso: É importante lembrar que qualquer função de callback deve ser rápida e não deve realizar operações demoradas ou bloqueantes, já que ela é chamada em uma interrupção de hardware.
Para saber o endereço MAC do ESP32, carregue o seguinte sketch no ESP32:
Nota: Não é necessário nenhum hardware extra conectado ao ESP32.
/****************************************************************************** ESP-NOW: Comunicação entre ESP32s Sketch Obtendo endereço MAC do ESP32 Criado em 08 de Dezembro de 2022 por Michel Galvão Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão das bibliotecas #include <WiFi.h> void setup() { Serial.begin(115200); // configurada a taxa de transferência em 115200 bits por segundo para a transmissão serial WiFi.mode(WIFI_MODE_STA); // configura o WIFi para o Modo de estação WiFi Serial.print("Endereço MAC: "); Serial.println(WiFi.macAddress()); // retorna o endereço MAC do dispositivo while(1); // loop infinito } void loop() {}
Este sketch usa a biblioteca WiFi.h para se conectar a uma rede WiFi e exibir o endereço MAC do dispositivo na porta serial.
Na função setup()
, a taxa de transmissão serial é configurada para 115200 bits por segundo com o comando Serial.begin(115200)
. Em seguida, a configuração do WiFi é definida para o modo estação com WiFi.mode(WIFI_MODE_STA)
.
A próxima linha de código exibe o endereço MAC do dispositivo na porta serial com a instrução Serial.print("Endereço MAC: "); Serial.println(WiFi.macAddress());
.
Por fim, o programa entra em um loop infinito com o comando while(1)
na última linha da função setup()
, enquanto a função loop()
está vazia.
Após o upload do sketch para o ESP32, abra o monitor serial (Ctrl + Shift + M, na IDE Arduino) e visualize e guarde o endereço MAC da placa ESP32.
Uma rede de comunicação unidirecional com mestre enviando para um escravo é aquela em que o mestre envia dados para um único escravo, sem que haja retorno de dados do escravo para o mestre pelo mesmo canal de comunicação.
Nesse modo de comunicação, o mestre pode enviar informações específicas para o escravo, como um comando para ligar ou desligar um dispositivo. Por exemplo, em um sistema de monitoramento de temperatura em um data center, o mestre pode enviar comandos para um dispositivo escravo, como um termostato, para ajustar a temperatura em uma sala específica.
Neste exemplo, será enviado uma estrutura com variáveis de tipos diferentes utilizando a Comunicação Unidirecional com mestre enviando para escravo.
Para funcionar os sketches abaixo, primeiro faça o upload do sketch de obtenção de endereço MAC na placa ESP32 escravo, onde o código está disponível no tópico Obtendo endereço MAC do ESP32. Com o endereço MAC obtido, substitua o valor da variável slaveMacAddress
pelo valor do MAC obtido acrescido do prefixo 0x à cada intervalo do MAC (Como exemplo, a formatação do endereço MAC 00:1A:2B:3C:4D:5E seria {0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E}
).
/****************************************************************************** Comunicação Unidirecional com mestre enviando para um escravo Sketch Mestre Criado em 26 de Fevereiro de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ #include <LiquidCrystal_I2C.h> // Inclui a biblioteca LiquidCrystal_I2C para controle do display LCD I2C #include <esp_now.h> // Inclui a biblioteca esp_now para o uso do protocolo de comunicação ESP-NOW #include <Wire.h> // Inclui a biblioteca Wire para a comunicação I2C #include <WiFi.h> // Inclui a biblioteca WiFi para configuração da rede sem fio const uint8_t pinledVermelho = 13; // Define o pino do LED vermelho como 13 const uint8_t pinledVerde = 12; // Define o pino do LED verde como 12 const uint8_t pinSda = 14; // Define o pino SDA como 14 para a comunicação I2C const uint8_t pinScl = 27; // Define o pino SCL como 27 para a comunicação I2C uint8_t slaveMacAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Define o endereço MAC do dispositivo escravo. Coloque o endereço MAC de sua placa aqui struct DataStruct { // Define a estrutura DataStruct para troca de informações int integerData; // Variável inteira integerData float floatData; // Variável de ponto flutuante floatData bool booleanData; // Variável booleana booleanData }; esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW // Declara a estrutura dataToSend e atribui valores iniciais às variáveis DataStruct dataToSend = { .integerData = 0, .floatData = 0, .booleanData = false }; LiquidCrystal_I2C lcd(0x27, 16, 2); // Declara o objeto lcd para controle do display LCD I2C void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { // Função chamada quando os dados são enviados if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... digitalWrite(pinledVerde, HIGH); // Acende o LED verde digitalWrite(pinledVermelho, LOW); // Apaga o LED vermelho lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print("Envio OK "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display String valores = "I:"; // Cria uma string com o valor "I:" if (dataToSend.integerData < 10 && dataToSend.integerData >= 0) { // Se o valor inteiro for menor que 10 e maior ou igual a 0 valores += "0"; // Adiciona um 0 à string } valores += dataToSend.integerData; // Adiciona o valor inteiro à string valores += " F:"; // Adiciona o valor "F:" à string if (dataToSend.floatData < 10 && dataToSend.floatData >= 0) { // Se o valor de ponto flutuante for menor que 10 e maior ou igual a 0 valores += "0"; // Adiciona um 0 à string } valores += dataToSend.floatData; // Adiciona o valor de ponto flutuante à string valores valores += " B:"; // Adiciona o valor "B:" à string valores valores += dataToSend.booleanData; // Adiciona o valor booleano à string valores lcd.print(valores); // exibe no dispaly lcd o conteúdo va variável valores dataToSend.integerData++; // Incrementa o valor inteiro dataToSend.floatData = random(-9, 99) + (float)random(-9, 99) / 99; // Gera um valor de ponto flutuante aleatório dataToSend.booleanData = random(0, 2); // Gera um valor booleano aleatório // Se o valor inteiro ultrapassar 99, retorna para 0 if (dataToSend.integerData > 99) { dataToSend.integerData = 0; // o valor de dataToSend.integerData volta à ser Zero } } else { digitalWrite(pinledVerde, LOW); // Apaga o LED verde digitalWrite(pinledVermelho, HIGH); // Acende o LED vermelho lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print("Falha no Envio "); // Escreve a mensagem no display } } void setup() { Wire.begin(pinSda, pinScl); // Inicia a comunicação I2C com os pinos SDA e SCL definidos anteriormente lcd.init(); // Inicializa o display lcd.backlight(); // Acende o backlight do display pinMode(pinledVerde, OUTPUT); // Define o pino do LED verde como saída pinMode(pinledVermelho, OUTPUT); // Define o pino do LED vermelho como saída digitalWrite(pinledVermelho, LOW); // Apaga o LED vermelho digitalWrite(pinledVerde, LOW); // Apaga o LED verde lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print(" Comunicacao "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print(" ESPNOW "); // Escreve a mensagem no display delay(1200); // Espera por 1,2 segundos lcd.clear(); // Limpa o display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Mestre /"); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("escravo"); // Escreve a mensagem no display delay(1000); // Espera por 1 segundo lcd.clear(); // Limpa o display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Este dispositivo"); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("eh Mestre"); // Escreve a mensagem no display delay(1000); // Espera por 1 segundo lcd.clear(); // Limpa o display WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_STA); // Define o modo WiFi como Station (cliente) if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Inicializacao "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("ESP-NOW com Erro"); // Escreve a mensagem no display digitalWrite(pinledVermelho, HIGH); // Acende o LED vermelho delay(2500); // Espera por 2,5 segundos ESP.restart(); // Reinicia o dispositivo } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados memcpy(peerInfo.peer_addr, slaveMacAddress, 6); // Copia o endereço MAC do escravo para a estrutura peerInfo peerInfo.channel = 0; // Define o canal de comunicação como 0 na estrutura peerInfo peerInfo.encrypt = false; // Define a encriptação como desativada na estrutura peerInfo // Tenta adicionar o escravo à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o escravo, exibe mensagem de falha no display, acende LED vermelho e reinicia o dispositivo lcd.setCursor(0, 0); lcd.print("Falha ao"); lcd.setCursor(0, 1); lcd.print("adicionar peer"); digitalWrite(pinledVermelho, HIGH); delay(2500); ESP.restart(); } } void loop() { esp_now_send(slaveMacAddress, (uint8_t *)&dataToSend, sizeof(DataStruct)); // Envie os dados para o endereço MAC do dispositivo escravo delay(1000); // Espere 1 segundo antes de enviar novamente }
O código usa a biblioteca LiquidCrystal_I2C para controlar o display LCD I2C, a biblioteca esp_now para usar o protocolo de comunicação ESP-NOW, a biblioteca Wire para comunicação I2C e a biblioteca WiFi para configuração da rede sem fio.
As variáveis pinledVermelho e pinledVerde são usadas para definir o pino do LED vermelho e verde, respectivamente, enquanto pinSda e pinScl são usados para definir os pinos SDA e SCL, que são usados para a comunicação I2C. O endereço MAC do dispositivo escravo é definido em slaveMacAddress.
O código cria a estrutura DataStruct para troca de informações, que contém três variáveis: integerData, floatData e booleanData. A função OnDataSent é chamada quando os dados são enviados e lida a estrutura de dados enviada.
A variável dataToSend é usada para armazenar os dados para serem enviados, com valores iniciais atribuídos às variáveis. O código também usa o objeto lcd para controlar o display LCD I2C e exibir informações.
Quando os dados são enviados com sucesso, o LED verde é aceso e o LED vermelho é apagado. As informações sobre os dados enviados são exibidas no display LCD I2C. As variáveis integerData, floatData e booleanData são atualizadas com novos valores aleatórios gerados. Se o valor inteiro ultrapassar 99, ele retorna a zero.
/****************************************************************************** Comunicação Unidirecional com mestre enviando para um escravo Sketch Escravo Criado em 26 de Fevereiro de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão de bibliotecas #include <LiquidCrystal_I2C.h> // Biblioteca para utilizar um display de LCD com comunicação I2C #include <esp_now.h> // Biblioteca para utilizar o protocolo de comunicação ESP-NOW #include <Wire.h> // Biblioteca para comunicação I2C #include <WiFi.h> // Biblioteca para conectar em redes Wi-Fi // Definição dos pinos utilizados const uint8_t pinledVermelho = 13; // LED vermelho conectado ao pino 13 const uint8_t pinledVerde = 12; // LED verde conectado ao pino 12 const uint8_t pinSda = 14; // Pino SDA para comunicação I2C const uint8_t pinScl = 27; // Pino SCL para comunicação I2C // Estrutura de dados para troca de informações struct DataStruct { int integerData; // Dado do tipo inteiro float floatData; // Dado do tipo float bool booleanData; // Dado do tipo booleano }; // Variável para armazenar o tempo da última recepção de dados unsigned long temporizadorUltimaRecepcao = 0; // Variável para armazenar os dados recebidos DataStruct dataStruct; // Objeto para utilizar o display de LCD de 16 colunas, 2 linhas e conectado no endereço I2C 0x27 LiquidCrystal_I2C lcd(0x27, 16, 2); // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) { memcpy(&dataStruct, incomingData, sizeof(dataStruct)); // Copia os dados recebidos para a estrutura de dados temporizadorUltimaRecepcao = millis(); // Atualiza a variável de tempo da última recepção // Liga o LED verde e desliga o LED vermelho digitalWrite(pinledVerde, HIGH); digitalWrite(pinledVermelho, LOW); // Atualiza o display de LCD com informações sobre os dados recebidos lcd.setCursor(0, 0); lcd.print("Obtido "); lcd.print(len); lcd.print(" Bytes "); lcd.setCursor(0, 1); String valores = "I:"; if (dataStruct.integerData < 10 && dataStruct.integerData >= 0) {// Se o valor inteiro for menor que 10 e maior ou igual a 0, ... valores += "0"; // Adiciona o caractere "0" à string "valores" } valores += dataStruct.integerData; valores += " F:"; if (dataStruct.floatData < 10 && dataStruct.floatData >= 0) { // Se o valor de ponto flutuante for menor que 10 e maior ou igual a 0, ... valores += "0";; // Adiciona o caractere "0" à string "valores" } valores += dataStruct.floatData; valores += " B:"; valores += dataStruct.booleanData; valores += " "; lcd.print(valores); } void setup() { Wire.begin(pinSda, pinScl); // Inicia a comunicação I2C do display LCD I2C conectado nos pinos informados lcd.init(); // Inicia o display de LCD lcd.backlight(); // Liga a luz de fundo do display LCD // Configura os pinos dos LEDs como saídas e inicialmente desliga ambos pinMode(pinledVerde, OUTPUT); pinMode(pinledVermelho, OUTPUT); digitalWrite(pinledVermelho, LOW); digitalWrite(pinledVerde, LOW); // Mensagem de inicialização no display de LCD lcd.setCursor(0, 0); lcd.print(" Comunicacao "); lcd.setCursor(0, 1); lcd.print(" ESPNOW "); delay(1200); lcd.clear(); // Mensagem do modo de configuração mestre/escravo no display de LCD lcd.setCursor(0, 0); lcd.print("Mestre /"); lcd.setCursor(0, 1); lcd.print("escravo"); delay(1000); lcd.clear(); // Mensagem informando que este dispositivo é escravo lcd.setCursor(0, 0); lcd.print("Este dispositivo"); lcd.setCursor(0, 1); lcd.print("eh Escravo"); delay(1000); lcd.clear(); // Desconecta de alguma conexão WiFi anterior e define o modo estação (STA) WiFi.disconnect(); WiFi.mode(WIFI_STA); // Inicia a biblioteca ESP-NOW e, caso ocorra algum erro, reinicia o dispositivo if (esp_now_init() != ESP_OK) { lcd.setCursor(0, 0); lcd.print("Inicializacao "); lcd.setCursor(0, 1); lcd.print("ESP-NOW com Erro"); digitalWrite(pinledVermelho, HIGH); delay(2500); ESP.restart(); } // Registra a função OnDataRecv como a função a ser chamada quando receber dados via ESP-NOW esp_now_register_recv_cb(OnDataRecv); // Define o tempo atual como o temporizador da última recepção de dados temporizadorUltimaRecepcao = millis(); } void loop() { // Função loop() que executa repetidamente enquanto a placa estiver ligada // Verifica se o tempo desde a última recepção é maior que 2 segundos if (millis() - temporizadorUltimaRecepcao > 2000) { digitalWrite(pinledVermelho, HIGH); // Liga o LED vermelho indicando que a recepção ultrapassou o tempo limite digitalWrite(pinledVerde, LOW); // Desliga o LED verde lcd.clear();// Limpa o display de LCD lcd.setCursor(0, 0); // Mostra mensagem de tempo limite ultrapassado no display de LCD lcd.print("Tempo limite de "); lcd.setCursor(0, 1); lcd.print("recepcao ultrap."); delay(1000); // Aguarda 1 segundo antes de continuar a execução } }
A biblioteca LiquidCrystal_I2C e Wire é incluída para controlar um display LCD de 16×2 colunas e linhas com comunicação I2C. Além disso, as bibliotecas ESP-NOW e WiFi também são incluídas.
O código define alguns pinos para uso: pinledVermelho (pino 13) e pinledVerde (pino 12) para leds, e pinSda (pino 14) e pinScl (pino 27) para comunicação I2C. Uma estrutura de dados chamada DataStruct é criada para armazenar os dados recebidos.
Quando dados são recebidos via ESP-NOW, a função OnDataRecv é chamada. Essa função copia os dados recebidos para a estrutura de dados DataStruct, atualiza a variável de tempo temporizadorUltimaRecepcao e faz algumas outras ações, como ligar o LED verde e atualizar o display LCD com informações sobre os dados recebidos.
Na função setup, a comunicação I2C é iniciada, o display LCD é iniciado e a luz de fundo do display é ligada. Os pinos dos LEDs são configurados como saídas e inicialmente desligados. O display LCD é inicializado e uma mensagem de inicialização é exibida. Uma mensagem é exibida para informar que o dispositivo é escravo.
A função loop() contém uma verificação de tempo que compara o tempo atual com o tempo da última recepção. Se o tempo desde a última recepção for maior que 2 segundos, o LED vermelho é ligado e o LED verde é desligado. Além disso, o display LCD é limpo e uma mensagem é exibida indicando que o tempo limite de recepção foi ultrapassado. Após a exibição da mensagem, a execução é pausada por 1 segundo antes de continuar.
Uma rede de comunicação unidirecional com mestre enviando para dois escravos é aquela em que o mestre envia dados para dois escravos, sem que haja retorno de dados dos escravos para o mestre pelo mesmo canal de comunicação. O dispositivo mestre também pode enviar para dados para mais de dois escravos, se limitando à quantidade de dispositivos máximos oferecidos pela tecnologia ESP-NOW, mas neste exemplo, como exemplificação, só irá ser utilizado dois escravos. Nesse modo de comunicação, o mestre pode enviar as mesmas informações para ambos os escravos ao mesmo tempo, ou enviar dados diferentes para cada um dos escravos. Para enviar dados sequencialmente, o mestre deve enviar cada mensagem separadamente com esp_now_send(). Neste exemplo, como exemplificação, só irá ser utilizado o modo de envio de dados simultâneo.
Por exemplo, em um sistema de automação residencial, o mestre pode enviar comandos para dois dispositivos escravos, como acender as luzes em duas salas diferentes, sem que haja necessidade de enviar os comandos individualmente para cada dispositivo (simultâneo). Ou o mestre pode enviar comandos para duas persianas eletrônicas em quartos diferentes em ordem de prioridade, primeiro, por exemplo, para a persiana do quarto de um bebê, e depois para a persiana do quarto dos pais (sequencial).
/****************************************************************************** Comunicação Unidirecional com mestre enviando para dois escravos Sketch Mestre Criado em 28 de Fevereiro de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ #include <LiquidCrystal_I2C.h> // Inclui a biblioteca LiquidCrystal_I2C para controle do display LCD I2C #include <esp_now.h> // Inclui a biblioteca esp_now para o uso do protocolo de comunicação ESP-NOW #include <Wire.h> // Inclui a biblioteca Wire para a comunicação I2C #include <WiFi.h> // Inclui a biblioteca WiFi para configuração da rede sem fio const uint8_t pinledVermelho = 13; // Define o pino do LED vermelho como 13 const uint8_t pinledVerde = 12; // Define o pino do LED verde como 12 const uint8_t pinSda = 14; // Define o pino SDA como 14 para a comunicação I2C const uint8_t pinScl = 27; // Define o pino SCL como 27 para a comunicação I2C uint8_t slave1MacAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Define o endereço MAC do dispositivo escravo 1. Coloque o endereço MAC de sua placa 1 aqui uint8_t slave2MacAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Define o endereço MAC do dispositivo escravo 2. Coloque o endereço MAC de sua placa 2 aqui struct DataStruct { // Define a estrutura DataStruct para troca de informações int value; // Variável inteira value }; esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW DataStruct dataToSend; // Declara a estrutura dataToSend LiquidCrystal_I2C lcd(0x27, 16, 2); // Declara o objeto lcd para controle do display LCD I2C // Função para formatar um número inteiro como uma string String formatarNumero(int number) { // Inicializa uma variável temporária como uma string vazia String temp = ""; // Se o número fornecido for menor que 10, adiciona um "0" à string temporária if (number < 10) { temp = "0"; } // Adiciona o número à string temporária temp += number; // Retorna a string formatada return temp; } void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { // Função chamada quando os dados são enviados if (memcmp(mac_addr, slave1MacAddress, 6) == 0) { if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print(" OK! slave1:"); lcd.print(formatarNumero(dataToSend.value)); // Escreve o valor no display } else { lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print("FALHA! slave1 "); } } else if (memcmp(mac_addr, slave2MacAddress, 6) == 0) { if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... lcd.setCursor(0, 1); lcd.print(" OK! slave2:"); lcd.print(formatarNumero(dataToSend.value)); // Escreve o valor no display } else { lcd.setCursor(0, 1); // Define a posição do cursor do display lcd.print("FALHA! slave2 "); } } } void setup() { Wire.begin(pinSda, pinScl); // Inicia a comunicação I2C com os pinos SDA e SCL definidos anteriormente lcd.init(); // Inicializa o display lcd.backlight(); // Acende o backlight do display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print(" Comunicacao "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print(" ESPNOW "); // Escreve a mensagem no display delay(1200); // Espera por 1,2 segundos lcd.clear(); // Limpa o display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Mestre /"); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("2 escravos"); // Escreve a mensagem no display delay(1000); // Espera por 1 segundo lcd.clear(); // Limpa o display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Este dispositivo"); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("eh Mestre"); // Escreve a mensagem no display delay(1000); // Espera por 1 segundo lcd.clear(); // Limpa o display WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_STA); // Define o modo WiFi como Station (cliente) if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Inicializacao "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("ESP-NOW com Erro"); // Escreve a mensagem no display delay(2500); // Espera por 2,5 segundos ESP.restart(); // Reinicia o dispositivo } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados peerInfo.channel = 0; // Define o canal de comunicação como 0 na estrutura peerInfo peerInfo.encrypt = false; // Define a encriptação como desativada na estrutura peerInfo memcpy(peerInfo.peer_addr, slave1MacAddress, 6); // Copia o endereço MAC do escravo 1 para a estrutura peerInfo // Tenta adicionar o escravo 1 à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o escravo 1, exibe mensagem de falha no display e reinicia o dispositivo lcd.setCursor(0, 0); lcd.print("Falha ao"); lcd.setCursor(0, 1); lcd.print("adicionar peer1"); delay(2500); ESP.restart(); } memcpy(peerInfo.peer_addr, slave2MacAddress, 6); // Copia o endereço MAC do escravo 2 para a estrutura peerInfo // Tenta adicionar o escravo 2 à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o escravo 2, exibe mensagem de falha no display e reinicia o dispositivo lcd.setCursor(0, 0); lcd.print("Falha ao"); lcd.setCursor(0, 1); lcd.print("adicionar peer2"); delay(2500); ESP.restart(); } // Preparando os dados a serem enviados dataToSend.value = 0; } void loop() { esp_now_send(0, (uint8_t *)&dataToSend, sizeof(DataStruct)); // Envie os dados para todos os escravos cadastrados. Isso é definido através do primeiro argumento com valor 0 delay(1000); // Espere 1 segundo antes de enviar novamente dataToSend.value++; // Incrementa o valor de dataToSend.value // Se o valor inteiro ultrapassar 10, retorna para 0 if (dataToSend.value > 10) { dataToSend.value = 0; // o valor de dataToSend.value volta à ser Zero } }
Nesse sketch, o Arduino funciona como o mestre e envia informações simultâneas para dois dispositivos escravos, cada um identificado pelo seu endereço MAC exclusivo. O sketch também utiliza um display LCD I2C para mostrar informações sobre o status da comunicação.
A estrutura “DataStruct” é utilizada para enviar os dados através do protocolo ESP-NOW e a estrutura “esp_now_peer_info_t” é utilizada para registrar informações sobre os dispositivos escravos na rede ESP-NOW.
A função “formatarNumero” é utilizada para formatar um número inteiro como uma string de dois dígitos para ser exibido no display.
A função OnDataSent é chamada automaticamente pelo protocolo de comunicação ESP-NOW sempre que um pacote de dados é enviado pelo mestre para um dispositivo escravo. Esta recebe como parâmetros o endereço MAC do dispositivo que recebeu os dados e o status do envio (ESP_NOW_SEND_SUCCESS para envio bem-sucedido ou ESP_NOW_SEND_FAIL para envio falhado).
Dentro da função, é verificado qual dispositivo escravo recebeu os dados comparando o endereço MAC recebido com os endereços MAC dos dispositivos escravos previamente definidos no código. Em seguida, a função verifica se o envio foi bem-sucedido e atualiza o display LCD com uma mensagem indicando se o envio foi bem-sucedido ou falhou.
No caso de um envio bem-sucedido, a função também escreve o valor enviado no display LCD junto com a mensagem de sucesso.
Na seção “setup” do código, é iniciada a comunicação I2C nos pinos SDA e SCL especificados, e o display LCD I2C é inicializado e ligado. Em seguida, é exibida uma mensagem de inicialização no display, informando que a comunicação ESP-NOW está sendo configurada.
Na seção “loop” do código, um valor é atribuído à variável “dataToSend.value” e enviado para os dois dispositivos escravos simultaneamente através da função “esp_now_send” com o primeiro parâmetro sendo 0. Quando a transmissão é bem sucedida, a função “OnDataSent” é chamada para exibir uma mensagem de confirmação no display LCD I2C. Se a transmissão falhar, uma mensagem de erro é exibida no display.
/****************************************************************************** Comunicação Unidirecional com mestre enviando para dois escravos Sketch Escravo 1 Criado em 27 de Fevereiro de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão de bibliotecas #include <LiquidCrystal_I2C.h> // Biblioteca para utilizar um display de LCD com comunicação I2C #include <esp_now.h> // Biblioteca para utilizar o protocolo de comunicação ESP-NOW #include <Wire.h> // Biblioteca para comunicação I2C #include <WiFi.h> // Biblioteca para conectar em redes Wi-Fi // Definição dos pinos utilizados const uint8_t pinledVermelho = 13; // LED vermelho conectado ao pino 13 const uint8_t pinledVerde = 12; // LED verde conectado ao pino 12 const uint8_t pinSda = 14; // Pino SDA para comunicação I2C const uint8_t pinScl = 27; // Pino SCL para comunicação I2C // Estrutura de dados para troca de informações struct DataStruct { int value; // Dado do tipo inteiro }; // Variável para armazenar o tempo da última recepção de dados unsigned long temporizadorUltimaRecepcao = 0; // Variável para armazenar os dados recebidos DataStruct dataStruct; // Objeto para utilizar o display de LCD de 16 colunas, 2 linhas e conectado no endereço I2C 0x27 LiquidCrystal_I2C lcd(0x27, 16, 2); // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) { memcpy(&dataStruct, incomingData, sizeof(dataStruct)); // Copia os dados recebidos para a estrutura de dados temporizadorUltimaRecepcao = millis(); // Atualiza a variável de tempo da última recepção // Liga o LED verde e desliga o LED vermelho digitalWrite(pinledVerde, HIGH); digitalWrite(pinledVermelho, LOW); // Atualiza o display de LCD com informações sobre os dados recebidos lcd.setCursor(0, 0); lcd.print("Obtido "); lcd.print(len); lcd.print(" Bytes "); lcd.setCursor(0, 1); lcd.print("value: "); // Se o valor inteiro for menor que 10, adicionar um zero na frente if (dataStruct.value < 10) { lcd.print("0"); } // Imprimir o valor inteiro na tela lcd.print(dataStruct.value); // Limpar a tela para garantir que não haja caracteres antigos lcd.print(" "); } void setup() { Wire.begin(pinSda, pinScl); // Inicia a comunicação I2C do display LCD I2C conectado nos pinos informados lcd.init(); // Inicia o display de LCD lcd.backlight(); // Liga a luz de fundo do display LCD // Configura os pinos dos LEDs como saídas e inicialmente desliga ambos pinMode(pinledVerde, OUTPUT); pinMode(pinledVermelho, OUTPUT); digitalWrite(pinledVermelho, LOW); digitalWrite(pinledVerde, LOW); // Mensagem de inicialização no display de LCD lcd.setCursor(0, 0); lcd.print(" Comunicacao "); lcd.setCursor(0, 1); lcd.print(" ESPNOW "); delay(1200); lcd.clear(); // Mensagem do modo de configuração mestre/escravo no display de LCD lcd.setCursor(0, 0); lcd.print("Mestre /"); lcd.setCursor(0, 1); lcd.print("2 escravos"); delay(1000); lcd.clear(); // Mensagem informando que este dispositivo é escravo lcd.setCursor(0, 0); lcd.print("Este dispositivo"); lcd.setCursor(0, 1); lcd.print("eh Escravo 1"); delay(1000); lcd.clear(); // Desconecta de alguma conexão WiFi anterior e define o modo estação (STA) WiFi.disconnect(); WiFi.mode(WIFI_STA); // Inicia a biblioteca ESP-NOW e, caso ocorra algum erro, reinicia o dispositivo if (esp_now_init() != ESP_OK) { lcd.setCursor(0, 0); lcd.print("Inicializacao "); lcd.setCursor(0, 1); lcd.print("ESP-NOW com Erro"); digitalWrite(pinledVermelho, HIGH); delay(2500); ESP.restart(); } // Registra a função OnDataRecv como a função a ser chamada quando receber dados via ESP-NOW esp_now_register_recv_cb(OnDataRecv); // Define o tempo atual como o temporizador da última recepção de dados temporizadorUltimaRecepcao = millis(); } void loop() { // Função loop() que executa repetidamente enquanto a placa estiver ligada // Verifica se o tempo desde a última recepção é maior que 2 segundos if (millis() - temporizadorUltimaRecepcao > 2000) { digitalWrite(pinledVermelho, HIGH); // Liga o LED vermelho indicando que a recepção ultrapassou o tempo limite digitalWrite(pinledVerde, LOW); // Desliga o LED verde lcd.clear(); // Limpa o display de LCD // Mostra mensagem de tempo limite ultrapassado no display de LCD lcd.setCursor(0, 0); lcd.print("Tempo limite de "); lcd.setCursor(0, 1); lcd.print("recepcao ultrap."); delay(1000); // Aguarda 1 segundo antes de continuar a execução } }
Nesse exemplo, o código é para um dispositivo escravo, que recebe dados de um dispositivo mestre e os exibe em um display de LCD e acende um LED verde indicando que os dados foram recebidos com sucesso.
O código utiliza algumas bibliotecas, incluindo a “LiquidCrystal_I2C.h” para controlar o display de LCD e a “esp_now.h” para utilizar o protocolo de comunicação ESP-NOW. Também são definidos alguns pinos que serão utilizados, como os pinos para os LEDs e os pinos SDA e SCL para a comunicação I2C.
O dispositivo escravo tem uma estrutura de dados chamada “DataStruct” com um único campo do tipo inteiro chamado “value”. Quando o dispositivo mestre envia dados para o dispositivo escravo, ele copia esses dados para essa estrutura de dados e atualiza uma variável que armazena o tempo da última recepção de dados.
Quando a função “OnDataRecv” é chamada, ela copia os dados recebidos para a estrutura de dados “DataStruct” e atualiza a variável “temporizadorUltimaRecepcao” com o tempo atual. Em seguida, a função acende o LED verde e desliga o LED vermelho. Ela também atualiza o display de LCD com informações sobre os dados recebidos, como o número de bytes e o valor do campo “value”.
O setup é responsável pela configuração inicial do dispositivo, incluindo a configuração do display de LCD, dos LEDs e da comunicação ESP-NOW. Ele também exibe mensagens no display de LCD indicando o modo de configuração mestre/escravo e que o dispositivo é o escravo número 1. Em caso de erro durante a inicialização da comunicação ESP-NOW, o dispositivo reinicia.
No loop, é verificado se o tempo desde a última recepção é maior que 2 segundos e se o tempo de inatividade da recepção for maior, o LED vermelho é ligado, indicando que a recepção ultrapassou o tempo limite. Além disso, o LED verde é desligado e o display de LCD mostra uma mensagem indicando que o tempo limite de recepção foi ultrapassado.
/****************************************************************************** Comunicação Unidirecional com mestre enviando para dois escravos Sketch Escravo 2 Criado em 27 de Fevereiro de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão de bibliotecas #include <Adafruit_SSD1306.h> // Biblioteca para utilizar um display de OLED #include <esp_now.h> // Biblioteca para utilizar o protocolo de comunicação ESP-NOW #include <Wire.h> // Biblioteca para comunicação I2C #include <WiFi.h> // Biblioteca para conectar em redes Wi-Fi // Definição dos pinos utilizados const uint8_t pinledVermelho = 12; // LED vermelho conectado ao pino 13 const uint8_t pinledVerde = 14; // LED verde conectado ao pino 12 const uint8_t pinSda = 27; // Pino SDA para comunicação I2C const uint8_t pinScl = 26; // Pino SCL para comunicação I2C // Estrutura de dados para troca de informações struct DataStruct { int value; // Dado do tipo inteiro }; // Variável para armazenar o tempo da última recepção de dados unsigned long temporizadorUltimaRecepcao = 0; // Variável para armazenar os dados recebidos DataStruct dataStruct; // Cria uma instância da classe Adafruit_SSD1306 com as dimensões de 128x64 pixels, // utilizando a interface I2C (Wire) e com o endereço padrão do display (-1). Adafruit_SSD1306 display(128, 64, &Wire, -1); // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) { memcpy(&dataStruct, incomingData, sizeof(dataStruct)); // Copia os dados recebidos para a estrutura de dados temporizadorUltimaRecepcao = millis(); // Atualiza a variável de tempo da última recepção // Liga o LED verde e desliga o LED vermelho digitalWrite(pinledVerde, HIGH); digitalWrite(pinledVermelho, LOW); // Limpa a tela do display OLED e define a cor do texto como branco display.clearDisplay(); display.setTextColor(SSD1306_WHITE); // Atualiza o display de LCD com informações sobre os dados recebidos display.setCursor(0, 0); display.setTextSize(2); display.print("Obtido "); display.print(len); display.println(" B"); display.print("value: "); // Condição para verificar se o valor inteiro é menor que 10 if (dataStruct.value < 10) { // Se sim, exibe um zero à esquerda na tela display.print("0"); } // Imprime o valor da variável 'dataStruct.value' na tela OLED display.println(dataStruct.value); // Atualiza a tela OLED para exibir as mudanças display.display(); } void setup() { Wire.begin(pinSda, pinScl); // Inicia a comunicação I2C do display LCD I2C conectado nos pinos informados // Inicia a comunicação com o display OLED SSD1306 if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Se a alocação do display falhar, ... Serial.begin(115200); // Inicia a comunicação serial com uma taxa de 115200 bps Serial.println("SSD1306 allocation failed"); // Imprime uma mensagem de erro na porta serial delay(10000); // Espera 10 segundos ESP.restart(); // Reinicia o dispositivo ESP } display.clearDisplay(); // Limpa o display do OLED. // Configura os pinos dos LEDs como saídas e inicialmente desliga ambos pinMode(pinledVerde, OUTPUT); pinMode(pinledVermelho, OUTPUT); digitalWrite(pinledVermelho, LOW); digitalWrite(pinledVerde, LOW); // Mensagem de inicialização no display de LCD display.setCursor(0, 0); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.println(" Comunicacao "); display.println(" ESPNOW "); display.display(); delay(1200); display.clearDisplay(); // Mensagem do modo de configuração mestre/escravo no display de LCD display.setCursor(0, 0); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.println(" Mestre /"); display.println(" 2 escravos"); display.display(); delay(1000); display.clearDisplay(); // Mensagem informando que este dispositivo é escravo display.setCursor(0, 0); display.println(" Este dispositivo"); display.println(" eh Escravo 2"); display.display(); delay(1000); display.clearDisplay(); // Desconecta de alguma conexão WiFi anterior e define o modo estação (STA) WiFi.disconnect(); WiFi.mode(WIFI_STA); // Inicia a biblioteca ESP-NOW e, caso ocorra algum erro, reinicia o dispositivo if (esp_now_init() != ESP_OK) { display.setCursor(0, 0); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.println(" Inicializacao"); display.println(" ESP-NOW com Erro"); digitalWrite(pinledVermelho, HIGH); display.display(); delay(2500); ESP.restart(); } // Registra a função OnDataRecv como a função a ser chamada quando receber dados via ESP-NOW esp_now_register_recv_cb(OnDataRecv); // Define o tempo atual como o temporizador da última recepção de dados temporizadorUltimaRecepcao = millis(); } void loop() { // Função loop() que executa repetidamente enquanto a placa estiver ligada // Verifica se o tempo desde a última recepção é maior que 2 segundos if (millis() - temporizadorUltimaRecepcao > 2000) { digitalWrite(pinledVermelho, HIGH); // Liga o LED vermelho indicando que a recepção ultrapassou o tempo limite digitalWrite(pinledVerde, LOW); // Desliga o LED verde display.clearDisplay(); // Limpa o display do OLED display.setCursor(0, 0); // Posiciona o cursor no início do display display.setTextSize(1); // Define o tamanho do texto do display display.setTextColor(SSD1306_WHITE); // Define a cor do texto do display // Mostra mensagem de tempo limite ultrapassado no display de OLED display.println("Tempo limite de "); display.println("recepcao ultrapassado"); display.display(); // Exibe a mensagem no display delay(1000); // Aguarda 1 segundo antes de continuar a execução } }
Esse é um sketch para um dispositivo escravo que usa o protocolo de comunicação ESP-NOW para receber dados enviados por um dispositivo mestre. O objetivo é exibir os dados recebidos em um display OLED e acender um LED verde indicando que houve uma transmissão bem-sucedida.
Na seção “Inclusão de bibliotecas”, são importadas as bibliotecas necessárias para o código funcionar corretamente: Adafruit_SSD1306 para o display OLED, esp_now para o protocolo de comunicação e Wire para a comunicação I2C.
Em seguida, são definidos os pinos usados pelo dispositivo, como o LED vermelho, o LED verde e os pinos SDA e SCL para a comunicação I2C.
A estrutura de dados DataStruct é criada com um único campo de tipo inteiro chamado value, para armazenar os dados recebidos.
Depois, há uma variável temporizadorUltimaRecepcao que armazena o tempo da última recepção de dados e a variável dataStruct que armazena os dados recebidos. Essas variáveis serão usadas para verificar se houve alguma transmissão de dados e atualizar o display OLED com as informações recebidas.
O objeto display é criado usando a biblioteca Adafruit_SSD1306 para controlar o display OLED. A comunicação I2C é configurada usando os pinos SDA e SCL.
A função OnDataRecv é chamada sempre que os dados são recebidos via ESP-NOW. Nessa função, os dados recebidos são copiados para a estrutura de dados dataStruct, a variável temporizadorUltimaRecepcao é atualizada e o LED verde é ligado e o vermelho é desligado. Em seguida, o display OLED é limpo e atualizado com informações sobre os dados recebidos.
No setup, a comunicação I2C é iniciada usando os pinos SDA e SCL e a comunicação com o display OLED SSD1306 é iniciada. Se a alocação do display falhar, uma mensagem de erro é impressa na porta serial e o dispositivo é reiniciado. Os pinos dos LEDs são configurados como saída e ambos são desligados. Em seguida, mensagens de inicialização são exibidas no display OLED.
No loop, o dispositivo fica em espera para receber dados. Se a transmissão for bem-sucedida, a função OnDataRecv é chamada para atualizar as informações recebidas no display OLED e acender o LED verde. Se não houver nenhuma transmissão nos últimos 2 segundos, o LED verde é desligado e o vermelho é aceso, indicando que não houve comunicação.
A comunicação unidirecional com um escravo recebendo dados de dois mestres é aquela em que um único escravo recebe dados de dois mestres diferentes, sem que haja retorno de dados do escravo para os mestres pelo mesmo canal de comunicação.
Nesse modo de comunicação, o escravo pode receber informações específicas de cada mestre, permitindo que o dispositivo execute ações específicas para cada mestre. Por exemplo, em um sistema de controle de tráfego em uma cidade, um semáforo pode receber dados de dois mestres diferentes: um mestre responsável pelo controle do fluxo de veículos e outro mestre responsável pelo controle de pedestres.
Dessa forma, cada mestre pode enviar comandos específicos para o semáforo, permitindo que o dispositivo ajuste o tempo de sinalização de acordo com as necessidades de cada mestre. Essa comunicação unidirecional com um escravo recebendo dados de dois mestres é útil em situações em que diferentes dispositivos precisam receber informações específicas de diferentes fontes, sem que haja conflito entre os dados recebidos.
/****************************************************************************** Comunicação Unidirecional com dois mestres enviando para um escravo Sketch Mestre 1 Criado em 27 de Fevereiro de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão de bibliotecas #include <LiquidCrystal_I2C.h> // Biblioteca para utilizar um display de LCD com comunicação I2C #include <esp_now.h> // Biblioteca para utilizar o protocolo de comunicação ESP-NOW #include <Wire.h> // Biblioteca para comunicação I2C #include <WiFi.h> // Biblioteca para conectar em redes Wi-Fi // Definição dos pinos utilizados const uint8_t pinledVermelho = 13; // LED vermelho conectado ao pino 13 const uint8_t pinledVerde = 12; // LED verde conectado ao pino 12 const uint8_t pinSda = 14; // Pino SDA para comunicação I2C const uint8_t pinScl = 27; // Pino SCL para comunicação I2C // Estrutura de dados para troca de informações struct DataStruct { int value; // Dado do tipo inteiro }; const uint8_t slaveMacAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Define o endereço MAC do dispositivo escravo. Coloque o endereço MAC de sua placa aqui esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW DataStruct dataToSend; // Declara a estrutura dataToSend // Objeto para utilizar o display de LCD de 16 colunas, 2 linhas e conectado no endereço I2C 0x27 LiquidCrystal_I2C lcd(0x27, 16, 2); // Função para formatar um número inteiro como uma string String formatarNumero(int number) { // Inicializa uma variável temporária como uma string vazia String temp = ""; // Se o número fornecido for menor que 10, adiciona um "0" à string temporária if (number < 10) { temp = "0"; } // Adiciona o número à string temporária temp += number; // Retorna a string formatada return temp; } // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print("Envio OK:"); lcd.print(formatarNumero(dataToSend.value)); // Escreve o valor no display lcd.print(" "); // Limpar a tela para garantir que não haja caracteres antigos // Acionamento dos LEDs para indicar sucesso no envio digitalWrite(pinledVerde, HIGH); digitalWrite(pinledVermelho, LOW); } else { lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print("FALHA no envio "); digitalWrite(pinledVerde, LOW); digitalWrite(pinledVermelho, HIGH); } } void setup() { Wire.begin(pinSda, pinScl); // Inicia a comunicação I2C do display LCD I2C conectado nos pinos informados lcd.init(); // Inicia o display de LCD lcd.backlight(); // Liga a luz de fundo do display LCD // Configura os pinos dos LEDs como saídas e inicialmente desliga ambos pinMode(pinledVerde, OUTPUT); pinMode(pinledVermelho, OUTPUT); digitalWrite(pinledVermelho, LOW); digitalWrite(pinledVerde, LOW); // Mensagem de inicialização no display de LCD lcd.setCursor(0, 0); lcd.print(" Comunicacao "); lcd.setCursor(0, 1); lcd.print(" ESPNOW "); delay(1200); lcd.clear(); // Mensagem do modo de configuração mestre/escravo no display de LCD lcd.setCursor(0, 0); lcd.print("2 Mestres /"); lcd.setCursor(0, 1); lcd.print("1 escravo"); delay(1000); lcd.clear(); // Mensagem informando que este dispositivo é escravo lcd.setCursor(0, 0); lcd.print("Este dispositivo"); lcd.setCursor(0, 1); lcd.print("eh Mestre 1"); delay(1000); lcd.clear(); // Desconecta de alguma conexão WiFi anterior e define o modo estação (STA) WiFi.disconnect(); WiFi.mode(WIFI_STA); // Inicia a biblioteca ESP-NOW e, caso ocorra algum erro, reinicia o dispositivo if (esp_now_init() != ESP_OK) { lcd.setCursor(0, 0); lcd.print("Inicializacao "); lcd.setCursor(0, 1); lcd.print("ESP-NOW com Erro"); digitalWrite(pinledVermelho, HIGH); delay(2500); ESP.restart(); } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados peerInfo.channel = 0; // Define o canal de comunicação como 0 na estrutura peerInfo peerInfo.encrypt = false; // Define a encriptação como desativada na estrutura peerInfo memcpy(peerInfo.peer_addr, slaveMacAddress, 6); // Copia o endereço MAC do escravo para a estrutura peerInfo // Tenta adicionar o escravo à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o escravo, exibe mensagem de falha no display e reinicia o dispositivo lcd.setCursor(0, 0); lcd.print("Falha ao "); lcd.setCursor(0, 1); lcd.print("adicionar peer "); delay(2500); ESP.restart(); } } void loop() { // Função loop() que executa repetidamente enquanto a placa estiver ligada esp_now_send(slaveMacAddress, (uint8_t *)&dataToSend, sizeof(DataStruct)); // Envie os dados para todos os escravos cadastrados. Isso é definido através do primeiro argumento com valor 0 delay(1000); // Espere 1 segundo antes de enviar novamente dataToSend.value++; // Incrementa o valor de dataToSend.value // Se o valor inteiro ultrapassar 10, retorna para 0 if (dataToSend.value > 10) { dataToSend.value = 0; // o valor de dataToSend.value volta à ser Zero } }
Neste código, o dispositivo mestre 1 envia dados para o dispositivo escravo através do endereço MAC definido. Além disso, é exibido no display LCD se houve falha ou sucesso no envio dos dados.
No início do código, as bibliotecas necessárias são incluídas, e os pinos usados são definidos. Há a definição da estrutura DataStruct usada para enviar dados e o endereço MAC do dispositivo escravo que será usado na rede ESPNOW.
O display LCD I2C é inicializado com o endereço e as dimensões necessárias. Há a configuração dos pinos dos LEDs, a mensagem de inicialização do display e, em seguida, uma mensagem indicando o modo de configuração mestre/escravo e qual dispositivo é o mestre 1.
A função “formatarNumero” é utilizada para formatar o valor de um número inteiro como uma string.
A função OnDataSent é definida para ser executada quando os dados são enviados via ESP-NOW. Se o envio for bem-sucedido, o valor é escrito no display e os LEDs verde e vermelho são acionados, indicando que o envio foi bem-sucedido ou não, respectivamente.
No setup, a comunicação I2C é iniciada, o display é inicializado e a luz de fundo é ligada. O restante do código é dedicado à configuração da comunicação ESP-NOW. O peerInfo é preenchido com o endereço MAC do dispositivo escravo e ele é adicionado à lista de dispositivos da rede ESPNOW.
No loop, a cada execução, o programa envia uma estrutura de dados (DataStruct) para o escravo cadastrado na rede, utilizando a função esp_now_send(). A estrutura de dados contém um valor inteiro (dataToSend.value),incrementado em uma unidade a cada execução do loop. Além disso, há um delay de 1 segundo antes de enviar novamente os dados para os escravos. E, se o valor inteiro da estrutura de dados ultrapassar 10, ele é reiniciado para 0, utilizando uma estrutura de controle if.
/****************************************************************************** Comunicação Unidirecional com dois mestres enviando para um escravo Sketch Mestre 2 Criado em 27 de Fevereiro de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão de bibliotecas #include <Adafruit_SSD1306.h> // Biblioteca para utilizar um display de OLED #include <esp_now.h> // Biblioteca para utilizar o protocolo de comunicação ESP-NOW #include <Wire.h> // Biblioteca para comunicação I2C #include <WiFi.h> // Biblioteca para conectar em redes Wi-Fi // Definição dos pinos utilizados const uint8_t pinledVermelho = 12; // LED vermelho conectado ao pino 13 const uint8_t pinledVerde = 14; // LED verde conectado ao pino 12 const uint8_t pinSda = 27; // Pino SDA para comunicação I2C const uint8_t pinScl = 26; // Pino SCL para comunicação I2C // Estrutura de dados para troca de informações struct DataStruct { int value; // Dado do tipo inteiro }; const uint8_t slaveMacAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Define o endereço MAC do dispositivo escravo. Coloque o endereço MAC de sua placa aqui esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW DataStruct dataToSend; // Declara a estrutura dataToSend // Cria uma instância da classe Adafruit_SSD1306 com as dimensões de 128x64 pixels, // utilizando a interface I2C (Wire) e com o endereço padrão do display (-1). Adafruit_SSD1306 display(128, 64, &Wire, -1); // Função para formatar um número inteiro como uma string String formatarNumero(int number) { // Inicializa uma variável temporária como uma string vazia String temp = ""; // Se o número fornecido for menor que 10, adiciona um "0" à string temporária if (number < 10) { temp = "0"; } // Adiciona o número à string temporária temp += number; // Retorna a string formatada return temp; } // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... // Limpa a tela do display OLED e define a cor do texto como branco display.clearDisplay(); display.setTextColor(SSD1306_WHITE); // Atualiza o display de OLED com informações sobre os dados enviados display.clearDisplay(); display.setCursor(0, 0); display.setTextSize(2); display.println("Envio OK "); display.println(); display.print(formatarNumero(dataToSend.value)); display.display(); // Acionamento dos LEDs para indicar sucesso no envio digitalWrite(pinledVerde, HIGH); digitalWrite(pinledVermelho, LOW); } else { // Limpa a tela do display OLED e define a cor do texto como branco display.clearDisplay(); display.setTextColor(SSD1306_WHITE); // Atualiza o display de OLED com informações sobre os dados enviados display.clearDisplay(); display.setCursor(0, 0); display.setTextSize(2); display.println("Insucesso"); display.display(); // Acionamento dos LEDs para indicar sucesso no envio digitalWrite(pinledVerde, LOW); digitalWrite(pinledVermelho, HIGH); } } void setup() { Wire.begin(pinSda, pinScl); // Inicia a comunicação I2C do display OLED I2C conectado nos pinos informados // Inicia a comunicação com o display OLED SSD1306 if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Se a alocação do display falhar, ... Serial.begin(115200); // Inicia a comunicação serial com uma taxa de 115200 bps Serial.println("SSD1306 allocation failed"); // Imprime uma mensagem de erro na porta serial delay(10000); // Espera 10 segundos ESP.restart(); // Reinicia o dispositivo ESP } display.clearDisplay(); // Limpa o display do OLED. // Configura os pinos dos LEDs como saídas e inicialmente desliga ambos pinMode(pinledVerde, OUTPUT); pinMode(pinledVermelho, OUTPUT); digitalWrite(pinledVermelho, LOW); digitalWrite(pinledVerde, LOW); // Mensagem de inicialização no display de OLED display.setCursor(0, 0); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.println(" Comunicacao "); display.println(" ESPNOW "); display.display(); delay(1200); display.clearDisplay(); // Mensagem do modo de configuração mestre/escravo no display de OLED display.setCursor(0, 0); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.println(" 2 Mestre /"); display.println(" 1 escravo "); display.display(); delay(1000); display.clearDisplay(); // Mensagem informando que este dispositivo é escravo display.setCursor(0, 0); display.println(" Este dispositivo"); display.println(" eh Mestre 2"); display.display(); delay(1000); display.clearDisplay(); // Desconecta de alguma conexão WiFi anterior e define o modo estação (STA) WiFi.disconnect(); WiFi.mode(WIFI_STA); // Inicia a biblioteca ESP-NOW e, caso ocorra algum erro, reinicia o dispositivo if (esp_now_init() != ESP_OK) { display.clearDisplay(); display.setCursor(0, 0); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.println(" Inicializacao"); display.println(" ESP-NOW com Erro"); digitalWrite(pinledVermelho, HIGH); display.display(); delay(2500); ESP.restart(); } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados peerInfo.channel = 0; // Define o canal de comunicação como 0 na estrutura peerInfo peerInfo.encrypt = false; // Define a encriptação como desativada na estrutura peerInfo memcpy(peerInfo.peer_addr, slaveMacAddress, 6); // Copia o endereço MAC do escravo para a estrutura peerInfo // Tenta adicionar o escravo à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o escravo, exibe mensagem de falha no display e reinicia o dispositivo display.clearDisplay(); display.setCursor(0, 0); display.println("Falha ao "); display.println("adicionar peer "); display.display(); delay(2500); ESP.restart(); } } void loop() { // Função loop() que executa repetidamente enquanto a placa estiver ligada esp_now_send(slaveMacAddress, (uint8_t *)&dataToSend, sizeof(DataStruct)); // Envie os dados para todos os escravos cadastrados. Isso é definido através do primeiro argumento com valor 0 delay(1000); // Espere 1 segundo antes de enviar novamente dataToSend.value--; // Decrementa o valor de dataToSend.value // Se o valor inteiro ultrapassar 10, retorna para 0 if (dataToSend.value < 0) { dataToSend.value = 10; // o valor de dataToSend.value volta à ser Dez } }
Neste código, o dispositivo mestre 2 envia dados para o dispositivo escravo através do endereço MAC definido. Além disso, é exibido no display OLED se houve falha ou sucesso no envio dos dados.
A primeira parte do código é a inclusão de bibliotecas, como a biblioteca para utilizar um display de OLED, a biblioteca para utilizar o protocolo de comunicação ESP-NOW, a biblioteca para comunicação I2C e a biblioteca para conectar em redes Wi-Fi.
Em seguida, são definidos os pinos utilizados no sketch, o LED vermelho, conectado ao pino 13; o LED verde, conectado ao pino 12 e os pinos SDA (27) e SCL (26), para comunicação I2C.
A estrutura DataStruct é definida para a troca de informações, contendo apenas um campo do tipo inteiro. O endereço MAC do dispositivo escravo é definido em slaveMacAddress. É criada uma estrutura esp_now_peer_info_t, utilizada para registrar informações sobre o dispositivo peer que será adicionado à rede ESP-NOW. A instância da classe Adafruit_SSD1306 também é criada para controlar o display OLED conectado.
A função formatarNumero é definida para formatar um número inteiro como uma string. Já OnDataSent é chamada quando dados são recebidos via ESP-NOW. Ele atualiza o display OLED e aciona os LEDs conforme o status do envio.
Na função setup(), a comunicação I2C é iniciada com o display OLED, o display é inicializado e os pinos dos LEDs são configurados como saídas. Em seguida, é adicionado o dispositivo peer à rede ESP-NOW, com seu endereço MAC definido anteriormente. A função esp_now_register_send_cb é chamada para registrar a função OnDataSent como callback que será executada quando os dados forem enviados via ESP-NOW.
No loop, a cada execução, o programa envia uma estrutura de dados (DataStruct) para o escravo cadastrado na rede, utilizando a função esp_now_send(). A estrutura de dados contém um valor inteiro (dataToSend.value), que é decrementado em uma unidade a cada execução do loop. Além disso, há um delay de 1 segundo antes de enviar novamente os dados para os escravos. E, se o valor inteiro da estrutura de dados ficar menor que 0, ele é reiniciado para 10, utilizando uma estrutura de controle if.
/****************************************************************************** Comunicação Unidirecional com dois mestres enviando para um escravo Sketch Escravo Criado em 28 de Fevereiro de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ #include <LiquidCrystal_I2C.h> // Inclui a biblioteca LiquidCrystal_I2C para controle do display LCD I2C #include <esp_now.h> // Inclui a biblioteca esp_now para o uso do protocolo de comunicação ESP-NOW #include <Wire.h> // Inclui a biblioteca Wire para a comunicação I2C #include <WiFi.h> // Inclui a biblioteca WiFi para configuração da rede sem fio const uint8_t pinSda = 14; // Define o pino SDA como 14 para a comunicação I2C const uint8_t pinScl = 27; // Define o pino SCL como 27 para a comunicação I2C struct DataStruct { // Define a estrutura DataStruct para troca de informações int value; // Variável inteira value }; const uint8_t macAddrMestre1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Endereço MAC do dispositivo mestre 1. Coloque o endereço MAC de sua placa aqui const uint8_t macAddrMestre2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Endereço MAC do dispositivo mestre 2. Coloque o endereço MAC de sua placa aqui esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW DataStruct dadosMestre1; // Declara a estrutura dadosMestre1 DataStruct dadosMestre2; // Declara a estrutura dadosMestre2 LiquidCrystal_I2C lcd(0x27, 16, 2); // Declara o objeto lcd para controle do display LCD I2C // Função para formatar um número inteiro como uma string String formatarNumero(int number) { // Inicializa uma variável temporária como uma string vazia String temp = ""; // Se o número fornecido for menor que 10, adiciona um "0" à string temporária if (number < 10) { temp = "0"; } // Adiciona o número à string temporária temp += number; // Retorna a string formatada return temp; } void OnDataRecv(const uint8_t *mac_addr, const uint8_t *incomingData, int len) { // Função chamada quando os dados são recebidos if (memcmp(mac_addr, macAddrMestre1, 6) == 0) { memcpy(&dadosMestre1, incomingData, sizeof(DataStruct)); // Copia os dados recebidos para a estrutura de dados lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print("mestre1: "); lcd.print(formatarNumero(dadosMestre1.value)); // Escreve o valor no display lcd.print(" "); // Limpar a tela para garantir que não haja caracteres antigos } else if (memcmp(mac_addr, macAddrMestre2, 6) == 0) { memcpy(&dadosMestre2, incomingData, sizeof(DataStruct)); // Copia os dados recebidos para a estrutura de dados lcd.setCursor(0, 1); lcd.print("mestre2: "); lcd.print(formatarNumero(dadosMestre2.value)); // Escreve o valor no display lcd.print(" "); // Limpar a tela para garantir que não haja caracteres antigos } } void setup() { Wire.begin(pinSda, pinScl); // Inicia a comunicação I2C com os pinos SDA e SCL definidos anteriormente lcd.init(); // Inicializa o display lcd.backlight(); // Acende o backlight do display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print(" Comunicacao "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print(" ESPNOW "); // Escreve a mensagem no display delay(1200); // Espera por 1,2 segundos lcd.clear(); // Limpa o display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("2 Mestres /"); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("1 escravo"); // Escreve a mensagem no display delay(1000); // Espera por 1 segundo lcd.clear(); // Limpa o display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Este dispositivo"); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("eh Escravo"); // Escreve a mensagem no display delay(1000); // Espera por 1 segundo lcd.clear(); // Limpa o display WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_STA); // Define o modo WiFi como Station (cliente) Serial.begin(115200); Serial.println(WiFi.macAddress()); if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Inicializacao "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("ESP-NOW com Erro"); // Escreve a mensagem no display delay(2500); // Espera por 2,5 segundos ESP.restart(); // Reinicia o dispositivo } esp_now_register_recv_cb(OnDataRecv); // Registra a função de callback que é chamada quando os dados são recebidos } void loop() { delay(1); // Delay de 1 milissegundo }
Neste código, os dois dispositivos mestres enviam dados para o dispositivo escravo, que os exibe em um display LCD I2C.
A biblioteca LiquidCrystal_I2C é incluída para controlar o display LCD I2C, esp_now.h é incluído para o uso do protocolo de comunicação ESP-NOW e a biblioteca Wire é incluída para a comunicação I2C.
A estrutura DataStruct é definida para troca de informações e composta por uma variável inteira chamada value. São declaradas duas variáveis do tipo DataStruct, dadosMestre1 e dadosMestre2, para armazenar os dados recebidos de cada mestre.
A função OnDataRecv é chamada sempre que dados são recebidos. Ela verifica o endereço MAC do dispositivo que enviou os dados e copia-os para a variável correspondente (dadosMestre1 ou dadosMestre2). Em seguida, a mensagem recebida é exibida no display LCD.
A função setup é executada apenas uma vez no início do código. Ela inicia a comunicação I2C com os pinos SDA e SCL especificados, inicializa o display LCD e acende o backlight. Em seguida, exibe mensagens de inicialização no display.
A função loop não é usada neste código porque a lógica está toda incluída na função OnDataRecv. Portanto, após a inicialização, o dispositivo escravo aguarda os dados serem recebidos pelos mestres e os exibe no display LCD.
A comunicação bidirecional com dois ESP32s é aquela em que dois dispositivos ESP32 podem trocar informações entre si. Isso permite que ambos os dispositivos enviem e recebam dados um do outro.
Nesse modo de comunicação, cada dispositivo pode enviar e receber informações específicas do outro dispositivo. Por exemplo, em um sistema de controle de acesso a um prédio, um ESP32 pode ser responsável por ler cartões de acesso, enquanto o outro pode ser responsável por controlar a abertura das portas.
Dessa forma, cada dispositivo pode enviar comandos específicos para o outro dispositivo e receber informações sobre o estado atual do sistema. Essa comunicação bidirecional entre dois ESP32s é útil em situações em que diferentes dispositivos precisam trocar informações entre si para coordenar suas ações e tomar decisões conjuntas.
/****************************************************************************** Comunicação Bidirecional com dois ESP32s Sketch ESP32 1 Criado em 08 de Março de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ #include <LiquidCrystal_I2C.h> // Inclui a biblioteca LiquidCrystal_I2C para controle do display LCD I2C #include <esp_now.h> // Inclui a biblioteca esp_now para o uso do protocolo de comunicação ESP-NOW #include <Wire.h> // Inclui a biblioteca Wire para a comunicação I2C #include <WiFi.h> // Inclui a biblioteca WiFi para configuração da rede sem fio const uint8_t pinSda = 14; // Define o pino SDA como 14 para a comunicação I2C const uint8_t pinScl = 27; // Define o pino SCL como 27 para a comunicação I2C uint8_t macAddress2Esp32[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Define o endereço MAC do outro ESP32. Coloque o endereço da sua placa aqui struct DataStruct { // Define a estrutura DataStruct para troca de informações int value; // Variável inteira value }; DataStruct dataSend; // Declara a estrutura dataSend DataStruct dataRecv; // Declara a estrutura dataRecv bool recvJaExecutado = false; esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW LiquidCrystal_I2C lcd(0x27, 16, 2); // Declara o objeto lcd para controle do display LCD I2C // Variável para armazenar o tempo da última recepção de dados unsigned long temporizadorUltimaRecepcao = 0; // Função para formatar um número inteiro como uma string String formatarNumero(int number) { // Inicializa uma variável temporária como uma string vazia String temp = ""; // Se o número fornecido for menor que 10, adiciona um "0" à string temporária if (number < 10) { temp = "0"; } // Adiciona o número à string temporária temp += number; // Retorna a string formatada return temp; } void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { // Função chamada quando os dados são enviados if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... dataSend.value++; // Incrementa o valor inteiro // Se o valor inteiro ultrapassar 10, retorna para 0 if (dataSend.value > 10) { dataSend.value = 0; // o valor de dataSend.value volta à ser Zero } } } // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) { memcpy(&dataRecv, incomingData, sizeof(dataRecv)); // Copia os dados recebidos para a estrutura de dados recvJaExecutado = true; // Define a variável recvJaExecutado como verdadeira para indicar que a função OnDataRecv já foi executada alguam vez temporizadorUltimaRecepcao = millis(); // Atualiza a variável de tempo da última recepção } void setup() { Wire.begin(pinSda, pinScl); // Inicia a comunicação I2C com os pinos SDA e SCL definidos anteriormente lcd.init(); // Inicializa o display lcd.backlight(); // Acende o backlight do display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print(" Comunicacao "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print(" ESPNOW "); // Escreve a mensagem no display delay(1200); // Espera por 1,2 segundos lcd.clear(); // Limpa o display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print(" Bidirecional "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("ESP32 <-> ESP32 "); // Escreve a mensagem no display delay(1000); // Espera por 1 segundo lcd.clear(); // Limpa o display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Este dispositivo"); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("eh ESP32 1"); // Escreve a mensagem no display delay(1000); // Espera por 1 segundo lcd.clear(); // Limpa o display WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_STA); // Define o modo WiFi como Station (cliente) if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Inicializacao "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("ESP-NOW com Erro"); // Escreve a mensagem no display delay(2500); // Espera por 2,5 segundos ESP.restart(); // Reinicia o dispositivo } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados esp_now_register_recv_cb(OnDataRecv); // Registra a função de callback que é chamada quando os dados são recebidos memcpy(peerInfo.peer_addr, macAddress2Esp32, 6); // Copia o endereço MAC do outro ESP32 para a estrutura peerInfo peerInfo.channel = 0; // Define o canal de comunicação como 0 na estrutura peerInfo peerInfo.encrypt = false; // Define a encriptação como desativada na estrutura peerInfo // Tenta adicionar o outro ESP32 à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o outro ESP32, exibe mensagem de falha no display e reinicia o dispositivo lcd.setCursor(0, 0); lcd.print("Falha ao"); lcd.setCursor(0, 1); lcd.print("adicionar peer"); delay(2500); ESP.restart(); } // Define o tempo atual como o temporizador da última recepção de dados temporizadorUltimaRecepcao = millis(); } void loop() { esp_now_send(macAddress2Esp32, (uint8_t *)&dataSend, sizeof(DataStruct)); // Envie os dados para o endereço MAC do outro ESP32 // Verifica se o tempo desde a última recepção é maior que 2 segundos if (millis() - temporizadorUltimaRecepcao > 2000) { // Mostra mensagem de tempo limite ultrapassado no display de LCD lcd.setCursor(0, 1); lcd.print("Falha recepcao "); } else { if (recvJaExecutado) { // se a função já foi executada alguam vez, ... // Atualiza o display de LCD com informações sobre os dados recebidos lcd.setCursor(0, 1); lcd.print("Recebido:"); lcd.print(formatarNumero(dataRecv.value)); lcd.print(" "); } else { // se não, não mostra nada (na primeira vez de execução do programa) lcd.setCursor(0, 1); lcd.print(" "); } } static int valuePrevious; // variável estática que armazena o último valor de dataSend.value if (dataSend.value == valuePrevious) { // se o valor atual de dataSend.value for igual à valuePrevious, mostra falha no envio lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print("Falha no Envio "); // Escreve a mensagem de falha no display } else { lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print("Enviado :"); // Escreve a mensagem no display lcd.print(formatarNumero(dataSend.value)); // exibe no dispaly lcd o conteúdo da variável dataSend.value lcd.print(" "); valuePrevious = dataSend.value; // atualiza o valor de valuePrevious com o último valor de dataSend.value } delay(1000); // Espera 1 segundo }
Neste sketch, o ESP32 envia dados e também recebe dados do ESP32 2.
O código começa importando as bibliotecas requeridas para o uso do ESP-NOW e do display LCD I2C. Também configura o pino SDA e SCL para a conexão I2C e o endereço MAC do outro ESP32.
A estrutura DataStruct é criada para guardar as informações que serão compartilhadas entre os dispositivos. A variável bool recvJaExecutado é usada para checar se a função OnDataRecv já rodou alguma vez.
A função OnDataSent é executada quando os dados são transmitidos, aumentando o valor de dataSend. Se o valor passar de 10, ele volta para zero.
A função OnDataRecv é executada quando dados são obtidos via ESP-NOW. Ela copia os dados obtidos para a estrutura de dados e coloca a variável recvJaExecutado como verdadeira. Além disso, atualiza a variável de tempo da última obtenção.
Na função setup, a conexão I2C é iniciada e o display LCD é configurado e limpo. Também mostra algumas mensagens na tela.
Na função loop, é enviado a variável dataSend para o outro ESP32 usando o endereço MAC dele e a função esp_now_send. A variável dataSend é uma estrutura de dados que contém um valor numérico. Também é verificado se já se passaram mais de 2 segundos desde a última vez que recebeu dados do outro ESP32. Se sim, mostra uma mensagem de “Falha recepção” no display LCD. Se não, verifica se a função OnDataRecv já foi executada alguma vez (indicando que recebeu dados). Então, se sim, mostra o valor recebido na variável dataRecv no display LCD. Se não, mostra uma linha em branco no display LCD.
Também no loop, é criado uma variável estática chamada valuePrevious, que guarda o último valor enviado na variável dataSend, para comparar se esse valor é o mesmo valor atual de dataSend. Se forem iguais, mostra uma mensagem de “Falha no Envio” no display LCD. Se forem diferentes, mostra o valor atual de dataSend no display LCD e atualiza o valor de valuePrevious com ele. Então, através da função delay, é feita uma espera de 1 segundo antes de repetir o loop.
/****************************************************************************** Comunicação Bidirecional com dois ESP32s Sketch ESP32 2 Criado em 08 de Março de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ #include <LiquidCrystal_I2C.h> // Inclui a biblioteca LiquidCrystal_I2C para controle do display LCD I2C #include <esp_now.h> // Inclui a biblioteca esp_now para o uso do protocolo de comunicação ESP-NOW #include <Wire.h> // Inclui a biblioteca Wire para a comunicação I2C #include <WiFi.h> // Inclui a biblioteca WiFi para configuração da rede sem fio const uint8_t pinSda = 14; // Define o pino SDA como 14 para a comunicação I2C const uint8_t pinScl = 27; // Define o pino SCL como 27 para a comunicação I2C uint8_t macAddress1Esp32[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Define o endereço MAC do outro ESP32. Insira aqui o endereço MAC de sua placa struct DataStruct { // Define a estrutura DataStruct para troca de informações int value; // Variável inteira value }; DataStruct dataSend; // Declara a estrutura dataSend DataStruct dataRecv; // Declara a estrutura dataRecv bool recvJaExecutado = false; esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW LiquidCrystal_I2C lcd(0x27, 16, 2); // Declara o objeto lcd para controle do display LCD I2C // Variável para armazenar o tempo da última recepção de dados unsigned long temporizadorUltimaRecepcao = 0; // Função para formatar um número inteiro como uma string String formatarNumero(int number) { // Inicializa uma variável temporária como uma string vazia String temp = ""; // Se o número fornecido for menor que 10, adiciona um "0" à string temporária if (number < 10) { temp = "0"; } // Adiciona o número à string temporária temp += number; // Retorna a string formatada return temp; } void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { // Função chamada quando os dados são enviados if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... dataSend.value--; // Decrementa o valor inteiro // Se o valor inteiro for menor que 0, retorna para 10 if (dataSend.value < 0) { dataSend.value = 10; // o valor de dataSend.value volta à ser Dez } } } // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) { memcpy(&dataRecv, incomingData, sizeof(dataRecv)); // Copia os dados recebidos para a estrutura de dados recvJaExecutado = true; // Define a variável recvJaExecutado como verdadeira para indicar que a função OnDataRecv já foi executada alguam vez temporizadorUltimaRecepcao = millis(); // Atualiza a variável de tempo da última recepção } void setup() { Wire.begin(pinSda, pinScl); // Inicia a comunicação I2C com os pinos SDA e SCL definidos anteriormente lcd.init(); // Inicializa o display lcd.backlight(); // Acende o backlight do display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print(" Comunicacao "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print(" ESPNOW "); // Escreve a mensagem no display delay(1200); // Espera por 1,2 segundos lcd.clear(); // Limpa o display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print(" Bidirecional "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("ESP32 <-> ESP32 "); // Escreve a mensagem no display delay(1000); // Espera por 1 segundo lcd.clear(); // Limpa o display lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Este dispositivo"); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("eh ESP32 2"); // Escreve a mensagem no display delay(1000); // Espera por 1 segundo lcd.clear(); // Limpa o display WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_STA); // Define o modo WiFi como Station (cliente) if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros lcd.setCursor(0, 0); // Define a posição do cursor do display para a primeira linha e primeira coluna lcd.print("Inicializacao "); // Escreve a mensagem no display lcd.setCursor(0, 1); // Define a posição do cursor do display para a segunda linha e primeira coluna lcd.print("ESP-NOW com Erro"); // Escreve a mensagem no display delay(2500); // Espera por 2,5 segundos ESP.restart(); // Reinicia o dispositivo } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados esp_now_register_recv_cb(OnDataRecv); // Registra a função de callback que é chamada quando os dados são recebidos memcpy(peerInfo.peer_addr, macAddress1Esp32, 6); // Copia o endereço MAC do outro ESP32 para a estrutura peerInfo peerInfo.channel = 0; // Define o canal de comunicação como 0 na estrutura peerInfo peerInfo.encrypt = false; // Define a encriptação como desativada na estrutura peerInfo // Tenta adicionar o outro ESP32 à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o outro ESP32, exibe mensagem de falha no display e reinicia o dispositivo lcd.setCursor(0, 0); lcd.print("Falha ao"); lcd.setCursor(0, 1); lcd.print("adicionar peer"); delay(2500); ESP.restart(); } // Define o tempo atual como o temporizador da última recepção de dados temporizadorUltimaRecepcao = millis(); Serial.begin(115200); } void loop() { esp_now_send(macAddress1Esp32, (uint8_t *)&dataSend, sizeof(DataStruct)); // Envie os dados para o endereço MAC do outro ESP32 // Verifica se o tempo desde a última recepção é maior que 2 segundos if (millis() - temporizadorUltimaRecepcao > 2000) { // Mostra mensagem de tempo limite ultrapassado no display de LCD Serial.println(dataRecv.value); lcd.setCursor(0, 1); lcd.print("Falha recepcao "); } else { if (recvJaExecutado) { // se a função já foi executada alguam vez, ... // Atualiza o display de LCD com informações sobre os dados recebidos lcd.setCursor(0, 1); lcd.print("Recebido:"); lcd.print(formatarNumero(dataRecv.value)); lcd.print(" "); } else { // se não, não mostra nada (na primeira vez de execução do programa) lcd.setCursor(0, 1); lcd.print(" "); } } static int valuePrevious; // variável estática que armazena o último valor de dataSend.value if (dataSend.value == valuePrevious) { // se o valor atual de dataSend.value for igual à valuePrevious, mostra falha no envio lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print("Falha no Envio "); // Escreve a mensagem de falha no display } else { lcd.setCursor(0, 0); // Define a posição do cursor do display lcd.print("Enviado :"); // Escreve a mensagem no display lcd.print(formatarNumero(dataSend.value)); // exibe no dispaly lcd o conteúdo da variável dataSend.value lcd.print(" "); valuePrevious = dataSend.value; // atualiza o valor de valuePrevious com o último valor de dataSend.value } delay(1000); // Espera 1 segundo }
Este sketch faz o mesmo processo que o sketch do ESP32 1. A única diferença é de que em vez de incrementar a variável dataSend.value, é feito o decremento da variável dataSend.value (linha 53) e também é feita a verificação de que de se o valor inteiro for menor que 0, ele deve retornar para 10 (linhas 56 a 58).
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { // Função chamada quando os dados são enviados if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... dataSend.value--; // Decrementa o valor inteiro // Se o valor inteiro for menor que 0, retorna para 10 if (dataSend.value < 0) { dataSend.value = 10; // o valor de dataSend.value volta à ser Dez } } }
A comunicação multidirecional com ESP32s é aquela em que vários dispositivos ESP32 podem trocar informações entre si. Isso permite que cada dispositivo envie e receba dados de todos os outros dispositivos na rede. Nesse modo de comunicação, cada dispositivo pode enviar e receber informações específicas dos outros dispositivos.
Por exemplo, em um sistema de monitoramento ambiental, vários ESP32s podem ser responsáveis por medir diferentes parâmetros como temperatura, umidade e pressão atmosférica em diferentes locais. Dessa forma, cada dispositivo pode enviar seus dados para os outros dispositivos e receber informações sobre o estado atual do ambiente. Essa comunicação multidirecional entre ESP32s é útil em situações em que diferentes dispositivos precisam trocar informações entre si para compartilhar dados e conhecimento.
Neste exemplo, será utilizado três ESP32s para demonstrar a comunicação multidirecional, mas pode ser utilizado mais, de acordo com o limite de dispositivos em uma rede esp-now.
Para realizar a comunicação multidirecional com ESP32s, é preciso configurar cada dispositivo como um par (peer) da rede esp-now e definir os endereços MAC dos outros dispositivos que farão parte da rede. Além disso, é preciso definir as funções de envio e recebimento dos dados para cada dispositivo e criar as estruturas de dados que serão transmitidas pela rede. Por fim, é preciso definir as rotinas de callback que serão executadas quando os dados forem enviados ou recebidos pelos dispositivos.
/****************************************************************************** Comunicação Multidirecional com três ESP32s Sketch ESP32 1 Criado em 08 de Março de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ #include <esp_now.h> // Inclui a biblioteca esp_now para o uso do protocolo de comunicação ESP-NOW #include <WiFi.h> // Inclui a biblioteca WiFi para configuração da rede sem fio uint8_t macAddress2Esp32[] = { 0x10, 0x52, 0x1C, 0x68, 0x90, 0xFC }; // Define o endereço MAC do segundo ESP32. Coloque o endereço da sua placa aqui uint8_t macAddress3Esp32[] = { 0xA4, 0xE5, 0x7C, 0x46, 0xFF, 0x1C }; // Define o endereço MAC do terceiro ESP32. Coloque o endereço da sua placa aqui struct DataStruct { // Define a estrutura DataStruct para troca de informações int value; // Variável inteira value }; DataStruct dataRecv2; // Declara a estrutura dataRecv2 para armazenar dados recebidos da placa 2 DataStruct dataRecv3; // Declara a estrutura dataRecv3 para armazenar dados recebidos da placa 3 DataStruct dataSend2; // Declara a estrutura dataSend2 para armazenar dados enviados da placa 2 DataStruct dataSend3; // Declara a estrutura dataSend3 para armazenar dados enviados da placa 3 esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW // Função para formatar um número inteiro como uma string String formatarNumero(int number) { // Inicializa uma variável temporária como uma string vazia String temp = ""; // Se o número fornecido for menor que 10, adiciona um "0" à string temporária if (number < 10) { temp = "0"; } // Adiciona o número à string temporária temp += number; // Retorna a string formatada return temp; } void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { // Função chamada quando os dados são enviados if (memcmp(mac_addr, macAddress2Esp32, 6) == 0) // É comparado os primeiros 6 bytes do endereço MAC recebido com os primeiros // 6 bytes do endereço MAC ESP32 2 usando a função, e se os endereços MAC forem iguais, // o valor retornado será zero e o bloco de código dentro do if será executado. { Serial.print("╠» Enviado de 1 para 2 : "); if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... Serial.println(formatarNumero(dataSend2.value)); // Converte o valor de dataSend2.value para uma string formatada e envia para a porta serial dataSend2.value++; // Incrementa o valor de dataSend2.value para o próximo envio // Verifica se o valor de dataSend2.value ultrapassou o limite de 10 e, caso sim, retorna para 0 if (dataSend2.value > 10) { dataSend2.value = 0; } } else { Serial.println("??"); // Não foi enviado com sucesso } } else if (memcmp(mac_addr, macAddress3Esp32, 6) == 0) // se o endereço MAC recebido for igual ao endereço MAC do ESP32 3, ... { Serial.print("╠» Enviado de 1 para 3 : "); if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... Serial.println(formatarNumero(dataSend3.value)); // Converte o valor de dataSend3.value para uma string formatada e envia para a porta serial dataSend3.value--; // Decrementa o valor de dataSend3.value para o próximo envio // Verifica se o valor de dataSend3.value é menor do que 0 e, caso sim, retorna para 10 if (dataSend3.value < 0) { dataSend3.value = 10; } } else { Serial.println("??"); // Não foi enviado com sucesso } } } // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) { if (memcmp(mac, macAddress2Esp32, 6) == 0) { memcpy(&dataRecv2, incomingData, sizeof(dataRecv2)); // Copia os dados recebidos para a estrutura de dados dataRecv2 // Mostra na Serial o dado recebido Serial.print("«╣ Recebido de 2 para 1: "); Serial.println(formatarNumero(dataRecv2.value)); } else if (memcmp(mac, macAddress3Esp32, 6) == 0) { memcpy(&dataRecv3, incomingData, sizeof(dataRecv3)); // Copia os dados recebidos para a estrutura de dados dataRecv3 // Mostra na Serial o dado recebido Serial.print("«╣ Recebido de 3 para 1: "); Serial.println(formatarNumero(dataRecv3.value)); } } void setup() { delay(2000); Serial.begin(115200); // Inicia a comunicação serial com uma taxa de 115200 bits por segundo Serial.println("\n\nComunicação ESP-NOW multidirecional:\n\n ╔» ESP32 1 «═══» ESP32 2 «╗\n ╚═══════» ESP32 3 «═══════╝\n\n Este dispositivo é o ESP32 1\n\n"); // Imprime uma mensagem na porta serial para indicar o início da comunicação ESP-NOW multidirecional WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_STA); // Define o modo WiFi como Station (cliente) if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros Serial.println("Inicialização ESP-NOW com Erro. O dispositivo reiniciará em "); // Se houver um erro, imprime uma mensagem na porta serial // Loop de contagem regressiva para reiniciar o dispositivo for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } // Imprime uma mensagem de reinicialização e reinicia o dispositivo Serial.println("Reiniciando ESP32"); ESP.restart(); } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados esp_now_register_recv_cb(OnDataRecv); // Registra a função de callback que é chamada quando os dados são recebidos memcpy(peerInfo.peer_addr, macAddress2Esp32, 6); // Copia o endereço MAC do ESP32 2 para a estrutura peerInfo peerInfo.channel = 0; // Define o canal de comunicação como 0 na estrutura peerInfo peerInfo.encrypt = false; // Define a encriptação como desativada na estrutura peerInfo // Tenta adicionar o ESP32 2 à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o ESP32 2, exibe mensagem de falha no display e reinicia o dispositivo Serial.println("Falha ao adicionar peer 2. O dispositivo reiniciará em "); for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } Serial.println("Reiniciando ESP32"); ESP.restart(); } memcpy(peerInfo.peer_addr, macAddress3Esp32, 6); // Copia o endereço MAC do ESP32 3 para a estrutura peerInfo // Tenta adicionar o ESP32 3 à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o ESP32 3, exibe mensagem de falha no display e reinicia o dispositivo Serial.println("Falha ao adicionar peer 3. O dispositivo reiniciará em "); for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } Serial.println("Reiniciando ESP32"); ESP.restart(); } // Inicializa as variáveis com os valores 0 dataSend3.value = 0; dataSend2.value = 0; } void loop() { esp_now_send(macAddress2Esp32, (uint8_t *)&dataSend2, sizeof(DataStruct)); // Envie os dados para o endereço MAC do segundo ESP32 esp_now_send(macAddress3Esp32, (uint8_t *)&dataSend3, sizeof(DataStruct)); // Envie os dados para o endereço MAC do terceiro ESP32 delay(1000); // Espera 1 segundo }
Este sketch é o ESP32 1 do projeto de comunicação multidirecional com três ESP32s. Ele utiliza a biblioteca ESP-NOW para realizar a comunicação sem fio entre os dispositivos. As bibliotecas utilizadas neste sketch são a esp_now.h e a WiFi.h. A esp_now.h é uma biblioteca específica para o protocolo de comunicação ESP-NOW, enquanto a WiFi.h é utilizada para configurar a rede sem fio e é necessária para o funcionamento da esp_now.h.
Na função onDataSent, é definido o comportamento que deve ocorrer quando um pacote de dados é enviado para os outros ESP32s. Nessa função, são comparados os endereços MAC dos dispositivos que enviaram uma resposta. Se o endereço MAC for o do ESP32 2, o valor de dataSend2.value é enviado pela porta serial e o valor é incrementado em 1. Se o endereço MAC for o do ESP32 3, o valor de dataSend3.value é enviado pela porta serial e o valor é decrementado em 1.
Na função onDataRecv, define-se o comportamento que deve ocorrer quando um pacote de dados é recebido. Nessa função, são definidos os valores das estruturas de dados dataRecv2 e dataRecv3 que armazenam as informações recebidas do ESP32 2 e do ESP32 3, respectivamente. Quando um pacote é recebido, o valor é armazenado em dataRecv2 ou dataRecv3, dependendo do endereço MAC do dispositivo de origem.
Na função setup, é definido a taxa de transmissão de dados serial, inicializado o monitor serial, iniciada a conexão WiFi e registrado os dispositivos ESP-NOW. Além disso, nesta função, também são configurados os endereços MAC dos outros dois dispositivos da rede ESP-NOW (ESP32 2 e ESP32 3) e definidas as estruturas de dados para troca de informações entre as placas.
Na função loop, enviam-se os valores de dataSend2 e dataSend3 para os ESP32s 2 e 3, respectivamente, a cada 1 segundo, usando a função esp_now_send passando como argumento o endereço MAC do requerido ESP32 a enviar a mensagem ESP-NOW.
/****************************************************************************** Comunicação Multidirecional com três ESP32s Sketch ESP32 2 Criado em 08 de Março de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ #include <esp_now.h> // Inclui a biblioteca esp_now para o uso do protocolo de comunicação ESP-NOW #include <WiFi.h> // Inclui a biblioteca WiFi para configuração da rede sem fio uint8_t macAddress1Esp32[] = { 0x24, 0x62, 0xAB, 0xFE, 0x7B, 0xEC }; // Define o endereço MAC do ESP32 1. Coloque o endereço da sua placa aqui uint8_t macAddress3Esp32[] = { 0xA4, 0xE5, 0x7C, 0x46, 0xFF, 0x1C }; // Define o endereço MAC do ESP32 3. Coloque o endereço da sua placa aqui struct DataStruct { // Define a estrutura DataStruct para troca de informações int value; // Variável inteira value }; DataStruct dataRecv1; // Declara a estrutura dataRecv1 para armazenar dados recebidos da placa 1 DataStruct dataRecv3; // Declara a estrutura dataRecv3 para armazenar dados recebidos da placa 3 DataStruct dataSend1; // Declara a estrutura dataSend1 para armazenar dados enviados da placa 1 DataStruct dataSend3; // Declara a estrutura dataSend3 para armazenar dados enviados da placa 3 esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW // Função para formatar um número inteiro como uma string String formatarNumero(int number) { // Inicializa uma variável temporária como uma string vazia String temp = ""; // Se o número fornecido for menor que 10, adiciona um "0" à string temporária if (number < 10) { temp = "0"; } // Adiciona o número à string temporária temp += number; // Retorna a string formatada return temp; } void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { // Função chamada quando os dados são enviados if (memcmp(mac_addr, macAddress1Esp32, 6) == 0) // Se o MAC for do igual do ESP32 1, ... { Serial.print("╠» Enviado de 2 para 1 : "); if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... Serial.println(formatarNumero(dataSend1.value)); // Converte o valor de dataSend1.value para uma string formatada e envia para a porta serial dataSend1.value++; // Incrementa o valor de dataSend1.value para o próximo envio // Verifica se o valor de dataSend1.value ultrapassou o limite de 10 e, caso sim, retorna para 0 if (dataSend1.value > 10) { dataSend1.value = 0; } } else { Serial.println("??"); // Não foi enviado com sucesso } } else if (memcmp(mac_addr, macAddress3Esp32, 6) == 0) // se o endereço MAC recebido for igual ao endereço MAC do ESP32 3, ... { Serial.print("╠» Enviado de 2 para 3 : "); if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... Serial.println(formatarNumero(dataSend3.value)); // Converte o valor de dataSend3.value para uma string formatada e envia para a porta serial dataSend3.value--; // Decrementa o valor de dataSend3.value para o próximo envio // Verifica se o valor de dataSend3.value é menor do que 0 e, caso sim, retorna para 10 if (dataSend3.value < 0) { dataSend3.value = 10; } } else { Serial.println("??"); // Não foi enviado com sucesso } } } // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) { if (memcmp(mac, macAddress1Esp32, 6) == 0) { memcpy(&dataRecv1, incomingData, sizeof(dataRecv1)); // Copia os dados recebidos para a estrutura de dados dataRecv1 // Mostra na Serial o dado recebido Serial.print("«╣ Recebido de 1 para 2: "); Serial.println(formatarNumero(dataRecv1.value)); } else if (memcmp(mac, macAddress3Esp32, 6) == 0) { memcpy(&dataRecv3, incomingData, sizeof(dataRecv3)); // Copia os dados recebidos para a estrutura de dados dataRecv3 // Mostra na Serial o dado recebido Serial.print("«╣ Recebido de 3 para 2: "); Serial.println(formatarNumero(dataRecv3.value)); } } void setup() { delay(2000); Serial.begin(115200); // Inicia a comunicação serial com uma taxa de 115200 bits por segundo Serial.println("\n\nComunicação ESP-NOW multidirecional:\n\n ╔» ESP32 1 «═══» ESP32 2 «╗\n ╚═══════» ESP32 3 «═══════╝\n\n Este dispositivo é o ESP32 2\n\n"); // Imprime uma mensagem na porta serial para indicar o início da comunicação ESP-NOW multidirecional WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_STA); // Define o modo WiFi como Station (cliente) if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros Serial.println("Inicialização ESP-NOW com Erro. O dispositivo reiniciará em "); // Se houver um erro, imprime uma mensagem de reinicialização na porta serial e após alguns instantes, reinicia o dispositivo for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } Serial.println("Reiniciando ESP32"); ESP.restart(); // Reinicia o dispositivo } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados esp_now_register_recv_cb(OnDataRecv); // Registra a função de callback que é chamada quando os dados são recebidos memcpy(peerInfo.peer_addr, macAddress1Esp32, 6); // Copia o endereço MAC do ESP32 1 para a estrutura peerInfo peerInfo.channel = 0; // Define o canal de comunicação como 0 na estrutura peerInfo peerInfo.encrypt = false; // Define a encriptação como desativada na estrutura peerInfo // Tenta adicionar o ESP32 1 à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o ESP32 1, exibe mensagem de falha na Serial e reinicia o dispositivo Serial.println("Falha ao adicionar peer 1. O dispositivo reiniciará em "); for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } Serial.println("Reiniciando ESP32"); ESP.restart(); } memcpy(peerInfo.peer_addr, macAddress3Esp32, 6); // Copia o endereço MAC do ESP32 3 para a estrutura peerInfo // Tenta adicionar o ESP32 3 à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o ESP32 3, exibe mensagem de falha no display e reinicia o dispositivo Serial.println("Falha ao adicionar peer 3. O dispositivo reiniciará em "); for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } Serial.println("Reiniciando ESP32"); ESP.restart(); } // Inicializa as variáveis com os valores 0 dataSend3.value = 0; dataSend1.value = 0; } void loop() { esp_now_send(macAddress1Esp32, (uint8_t *)&dataSend1, sizeof(DataStruct)); // Envie os dados para o endereço MAC do primeiro ESP32 esp_now_send(macAddress3Esp32, (uint8_t *)&dataSend3, sizeof(DataStruct)); // Envie os dados para o endereço MAC do terceiro ESP32 delay(1000); // Espera 1 segundo }
Este sketch faz o mesmo que o ESP32 1, com a diferença que o envio e recepção de dados ocorrem entre os ESP32s 1 e 3.
/****************************************************************************** Comunicação Multidirecional com três ESP32s Sketch ESP32 3 Criado em 08 de Março de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ #include <esp_now.h> // Inclui a biblioteca esp_now para o uso do protocolo de comunicação ESP-NOW #include <WiFi.h> // Inclui a biblioteca WiFi para configuração da rede sem fio uint8_t macAddress1Esp32[] = { 0x24, 0x62, 0xAB, 0xFE, 0x7B, 0xEC }; // Define o endereço MAC do ESP32 1. Coloque o endereço da sua placa aqui uint8_t macAddress2Esp32[] = { 0x10, 0x52, 0x1C, 0x68, 0x90, 0xFC }; // Define o endereço MAC do ESP32 2. Coloque o endereço da sua placa aqui struct DataStruct { // Define a estrutura DataStruct para troca de informações int value; // Variável inteira value }; DataStruct dataRecv1; // Declara a estrutura dataRecv1 para armazenar dados recebidos da placa 1 DataStruct dataRecv2; // Declara a estrutura dataRecv2 para armazenar dados recebidos da placa 2 DataStruct dataSend1; // Declara a estrutura dataSend1 para armazenar dados enviados da placa 1 DataStruct dataSend2; // Declara a estrutura dataSend2 para armazenar dados enviados da placa 2 esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW // Função para formatar um número inteiro como uma string String formatarNumero(int number) { // Inicializa uma variável temporária como uma string vazia String temp = ""; // Se o número fornecido for menor que 10, adiciona um "0" à string temporária if (number < 10) { temp = "0"; } // Adiciona o número à string temporária temp += number; // Retorna a string formatada return temp; } void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { // Função chamada quando os dados são enviados if (memcmp(mac_addr, macAddress1Esp32, 6) == 0) /// Se o MAC for do igual do ESP32 1, ... { Serial.print("╠» Enviado de 3 para 1 : "); if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... Serial.println(formatarNumero(dataSend1.value)); // Converte o valor de dataSend1.value para uma string formatada e envia para a porta serial dataSend1.value++; // Incrementa o valor de dataSend1.value para o próximo envio // Verifica se o valor de dataSend1.value ultrapassou o limite de 10 e, caso sim, retorna para 0 if (dataSend1.value > 10) { dataSend1.value = 0; } } else { Serial.println("??"); // Não foi enviado com sucesso } } else if (memcmp(mac_addr, macAddress2Esp32, 6) == 0) // se o endereço MAC recebido for igual ao endereço MAC do ESP32 2, ... { Serial.print("╠» Enviado de 3 para 2 : "); if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... Serial.println(formatarNumero(dataSend2.value)); // Converte o valor de dataSend2.value para uma string formatada e envia para a porta serial dataSend2.value--; // Decrementa o valor de dataSend2.value para o próximo envio // Verifica se o valor de dataSend2.value é menor do que 0 e, caso sim, retorna para 10 if (dataSend2.value < 0) { dataSend2.value = 10; } } else { Serial.println("??"); // Não foi enviado com sucesso } } } // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) { if (memcmp(mac, macAddress1Esp32, 6) == 0) { memcpy(&dataRecv1, incomingData, sizeof(dataRecv1)); // Copia os dados recebidos para a estrutura de dados dataRecv1 // Mostra na Serial o dado recebido Serial.print("«╣ Recebido de 1 para 3: "); Serial.println(formatarNumero(dataRecv1.value)); } else if (memcmp(mac, macAddress2Esp32, 6) == 0) { memcpy(&dataRecv2, incomingData, sizeof(dataRecv2)); // Copia os dados recebidos para a estrutura de dados dataRecv2 // Mostra na Serial o dado recebido Serial.print("«╣ Recebido de 2 para 3: "); Serial.println(formatarNumero(dataRecv2.value)); } } void setup() { delay(2000); Serial.begin(115200); // Inicia a comunicação serial com uma taxa de 115200 bits por segundo Serial.println("\n\nComunicação ESP-NOW multidirecional:\n\n ╔» ESP32 1 «═══» ESP32 2 «╗\n ╚═══════» ESP32 3 «═══════╝\n\n Este dispositivo é o ESP32 3\n\n"); // Imprime uma mensagem na porta serial para indicar o início da comunicação ESP-NOW multidirecional WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_STA); // Define o modo WiFi como Station (cliente) if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros Serial.println("Inicialização ESP-NOW com Erro. O dispositivo reiniciará em "); // Se houver um erro, imprime uma mensagem de reinicialização na porta serial e após alguns instantes, reinicia o dispositivo for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } Serial.println("Reiniciando ESP32"); ESP.restart(); // Reinicia o dispositivo } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados esp_now_register_recv_cb(OnDataRecv); // Registra a função de callback que é chamada quando os dados são recebidos memcpy(peerInfo.peer_addr, macAddress1Esp32, 6); // Copia o endereço MAC do ESP32 1 para a estrutura peerInfo peerInfo.channel = 0; // Define o canal de comunicação como 0 na estrutura peerInfo peerInfo.encrypt = false; // Define a encriptação como desativada na estrutura peerInfo // Tenta adicionar o ESP32 1 à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o ESP32 1, exibe mensagem de falha no display e reinicia o dispositivo Serial.println("Falha ao adicionar peer 1. O dispositivo reiniciará em "); for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } Serial.println("Reiniciando ESP32"); ESP.restart(); } memcpy(peerInfo.peer_addr, macAddress2Esp32, 6); // Copia o endereço MAC do ESP32 2 para a estrutura peerInfo // Tenta adicionar o ESP32 2 à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o ESP32 2, exibe mensagem de falha no display e reinicia o dispositivo Serial.println("Falha ao adicionar peer 2. O dispositivo reiniciará em "); for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } Serial.println("Reiniciando ESP32"); ESP.restart(); } // Inicializa as variáveis com os valores 0 dataSend2.value = 0; dataSend1.value = 0; } void loop() { esp_now_send(macAddress1Esp32, (uint8_t *)&dataSend1, sizeof(DataStruct)); // Envie os dados para o endereço MAC do primeiro ESP32 esp_now_send(macAddress2Esp32, (uint8_t *)&dataSend2, sizeof(DataStruct)); // Envie os dados para o endereço MAC do segundo ESP32 delay(1000); // Espera 1 segundo }
Este sketch faz o mesmo que o ESP32 1, com a diferença que o envio e recepção de dados ocorrem entre os ESP32s 1 e 2.
Para utilizar a comunicação ESP-NOW juntamente com a comunicação WiFi, é necessário seguir algumas recomendações. Utilizar estes dois modos de comunicações juntos permite soluções interessantes para dispositivos que requerem baixo consumo de energia, alta confiabilidade e grande alcance de comunicação, além de acesso à Internet.
As recomendações incluem:
WiFi.mode(WIFI_AP_STA);
).Neste exemplo, haverá um ESP32 mestre que envia comandos ao ESP32 escravo (slave). O ESP32 mestre se conectará à rede WiFi do roteador para verificar se a rede WiFi do roteador tem Internet.
/****************************************************************************** Uso integrado das comunicações ESP-NOW e WiFi Sketch Mestre Criado em 12 de Março de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão de bibliotecas //#include <esp_wifi.h> // Biblioteca para configuração do módulo Wi-Fi do ESP32 #include <esp_now.h> // Biblioteca para utilizar o protocolo de comunicação ESP-NOW #include <WiFi.h> // Biblioteca para conectar em redes Wi-Fi uint8_t slaveMacAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Define o endereço MAC do dispositivo escravo. Coloque o endereço MAC de sua placa aqui // Estrutura de dados para troca de informações struct DataStruct { int value; // Dado do tipo inteiro }; esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW // Declara a estrutura dataToSend DataStruct dataToSend; const char* ssid = "SSID"; // variável para armazenar o nome da rede Wi-Fi. Coloque o ssid da sua rede aqui const char* pass = "PASSWORD"; // variável para armazenar a senha da rede Wi-Fi. Coloque a senha da sua rede aqui void OnDataSent(const uint8_t* mac_addr, esp_now_send_status_t status) { // Função chamada quando os dados são enviados if (memcmp(mac_addr, slaveMacAddress, 6) == 0) { // Verifica se o endereço MAC do dispositivo recebedor é igual ao endereço do escravo if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, escreve a mensagem de sucesso na Serial Monitor Serial.print("ESP-NOW: Envio OK. Valor enviado:"); Serial.println((dataToSend.value)); // Escreve o valor no display } else { // Caso contrário, escreve a mensagem de falha na Serial Monitor Serial.println("ESP-NOW: Falha ao enviar dados para o dispositivo escravo!"); } } } bool haConexaoInternet() { // Função que verifica se há conexão com a internet bool conectado = false; // Variável que armazenará se há conexão com a internet ou não WiFiClient client; // Objeto da classe WiFiClient, que permite se conectar a um servidor na internet if (WiFi.status() == WL_CONNECTED) { // Verifica se o dispositivo está conectado à rede Wi-Fi if (client.connect("1.1.1.1", 80)) { // Tenta se conectar a um servidor externo (Cloudflare DNS, que está em 1.1.1.1 na porta 80) conectado = true; // Se a conexão for bem sucedida, define a variável conectado como verdadeira } client.stop(); // Encerra a conexão com o servidor } return conectado; // Retorna se há conexão com a internet ou não } void setup() { delay(2000); // Atraso de 2 segundos antes de executar o código do Setup Serial.begin(115200); // Inicializa a comunicação serial com uma taxa de transmissão de 115200 bits por segundo Serial.println("\n\nUso integrado das comunicações ESP-NOW e WiFi:\n\n ESP32 Mestre ═══» ESP32 Escravo \n\n Este dispositivo é o ESP32 Mestre\n\n"); // Imprime uma mensagem na porta serial para indicar o início da comunicação de Uso integrado ESP-NOW e WiFi WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_AP_STA); // Define o modo WiFi como Access Point (AP) e Station (cliente) WiFi.begin(ssid, pass); // Conecta ao roteador WiFi com o nome de rede (SSID) e a senha (password) especificados while (WiFi.status() != WL_CONNECTED) { // Aguarda a conexão com o AP WiFi e exibe um ponto no monitor serial a cada segundo até que a conexão seja bem-sucedida delay(1000); Serial.print("."); } Serial.println(); // Inicializa o ESP-NOW e verifica se há erros if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros Serial.println("Inicialização ESP-NOW com Erro. O dispositivo reiniciará em "); // Se houver um erro, imprime uma mensagem na porta serial // Loop de contagem regressiva para reiniciar o dispositivo for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } // Imprime uma mensagem de reinicialização e reinicia o dispositivo Serial.println("Reiniciando ESP32"); ESP.restart(); } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados peerInfo.encrypt = false; // Define a criptografia como desativada para a conexão com o escravo memcpy(peerInfo.peer_addr, slaveMacAddress, 6); // Copia o endereço MAC do escravo para a estrutura peerInfo // Tenta adicionar o escravo à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o escravo, exibe mensagem de falha no display e reinicia o dispositivo Serial.println("Falha ao adicionar peer"); delay(2500); ESP.restart(); } dataToSend.value = 0; // Prepara os dados a serem enviados com o valor igual a zero } void loop() { // Função loop() que executa repetidamente enquanto a placa estiver ligada delay(1000); // Aguarda por 1 segundo antes de executar a próxima iteração do loop esp_now_send(slaveMacAddress, (uint8_t*)&dataToSend, sizeof(DataStruct)); // Envia os dados armazenados em dataToSend para o endereço MAC do escravo por meio do protocolo ESP-NOW dataToSend.value++; // Incrementa o valor de dataToSend.value // Se o valor inteiro ultrapassar 10, retorna para 0 if (dataToSend.value > 10) { dataToSend.value = 0; // o valor de dataToSend.value volta à ser Zero } // Verifica se há conexão com a internet por meio da função haConexaoInternet() bool result = haConexaoInternet(); Serial.print("Há Internet? "); Serial.println(result ? "SIM" : "NÃO"); }
Este é um sketch para uso integrado das comunicações ESP-NOW e WiFi em um dispositivo ESP32. Ele permite que um dispositivo ESP32 opere como mestre e se comunique com um dispositivo escravo via protocolo ESP-NOW. Além disso, a conexão WiFi também é configurada para permitir a verificação de conexão à internet.
As bibliotecas necessárias são incluídas no início do sketch, incluindo a biblioteca esp_now.h para usar o protocolo de comunicação ESP-NOW e a biblioteca WiFi.h para conectar em redes WiFi. O endereço MAC do dispositivo escravo é definido em “slaveMacAddress”.
A estrutura de dados “DataStruct” é declarada para troca de informações entre o mestre e o escravo. Já “esp_now_peer_info_t” é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW. A variável “dataToSend” é declarada como uma instância da estrutura DataStruct.
A função “OnDataSent” é chamada quando os dados são enviados. Ele verifica se o endereço MAC do dispositivo receptor é igual ao endereço do escravo e se o envio foi bem-sucedido.
A função “haConexaoInternet” verifica se há conexão com a internet através de uma tentativa de conexão ao servidor 1.1.1.1 na porta 80 da Cloudflare DNS.
No código “setup”, o dispositivo é inicializado e a conexão WiFi é estabelecida. O ESP-NOW é inicializado e verificado se há erros. Se houver erros, uma mensagem é impressa na porta serial e o dispositivo é reiniciado. Se tudo ocorrer bem, uma mensagem é impressa indicando que o dispositivo é o ESP32 mestre.
No código “loop”, é feita a espera por um segundo usando a função delay para evitar sobrecarregar a rede com muitas mensagens. Em seguida, a função esp_now_send() é usada para enviar os dados armazenados na variável dataToSend para o endereço MAC do dispositivo escravo por meio do protocolo ESP-NOW.
Depois, o valor da variável dataToSend é incrementado em 1 usando o operador ++. Se o valor inteiro da variável dataToSend ultrapassar 10, ele é redefinido para 0.
Por fim, a função haConexaoInternet() é usada para verificar se há conexão com a Internet e o resultado é exibido no monitor serial.
/****************************************************************************** Uso integrado das comunicações ESP-NOW e WiFi Sketch Escravo Criado em 12 de Março de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate | Arduino, Robótica, IoT, Apostilas e Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão de bibliotecas #include <esp_now.h> // Biblioteca para utilizar o protocolo de comunicação ESP-NOW #include <WiFi.h> // Biblioteca para conectar em redes Wi-Fi #include <esp_wifi.h> // Biblioteca para configuração do módulo Wi-Fi do ESP32 // Estrutura de dados para troca de informações struct DataStruct { int value; // Dado do tipo inteiro }; // Variável para armazenar os dados recebidos DataStruct dataStruct; void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) { // Função que é chamada quando dados são recebidos via ESP-NOW memcpy(&dataStruct, incomingData, sizeof(dataStruct)); // Copia os dados recebidos para a estrutura de dados // Mostra na Serial o dado recebido Serial.print("Dado recebido: "); Serial.println(dataStruct.value); } void setup() { delay(2000); // Atraso de 2 segundos antes de executar o código do Setup Serial.begin(115200); // Inicia a comunicação serial com uma taxa de 115200 bits por segundo Serial.println("\n\nUso integrado das comunicações ESP-NOW e WiFi:\n\n ESP32 Mestre ═══» ESP32 Escravo \n\n Este dispositivo é o ESP32 Escravo\n\n"); // Imprime uma mensagem na porta serial para indicar o início da comunicação de Uso integrado ESP-NOW e WiFi WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_STA); // Define o modo WiFi como Station (cliente) int32_t channel = 3; // Define o canal WiFi a ser utilizado // Configura a placa ESP32 para escutar o canal especificado esp_wifi_set_promiscuous(true); esp_wifi_set_channel(3, WIFI_SECOND_CHAN_NONE); esp_wifi_set_promiscuous(false); WiFi.channel(channel); // Configura o canal WiFi if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros Serial.println("Inicialização ESP-NOW com Erro. O dispositivo reiniciará em "); // Se houver um erro, imprime uma mensagem na porta serial // Loop de contagem regressiva para reiniciar o dispositivo for (int i = 3; i > 0; i--) { Serial.print(i); delay(1000); } // Imprime uma mensagem de reinicialização e reinicia o dispositivo Serial.println("Reiniciando ESP32"); ESP.restart(); } esp_now_register_recv_cb(OnDataRecv); // Registra a função de callback que é chamada quando os dados são recebidos } void loop() {}
Este é um sketch para o dispositivo ESP32 Escravo que utiliza comunicação ESP-NOW. Ele começa com a inclusão de três bibliotecas: esp_now.h, WiFi.h e esp_wifi.h. A primeira biblioteca é responsável por permitir o uso do protocolo ESP-NOW, enquanto a segunda e terceira bibliotecas são usadas para conectar a placa ESP32 a redes WiFi e configurar o módulo WiFi.
Em seguida, é definida uma estrutura de dados chamada “DataStruct” que contém apenas um campo do tipo inteiro chamado “value”. Essa estrutura será usada para receber informações do dispositivo mestre.
Depois disso, é definida uma função chamada “OnDataRecv” que é chamada quando dados são recebidos via ESP-NOW. Essa função copia os dados recebidos para a variável “dataStruct” e os imprime na porta serial.
Na função “setup”, é definido um atraso de 2 segundos antes de iniciar a comunicação serial e imprimir uma mensagem para indicar o início da comunicação ESP-NOW. Em seguida, a placa ESP32 é desconectada de qualquer rede WiFi previamente conectada e o modo WiFi é definido como Station (cliente). Um canal WiFi é definido e a placa ESP32 é configurada para escutar o canal especificado. A função “esp_now_init” é chamada para inicializar o protocolo ESP-NOW e, se houver erros, o dispositivo é reiniciado. Por fim, a função “esp_now_register_recv_cb” é chamada para registrar a função “OnDataRecv” como a função de callback que será chamada quando os dados são recebidos.
A função “loop” é deixada em branco, pois não há nenhuma operação que precise ser repetida continuamente.
Testar o alcance da comunicação wireless é essencial para garantir a confiabilidade e a eficiência da transmissão de dados em diferentes ambientes e condições.
Para realizar o teste de alcance ESP-NOW, será necessário configurar duas placas ESP32s para se comunicarem entre si: uma como mestre e outra como escravo.
A placa mestre será responsável por enviar dados para a placa escravo. Caso a placa escravo receba os dados, o LED ONBOARD é acesso. Caso contrário, ele é apagado.
Para medir a distância, foi obtido as coordenadas geográficas da base do ESP32 Mestre (fixo) e também as coordenadas geográficas de até onde o ESP32 escravo (móvel) conseguiu obter os dados do Mestre. Para cálculo da distância foi inserido no Google Earth Pro as coordenadas geográficas e feita a medição em metros entre os pontos. Abaixo está os resultados:
/****************************************************************************** Comunicação Unidirecional com mestre enviando para um escravo Sketch Mestre Criado em 14 Março de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ #include <esp_now.h> // Inclui a biblioteca esp_now para o uso do protocolo de comunicação ESP-NOW #include <WiFi.h> // Inclui a biblioteca WiFi para configuração da rede sem fio uint8_t slaveMacAddress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Define o endereço MAC do dispositivo escravo struct DataStruct { // Define a estrutura DataStruct para troca de informações int integerData; // Variável inteira integerData }; DataStruct dataToSend; esp_now_peer_info_t peerInfo; // Cria uma estrutura esp_now_peer_info_t, que é utilizada para registrar informações sobre um peer (dispositivo) que será adicionado à rede ESPNOW void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { // Função chamada quando os dados são enviados if (status == ESP_NOW_SEND_SUCCESS) { // Se o envio foi bem sucedido, ... Serial.println("Envio OK"); // Mostra sucesso } else { Serial.println("Envio Falho"); // Mostra falha } dataToSend.integerData++; // Incrementa o valor inteiro // Se o valor inteiro ultrapassar 10, retorna para 0 if (dataToSend.integerData > 10) { dataToSend.integerData = 0; // o valor de dataToSend.integerData volta à ser Zero } } void setup() { Serial.begin(115200); Serial.println("Teste de Alcance ESP-NOW"); WiFi.disconnect(); // Desconecta de qualquer rede WiFi previamente conectada WiFi.mode(WIFI_STA); // Define o modo WiFi como Station (cliente) if (esp_now_init() != ESP_OK) { // Inicializa o ESP-NOW e verifica se há erros Serial.println("Inicialização ESP-NOW com Erro. Reiniciando..."); delay(2500); // Espera por 2,5 segundos ESP.restart(); // Reinicia o dispositivo } esp_now_register_send_cb(OnDataSent); // Registra a função de callback que é chamada quando os dados são enviados memcpy(peerInfo.peer_addr, slaveMacAddress, 6); // Copia o endereço MAC do escravo para a estrutura peerInfo peerInfo.channel = 0; // Define o canal de comunicação como 0 na estrutura peerInfo peerInfo.encrypt = false; // Define a encriptação como desativada na estrutura peerInfo // Tenta adicionar o escravo à lista de pares de comunicação ESP-NOW e verifica se foi bem sucedido if (esp_now_add_peer(&peerInfo) != ESP_OK) { // Caso não seja possível adicionar o escravo, exibe mensagem de falha na Serial e reinicia o dispositivo Serial.println("Falha ao adicionar peer. Reiniciando..."); delay(2500); ESP.restart(); } } void loop() { esp_now_send(slaveMacAddress, (uint8_t *)&dataToSend, sizeof(DataStruct)); // Envie os dados para o endereço MAC do dispositivo escravo delay(20); // Pausa no programa por 20 milissegundos }
Esse é um sketch de um mestre ESP32 que utiliza o protocolo de comunicação sem fio ESP-NOW para enviar dados para um escravo unidirecionalmente.
O sketch é iniciado com a inclusão de duas bibliotecas: a biblioteca ESP-NOW, para comunicação sem fio, e a biblioteca WiFi, para conexão a redes Wi-Fi. Em seguida é feita a definição do endereço MAC do dispositivo escravo, a definição da estrutura DataStruct que será utilizada para trocar informações, a criação da estrutura esp_now_peer_info_t que registra informações sobre um peer (dispositivo) que será adicionado à rede ESP-NOW, é criada a função OnDataSent que é chamada quando os dados são enviados e as funções setup() e loop().
Na função setup(), o código define a porta serial, desconecta qualquer rede WiFi previamente conectada, define o modo WiFi como Station (cliente), inicializa o ESP-NOW e registra a função de callback que é chamada quando os dados são enviados. Em seguida, o endereço MAC do escravo é copiado para a estrutura peerInfo, o canal de comunicação é definido como 0 e a encriptação é desativada na estrutura peerInfo. Por fim, o código tenta adicionar o escravo à lista de pares de comunicação ESP-NOW e verifica se foi bem-sucedido. Se não for possível adicionar o escravo, o código exibe uma mensagem de falha na Serial e reinicia o dispositivo.
Na função loop(), o código envia os dados para o endereço MAC do dispositivo escravo utilizando a função esp_now_send() e pausa o programa por 20 milissegundos. A função OnDataSent() é chamada após a transmissão dos dados e exibe uma mensagem no monitor serial informando que os dados foram enviados com sucesso ou com falha, além de administrar o valor dos dados a enviar. O valor dos dados é incrementado a cada envio e, caso ultrapasse 10, retorna para 0.
/****************************************************************************** Comunicação Unidirecional com mestre enviando para um escravo Sketch Escravo Criado em 14 de Março de 2023 por Michel Galvão (https://micsg.com.br) Eletrogate - Loja de Arduino \\ Robótica \\ Automação \\ Apostilas \\ Kits https://www.eletrogate.com/ ******************************************************************************/ // Inclusão de bibliotecas #include <esp_now.h> // Biblioteca para utilizar o protocolo de comunicação ESP-NOW #include <WiFi.h> // Biblioteca para conectar em redes Wi-Fi // Estrutura de dados para troca de informações struct DataStruct { int integerData; // Dado do tipo inteiro }; // Variável para armazenar os dados recebidos DataStruct dataStruct; // Variável para armazenar o tempo da última recepção unsigned long ultimaRecepcao = 0; // Função que é chamada quando dados são recebidos via ESP-NOW void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) { memcpy(&dataStruct, incomingData, sizeof(dataStruct)); // Copia os dados recebidos para a estrutura de dados // Mostra na Serial as informações sobre os dados recebidos Serial.print("Obtido "); Serial.print(len); Serial.print(" Bytes: "); digitalWrite(LED_BUILTIN, HIGH); ultimaRecepcao = millis(); Serial.println(dataStruct.integerData); } void setup() { Serial.begin(115200); // Desconecta de alguma conexão WiFi anterior e define o modo estação (STA) WiFi.disconnect(); WiFi.mode(WIFI_STA); pinMode(LED_BUILTIN, OUTPUT); // Configura o pino do LED BUILTIN como saída // Inicia a biblioteca ESP-NOW e, caso ocorra algum erro, reinicia o dispositivo if (esp_now_init() != ESP_OK) { Serial.println("Inicializacao ESP-NOW com Erro. Reiniciando..."); delay(2500); ESP.restart(); } // Registra a função OnDataRecv como a função a ser chamada quando receber dados via ESP-NOW esp_now_register_recv_cb(OnDataRecv); ultimaRecepcao = millis(); } void loop() { if (millis() - ultimaRecepcao > 1000) { // se o tempo até a última recepção passar de 1 segundo, mostra erro apagando o LED digitalWrite(LED_BUILTIN, LOW); } }
Este é um sketch para um dispositivo escravo ESP32 que utiliza o protocolo de comunicação sem fio ESP-NOW para receber dados unidirecionalmente de um dispositivo mestre.
O sketch é iniciado com a inclusão de duas bibliotecas: a biblioteca ESP-NOW, para comunicação sem fio, e a biblioteca WiFi, para conexão a redes Wi-Fi. Em seguida, é definida a estrutura de dados DataStruct para trocar informações. A variável dataStruct é definida para armazenar os dados recebidos e ultimaRecepcao é definida para armazenar o tempo da última recepção.
Na função OnDataRecv(), que é chamada quando os dados são recebidos via ESP-NOW, os dados são copiados para a estrutura de dados dataStruct, e as informações sobre os dados recebidos são mostradas na Serial. O LED embutido é aceso para indicar a recepção de dados, e a variável ultimaRecepcao é atualizada com o valor atual de millis().
Na função setup(), a Serial é iniciada com uma taxa de transmissão de 115200 baud. O dispositivo se desconecta de qualquer conexão Wi-Fi anterior e é definido como estação (STA). O pino do LED embutido é definido como saída. Em seguida, a biblioteca ESP-NOW é iniciada, e caso ocorra algum erro, o dispositivo é reiniciado. A função OnDataRecv() é registrada como a função a ser chamada quando receber dados via ESP-NOW.
Na função loop(), é verificado se o tempo até a última recepção é maior que 1 segundo. Se for o caso, o LED embutido é apagado para indicar que não houve recepção de dados recentemente. A função loop() é executada continuamente enquanto o dispositivo estiver ligado.
Observação: O resultado do teste pode variar de acordo com:
Em conclusão, o ESP-NOW é uma tecnologia de rede sem fio altamente eficiente e flexível que oferece uma solução segura e eficiente em termos de energia para a comunicação entre dispositivos ESP32s ou ESP8266s. Uma das principais vantagens que o ESP-NOW tem, é que ele permite aumentar o alcance da comunicação quando utilizado no modo de comunicação multidirecional. Ao permitir que vários dispositivos sejam usados como repetidores, ele pode ampliar a área de cobertura da rede sem fio.
Gostaríamos de saber se você curtiu este post! Por favor, avalie e deixe um comentário sobre o conteúdo. E não esqueça de nos seguir no Instagram e nos marcar quando fizer algum projeto nosso: @eletrogate.
Até a próxima!
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!