[DefiDIY25] Chatière connectée avec détection des chats via leur puce RFID implantée

Bonjour,

Je partage avec vous mon projet de chatière connectée, entièrement intégrée à Home Assistant, qui me permet de savoir à tout moment si mes chats (Kiwi et Maya) sont à l’intérieur ou à l’extérieur de la maison. Le système est basé sur la lecture de leur puce électronique vétérinaire implantée “FDX-B”.

Ma motivation pour ce projet

Mes deux chats sont libres de rentrer ou sortir de la maison comme ils le souhaitent, via une chatière. Lorsque je suis absent ou en vacances, je veux m’assurer que les chats sont là et savoir qui est à la maison et qui est dehors. Je ne veux pas utiliser un collier avec tag RFID, car mes chats sortent souvent, et je trouve que le risque d’étranglement est réel.
Il y a quelques années, j’avais acheté une chatière qui lit les puces des chats “Sureflap”, mais j’ai du la retourner car la puce de l’un de mes deux chats n’était presque jamais détectée, certainement car elle trop profonde sous la peau. De plus le bruit qu’elle faisait leur faisait peur. J’ai aussi essayé de détecter l’entrée et la sortie des chats en analysant des images de caméra par IA mais la nuit ca ne fonctionne pas (la nuit, tous les chats sont gris :slight_smile: )

Après plusieurs semaines de réflexion et de recherches infructueuses en ligne, j’ai finalement réussi à concevoir ma propre chatière intelligente.

Matériel nécessaire

Voici la liste complète du matériel que j’ai utilisé pour ce projet :

Ce que j’ai du acheter :

  • Module RFID WL134 (FDX-B / EM4100 / ISO11784/11785)
    C’est le cœur du système : il lit directement les puces de vos chats. L’antenne est fournie avec le module. J’ai essayé de fabriquer une antenne plus large pour entourer toute la chatière, mais cela ne marche pas : la sensibilité chute trop.
    Le meilleur résultat est obtenu en collant l’antenne incluse sur la porte intérieure de la chatière.

  • Alimentation linéaire 6 V DC
    Quand je l’ai achetée elle était moins chère… Peut être il vous faut trouver une autre référence, mais évitez absolument les alimentations à découpage premier prix (j’y reviens plus tard)

  • Convertisseur DC-DC
    Pour convertir le 6V en 5V et alimenter le microcontrôleur (ESP32) à partir de la même source. On évite ainsi les multi blocs de prises.

  • Module ESP32
    Pour envoyer les données du lecteur RFID vers Home Assistant via MQTT.

  • Adaptateur DC 5.5 x 2.1 mm, bien que j’aurais pu couper le câble et directement faire mes soudures mais c’est moins joli

Ce que j’avais déjà mais dont vous aurez besoin :

  • Deux détecteurs de mouvement par exemple Aqara

  • des câbles, type Dupont au autre mais pas trop épais, et deux fiches wago

  • un fer à souder et de l’étain

  • un multimètre

  • deux petits boitiers

  • optionnel : quelques puces RFID pour tester parce qu’à un moment le chat n’était plus trop coopératif lors de mes tests et en avait marre de mon antenne :slight_smile:

Expériences et erreurs à éviter

L’alimentation du module WL-134 est la clé de la réussite, car elle est très sensible aux interférences. Au départ, j’ai essayé de l’alimenter avec un bloc 9 V classique, couplé à des résistances et transistors pour réguler la tension. Résultat : un week-end entier de perdu, la lecture était trop aléatoire.

J’ai demandé une documentation au vendeur du module, que je mets à disposition sur mon serveur. Il explique qu’il faut une alimentation linéaire 6 V stable délivrant environ 80 mA pour que le WL-134 lise correctement les puces FDX-B. Le document nous apprend aussi qu’on peut alimenter le module avec une batterie Li-ion (entre 5V et 9V) et que dans ce cas les interférences sont quasi nulles. Je n’ai personnellement pas essayé la méthode de la batterie parce que mon automatisation doit fonctionner en permanence.
Il existe peut être de meilleures méthodes pour alimenter le module, ou des alims moins cher qui n’enverront pas d’interférences, mais mes connaissances sont limitées. Vous pouvez toujours étudier le fichier joint et décider de l’alimentation pour laquelle vous allez opter.

