
28/08/2007
La salle serveurs d’Erasme se devait d’être dotée de capteurs pour "monitorer" ce qui se passait à l’intérieur, les serveurs étaient déjà surveillés au niveau logiciel mais pas encore au niveau matériel.
L’importance de cette démarche s’est faite ressentir lors des derniers orages, en effet la climatisation a la fâcheuse habitude de disjoncter et de ne plus redémarrer jusqu’à une intervention humaine. Selon nos mesure la température ambiante de la pièce monte de 2°C par minute à partir du moment où la climatisation s’arrête et cette augmentation est exponentielle...
Afin de surveiller la température de la salle et le bon fonctionnement de la climatisation il a été décidé d’installer une centrale de capteurs reliée à un serveur. Il a ensuite été rajouté un capteur lumière pour savoir si la salle est éclairée ou pas et un capteur pyroélectrique pour détecter les mouvements. Il y a deux capteurs de température : un placé directement à la sortie d’air de la climatisation et un autre au-dessus d’une baie de serveurs pour connaître la température ambiante.
Un relais peut également être piloté depuis le PC et via l’arduino. Il est utilisé pour délencher l’alarme incendie (qui est prévue pour être pilotée ainsi) qui va ensuite se charger d’appeler des personnes lorsque la climatisation s’est arrêtée.
Le fonctionnement se décrit comme tel :
Le projet appelé sensorsd se veut d’être une base saine la plus générique possible et peut être réutilisé dans un autre contexte, avec des capteurs différents et dans un but différent.
Voici quelques photo de l’état actuel du projet. Il va être refait dans un boitier plus grand pour accueillir le relais (qui n’avait pas été prévu initialement).






A vous d’adapter ce qui doit l’être...
Les capteurs de type « résistance variable » (LDR et CTN) sont employés dans un montage de type « diviseur de tension ».
La résistance talon (en série avec le capteur) se calcule approximativement selon la formule

où Rcapteur sont les 2 résistances extrêmes que peut exercer le capteur.
Les capteurs ont été reliés au boîtier électrique à l’aide de fiches Jack 3,5.




Tous les connecteurs d’alimentation et de masse sont reliés ensemble puis reliés à VCC ou à la masse de l’arduino.
Le capteur température utilisé est une simple CTN : une résistance dont la valeur diminue avec la température.
Elle a été placée dans le pont diviseur de tension comme expliqué auparavant et une liste de valeurs sur l’entrée analogique ont été récupérées expérimentalement avec l’arduino.
1023 = 5V
0 = 0V

