Vous êtes sur la page 1sur 45

Étude de cas : Mise en place de capteurs de température

à bas-coût dans une salle machines

Henri MEURDESOIF
Gestionnaire de parc informatique
ICARE UMS 2877 - Polytech'Lille
Avenue Paul Langevin
59650 Villeneuve d'Ascq

16 Mars 2017 - V 1.0 1/45


SOMMAIRE

Vous qui me lisez, bonjour !

Dans les pages qui suivent je vais vous décrire les étapes de ce projet en suivant simplement les
étapes de son évolution.

Nous allons voir :

Sommaire
01. Pourquoi ce besoin ?................................................................................................................................4
02. Comment contrôler la température?.........................................................................................................4
03. Avec quel matériels ?...............................................................................................................................5
04. Je résume..................................................................................................................................................7
05. Du matériel, mais quelle quantité ?..........................................................................................................7
06. À quel prix ?...........................................................................................................................................11
07. Mais comment cela fonctionne ?...........................................................................................................12
08. Premier montage test : DS18B20 (température)....................................................................................13
09. Second montage test : DHT22 (humidité).............................................................................................16
10. La programmation..................................................................................................................................18
11. Quelques graphiques..............................................................................................................................22
12. La fabrication.........................................................................................................................................25
13. Dernier test avec une mise en œuvre localement...................................................................................27
14. Mise en œuvre en salle...........................................................................................................................28
15. Une interface Web ?...............................................................................................................................29
16. Je résume les difficultés.........................................................................................................................32
17. Conclusion et évolution.........................................................................................................................32
Annexe 1 : 18B20_read_adresses................................................................................................................34
Annexe 2 : DHT...........................................................................................................................................35
Annexe 3 : 18B20PAR_DHT22_115200bauds............................................................................................35
Annexe 4 : probe.py.....................................................................................................................................38
Annexe 5 : probe_average.py.......................................................................................................................39
Annexe 6 : probe18B20P_munin.py............................................................................................................41
Annexe 7 : probeDHT22_munin.py.............................................................................................................42
Annexe 8 : index.php...................................................................................................................................44
Annexe 9 : cold.php.....................................................................................................................................45

16 Mars 2017 - V 1.0 2/45


01. Pourquoi ce besoin ?

Notre matériel est hébergé au Centre de Ressources Informatiques de l'Université de Lille Sciences
et Technologies. En 2014 nous nous sommes retrouvés confrontés à un problème de structure : le faux-
plancher était limité à 600 kg/m2 alors qu'une armoire informatique pouvait accueillir 1 200 kg/m2 en
baies de disques.
De ce fait, nous avons fait les travaux nécessaires dans une nouvelle salle et nous y sommes
installés. Nous en avons profité pour entrer dans le plan de « développement durable » du CRI en mettant
en place un corridor froid.

Une fois cette nouvelle salle informatique en place, j'ai


découvert un nouveau terme : la thermoception1 ! La thermoception
est le sens de perception de la chaleur (chaud) et de l'absence de
chaleur (froid) par la peau.
Autrement dit, certaines personnes trouvaient qu'il faisait froid,
d'autre bon, d'autre chaud et d'autre trop chaud dans les corridors
(chaud et/ou froid) de la salle …
De plus, certaines personnes percevaient aussi une différence de température dans différents points
du même corridor…2

02. Comment contrôler la température?

Vous me répondrez : tout simplement en mesurant la température !

Oui, mais… C'est là que les choses se compliquent un peu, car arrivent les questions :
- Où ? Quand ? Comment ? Etc.
- Ai-je bien fait de faire une armoire avec uniquement des baies de disques, une autre armoire
avec des serveurs, ainsi qu'une autre avec l'équipement réseau ?
- Que se passera t-il quand je vais ajouter ou retirer du matériel ? Quel impact cela aura t-il sur les
flux de température ?

Essayons de voir tout cela dans l'ordre :

• Le thermomètre à mercure : Ils sont interdits à la vente depuis le 1er mars 1999 non pas à cause
de leur inefficacité, mais à cause de la dangerosité pour la santé du mercure qu'ils déversent dans
nos habitations quand ils se cassent accidentellement et aussi grâce à la mise sur le marché de
thermomètres électroniques de toutes sortes pouvant les remplacer efficacement. Cette mesure
réduit aussi les sources d'agents polluants de l'environnement.

• Le thermomètre électronique : Oui, nous avons pu mesurer la température, mais seulement à un


endroit et à un moment donné. Car là se posent d'autres problèmes : le fait d'être dans le couloir et
de tenir le thermomètre influent sur la température mesurée. Le fixer, sortir du couloir et y revenir
pour le relever influent aussi sur celui-ci par l'ouverture du corridor froid.

1 Source : fr.wikipedia.org/wiki/Sens_(physiologie)#Thermoception
2 Image : store.glennz.com/collections/animals/products/less-formal

16 Mars 2017 - V 1.0 3/45


• La station météo multi-capteurs : Elle relève quelques zones en même temps en plaçant une
sonde dans le corridor froid, une autre dans le corridor chaud et la « centrale » dans un endroit
facile à relever et dont notre présence n'influe pas sur les sondes. Mais il faut aller régulièrement
sur place faire les relevés, car cela n'enregistre pas et ne peut être fait à distance.

• Le thermomètre enregistreur USB : Il n''enregistre qu'une zone pendant un laps de temps assez
court.

• L'unité de distribution d'alimentation (Power Distribution Unit) intelligente ou le système de


gestion multi-capteurs avec capteurs de température sont chers… Comptez 10x le prix d'un PDU
standard, soit 1 000 € HT + les sondes (environ 150 € HT le groupes de 3) + si vous ne le faites
pas par logiciel de monitoring/grapheur tels Nagios, Cacti, l'éventuel logiciel de gestion, etc.

• Et relever la température via le matériel en place tels que les serveurs, baies, switchs, etc ? Oui
c'est possible, mais selon le constructeur cela s'avère plus ou moins facile. Et centraliser toutes les
informations via un seul protocole demande d'y consacrer un peu de temps, à trouver la bonne
MIB (Management Information Base, terme informatique désignant une base de données utilisées
pour la gestion des réseaux, manipulable par des protocoles tels que SNMP (Simple Network
Management Protocol)) et à l'exploiter.

De plus :
• Comment connaître la température là où il n'y a pas de machine (partie du corridor encore
inoccupée) ?
• Pourquoi ne pas contrôler aussi la température dans le faux-plafond et le
faux-plancher ?
• Comment relever et enregistrer plusieurs zones à un prix intéressant et
pouvoir consulter le tout à distance ?

Là intervient un concept à la mode le : « Faites-le vous-même » (Do It Yourself) !


Qui plus est popularisé via certains magazines informatique comme : Hackble3

03. Avec quel matériels ?

Dans tous les cas, il y a plusieurs possibilités pour répondre à ce projet :

• Les « cartes » pour brancher les sondes :

- Arduino4 : Micro-contrôleur programmable qui est une sorte de mini


automate fait pour exécuter du mono-tâche. Une fois que l'on charge son programme,
il devient autonome et l’exécute en boucle. Il est plus proche de l'électronique que le
Raspberry. Idéal pour commander des volets roulants, régulation de chaudière,
gestion d'aquarium, pilotage de moteurs …

- Raspberry5: Sorte de mini PC avec un système d'exploitation, donc


peut faire tourner plusieurs programmes. Les interactions avec la carte sont plus
3 www.hackable.fr
4 www.arduino.cc
5 fr.farnell.com/raspberrypi-boards

16 Mars 2017 - V 1.0 4/45


faciles. Il est plus proche de l'informatique que l'Arduino. Idéal pour l'utiliser comme serveur, pour la
surveillance vidéo (domotique), comme passerelle …

On peut donc l'utiliser comme un serveur pour faire les relevés, les mémoriser, les consulter, etc.
Dans un premier temps ce fût le choix du Raspberry que j'ai retenu en modèle B Rev 2, car (à l'époque,
fin 2014) ce modèle était le plus courant, disponible et pas cher.

• Les capteurs de température6 :

- Analogique : faible coût, « petite précision » (ou il faut corriger l'erreur de


linéarité), ultra simple à utiliser.

- Numérique : coût un peu plus important, plus précis, puce identifiable.

De plus, le premier n’est pas directement utilisable sur le Raspberry Pi car il délivre une tension/un
courant (donc analogique) et nécessite la mise en œuvre d’une carte de mesure avec convertisseur
analogique => digital (ADC ou CAN).

Dans les seconds, une famille est particulièrement adaptée au Raspberry Pi, c’est la série de 1-wire,
ou capteurs utilisants un seul fil (nous verrons qu’il en faut tout de même trois en comptant
l'alimentation). Le Raspberry Pi (en fait Linux) quant à lui embarque le pilote capable de gérer cette
famille de composants.

Dans mes différentes lectures (magazines, sites web, forums…) les plus souvent cités sont les
capteurs de températures Dallas 18B20.

De ces faits , ils seront retenus pour la suite du projet.

• Mais là encore, quel modèle ? :

- DS18B20 : Il faut relier la patte de masse (Terre, ground, GND) à gauche et celle
de tension d'alimentation (Power Supply Voltage/VDD) à droite soit du côté du
circuit, soit du côté de la sonde. La patte centrale étant celle des données (Data
In/Out, DQ).

- DS18B20-PAR7 : Celui-ci est déjà parasité en interne. 8 De ce fait, pour


moi il me fait « économiser » soit un fil, soit le fait de devoir parasiter les
deux pattes côté sonde, je l'ai donc retenu. (3,0-5,5V DC / 0-100 %RH à ±
2 % / -10 - +85°C à ± 0,5°C / Période de détection moyenne : 2s)

- DHT229 : Il existe aussi des sondes de température/humidité


(3,3-6V DC / 0-100 %RH à ± 2 % / -40 - 80°C à ± 0,5°C / Période de détection moyenne : 2s)

