ESP8266 NodeMCU : Tableau de bord du capteur de serveur Web ESP-NOW

ESP8266 NodeMCU : Tableau de bord du capteur de serveur Web ESP-NOW

Dans ce projet, vous apprendrez à héberger un serveur Web avec la carte ESP8266 NodeMCU et à utiliser le protocole de communication ESP-NOW en même temps. Vous pouvez avoir plusieurs cartes ESP8266 envoyant des lectures de capteur via ESP-NOW à un récepteur ESP8266 qui affiche toutes les lectures sur un serveur Web. Les cartes seront programmées à l’aide de l’IDE Arduino.

ESP8266 NodeMCU : tableau de bord de capteur de serveur Web ESP-NOW utilisant Arduino IDE (ESP-NOW et Wi-Fi simultanément)

Nous avons d’autres guides liés à ESP-NOW qui pourraient vous intéresser :

Utilisation simultanée d’ESP-NOW et du Wi-Fi

Utilisation simultanée d'ESP-NOW et du Wi-Fi : serveur Web récepteur ESP-NOW et cartes d'envoi ESP-NOW avec ESP8266 NodeMCU

Il y a quelques éléments dont vous devez tenir compte si vous souhaitez utiliser le Wi-Fi pour héberger un serveur Web et utiliser ESP-NOW simultanément pour recevoir les lectures des capteurs d’autres cartes :

  • Les cartes émettrices ESP8266 doivent utiliser le même canal Wi-Fi de la carte réceptrice.
  • Le canal Wi-Fi de la carte récepteur est automatiquement attribué par votre routeur Wi-Fi.
  • Le mode Wi-Fi de la carte réceptrice doit être point d’accès et station (WIFI_AP_STA).
  • Vous pouvez configurer le même canal Wi-Fi manuellement, ou vous pouvez ajouter une simple colonne vertébrale de code sur l’expéditeur pour définir son canal Wi-Fi sur le même que celui de la carte réceptrice.

Aperçu du projet

Le diagramme suivant montre une vue d’ensemble de haut niveau du projet que nous allons construire.

ESP-NOW Receiver Web Server et cartes ESP32 envoyant des relevés de température et d'humidité avec ESP-NOW à l'aide de cartes ESP8266 NodeMCU
  • Il y a deux cartes émettrices ESP8266 qui envoient BME280 lectures de température et d’humidité via ESP-NOW vers une carte récepteur ESP8266 (configuration ESP-NOW plusieurs à un);
  • La carte réceptrice ESP8266 reçoit les paquets et affiche les lectures sur un serveur Web ;
  • La page Web est mise à jour automatiquement chaque fois qu’elle reçoit une nouvelle lecture à l’aide des événements envoyés par le serveur (SSE).
  • La page Web indique également la dernière fois que les lectures ont été mises à jour à l’aide de JavaScript.

Conditions préalables

Avant de poursuivre ce projet, assurez-vous de vérifier les prérequis suivants.

EDI Arduino

Nous allons programmer les cartes ESP8266 à l’aide de l’IDE Arduino, donc avant de poursuivre ce tutoriel, assurez-vous que la carte ESP8266 est installée dans votre IDE Arduino.

Bibliothèques BME280

La carte émettrice ESP8266 enverra les lectures de température et d’humidité à partir d’un capteur BME280.

Pour lire à partir du capteur BME280, nous utiliserons le Bibliothèque Adafruit_BME280. Pour utiliser cette bibliothèque, vous devez également installer le Bibliothèque de capteurs unifiés Adafruit. Suivez les étapes suivantes pour installer ces bibliothèques.

Recherchez « adafruit bme280 » dans la zone de recherche et installez la bibliothèque.

Installer la bibliothèque de capteurs BME280 dans l'IDE Arduino

Pour utiliser la bibliothèque BME280, vous devez également installer la bibliothèque Adafruit_Sensor. Suivez les étapes suivantes pour installer la bibliothèque dans votre IDE Arduino :

