Avant toute chose

Prérequis

Avoir suivi l’article précédent concernant le montage du thermomètre connecté.

Si vous avez commandé le matériel, et ne l’avez pas reçu, ou bien si vous avez brûlé le matériel reçu et attendez une seconde livraison, pas de panique, nous traiterons ces cas en cours d’article, en préparant des tests d’intégration.

Si par contre, vous avez déjà tout reçu et que la première partie s’est bien déroulée, fantastique : branchez votre circuit avec un câble USB, et passez à la suite de l’article.

Nous utiliserons des outils facilement accessibles sous Linux ou MacOS. Nous manquons de recul sur les outils Windows, mais il doit être possible de réaliser peu ou prou la même chose. N’hésitez pas à commenter si vous parvenez à une solution pour enrichir l’article.

Le projet

Notre thermomètre nous envoie pour le moment des valeurs entières (de l’ordre de 600 à 800 pour une température « normale » dans un bâtiment…) dans un moniteur série, complètement incompréhensibles aux « non geeks », voir même, à l’ensemble des humains.

Nous allons lire les données, les envoyer à une base de données spécialisée via un outil de collecte, et les exposer au reste des collègues sous un format agréable avec un outil de dashboard.

Nous utiliserons StatsD pour la collecte, Graphite pour le stockage et enfin Tessera pour le dashboard. Ces outils sont ceux que nous utilisons chez LesFurets.com pour le monitoring des connecteurs de nos assureurs.

Écrire des tests

Pourquoi ?

Le matériel électronique peut parfois être capricieux, la température dans un open space également. Nous allons avoir besoin de données de référence pour valider que l’ensemble de notre chaîne d’outils fonctionne correctement.

Comment ?

Puisque les données nous arrivent par un port série, et que nous avons choisi d’être sous un Unix-like (Linux ou MacOS), nous écrirons un outil qui se comporte comme un port série. En l’occurrence, nous avons besoin d’écrire une valeur issue d’un convertisseur analogique / numérique toutes les secondes. Un simple script bash avec une pause et un echo devrait faire l’affaire:

#!/bin/bash
while [ 1 ]; do
  sleep 1
  echo "137"
done

Et voilà ! Lancez cet outil en ligne de commande, s’il envoie bien des lignes comportant 137, une par seconde, le test est prêt.

Attention

Dans tout ce tutoriel, nous utilisons l’adresse IP 127.0.0.1 pour accéder à des services lancés avec Docker sur la machine locale. Si vous utilisez un service Docker distant, ou docker-machine par exemple, remplacez toutes les occurrences (y compris dans les liens à cliquer…) par l’adresse IP publique du service docker (par exemple, pour docker-machine, utilisez la commande docker-machine env <ma_machine>)

La base de données et son aggrégateur

Nous allons utiliser une base de données temporelle pour enregistrer la température dans le temps. Graphite est un outil conçu exactement pour ce besoin. Et parce qu’il utilise du Python et pas mal de configuration, nous allons utiliser une image docker pour ne pas nous mélanger les pinceaux.

Pour envoyer les données de façon ultra simplifiée dans Graphite, nous allons utiliser un service compagnon, StatsD. StatsD est un agrégateur de métriques : il est capable de prendre plusieurs points de données, et de les agréger pour les pousser dans Graphite à un intervalle de temps fixe.

Lancer Graphite/StatsD

docker run -d \
--name graphite \
--restart=always \
-p 8000:80 \
-p 2003-2004:2003-2004 \
-p 2023-2024:2023-2024 \
-p 8125:8125/udp \
-p 8126:8126 \
hopsoft/graphite-statsd

Pour vérifier que votre service Graphite est correctement allumé, allez sur sa page de gestion

Pour vérifier que StatsD fonctionne correctement, vous pouvez pousser une première valeur à la main, avec un outil de gestion réseau comme socat. Par exemple:

printf "thermo.openspace.data:137|g" | socat -t 0 - UDP:127.0.0.1:8125