Schéma de branchement

Un schéma est toujours plus parlant que des mots, voici les soudures ou branchements à effectuer :

A l’intérieur du boitier contenant le LM2596 et l’ESP32 :

Mise en situation (je dois encore améliorer l’esthétique) :

Un autre capteur de mouvement est aussi de l’autre côté de la porte, à l’extérieur .

Quelques informations :

  • si possible souder l’antenne directement sur le WL-134. J’ai ajouter deux petites rallonges (le câble doit être identique et avoir la même longueur). Mais il faut éviter des rallonges de fils à cet endroit, car cela fait baisser la résonance du module
  • les LM2596, ESP32, eux, peuvent être légèrement déportés par rapport au WL-134
  • bien relier le RX2 du ESP32 sur la borne 3.3vTX du WL-134 et pas sur la borne 5vTX sinon c’est l’assurance de griller l’ESP32

Calibrage du WL-134

Le WL-134 doit être calibré pour que l’antenne soit en parfaite résonance.


Le module a 5 cavaliers J1 à J5 qui règlent la capacité de résonance de l’antenne. Si vous achetez la même alimentation que la mienne, vous pouvez ignorer cette étape. Vérifiez juste que les cavaliers J4 et J5 sont reliés (soudés) et que les cavaliers J1 à J3 sont libres (dessoudés), car j’ai remarqué que c’est la meilleure configuration avec cette alim.

Si vous utilisez une autre alimentation ou voulez mieux régler la résonance de l’antenne, voici comment procéder. Un WL-134 bien réglé consomme environ 80 mA à 6 V et lit les puces à plusieurs centimètres. Un mauvais réglage se traduit par une consommation beaucoup plus faible (< 50 mA) et une portée réduite.

  1. Coupez l’alimentation. On garde les branchements précédents (on peut déconnecter l’esp32 par précaution), mais on va insérer le multimètre en série sur le fil +6V, afin qu’il devienne un « pont » entre l’alim et le module WL-134.
    Multimètre réglé sur mA DC. Borne mA rouge du multimètre à la sortie +6V de l’alimentation et borne noire COM du multimètre sur l’entrée+5-9V du WL-134.
    Rebranchez l’alimentation

  2. Dessoudez tous les cavaliers (J1 à J5 ouverts). Notez le courant de départ (par ex. 55 mA)

  3. Remettez J5 (ressoudez) et observez si le courant augmente ou diminue. S’il augmente, gardez J5 : vous allez dans la bonne direction. S’il baisse, retirez le et passez à J4.

  4. Continuez ainsi (J4, puis J3, etc.) en testant chaque combinaison.

  5. L’objectif est d’obtenir la consommation la plus élevée possible (souvent entre 75 et 85 mA à 6 V). Si le courant dépasse 100 mA, c’est que la résonance est trop forte (capacité trop élevée) ; retirez alors un cavalier

Le code à injecter dans l’ESP32

L’ESP lit les trames envoyées par le WL-134 et les publie dans Home Assistant.

Dans Home Assistant, un sensor permet ensuite d’afficher le nom du chat détecté et de déclencher des automatisations (notifications Telegram, journal des passages, etc.).

Je ne vais pas étendre ce tuto au paramétrage d’un ESP32, mais basiquement, vous le branchez à votre PC, vous installez les drivers, et vous y injectez le code yaml ci-dessous. Le module complémentaire “ESPHome Device Builder” dans home assistant vous aidera aussi.

Collez le code ci-après, n’oubliez pas d’éditer le SSID wifi et le mot de passe en fonction de votre propre réseau. Branchez l’ESP32 et incluez le dans home assistant. Normalement il va apparaitre tout seul dans les paramètres des “appareils et services” et il suffira de l’ajouter à votre HA.

esphome:
  name: lecteur-chatiere
  on_boot:
    priority: -10
    then:
      - text_sensor.template.publish:
          id: rfid_chat
          state: ""

esp32:
  board: nodemcu-32s
  framework:
    type: arduino

wifi:
  ssid: "VOTRE RESEAU WIFI"
  password: "VOTRE MOT DE PASSE WIFI"
  power_save_mode: none

api:
  reboot_timeout: 0s

