M-Bus (aussi appelé Meter-Bus) est un standard datant des années '90, destiné au relevé de compteurs d'eau et d'électricité. Une variante wireless qui utilise le même codage des données a été définie ultérieurement, mais pour cet article nous allons nous intéresser à la version d'origine, qui permet d'alimenter et de relever jusqu'à 250 compteurs sur 1 km, reliés par une simple paire de fils torsadés.
Objectif du projet
Notre objectif est de mettre en place une solution pérenne pour mesurer différents points de consommation d'eau dans un foyer, afin de pouvoir
- mesurer leur importance relative pour mieux cibler les mesures d'économie;
- détecter les fuites éventuelles rapidement et efficacement.
Il y a six ans, nous avions montré comment interfacer un compteur d'eau à impulsion. Mais ce type de compteur ne fournit pas une valeur absolue de la consommation totale, et est donc sujet à des erreurs en cas de coupure de courant.
Au contraire, un compteur M-Bus intègre une batterie qui lui permet de fournir un relevé numérique absolu de la consommation totale, même après une coupure d'alimentation électrique. C'est donc une solution plus fiable. Voici le compteur fourni par notre plombier:
Un compteur d'eau avec interface M-Bus
Il nous faut maintenant trouver comment raccorder ce compteur M-Bus à notre infrastructure MQTT, de sorte à ce qu'il soit disponible dans Home Assistant pour être surveillé en permanence.
M-Bus: principes de transmission
A la base, M-Bus est basé sur une simple transmission série synchrone, comme RS-485. Mais pour permettre aux capteurs de s'alimenter directement par le bus de donnée, sans électronique complexe, M-Bus a opté pour les conventions suivantes:
- Lorsqu'il ne transmet pas, le maître du bus fournit une tension constante de 36V (état "1").
- Pour transmettre un bit "0", le maître descend la tension à 24V.
- Lors qu'il ne transmet pas, un compteur doit consommer un courant constant.
- Pour transmettre un bit "1", un compteur augmentent sa consommation de 15mA(+-5mA).
Le maître transmet donc ses bits en modulant la tension, alors qu'un compteur transmet ses bits en modulant sa consommation. Le fait d'utiliser des tensions et des courants importants permet de garantir une bonne immunité aux interférences. Pour simplifier l'électronique, quelques règles supplémentaires s'appliquent:
- Un seul compteur transmet à la fois, et seulement lorsque le maître ne transmet pas.
- La charge maximale sur le bus est <400mA.
- La résistance max. du bus est <30 Ohm.
- Même à la charge maximale, chaque compteur est sûr de recevoir au moins 12V.
La vitesse de communication peut être configurée par l'installateur entre 300 baud et 9600 baud, selon la longueur du bus. M-Bus utilise 8 bits de données, une parité paire et un stop bit (8E1).
Raccorder un bus M-Bus à Home Assistant
Au vu de la description ci-dessus, on ne va pas pouvoir utiliser directement une interface RS-232 ou RS-485 pour communiquer avec des capteurs M-Bus: il faut une électronique dédiée.
Dans l'idéal nous aurions utilisé un Yocto-MBus, mais il n'existe pas: nous n'avons eu à ce jour qu'une demande en ce sens, il y a une dizaine d'années...
Nous aurions éventuellement pu utiliser le M-Bus Master Hat pour Raspberry Pi, mais d'une part ses capacités d'alimentation du bus sont très limitées, et d'autre part nous ne voulons pas complexifier le maintenance de notre système de domotique par l'ajout d'un mini-PC supplémentaire.
Nous avons donc choisi une option un peu plus chère, mais entièrement stateless: la passerelle RS232 vers M-Bus MM20-24, fabriquée par la société DECODE. Nous n'avons pas besoin de la variante Ethernet plus chère, car nous allons raccorder la passerelle directement à un Yocto-RS232, qui se chargera de l'interrogation périodique des compteurs:
Un Yocto-RS232 et une passerelle MM20-24 permettent d'intégrer M-Bus à l'écosystème Yoctopuce
A partir de là, l'intégration dans Home Assistant pourra se faire via MQTT comme pour l'intégration de la pompe IntelliFlo présentée l'année dernière.
Le chemin complet d'un compteur M-Bus vers Home Assistant
Cette solution de conversion rajoute quelques hops par rapport à ce que pourrait être une intégration native de M-Bus dans Home Assistant, mais elle a l'énorme avantage de faciliter grandement la maintenance du système et la traçabilité des données.
Mais pour que cela fonctionne, il nous faut encore configurer le Yocto-RS232 pour qu'il interroge les compteurs d'eau M-Bus et publie les mesures, de sorte à ce qu'elles soient automatiquement injectées dans l'infrastructure MQTT.
Configuration du Yocto-RS232
La première étape consiste à vérifier que l'on peut communiquer avec les compteurs. Nous allons donc commencer par envoyer des messages codés à la main avec l'interface de test du Yocto-RS232.
Selon le standard M-Bus, tous les compteurs M-Bus doivent répondre au message appelé SND_NKE, qui correspond à un ping. Il est formé de 5 octets, donnés en hexadécimal ci-dessous:
10 | code "start", présent au début de chaque message envoyé |
40 | octet de contrôle correspondant à la commande SND_NKE |
00 | adresse du compteur à qui le message est destiné |
40 | somme de contrôle = (0x40 + 0x00) modulo 0x100 |
16 | code "stop", présent à la fin de chaque message envoyé |
La réponse attendue est l'octet E5.
Test de connectivité M-Bus avec un Yocto-RS232
Pour que cela fonctionne, il faut que vous ayez configuré correctement votre Yocto-RS232. Le standard nous dit qu'il s'agit d'un protocole de messages binaires (frame-based binary protocol), transmis en utilisant huit bits de données, un bit de parité paire et un stop bit (8E1). Il est aussi précisé qu'à la sortie d'usine, un compteur devrait répondre à l'adresse 00. Par contre il n'est pas précisé quelle est la vitesse de communication par défaut, donc il faut faire quelques essais entre 300 baud et 9600 baud. Notre capteur nous a répondu lorsque qu'on lui a envoyé le SND_NKE à 2400 baud. Voici donc la configuration de notre Yocto-RS232:
Configuration du Yocto-RS232 pour communiquer en M-Bus
Pour le cas où votre compteur ne répondrait jamais, quelque soit la vitesse de communication, vous pouvez essayer d'utiliser l'adresse FE: elle correspond à un broadcast auquel tous les compteurs doivent répondre, quelle que soit leur adresse. Si on y ajoute la somme de contrôle, le message à envoyer est 10 40 FE 3E 16.
Test d'un relevé de compteur
Pour demander un relevé du compteur 00, il faut envoyer le message REQ_UD2, soit:
10 | code "start", présent au début de chaque message envoyé |
7B | octet de contrôle correspondant à la commande REQ_UD2 |
00 | adresse du compteur à qui le message est destiné |
7B | somme de contrôle = (0x7B + 0x00) modulo 0x100 |
16 | code "stop", présent à la fin de chaque message envoyé |
Le format exact de la réponse est un peu plus compliqué, et il va varier selon le type de compteur. Mais il devrait respecter la structure décrite ci-dessous:
partie fixe | |
---|---|
68 C4 C4 68 | entête RSP_UD, longueur=C4 (dans notre cas) |
08 | octet de contrôle correspondant à une réponse RSP_UD |
00 | adresse du compteur qui répond |
72 | info supplémentaire: 72 = réponse de taille variable |
23 42 27 00 | numéro de série du compteur (format BCD): 00274223 |
47 5F | identification du fabricant = 5F47 |
03 | version du matériel: 03 |
07 | type de compteur: 07 = compteur d'eau |
44 40 00 00 | code d'accès, statut, signature |
data block 1 | |
04 | variable 0, stockée sur 4 octets (32 bit) |
13 | unité de mesure: code 13 = 1 litre |
E1 28 00 00 | 0x28E1 = 10'465 litres |
data block 2 | |
04 | variable 0, stockée sur 4 octets (32 bit) |
6D | unité de mesure: timestamp |
2B 11 1A 36 | 0x361a112b = 2024/06/26 19:49 |
etc. |
Si nécessaire, vous trouverez tous les détails sur ces encodages mystérieux dans la spécification M-Bus, mais autant vous avertir, c'est assez indigeste. Elle a visiblement été rédigée par des automaticiens qui n'ont pas la même conception des structure de données que les informaticiens: c'est un mélange de représentation mélangeant inefficacement stockage et affichage (type BCD), de bits de contrôles avec des noms tous sauf explicites coincés partout là où c'est possible, et de codes arbitraires organisés sans logique apparente... Pour vous facilitez la tâche, vous trouverez:
- à la page 74, la table des types de compteurs
- aux pages 38-39, le codage du premier octet du data block, appelé le DIF
- aux pages 42 et 78, le codage de l'octet du data block qui indique l'unité, appelé le VIF
- à la page 41, l'explication des octets optionnels DIFE qui sont parfois insérés entre
le DIF et le VIF; on identifie leur présence au fait que le bit supérieur de l'octet
précédent est actif (valeur >= 0x80) - à la page 72, le format de représentation des dates et des timestamps sur 16 et 32 bits, qui illustre bien la remarque faite précédemment sur les formats de données de M-Bus...
Si vous êtes perdus, vous pouvez toujours envoyer un email à support@yoctopuce.com et on vous aidera.
Interrogation automatique
Une fois que vous avez vérifié que vous pouvez interroger votre compteur et que vous savez où trouver la réponse, vous êtes prêt à créer un job d'interrogation automatique dans le Yocto-RS232.
Si l'on se contente de remonter la valeur du compteur lui-même, le job peut se résumer à une tâche périodique de type custom protocol faite de deux commandes: l'envoi du message REQ_UD2, et la réception de la réponse:
writeHex | 107B007B16 |
expect | 68(WORD)68080072(DWORD)(DWORD)(DWORD)0413($1:DWORDL).* |
L'expression expect se comprend comme suit: à la réception d'un message respectant le format attendu, on affecte au genericSensor1 (noté $1) la valeur du mot 32 bit en little-endian (noté DWORDL) à la position donnée. Dans notre cas, le format attendu correspond à l'extraction de la donnée du data block 1.
Si l'on veut faire un peu plus subtil, on peut même calculer directement dans le job le débit par minute, de sorte à ce qu'il puisse aussi être publié via MQTT. Pour cela, on stocke la valeur du compteur dans une variable $prevVal, et on calcule la différence toutes les 60 secondes. Basé sur ce principe, voici le job que nous utilisons pour relever deux compteurs (adresses 01 et 02) en parallèle, et publier le débit (en l/min) et la relevé du compteur (en m^3):
Tâche Init, exécutée une seule fois: | |
---|---|
compute | $prevTime = 0 |
compute | $prevVal1 = 0 |
compute | $prevVal2 = 0 |
Tâche Measure, exécutée toutes les 500ms: | |
compute | $currTime = utctime() div 60 |
assert | $currTime != $prevTime |
writeHex | 107B017C16 |
expect | 68(WORD)68080172(DWORD)(DWORD)(DWORD)0413($currVal1:DWORDL).* |
writeHex | 107B027D16 |
expect | 68(WORD)68080272(DWORD)(DWORD)(DWORD)0413($currVal2:DWORDL).* |
compute | $6 = $currVal1 / 1000.0 |
compute | $7 = $currVal2 / 1000.0 |
compute | $delta1 = $currVal1 - $prevVal1 |
compute | $delta2 = $currVal2 - $prevVal2 |
compute | $prevTime = $currTime |
assert | $delta1 != $currVal1 || $delta2 != $currVal2 |
compute | $1 = $delta1 |
compute | $2 = $delta2 |
Une fois ce job lancé, les mesures apparaissent toutes les minutes dans les fonctions genericSensor correspondantes.
Publication vers MQTT
Pour faciliter la récupération des mesures avec la Discovery MQTT, on a intérêt à indiquer leur nom et l'unité de mesure directement dans la configuration du Yocto-RS232:
Configuration des fonctions genericSensor
Et surtout, pour que les mesures soient envoyées au broker MQTT, il faut activer l'envoi des timed reports pour les fonctions correspondantes, à la fréquence désirée:
Activation des timed reports, fréquence 1/min
Pour connecter votre Yocto-RS232 au broker MQTT, vous pouvez soit le connecter à un YoctoHub-Ethernet, soit le connecter directement au PC de Home Assistant en utilisant VirtualHub. Dans les deux cas, si ce n'est pas encore fait, configurez un callback HTTP de type MQTT vers votre broker MQTT, et si tout a été fait correctement, vous devriez rapidement voir apparaître votre Yocto-RS232 apparaître dans la liste des devices MQTT. Dans ses propriétés, vous découvrirez directement les capteurs que nous avons configurés. Il ne vous reste plus qu'à leur attribuer une jolie icône, et à les ajouter à votre dashboard !
Les capteurs trouvés par la Discovery MQTT