Comment j'ai créé une station météo Arduino entièrement fonctionnelle
Publié: 2022-03-11MISE À JOUR : Les travaux sur notre station météo Arduino se sont poursuivis après la publication de cet article, aboutissant à la sortie de Open Weather Station (OWS). Consultez-le pour des mises à jour supplémentaires, des ressources, ainsi que du code et de nouveaux didacticiels.
Qu'est-ce que tout cela?
Le kitesurf est l'un des sports les plus addictifs au monde. Tout ce qu'il faut, c'est une planche de kitesurf, un plan d'eau et quelques accessoires. C'est un excellent moyen d'entrer en contact avec la nature, de libérer votre esprit et de faire de l'exercice. De plus, vous pouvez vraiment devenir fou avec.
Donc quel est le problème?
Oh, j'ai oublié une condition essentielle : le vent. Et c'est là que nous avons notre problème : on ne sait jamais s'il y aura du vent ou pas à moins d'habiter juste à côté de son spot de kitesurf préféré.
Je vis à Cordoba, en Argentine, à environ 130 kilomètres (~80 miles) du lac où je fais du kitesurf. C'est à peu près deux heures de route, ce que je peux gérer. Mais je ne peux pas accepter le fait que les prévisions météorologiques sont inexactes. Et là où j'habite, les bonnes conditions de vent ne durent que quelques heures. La dernière chose que vous voulez faire est d'éclaircir votre emploi du temps du lundi pour aller faire du kitesurf et vous retrouver à maudire les dieux sur un lac sans vent après deux heures de conduite.
J'avais besoin de connaître les conditions de vent de mon spot de kitesurf préféré, en temps réel. J'ai donc décidé de construire ma propre station météo.
Mesurer la météo en temps réel, dans un environnement hostile
L'objectif était de fournir des données météorologiques en temps réel au navigateur à la maison :
Avant d'entrer dans les détails, prenons un moment pour examiner les questions clés et les mises en garde impliquées dans un projet comme celui-ci :
- Comment puis-je créer une station météo qui n'est ni précieuse ni attrayante pour un voleur ?
- Comment puis-je réduire au minimum les coûts matériels et le temps de développement ?
- Comment puis-je mesurer et accéder aux données météorologiques en temps réel et les afficher de manière utile ?
- Mesures requises : vent et rafales de vent, direction du vent, pluie, pression atmosphérique, température, humidité
- Connecter la borne à Internet
- Stockez et récupérez les données météorologiques locales
- Communiquer entre la station météo et le serveur
- Comment puis-je réduire la maintenance à (presque) zéro ?
- Gérer l'accrochage des logiciels
- Gérer la perte de connectivité
- Gérer la perte d'approvisionnement énergétique
Dites bonjour à mon petit ami !
Vous pensez peut-être que le gant est là pour rendre la station plus conviviale ; mais il sert en fait à tester le capteur barométrique (la pression du gant augmente à l'intérieur du gant gonflé). Sur la droite, vous pouvez voir la gare à son emplacement définitif, perchée au sommet d'une tour voisine.
J'ai également conçu et programmé un site Web sur le kitesurf, qui comprend un tracé en temps réel des mesures de la station pour aider la communauté du kitesurf. Enfin, j'ai créé un groupe de kitesurf sur Facebook.
C'est génial! Alors comment l'avez-vous fait?
Eh bien, je vais aborder chaque point à tour de rôle :
"Comment puis-je créer une station météo qui n'est ni précieuse ni attrayante pour un voleur ?"
C'était un facteur critique qui, à bien des égards, a guidé le reste du processus de conception. La plupart des stations préfabriquées en dessous de la ligne de 2 000 $ nécessitaient une connexion USB à un ordinateur. Si un voleur reconnaissait que la station avait un PC à côté, ce serait la fin des choses, car le coût de remplacement de l'ordinateur et de la station serait supérieur à mon budget personnel. J'ai donc décidé de tester plusieurs plates-formes matérielles pour implémenter la station à partir de zéro, à moindre coût.
« Comment puis-je réduire au minimum les coûts matériels et le temps de développement ? »
Je supportais seul les coûts de ce projet parallèle et faisais tout le travail pendant mon temps libre, donc bien sûr c'était une grande préoccupation. J'ai commencé avec le populaire PIC32 et certains modules Ethernet à micropuce pré-assemblés, mais les coûts n'étaient pas aussi bas que prévu et il y avait beaucoup trop de frais généraux impliqués dans l'assemblage et l'extension du matériel. Ensuite, j'ai commencé à étudier l'Arduino : un matériel et un logiciel open source pour le prototypage électronique en langage C. C'était exactement ce que je voulais et j'ai pu acheter des modules sur DealeXtreme. J'ai pu commencer à jouer avec seulement 15 $ de dépenses et deux jours de mon temps.
Bien sûr, l'Arduino a aussi ses limites : seulement 2 ko de RAM et 32 ko pour mon logiciel compilé, ce qui ne laisse pas beaucoup de place aux chaînes fantaisistes ou aux variables inutiles 1 .
« Comment puis-je mesurer et accéder aux données météorologiques en temps réel et les afficher de manière utile ? »
Actuellement, ma station peut mesurer : la vitesse du vent, les rafales de vent, la direction du vent, la température, l'humidité, la pluie et la pression atmosphérique. La température, l'humidité et la pression sont gérées par quelques bibliothèques, ce qui a rendu la vie beaucoup plus facile.
Mesurer la vitesse du vent et la pluie était un peu compliqué. Les capteurs fonctionnaient en ouvrant et en fermant un interrupteur (interrupteur Reed). Ainsi, j'avais besoin d'implémenter des interruptions matérielles afin d'attraper le capteur dès qu'il déclenche l'entrée. Autrement dit, j'avais besoin d'appeler une méthode:
attachInterrupt(RAINGAUGE_PIN, countRainCycles, FALLING);
Cette interruption interromprait l'exécution normale du code et appellerait la fonction countAnemometerCycles ou countRainCycles dès que le commutateur subit un front descendant, produit par la fermeture ou l'ouverture du circuit. Quelques variables sont incrémentées à chaque déclenchement du commutateur. (Plus tard, vous pondérez ces variables pour tenir compte des conversions d'unités.)
void countRainCycles() { rainCyclesCounter++; // This is easy! And it actually works. }
Mais pas si vite ! Ce processus génère des centaines de faux déclenchements en raison de l'effet de rebondissement inhérent à tout commutateur matériel. Heureusement, il existe des solutions matérielles et logicielles à ce problème.
À propos de l'effet de rebond
L'effet de rebond se produit à la suite de l'ouverture ou de la fermeture physique du commutateur de ses «contacts», qui établissent le contact avec le reste du circuit. Lorsque les contacts commencent à se séparer (ouverture de l'interrupteur) ou à s'unir (fermeture de l'interrupteur), de petits arcs électriques peuvent être générés, ainsi qu'une élasticité mécanique dans le circuit qui déclenchera l'activation et la désactivation du circuit pendant quelques millisecondes. Lorsque vous actionnez un interrupteur d'éclairage, cet effet n'est pas apparent ; mais lorsque vous attachez une interruption au front descendant d'un signal, cet effet de rebond déclenche une tonne d'interruptions. Plus ici.
J'ai implémenté à la fois un circuit anti-rebond matériel et une version similaire dans le logiciel. Mais comment implémentez-vous exactement un anti-rebond logiciel ? Facile! Une fois que le premier déclencheur attendu s'est produit, « attendez » suffisamment de temps pour que le rebond se stabilise avant de commencer à écouter les nouvelles interruptions. Cela peut être accompli en quelques lignes de C:
void countRainCycles() { if (nextTimeRainIterrupt == 0 || nextTimeRainIterrupt < millis()) { rainCyclesCounter++; // The interrupts counter nextTimeRainIterrupt = millis() + 100; // Wait 100msecs before next trigger } }
La fonction millis() renvoie le temps d'exécution actuel en millisecondes depuis la mise sous tension de l'Arduino. Il est également intéressant de noter que ces variables doivent être définies comme volatiles pour indiquer au compilateur de ne pas optimiser l'exécution et donc d'éviter des valeurs inexactes lors des interruptions matérielles.

D'une manière ou d'une autre, j'avais besoin que la station stocke les données accumulées et envoie périodiquement ces mesures à une base de données MySQL. J'ai donc ajouté un module Ethernet avec un slot SD pour enregistrer les valeurs et les récupérer à chaque fois qu'un utilisateur (le serveur) se connecte à la station. Lors des tests à domicile avec la connectivité ADSL, cela a étonnamment bien fonctionné, mais j'ai presque perdu mes cheveux lorsque j'ai testé cela "sur le terrain" avec Internet 3G (en utilisant un modem 3G), car la station se réinitialisait de manière aléatoire lorsque j'essayais de récupérer le des mesures! Après des tests significatifs, j'ai finalement découvert que les exemples fournis partout sur Internet qui décrivent le « service » des données à un client connecté ne tenaient pas compte du fait que la connexion pouvait être si mauvaise que la connexion au client pouvait être perdue au milieu de la transmission du paquet, provoquant le le tampon de sortie déborderait. Mais pourquoi une connexion interrompue provoquerait-elle un débordement de tampon ? Eh bien, supposons que la session de transmission démarre et que la station commence à remplir le tampon de sortie avec des données. Idéalement, le client consomme alors ce tampon plus rapidement qu'il ne se remplit. Cependant, lors de la connexion avec un modem 3G, ce n'était pas le cas ! La connexion au client était bien trop mauvaise, de sorte que le tampon se remplissait plus vite qu'il n'était consommé, ce qui provoquait à la fois un débordement de tampon et un redémarrage soudain de la station.
Pour résoudre le problème, j'avais besoin d'ajouter une fonction à la bibliothèque Ethernet fournie avec l'Arduino qui ressemblait à ceci :
int EthernetClient::free() { if (_sock != MAX_SOCK_NUM) return W5100.getTXFreeSize(_sock); return 0; }
Ensuite, j'ai pu vérifier si le client avait de l'espace dans le tampon avant d'essayer de le remplir avec plus de données :
while (file.available() > 0) { if (client.free() > 0) { // This was key to solving the issue c = file.read(); client.print((char)c); } else { // No free buffer? Ok, I'll wait a couple of millis... delay(50); } } file.close();
Au fait, si vous êtes intéressé par la programmation d'un Arduino, voici un excellent guide.
Une autre tâche intéressante était la mise en œuvre d'un journal LIFO. Pourquoi était-ce nécessaire ? Eh bien, généralement, lorsque j'enregistre des mesures dans un fichier donné, l'approche est simple : ouvrez le fichier, ajoutez les nouveaux échantillons à la fin et fermez le fichier. Mais disons que je veux récupérer les 1000 dernières mesures, triées par ordre chronologique. Ces mesures sont à la fin du fichier ; je dois donc ouvrir le fichier, déplacer le curseur jusqu'à la fin, sortir les dernières mesures, puis ramener le curseur du fichier à la mesure précédente et sortir cela en recherchant un délimiteur d'échantillon pour détecter où commencer et s'arrêter. L'Arduino n'a ni assez de RAM ni de puissance de processeur pour exécuter ce processus rapidement, j'avais donc besoin d'une autre approche. Au lieu de cela, j'ai décidé de sortir le fichier dans l'ordre inverse sur le serveur, puis de rétablir les littéraux de chaîne côté serveur :
unsigned long filePosition = file.size(); file.seek(filePosition); while (filePosition >= 0) { if (client.free() > 0){ file.seek(filePosition); c = file.peek(); if (c != -1) { client.print((char)c); } if (filePosition <= 0) { break; } filePosition--; } }
Avec n'importe quelle expérience en tant que développeur PHP, il est facile d'obtenir les derniers exemples avec les caractères dans le bon ordre :
// $output has the reversed string measures, each sample is delimited by ; $rows = split(";", trim($output)); array_walk_recursive($rows, 'reverseString'); if (strlen($rows[0]) == 0) { array_shift($rows); // Remove the first line if empty } function reverseString(&$row, $key) { $row = trim(strrev($row)); } // $rows is now the array of the latest samples :)
Côté serveur, j'ai ensuite configuré un processus cron pour récupérer les dernières mesures toutes les deux minutes et insérer les données dans un moteur MySQL. Pour afficher les données, j'ai créé www.kitesurfcordoba.com.ar et utilisé jQuery pour mettre à jour automatiquement les graphiques (qui sont eux-mêmes générés à l'aide de pChart v2.0, une excellente bibliothèque open source).
Il y avait un tas d'autres astuces nécessaires pour faire fonctionner les choses, liées à la fois à l'ingénierie logicielle et matérielle, mais j'ai traîné assez longtemps - alors parlons de minimiser la maintenance.
« Comment puis-je réduire la maintenance à (presque) zéro ? »
C'était une préoccupation majeure car il n'est certainement pas facile pour moi d'atteindre la gare - si j'étais prêt à conduire deux heures juste pour réparer un dysfonctionnement mineur, je n'aurais pas eu à la faire entrer en premier lieu (je Je ne l'ai pas mentionné auparavant, mais après tout ce que nous avons vécu, la station est en fait une "elle", et son nom est Dorothy).
Alors, de quels types d'erreurs parlons-nous ici ? Eh bien, par exemple : le logiciel peut se bloquer, le réseau peut perdre la connectivité, l'alimentation en énergie peut échouer (et c'est le cas), etc.
Essentiellement, la station doit effectuer autant d'auto-récupération que possible. C'est pourquoi j'ai utilisé à la fois des chiens de garde doux et durs. Pour ceux qui ne sont pas familiers, un chien de garde est un logiciel ou un matériel qui vérifie si un système fonctionne correctement et, si ce n'est pas le cas, tente de le ramener à la vie. L'Arduino a un chien de garde intégré que vous pouvez utiliser. Je l'ai configuré pour attendre 8 secondes : si un appel prend plus de temps que ce délai, le chien de garde du logiciel réinitialisera la carte.
wdt_enable(WDTO_8S); // "wdt" stands for "watchdog timer"
J'adore cette fonction. Cependant, il arrive parfois que la carte se réinitialise et que le module Ethernet ne le fasse pas. Pourquoi? Eh bien, il s'agit d'une carte de prototypage relativement abordable, pas d'un appareil très coûteux et infaillible (vous ne devriez certainement pas construire un stimulateur cardiaque avec). Pour surmonter cet inconvénient, j'ai dû pirater l'Arduino en croisant une entrée de réinitialisation matérielle à une sortie numérique sur la carte elle-même. Afin d'éviter une boucle de réinitialisation, quelques lignes de code doivent également être ajoutées :
void setup() { digitalWrite(RESET_ARDUINO_PIN, HIGH); // Set it to HIGH immediately on boot pinMode(RESET_ARDUINO_PIN, OUTPUT); // We declare it an output ONLY AFTER it's HIGH digitalWrite(RESET_ARDUINO_PIN, HIGH); // Default to HIGH, set to LOW to HARD RESET ...
Après cela, j'ai pu effectuer une réinitialisation matérielle de l'Arduino et de tous les modules qui s'y trouvent (y compris le module Ethernet) en appelant simplement digitalWrite(RESET_ARDUINO_PIN, LOW)
, ce qui a ramené Dorothy à la vie après quelques secondes.
De plus, la carte redémarre automatiquement après une perte d'énergie. Et si la connectivité Internet échoue, nous exploitons les capacités de stockage de la carte SD (les données peuvent être stockées sur la carte pendant plus d'une semaine, et le serveur peut récupérer les anciennes données pour récupérer les échantillons manquants). La combinaison de toutes ces caractéristiques nous donne une station météorologique très robuste qui peut survivre aux conditions hostiles pour lesquelles elle a été conçue. Au total, cette chose m'a coûté environ 300 $.
Et à la fin
La station fonctionne depuis décembre 2012. À ce jour, elle n'a pas échoué (ou si c'était le cas, la station s'est rétablie assez rapidement pour que la communauté du kitesurf et moi ne l'ayons pas remarqué). Il y a environ 500 kitesurfeurs qui vérifient régulièrement la station météo avant de se rendre sur le spot. Donc, en plus de la récompense de la résolution de défis techniques difficiles, j'ai également eu l'opportunité d'offrir à un groupe de personnes une expérience de kitesurf plus agréable.
1 Au départ, j'utilisais un Arduino Uno. Plus tard, je suis passé à un Arduino Mega en raison du besoin d'augmenter la RAM et la mémoire flash.
En relation: Travailler avec l'échantillonnage audio ESP32