Dans la console de gestion de Graphite, vous devriez voir apparaître alors (après un délai d’agrégation par défaut de 10s) une métrique sous le nom Metrics/stats/gauges/thermo/openspace/data. Cliquez dessus, vous verrez un point unique dans la vue graphique.

Bravo ! Vous avez correctement configuré Graphite/StatsD.

Lancer Tessera

 docker run -d --name tessera -p 80:80 -P -e GRAPHITE_URL=http://127.0.0.1:8000 aalpern/tessera-simple

Si tout s’est bien passé, vous trouverez votre système de dashboard Tessera ici.

Construire le dashboard

Je vous laisse découvrir l’interface (très riche) de Tessera. Le plus simple pour obtenir un premier dashboard rapidement reste d’importer celui que nous avons préparé pour vous :

{
  "id": 20,
  "title": "OpenSpace",
  "category": "",
  "summary": "",
  "description": "",
  "creation_date": "2017-01-30T15:59:31.619506Z",
  "last_modified_date": "2017-01-30T16:07:58.099947Z",
  "imported_from": null,
  "tags": [],
  "definition": {
    "options": {
      "from": "-3h"
    },
    "item_type": "dashboard_definition",
    "item_id": "d3",
    "items": [
      {
        "item_type": "section",
        "item_id": "d4",
        "items": [
          {
            "item_type": "row",
            "item_id": "d5",
            "items": [
              {
                "item_type": "cell",
                "item_id": "d6",
                "items": [
                  {
                    "title": "Température de l'open space",
                    "item_type": "singlestat",
                    "item_id": "d7",
                    "query": "temperature",
                    "format": ",.3s",
                    "transform": "last_non_zero",
                    "units": "°C"
                  }
                ],
                "span": 3
              }
            ]
          }
        ],
        "level": 1,
        "horizontal_rule": false,
        "layout": "fixed"
      }
    ],
    "queries": {
      "temperature": {
        "name": "temperature",
        "targets": [
          "aliasByNode(offset(movingAverage(scale(offset(scale(scale(stats.gauges.thermo.openspace.data,1.1),0.00097751710655),-0.5),100),6),1.5),3)"
        ]
      }
    }
  },
  "href": "/api/dashboard/20",
  "view_href": "/dashboards/20/openspace",
  "definition_href": "/api/dashboard/20/definition"
}

Vous pouvez l’importer depuis la page « Dashboards », avec le bouton « Import » (juste en dessous de « New Dashboard ») dans la section « Actions ».

L’essentiel, dans un dashboard Tessera, ce sont les données. Ici, nous utilisons la formule suivante :

aliasByNode(offset(movingAverage(scale(offset(scale(scale(stats.gauges.thermo.openspace.data,1.1),0.00097751710655),-0.5),100),6),1.5),3)

Détaillons ce qu’il s’y passe :

  • Nous dérivons la température de la valeur brute du capteur, selon la formule t = 100 * (capteur * 1.1 / 1024 - 0.5)
  • Nous lissons à la minute (le pas de temps est de 10s, nous utilisons un movingAverage(mesure, 6))
  • Nous décalons d’un offset arbitraire (ici 1.5), que nous expliquerons plus loin
  • Nous prenons un alias du nom de la mesure, ici l’élément 3 (à partir de 0) de stats.gauges.thermo.openspace.data donc openspace

Si tout se passe bien (et que vous avez bien lancé le « test unitaire » de StatsD), vous devriez avoir une température de -33.8°C. Brrr…

Nous laisserons en exercice au lecteur le soin de trouver dans la documentation capteur comment est construite la formule t = 100 * (capteur * 1.1 / 1024 - 0.5).

Et le vrai truc, alors ?

Données de test en streaming

Nous allons maintenant lancer un test grandeur nature, toujours sans matériel.

Pour émuler un Arduino branché en série sur le port USB, nous avons écrit un fichier de test. Il est maintenant temps de s’en servir à bon escient :

mkfifo /tmp/virtualusb
./tests.bash > /tmp/virtualusb