6 www.mon-club-elec.fr/pmwiki_mon_club_elec/pmwiki.php?n=MAIN.ArduinoExpertCapteursComplexesDS18B20Test
7 datasheets.maximintegrated.com/en/ds/DS18B20-PAR.pdf
8 www.maximintegrated.com/en/app-notes/index.mvp/id/203
9 www.sparkfun.com/datasheets/Sensors/Temperature/DHT22.pdf

16 Mars 2017 - V 1.0 5/45


• Les câbles : Suite au déménagement j'ai un bon stock de câbles RJ45-5E qui devrait convenir.

Et : Une platine de test, quelques résistances (4,7 kΩ et moins selon la longueur des câbles).

04. Je résume

• À un prix intéressant je veux :


◦ Mesurer la température dans :
▪ différents points de mes salles (même là où
il n'y a pas encore de machine)
▪ le faux-plancher
▪ le faux-plafond

◦ Les enregistrer
◦ Les consulter à distance

Mes salles ? Oui depuis le début je dis « ma salle », mais vu qu'en plus de la salle machines, nous
avons une seconde pièce où est implantée notre librairie de sauvegarde. Et que cette pièce est dépourvue
de ventilation, pourquoi ne pas aussi y contrôler la température … ?

• Pour cela je vais :


◦ Mettre des sondes DS18B20-PAR
◦ et une DHT22 par zone (faux-plancher, salle machine, faux-
plafond et librairie)
◦ le tout sur un Arduino UNO

05. Du matériel, mais quelle quantité ?

Sachant que les flux d'air sont : 10

Et que j'aimerai mesurer les


différences entre le bas et le haut d'une
armoire de machines et d'un bout à l'autre des
corridors, voila ce que pourrait être
l'emplacement des sondes et de ce fait leur
quantité :

10 Source image : www.dataclean.com/data-center-curtains/aisle-containment.htm

16 Mars 2017 - V 1.0 6/45


• Sondes DS18B20-PAR : 62

9 d'un côté du corridor froid


9 de l'autre côté

8 d'un côté du corridor chaud


9 de l'autre côté

5 d'un côté de la librairie


6 de l'autre côté

8 dans le faux-plafond

8 dans le faux-plancher

• Sondes DHT22 : 4 (en vert dans les schémas ci-dessus)

• Arduino UNO : 1

16 Mars 2017 - V 1.0 7/45


• Les câbles : Je vois deux solutions :
- soit je mets des longueurs au hasard et j'aurai des surplus de-ci de-là à enrouler et qui
« traîneront » dans les armoires en plus de ce déjà présents…
- soit je mesure les chemins que parcourront les câbles dans les armoires pour les faire plus
courts…

Pour faire quelque chose de propre, qui n'est pas censé évoluer et économiser du câble j'ai préféré
prendre un peu de temps et mesurer.

Par exemple pour atteindre le faux-plafond il faut que je fasse ce cheminement :

3,0m

1,0m
4,5m
0,5m

0,5m
0,5m d
ans l'amoir
e

Schématiquement cela donne : 3,0 > B (plafond)


1,0

Il me faut donc de l'Arduino au plafond : 10 m


0,5
4,5
0,5

Une fois dans le plafond : 20m


0,5
Arduino >
>B+H2
9 2,5 2,5 10
2,5

11 2,5 12 2,5 13
2,5

14 2,5 15 2,5 16

16 Mars 2017 - V 1.0 8/45


Au final 62 sondes DS18B20-PAR et 4 sondes DHT22 sur 5 câbles en parallèles le tout sur une
longueur totale de 156m de câbles grâce à 75 raccords Wago

7 Côté pair : 30m (4+26)

4m

29-2 6 27-2 17-2


1,2 1,2 1,2
m 30 28 18

1,0
1,0

1,0
5m
10
31-2 25-2 19-2

10m
1,2 1,2 1,2
32 26 20

1,0
1,0

1,0
Plafond : 30m (10+20) Plancher : 25m (5+20)
H1-4 H2-4 33-2 23-2 2,5 21-2
1 2,5 2,5 2 9 2,5 2,5 10
1,2 1,2 1,2
34 24 22
2,5

2,5

3 2,5 4-2 2,5 5 11 2,5 12-2 2,5 13 10m


2,5

2,5

5,0 53-2 5,0 55-2


6 2,5 7-2 2,5 8 14 2,5 15-2 2,5 16 1,0 H4-4 1,0 1,0
52 54 56
2,0

57-2 5,0 59-2 5,0 61-2


1,0 1,0 1,0
58 60 62
Côté impair : 32m (10+22) Coté libraire : 39m (11+28)
47 45 35
1,2 1,2 1,2
46-2 6,0 44-2 H3-4
1,0

1,0
1,0

49 43 37
1,2 1,2 1,2
48-2 42-2 36-2
1,0

1,0
1,0

51 41 39
1,2 1,2 1,2
50-2 40-2 2,5 38-2

• Soit en câbles : 156 m


La plus grande longueur étant 11m on peut considérer qu'il faut 20 câbles réseau de 10m
(16 + quelques uns pour les sondes directement reliées aux points de connexion comme les
numéros 4, 15, 17, 25, 42, etc.).

16 Mars 2017 - V 1.0 9/45


• Raccords : 75
J'ai choisi comme raccord des WAGO 221-41211
Pour des sections de conducteurs de 0,14 à 4 mm² (fil fin) et 0,2 à 4 mm² (un fil
ou plusieurs fils). De ce fait, il est censé couvrir des sections de fil informatique RJ45
de section 0,5mm². Hors il est arrivé (fréquemment) lors de l'installation que un fil soit
mal pincé et ne fasse pas/plus contact en manipulant une sonde. Pourtant, comme vous
le lirez après, en doublant mes fils et en les étamants je pensais être tranquille sur ce
point… Donc j'avoue que si je fais de nouveau un tel projet je regarderai les alternatives possibles…

• Gaine thermorétractable pour isoler les pattes des sondes : 2m


DS18B20 : 62 x 2 pattes de 1,5cm = 186 cm
+ DHT22 : 4 x 3 pattes de 1,0cm = 12 cm

• Gaine thermorétractable pour isoler le câble RJ45 : 3m


68 sondes x 4 cm = 272 cm

• Divers : 1 platine de prototypage et des résistances

06. À quel prix ?

183,40 € TTC : 70 sondes 18B20-PAR + DHT22


44,00 € TTC : Raspberry modèle B Rev 2
0,00 € TTC : Câbles RJ45 5E (en stock)
44,15 € TTC : Raccords Wago x100
17,76 € TTC : Gaines thermorétractable 9mm/3mm 5m et 3mm/1mm 6m
xx,xx € TTC: Quelques jours d'un Assistant Ingénieur...
-------------------
289,31 € TTC : au total

Soit environ le coût de 6 sondes toutes faites (sans PDU ou système multi-capteurs pour les
brancher). Cela semble acceptable.

À ce stade, j'avoue être passé à côté d'une information : Le Raspberry ne sait pas dialoguer avec
un capteur DS18B20-PAR… Comme je les avais déjà commandés et reçus… Je me suis tourné vers la
solution Arduino Uno, modèle de base qui devrait être suffisant à mon projet.

Arduino UNO= 19,50 € TTC


http://www.gotronic.fr/art-carte-arduino-uno-12420.htm
(on peut trouver des génériques au tour des 10 € pièce sur des sites de ventes bien connu)

Total = 308,81 € TTC

Note : On peut trouver tout cela moins chers sur des sites de ventes bien connus…

11 www.conrad.fr/ce/fr/product/1188439/Borne-de-polarite-WAGO-221-412-Nombre-de-poles-2-transparent-orange-100-pcs

Mars 2016 - V 0.1 10/45


07. Mais comment cela fonctionne ?12

Comme dit précédemment, la sonde numérique


18B20 est de type 1-wire. Le bus 1-wire est basé sur une
architecture maître-esclave. Le maître est l’équipement
qui contrôle le bus, interroge les périphériques, ou leur
envoie des ordres.

Dans notre cas, le maître sera l'Arduino et il n’y


aura 62 esclaves, nos capteurs de température DS18B20.

Sur le schéma ci-dessus, on voit que le bus 1-wire


est relié au +3,3 V par une résistance de tirage (pull-up)
de 4,7 kΩ. En fonction de la longueur des fils de liaison, il sera parfois nécessaire d’ajuster sa
valeur. Tous les appareils sont branchés en parallèle sur le bus et c’est en mettant (ou pas) leur sortie à la
masse qu’ils envoient les données au maître.

Le maître initialise le bus en forçant un zéro pendant plus de 480 µs. Les esclaves lui répondent en
mettant eux aussi leur sortie à zéro pendant un certain temps, indiquant ainsi leur présence.

Le maître déclenche une lecture dite ROM et l’esclave renvoie son identifiant unique, gravé lors
de la fabrication. Chaque identifiant a une longueur de 48 bits (8 octets) encadré par un octet indiquant le
type de matériel (ici le type est : sonde de température = 28H) et un octet de CRC qui permet au maître
de vérifier qu’il a correctement reçu les informations d’identification du composant. Les 48 bits
permettent d’individualiser à peu près 280 000 milliards de composants…

Dans le mode parasite, l’esclave est connecté en reliant ses broches d’alimentation ensemble et en
les reliant à la masse. Il n’y a plus besoin dans ce cas que de deux fils pour assurer la liaison avec les
esclaves connectés au bus 1-wire.

Lorsque le bus est au niveau 1, un condensateur interne au composant esclave est chargé, et c’est
lui qui assurera l’alimentation du composant pendant que le niveau du bus sera bas.

Le DS18B20 a une gamme de mesure qui s’étend de -55 °C à +125 °C. Il transmet sa mesure
directement en degrés Celsius, codée sur 16 bits. Sa précision est de ±0,5 °C entre -10 °C et +85 °C.

Un tour sur Internet et chez les vendeurs asiatiques montre que ce composant est disponible sous
forme de boîtier TO92 (cela ressemble à un transistor, mais ce n’est pas un transistor !) ou de sonde
étanche, montée, avec les fils de sortie.

12 www.framboise314.fr/mesure-de-temperature-1-wire-ds18b20-avec-le-raspberry-pi/

Mars 2016 - V 0.1 11/45


08. Premier montage test : DS18B20 (température)

8.1. : Je n'entrerai pas dans le détail de l'utilisation d'un Arduino, une simple recherche sur un
moteur de recherche vous donnera toutes les informations nécessaires. Mais en bref il suffit
- de télécharger le logiciel « Arduino » (1.6.7 au moment du projet)
www.arduino.cc/en/Main/Software
- de l'installer
- de brancher la carte via câble USB sur votre machine

8.2. : Toutes mes manipulations sont faites sous Linux


(Red Hat Enterprise Linux 6.7, noyau 2.6.32 64b)

8.3. : Je fais mon premier montage :

Mars 2016 - V 0.1 12/45


8.4. : Et je trouve sur internet mon premier programme qui me donnera le numéro unique d'une
sonde DS18B20

ATTENTION :
• À la première ligne : j'insère une librairie (disponible sur le site Arduino) qu'il vous
faudra télécharger auparavant pour que le programme puisse l'utiliser.
• À la seconde ligne de bien mettre le numéro de la PIN de l'Arduino :
SENSOR_PIN 2 (sur mon précédent schéma elle est aussi sur la PIN 2)

Note : Le programme 18B20_read_adresses est en Annexe 1, pour être copié/collé ;)