Accédez à Sketch > Inclure la bibliothèque > Gérer les bibliothèques et tapez « Adafruit Unified Sensor » dans la zone de recherche. Faites défiler vers le bas pour trouver la bibliothèque et installez-la.

Installer la bibliothèque de capteurs unifiés Adafruit dans l'IDE Arduino

Pour en savoir plus sur le capteur de température, d’humidité et de pression BME280, lisez notre guide : ESP8266 avec BME280 utilisant Arduino IDE (Pressure, Temperature, Humidity).

Bibliothèques de serveurs Web asynchrones

Pour construire le serveur Web, vous devez installer les bibliothèques suivantes :

Ces bibliothèques ne peuvent pas être installées via le gestionnaire de bibliothèque Arduino, vous devez donc copier les fichiers de bibliothèque dans le dossier Bibliothèques d’installation Arduino. Alternativement, dans votre IDE Arduino, vous pouvez aller à Sketch> Inclure la bibliothèque> Ajouter une bibliothèque .zip et sélectionner les bibliothèques que vous venez de télécharger.

Bibliothèque Arduino_JSON

Vous devez installer la bibliothèque Arduino_JSON. Vous pouvez installer cette bibliothèque dans le gestionnaire de bibliothèque Arduino IDE. Accédez simplement à Sketch > Inclure la bibliothèque > Gérer les bibliothèques et recherchez le nom de la bibliothèque comme suit :

Installer la bibliothèque Arduino JSON Arduino IDE

Pièces requises

Pour suivre ce tutoriel, vous avez besoin de plusieurs cartes ESP8266. Nous utiliserons trois cartes ESP8266. Tu as aussi besoin:

Vous pouvez utiliser les liens précédents ou accéder directement à MakerAdvisor.com/tools pour trouver toutes les pièces pour vos projets au meilleur prix !

1689840798 148 ESP8266 NodeMCU Tableau de bord du capteur de serveur Web

Obtention de l’adresse MAC de la carte du récepteur

Pour envoyer des messages via ESP-NOW, vous devez connaître l’adresse MAC de la carte réceptrice. Chaque carte a une adresse MAC unique (apprenez comment obtenir et modifier l’adresse MAC ESP8266).

Téléchargez le code suivant sur votre carte récepteur ESP8266 pour obtenir son adresse MAC.

// Complete Instructions to Get and Change ESP MAC Address: https://Raspberryme.com/get-change-esp32-esp8266-mac-address-arduino/

#ifdef ESP32
  #include <WiFi.h>
#else
  #include <ESP8266WiFi.h>
#endif

void setup(){
  Serial.begin(115200);
  Serial.println();
  Serial.print("ESP Board MAC Address:  ");
  Serial.println(WiFi.macAddress());
}
 
void loop(){

}

Afficher le code brut

Après avoir téléchargé le code, appuyez sur le bouton RST/EN et l’adresse MAC devrait s’afficher sur le moniteur série.

Adresse MAC de la carte ESP32 avec moniteur série Arduino IDE

Récepteur ESP8266 (ESP-NOW + Serveur Web)

La carte réceptrice ESP8266 NodeMCU reçoit les paquets des cartes émettrices et héberge un serveur Web pour afficher les dernières lectures reçues.

Téléchargez le code suivant sur votre carte réceptrice – le code est préparé pour recevoir des lectures de deux cartes différentes.