Nous poussons de la donnée « test » dans une file d’attente virtuelle, qui simule parfaitement notre contrôleur USB série. Maintenant, place au driver (dans une seconde console, le premier process ne termine jamais…).

Voyons en le contenu :

#!/bin/bash
STATSD_IP=192.168.99.100
SERIAL_INPUT=virtualusb
DATA_NAME=thermo.openspace.data

LINE_COUNT=0
while read LINE; do
	echo "Seen ${LINE}"
	if [ ! -z "$LINE" ]; then
		LINE_COUNT=$(($LINE_COUNT+1))
	fi
	if [[ $LINE_COUNT == 3 ]]; then
		printf "${DATA_NAME}:${LINE}|g" | socat -t 0 - UDP:${STATSD_IP}:8125
		echo "Done ${LINE}@${DATA_NAME}"
		exit 0
	fi
done <${SERIAL_INPUT}

Lançons le :

./push.bash

Nous allons lire les données sur SERIAL_INPUT (ici notre device virtuel), et les pousser dans StatsD sur l’adresse STATSD_IP, sous le nom DATA_NAME. Nous ignorons les deux premières lignes (parce que les devices USB série ont tendance à écrire n’importe quoi lors de l’établissement de la connexion série), et nous envoyons la troisième ligne seulement, avant de quitter.

Si maintenant, nous exécutons ce script toutes les minutes, nous aurons un relevé à la minute des températures. Utilisons un cron, avec crontab -e. Voici la ligne qui nous intéresse:

* * * * * /chemin_vers_le_script/push.bash

Et voilà ! Les données vont arriver en flux continu depuis l’interface virtuelle vers StatsD.

Mais, et les vraies données, à la fin ?

Facile, nous avons prouvé que l’ensemble de la chaine cron > bash > statsd > graphite > tessera fonctionne convenablement. Reste à se brancher sur le matériel.

Pour ce faire, rien de plus simple, remplacez la ligne :

SERIAL_INPUT=virtualusb

Par (sous Linux):

SERIAL_INPUT=/dev/ttyUSB0

Ou bien (sous Mac):

SERIAL_INPUT=/dev/cu.usbserial-A601EQIH

dans le script push.bash (attention : ces valeurs sont valables pour les cartes indiquées en référence de l’article précédent, avec les drivers du constructeur. Pour d’autres cartes et d’autres constructeurs, se référer au manuel, ou essayer de trouver dans les logs de la machine lorsqu’on branche le câble)

La minute suivante, les vrai données vont arriver dans Tessera.

Et les 1.5°C d’offset ?

Souvenez-vous : nous avons décalé la température d’un offset arbitraire de 1.5°C dans le dashboard Tessera. Maintenant que la vrai donnée arrive, la température du dashboard va s’ajuster; et elle ne va pas vous plaire… En effet, ces capteurs ont une erreur (constante) absolue de +/- 2°C. Il faut donc calibrer le capteur. Pour ceci, il suffit de placer un thermomètre déjà calibré (à alcool, ou électronique, peu importe) juste à côté du capteur, attendre que les deux se stabilisent, et ajuster de l’écart entre les deux. Les 1.5°C sont donc l’écart de température sur le composant que nous avons utilisé chez LesFurets pour notre open space. Le votre différera très probablement.

Conclusion

Nous avons construit ensemble un thermomètre électronique relativement précis, qui enregistre chaque minute la température ambiante « brute capteur » dans une base de données temporelle. La température courante est affichée dans un dashboard, lisible par des humains. L’ensemble est calibré en utilisant un thermomètre du commerce.

Avec un minimum de modifications, vous saurez adapter le dashboard pour afficher un graphique. En cherchant un peu sur Internet, vous saurez ajouter des alertes avec des outils travaillant sur les données depuis Graphite. Et vous pourrez modifier le système en un tournemain pour grapher vos résultats de tests de webperf, ou les temps de réponse de votre base de données. Mais ça, c’est l’objet d’autres articles…