Mars 2016 - V 0.1 13/45


8.5. : Je le transfère dans l'Arduino

8.6. : via un moniteur série (du logiciel Arduino)


j'observe le résultat du programme.

Et de ce fait j'ai le numéro de la sonde :

8.7. : Et pour les identifier visuellement : je les numérote via un coup de feutre argenté et un feutre
pointe fine (oui il faut écrire très petit...).

8.8. : je fais pareil avec les autre sondes

Je change de sonde, j'appuie sur le bouton RESET sur la carte Arduino, l'adresse de la nouvelle
sonde apparaît dans le moniteur série, je copie/colle l'adresse et je répète l'opération encore soixante
fois… Cela me donne ce fichier :

DeviceAddress Probe01 = { 0x28, 0x1C, 0xB5, 0xA0, 0x06, 0x00, 0x00, 0xDD };
DeviceAddress Probe02 = { 0x28, 0x1D, 0xC3, 0xA0, 0x06, 0x00, 0x00, 0x1B };
DeviceAddress Probe03 = { 0x28, 0xAF, 0xD6, 0xA0, 0x06, 0x00, 0x00, 0xDC };

DeviceAddress Probe62 = { 0x28, 0xFC, 0xD3, 0xA0, 0x06, 0x00, 0x00, 0x79 };

8.9. : Pour mieux le lire, je convertis l’hexadécimale en décimale :

Mars 2016 - V 0.1 14/45


$ cat 18B20_adresses_HEX | sed "s/DeviceAddress//g" | sed "s/0x//g" | sed "s/{ //g" | sed "s/, //g" |
sed "s/ };//g" > 18B20_adresses_DEC

Ce qui me donne :
Probe01 = 281CB5A0060000DD
Probe02 = 281DC3A00600001B
Probe03 = 28AFD6A0060000DC

Probe62 = 28FCD3A006000079

Voilà 62 sondes identifiées et numérotées. Et aucune de défaillante (ouf !).

09. Second montage test : DHT22 (humidité)

9.1. : Montage

Mars 2016 - V 0.1 15/45


9.2. : Programme

Note : Le programme DHT est en Annexe 2

ATTENTION : à la seconde ligne de bien mettre le numéro de la PIN de l'Arduino :


DHTPIN 3 (sur mon précédent schéma elle est sur la PIN 5)

Avec une réponse dans le moniteur série :


Humidity: 42 %
Temperature: 18.5 °C

Une DHT22 n'a pas de numéro d'identification, ce qui n'est pas trop grave puisque j'en mets une
par zone et que chacune est reliée sur une entrée de l'Arduino, donc selon l'entrée/PIN interrogée on sait
quelle sonde/zone répond.

Changement de sonde, RESET pour les trois autres sondes : OK elles fonctionnent.

Mars 2016 - V 0.1 16/45


10. La programmation

Je vais devoir compliquer un peu les choses :

• Les deux premiers programmes ne gèrent qu'une sonde à la fois. Je vais devoir maintenant en
gérer « N »…

• En même temps des DS18B20 et des DHT22.

• Et avoir les résultats non plus via le moniteur série de l'Arduino, mais via un serveur > USB >
Arduino.

