Monthly Archives: octobre 2015

Suvi de la consommation d’eau avec des compteurs à impulsions sur un bus 1-wire

Contexte

Si vous avez suivi les posts précédents, vous savez peut-être que j’ai aménagé dans une maison neuve, et la construction a ses avantages, comme le fait de penser à installer des compteurs à impulsion par le sanitaire, puis une prise réseau à proximité par l’electricien, ainsi je me retrouve avec ça :

Compteur à impulsion en attente

Compteur à impulsion en attente

Le but étant de suivre la consomation d’eau chaude et d’eau froide.
Le compteur pour l’eau froide est placé avant la nourrice et après l’alimentation du CESI.
Le compteur pour l’eau chaude est placé à la sortie du CESI.
CQFD : inutile dans mon cas de déduire l’eau froide sur l’eau chaude.

Principe

Je vais utiliser un Raspberry Pi et utilise le bus 1-wire pour le comptage des impulsions.
En 1-wire, j’utilise l’interface DS1490 (USB/1-wire) que j’ai acqueri lors des mes découvertes du 1-wire ; car sauf erreur ma part, l’utilisation du 1-wire par les pins du Raspberry Pi ne permettent pas d’exploitrer un DS2423.
Un DS2423 est un double compteur avec mémoire.
Plus tard, je profiterai d’avoir un bus 1-wire pour mettre des sondes de températures DS18B20 à coller aux tuyaux du chauffage qui passent à coté.

Le raspberry est alimenté par le réseau, de plus, ayant besoin d’une alimentation 5 volts pour les composants 1-wire, je ne la prends pas du USB, mais du PoE passif grâce à un simple splitter.

Un schéma de principe :

compteur-impulsion-raspberry-principe

Schéma de principe

 

Dans l’ordre :

  • Les compteurs d’eau génère une impulsion à chaque littre qui passe.
  • Le DS2423 compte chaque impulsion de chaque compteur.
  • Le raspberry consulte le compteur du DS2423.
  • Le raspberry envoi le chiffre obtenu dans une table MySQL sur un serveur.
  • Une page web traire ces chiffres de la table pour générer des graphiques.
  • Je peux me faire la morale parce que mes douches sont trop longues.

Go ?!

Montage du DS2423 pour compter des impulsions

Pour commencer, le DS2423 n’est plus produit, il est en fin de vie.
On peut encore s’en procurer chez hobby-boards.com à 10.50 $ / pièce, auquel on rajoute 10,55 $ de frais de port ! Ce n’est pas rien !

Voici comment j’utilise le DS2423 :

ds2423 schéma

ds2423 : schéma

Il faut donc fournir un bus 1-wire composé d’un « DATA » et d’une masse. Il faut également fournir une alimentation 5V qui a une masse commune avec le bus 1-wire.
Pour l’alimentation, je vais m’inspirer de mon injecteur 5 Volts → 1Wire, sauf que je vais l’inclure dans un seul et même boitier.

 

Schéma injecteur 1-wire

Schéma injecteur 1-wire

+

ds2423 schéma

ds2423 schéma

=

ds2423 avec alimentation

ds2423 avec alimentation

Je me laisse la possibilité de repiquer le bus 1-wire et une alimentation (à droite du schéma) pour ajouter un autre DS2423 dans le but de compter un 3ième compteur à impulsion, voir un 4ième ou un pluviomètre qui se comporte de la même façon qu’un compteur d’eau à impulsion.

Réalisation et mise en place

Est-ce que la théorie rejoint la pratique ?
Je suis parti sur une « prototype board », des borniers, le tout placé dans une prise RJ45 en saillie.
L’avantage d’une prise RJ45 en saillie, c’est quelle permet de planquer le PCB, le tout se  fixe proprement au mur. De plus, j’y connecte à travers le RJ45, un câble RJ11 du DS9490R.
Place aux photos.

Comme on peut le voir, il y a deux DS2423 sur ce montage, c’est en prévision d’un 3ième compteur à impulsion pour un pluviomètre.
Pour ne pas compliquer la chose, on va faire comme si ce deuxième DS2423 n’existe pas.
Sur la dernière photo, on voit clairement les deux compteurs à impulsion raccordés au montage.
La LED permet de vérifier que le circuit est bien alimenté.
Le 5 Volts, provient de l’USB du Raspberry Pi, mais il est préférable de le sortir d’une alimentation sans rapport aux Raspberry Pi, ce qui a été fait entre temps, j’ai utilisé un splitter DC 3,5mm pour alimenter le montage ; le 5V provient du PoE passif.