/*
  Rui Santos
  Complete project details at https://Raspberryme.com/esp8266-esp-now-wi-fi-web-server/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <espnow.h>
#include <ESP8266WiFi.h>
#include "ESPAsyncWebServer.h"
#include "ESPAsyncTCP.h"
#include <Arduino_JSON.h>

// Replace with your network credentials (STATION)
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  int id;
  float temp;
  float hum;
  unsigned int readingId;
} struct_message;

struct_message incomingReadings;

JSONVar board;

AsyncWebServer server(80);
AsyncEventSource events("/events");

// callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac_addr, uint8_t *incomingData, uint8_t len) { 
  // Copies the sender mac address to a string
  char macStr[18];
  Serial.print("Packet received from: ");
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.println(macStr);
  memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));
  
  board["id"] = incomingReadings.id;
  board["temperature"] = incomingReadings.temp;
  board["humidity"] = incomingReadings.hum;
  board["readingId"] = String(incomingReadings.readingId);
  String jsonString = JSON.stringify(board);
  events.send(jsonString.c_str(), "new_readings", millis());
  
  Serial.printf("Board ID %u: %u bytes\n", incomingReadings.id, len);
  Serial.printf("t value: %4.2f \n", incomingReadings.temp);
  Serial.printf("h value: %4.2f \n", incomingReadings.hum);
  Serial.printf("readingID value: %d \n", incomingReadings.readingId);
  Serial.println();
}

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP-NOW DASHBOARD</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="icon" href="data:,">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h1 {  font-size: 2rem;}
    body {  margin: 0;}
    .topnav { overflow: hidden; background-color: #2f4468; color: white; font-size: 1.7rem; }
    .content { padding: 20px; }
    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }
    .cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
    .reading { font-size: 2.8rem; }
    .timestamp { color: #bebebe; font-size: 1rem; }
    .card-title{ font-size: 1.2rem; font-weight : bold; }
    .card.temperature { color: #B10F2E; }
    .card.humidity { color: #50B8B4; }
  </style>
</head>
<body>
  <div class="topnav">
    <h1>ESP-NOW DASHBOARD</h1>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card temperature">
        <p class="card-title"><i class="fas fa-thermometer-half"></i> BOARD #1 - TEMPERATURE</p><p><span class="reading"><span id="t1"></span> &deg;C</span></p><p class="timestamp">Last Reading: <span id="rt1"></span></p>
      </div>
      <div class="card humidity">
        <p class="card-title"><i class="fas fa-tint"></i> BOARD #1 - HUMIDITY</p><p><span class="reading"><span id="h1"></span> &percnt;</span></p><p class="timestamp">Last Reading: <span id="rh1"></span></p>
      </div>
      <div class="card temperature">
        <p class="card-title"><i class="fas fa-thermometer-half"></i> BOARD #2 - TEMPERATURE</p><p><span class="reading"><span id="t2"></span> &deg;C</span></p><p class="timestamp">Last Reading: <span id="rt2"></span></p>
      </div>
      <div class="card humidity">
        <p class="card-title"><i class="fas fa-tint"></i> BOARD #2 - HUMIDITY</p><p><span class="reading"><span id="h2"></span> &percnt;</span></p><p class="timestamp">Last Reading: <span id="rh2"></span></p>
      </div>
    </div>
  </div>
<script>
function getDateTime() {
  var currentdate = new Date();
  var datetime = currentdate.getDate() + "/"
  + (currentdate.getMonth()+1) + "/"
  + currentdate.getFullYear() + " at "
  + currentdate.getHours() + ":"
  + currentdate.getMinutes() + ":"
  + currentdate.getSeconds();
  return datetime;
}
if (!!window.EventSource) {
 var source = new EventSource('/events');
 
 source.addEventListener('open', function(e) {
  console.log("Events Connected");
 }, false);
 source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
 }, false);
 
 source.addEventListener('message', function(e) {
  console.log("message", e.data);
 }, false);
 
 source.addEventListener('new_readings', function(e) {
  console.log("new_readings", e.data);
  var obj = JSON.parse(e.data);
  document.getElementById("t"+obj.id).innerHTML = obj.temperature.toFixed(2);
  document.getElementById("h"+obj.id).innerHTML = obj.humidity.toFixed(2);
  document.getElementById("rt"+obj.id).innerHTML = getDateTime();
  document.getElementById("rh"+obj.id).innerHTML = getDateTime();
 }, false);
}
</script>
</body>
</html>)rawliteral";

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);

  // Set the device as a Station and Soft Access Point simultaneously
  WiFi.mode(WIFI_AP_STA);
  
  // Set device as a Wi-Fi Station
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Setting as a Wi-Fi Station..");
  }
  Serial.print("Station IP Address: ");
  Serial.println(WiFi.localIP());
  Serial.print("Wi-Fi Channel: ");
  Serial.println(WiFi.channel());

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_register_recv_cb(OnDataRecv);

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html);
  });
   
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
    }
    // send event with message "hello!", id current millis
    // and set reconnect delay to 1 second
    client->send("hello!", NULL, millis(), 10000);
  });
  server.addHandler(&events);
  server.begin();
}
 
void loop() {
  static unsigned long lastEventTime = millis();
  static const unsigned long EVENT_INTERVAL_MS = 5000;
  if ((millis() - lastEventTime) > EVENT_INTERVAL_MS) {
    events.send("ping",NULL,millis());
    lastEventTime = millis();
  }
}

Afficher le code brut

Comment fonctionne le code

Tout d’abord, incluez les bibliothèques nécessaires.

#include <espnow.h>
#include <ESP8266WiFi.h>
#include "ESPAsyncWebServer.h"
#include "ESPAsyncTCP.h"
#include <Arduino_JSON.h>

Le Bibliothèque Arduino_JSON est nécessaire car nous allons créer une variable JSON avec les données reçues de chaque carte. Cette variable JSON sera utilisée pour envoyer toutes les informations nécessaires à la page Web, comme vous le verrez plus tard dans ce projet.

Insérez vos informations d’identification réseau sur les lignes suivantes afin que l’ESP8266 puisse se connecter à votre réseau local.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Structure de données

Ensuite, créez une structure qui contient les données que nous recevrons. Nous avons appelé cette structure struct_message et elle contient l’ID de la carte, les lectures de température et d’humidité et l’ID de lecture.

typedef struct struct_message {
    int id;
    float temp;
    float hum;
    int readingId;
} struct_message;

Créez une nouvelle variable de type struct_message appelée entrantReadings qui stockera les valeurs des variables.

struct_message incomingReadings;

Créez une variable JSON appelée board.

JSONVar board;

Créez un serveur Web asynchrone sur le port 80.

AsyncWebServer server(80);

Créer une source d’événement

Pour afficher automatiquement les informations sur le serveur Web lorsqu’une nouvelle lecture arrive, nous utiliserons Server-Sent Events (SSE).

La ligne suivante crée une nouvelle source d’événement sur /events.

AsyncEventSource events("/events");

Les événements envoyés par le serveur permettent à une page Web (client) d’obtenir des mises à jour d’un serveur. Nous l’utiliserons pour afficher automatiquement de nouvelles lectures sur la page du serveur Web lorsqu’un nouveau paquet ESP-NOW arrivera.

Important : les événements envoyés par le serveur ne sont pas pris en charge sur Internet Explorer.

Fonction OnDataRecv()

La fonction OnDataRecv() sera exécutée lorsque vous recevrez un nouveau paquet ESP-NOW.

void OnDataRecv(uint8_t * mac_addr, uint8_t *incomingData, uint8_t len) { 

Dans cette fonction, imprimez l’adresse MAC de l’expéditeur :

char macStr[18];
Serial.print("Packet received from: ");
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
         mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
Serial.println(macStr);

Copiez les informations de la variable entrantData dans la variable de structure entranteReadings.

memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));

Ensuite, créez une variable JSON String avec les informations reçues (variable jsonString) :

board["id"] = incomingReadings.id;
board["temperature"] = incomingReadings.temp;
board["humidity"] = incomingReadings.hum;
board["readingId"] = String(incomingReadings.readingId);
String jsonString = JSON.stringify(board);

Voici un exemple de la façon dont la variable jsonString peut ressembler après avoir reçu les lectures :

board = {
  "id": "1",
  "temperature": "24.32",
  "humidity" = "65.85",
  "readingId" = "2"
}

Après avoir rassemblé toutes les données reçues sur la variable jsonString, envoyez ces informations au navigateur sous forme d’événement (« new_readings »).

events.send(jsonString.c_str(), "new_readings", millis());

Plus tard, nous verrons comment gérer ces événements côté client.

Enfin, imprimez les informations reçues sur le moniteur série Arduino IDE à des fins de débogage :

Serial.printf("Board ID %u: %u bytes\n", incomingReadings.id, len);
Serial.printf("t value: %4.2f \n", incomingReadings.temp);
Serial.printf("h value: %4.2f \n", incomingReadings.hum);
Serial.printf("readingID value: %d \n", incomingReadings.readingId);
Serial.println();

Construire la page Web

La variable index_html contient tout le HTML, CSS et JavaScript pour construire la page Web. Nous n’entrerons pas dans les détails du fonctionnement du HTML et du CSS. Nous allons juste voir comment gérer les événements envoyés par le serveur.

Gérer les événements

Créez un nouvel objet EventSource et spécifiez l’URL de la page qui envoie les mises à jour. Dans notre cas, il s’agit de /events.

if (!!window.EventSource) {
  var source = new EventSource('/events');

Une fois que vous avez instancié une source d’événement, vous pouvez commencer à écouter les messages du serveur avec addEventListener().

Ce sont les écouteurs d’événements par défaut, comme indiqué ici dans AsyncWebServer Documentation.

source.addEventListener('open', function(e) {
  console.log("Events Connected");
}, false);
source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
}, false);

source.addEventListener('message', function(e) {
  console.log("message", e.data);
}, false);

Ensuite, ajoutez l’écouteur d’événement pour « new_readings ».

source.addEventListener('new_readings', function(e) {

Lorsque l’ESP8266 reçoit un nouveau paquet, il envoie une chaîne JSON avec les lectures en tant qu’événement (« new_readings ») au client. Les lignes suivantes gèrent ce qui se passe lorsque le navigateur reçoit cet événement.

console.log("new_readings", e.data);
var obj = JSON.parse(e.data);
document.getElementById("t"+obj.id).innerHTML = obj.temperature.toFixed(2);
document.getElementById("h"+obj.id).innerHTML = obj.humidity.toFixed(2);
document.getElementById("rt"+obj.id).innerHTML = getDateTime();
document.getElementById("rh"+obj.id).innerHTML = getDateTime();

Fondamentalement, imprimez les nouvelles lectures sur la console du navigateur et placez les données reçues dans les éléments avec l’identifiant correspondant sur la page Web. Nous mettons également à jour la date et l’heure auxquelles les relevés ont été reçus en appelant la fonction JavaScript getDateTime().

function getDateTime() {
  var currentdate = new Date();
  var datetime = currentdate.getDate() + "/"
  + (currentdate.getMonth()+1) + "/"
  + currentdate.getFullYear() + " at "
  + currentdate.getHours() + ":"
  + currentdate.getMinutes() + ":"
  + currentdate.getSeconds();
  return datetime;
}

installation()

Dans le setup(), configurez le récepteur ESP8266 comme point d’accès et station Wi-Fi :

WiFi.mode(WIFI_AP_STA);

Les lignes suivantes connectent l’ESP8266 à votre réseau local et impriment l’adresse IP et le canal Wi-Fi :

// Set device as a Wi-Fi Station
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Setting as a Wi-Fi Station..");
}
Serial.print("Station IP Address: ");
Serial.println(WiFi.localIP());
Serial.print("Wi-Fi Channel: ");
Serial.println(WiFi.channel());

Initialisez ESP-NOW.

if (esp_now_init() != 0) {
  Serial.println("Error initializing ESP-NOW");
  return;
}

Enregistrez-vous pour la fonction de rappel OnDataRecv, afin qu’elle soit exécutée lorsqu’un nouveau paquet ESP-NOW arrive.

esp_now_register_recv_cb(OnDataRecv);

Traiter les demandes

Lorsque vous accédez à l’adresse IP ESP8266 sur la root/URL, envoyez le texte qui est stocké sur la variable index_html pour créer la page Web.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/html", index_html);
});

Source d’événement du serveur

Configurez la source d’événement sur le serveur.

events.onConnect([](AsyncEventSourceClient *client){
  if(client->lastId()){
    Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
  }
  // send event with message "hello!", id current millis
  // and set reconnect delay to 1 second
  client->send("hello!", NULL, millis(), 10000);
);
server.addHandler(&events);

Enfin, démarrez le serveur.

server.begin();

boucle()

Dans la boucle(), envoyez un ping toutes les 5 secondes. Ceci est utilisé pour vérifier côté client, si le serveur est toujours en cours d’exécution (ces lignes ne sont pas obligatoires).

static unsigned long lastEventTime = millis();
static const unsigned long EVENT_INTERVAL_MS = 5000;
if ((millis() - lastEventTime) > EVENT_INTERVAL_MS) {
  events.send("ping",NULL,millis());
  lastEventTime = millis();
}

Le diagramme suivant résume le fonctionnement des événements envoyés par le serveur sur ce projet et comment il met à jour les valeurs sans actualiser la page Web.

Aperçu du projet de tableau de bord du capteur de serveur Web ESP8266 ESP-NOW

Après avoir téléchargé le code sur la carte du récepteur, appuyez sur le bouton EN/RST intégré. L’adresse IP ESP8266 doit être imprimée sur le moniteur série ainsi que sur le canal Wi-Fi.

ESP-NOW obtient l'adresse IP ESP32 et le canal Wi-Fi

Circuit émetteur ESP8266

Les cartes émettrices ESP8266 sont connectées à un capteur BME280. Câblez le capteur aux broches ESP8266 I2C par défaut :

  • GPIO 5 (D1) -> SCL
  • GPIO 4 (D2) -> SDA
ESP8266 NodeMCU BME280 Capteur Température Humidité Pression Schéma de câblage Circuit

Code d’expéditeur ESP8266 (ESP-MAINTENANT)

Chaque carte émettrice enverra une structure via ESP-NOW qui contient l’ID de la carte (afin que vous puissiez identifier quelle carte a envoyé les lectures), la température, l’humidité et l’ID de lecture. L’ID de lecture est un nombre entier pour savoir combien de messages ont été envoyés.

Carte émetteur-récepteur ESP8266 avec ESP-NOW utilisant Arduino IDE

Téléchargez le code suivant sur chacun de vos tableaux d’envoi. N’oubliez pas d’incrémenter le numéro d’id de chaque carte émettrice et d’insérer votre SSID dans la variable WIFI_SSID.

/*
  Rui Santos
  Complete project details at https://Raspberryme.com/esp8266-esp-now-wi-fi-web-server/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <espnow.h>
#include <ESP8266WiFi.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>

// Set your Board ID (ESP32 Sender #1 = BOARD_ID 1, ESP32 Sender #2 = BOARD_ID 2, etc)
#define BOARD_ID 2

Adafruit_BME280 bme; 

//MAC Address of the receiver 
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

//Structure example to send data
//Must match the receiver structure
typedef struct struct_message {
    int id;
    float temp;
    float hum;
    int readingId;
} struct_message;

//Create a struct_message called myData
struct_message myData;

unsigned long previousMillis = 0;   // Stores last time temperature was published
const long interval = 10000;        // Interval at which to publish sensor readings

unsigned int readingId = 0;

// Insert your SSID
constexpr char WIFI_SSID[] = "REPLACE_WITH_YOUR_SSID";

int32_t getWiFiChannel(const char *ssid) {
  if (int32_t n = WiFi.scanNetworks()) {
    for (uint8_t i=0; i<n; i++) {
      if (!strcmp(ssid, WiFi.SSID(i).c_str())) {
        return WiFi.channel(i);
      }
    }
  }
  return 0;
}

void initBME(){
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

float readTemperature() {
  float t = bme.readTemperature();
  return t;
}

float readHumidity() {
  float h = bme.readHumidity();
  return h;
}

// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0){
    Serial.println("Delivery success");
  }
  else{
    Serial.println("Delivery fail");
  }
}
 
void setup() {
  //Init Serial Monitor
  Serial.begin(115200);
  initBME(); 

  // Set device as a Wi-Fi Station and set channel
  WiFi.mode(WIFI_STA);

  int32_t channel = getWiFiChannel(WIFI_SSID);

  WiFi.printDiag(Serial); // Uncomment to verify channel number before
  wifi_promiscuous_enable(1);
  wifi_set_channel(channel);
  wifi_promiscuous_enable(0);
  WiFi.printDiag(Serial); // Uncomment to verify channel change after

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
   esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);

  esp_now_register_send_cb(OnDataSent);
  
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
}
 
void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // Save the last time a new reading was published
    previousMillis = currentMillis;
    //Set values to send
    myData.id = BOARD_ID;
    myData.temp = readTemperature();
    myData.hum = readHumidity();
    myData.readingId = readingId++;
     
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));

    Serial.print("loop");
  }
}

Afficher le code brut

Comment fonctionne le code

Commencez par importer les bibliothèques requises :

#include <espnow.h>
#include <ESP8266WiFi.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>

Définir l’ID du tableau

Définissez l’ID de la carte émettrice ESP8266, par exemple définissez BOARD_ID 1 pour ESP8266 Sender #1, etc…

#define BOARD_ID 1

Capteur BME280

Créez un objet Adafruit_BME280 appelé bme.

Adafruit_BME280 bme;

Adresse MAC du destinataire

Insérez l’adresse MAC du récepteur sur la ligne suivante (par exemple) :

uint8_t broadcastAddress[] = {0x30, 0xAE, 0xA4, 0x15, 0xC7, 0xFC};

Structure de données

Ensuite, créez une structure qui contient les données que nous voulons envoyer. Le struct_message contient l’ID de la carte, la lecture de la température, la lecture de l’humidité et l’ID de lecture.

typedef struct struct_message {
    int id;
    float temp;
    float hum;
    int readingId;
} struct_message;

Créez une nouvelle variable de type struct_message appelée myData qui stocke les valeurs des variables.

struct_message myData;

Intervalle de minuterie

Créez des variables de minuterie auxiliaires pour publier les lectures toutes les 10 secondes. Vous pouvez modifier le temps de retard sur la variable d’intervalle.

unsigned long previousMillis = 0; // Stores last time temperature was published
const long interval = 10000; // Interval at which to publish sensor readings

Initialisez la variable readingId – elle garde une trace du nombre de lectures envoyées.

unsigned int readingId = 0;

Changer de canal Wi-Fi

Maintenant, nous allons obtenir le canal Wi-Fi du récepteur. Ceci est utile car cela nous permet d’attribuer automatiquement le même canal Wi-Fi à la carte émettrice.

Pour cela, vous devez insérer votre SSID dans la ligne suivante :

constexpr char WIFI_SSID[] = "REPLACE_WITH_YOUR_SSID";

Ensuite, la fonction getWiFiChannel() recherche votre réseau et obtient son canal.

int32_t getWiFiChannel(const char *ssid) {
  if (int32_t n = WiFi.scanNetworks()) {
    for (uint8_t i=0; i<n; i++) {
      if (!strcmp(ssid, WiFi.SSID(i).c_str())) {
        return WiFi.channel(i);
      }
    }
  }
  return 0;
}

Ce bout de code a été proposé par Stéphane (un de nos lecteurs). Vous pouvez voir son exemple complet ici.

Initialiser le capteur BME280

La fonction initBME() initialise le capteur BME280.

void initBME(){
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

Température de lecture

La fonction readTemperature() lit et renvoie la température du capteur BME280.

float readTemperature() {
  float t = bme.readTemperature();
  return t;
}

Lecture de l’humidité

La fonction readHumidity() lit et renvoie l’humidité du capteur BME280.

float readHumidity() {
  float h = bme.readHumidity();
  return h;
}

Remarque : pour en savoir plus sur l’obtention de la température et de l’humidité à partir du capteur BME280, lisez : ESP8266 avec BME280 à l’aide de l’IDE Arduino (pression, température, humidité).

Fonction de rappel OnDataSent

La fonction de rappel OnDataSent() sera exécutée lors de l’envoi d’un message. Dans ce cas, cette fonction imprime si le message a été livré avec succès ou non.

void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0){
    Serial.println("Delivery success");
  }
  else{
    Serial.println("Delivery fail");
  }
}

installation()

Initialisez le moniteur série.

Serial.begin(115200);

Initialisez le capteur BME280 :

initBME();

Définissez l’ESP8266 comme station Wi-Fi.

WiFi.mode(WIFI_STA);

Réglez son canal pour qu’il corresponde au canal Wi-Fi du récepteur :

int32_t channel = getWiFiChannel(WIFI_SSID);

WiFi.printDiag(Serial); // Uncomment to verify channel number before
wifi_promiscuous_enable(1);
wifi_set_channel(channel);
wifi_promiscuous_enable(0);
WiFi.printDiag(Serial); // Uncomment to verify channel change after

Initialisez ESP-NOW.

// Init ESP-NOW
if (esp_now_init() != 0) {
  Serial.println("Error initializing ESP-NOW");
  return;
}

Définissez le rôle ESP8266 :

esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);

Après avoir initialisé ESP-NOW avec succès, enregistrez la fonction de rappel qui sera appelée lors de l’envoi d’un message. Dans ce cas, enregistrez-vous pour la fonction OnDataSent() créée précédemment.

esp_now_register_send_cb(OnDataSent);

Ajouter un pair

Pour envoyer des données à une autre carte (le récepteur), vous devez la coupler en tant que pair. Les lignes suivantes enregistrent et ajoutent le récepteur en tant qu’homologue.

esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);

boucle()

Dans la boucle (), vérifiez s’il est temps d’obtenir et d’envoyer de nouvelles lectures.

unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
  // Save the last time a new reading was published
  previousMillis = currentMillis;

Envoyer un message ESP-NOW

Enfin, envoyez la structure du message via ESP-NOW.

esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));

Lecture recommandée : Premiers pas avec ESP-NOW (ESP8266 avec Arduino IDE)

Téléchargez le code sur vos tableaux d’envoi. Vous devriez remarquer que les cartes changent leur canal Wi-Fi pour le canal de la carte réceptrice.

Manifestation

Après avoir téléchargé le code sur toutes les cartes et si tout se passe comme prévu, la carte réceptrice ESP8266 devrait commencer à recevoir les lectures des capteurs des autres cartes.

Ouvrez un navigateur sur votre réseau local et saisissez l’adresse IP ESP8266.

Tableau de bord du capteur de serveur Web ESP8266 NodeMCU ESP-NOW Lectures du capteur de démonstration ESP-NOW et Wi-Fi

Il devrait charger la température, l’humidité et la dernière fois que les lectures ont été mises à jour sur la page Web de chaque carte. Lors de la réception d’un nouveau paquet, votre page Web se met à jour automatiquement sans actualiser la page Web.

Démonstration du tableau de bord du capteur de serveur Web ESP8266 NodeMCU ESP-NOW

Conclusion

Dans ce didacticiel, vous avez appris à utiliser ESP-NOW et Wi-Fi pour configurer un serveur Web afin de recevoir des paquets ESP-NOW de plusieurs cartes (configuration plusieurs à un) à l’aide de la carte ESP8266 NodeMCU. Nous avons un projet similaire utilisant l’ESP32 :

De plus, vous avez également utilisé les événements envoyés par le serveur pour mettre automatiquement à jour la page Web chaque fois qu’un nouveau paquet est reçu sans actualiser la page Web. Nous avons un guide dédié aux événements envoyés par le serveur :

Si vous aimez l’ESP8266, vous pouvez envisager de vous inscrire à notre eBook « La domotique avec l’ESP8266 ». Vous pouvez également accéder à nos ressources ESP8266 gratuites ici.

Merci d’avoir lu.

Apprenez l’histoire de Raspberry Pi à travers cette vidéo :

YouTube video