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
)
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

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.
-
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 -
Dessoudez tous les cavaliers (J1 à J5 ouverts). Notez le courant de départ (par ex. 55 mA)
-
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.
-
Continuez ainsi (J4, puis J3, etc.) en testant chaque combinaison.
-
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
(le tigré avec les yeux sournois)







