Archives du blog

Capteur de pluie – DIY

J’ai eu l’idée saugrenue de fabriquer un capteur de pluie pour m’envoyer un signal sonore quand il se met à pleuvoir la nuit et que la fenêtre est ouverte.

Le montage

Je suis parti d’une plaque de polystyrène mince et de deux fils de cuivre (une chute de câble multi brin dénudé). Il est tout a fait possible de prendre d’autres matériaux tant qu’ils ne craignent pas l’eau et qu’ils ne sont pas trop flexibles (il faut tendre les fils pour éviter qu’ils fassent contact entre eux). Sur le schéma les fils se chevauchent mais il faut imaginer qu’il passent derrière la carte par des trous percés. Il serait aussi tout a fait possible de faire un PCB mais pas sûr que cela soit rentable si la surface est énorme.

Le but est d’avoir deux fils dénudés qui ne sont pas en contact entre eux. C’est l’eau qui assurera leur contact, fermant ainsi le circuit comme un interrupteur. Reste a voir à l’usage à quelle vitesse l’eau va encrasser/oxyder les fils.

 

Capteur de pluie - DIY

 

Je mesure la résistance entre A et B (cf schéma), au repos il ne dois pas y avoir de contact. A est relié à GND et B à A0, une résistance de 1k Ohm est placée entre A0 et 5V.

 

Le code Arduino

J’ai réalisé un rapide programme d’alerte sur l’Arduino qui utilise mon bus ARV pour la communication. C’est le WR703N qui assure la connexion à distance de la sonde.

Je me contente d’une lecture toutes les 100 millisecondes, ça doit être assez précis dans la majorité des cas.

Pour éviter de noyer le bus sous les messages j’évite d’envoyer un message si la variation entre deux mesures est trop faible, j’estime que le changement ne se fera que brutalement, si la variation en 100ms est de 1 unité (sur 1023 …) c’est uniquement dû à l’incertitude de mesure.

De la même manière je considère que les valeurs qui m’intéressent sont < 1000, au dessus c’est sec , sauf si j’ai besoin de savoir si le capteur a séché … à voir si physiquement l’inclinaison du capteur peut aider à le sécher naturellement.

int sensorPin = A0;    // select the input pin for the potentiometer
int sensorValue = 0;  // variable to store the value coming from the sensor
int oldValue = 0;
int sensorDelay=100;

// Seuil en dessous duquel regarder l'évolution
int seuil = 1000;

void setup() {
  Serial.begin( 115200 );
}

void loop() {
  // read the value from the sensor:
  sensorValue = analogRead(sensorPin); 

  if ( sensorValue < seuil )
  {
    // si la valeur a changé de 1 on n'en tient pas compte, il y a trop de fluctuation sur la ligne pour que ce soit important
    if ( abs(  oldValue - sensorValue ) > 1 )
    {
      Serial.print("D");
      Serial.println(sensorValue);
    }
  }

  delay(sensorDelay);
  oldValue = sensorValue;
}

Paramétrage d’ARV

Pour ceux qui suivent ce carnet de note, j’ai développé un bus de communication en TCP/IP qui peut acheminer des messages lus depuis le port série de  l’arduino pour être traités ailleurs.

Pour le test je trace les informations dans un fichier texte grâce au plugin toFile d’ARV. Il faudra d’ailleurs que je développe un plugin qui peut appeler un programme/lire un son/tweeter/… quand il reçoit un message. Le problème sera de régler la sensibilité pour ne pas envoyer de messages trop fréquemment … Peut être en fixant un seuil et en ne notifiant que ce passage de seuil ?

collector.ini

Le fichier collector.ini permet d’associer à un type de message reçu un plugin qui va traiter celui-ci. La « paramétrabilité » est encore sommaire pour l’instant, mais ça viendra au fil du temps.

Pour l’exemple ci-dessous, l’arvcollector lisant le port série arduino envoie des messages de type « arduinoTP.WarningStation ». Un autre arvcollector déporté (celui ayant le collector.ini renseigné comme703 ci-dessous)  tracera les messages reçus (plugin toFile) en les horodatant (timestamp=TRUE).

[plugins]
arduinoTP.WarningStation=toFile.so

[toFile:arduinoTP.WarningStation]
timestamp=TRUE
fileOut=warning.csv

Lancement des processus

Sur le serveur je lance l’arvserveur et l’arvcollector pour traiter les messages via les plugins

./arvserver
./arvcollector [ip_serveur] PluginMaster

Enfin sur le WR703N je lance un collecteur lisant le port série

./arvcollector [ip_serveur] DropWarner /dev/ttyACM0 arduinoTP.WarningStation

L’instance DropWarner enverra des messages au serveur quand la valeur du capteur descendra en dessous de 1000. Le serveur va relayer à tous les autres collecteurs, dont PluginMaster qui a été configuré pour tracer dans le fichier warning.csv.

Application du capteur

Pour tester en situation j’ai mis le capteur cette nuit sous ma chaudière (qui depuis une semaine à une fuite une ou deux fois pas jour et dont on ne connait pas la provenance ). Cela m’a permis de détecter que la fuite a eu lieu à 2h34 du matin, au moment où tout le monde était assoupi dans la maison … surement la faute des chats.

Avec des Plugins c’est plus festif

Il y a deux semaines je vous avais parlé de mon Bus Arduino (qu’entre temps j’ai nommé ARV, comme Arduino Rendez-Vous). J’ai décidé de le faire évoluer pour que le collector puisse faire d’autres choses.