ota:
  platform: esphome

uart:
  id: rfid_uart
  rx_pin: GPIO16
  baud_rate: 9600
  data_bits: 8
  parity: NONE
  stop_bits: 2  

text_sensor:
  - platform: template
    name: "RFID UID"
    id: rfid_uid
    icon: "mdi:identifier"

interval:
  - interval: 100ms
    then:
      - lambda: |-
          static std::string buf;
          static uint32_t last = 0;

          // Lire tout ce qui arrive
          while (id(rfid_uart).available()) {
            uint8_t b; id(rfid_uart).read_byte(&b);
            buf.push_back((char)b);
            last = millis();
          }

          // Traiter après ~120 ms de silence
          if (!buf.empty() && (millis() - last) > 120) {
            // Chercher STX(0x02) ... ETX(0x03)
            auto stx = buf.find('\x02');
            auto etx = buf.rfind('\x03');
            if (stx != std::string::npos && etx != std::string::npos && etx > stx + 1) {
              // Charge utile entre STX et ETX, retirer tout après 0x7E s'il existe
              std::string payload = buf.substr(stx + 1, etx - (stx + 1));
              auto tilde = payload.find('\x7E');
              if (tilde != std::string::npos) payload = payload.substr(0, tilde);

              // On attend 26 caractères hex ASCII (ex: "5460D5C3D3483011000000000000")
              if (payload.size() >= 16) {
                // Prendre les 16 premiers caractères = UID (8 octets)
                std::string uid_hex = payload.substr(0, 16);

                // Anti-doublon 5 s
                static std::string last_uid;
                static uint32_t last_uid_ms = 0;
                if (uid_hex != last_uid || (millis() - last_uid_ms) > 5000) {
                  id(rfid_uid).publish_state(uid_hex.c_str());
                  last_uid = uid_hex;
                  last_uid_ms = millis();
                }
              }
            }
            buf.clear();
          }

Faites passer une puce RFID type FDX-B (ou votre chat) devant l’antenne. Une LED sur le WL-134 devient bleue si la puce est détectée.

Regardez dans home assistant les caractères qui sont lus. Vous devriez voir un code d’identification au format UID hex, composé de 16 caractères

Une fois que vous avez les codes de tous vos animaux, vous pouvez demander à home assistant de noter le nom du chat plutôt que le code hex. C’est plus explicite que de lire un numéro. Pour cela, remplacez le code par celui ci-dessous.
Vous devez à nouveau éditer les paramètres wifi et surtout éditer la partie vers la fin du yaml en éditant le nom de vos chats (exemple name = “Kiwi”) avec leurs identifiants correspondants.

esphome:
  name: lecteur-chatiere
  on_boot:
    priority: -10
    then:
      - text_sensor.template.publish:
          id: rfid_chat
          state: ""

esp32:
  board: nodemcu-32s
  framework:
    type: arduino

wifi:
  ssid: "VOTRE SSID WIFI"
  password: "VOTRE MOT DE PASSE WIFI"
  power_save_mode: none

api:
  reboot_timeout: 0s

ota:
  platform: esphome

uart:
  id: rfid_uart
  rx_pin: GPIO16
  baud_rate: 9600
  data_bits: 8
  parity: NONE
  stop_bits: 2 

text_sensor:
  - platform: template
    name: "RFID Chat"
    id: rfid_chat
    icon: "mdi:cat"

script:
  - id: clear_chat
    then:
      - delay: 2s
      - text_sensor.template.publish:
          id: rfid_chat
          state: ""     # reset after showing the name

