1. Arduino¶
1.1. Ajouter un utlisateur au groupe Arduino¶
sudo usermod -a -G dialout <username>
1.2. Eviter les problèmes de téléversement du syle avr_dude()¶
Si par exemple un message d’erreur du style stk500_recv(): programmer is not responding apparaît alors que votre arduino est branché sur ttyACM0 et que cela s’affiche correctement essayer
sudo chmod a+rw /dev/ttyACM0
1.3. Eviter d’utiliser des String¶
Avertissement
je le fais souvent parceque cela allège l’écriture et rend le code plus digeste et plus léger mais c’est NUL et fait planter les programmes utilisant plusieurs capteurs.
Évitez d’utiliser des Strings. Cela prend un temps fou et produit plein d’erreurs à l’exécution. Mieux vaut et de loin utiliser une écriture apparemment plus lourde.
Ainsi plutôt que
DateTime now = rtc.now();
String sentence = String(now.year())+";"+String(now.month())+";"+String(now.day())+";"+String(now.hour()) + ";" + String(now.minute())+ ";" + String(now.second()) + ";";
Serial.println(sentence)
il vaut mieux écrire
DateTime now = rtc.now();
Serial.print(now.year());
Serial.print(F(","));
Serial.print(now.month());
Serial.print(F(","));
Serial.print(now.day());
Serial.print(F(","));
Serial.print(now.hour());
Serial.print(F(","));
Serial.print(now.minute());
Serial.print(F(","));
Serial.print(now.second());
Cela semble plus lourd et plus lent alors qu’en fait c’est l’inverse:ça va bien plus vite et surtout je me suis rendu compte après bien des tests que c’est ce qui faisait planter mes programmes dès que j’avais une horloge, deux capteurs et un lecteur SD.
1.4. Mesurer le courant¶
Code utilisé avec un Nano 33 IOT équipé d’un convertisseur 12 bits. Si on ne dispose que d’un Uno commenter la ligne analogReadResolution(12)
/*
Small code to measure current and voltage the
resitance added to the circuit is 10 ohm
*/
float V;
float R = 1.0;
float I;
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
analogReadResolution(12);
}
// the loop routine runs over and over again forever:
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
V = sensorValue * 3300.0 / 4092.0;
I = V/R;
// print out the value you read:
Serial.print(sensorValue);
Serial.print(" ; ");
Serial.print(V);
Serial.print(" ; ");
Serial.println(I);
delay(100); // delay in between reads for stability
}
Autre code utilisé cette fois avec un convertisseur Analogique-Numérique 16bit de joy-it et un arduino MKRZero. L’avantage dans ce cas c’est la possibilité d’enregistrer directement sur la carte à vitesse maximale pour suivre le signal.
#include <Adafruit_ADS1X15.h>
#include <math.h>
Adafruit_ADS1115 ads;
#include <SPI.h>
#include <SD.h>
const int chipSelect = SDCARD_SS_PIN;
const int cut = 1000; // définition de la limite pour les valeurs abhérentes
void setup(void)
{
Serial.begin(57600);
//while (!Serial);
Serial.print("Initializing SD card...");
if (!SD.begin(chipSelect)) {
Serial.println("Echec de l'initialisation. Vérifiez:");
Serial.println("1. une carte est elle insérée ?");
Serial.println("2. les connexions sont elles correctes ?");
Serial.println("3. avez vous bien choisis le chipselect?");
Serial.println("Note: appuyer sur le bouton reset pour redémarrer la carte une fois vos modifications faites!");
while (true);
}
Serial.println("initialization done.");
// ADS1115
// -------
ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 0.1875mV
//ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 0.125mV
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 0.0625mV
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.03125mV
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.015625mV
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.0078125mV
ads.begin();
}
void loop(void)
{
uint16_t adc0;
float voltage0;
float gain_conversion_factor;
adc0 = ads.readADC_SingleEnded(0); // lecture sur le port A0 de la carte
gain_conversion_factor= 0.1875; //facteur de gain correspondant au choix du setup
voltage0 = (adc0 * gain_conversion_factor); // calcul du voltage
// écriture sur la carte SD des valeurs non abhérantes
if (voltage0 < cut){
String dataString = "";
File dataFile = SD.open("datalog.txt", FILE_WRITE);
dataString += String(millis());
dataString += ";";
dataString += String(voltage0);
if (dataFile) {
dataFile.println(dataString);
dataFile.close();
// print to the serial port too:
Serial.println(dataString);
}
}
}
Précision très moyenne n’en attendez pas des miracles. Grosse déception pour le convertisseur 16 bits.
Avertissement
Dans les très basses tensions l’arduino est mauvais quelle que soit la résolution du convertisseur.
1.5. Nano et auxilliaires¶
Testé avec un capteur BME 680 et une horloge RTC DS1307 d’Adafruit .
Les capteurs et le Nano ne se parlent que si la tension d’alimentation du capteur et de l’horloge est la même que celle du Nano
La tension de sortie des pins digitaux est elle aussi la même que la tension d’entrée donc attention car en alimentation USB on est à 5V alors que sur une alim en 3.3 on tombe à 3.3V C’est un peu perturbant car la doc indique un fonctionnement nominal à 5V et une tension de sortie de 5V.
1.6. Baisser la consommation d’un Nano¶
Nano + librairie LowPower -> 3.8 mA
En enlevant la Led « on » et le régulateur de tension -> 0.2 à 0.3 mA
Si on enlève le convertisseur USB-Série on baisse encore et on doit descendre en dessous de 0.1mA. Pour l’instant je ne l’enlève pas sinon ça devient un peu galère de transférer des programmes et alors autant passer sur un mini pro.
Enfin la modification de l’horloge du processeur, souvent présentée comme une possibilité de gain intéressante, n’a d’intérêt que si on utilise celui-ci (ben oui). Dans une situation comme la notre où le processeur est utilisé moins de 0.1% du temps le gain n’est pas très intéressant.
Dans cette configuration un Nano peut se transformer en datalogger tout à fait correct. D’autant qu’il peut délivrer 40mA sur les ports digitaux. C’est très bien pour les capteurs que l’on peut alimenter par un port digital et éteindre durant le sommeil pour baisser la consommation.
1.7. Réveiller le Nano quand il dort¶
l’utilisation d’une horloge RTC DS3231 munie d’une batterie propre permet de réveiller un arduino en connectant le pin SQw de l’horloge à un pin numérique autorisant le pullup du microcontrôleur
/*
Réveiller un Nano qui dort
avec un DS3231
F. Métivier
CC BY-SA 4.0
*/
#include "LowPower.h"
#include "RTClib.h"
RTC_DS3231 rtc;
const int alarmPin = 2;
const int DT=30;
// Interrupt signaling byte
volatile int jedors = 0;
void setup()
{
// pour affichage dans le moniteur série
Serial.begin(57600);
delay(10);
// déclaration du port de réception de l'alarme
pinMode(alarmPin,INPUT_PULLUP);
rtc.begin();
// Reset des deux alarmes de l'horloge
rtc.disableAlarm(1);
rtc.disableAlarm(2);
rtc.clearAlarm(1);
rtc.clearAlarm(2);
rtc.writeSqwPinMode(DS3231_OFF); // met le SQW en mode alarme
// Démarrage du test
DateTime now = rtc.now();
char buff[] = "démarrage hh:mm:ss DDD, DD MM YYYY";
Serial.println(now.toString(buff));
// Démarrage de l'alarme côté horloge
rtc.setAlarm1(now+TimeSpan(0,0,0,DT),DS3231_A1_Second);
delay(10);
}
void loop()
{
//Fermeture du port série (important)
Serial.end();
//création de l'interruption sur le microcontrôleur
attachInterrupt(digitalPinToInterrupt(alarmPin), reveillesToi, LOW);
//au dodo
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
//arrêt de la procédure d'interruption
detachInterrupt(digitalPinToInterrupt(alarmPin));
//reconnexion du port série
Serial.begin(57600);
//Noter le réveil
DateTime now = rtc.now();
char buff[] = "réveil hh:mm:ss DDD, DD MM YYYY";
Serial.println(now.toString(buff));
delay(10);
//rédémarrage de l'alarme
rtc.disableAlarm(1);
rtc.clearAlarm(1);
rtc.setAlarm1(now+TimeSpan(0,0,0,DT),DS3231_A1_Second);
delay(1000); //juste pour le plaisir d'attendre :))
//test des comptages
Serial.println("Réveillé " + String(jedors) + " fois");
} //end void
void reveillesToi(){
jedors++;
}
La fontion reveillesToi ici est intéressante car elle montre que l’interruption manque de propreté… cf le paragraphe sur la mesure de la pluie.
1.8. Éviter de compter la pluie plusieurs fois :)¶
Les problèmes rencontrés avec les interrupteurs ILS et Arduino sont de deux types. Quand l’auget du pluviomètre bascule l’ILS semble s’ouvrir et se fermer plusieurs fois d’affilée en très peu de temps si bien que
pour un même basculement le compteur de l’Arduino s’incrémente plusieurs fois,
l’Arduino enregistre plusieurs basculement.
D’un point de vue mécanique il est probable soit que la bascule est lente ou hésitante soit que l’aimant est un peu gros. toujours est-il qu’il faut calmer l’Arduino. Au début j’ajoutais un délais dans la fonction appelée par l’interruption mais j’ai fini par comprendre que delay
ne fonctionnait pas dans une fonction appelée par un interrupt
. seul millis
fonctionne. Le delay peut être utilisé pour résoudre le problème des enregistrements multiples mais dans la fonction principale.
if (pcount > 0){ // test si pCount a bien été appelée
/*
écrit les données sur la carte (voir code complet)
*/
pcount=0; // remise à zéro du compteur
delay(500); // délais empêchant un second déclenchement intempestif
}
Pour les incrémentations il faut donc utiliser une autre technique le « debouncing ». l’usage d’une variable static a cet avantage que t0 garde sa dernière valeur en mémoire. donc la première fois ce sera 0 la suivante ce sera t etc… Ici on filtre donc tout signal qui se déclenche à moins de 100 ms du précédent.
void pCount()
{
static unsigned long t0 = 0;
unsigned long t = millis();
if (t - t0 > 100) //choix du seuil de debouncing ici 100ms
{
pcount++;
}
t0 = t;
}
1.9. Eviter de compter un réveil plusieurs fois¶
Dans le cas de l’utilisation d’un DS3231 comme réveil il faut faire attention aux réveils multiples qui gachent la vie. La solution précédente ne fonctionne que dans le cas d’une arduino qui ne dort pas. En effet quand un Arduino est mis en sommeil profond l’horloge interne est remise à zero millis() ne sert donc plus à rien… Il faut donc une horloge externe. Une horloge RTC fera bien l’affaire tout en sachant que sa précision est d’une seconde. Pour nos applications environnementales c’est presque toujours largement suffisant. Dans le script suivant je vérifie que l’interruption se produit à une H:M:S différente de la précédente.
/*
* Exemple de contrôle des interruptions parasites en mode deepSleep
* avec une horloge RTC
*
* La précision des mesures étant de 1 secondes on interdit le comptage des interruptions plus fréquentes qu'une seconde
* le test vérifie que H:M:S sont distincts entre deux interruptions
*
* testé avec un Nano le 01/08/2024
*
* F. Métivier
* CC BY-SA 4.0
*/
#include "LowPower.h"
#include "Wire.h"
#include <TimeLib.h>
#include <DS1307RTC.h>
const int pullupPin = 2;
volatile int nl=0;
tmElements_t t0;
void setup() {
// put your setup code here, to run once:
Serial.begin(57600);
Serial.println("Initilisation");
pinMode(2,INPUT_PULLUP);
tmElements_t tm;
if (RTC.read(tm)){
Serial.print(tm.Hour);
Serial.print(":");
Serial.print(tm.Minute);
Serial.print(":");
Serial.println(tm.Second);
}
t0=tm;
}
void loop() {
attachInterrupt(digitalPinToInterrupt(2),reveil,FALLING);
Serial.end();
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
detachInterrupt(digitalPinToInterrupt(2));
Serial.begin(57600);
tmElements_t tm;
if (RTC.read(tm)){
if (tm.Second != t0.Second | tm.Minute != t0.Minute | tm.Hour != t0.Hour ){
nl++;
Serial.print("réveillé ");
Serial.print(nl);
Serial.print(" ou ");
Serial.print(nr);
Serial.print(" fois ? ");
Serial.print(tm.Hour);
Serial.print(":");
Serial.print(tm.Minute);
Serial.print(":");
Serial.println(tm.Second);
Serial.print(t0.Hour);
Serial.print(":");
Serial.print(t0.Minute);
Serial.print(":");
Serial.println(t0.Second);
t0 = tm;
}
}
delay(100);
}
void reveil(){
// Rien !
}
2. Synchroniser ses horloges¶
2.1. DS1307¶
/*
Synchronisation d'une horloge DS1307
Last tested: August 3 2024 on mac and linux machines
Borad: Nano
F. Métivier
CC BY-SA 4.0
*/
#include <RTClib.h>
RTC_DS1307 rtc;
void setup() {
Serial.begin(57600);
while (!Serial);
Serial.println("Port série connecté");
rtc.begin();
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
Serial.print("Horloge DS1307 synchronisée à:");
Serial.print(__DATE__);
Serial.println(" "+ String(__TIME__));
DateTime now = rtc.now();
Serial.print("DS1307 Test d'horloge:");
Serial.print(" "+String(now.month())+'/'+String(now.day())+'/'+String(now.year()));
Serial.println(" "+String(now.hour())+':'+String(now.minute())+':'+String(now.second()));
delay(100);
}
void loop() {
// put your main code here, to run repeatedly:
DateTime now = rtc.now();
Serial.print("DS1307 Test d'horloge:");
Serial.print(" "+String(now.month())+'/'+String(now.day())+'/'+String(now.year()));
Serial.println(" "+String(now.hour())+':'+String(now.minute())+':'+String(now.second()));
delay(2000);
}
2.2. DS3231¶
/*
Synchronisation d'une horloge RTC DS3231 à la date courante
Dernier test: 17 Juillet 2024 sur mac et linux
Carte: Nano
F. Métivier
CC BY-SA 4.0
*/
#include <DS3231.h>
#include <Wire.h>
DS3231 rtc;
DateTime today;
enum monthes {Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec};
char cmonth[5];
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
int day, month, year, hour, minute, second;
void setup() {
Serial.begin(9600);
Wire.begin();
pinMode(2,OUTPUT);
digitalWrite(2,HIGH);
sscanf(__DATE__,"%s %d %d",&cmonth, &day, &year);
sscanf(__TIME__,"%d:%d:%d",&hour,&minute,&second);
rtc.setSecond(second);
rtc.setMinute(minute);
rtc.setHour(hour);
month = (strstr(month_names, cmonth)-month_names)/3 +1;
Serial.println(String(cmonth)+ " -> " +String(month));
rtc.setDate(day);
rtc.setMonth(month);
rtc.setYear(year);
Serial.print("Horloge DS3231 synchronisée à:");
Serial.print(" "+String(month)+'/'+String(day)+'/'+String(year));
Serial.println(" "+String(hour)+':'+String(minute)+':'+String(second));
DateTime now = RTClib::now();
Serial.print("DS3231 test d'horloge:");
Serial.print(" "+String(now.month())+'/'+String(now.day())+'/'+String(now.year()));
Serial.println(" "+String(now.hour())+':'+String(now.minute())+':'+String(now.second()));
digitalWrite(2,LOW);
}
void loop() {
// put your main code here, to run repeatedly:
}
2.3. Références¶
Dernière modification le 2024-11-08