J’ai imaginé plusieurs architectures pour pouvoir évoluer facilement suivant les besoins et j’en suis arrivé à développer des plugins pour le collector.

Un plugin c’est quoi ?

Un plugin c’est une librairie partagée (dll ou .so suivant votre plateforme) ayant des noms de fonction normalisée.

Tous les plugins pour une même application auront donc des fonctions et des variables en communs : celles qui seront appelées par le programme « maitre ».

Ce programme va donc ouvrir chaque plugin à charger et récupérer un pointeur sur chaque fonction/variable qu’il compte utiliser.

Comment ça s’implémente ?

On commence par « ouvrir » le plugin

void * plugin = dlopen (fileName, RTLD_NOW);
 if (!plugin)
 {
  printf ("Cannot load %s: %s\n", fileName, dlerror ());
  return 1;
 }

et après on peu « lier » une variable  (par exemple la variable pluginName contenu dans le plugin)

char * pPluginName = dlsym (plugin, "pluginName");
result = dlerror ();
if (result)
[...] traitement des cas d'erreur

ou une fonction

typedef int (*init_f) ( char * _subject, sirElement * config );
init_f init = dlsym (plugin, "init");
 result = dlerror ();

Notez bien le typedef du prototype de fonction.

Pour chaque plugin chargé je stocke un pointeur vers une fonction d’initialisation du plugin (init), qui va me permettre d’ouvrir des fichiers ou de me connecter à une base de donnée, et une fonction de traitement du message reçu (parse).

Le chargement ? quel chargement ?

Oui, car il faut charger tous les plugins qu’on compte utiliser, j’ai opté pour un fichier de paramétrage (collector.ini dont un exemple est reproduit ici) qui va définir les plugins à charger et quand les utiliser.

[base]
pluginDir=plugins
[plugins]
arduinoTP.StationMeteo=stationMeteo.so
arduino.StationMeteo=stationMeteo.so
arduinoTP.StationMeteo=toFile.so
[stationMeteo]
DBHost=127.0.0.1
DBName=BaseArduino
DBUser=UserArduino
DBPassword=MotDePasseArduino
[toFile]
#Par défaut le plugin prends "toFile.csv", on peut aussi le fixer par configuration
fileOut=
[toFile:arduinoTP.StationMeteo]
#On peu aussi définir des paramétrage pour un couple plugin/sujet
fileOut=fichierOut.csv

Détaillons ce fichier de configuration

La section [base] contient pour le moment le chemin vers le répertoire des plugins. Tous les plugins devront être dedans au risque de ne pas être chargés.

La section [plugins] détaille les plugins à charger. Attardons nous un peu plus sur cette syntaxe.

arduinoTP.StationMeteo=stationMeteo.so

Il y a donc une clef  (arduinoTP.StationMeteo) qui à une valeur (stationMeteo.so),par cette directive on définie quel plugin utiliser (stationMeteo.so) lors de la réception d’un message de type « arduinoTP.StationMeteo ». C’est la fonction parse() du plugin qui sera utilisée pour traiter le message.

Le plugin stationMeteo.so a pour mission de stocker dans une base MySQL les message reçus (deux photoresistances et 5 capteurs de température), dans le but d’être accessible sur mon site web personnel.

Il est possible de mettre plusieurs ligne ayant une même clef afin de multiplier les actions à réaliser sur réception d’un message (par exemple stocker un message en base ET faire agir un autre arduino en envoyant un autre message sur le bus)

Le plugin de type stationMeteo a droit à une section de paramétrage, j’y ai placé les paramètres d’accès à la base de donnée.

[stationMeteo]
DBHost=127.0.0.1
DBName=BaseArduino
DBUser=UserArduino
DBPassword=MotDePasseArduino
J’ai écrit un autre plugin : toFile.so qui se contente d’écrire ce qu’il reçoit dans un fichier. C’est aussi une bonne méthode pour illustrer une autre façon de paramètrer des plugins.
[toFile]
#Par défaut le plugin prends "toFile.csv", on peut aussi le fixer par configuration
fileOut=
[toFile:arduinoTP.StationMeteo]
#On peu aussi définir des paramétrage pour un couple plugin/sujet
fileOut=fichierOut.csv

toFile a deux sections de configuration : [toFile] et [toFile:arduinoTP.StationMeteo]. Dans le second cas vos pouvez reconnaître un nom de message qui a déjà été paramétré dans la section [plugins]. Cela implique qu’un message de type arduinoTP.StationMeteo utilisera un plugin initialisé grâce à la section [toFile:arduinoTP.StationMeteo] et [toFile] dans les autres cas.

Dans le cas présent cela influe sur le nom de fichier, mais cette méthode est applicable par tout les plugin installé pour avoir des particularité suivant le message reçu (base de donnée différente, fichier différent, …).

Concernant la lecture du fichier d’ini

Par défaut le C n’a pas de librairie d’accès standard pour ce genre de fichier alors j’en ai ré-implémenté une aux fonctions minimales (sir.c dans le repository). J’avais d’abord cherché à utiliser ce qui existe déjà mais j’avais la contrainte de la taille (n’oublions pas que je compile pour une machine ayant 1.5Mo de filesystem ^^) , dans mes pérégrinations je suis tombé sur iniParser qui fait le boulot de recherche dans un .ini mais qui n’accepte pas d’avoir plusieurs clefs identiques, et comme j’utilise cette fonction pour multiplier les actions sur réception d’un message … j’ai été contraint d’en réécrire un plus simple.
L’accès au code se trouve comme d’habitude : ici