Esp32 avec un module zigbee

Bonjour,
Voici un petit tuto pour les débutants comme moi qui souhaiteraient faire remonter des informations via zigbee d’un esp32-h2.

J’ai pas mal galéré et je ne maitrise pas encore toutes les subtilités pour faire communiquer correctement l’esp32-h2 avec zigbee2mqtt.

J’ai donc pensé à détourner une configuration existante pour envoyé des données.
Ce n’est pas tout à fait propre, car j’utilise un cluster qui n’a rien à voir avec la bonne unité, mais bon cela fonctionne en attendant que j’arrive à créer mes propres cluster et fichier converter.

Voici l’idée : Je souhaitais équiper ma cuve d’un capteur ultrason de type hc-sr04 raccordé à un esp32-h2 pour envoyer les données de distances via zigbee à HA.
La cuve étant trop loin de la maison, l’alimentation de l’esp32-h2 est assurée par batterie 12V et petit panneau solaire.

Voici les différentes étapes :

1 - le montage :
Connexion de l’HC-SR04 à l’esp32-H2 :
VCC HC-SR04 sur le 5V de l’ESP32-H2
GND HC-SR04 sur GNDde l’ESP32-H2.
TRIG HC-SR04 sur GPIO 4 de l’ESP32-H2
ECHO HC-SR04 rsur GPIO 5 de l’ESP32-H2

J’ai fait également un pont diviseur avec deux résistances, 1kΩ et 2kΩ) sur la broche ECHO pour abaisser le signal à 3.3V pour ne pas endommager l’ESP32-H2.

2 - rechercher les informations existantes sur ce matériel et le zigbee.
Le github suivant permet de récupérer pas mal de configurations existantes :

Télécharger le zip et décompresser le pour pouvoir accéder aux exemples.

3 - installation et configuration d’Arduino : j’ai la version 2.3.6 sur windows.
Ouvrir un fichier .ino de la bibliothèque issu de Github.
Ici pour faire simple je suis aller dans la bibliothèque décompressé et j’ai ouvert le fichier .ino du dossier Zigbee_On_Off_Light.
J’ai fait une copie du dossier avant de travailler dedans, comme cela il y avait déjà toute la structure de faite.

Puis j’ai configuré :
a - Aller dans fichier/préférences et indiquer l’url de gestionnaire de cartes supplémentaires suivantes : https://espressif.github.io/arduino-esp32/package_esp32_index.json
b - Aller dans / Outils / gestionnaire de cartes : installer : Adruino ESP32 Boards et ESP32 par Espressif systems (j’ai la version 3.2.0)
c - Aller dans Outils et mettre la configuration suivante :

  • Carte : ESP32H2 Dev Module
  • Port : sélectionner votre port usb de votre PC (possible de le trouver dans le gestionnaire des périphérique de windows)
  • USB CDC On Boot : Enable (permet de regarder par la suite le moniteur serie et les logs)
  • Core Débug Level : None (j’ai pas utilisé cet outil)
  • Erase All Flash Béfore Sketech Upload : Disabled (permet de faire un hard reset, pour plus d’info voir le github où c’est bien expliqué)
  • Flash Frequency : 64Mhz
  • Flash Mode: QIO
  • Flash Size: 4MB (32Mb)
  • JTAG Adapter : Disabled
  • Partition Scheme: Zigbee 4MB with spiffs
  • Upload Speed: 921600
  • Zigbee Mode : Zigbee ED (end Device)

4- J’ai remplacé le code existant par mon code *.ino :

// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// ...

/**
 * @brief Zigbee distance sensor using HC-SR04 - Simplified version
 * 
 * Measures distance only, all calculations done in Home Assistant
 * HC-SR04 ultrasonic sensor connected to:
 * - Trigger: GPIO4
 * - Echo: GPIO5
 */

#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif

#include "Zigbee.h"
#include <cmath>