Pour ce dernier point (faire les relevés et dialoguer avec l'Arduino), je vais utiliser Python côté
serveur et une communication série côté Arduino.

10.1. : Programme Arduino

Il serait trop long à mettre ici.


Vous le retrouverez donc en Annexe 3 : 18B20PAR_DHT22_115200bauds

Attention :

Il y a trois librairies à inclure, leurs adresses sont dans le programme.

Et comme souvent, il faut faire attention à certaines lignes. Ici se sont les lignes :

• ONE_WIRE_BUS 2 qui définit sur quelle port de l'Arduino sont connectées les DS18B20.

• La note pas de « ; » après une commande « défine » est une note personnel, car (peut être
comme vous) je découvre le langage Arduino.

• TEMPERATURE_PRECISION 9 : valeur par défaut qui me convient

• FirstDHT = 3 : la première sonde DHT sera sur le port 3 de l'Arduino

• nSensors = 4 : nous aurons 4 sondes DHT

Cela induit que la première sonde DHT sera sur le port 3, la seconde sur le 4, la troisième
sur le 5 et la quatrième et dernière sera sur le port 6.

• Serial.begin(115200) : donne la vitesse de transmission du port série, par défaut 9600 (j'en
reparlerai un peu plus loin dans la section Version 2).

Sinon, le programme en lui même est simple :

• On importe les librairies nécessaires.


• On définie les variables
• On ouvre un port de communication

Mars 2016 - V 0.1 17/45


• Et on attend un message…
• Si le message reçu (du serveur via python sérial, que j'expliquerai après) est 1
◦ On compte les sondes DS18B20 (température)
• Si le message est 2
◦ On interroge les sondes DS18B20 (température)
◦ On imprime l'identifiant et la température pour chacune
• Si le message est 3
◦ On interroge les sondes DHT22 (humidité)
◦ On imprime son « numéro » (par exemple 4 pour la quatrième), l'humidité et la
température pour chacune

10.2. : Programme Python pour relever les sondes

J'avoue qu'à la base, je ne suis pas programmeur. Ce projet fut pour moi l'occasion de m'initier à
Python (et de découvrir autre chose que Bash, Sed et Awk).

Mais pourquoi Python ? Simplement parce qu'il est fortement utilisé au sein de mon laboratoire et
que je pouvais avoir de l'aide localement par le biais de mes collègues. On le dit un langage facile, ce que
je confirme ;-)

Donc pour faire les relevés et dialoguer avec l'Arduino, je vais utiliser Python côté serveur pour
établir une communication de type série avec l'Arduino via le port USB.

Pour cela il faut installer la librairie pyserial, en tant que root (administrateur sous Linux), cela
donne les commandes suivantes :
$ yum install python-pip
$ pip install –upgrade pip
$ pip install pyserial
$ yum install numpy

Le programme probe.py est en Annexe 4, toujours pour que vous puissiez le copier/coller et
l'utiliser sans à avoir à le retaper.

10.2.1 : Si on le lance simplement :

$ ./probe.py

IL NE RENVOIE RIEN A L ECRAN ! Pour contrôler il faut regarder dans le fichier qu'il a créé :

$ cat /tmp/probe
14/01/16 05:35:29 61
14/01/16 05:35:29 2870CCA0060000FD 28.00
...
14/01/16 05:35:59 28DFCAA006000019 25.50
14/01/16 05:36:30 Probe1 45.00 16.60
14/01/16 05:36:31 Probe2 18.60 33.10
14/01/16 05:36:32 Probe3 No good boss.
14/01/16 05:36:33 Probe4 29.90 25.30

Mars 2016 - V 0.1 18/45


• La première ligne indique que nous avons 61 sondes (DS18B20 sur 62, donc une est mal
branchée et vous comprenez d'où vient ma critique des connecteurs Wago).
• Les suivantes nous donnent l'identifiant et la température sonde par sonde,
• Et nous terminons par le « numéro », l'humidité et la température des 4 sondes DHT22

Vous noterez au passage que la 3ème à mal répondu…

10.2.2 : Le mode débug

Pour afficher directement des informations ET AVOIR LA DUREE DES ETAPES ET DU


PROGRAMME

$ ./probe.py debug

Serial<id=0x1c90590, open=True>(port='/dev/ttyACM0', baudrate=115200, bytesize=8, parity='N',


stopbits=1, timeout=30, xonxoff=False, rtscts=False, dsrdtr=False)
-> sleep10 : start : 14/01/16 10:09:07
-> sleep10 : end : 14/01/16 10:09:17
-> 1 : start : 14/01/16 10:09:17
-> end : 14/01/16 10:09:43
-> 2 : start : 14/01/16 10:09:43
-> end : 14/01/16 10:10:43
-> 3 : start : 14/01/16 10:10:43
-> end : 14/01/16 10:11:17
--------------------------------------------------------------------------------
14/01/16 10:09:43 61
14/01/16 10:09:43 2870CCA0060000FD 28.50
...
14/01/16 10:10:13 28DFCAA006000019 25.50
14/01/16 10:10:44 Probe1 45.00 16.60
14/01/16 10:10:45 Probe2 18.30 33.40
14/01/16 10:10:46 Probe3 26.00 26.60
14/01/16 10:10:47 Probe4 29.90 25.30
--------------------------------------------------------------------------------

Au passage on peut noter que le programme dure 10:09:07 -> 10:11:17 = 2mn10s
Je reviendrai sur ce point par la suite…

10.3. : Automatisons tout cela

Une fois le programme dans l'Arduino celui-ci tourne en boucle dès qu'il est alimenté, donc rien
d'autre à faire.

Pour le programme en python, je vais simplement l'ajouter dans la table Cron, pour un lancement
toutes les trois minutes vu que le programme dure 2mn10s. Pour cela :

$ crontab -e

MAILTO=""
# NE PAS ENVOYER DE MAIL A CHAQUE CRON

# _________________________ 1. Minute - Minutes after the hour (0-59)


# |
# | ______________________ 2. Hour - 24-hour format (0-23).

Mars 2016 - V 0.1 19/45


# | |
# | | ___________________ 3. Day - Day of the month (1-31)
# | | |
# | | | ________________ 4. Month - Month of the year (1-12)
# | | | |
# | | | | ______________5. Weekday - Day of the week. (0-6, 0 indicates Sunday)
# | | | | |
#__|_____|_____|_____|____|___Command____________________________________________________________

# TOUTES LES 3 MINUTES interrogation des sondes de températures/humidités et enregistrement dans /tmp/probe
*/3 * * * * /usr/bin/python /root/arduino/programmes/probe.py

Vérifier la prise en compte : $ crontab -l

et
$ ll /tmp/
-rw-r--r-- 1 root root 2671 14 janv. 10:32 probe
… attendre 3 minutes ...
$ ll /tmp/
-rw-r--r-- 1 root root 2671 14 janv. 10:35 probe

OK le programme tourne et toutes les 3 minutes le fichier temporaire change.

10.4. : Préparons la suite : moyennes, écart type et affichage

Lire manuellement les 66 valeurs de températures et avoir directement une idée de la moyenne est
illusoire, encore plus si on veut le faire par zone (faux-plancher et faux-plafond, corridor chaud et froid,
librairie), donc je vais automatiser cela par un autre programme et en profiter pour avoir aussi les écarts
types par zone et préparer cela en vue de l'afficher dans une page Web.

Pour cela rappelez-vous que j'ai, par exemple pour une rangée d'armoires, mis 9 sondes dans le
corridor froid et 9 dans le corridor chaud.

Et qu'elles sont numérotées ainsi :


29 27 17
30 28 18

31 25 19
32 26 20

33 23 21
34 24 22

Mars 2016 - V 0.1 20/45


Donc si je veux isoler cette zone du corridor froid cela concerne les sondes 18, 20, 22, 24, 26, 28,
30, 32 et 34. Et si je veux toute la zone corridor froid il faut que j'ajoute l'autre côté de cette zone (sondes
35, 37, 39 … 47, 49, et 51).

C'est ce que je définie au début du programme probe_average.py est en annexe 5.

Puis je calcule la moyenne pour chaque zone, ainsi que son écart type.

J'enregistre le tout dans un fichier et je l'encapsule de balises php, car les résultats me serviront par
la suite dans des pages web.

Pour le tester : $ ./probe_average.py (pas de retour à l'écran)

Le résultat : $ cat /tmp/probe_calcul


<?php
$P01 = 17.50;
$P02 = 15.50;
$P03 = 23.50;
$P04 = 26.00;
...
$P1t = 14.80;
$P1h = 38.70;
$floor_moyenne = 21.1;
$floor_ecartype = 4.9;
...
?>

Idem, automatisations via une mise en cron et un lancement toutes les trois minutes

# TOUTES LES 3 MINUTES calcul de la moyenne des sondes et préparation du fichier php dans /tmp/probe_calcul
*/3 * * * * /usr/bin/python /root/arduino/programmes/probe_average.py

11. Quelques graphiques...

Voilà maintenant j'ai des sondes et je les relève régulièrement. Mais visuellement, pour suivre
tout cela, ce n'est pas terrible de lire des fichiers textes. Comme une image vaut mieux qu'un long
discours, un graphique vaut mieux que plein de chiffres !

Il existe différentes façon de faire des graphiques :


• Pour les administrateurs système :
◦ RRDTool directement
◦ ou via un logiciel comme Cacti, Nagios Munin, etc.
• Pour rester dans python la librairie Matplotlib
• Etc

Mars 2016 - V 0.1 21/45


11.1. : Les programmes

Personnellement, ayant déjà Munin en place et étant lancé dans mes programmes en Python, je
combine les deux.

Encore une fois merci à mon moteur de recherche pour trouver des réponses à mes questions et
merci à ces personnes qui partagent leurs astuces sur le net… Ma source d'inspiration fut donc :
http://munin-monitoring.org/wiki/HowToWritePlugins

Vous trouverez ainsi en annexe 6 et 7 mes programmes probe18B20P_munin.py et


probeDHT22_munin.py.

Pas grand-chose à dire dessus, chacun trouve les valeurs d'un type de sonde et fait le nécessaire
pour que Munin puisse les exploiter.

Test manuel : $ ./probe18B20P_munin.py


temperature.value 19.0

$ ./probeDHT22_munin.py
temperature.value 16.6
humidity.value 45.0

avec l'option config : $ ./probeDHT22_munin.py config


graph_title Temperature and Humidity Monitoring Probe1
graph_args --base 1000 -l 0
graph_vlabel Temperature and Humidity
graph_category temperature
temperature.label Teimperature (C)
temperature.type GAUGE
temperature.warning 25:30
temperature.critical 30:
humidity.label Humidity (%)
humidity.min 0.0
humidity.max 100.0
humidity.warning 60
humidity.critical 65

11.2. : Mise en place dans Munin

Je ne vais pas expliquer chaque étape, car tout se trouve dans la documentation de Munin.

$ cp probeDHT22_munin.py /usr/share/munin/plugins/DHT22_1

$ ln -s /usr/share/munin/plugins/DHT22_1 /etc/munin/plugins/DHT22_1

Test via Munin :


$ cd /etc/munin/plugins
$ munin-run DHT22_1
temperature.value 16.6
humidity.value 45.0

Mars 2016 - V 0.1 22/45


$ munin-run DHT22_1 config
graph_title Temperature and Humidity Monitoring Probe1
graph_args --base 1000 -l 0
graph_vlabel Temperature and Humidity
graph_category temperature
temperature.label Teimperature (C)
temperature.type GAUGE
temperature.warning 25:30
temperature.critical 30:
humidity.label Humidity (%)
humidity.min 0.0
humidity.max 100.0
humidity.warning 60
humidity.critical 65

• Il reste à copier encore 3 fois le programme en DHT22_2, DHT22_3 et DHT22_4


• Faire leurs liens
• Et les éditer pour changer les lignes 2 et 4 en partant du bas et faire correspondre le numéro
du nom du programme DHT22_X avec les variables de ces 2 lignes

if __name__ == "__main__":
if len(sys.argv) == 2:
print_munin_config(sys.argv[1],"X")
else:
timestamp, humidity, temperature = get_temperature_from_sensor("X") # Probe Number
print_temperature_for_munin(humidity,temperature)

• Maintenant je dois « dire » à Munin d'utiliser ces programmes


• Pour cela il faut créer le fichier :

$ vi /etc/munin/plugin-conf.d/dht
[DHT22_1]
user root

[DHT22_2]
user root

[DHT22_3]
user root

[DHT22_4]
user root

• Et pour qu'il commence à les utiliser, il suffit de le relancer :


$ service munin-node restart
Stopping Munin Node agents: [ OK ]
Starting Munin Node: [ OK ]

• Il reste à faire la même chose pour les sondes DS18B20

Par contre je ne vais pas copier à la main 61 fichiers, donc une petite ligne de commande pour le
faire à ma place :
$ for i in {1..9}; do cp probe18B20P_munin.py /usr/share/munin/plugins/18B20P_0$i ; /bin/sed -i "s/\"01\"/\"0$i\"/g"
/usr/share/munin/plugins/18B20P_0$i ; done

Mars 2016 - V 0.1 23/45


$ for i in {10..62}; do cp probe18B20P_munin.py /usr/share/munin/plugins/18B20P_$i ; /bin/sed -i "s/\"01\"/\"$i\"/g"
/usr/share/munin/plugins/18B20P_$i ; done

• Pareil pour modifier les lignes des 62 fichiers :

$ for i in {1..9}; do ln -s /usr/share/munin/plugins/18B20P_0$i /etc/munin/plugins/18B20P_0$i ; done

$ for i in {10..62}; do ln -s /usr/share/munin/plugins/18B20P_$i /etc/munin/plugins/18B20P_$i ; done

• Et créer le fichier de configuration de Munin :

$ for i in {1..9}; do echo "[18B20P_0$i]" >> /etc/munin/plugins/18b20p ; echo "user root" >> /etc/munin/plugins/18b20p ;
echo "" >> /etc/munin/plugins/18b20p ; done

$ for i in {10..62}; do echo "[18B20P_$i]" >> /etc/munin/plugins/18b20p ; echo "user root" >> /etc/munin/plugins/18b20p ;
echo "" >> /etc/munin/plugins/18b20p ; done

Je sais, on peut simplifier un peu ces lignes de codes, mais j'aime bien faire une première passe sur
quelques fichiers, ici 9 et continuer si tout s'est bien déroulé… Chacun ses petites habitudes...

Voilà tout fonctionne !


Il ne reste plus qu'à passer du prototypage à la fabrication des sondes !

12. La fabrication

12.1. : Avant de sortir le fer à souder, encore un peu de réflexion et de schémas…

Un fil RJ 45 est constitué de 4 paires : Orange + orange/blanc


Vert + vert/blanc
Marron + marron/blanc
Bleu + bleu/blanc

Le fait de réunir les fil par paire réduit les risques en cas de coupure d'un fil et réduit de moitié la
résistance, comme elle sera importante vu les longueurs de câbles, c'est intéressant.

Donc avec 4 « fils » (4 groupes de 2) je devrai pouvoir faire :

Côté Arduino :
noir = GND ? = data DHT22 bleu = data DS18 rouge = Vcc 5V

Côté câble RJ45 :


marron/marron-blanc vert/vert-blanc bleu/bleu-blanc orange/orange-blanc

Mars 2016 - V 0.1 24/45


Voyons cela concrètement dans le cas du faux-plancher :

Arduino RJ45

Maintenant je sors :

• le mètre ruban, la pince et je coupe mes longueurs de câbles en pensant à les repérer
• Je coupe aussi mes gaines thermorétractables
• la pince à dénuder ou le cutter, attention au doigts…
• le fer à souder…

Mars 2016 - V 0.1 25/45


13

13. Dernier test avec une mise en œuvre localement

Je branche le tout sur mon bureau et ?


Rien !

Je débranche, rebranche un fil : OK


Ajoute un second : Rien...

Mince ! Comme je l'avais lu sur internet et vous le disais au chapitre « 7 : Comment cela
fonctionne » Le bus 1-wire est relié au Vcc par une résistance de tirage de 4,7 kΩ. En fonction de la
longueur des fils de liaison, il sera parfois nécessaire d’ajuster sa valeur.

Mais les informations sur le sujet son rares, je n'ai trouvé que sur :
http://www.framboise314.fr/mesure-de-temperature-1-wire-ds18b20-avec-le-raspberry-pi/
Où l'on site du 200m, 10 capteurs et 1,5 kΩ de résistances

13

Mars 2016 - V 0.1 26/45


Théoriquement14 : Soit R( ) la résistance d'un fil de section S (m2) et de longueur l (m),
fabriqué dans un matériau de résistivité: ( m)

Application: calculons la résistance de notre fil de cuivre RJ45

Résistivité du cuivre : = 1,7 x 10-8 Wm

Longueur : l = 156 m

Section (0,5mm2) : S = 0,5 x 10-6 m2


156 m
R=1,7×10−8 Ωm ≈5,3Ω
0,5×10−6 m2
Mais comme je mets 2 fils ensemble (marron avec marron/blanc) pour en faire un seul, c'est
comme si je doublais la section :

156 m
R=1,7×10−8 Ωm ≈2,65Ω
1,0×10−6 m2

OK… mais j'en fais quoi ? J'avoue que n'étant pas du domaine, il faudrait que je creuse un peu
plus pour savoir si je dois simplement déduire 2,65Ω des « habituels » 4,7 kΩ des exemples et de ce fait
mettre une résistance de 4,6 kΩ ?

En pratique : Je n'avais qu'une résistance de 4,7 kΩ et une autre de 470 Ω à ma disposition. J'ai
essayé celle de 470 Ω avec mes 156m de câbles et cela à fonctionné.

14 webetab.ac-bordeaux.fr/Pedagogie/Physique/Physico/Electro/e07fil.htm

Mars 2016 - V 0.1 27/45


14. Mise en œuvre en salle

15. Une interface Web ?

Voilà, tout est en place. Maintenant comment vais-je visualiser toutes mes sondes sans regarder X
graphiques ? En résumant le tout par une petite page Web qui me conduira si besoin aux graphiques.

14.1. : Encore un peu de code… Mais HTML/CSS cette fois :

Mars 2016 - V 0.1 28/45


En annexe le code des pages, mais encore une fois rien de compliqué :

• Une demande de rafraîchissement automatique de la page toutes les 3 minutes :


<meta http-equiv="refresh" content="180;url=http://adresses/temperatures/" />

• Une image en fond, servant de point de référence, autrement dit les mes zones de textes se
baseront sur cette image pour se positionner d'après le point en HAUT a GAUCHE de l'image
qui a la référence 0px : 0px.

• Inclure le précédent calcul de moyenne et écart type :


<?php include('/tmp/probe_calcul'); ?>

• Inclure un petit librairie15 javascript pour afficher proprement l'heure (au lieu de faire une
grosse programmation à la main…)
<script type="text/javascript" src="date.js"></script>
<script>
(function ()
{
document.write(new Date().toString("hh:mm tt"));
})();
</script>

• Et placer les liens menant vers les pages des zones : froide, chaude, plancher/plafond et
librairie.

Le résultat :

Les autres pages :

15 www.datejs.com/

Mars 2016 - V 0.1 29/45


Pareil, rafraîchissement toutes les 3 minutes, placer le texte par rapport à l'image de fond, inclure notre fichier php de
moyennes, écarts types et valeurs des sondes, et placer les liens menant aux graphique de Munin :

Les avis divergent, certaines personnes aiment la représentation 3D, d'autres préfèrent un tableau
(2D), personnellement pour voir de suite s'il fait chaud en bas à droite par rapport à en haut à gauche ou
comme nous avons eu le cas : avoir une température dans la moitié du faux-plancher et une autre dans
l'autre moitié, mais une moitié étant le faux-plancher coupé en diagonale, car une climatisation tournait
plus que l'autre… Rien ne vaut, pour moi, une représentation 3D…

Maintenant rien ne vous empêche de faire les deux.

Mars 2016 - V 0.1 30/45


Il ne reste plus qu'à cliquer sur le « numéro » d'une sonde, qui est un lien, pour aller dans Munin
et avoir son graphique :

16. Je résume les difficultés

• Les exemples de programmes trouvés sur internet sont souvent mono-capteur, ou avec 2 ou 3
capteurs mais, dans ce cas, utilisés manuellement et unitairement en multipliant les lignes de
codes. Mais jamais mis dans une boucle pour, comme ici, en gérer des dizaines.

• Les câbles que j'ai bien fait de faire sur mesure, car cela diminue la longueur totale, donc la
résistance.

• Justement en parlant de la résistance, tous les exemples du net étant basés sur un branchement via
une platine de test, donc une longueur de câble nulle, ils donnent la valeur de 4,7 kΩ, hors plus on
met de câbles, plus il faut baisser la résistance. Mais de combien ? Je ne connais pas assez ce
domaine pour répondre à cette question.

• Section 10.1 : Le programme qui lit les sondes.


Si vous ouvrez le moniteur série de l'Arduino et que dans la partie haute vous tapez

1 (ENTRER) L'Arduino affiche dans la partie basse le nombre de sondes


2 L'Arduino affiche l'identifiant et la température des sondes DS18B20
3 L'Arduino affiche le « numéro » l'humidité et la température des sondes DHT22

L'Arduino affiche ces résultats en quelques dizaine de secondes…

Mars 2016 - V 0.1 31/45


Hors en section 20.2.2 la même chose via le programme python prend plus de 2 minutes…
J'avoue ne pas encore avoir trouvé pourquoi...

• Les difficultés d'assemblage à cause des connecteurs Wago (malgré des sections de fils
doublées et étamées.).

17. Conclusion et évolution

Comme vous venez de le lire, pour :

• Environ le coût de 2 lots de 3 sondes toutes faites (sans les à cotés : PDU ou système multi-capteurs
pour les brancher).

• Un peu de temps.

J'ai mis en place une solution à une échelle plus grande, sans trop de difficulté.

Donc pour moi je dirai que c'est un bilan positif !

Maintenant je ne dis pas qu'elle ne peut pas être améliorée, puisqu'au contraire j'y pense en :

• Essayant de trouver la raison de la latence d'interrogation des sondes via Python

• Mettre le tout dans un « joli » boiter16 1U rackable permettant au passage d'y mettre une
alimentation pour alimenter séparément l'Arduino et les sondes, une possibilité de faire des
connectiques en façade via prises jack ou RJ45...

16 www.selectronic.fr/coffret-rack-19-en-plastique-1u-noir.html

Mars 2016 - V 0.1 32/45


Annexe 1 : 18B20_read_adresses
/* YourDuino Example: Find Address of a DS18B20 Temperature Sensor
Cut and paste the address to a text file for later use. V1.1 01/17/2013
Questions: terry@yourduino.com

Connections: DS18B20 Pinout (Left to Right, pins down, flat side toward you)
- Left = Ground
- Center = Signal (Pin 2): (with 3.3K to 4.7K resistor to +5 or 3.3 )
- Right = +5 or +3.3 V
This sketch looks for 1-wire devices and prints their addresses (serial number)
to the Serial Monitor in a format that is useful in Arduino sketches.
Based on example at: http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html

TELEVERSER LE PROGRAMME
MONITOR SERIE
PUIS CHANGER DE SONDE ET RESET SUR ARDUINO
LA NOUVELLE ADRESSE APPARAIT DANS LE MONITOR
*/

/*-----( Import needed libraries )-----*/


#include <OneWire.h>

/*-----( Declare Constants and Pin Numbers )-----*/


#define SENSOR_PIN 2 // Any pin 2 to 12 (not 13) and A0 to A5

/*-----( Declare objects )-----*/


OneWire ourBus(SENSOR_PIN); // Create a 1-wire object

void setup() /****** SETUP: RUNS ONCE ******/


{
Serial.begin(9600);

discoverOneWireDevices(); // Everything happens here!


}//--(end setup )---

void loop() /****** LOOP: RUNS CONSTANTLY ******/


{
// Nothing happening here
}

/*-----( Declare User-written Functions )-----*/


void discoverOneWireDevices(void) {
byte i;
byte present = 0;
byte data[12];
byte addr[8];

Serial.print("Looking for 1-Wire devices... ");


while(ourBus.search(addr)) {
Serial.print("Found \'1-Wire\' device with address:\n");
for( i = 0; i < 8; i++) {
Serial.print("0x");
if (addr[i] < 16) {
Serial.print('0');
}
Serial.print(addr[i], HEX);
if (i < 7) {
Serial.print(", ");
}
}
if ( OneWire::crc8( addr, 7) != addr[7]) {
Serial.print("CRC is not valid!\n\r");
return;
}
}
Serial.println();
Serial.print("Done \n");
ourBus.reset_search();
return;
}

//*********( THE END )***********

Mars 2016 - V 0.1 33/45


Annexe 2 : DHT
#include "DHT.h"

#define DHTPIN 3 // what pin we're connected to


#define DHTTYPE DHT22 // DHT 22 (AM2302)

DHT dht(DHTPIN, DHTTYPE);

void setup() {
// pinMode(fan, OUTPUT);
Serial.begin(9600);
dht.begin();
}

void loop() {
// Wait a few seconds between measurements.
delay(2000);

// Reading temperature or humidity takes about 250 milliseconds!


// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = dht.readHumidity();
// Read temperature as Celsius
float t = dht.readTemperature();

// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}

Serial.print("Humidity: ");
Serial.print(h);
Serial.print(" %\t");
Serial.print("Temperature: ");
Serial.print(t);
Serial.println(" *C ");

Annexe 3 : 18B20PAR_DHT22_115200bauds
/* ICARE : Multiple DS18B20 Temperature Sensors
and Multiple DHT 22 (AM2302) humidity/temperature sensors

DS18B20 Pinout (Left to Right, pins down, flat side toward you)
- Left = Ground
- Center = Signal (Pin 2): (with 3.3K to 4.7K resistor to +5 or 3.3 )
- Right = +5 or +3.3 V

DS18B20-PAR for Parasite-Power Pinout (Left to Right, pins down, flat side toward you)
- Left = Ground
- Center = Signal (Pin 2): (with 3.3K to 4.7K resistor to +5 or 3.3 )
- Right = No Connect

DHT 22 (AM2302) (Left to Right, pins down)


- Pin 1 Left = +5V
- Pin 2 DHTPIN = Signal: (with 10K resistor to +5 )
- Pin 3 = No Connect
- Pin 4 Right = Ground

/*-----( Import needed libraries )-----*/

// Get 1-wire Library here: http://www.pjrc.com/teensy/td_libs_OneWire.html


#include <OneWire.h>

// Get DallasTemperature Library here: http://milesburton.com/Main_Page?title=Dallas_Temperature_Control_Library


#include <DallasTemperature.h>

// Get DHT-sensor-library here : https://github.com/adafruit/DHT-sensor-library

Mars 2016 - V 0.1 34/45


#include "DHT.h"

/*-----( Declare Constants and Pin Numbers )-----*/

/*-- FOR 18B20 --*/

// Data wire is plugged into port 2 on the Arduino (can be changed)


#define ONE_WIRE_BUS 2 // NOTE: No ";" on #define

// Set the resolution to 9 bit (Can be 9 to 12 bits .. lower is faster)


// 9-bit : 93.75 ms 10-bit : 187.5 ms 11-bit : 375 ms 12-bit : 750 ms
#define TEMPERATURE_PRECISION 9

/*-----( Declare objects )-----*/

/*-- FOR 18B20 --*/

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.


DallasTemperature sensors(&oneWire);

int numberOfDevices; // Number of temperature devices found

int message = 0; // Number for initialize bidirectional communication

// To possibly pause between each loop (in miliseconds)


// 0 = no loop 30000 = 30s
int tempoloop;

DeviceAddress tempDeviceAddress; // We'll use this variable to store a found device address

/*-- FOR DHT22 --*/

// The First DHT22 is plugged into port 3 on the Arduino (can be changed)
// supposed second in port 4, third in port 5...
int FisrtDHT = 3;

// number of connected probe DHT22


const int nSensors = 4;

// Template to use : DHT 22 (AM2302)


DHT* dht;

void setup(void) /****** SETUP: RUNS ONCE ******/


{
// start serial port
Serial.begin(115200);
// Serial.println(" \n\nLaunching the program \n");

// Start up the library


sensors.begin();

dht = (DHT*)malloc(nSensors*sizeof(DHT));
for (int i=0; i<nSensors; i++)
{
// assumes first sensor is pin 3 and
// each subsequent sensor is plugged into the next pin down
dht[i] = DHT(i+FisrtDHT, DHT22);
dht[i].begin();
}

// Grab a count of devices on the wire


numberOfDevices = sensors.getDeviceCount();

// Loop through each device, print out address


for(int i=0;i<numberOfDevices; i++)
{
// Search the wire for address
if(sensors.getAddress(tempDeviceAddress, i))
{

Mars 2016 - V 0.1 35/45


sensors.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION);
}
}
}

// function to print the temperature for a device


void printTemperature(DeviceAddress deviceAddress)
{
// To simply transmit the values to Raspberry
// Wire address;Temp in Celsiuis
printAddress(tempDeviceAddress);
Serial.print(" ");
float tempC = sensors.getTempC(deviceAddress);
Serial.print(tempC);
Serial.print("\n");
}

void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
sensors.requestTemperatures(); // Send the command to get temperatures

if (Serial.available())
{
message = Serial.read()-'0'; // on soustrait le caractère 0, qui vaut 48 en ASCII

if (message == 1) // it is the number of probe ?


{
numberOfDevices = sensors.getDeviceCount();
Serial.println(numberOfDevices, DEC);
}
if (message == 2) // what is their address and value
{
// Loop through each device, print out temperature data
for(int i=0;i<numberOfDevices; i++)
{
// Search the wir for address
if(sensors.getAddress(tempDeviceAddress, i))
{
// It responds almost immediately. Let's print out the data
printTemperature(tempDeviceAddress); // Use a simple function to print out the data
}
}
}

if (message == 3) // what is their address and value


{
for (int i=0; i<nSensors; i++)
{
// iterate the entire set of sensors in approximately 2 seconds, with a little buffer.
delay(2000./nSensors + 250);
float h = dht[i].readHumidity();
float t = dht[i].readTemperature();
if (isnan(h) || isnan(t))
{
Serial.print("Probe");
Serial.print(i+1); // number probe i=0=pin3=probe1 i=1=pin4=probe2 ...
Serial.print(" ");
Serial.println("No good boss.");
continue;
}
Serial.print("Probe");
Serial.print(i+1); // number probe i=0=pin3=probe1 i=1=pin4=probe2 ...
Serial.print(" ");
Serial.print(h); //Humidity
Serial.print(" ");
Serial.print(t); // Temperature
Serial.print("\n");
}
}
}
}