interval:
  - interval: 100ms
    then:
      - lambda: |-
          static std::string buf;
          static uint32_t last_rx_ms = 0;

          // Cooldown (ignore le même UID pendant N ms)
          const uint32_t COOLDOWN_MS = 180000; // 3 minutes

          while (id(rfid_uart).available()) {
            uint8_t b; id(rfid_uart).read_byte(&b);
            buf.push_back((char)b);
            last_rx_ms = millis();
          }

          if (!buf.empty() && (millis() - last_rx_ms) > 120) {
            auto stx = buf.find('\x02');
            auto etx = buf.rfind('\x03');
            if (stx != std::string::npos && etx != std::string::npos && etx > stx + 1) {
              std::string payload = buf.substr(stx + 1, etx - (stx + 1));
              auto tilde = payload.find('\x7E');
              if (tilde != std::string::npos) payload = payload.substr(0, tilde);

              if (payload.size() >= 16) {
                std::string uid = payload.substr(0, 16);
                std::string name;

                if (uid == "AA22334455661122") name = "Kiwi";
                else if (uid == "AABBCCDDEEFFAABB") name = "Maya";
			//	else if (uid == "AABBDDCC11223344") name = "Chat 3";
                else name = "Inconnu (" + uid + ")";

                // Mémoire du dernier UID publié + horodatage
                struct Seen { std::string u; uint32_t t; };
                static Seen last_seen = {"", 0};
                const uint32_t now = millis();

                const bool is_new_uid = (uid != last_seen.u);
                const bool cooldown_over = (now - last_seen.t) > COOLDOWN_MS;

                if (is_new_uid || cooldown_over) {
                  id(rfid_chat).publish_state(name.c_str());  // publie une fois
                  id(clear_chat).execute();                   // efface après 2 s 
                  last_seen = {uid, now};
                  ESP_LOGI("rfid", "Publish: %s", name.c_str());
                } else {
                  ESP_LOGD("rfid", "Duplicate ignored: %s", name.c_str());
                }
              }
            }
            buf.clear();
          }

Le script lit un RFID puis 2 secondes après il envoie un code vide. Ainsi, quand le même chat repasse par la chatière peu de temps après, son identification sera bien renvoyée vers home assistant.

Intégration dans Home Assistant

On en vient enfin à ce qui nous intéresse. Une entité va être créée automatiquement via l’intégration ESP32 : sensor.lecteur_chatiere_rfid_chat.

On pourrait déjà créer des automatisations avec ce sensor mais ce n’est pas suffisant, puisque je veux savoir si les chats rentrent ou sortent.

Je décide donc de placer un capteur de mouvement au raz du sol, près de la chatière à l’intérieur de la maison (binary_sensor.chatiere_interieur_occupancy), et un autre capteur de mouvement au même niveau, mais à l’extérieur de la maison (binary_sensor.chatiere_exterieur_occupancy). L’automatisation regardera quel capteur de mouvement s’est déclenché en premier pour en déduire le sens de passage.

On commence par créer des helpers dans paramètres > appareils & services > entrées :
input_boolean.kiwi_home (entrée de type interrupteur)
input_boolean.maya_home (entrée de type interrupteur)

On créé aussi des capteurs de présence pour que home assistant puisse afficher clairement “Maison” ou “Absent” quand le chat est là ou non.
Dans configuration.yaml :

template:
  - binary_sensor:
      - name: "Kiwi présent"
        unique_id: kiwi_present
        device_class: presence
        state: "{{ is_state('input_boolean.kiwi_home','on') }}"
      - name: "Maya présente"
        unique_id: maya_presente
        device_class: presence
        state: "{{ is_state('input_boolean.maya_home','on') }}"

Et voici l’automatisation principale, celle qui va décider le sens de la chatière.

alias: "Chatière - lecture RFID et Sens "
triggers:
  - id: ext_first
    entity_id: binary_sensor.chatiere_exterieur_occupancy
    to: "on"
    trigger: state
  - id: int_first
    entity_id: binary_sensor.chatiere_interieur_occupancy
    to: "on"
    trigger: state
variables:
  WINDOW_RFID: 60
  WINDOW_OTHER: 60