/* Zigbee configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
uint8_t button = BOOT_PIN;

// HC-SR04 pins
#define TRIGGER_PIN 4
#define ECHO_PIN 5

// Measurement limits (in cm)
#define MIN_DISTANCE_CM 5
#define MAX_DISTANCE_CM 200

// Reporting configuration
#define MEASURE_INTERVAL_MS 60000       // Mesure toutes les 60 secondes
#define MIN_CHANGE_CM 1.0               // Écart minimal pour rapporter
#define MAX_REPORT_INTERVAL_MS 300000   // Envoi forcé toutes les 5 minutes

ZigbeeTempSensor zbDistanceSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);

// Variables de suivi
float lastReportedDistance = -1;
unsigned long lastReportTime = 0;

/************************ HC-SR04 Functions *****************************/
float measureDistance() {
  digitalWrite(TRIGGER_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIGGER_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIGGER_PIN, LOW);

  long duration = pulseIn(ECHO_PIN, HIGH, 30000); // timeout 30 ms max

  if (duration == 0) {
    Serial.println("HC-SR04: No echo received");
    return -1;
  }

  float distance = (duration * 0.0343) / 2.0;

  if (distance < MIN_DISTANCE_CM || distance > MAX_DISTANCE_CM) {
    Serial.printf("HC-SR04: Out of range: %.2f cm\n", distance);
    return -1;
  }

  return distance;
}

bool shouldReport(float currentDistance) {
  unsigned long currentTime = millis();

  if (currentTime - lastReportTime > MAX_REPORT_INTERVAL_MS) {
    return true;
  }

  if (lastReportedDistance < 0) {
    return true;
  }

  if (fabs(currentDistance - lastReportedDistance) >= MIN_CHANGE_CM) {
    return true;
  }

  return false;
}

/************************ Sensor task *****************************/
static void sensor_task(void *arg) {
  for (;;) {
    float distance = measureDistance();

    if (distance > 0) {
      Serial.printf("Distance: %.2f cm\n", distance);

      if (shouldReport(distance)) {
        Serial.printf("Reporting: %.2f cm (change: %.2f)\n", 
                      distance, fabs(distance - lastReportedDistance));
        zbDistanceSensor.setTemperature(distance);
        lastReportedDistance = distance;
        lastReportTime = millis();
      }
    } else {
      Serial.println("Measurement error");

      if (lastReportedDistance >= 0) {
        zbDistanceSensor.setTemperature(-1);
        lastReportedDistance = -1;
        lastReportTime = millis();
      }
    }

    delay(MEASURE_INTERVAL_MS);
  }
}