// function to print a device address

Mars 2016 - V 0.1 36/45


void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}

Annexe 4 : probe.py
#!/usr/bin/env python

# Program : Python probe.py


# organization : UMS ICARE
# author : Henri MEURDESOIF
# Created on : 201610113
# Objective: Through the arduino uno card, question DS18B20-PAR and DHT22 sensors
# Modifications: who - when - reason ..

# Importing external functions :


# IN ORDER, as some of previous need to press it s
## os : Module contains functions to communicate with the operating system
## sys : contains the functions and parameters specific to the system
## time : simple way to manipulate time
## re : to create the Regular Expression
## serial : for serial or USB communication port emulating series
import os, sys, time, re, serial

# |-address of the serial port to read the data


# | |-baud rate of serial port
# | | |-if not answered closing the port after XXs
ser = serial.Serial('/dev/ttyACM0', 115200, timeout=30)

def get_temperature(DEBUG):

read_nb_18B = "" # number of 18B20-PAR probe found


read_val_18B = "" # 18B20-PAR probe values
read_val_DHT = "" # DHT22 probe values
data = "" # provisional result
fichier = "" # Provisional playback file

if DEBUG == "debug" :
print ser
print "-> sleep10 : start : " + time.strftime('%d/%m/%y %H:%M:%S',time.localtime())