Récupération des valeurs des compteurs

C’est partie pour une installation de Raspbian sur le Raspberry Pi.
Mes quelques commandes de bases sur le Raspberry :

# sudo passwd
# su
# apt-get update
# apt-get dist-upgrade
# apt-get install htop vim screen usbutils

Puis les choses sérieuses commecent, on branche le dongle DS1490 :

# lsusb
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 004: ID 04fa:2490 Dallas Semiconductor DS1490F 2-in-1 Fob, 1-Wire adapter

On installe owserver, owfs-fuse et surtout owhttps qui permettra de visualiser facilement l’état des compteurs le temps de scripter tout ça.

# apt-get install owserver owfs owhttpd

Puis on modifie le fichier /etc/owfs.conf :
On commente la ligne 16 et décommente la ligne 19.

# vi /etc/owfs.conf
# ...and owserver uses the real hardware, by default fake devices
# This part must be changed on real installation
#server: FAKE = DS18S20,DS2405
#
# USB device: DS9490
server: usb = all
#
# Serial port: DS9097
#server: device = /dev/ttyS1
#
# owserver tcp address
#server: server = 192.168.10.1:3131

Puis on redemarre owserver

# /etc/init.d/owserver restart
[ ok ] Restarting 1-Wire TCP Server: owserver.

Direction le navigateur pour consulter owhttp : http://adresseipduraspberry:2121
Owhttp devrait lister une sonde DS2423 commençant par 1D. 

DS2423 sur OWHTTPD

DS2423 sur OWHTTPD

Ohhhh ! counters.A correspond à mon compteur d’eau froide ; counters.B à mon compteur d’eau chaude.
Après un petit test au verre mesureur, 1 litre d’eau = 1 impulsion. Autrement, la valeur d’une impulsion est généralement marquée sur le compteur, ce n’est pas le cas pour ma part.

Bon, c’est en bonne voie, il reste maintenant à scripter tout ça afin d’alimenter une base de données MySQL sur un serveur distant.
A noter qu’on peut également ecrire les relevé des compteurs sur une base MySQL en local sur le Raspberry ; dans mon cas j’utilise un serveur distant pour d’autres applications lié à la domotique et donc je centralise toutes les données sur une même machine.

Pour insérer les valeurs des compteurs, je m’inspire d’un des mes tuto : Insérer les valeurs 1-wire dans une base MySQL.

Coté serveur MySQL (local/distant, à adapter selon votre convenance)

Nécéssite apache2, php5 et mysql-server  : apt-get install apache2 mysql-server php5
Je crée une base et les deux tables, comme dans mon article, à l’exception de la table 1wire_sensor qui est modifiée pour prendre en compteur les compteurs.
Ce qui donne :