/********************* Arduino functions **************************/
void setup() {
  Serial.begin(115200);
  
  pinMode(button, INPUT_PULLUP);
  pinMode(TRIGGER_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  digitalWrite(TRIGGER_PIN, LOW);

  Serial.println("HC-SR04 Distance Sensor - Simplified Version");
  Serial.printf("Measurement range: %d-%d cm\n", MIN_DISTANCE_CM, MAX_DISTANCE_CM);
  Serial.printf("Reporting threshold: %.1f cm change\n", MIN_CHANGE_CM);

  zbDistanceSensor.setManufacturerAndModel("Espressif", "HC-SR04-Simple");
  zbDistanceSensor.setMinMaxValue(MIN_DISTANCE_CM, MAX_DISTANCE_CM);
  zbDistanceSensor.setTolerance(0.5);

  Zigbee.addEndpoint(&zbDistanceSensor);
  Zigbee.setRebootOpenNetwork(60);

  Serial.println("Starting Zigbee...");
  if (!Zigbee.begin()) {
    Serial.println("Zigbee failed to start!");
    ESP.restart();
  }

  Serial.println("Connecting to Zigbee network...");
  while (!Zigbee.connected()) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("\nConnected to Zigbee network!");

  zbDistanceSensor.setReporting(60, 300, 1); // min 60s, max 300s, delta 1cm

  xTaskCreate(sensor_task, "sensor", 4096, NULL, 10, NULL);
  Serial.println("Distance sensor ready!");
}

void loop() {
  if (digitalRead(button) == LOW) {
    delay(100);

    int startTime = millis();
    while (digitalRead(button) == LOW) {
      delay(50);
      if ((millis() - startTime) > 3000) {
        Serial.println("Factory reset in 1s...");
        delay(1000);
        Zigbee.factoryReset();
      }
    }

    Serial.println("Manual report triggered");
    zbDistanceSensor.reportTemperature();
  }

  delay(100);
}

On voit dans ce code que :

  • on va chercher les librairie zigbee.h (issu du github) et cmath
  • J’ai défini le endpoint sur 10
  • J’ai défini les Pin du HC-SR4 comme sur le montage réalisé.
  • Ensuite j’ai créer une validation des données dans une limite, pas besoin de surcharger l’esp avec des mesures fausses. Ma cuve fait 1000 l et cubique, j’ai limité la mesure entre 5cm et 2 m, j’aurais pu réduire à 1m le max.
  • Je déclare la variable : ZigbeeTempSensor zbDistanceSensor = ZigbeeTempSensor, c’est là que nous détournons le capteur de température pour envoyer une distance.
  • après j’ai configuré le capteur ultrason, comme dans les docs
  • configuration du sensor avec une vérification si la distance à changer de plus de 1cm.
  • Dans la partie Arduino fonction :
    On défini ce qui est envoyé en zigbee et la fréquence d’envoi : 60 secondes et un envoi forcé toutes les 5 minutes.
    J’ai ajouté : Zigbee.setRebootOpenNetwork(60) afin qu’à chaque boot, il ouvre la connexion pour faciliter l’appareillage.
    Pour finir, j’ai laissé la fonction de reset sur un appui de plus de 3 secondes sur le bouton de l’esp.

5 - J’ai compilé et téléversé à mon Esp32-h2 connecté au COM6 de mon pc en usb.
A la fin j’ai débranché l’usb.

6 - Je suis allé dans home assistant et dans l’onglet zigbee2mqtt.
J’ai lancé l’appareillage, puis j’ai connecté l’usb pour alimenter mon esp32-h2.
Zigbee2mqtt à reconnu directement l’objet et j’ai vérifier dans expose qu’il y avait bien une remonté d’information.
J’ai aussi vérifier dans la fenetre Moniteur serie d’Arduino ce qui était emis.

Une fois tout bien connecté, nous avons donc une mesure de distance en cm qui est envoyé à zigbee2mqtt comme une température en °C.

7 - J’ai créé un template dans la configuration de home assistant :

template:
  - sensor:
      # ---- Mesure cuve Zigbee ----
      - name: "Hauteur d'eau cuve"
        unit_of_measurement: "cm"
        state: "{{ states('sensor.0x744dbdfffe63e790_temperature') }}"

      - name: "Volume d'eau cuve"
        unit_of_measurement: "L"
        state: >
          {% set hauteur = states('sensor.0x744dbdfffe63e790_temperature') | float(0) %}
          {% set hauteur_max = 94 %}
          {% set volume_max = 930.6 %}
          {% if hauteur > 0 and hauteur <= hauteur_max %}
            {{ (volume_max * (hauteur / hauteur_max)) | round(1) }}
          {% else %}
            0
          {% endif %}

      - name: "Niveau d'eau cuve"
        unit_of_measurement: "%"
        state: >
          {% set hauteur = states('sensor.0x744dbdfffe63e790_temperature') | float(0) %}
          {% set hauteur_max = 94 %}
          {% if hauteur > 0 and hauteur <= hauteur_max %}
            {{ ((hauteur / hauteur_max) * 100) | round(1) }}
          {% else %}
            0
          {% endif %}

Cela permet d’avoir 3 sensors :

  • Hauteur d’eau cuve : récupère l’information de mqtt et zigbee2mqtt et transforme le °C en cm
  • Volume d’eau en L, converti la distance en l avec les dimensions de la cuve
  • Niveau d’eau cuve : permet de connaitre le pourcentage de remplissage.

8 - J’ai créer une card lovelace :

type: glance
title: Cuve d'eau
entities:
  - entity: sensor.hauteur_d_eau_cuve
    name: Hauteur (cm)
    icon: mdi:water
  - entity: sensor.volume_d_eau_cuve
    name: Volume (L)
    icon: mdi:cup-water
  - entity: sensor.niveau_d_eau_cuve
    name: Niveau (%)
    icon: mdi:gauge

Ce qui permet d’afficher les informations sur un tableau de bord :

J’espère que cela vous aidera.

Maintenant je vais essayer de faire un fichier ino propre sans détourner un cluster.
Je vous tiendrais au courant.

2 « J'aime »