time.sleep(10) # we wait a bit so that the Arduino is ready

if DEBUG == "debug" :
print "-> sleep10 : end : " + time.strftime('%d/%m/%y %H:%M:%S',time.localtime())

try:
ser.flush() # serial : erasing buffer information

if DEBUG == "debug" :
print "-> 1 : start : " + time.strftime('%d/%m/%y %H:%M:%S',time.localtime())

ser.write('1') # displays the number of 18B20-PAR sensors found by Arduino


data = str(ser.readline())
read_nb_18B += time.strftime('%d/%m/%y %H:%M:%S',time.localtime()) + " " + data
ser.flush()

if DEBUG == "debug" :
print "-> end : " + time.strftime('%d/%m/%y %H:%M:%S',time.localtime())
print "-> 2 : start : " + time.strftime('%d/%m/%y %H:%M:%S',time.localtime())

ser.write('2') # displays the addresses of 18B20-PAR with their temperature sensors


data = ser.readline()
while len(data) > 0:

Mars 2016 - V 0.1 37/45


read_val_18B += time.strftime('%d/%m/%y %H:%M:%S',time.localtime()) + " " + data
data = str(ser.readline())
ser.flush()

if DEBUG == "debug" :
print "-> end : " + time.strftime('%d/%m/%y %H:%M:%S',time.localtime())
print "-> 3 : start : " + time.strftime('%d/%m/%y %H:%M:%S',time.localtime())

ser.write('3') # displays humidity and temperature probes of DHT22 (AM2302)


data = ser.readline()
while len(data) > 0:
read_val_DHT += time.strftime('%d/%m/%y %H:%M:%S',time.localtime()) + " " + data
data = str(ser.readline())
ser.flush()

if DEBUG == "debug" :
print "-> end : " + time.strftime('%d/%m/%y %H:%M:%S',time.localtime())

except Exception,e:
if DEBUG == "debug" :
print e
else:
pass

finally:
ser.flush()
ser.close()

with open("/tmp/probe", "w") as fichier:


fichier.write(read_nb_18B)
fichier.write(read_val_18B)
fichier.write(read_val_DHT)

if DEBUG == "debug" :
print "-"*80
print read_nb_18B
print read_val_18B
print read_val_DHT
print "-"*80

if __name__ == "__main__":
if len(sys.argv) == 2:
get_temperature(sys.argv[1])
else:
get_temperature("no")

Annexe 5 : probe_average.py
#!/usr/bin/env python

# Program : Python probe_average.py


# organization : UMS ICARE
# author : Henri MEURDESOIF
# Created on : 201610113
# Objective: average the ceiling, floor, library and corridors probes
# Modifications: who - when - reason ..

# Importing external functions :