actions:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ trigger.id == 'ext_first' }}"
        sequence:
          - variables:
              t_first: "{{ as_timestamp(now()) }}"
              int_was_on_before: |-
                {{ is_state('binary_sensor.chatiere_interieur_occupancy','on')
                   and as_timestamp(states.binary_sensor.chatiere_interieur_occupancy.last_changed) < t_first }}
          - condition: template
            value_template: "{{ not int_was_on_before }}"
          - wait_for_trigger:
              - entity_id: sensor.lecteur_chatiere_rfid_chat
                to: Kiwi
                id: rfid_kiwi
                trigger: state
              - entity_id: sensor.lecteur_chatiere_rfid_chat
                to: Maya
                id: rfid_maya
                trigger: state
            timeout: 00:00:{{ WINDOW_RFID }}
            continue_on_timeout: false
          - variables:
              who: >-
                {% if wait.trigger and wait.trigger.id == 'rfid_kiwi' %}Kiwi {%
                elif wait.trigger and wait.trigger.id == 'rfid_maya' %}Maya {%
                else %}Unknown{% endif %}
              t_rfid: "{{ as_timestamp(wait.trigger.to_state.last_changed) }}"
          - wait_template: >
              {{
              as_timestamp(states.binary_sensor.chatiere_interieur_occupancy.last_changed)
              > t_first }}
            timeout: 00:00:{{ WINDOW_OTHER }}
            continue_on_timeout: true
          - choose:
              - conditions:
                  - condition: template
                    value_template: "{{ wait.completed }}"
                sequence:
                  - choose:
                      - conditions:
                          - condition: template
                            value_template: "{{ who == 'Kiwi' }}"
                        sequence:
                          - target:
                              entity_id: input_boolean.kiwi_home
                            action: input_boolean.turn_on
                          - data:
                              message: Kiwi est entré.
                            action: notify.telegram
                      - conditions:
                          - condition: template
                            value_template: "{{ who == 'Maya' }}"
                        sequence:
                          - target:
                              entity_id: input_boolean.maya_home
                            action: input_boolean.turn_on
                          - data:
                              message: Maya est entrée.
                            action: notify.telegram
            default:
              - data:
                  message: >-
                    {{ who }} détecté(e), sens incertain
                action: notify.telegram
      - conditions:
          - condition: template
            value_template: "{{ trigger.id == 'int_first' }}"
        sequence:
          - variables:
              t_first: "{{ as_timestamp(now()) }}"
              ext_was_on_before: |-
                {{ is_state('binary_sensor.chatiere_exterieur_occupancy','on')
                   and as_timestamp(states.binary_sensor.chatiere_exterieur_occupancy.last_changed) < t_first }}
          - condition: template
            value_template: "{{ not ext_was_on_before }}"
          - wait_for_trigger:
              - entity_id: sensor.lecteur_chatiere_rfid_chat
                to: Kiwi
                id: rfid_kiwi
                trigger: state
              - entity_id: sensor.lecteur_chatiere_rfid_chat
                to: Maya
                id: rfid_maya
                trigger: state
            timeout: 00:00:{{ WINDOW_RFID }}
            continue_on_timeout: false
          - variables:
              who: >-
                {% if wait.trigger and wait.trigger.id == 'rfid_kiwi' %}Kiwi {%
                elif wait.trigger and wait.trigger.id == 'rfid_maya' %}Maya {%
                else %}Unknown{% endif %}
              t_rfid: "{{ as_timestamp(wait.trigger.to_state.last_changed) }}"
          - wait_template: >
              {{
              as_timestamp(states.binary_sensor.chatiere_exterieur_occupancy.last_changed)
              > t_first }}
            timeout: 00:00:{{ WINDOW_OTHER }}
            continue_on_timeout: true
          - choose:
              - conditions:
                  - condition: template
                    value_template: "{{ wait.completed }}"
                sequence:
                  - choose:
                      - conditions:
                          - condition: template
                            value_template: "{{ who == 'Kiwi' }}"
                        sequence:
                          - target:
                              entity_id: input_boolean.kiwi_home
                            action: input_boolean.turn_off
                          - data:
                              message: Kiwi est sorti.
                            action: notify.telegram
                      - conditions:
                          - condition: template
                            value_template: "{{ who == 'Maya' }}"
                        sequence:
                          - target:
                              entity_id: input_boolean.maya_home
                            action: input_boolean.turn_off
                          - data:
                              message: Maya est sortie.
                            action: notify.telegram
            default:
              - data:
                  message: >-
                    {{ who }} détecté(e), sens incertain
                action: notify.telegram
mode: parallel
max: 2


Explications :
Sortie : capteur intérieur → RFID → capteur extérieur
Entrée : capteur extérieur → RFID → capteur intérieur
Le code attend une séquence complète dans un délai de 2 x 60 secondes (éditable) pour confirmer le sens. Il identifie quel chat passe (Maya ou Kiwi) grâce au RFID, met à jour leur statut présence, et envoie une notification Telegram.