# mysql -p
create database 1wire;
use 1wire;
CREATE TABLE IF NOT EXISTS `1wire_sensor` (
`1wire_sensor_id` int(3) NOT NULL AUTO_INCREMENT,
`1wire_sensor_enable` enum('true','false') NOT NULL,
`1wire_sensor_type` enum('temperature','presence','sensed.A','counters.A','counters.B') NOT NULL,
`1wire_sensor_family` tinytext NOT NULL,
`1wire_sensor_idaddress` tinytext NOT NULL,
`1wire_sensor_comment` text NOT NULL,
PRIMARY KEY (`1wire_sensor_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `1wire_data` (
`1wire_data_id` int(11) NOT NULL AUTO_INCREMENT,
`1wire_data_sensor_id` int(3) NOT NULL,
`1wire_data_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`1wire_data_value` text NOT NULL,
PRIMARY KEY (`1wire_data_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
GRANT ALL PRIVILEGES ON 1wire.* TO '1wire'@'%' IDENTIFIED BY '1wire';

En détail, j’ai ajouté counters.A et counters.B dans le champs 1wire_sensor_type.
Ceci afin de m’adapter aux DS2423 ; counters.A et counters.B sont les noms exact des champs a récupérer.
Faut penser à permettre à notre serveur MySQL d’autoriser les connexions distantes et commenter la ligne bind-address.

# vi /etc/mysql/my.cnf
#
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
# bind-address = 127.0.0.1
#
# /etc/init.d/mysql restart

Et voila, la partie serveur se termine là pour l’instant, nous y reviendrons pour créer un graphique avec la consomation d’eau.

Pour résumer :
Host : 192.168.1.100
Database MySQL : 1wire
User MySQL : 1wire
Password MySQL : 1wire

Coté RaspberryPi

Là encore, je m’inspire de mon article de l’époque, cependant mon fichier PHP ne traite que les sondes de témpérature et de présence, quelques adaptations sont nécessaires pour explorer un DS2423 :

# cd /root/
# wget https://www.domolio.fr/wp-content/uploads/2012/05/1-wire-mysql.tgz
# tar zxvf 1-wire-mysql.tgz
# cd 1-wire/

J’apporte de suite les informations nécessaires pour la connexion au serveur :

# vi require/main.inc.php

A noter que je travail dans /root/1-wire .

<?php

// -- BASE --

$DEBUG = true;
$DEBUG_TXCOLOR = "#fff";
$DEBUG_BGCOLOR = "#000";
$PATH = "/root/1-wire";

// --

define("DEBUG", $DEBUG);
define("DEBUG_TXCOLOR", $DEBUG_TXCOLOR);
define("DEBUG_BGCOLOR", $DEBUG_BGCOLOR);
define("PATH", $PATH);

// ----
// -- MYSQL --

// Préfixe de toutes les tables du site
$MYSQLHOST = "192.168.1.100";
$MYSQLLOGIN = "1wire";
$MYSQLPWD = "1wire";
$MYSQLBASE = "1wire";

//--

Ah oui, il faut php et php5-mysql

# apt-get install php5-cli php5-mysql mysql-client

Et je créer mon script qui récupère les valeurs des sondes :

# vi /usr/local/bin/1wire-fetch.php

Puis je copie/colle le script de mon article précédent, modifié pour permettre l’intérogation de sondes DS2423 :

#!/usr/bin/php
<?php
$pathconfig = "/root/1-wire";
require_once($pathconfig."/require/main.inc.php");
require_once(PATH."/require/database.inc.php");
require_once(PATH."/require/function-1wire.inc.php");

// Recup des données des sondes depuis la BDD
$req_sensor = "SELECT * FROM 1wire_sensor WHERE 1wire_sensor_enable IS TRUE";
$qur_sensor = mysql_query($req_sensor);
while($dat_sensor = mysql_fetch_array($qur_sensor)) {
 // Pour chaque sonde on constitue son adresse :
 $onewire_address = $dat_sensor['1wire_sensor_family'].".".$dat_sensor['1wire_sensor_idaddress'];
 // On teste sa présence sur le bus
 if(onewire_presence($onewire_address)) {
 debug("OUI ! ".$onewire_address." est présent sur le bus","cli");
 if ($dat_sensor['1wire_sensor_type'] == "presence") {
 debug("Seul sa presence sur le bus est nécessaire","cli");
 // on ecris dans la BDD qu'il est présent et on s'arrete là
 $value = 1;
 }
 elseif ($dat_sensor['1wire_sensor_type'] == "temperature" || $dat_sensor['1wire_sensor_type'] == "sensed.A" || $dat_sensor['1wire_sensor_type'] == "counters.A" || $dat_sensor['1wire_sensor_type'] == "counters.B" ) {
 // on récupère sa valeur, que ca soit la température ou l'état du PIO (DS2406) ou le compteur (DS2423).
 $value = onewire_read($onewire_address,$dat_sensor['1wire_sensor_type']);
 }
 }
 else {
 // Composant absent du bus
 debug ("NON ! ".$onewire_address." n'est pas présent sur le bus","cli");
 $value = 0;
 }

 // on écris le resultat dans la BDD
 $req_insdata = "INSERT INTO 1wire_data SET
 1wire_data_sensor_id = ".$dat_sensor['1wire_sensor_id'].",
 1wire_data_value = '".$value."'";
 mysql_query($req_insdata) or die ("Erreur à l'insertion des données 1-wire : ".mysql_error());
}
mysql_close();
?>
# chmod +x /usr/local/bin/1wire-fetch.php

Puis on fait un break ! Une petite explication s’impose !
Le script 1wire-fetch.php sera appelé par le cron pour remplir une base de donnée sur un serveur distant.
Ce script fait appel à des fichiers stockés dans /root/1-wire/require/ ; ces fichiers sont des « functions » que j’ai pondu il y a quelque temps en utilisant une libraire fournis par l’équipe qui développe 1wire sur linux (owfs.org).
La libraire maitresse, qui permet d’intéroger des sondes par PHP, s’appel libownet-php et fournis un fichier miraculeux qui se nomme ownet.php.

Pour aller plus loin, consulter mon article : Interroger les sondes 1-wire par un script PHP.

Le fichier ownet.php fourni par le paquet libownet-php est buggé, dans l’état, il ne permet pas d’exploiter les DS2423, de plus, c’est ce même paquet que j’ai intégré dans l’archive que vous avez décompressé tout à l’heure.
Mais c’est pas grave, on va débugger ça ensemble.

Pour commencer, adios le fichier ownet.php de mon archive, on va reprendre la version packagé par Debian.

# apt-get install libownet-php
# cd /root/1-wire/require/
# cp /usr/share/php/OWNet/ownet.php .

Puis deux corrections sont nécessaire :

# vi ownet.php

Ligne 516 :

if (bccmp($ret['data_php'],0,0)==-1){

Devient,

if (bccomp($ret['data_php'],0,0)==-1){

Ligne 484 :

$ret['data']    =substr($data,0,$data_len);     // data_php length is the same as $ret[2]

Devient,

$ret['data']    =trim(substr($data,0,$data_len));     // data_php length is the same as $ret[2]

Le premier bug bccmp/bccomp est assez connu, traine depuis très longtemps.
Le second bug est propre au DS2423, j’ai trouvé un seul appel à l’aide sur internet, datant de mars 2010 :
Cannot acces counters DS2423 with ownet.php
C’est donc bien lié aux espaces qui trainent devant la valeur du compteur, chose corrigé avec la fonction php trim().

Ou plus simplement, télécharger directement ma version du fichier ownet.php corrigée :

# cd /root/1-wire/require/
# wget "https://domolio.fr/wp-content/uploads/2015/01/ownet.php.txt" -O ownet.php

Voila, notre break/débug est terminé, pour résumer step by step :

  1. Le cron lance le script /usr/local/bin/1wire-fetch.php
  2. Ce script utilise les fichiers stocké dans /root/1-wire pour se connecter au serveur MySQL distant et au bus 1-wire
  3. Le script consulte la table MySQL 1wire_sensor pour lister les sondes à intéroger sur le bus
  4. Ce même script consulte chaque sonde en verifiant sa présence sur le bus 1-wire et récupère la valeur souhaitée.
  5. Toujours ce script, ajoute les valeurs récupérées dans la table MySQL 1wire_data du serveur distant.
  6. Le script est terminé, il recommencera dans une heure.
  7. Une page web sur notre serveur distant, intéroge les valeurs dans les différentes tables MySQL pour afficher de beaux graphiques.

Ainsi nous allons maintenant alimenter la table 1wire_sensor avec les sondes à intéroger.
Et comme je suis sûr de moi, je le fais depuis mon client, selon les informations MySQL que je me suis mis de coté :

# mysql -h192.168.1.100 -u1wire -p1wire 1wire
INSERT INTO 1wire_sensor SET 1wire_sensor_enable='true', 1wire_sensor_type='counters.A', 1wire_sensor_family='1D', 1wire_sensor_idaddress='641710000000', 1wire_sensor_comment='eau froide';
INSERT INTO 1wire_sensor SET 1wire_sensor_enable='true', 1wire_sensor_type='counters.B', 1wire_sensor_family='1D', 1wire_sensor_idaddress='641710000000', 1wire_sensor_comment='eau chaude';

Pour rappel :
1wire_sensor_family=’1D’ : 1D correspond à la famille des DS2423.
1wire_sensor_idaddress=’641710000000′ : 641710000000 correspond à l’ID de ce DS2423 tel que owhttp me l’a indiqué tout à l’heure.

Il ne reste plus qu’à tester :

# php /usr/local/bin/1wire-fetch.php
[function] onewire_presence pour : 1D.641710000000
OUI ! 1D.641710000000 est présent sur le bus
[function] onewire_read pour : 1D.641710000000 et le type counters.A
La variable value est vide pour le mode counters.A, on recommence (1 fois est normal)
Valeur brute trouvée :972
[result] Donnée trouvée pour /1D.641710000000/counters.A : 972
[function] onewire_presence pour : 1D.641710000000
OUI ! 1D.641710000000 est présent sur le bus
[function] onewire_read pour : 1D.641710000000 et le type counters.B
La variable value est vide pour le mode counters.B, on recommence (1 fois est normal)
Valeur brute trouvée :638
[result] Donnée trouvée pour /1D.641710000000/counters.B : 638

Oh 🙂
Grâce à la première ligne du script ( #!/usr/bin/php ) on peut même directement lancer 1wire-fetch.php :

# 1wire-fetch.php
[function] onewire_presence pour : 1D.641710000000
OUI ! 1D.641710000000 est présent sur le bus
[function] onewire_read pour : 1D.641710000000 et le type counters.A
La variable value est vide pour le mode counters.A, on recommence (1 fois est normal)
Valeur brute trouvée :975
[result] Donnée trouvée pour /1D.641710000000/counters.A : 975
[function] onewire_presence pour : 1D.641710000000
OUI ! 1D.641710000000 est présent sur le bus
[function] onewire_read pour : 1D.641710000000 et le type counters.B
La variable value est vide pour le mode counters.B, on recommence (1 fois est normal)
Valeur brute trouvée :638
[result] Donnée trouvée pour /1D.641710000000/counters.B : 638

Au passage, une petite chasse d’eau + lavage de main aura utilisé 3 litres.
Coté mysql, je devrais avoir toutes les données :

select * from 1wire_data;
+---------------+----------------------+----------------------+------------------+
| 1wire_data_id | 1wire_data_sensor_id | 1wire_data_timestamp | 1wire_data_value |
+---------------+----------------------+----------------------+------------------+
|             1 |                    1 | 2015-01-13 00:37:06  | 975              |
|             2 |                    2 | 2015-01-13 00:37:06  | 638              |
+---------------+----------------------+----------------------+------------------+
2 rows in set (0.00 sec)

C’est le cas !

Je mets en place le cron pour une execution toutes les heures :

# vi /etc/cron.d/crononewire
# Récup des valuers 1-wire tout les 1h et 5 minutes
5 * * * * root /usr/local/bin/1wire-fetch.php

Donc le script se lancera à 13h05, 14h05, 15h05, etc…

Visualisation des données

Le temps passe, la table MySQL se remplit, place aux graphiques !
J’utilise Highcharts et plus précisement sur Highstock qui a l’avantage d’avoir une échelle du temps.
On part du principe que sur la machine qui herberge la partie web pour les graphiques dispose des outils nécessaire : apache2, php-mysql, php5, etc..
Bien entendu, cette partie peut également se situer sur le raspberry, tout comme les données MySQL.

# cd /var/www
# wget https://www.domolio.fr/wp-content/uploads/2015/10/graph-eau.tar.gz
# tar zxvf graph-eau.tar.gz
# cd eau

Le graphique se scinde en deux parties :
– data.php qui récupère les valeurs dans la base de donnéess et qui met en forme au format json.
– index.php et son répertoire « js » qui traite les données et crée les graphiques.

# vi data.php
<php 
header('Cache-Control: no-cache, must-revalidate');
header('Content-type: text/javascript');

$mysql_host = "127.0.0.1";
$mysql_db = "1wire";
$mysql_user = "1wire";
$mysql_pwd = "1wire";

$id_eau_froide = 1;
$id_eau_chaude = 2;

Mon eau froide correspond à l’id « 1wire_data_sensor_id » n°1 dans la table 1wire_data qui correspond à COUNTERS.A de mon DS2423.
L’eau chaude à l’id n°2 -> COUNTERS.B.

Dans le fichier index.php, une seule modification est importante :

$pointStart = "Date.UTC(2015, 0, 13, 0, 0, 0)"
...
$pointStart =  "Date.UTC(2015, 0, 13)";
...
$pointStart =  "Date.UTC(2015, 0)";

« 2015, 0, 13, 0, 0, 0 » correspond à mon tout premier relevé de compteurs :
Le 13 janvier 2015 à 01h soit 00h GMT.

Ici, HighStock travail en intervalle avec les données json reçues par le fichier data.php
On récupère la valeur des compteurs toutes les heures, donc l’intervalle entre deux points pointInterval pour le format :
– Journalier : 3600000 millisecondes (3600 x 1000).
– Hebdomadaire : 3600 x 1000 x 24 x 7.
– Mensuel : 3600 x 1000 x 24 x 31.

Là est le petit faux dans ce script, tous les mois ne font pas 31 jours…
Ça implique un petit décalage visuel de mois en mois.
A corriger à l’occasion…

Voila le résultat :

consomation eau hebdomadaire

consomation eau hebdomadaire

consomation d'eau heure par heure

consomation d’eau heure par heure

consomation d'eau par mois

consomation d’eau par mois

Demo

Une demo figée au 11/10/2015 ici :

http://www.chocolio.com/demo-eau/

Et voila ! Très content de mes compteurs et de mon suivi !
La prochaine étape pour moi, sera d’ajouter un pluviomètre sur le dernier compteur du DS2423, et cela afin d’actionner ou pas un arrosage automatique.

Posté dans 1-wire, Debian, graphs, php, Raspberry Pi, Web | 6 Commentaires