# IN ORDER, as some of previous need to press it s
## os : Module contains functions to communicate with the operating system
## sys : contains the functions and parameters specific to the system
## time : simple way to manipulate time
## re : to create the Regular Expression
## numpy : A fast multidimensional array facility for Python
import os, sys, time, re
import numpy as np

Mars 2016 - V 0.1 38/45


def get_temperature(onwhat,phid):

DS18B20_ID = "" # mac address of probe example 28BDB3A00600002A


ligne = ""
fichier = ""
temperature = ""
humidity = ""
malist = []

probe = re.compile("^(?P<probe_name>Probe(?P<probe_id>[0-9]{2})) (?P<probe_macadd>[0-9A-Z]{16})$")


records18 = re.compile("^(?P<date_time>[0-9]{2}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}) (?P<mac_add>[0-9A-Z]{16}) (?P<temp>[0-9.]+)$")
records22 = re.compile("^(?P<date_time>[0-9]{2}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}) (?P<probe_name>Probe(?P<probe_id>[0-9]{1})) (?
P<hum>[0-9]{2}.[0-9]{2}) (?P<temp>[0-9.]+)$")

## Prode 18B20
if onwhat == "floor": # floor for probe01 to probe08 and humidity 1
a = range(1,9)

elif onwhat == "ceiling": # ceiling for probe09 to probe16 and humidity 2


a = range(9,17)

elif onwhat == "cold":


listcpeer = list(range(17,35)) # cold corridor for probe PEER in range probe17 to probe34 and NO humidity
listcodd = list(range(35,52)) # AND probe ODD in range probe35 to probe51 and NO humidity
a = listcpeer[1::2] + listcodd[0::2]

elif onwhat == "library": # library for probe52 to probe62 and humidity 4


a = range(52,63)

elif onwhat == "hotpeer":


listhodd = list(range(17,35)) # "hot corridor peer" = "probe odd" in range probe17 to probe34 and NO humidit
a = listhodd[0::2]

elif onwhat == "hotodd":


listhpeer = list(range(35,52)) # "hot corridor odd" = "probe peer" in range probe35 to probe52 and NO humidit
a = listhpeer[1::2]

else:
a = "None"

#if onwhat != "start" and onwhat != "stop":


if onwhat not in ["start", "stop"]:
for i in a:
if i < 10:
ii = ("0"+str(i))
else:
ii = (str(i))

# find the correspondence between the number of the probe and its id
# |-Path of the file containing the correspondence between the number of the probe and its ID
with open("/home/root/arduino/18B20_adresses_DEC") as fichier:
for ligne in fichier:
m = probe.match(ligne.strip())
if m is not None:
if ii == m.group("probe_id"):
DS18B20_ID = m.group("probe_macadd")

# find the temperature probe


# |-File path containing the recording probes made by the program probe.py
with open("/tmp/probe") as fichier:
for ligne in fichier:
m = records18.match(ligne.strip())
if m is not None:
if DS18B20_ID == m.group("mac_add"):
temperature = m.group("temp")
#print ("Probe"+str(ii)+" : "+ DS18B20_ID + " : "+ temperature)
malist.append(temperature)
with open("/tmp/probe_calcul", "a") as fichier:
fichier.write("$P"+ii+" = "+temperature+"; \n")

Mars 2016 - V 0.1 39/45


## Probe DHT22
if phid is not None:
# find the humidity and temperature probe
# |-File path containing the recording probes made by the program probe.py
with open("/tmp/probe") as fichier:
for ligne in fichier:
m = records22.match(ligne.strip())
if m is not None:
if phid == m.group("probe_id"):
humidity = m.group("hum")
temperature = m.group("temp")
#print ("Probe : H:"+ humidity + " T:"+ temperature)
malist.append(temperature)
with open("/tmp/probe_calcul", "a") as fichier:
fichier.write("$P"+phid+"t = "+temperature+"; \n")
fichier.write("$P"+phid+"h = "+humidity+"; \n")

tableau = np.array(malist).astype(np.float)
moyenne = "%.1f" % np.mean(tableau)
ecartype = "%.1f" % np.std(tableau)
with open("/tmp/probe_calcul", "a") as fichier:
fichier.write("$"+onwhat+"_moyenne = "+moyenne+"; \n")
fichier.write("$"+onwhat+"_ecartype = "+ecartype+"; \n")

if onwhat == "start":
with open("/tmp/probe_calcul", "w") as fichier:
fichier.write("<?php \n")

if onwhat == "stop":
with open("/tmp/probe_calcul", "a") as fichier:
fichier.write("?> \n")

if __name__ == "__main__":
get_temperature("start","None")
get_temperature("floor","1") # on what + humidity probe number
get_temperature("ceiling","2")
get_temperature("library","4")
get_temperature("cold","None")
get_temperature("hotpeer","None")
get_temperature("hotodd","3")
get_temperature("stop","None")

Annexe 6 : probe18B20P_munin.py
#!/usr/bin/env python

# Program : Python probe18B20P_munin.py


# organization : UMS ICARE
# author : Henri MEURDESOIF
# Created on : 201610113
# Objective: Send a munin for grapher, the values of DS18B20-PAR sensors
# Modifications: who - when - reason ..

# Importing external functions :


# IN ORDER, as some of previous need to press it s
## os : Module contains functions to communicate with the operating system
## sys : contains the functions and parameters specific to the system
## time : simple way to manipulate time
## re : to create the Regular Expression
import os, sys, time, re

def get_temperature_from_sensor(PN):

DS18B20_ID = "" # mac address of probe example 28BDB3A00600002A


fichier = "" # Provisional playback file
ligne = "" # Provisional playback line
temperature = ""

probe = re.compile("^(?P<probe_name>Probe(?P<probe_id>[0-9]{2})) (?P<probe_macadd>[0-9A-Z]{16})$")

Mars 2016 - V 0.1 40/45


records = re.compile("^(?P<date_time>[0-9]{2}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}) (?P<mac_add>[0-9A-Z]{16}) (?P<temp>[0-9.]+)$")

# find the correspondence between the number of the probe and its id
# |-Path of the file containing the correspondence between the number of the probe and its ID
with open("/home/root/arduino/18B20_adresses_DEC") as fichier:
for ligne in fichier:
m = probe.match(ligne.strip())
if m is not None:
if PN == m.group("probe_id"):
DS18B20_ID = m.group("probe_macadd")

# find the temperature probe


# |-File path containing the recording probes made by the program probe.py
with open("/tmp/probe") as fichier:
for ligne in fichier:
m = records.match(ligne.strip())
if m is not None:
if DS18B20_ID == m.group("mac_add"):
temperature = m.group("temp")
timestamp = int(time.time())
if temperature is not None:
return (timestamp, temperature)
else:
return (timestamp, None)

def print_munin_config(action,PN):
if action == "autoconf":
print "yes"
return

if action == "suggest":
print "temperature"
return

if action != "config":
return

print 'graph_title Temperature Monitoring Probe {NUM}'.format(NUM=PN)


print 'graph_args --base 1000 -l 0'
print 'graph_vlabel Celsius'
print 'graph_category temperature'

print 'temperature.label Temperature (C)'


print 'temperature.type GAUGE'
print 'temperature.warning 25:30';
print 'temperature.critical 30:';

def print_temperature_for_munin(temperature):
print 'temperature.value %0.1f' % float(temperature)

if __name__ == "__main__":
if len(sys.argv) == 2:
print_munin_config(sys.argv[1],"01")
else:
timestamp, temperature = get_temperature_from_sensor("01") # Probe Number
print_temperature_for_munin(temperature)

Annexe 7 : probeDHT22_munin.py
#!/usr/bin/env python

# Program : Python probeDHT22_munin.py


# organization : UMS ICARE
# author : Henri MEURDESOIF
# Created on : 201610113
# Objective: Send a munin for grapher, the values of DHT22 sensors
# Modifications: who - when - reason ..

Mars 2016 - V 0.1 41/45


# Importing external functions :
# IN ORDER, as some of previous need to press it s
## os : Module contains functions to communicate with the operating system
## sys : contains the functions and parameters specific to the system
## time : simple way to manipulate time
## re : to create the Regular Expression
import os, sys, time, re

def get_temperature_from_sensor(PN):

DS18B20_ID = "" # mac address of probe example 28BDB3A00600002A


fichier = "" # Provisional playback file
ligne = "" # Provisional playback line
temperature = ""
humidity = ""

records = re.compile("^(?P<date_time>[0-9]{2}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}) (?P<probe_name>Probe(?P<probe_id>[0-9]{1})) (?


P<hum>[0-9]{2}.[0-9]{2}) (?P<temp>[0-9.]+)$")

# find the humidity and temperature probe


# |-File path containing the recording probes made by the program probe.py
with open("/tmp/probe") as fichier:
for ligne in fichier:
m = records.match(ligne.strip())
if m is not None:
if PN == m.group("probe_id"):
humidity = m.group("hum")
temperature = m.group("temp")
timestamp = int(time.time())
if humidity is None or temperature is None:
return (timestamp, None)
return (timestamp, humidity, temperature)

def print_munin_config(action,PN):
if action == "autoconf":
print "yes"
return

if action == "suggest":
print "humidity temperature"
return

if action != "config":
return

print 'graph_title Temperature and Humidity Monitoring Probe{NUM}'.format(NUM=PN)


print 'graph_args --base 1000 -l 0'
print 'graph_vlabel Temperature and Humidity'
print 'graph_category temperature'

print 'temperature.label Teimperature (C)'


print 'temperature.type GAUGE'
print 'temperature.warning 25:30'
print 'temperature.critical 30:'

print 'humidity.label Humidity (%)'


print 'humidity.min 0.0'
print 'humidity.max 100.0'
print 'humidity.warning 60'
print 'humidity.critical 65'

def print_temperature_for_munin(humidity,temperature):
print 'temperature.value %0.1f' % float(temperature)
print 'humidity.value %0.1f' % float(humidity)

if __name__ == "__main__":
if len(sys.argv) == 2:
print_munin_config(sys.argv[1],"1")
else:
timestamp, humidity, temperature = get_temperature_from_sensor("1") # Probe Number
print_temperature_for_munin(humidity,temperature)