Affichage dans Home Assistant Lovelace

Je place mes deux photos dans config/www/

www/kiwi.jpg et www/maya.jpg

type: horizontal-stack
cards:
  - type: vertical-stack
    cards:
      - show_state: false
        show_name: true
        type: picture-entity
        entity: input_boolean.kiwi_home
        name: Kiwi
        image: /local/kiwi.jpg
      - type: conditional
        conditions:
          - entity: input_boolean.kiwi_home
            state: "on"
        card:
          type: markdown
          content: >
            <ha-icon icon="mdi:home"></ha-icon> **PRÉSENT**
            Depuis: {{ relative_time(states.input_boolean.kiwi_home.last_changed) }}
      - type: conditional
        conditions:
          - entity: input_boolean.kiwi_home
            state: "off"
        card:
          type: markdown
          content: >
            <ha-icon icon="mdi:home-export-outline"></ha-icon> **ABSENT**
            Depuis: {{ relative_time(states.input_boolean.kiwi_home.last_changed) }}
            
  - type: vertical-stack
    cards:
      - type: picture-entity
        entity: input_boolean.maya_home
        name: Maya
        image: /local/maya.jpg
        show_state: false
      - type: conditional
        conditions:
          - entity: input_boolean.maya_home
            state: "on"
        card:
          type: markdown
          content: >
            <ha-icon icon="mdi:home"></ha-icon> **PRÉSENT**

            Depuis: {{ relative_time(states.input_boolean.maya_home.last_changed) }}
      - type: conditional
        conditions:
          - entity: input_boolean.maya_home
            state: "off"
        card:
          type: markdown
          content: >
            <ha-icon icon="mdi:home-export-outline"></ha-icon> **ABSENT**
            Depuis: {{ relative_time(states.input_boolean.maya_home.last_changed) }}


Ce qui donne ce résultat visuel, avec mes deux stars de ce post :

Evolutions possibles :

  • Je remarque que la détection de mouvement n’est pas toujours efficace. J’envisage de placer plutôt des faisceaux infrarouges TCRT5000 que le chat coupe avant de passer la chatière. A voir si je peux les relier au même ESP32.
  • On pourrait aussi envisager une chatière qui ne s’ouvre que si le code RFID d’un de mes chats est lu, mais je n’ai personnellement pas cette utilité . Mon chat est le caïd du coin, personne ne vient chez lui :rofl: (le tigré avec les yeux sournois)
16 « J'aime »

Superbe projet.

Ça me donne envie de rester le même genre de système pour les gamelles, chez nous c’est un des chats qui vide tout.

Par contre pour le détection du sens de passage tu pourrais utiliser un accéléromètre pour voir le sens de mouvement de la porte, comme ça tu sais si ça rentre ou si ça sort

1 « J'aime »

Pour les distributeurs de croquettes , cela existe déjà en tout fait je sais qu’il en existe chez Xiaomi ou Aqara
https://www.domadoo.fr/fr/peripheriques/6414-aqara-distributeur-de-croquettes-connecte-c1-zigbee-30-zncwwsq01lm-6970504218178.html

le truc c’est que celle qui à la nourriture à volonté n’est pas pucée, sinon en effet je serais parti sur les gamelles toute faite.

Et tu veux l’identifier comment même en diy ?
Je me demande si la Xiaomi n’a pas une reconnaissance ‹ ‹ faciale › ›

Sinon faire pucer ton chat

Super bonne idée l’accelerometre / gyroscope, je n’y avais pas du tout pensé. Je vais me pencher la dessus, mais ça va rajouter des câbles sur la porte, ça commence à en faire beaucoup. Sinon un système d’aimants

reprend ce que j’ai fait l’an dernier avec frigate couplé au distributeur

tu rajoute genai dans frigate et tu lui demande de reconnaître la couleur du chat pour les distinguer et après tu gères en fonction. en plus la maj de l’intégration remonte les infos générées par ia maintenant.

ca fait 2 ans que je gère la bouffe de mon chat avec ça et ça marche impec

exemple avec reconnaissance d’une voiture :

En fait je laisse la gamelle ouverte.

Mais quand je détecte l’autre une puce je ferme la gamelle