On peut en déduire une approximation (max 3°C d’erreur sur la plage 10°C-60°C) linéaire selon une fonction affine :
Valeur = -9,44 * T° + 719,4
~<=> Valeur = -9 * T° + 719 (décimales négligeables...)
<=> T° = (Valeur – 719) / 9
<=> T°= 719 X 2 < - 9 / (expression RPN à utiliser dans la config dans ce cas, « < » correspondant au décalage de bits nécessaire)Code : Cf archive
Pour communiquer avec l’arduino un mini-protocole sur un byte est utilisé.
Les capteurs analogiques ont un code attribué de 0 à 5 codé sur 1 byte. Les capteurs logiques sont quant à eux codés de ’a’ à ’n’ pour les 12 pins (le 13ème étant utilisé en sortie pour allumer la LED). Il suffit donc d’envoyer le code correspondant au capteur désiré et l’arduino renverra la valeur correspondante codée sur un byte également.
Les valeurs analogiques s’étendent de 0 à 1023 (de 0V à 5V) et donc de 0 à 10 bits. Or 1 byte = 8 bits. Les valeurs proches de 1023 ne pourront donc pas être codées sur 1 byte. C’est pour cela qu’un décalage de 2 bits vers la droite est effectué pour réduire la « résolution ». La perte maximale est de 11 en binaire (=3 en base 10 "classque") ce qui est négligeable. Il faut par contre penser du côté du PC à faire un décalage de 2 bits sur la gauche.
Les valeurs des capteurs analogiques sont enregistrées en permanence dans un tableau contenant les 10 dernières valeurs de chacun. Cet enregistrement se fait toutes les 200 millisecondes. Quand la valeur d’une entrée analogique est demandée, une moyenne des enregistrements est renvoyée. Le tout afin de lisser les mesures pour éviter les faux-positifs.
Code : Cf archive
Pour compiler exécuter :
gcc -Wall -lconfuse -llo main.c serial.c rpn.c daemonize.c osc.c -o sensorsd
Pour exécuter le programme il faut avoir la librairie libLo et libConfuse (paquets debian liblo0 et libconfuse0) et pour le compiler il faut rajouter les paquets libl0-dev et libconfuse0-dev (d’où les options -lconfuse et -llo de gcc pour aller chercher /usr/lib/liblo.a et /usr/lib/libconfuse.a)
La librairie libLo permet de gérer la couche de communication OSC et la couche de réseau qui va avec. Elle ne supporte pas malheureusement les envois en broadcast qui sont désactivés par défaut pour les sockets (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, 1, 1) pour y remédier il faudrait inclure cette ligne dans les sources de la librairie).
La libraire libConfuse permet de gérer un fichier de configuration. Les valeurs par défaut (si elles ne sont pas définies dans le fichier) sont configurées dans le code source.
Voici la liste des paramètres disponibles, leur type, leur valeur par défaut et leur description :
Les paramètres de type string doivent être entourés de quotes : " "
Une expression RPN peut être appliquée à la valeur des capteurs. 6 opérations sont disponibles : +,-,*,/,<,>. < et > correspondent respectivement au décalage de bits à gauche (<<) et à droite (>>). Il est obligatoire d’utiliser la notation à 1 caractère. La valeur renvoyée par l’arduino remplacera le caractère X dans l’expression. Pour nos capteurs température nous avons utilisé l’expression : "719 X 2 < - 9 /".
Le programme prend en paramètre le chemin du fichier de configuration, si rien n’est spécifié le fichier /etc/sensorsd.conf sera utilisé.
L’ordre des paramètres n’a aucune importance, il faut néanmoins remarquer que les valeurs des capteurs seront affichées et envoyées en OSC dans le même ordre que celui des paramètres.
Le programme en plus d’envoyer un message OSC pour chaque capteur va afficher la valeur de ce capteur (uniquement si daemonize=FALSE) sur la sortie standard (stdout) et l’écrire dans le fichier status_file sous la forme :
sensor_code,sensor_name,valeur
Il s’agit ainsi d’un fichier facilement grep-able pour écrire par dessus un script shell d’alerte etc...
Il est possible de mémoriser un état logique. Par exemple si le capteur de mouvement passe à 1, sa valeur dans le programme (et donc sur stdout, dans status_file et dans les messages OSC) est alors bloquée et restera à 1 même si l’état logique du capteur repasse à 0. En réalité et pour éviter les faux-positifs il faut que la valeur reste à memorize_state pendant 2 cycles, c’est-à-dire qu’une première mesure est faite à T=0 et qu’à la deuxième mesure à T=refresh_time la valeur doit toujours être à memorize_state pour être bloquée. Pour débloquer toutes les valeurs il suffit d’envoyer le signal HUP avec kill -HUP <PID>.
En remplaçant <PID> par le PID du démon disponible dans pid_file.
Le fonctionnement en mode démon est fortement conseillé. Le programme se détache alors du processus parent. Le but est d’obtenir un serveur autonome. Le but est d’obtenir un serveur autonome. Pour terminer proprement (fermeture des fichiers, libération de la mémoire...) le démon il faut exécuterkill -INT <PID>
En remplaçant <PID> par le PID du démon disponible dans pid_file.
Quand le programme tourne en mode démon les erreurs et informations ne sont plus affichées, pour identifier un problème il peut être très utile de mettre l’option daemonize à false.
En mode démon, et pour des raisons des sécurité, le programme ne tourne pas en tant que root mais avec l’identité de l’utilisateur user qui est par défaut sensorsd. sensorsd doit exister sur la machine, appartenir au groupe group (pour pouvoir communiquer via le port série) et les fichiers status_file et pid_file doivent lui appartenir.
Le déclenchement du relais s’effectue en envoyant le signal SIGUSR1 au programme.
Le code source est commenté, pour en savoir plus : « may the source be with you ».
Pour la petite histoire : j’ai rencontré quelques problèmes lors du développement au niveau des signaux et je pense qu’il est intéressant d’en parler.
Premier comportement dérangeant : la réception d’un signal termine le sleep() (main.c) en cours, je ne l’avais pas prévu mais c’est bien indiqué dans la page de man... Le principal effet est que la temporisation (entre deux vérifications des valeurs) peut être plus courte que prévue initialement.
Un autre problème plus gênant a été rencontré. Le symptôme : un décalage dans les valeurs entre les différents capteurs après l’utilisation d’un signal. Le problème apparaissait de manière aléatoire. J’ai réussi à le provoquer en envoyant beaucoup de signaux à la fois il est donc paru évident que ce signal devait être reçu à un endroit précis du déroulement du programme pour déclencher le "bug". Après une sorte de mini-profiling du programme (à grands coups de printf()) il s’est avéré que le problème se situait au niveau de l’appel à read() (serial.c, fonction readport()). La réception du signal stoppait le read() bloquant qui renvoyait alors la valeur -1 et ne vidait pas le buffer ce qui avait ensuite pour conséquence le décalage observé... Morale : faire attention aux signal handlers qui peuvent interrompre le programme là où ce n’est pas prévu et toujours vérifier le code retour des appels système (ce que j’avais élégamment esquivé au début).
GNU/Linux
ceux oubliés ou non-cités
Clément NOTIN, 17 ans, lycéen en section Scientifique option SI au lycée René Cassin.
Mail : clement@notin.org
Je remercie tous ceux qui m’ont accueilli : Michel, Jean-Philippe, Sophie, Daniel, Hélène, Bruno, Patrick, Pierre-Gilles, Yves-Armel Martin.