Mars 2016 - V 0.1 42/45


Annexe 8 : index.php
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="refresh" content="180;url=http://adm-pub.icare.univ-lille1.fr/temperatures/" />
<link rel="icon" type="image/png" href="icons/temp4.png" />
<title>Temeperatures</title>

<style> /* partie CSS */

/* image de la salle = 1ere div de reference


les autres div se baseront sur cette image pour se positionner
d apres le point en HAUT a GAUCHE de l image qui a la reference 0px : 0px */

.salle {
position:relative;
}

/* librairie de sauvegarde */
.library {
position:absolute;
color:blue;
top:255px;
left:30px;
}

/* couloir chaud pair */


.hotpeer {
position:absolute;
color:blue;
top:80px;
left:360px;
}

/* couloir chaud impair */


.hotodd {
position:absolute;
color:blue;
top:445px;
left:350px;
}

/* plancher */
.floor {
position:absolute;
color:blue;
top:485px;
left:685px;
}

/* plafond */
.ceiling {
position:absolute;
color:blue;
top:438px;
left:685px;
}

/* corridor froid */
.cold {
position:absolute;
color:blue;
top:260px;
left:385px;
}

</style> <!-- FIN DE PARTIE CSS -->


</head>

<body style="width:824px;height595px">

Mars 2016 - V 0.1 43/45


<?php include('/tmp/probe_calcul'); ?>

<h2>Temp&eacute;rature de la salle machine ICARE</h2>

AFFICHAGE RAFRAICHIT AUTOMATIQUEMENT TOUTES LES 3 MINUTES :

<script type="text/javascript" src="date.js"></script>

<script>
(function ()
{
document.write(new Date().toString("hh:mm tt"));
})();
</script>

<br/><br/>Cliquez sur la zone dont vous voulez afficher les temp&eacute;ratures: Corridors chauds, froid, serveurs, librairie, plancher ou
plafond<br/><br/>

<div class="salle">
<img src="images/salle_html.jpg" border="0" alt="Salle machines ICARE" usemap="#zones" />
<map name="zones">
<area shape="rect" coords="90,65,165,250" href="lib.php" alt="Temperatures de la librairie" />
<area shape="rect" coords="250,25,660,110" href="hot.php" alt="Temperatures du coridor chaud pair" />
<area shape="rect" coords="250,200,660,295" href="cold.php" alt="Temperatures du coridor froid" />
<area shape="rect" coords="250,385,660,475" href="hot.php" alt="Temperatures du coridor chaud impair" />
<area shape="rect" coords="665,410,790,455" href="pla.php" alt="Temperatures du plafond" />
<area shape="rect" coords="665,455,790,500" href="pla.php" alt="Temperatures du plancher" />
<area shape="rect" coords="250,110,660,200" href="serveurs.html" alt="Temperatures des serveurs" />
<area shape="rect" coords="250,295,660,385" href="serveurs.html" alt="Temperatures des serveurs" />
</map>

<div class="library"><b>T° moyenne : <?php echo $library_moyenne ?>°C<br>Ecart type : <?php echo $library_ecartype ?></b></div>
<div class="hotpeer"><b><?php echo $hotpeer_moyenne ?>°C, <?php echo $hotpeer_ecartype ?></b></div>
<div class="cold"><b><?php echo $cold_moyenne ?>°C, <?php echo $cold_ecartype ?></b></div>
<div class="hotodd"><b><?php echo $hotodd_moyenne ?>°C, <?php echo $hotodd_ecartype ?></b></div>
<div class="ceiling"><b><?php echo $ceiling_moyenne ?>°C, <?php echo $ceiling_ecartype ?></b></div>
<div class="floor"><b><?php echo $floor_moyenne ?>°C, <?php echo $floor_ecartype ?></b></div>

</body>
</html>

Annexe 9 : cold.php
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="refresh" content="180;url=http://adm-pub.icare.univ-lille1.fr/temperatures/cold.php" />
<link rel="icon" type="image/png" href="icons/temp4.png" />
<title>Temeperatures du corridor froid</title>

<style> /* partie CSS */


/* image de la salle = 1ere div de reference
les autres div se baseront sur cette image pour se positionner
d apres le point en HAUT a GAUCHE de l image qui a la reference 0px : 0px */

.salle {
position:relative;
}

/* pair */
.div_18 { position:absolute;color:blue;top:206px;left:510px; }
.div_20 { position:absolute;color:blue;top:260px;left:510px; }
.div_22 { position:absolute;color:blue;top:310px;left:510px; }
.div_28 { position:absolute;color:blue;top:110px;left:400px; }
.div_26 { position:absolute;color:blue;top:160px;left:400px; }
.div_24 { position:absolute;color:blue;top:220px;left:400px; }
.div_30 { position:absolute;color:blue;top:10px;left:310px; }
.div_32 { position:absolute;color:blue;top:65px;left:310px; }
.div_34 { position:absolute;color:blue;top:120px;left:310px; }

/* impair */
.div_35 { position:absolute;color:#99CCFF;top:206px;left:370px; }

Mars 2016 - V 0.1 44/45


.div_37 { position:absolute;color:#99CCFF;top:260px;left:370px; }
.div_39 { position:absolute;color:#99CCFF;top:310px;left:370px; }
.div_45 { position:absolute;color:#99CCFF;top:100px;left:270px; }
.div_43 { position:absolute;color:#99CCFF;top:160px;left:270px; }
.div_41 { position:absolute;color:#99CCFF;top:220px;left:270px; }
.div_47 { position:absolute;color:#99CCFF;top:10px;left:185px; }
.div_49 { position:absolute;color:#99CCFF;top:65px;left:185px; }
.div_51 { position:absolute;color:#99CCFF;top:120px;left:185px; }

</style> <!-- FIN DE PARTIE CSS -->


</head>

<body style="width:824px;height595px">

<?php include('/tmp/probe_calcul'); ?>

AFFICHAGE RAFRAICHIT AUTOMATIQUEMENT TOUTES LES 3 MINUTES :

<script type="text/javascript" src="date.js"></script>

<script>
(function ()
{
document.write(new Date().toString("hh:mm tt"));
})();
</script>

<br/><br/>Le num&eacute;ro de la sonde repr&eacute;sente aproximativmeent son emplacement...


<br/>Cliquez sur un num&eacute;ro de sonde pour acc&eacute;der &aacute; son graphique.<br/>
<br/>

<div class="salle">
<img src="images/corridor2.jpg" border="0" alt="corridor"/>
<div class="div_18"><b><a href="/munin/localhost/localhost/18B20P_18.html">18:</a> <?php echo $P18?>°C</b></div>
<div class="div_20"><b><a href="/munin/localhost/localhost/18B20P_20.html">20:</a> <?php echo $P20?>°C</b></div>
<div class="div_22"><b><a href="/munin/localhost/localhost/18B20P_22.html">22:</a> <?php echo $P22?>°C</b></div>
<div class="div_24"><b><a href="/munin/localhost/localhost/18B20P_24.html">24:</a> <?php echo $P24?>°C</b></div>
<div class="div_26"><b><a href="/munin/localhost/localhost/18B20P_26.html">26:</a> <?php echo $P26?>°C</b></div>
<div class="div_28"><b><a href="/munin/localhost/localhost/18B20P_28.html">28:</a> <?php echo $P28?>°C</b></div>
<div class="div_30"><b><a href="/munin/localhost/localhost/18B20P_30.html">30:</a> <?php echo $P30?>°C</b></div>
<div class="div_32"><b><a href="/munin/localhost/localhost/18B20P_32.html">32:</a> <?php echo $P32?>°C</b></div>
<div class="div_34"><b><a href="/munin/localhost/localhost/18B20P_34.html">34:</a> <?php echo $P34?>°C</b></div>

<div class="div_35"><b><a href="/munin/localhost/localhost/18B20P_35.html">35:</a> <?php echo $P35?>°C</b></div>


<div class="div_37"><b><a href="/munin/localhost/localhost/18B20P_37.html">37:</a> <?php echo $P37?>°C</b></div>
<div class="div_39"><b><a href="/munin/localhost/localhost/18B20P_39.html">39:</a> <?php echo $P39?>°C</b></div>
<div class="div_41"><b><a href="/munin/localhost/localhost/18B20P_41.html">41:</a> <?php echo $P41?>°C</b></div>
<div class="div_43"><b><a href="/munin/localhost/localhost/18B20P_43.html">43:</a> <?php echo $P43?>°C</b></div>
<div class="div_45"><b><a href="/munin/localhost/localhost/18B20P_45.html">45:</a> <?php echo $P45?>°C</b></div>
<div class="div_47"><b><a href="/munin/localhost/localhost/18B20P_47.html">47:</a> <?php echo $P47?>°C</b></div>
<div class="div_49"><b><a href="/munin/localhost/localhost/18B20P_49.html">49:</a> <?php echo $P49?>°C</b></div>
<div class="div_51"><b><a href="/munin/localhost/localhost/18B20P_51.html">51:</a> <?php echo $P51?>°C</b></div>

<p></p>

<table width="100%" border="0">


<tr>
<td VALIGN = Top Align = Left>
Sonde DHT22 de tempd&eacute;rature et d&apos;humidit&eacute; : <br/>
- Humidit&eacute;: 0 - 100 % RH"<br/>
- Pr&eacute;cision: +/- 2% RH"<br/>
- Temp&eacute;rature: -40 ~ 80 °C<br/>
- Pr&eacute;cision: +/- 0,5 °C

</td>
<td VALIGN = Top Align = Left>
Sonde de temp&eacute;rature DS18B20P : <br/>
- Temp&eacute;rature: -55 ~ 125 °C<br/>
- Pr&eacute;cision: +/- 0,5 °C
</td>
</tr>
</table>
</body>
</html>

Mars 2016 - V 0.1 45/45

Vous aimerez peut-être aussi