J’ai fait une petite mise à jour sur les conseils de @Guesn : très bonne idée l’accéléromètre. J’ai commandé ce module basé sur MPU6050. J’ai réarrangé la chatière pour faire quelque chose d’un peu plus propre. J’ai soudé l’antenne directement sur le module RFID sans passer par une rallonge câble. Depuis 2 jours à chaque fois le chat est détecté, même Maya qui avait du mal à se faire reconnaitre par la chatière Sureflap.

L’accéléromètre est collé en haut de la chatière, sens horizontal, soudures vers le haut et led à l’extérieur. 4 soudures à faire :

  • 3V3 ESP32 vers VCC MPU6050
  • GND ESP32 vers GND MPU6050
  • GPIO22 ESP32 vers SCL MPU6050
  • GPIO21 ESP32 vers SDA MPU6050

Dans ce cas le code à injecter au ESP32 devient :

esphome:
  name: lecteur-chatiere
  on_boot:
    priority: -10
    then:
      - text_sensor.template.publish:
          id: rfid_chat
          state: ""
      - text_sensor.template.publish:
          id: sens_chatiere
          state: "Fermée"

esp32:
  board: nodemcu-32s
  framework:
    type: arduino

wifi:
  ssid: "VOTRE SSID WIFI"
  password: "votre_mot_de_passe_wifi"
  power_save_mode: none

logger:
  level: INFO
  baud_rate: 0
  logs:
    sensor: WARN
    text_sensor: WARN
    component: ERROR

api:
  reboot_timeout: 0s

ota:
  platform: esphome

# Configuration I2C pour le MPU-6050
i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true
  id: bus_a

# Configuration UART pour le lecteur RFID
uart:
  id: rfid_uart
  rx_pin: GPIO16
  baud_rate: 9600
  data_bits: 8
  parity: NONE
  stop_bits: 2

# Capteur MPU-6050
sensor:
  - platform: mpu6050
    address: 0x68
    i2c_id: bus_a
    update_interval: 100ms
    accel_x:
      name: "Chatière Accel X"
      id: accel_x
      internal: true
      filters:
        - sliding_window_moving_average:
            window_size: 5
            send_every: 1
    accel_y:
      name: "Chatière Accel Y"
      id: accel_y
      internal: true
      filters:
        - sliding_window_moving_average:
            window_size: 5
            send_every: 1
    accel_z:
      name: "Chatière Accel Z"
      id: accel_z
      internal: true
      filters:
        - sliding_window_moving_average:
            window_size: 5
            send_every: 1
    gyro_x:
      name: "Chatière Gyro X"
      id: gyro_x
      internal: true
    gyro_y:
      name: "Chatière Gyro Y"
      id: gyro_y
      internal: true
    gyro_z:
      name: "Chatière Gyro Z"
      id: gyro_z
      internal: true
    temperature:
      name: "Température MPU-6050"
      id: mpu_temp
      internal: true

  # Accel Z (optionnel, pour monitoring)
  - platform: template
    name: "Accel Z Chatière"
    id: accel_z_monitor
    unit_of_measurement: "m/s²"
    accuracy_decimals: 1
    update_interval: 1s
    internal: true
    lambda: |-
      return id(accel_z).state;

# Capteurs texte
text_sensor:
  # Capteur RFID (comme avant)
  - platform: template
    name: "RFID Chat"
    id: rfid_chat
    icon: "mdi:cat"
  
  # Nouveau capteur pour le sens de la chatière
  - platform: template
    name: "Sens Chatière"
    id: sens_chatiere
    icon: "mdi:door"
    # Valeurs possibles: "Fermée", "Entrée", "Sortie"

# Scripts
# Scripts
script:
  - id: clear_chat
    then:
      - delay: 2s
      - text_sensor.template.publish:
          id: rfid_chat
          state: ""

  - id: detect_direction
    mode: restart
    then:
      - lambda: |-
          static uint32_t last_change_ms = 0;
          static std::string current_state = "Fermée";
          static std::string last_published = "";
          static uint32_t state_locked_until = 0;
          
          float az = id(accel_z).state;
          uint32_t now = millis();
          
          const float THRESHOLD_SORTIE = -4.0;
          const float THRESHOLD_ENTREE = 4.0;
          const float THRESHOLD_FERME = 2.0;
          const uint32_t DEBOUNCE_MS = 400;
          const uint32_t STATE_LOCK_MS = 3000;
          
          bool state_changed = false;
          std::string new_state = current_state;
          
          if (now < state_locked_until) {
            return;
          }
          
          // Détection SORTIE (Z < -4.0)
          if (current_state == "Fermée" && az < THRESHOLD_SORTIE && 
              (now - last_change_ms) > DEBOUNCE_MS) {
            new_state = "Sortie";
            state_changed = true;
            ESP_LOGI("chatiere", "SORTIE (Z=%.1f)", az);
          }
          // Détection ENTRÉE (Z > 4.0)
          else if (current_state == "Fermée" && az > THRESHOLD_ENTREE && 
                   (now - last_change_ms) > DEBOUNCE_MS) {
            new_state = "Entrée";
            state_changed = true;
            ESP_LOGI("chatiere", "ENTRÉE (Z=%.1f)", az);
          }
          // Retour FERMÉE (Z entre -2.0 et 2.0)
          else if ((current_state == "Entrée" || current_state == "Sortie") && 
                   az > -THRESHOLD_FERME && az < THRESHOLD_FERME && 
                   (now - last_change_ms) > DEBOUNCE_MS) {
            new_state = "Fermée";
            state_changed = true;
            ESP_LOGI("chatiere", "✓ FERMÉE (Z=%.1f)", az);
          }
          
          if (state_changed && new_state != last_published) {
            current_state = new_state;
            last_published = new_state;
            id(sens_chatiere).publish_state(new_state.c_str());
            last_change_ms = now;
            state_locked_until = now + STATE_LOCK_MS;
            ESP_LOGI("chatiere", "✓ Publié: %s | Verrouillé pour 3s", new_state.c_str());
          }


# Intervalles
interval:
  # Détection RFID
  - interval: 100ms
    then:
      - lambda: |-
          static std::string buf;
          static uint32_t last_rx_ms = 0;
          const uint32_t COOLDOWN_MS = 60000; // 1 minute evite double detection rfid
          
          while (id(rfid_uart).available()) {
            uint8_t b; 
            id(rfid_uart).read_byte(&b);
            buf.push_back((char)b);
            last_rx_ms = millis();
          }
          
          if (!buf.empty() && (millis() - last_rx_ms) > 120) {
            auto stx = buf.find('\x02');
            auto etx = buf.rfind('\x03');
            
            if (stx != std::string::npos && etx != std::string::npos && etx > stx + 1) {
              std::string payload = buf.substr(stx + 1, etx - (stx + 1));
              auto tilde = payload.find('\x7E');
              if (tilde != std::string::npos) payload = payload.substr(0, tilde);
              
              if (payload.size() >= 16) {
                std::string uid = payload.substr(0, 16);
                std::string name;
                
                if (uid == "AAAAAAAAAAAAAAAA") name = "Kiwi";
                else if (uid == "BBBBBBBBBBBBBBBB") name = "Maya";
                else name = "Inconnu (" + uid + ")";
                
                struct Seen { std::string u; uint32_t t; };
                static Seen last_seen = {"", 0};
                const uint32_t now = millis();
                const bool is_new_uid = (uid != last_seen.u);
                const bool cooldown_over = (now - last_seen.t) > COOLDOWN_MS;
                
                if (is_new_uid || cooldown_over) {
                  id(rfid_chat).publish_state(name.c_str());
                  id(clear_chat).execute();
                  last_seen = {uid, now};
                  ESP_LOGI("rfid", "Publish: %s", name.c_str());
                } else {
                  ESP_LOGD("rfid", "Duplicate ignored: %s", name.c_str());
                }
              }
            }
            buf.clear();
          }

  # Détection direction chatière (réduit à 500ms)
  - interval: 500ms
    then:
      - script.execute: detect_direction

Il y a des garde fous pour éviter de multiple détection ouverture/fermeture quand la chatière rebondit (quand ils entrent super vite).

1 « J'aime »

Screuneugneu que c’est tentant…
Un projet de vacances d’hiver a venir ça.

Merci pour le partage :+1:.

Ps:
Avec plus qu’un seul chat ça sera plus facile de savoir s’il est dedans ou dehors.

1 « J'aime »