Mettre en veille l'arduino pour gagner de l'autonomie

Salut à tous,

Bon je sais, vous allez raler parce que le sujet que je vais exposer a été abordé un paquet de fois sur le forum et des dizaines de docs existent sur le sujet.

Cependant, comme j’y arrive toujours pas je viens solliciter votre aide.

J’ai un projet de timelapse qui va courir sur quelques semaines. En gros, je dois prendre des photos d’un chantier tous les jours de 8 h à 17h sauf le samedi et dimanche

Pour déclencher mes prises de vue j’utilise un arduino Uno relié a un RTC 1307.

Mon code fonctionne bien mais j’ai un problème d’autonomie. Pour éviter de venir changer ma batterie (une 15000mAh) trop souvent j’aurais aimé passer en veille l’arduino durant la nuit ou, mieux, entre chaque photo.

D’après ce que j’ai lu, ce n’est pas possible de le faire à moins d’utiliser la fonction SLEEP_MODE_PWR_DOWN avec le Watchdog timer qui réveille la carte toutes les 8secondes.

Cette astuce (décrite ici) me permettrait cependant de faire de belles économies déjà.

Seulement… Je ne comprends vraiment pas comment la mettre en place dans mon cas.

Je suis débutant en programmation et j’ai peur de faire une bêtise (si on fait une erreur, le risque de mettre en veille l’arduino pour toujours existe-t-il par exemple ?)

Dans ma logique il s’agirait de dire à l’arduino. Si il est plus de 17h et moins de 8h > passe en veille (avec réveil toutes les 8sec pour vérifier qu’il rentre toujours dans le cadre horaire).

Question subsidiaire, quelle consommation puis-je espérer avec un arduino en veille ?

Si jamais j’ai pas été clair, je vous mets le code ci dessous (hésitez pas à me dire s’il déconne d’ailleurs)

Merci !

#include <Wire.h>
#include "RTClib.h"

RTC_DS1307 RTC; //Classe RTC_DS1307
const int relai = 10; //Pin du relai

void setup () {
  Serial.begin(57600); //Démarrage de la communication
  Wire.begin(); //Démarrage de la librairie wire.h
  RTC.begin(); //Démarrage de la librairie RTClib.h
  
  //RTC.adjust(DateTime(__DATE__, __TIME__));
  //Met  lheure laquelle le sketch est compile
  
  pinMode(relai, OUTPUT);


  //Si RTC ne fonctionne pas
  if (! RTC.isrunning()) {
    Serial.println("RTC ne fonctionne pas !");

    //Met à l'heure à date à laquelle le sketch est compilé
    //Cela fonctionne également :
    //RTC.adjust(DateTime("Dec  5 2012","12:00:00"));
    //RTC.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
}

void loop() {
  DateTime now = RTC.now();

  if (now.dayOfWeek() >= 1 && now.dayOfWeek() <= 5 && now.hour() >= 8 && now.hour() <= 17) // && now.minute() >= 15 && now.minute() < 20)
  {
    digitalWrite(relai, 1);
    delay (50);
    digitalWrite(relai, 0);
    delay (60000);
    /*Serial.print(now.dayOfWeek(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.year(), DEC);
    Serial.print(' ');
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
    */
 
  }
else {
     Serial.print ("ERREUR");
    delay (5000);
}
}

bonjour,
avec 15Ah tu ne tiens pas si longtemps?
tu as une perte de jus quelque part à mon avis, j’ai une 12Ah pour mon télescope et je la recharge tous les 6 mois et encore avec une utilisation de 4-8h par semaine.
bon, c’est une batterie gel aussi, donc décharge lente.

tu consomme combien avec ton montage?
c’est pas le nono qui consomme beaucoup en tous cas.

sur certains RTC, il y a une fonction qui permet de réveiller un matos de mémoire.

Je ne dois pas consommer beaucoup plus de 50mAh/h puisque mon code et mon montage sont très simples (juste un relai en plus de l'arduino, le crt étant autoalimenté).

En fait je m'inquiete puisque j'ai fait le test en plugant mon arduino tout seul avec un programme qui tournait dans le vide sur ma batterie (via le port usb 1A).

Celle-ci a semblé perdre 1/4 de sa capacité en 20h. Je dis "semblé" puisque je me suis référé à la jauge de la batterie qui avait perdu une led sur 4.

Mais c'est vrai que ça parait étonnant. En faisant des estimations basses et en imaginant que seuls 10000mAh sont effectif sur ma batterie (c'est une LiPo, elle doit garder 20% de charge sous le coude pour ne pas mourir par ex), ça ferait une conso de 500mAh/h.

Peut-etre que ma batterie est nulle remarque.

jète un oeil la dessus, tu peux baisser la fréquence pour moi consommer par exemple http://www.robot-maker.com/forum/blog/45/entry-41-tout-ce-que-vous-pourriez-vous-demander-sur-la-consommation-dune-puce-atmega328p-puce-darduino/

Merci pour le lien, je l'avais déjà parcouru. Mais c'est typiquement le genre de chose que je comprends pas.

Il y a des exemples de tous les cotés et je suis malheureusement pas assez doué pour les comprendre ou pour les appliquer à mon cas perso.

Pour la fréquence, tout ce qu'il y a faire selon lui c'est ça :

avrdude -c usbtiny -p m328p -U lfuse:w:0xE2:m

On met cette ligne en tete du code ? Et quid de la fiabilité ? Ca peut faire péter un truc d"underclocker" à 8MHz ?

Bonjour, AVRDude est un outil en ligne de commande qu'il faut lancer à la main (il est situé dans le dossier tools/avr/bin si j'ai bonne mémoire).

Sinon, l'autre problème est que les cartes Arduino ne sont pas des cartes basse consommation. Tu peux certes gagner un peu plus d'une dizaine de mA en utilisant le watchdog (via la librairie LowPower par exemple, qui est extrèmement simple à utiliser https://github.com/rocketscream/Low-Power ), mais tu auras quand même la consommation de la Led Power, des deux régulateurs de tension, et du circuit qui fait le pont USB-Série.

Si tu veux vraiment optimiser la conso, tu pourrais t'inspirer de ça par exemple: https://www.openhomeautomation.net/arduino-battery/

Faire tourner l'ATMega à 8MHz (en fait à 1MHz en interne) au lieu de 16, c'est pas simple pour un débutant et je ne suis pas sûr que ça apporte beaucoup niveau conso par rapport aux optimisations de la carte et à l'utilisation du watchdog.

J'ai récemment fait un tuto pour expliquer comment utiliser AVRDude pour faire tourner l'ATMega à 8MHz en utilisant son quartz interne (voir lien vers mon blog dans ma signature) mais dans mon cas c'est pour gagner de la place sur le circuit et économiser le coût d'un quartz+condos. J'utilise la lib LowPower pour endormir indéfiniment l'ATMega et il est réveillé toutes les secondes par une interruption externe qui provient d'une horloge RTC. Pour un autre projet j'endors l'ATMega et j'utilise le watchdog pour le réveiller toutes les 8s (pour un autre type d'horloge).

Voilà pour quelques exemples de mise en oeuvre.

Malheureusement ta DS1307 ne possède pas d'alarme, qui te permettrait d'économiser beaucoup plus de batterie, en réveillant l'arduino moins souvent. Avec ta DS1307, au mieux, tu peux réveiller ton arduino toutes les secondes, avec la sortie Square wave réglée en 1Hz. De plus, cette RTC n'est vraiment pas précise et peut dériver de plus d'une minute par jour :S

Je suggère la DS3231 qui, en plus d'être beaucoup plus précise (quelques minutes par an), possède une sortie alarme, ce qui permettrait de réveiller l'arduino toutes les minutes, ou même toutes les heures. Donc si ton plan est de prendre une photo par heure, ce serait l'idéal ;)

A lire: http://www.gammon.com.au/interrupts

Moi dès qu'il s'agit de RTC et d'économie d'énergie je conseille le launchpad de texas instrument. Avec Energia y'a deux commandes directe pour la mise en sommeil et il suffit de souder de quartz (fourni avec la carte) pour avoir une RTC ... Et il est designer pour la basse conso

En effet, j’utilise actuellement une DS3231 et c’est très pratique. L’autre avantage, du moins avec la breakout-board que j’ai achetée chez dx.com ( http://www.dx.com/fr/p/ds3231-high-precision-real-time-clock-module-blue-3-3-5-5v-222910#.VWL2KU_tlBc ), c’est qu’elle intègre une pile rechargeable pour la sauvegarde en cas de coupure de courant, ainsi qu’une eeprom série.
L’inconvénient majeur de cette breakout board est qu’elle intègre une led power qui la rend inadaptée aux économies d’énergie (mais on peut la dessouder relativement facilement).

Je l’utilise avec la librairie dont le lien figure sur la page.

Merci à tous pour vos réponses.

Après réflexion, je laisse tomber le changement de fréquence, en effet j’ai peu de ne rien comprendre.

Je vais essayer la librairie low-power que tu indiques Patg_, elle me parait parfaitement prémâchée, c’est parfait pour moi :wink:

L’exemple que tu donnes est très bien aussi mais ce sera pour une V2, je n’aurais pas le temps de faire un montage comme celui-ci avant l’installation.

Pareil pour l’horloge DS3231 que tu me conseille Guix, je verrai ça ultérieurement. Pour le moment après une dizaine d’heures, je n’ai pas encore remarqué de décalage.

Mais c’est vrai qu’une alarme serait la bienvenue !

Et pendant que j’y suis, avec la librairie low-power, je peux bien écrire cela (je vous met que le loop) ? :

void loop() {
  if (now.dayOfWeek() >= 0 && now.dayOfWeek() <= 5
      && now.hour() >= 8 && now.hour() <= 17 &&
      now.minute() == 0 || now.minute() == 5 || now.minute() == 10
      || now.minute() == 15 || now.minute() == 20 || now.minute() == 30 ||
      now.minute() == 35 || now.minute() == 40 || now.minute() == 50 ||
      now.minute() == 55 || now.minute() == 60 || ) //Une photo toute les 5minutes
  {
    digitalWrite(relai, 1);
    delay (50);
    digitalWrite(relai, 0);
    for (count = 0; count < 60 ; count++)
    {
      delay(1000); // delai de 60 secondes
    }
    LowPower.idle(SLEEP_8S); //Endormissement de 8sec si occurence si dessus pas respectees rendormissement de 8sec
  };

  if else (now.dayOfWeek() >= 1 && now.dayOfWeek() <= 5
             && now.hour() >= 17 || now.hour() <= 8)
    {
      LowPower.idle(SLEEP_8S);
    }

  if else (now.dayOfWeek() == 0 || now.dayOfWeek() == 6
            )
    {
      LowPower.idle(SLEEP_8S);
    }

Je ne pense pas que le code soit très optimisé mais est-ce qu’il est valable au moins ?

Ciao

J'ai pas le temps de vérifier ton code (je bricole sur mes propres montages Arduino ;) ) mais ton code n'est pas efficace. Pour faire une pause d'1 min il vaudrait mieux utiliser le watchdog avec une période de 4s et compter 15 périodes. Ou bien plus simple, récupérer l'heure courante à chaque période et comparer à l'heure à laquelle le programme doit continuer. Ca change complètement la logique du code, mais c'est bien plus efficace.

Exemple concret: Une de mes horloges doit se mettre à l'heure la nuit entre 2h et 3h du matin en allumant un récepteur DCF77 pendant quelques minutes. A chaque réveil du watchdog, toutes les 8s, je récupère l'heure, compare à l'heure de début et si elle est passée et que je n'ai pas encore atteint l'heure à laquelle il faut couper le récepteur, je l'allume. Etc...

Enfin pour faire une pause d'une minute si tu tiens à utiliser delay(), un delay(60000) est plus précis que de faire une boucle de 10 delay(1000) car le temps d'exécution des instructions de la boucle s'ajoute à ton temps d'attente.

Yep, je réfléchissais à une simplification justement et voici ce que ça donne (mon autre code était truffé d’erreurs)

void loop() {
  DateTime now = RTC.now();


  if (now.dayOfWeek() >= 0 && now.dayOfWeek() <= 5
      && now.hour() >= 8 && now.hour() <= 17 &&
      now.minute() == 0 || now.minute() == 5 || now.minute() == 10
      || now.minute() == 15 || now.minute() == 20 || now.minute() == 30 ||
      now.minute() == 35 || now.minute() == 40 || now.minute() == 50 ||
      now.minute() == 55 || now.minute() == 60) //Une photo toute les 5minutes
  {
    digitalWrite(relai, 1);
    delay (50);
    digitalWrite(relai, 0);

    for (count = 0; count < 60 ; count++)
    {
      delay(1000); // delai de 60 secondes
    }

LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
    //LowPower.idle(SLEEP_8S); //Endormissement de 8sec si occurence si dessus pas respectees rendormissement de 8sec*/
  }

}

En gros là il regarde quelle heure il est toute les 8 secondes. S’il rentre pas dans les conditions du if, il reprend sa veille. Si il rentre dans le créneau horaire (toutes les 5min de 8h à 17h), il ferme le relai et attend une minute (ça évite de faire plein de photos) puis se rendort.

hello, ton premier lien m’a interressé
veux tu tester ce code ( liaison en 57600 )

il fonctionne correctement chez moi (avec UNO)
avec compteur et tempo_desiree en “int” tu peux mettre un sommeil de 9 heures
avec compteur et tempo_desiree en “unsigned int” tu peux mettre un sommeil de 18 heures

//Optimisation de la consommation
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
volatile int f_wdt = 1;
unsigned int tempo_desiree = 32; // mettre un multiple de 8, mais pas obligatoire
unsigned int compteur_reveil = 0;
unsigned int compteur_travail=0;
// Watchdog Interrupt Service est exécité lors d'un timeout du WDT
ISR(WDT_vect)
{
  if (f_wdt == 0)
  {
    f_wdt = 1; // flag global }
  }
}
// paramètre : 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms, 6=1 sec,7=2 sec, 8=4 sec, 9=8 sec
void setup_watchdog(int ii) {
  byte bb;
  int ww;
  if (ii > 9 ) ii = 9;
  bb = ii & 7;
  if (ii > 7) bb |= (1 << 5);
  bb |= (1 << WDCE);
  ww = bb;
  // Clear the reset flag
  MCUSR &= ~(1 << WDRF);
  // start timed sequence
  WDTCSR |= (1 << WDCE) | (1 << WDE);
  // set new watchdog timeout value
  WDTCSR = bb;
  WDTCSR |= _BV(WDIE);
}

void enterSleep(void) {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode(); //Entre dans le mode veille choisi
  //le micro passe en sommeil pour 8 secondes
  // Le programme va reprendre ici après le timeout du WDT
  sleep_disable(); // La 1ère chose à faire est de désactiver le mode veille
}

//************* SETUP *************
void setup() {
  //Optimisation de la consommation
  //power_adc_disable(); // Convertisseur Analog / Digital pour les entrées analogiques
  //power_spi_disable();
  //power_twi_disable();
  // Si pas besoin de communiquer par l'usb
  //power_usart0_disable();
  //Extinction des timers, attention timer0 utilisé par millis ou delay
  //power_timer0_disable();
  //power_timer1_disable();
  //power_timer2_disable();
 
  setup_watchdog(9);
  Serial.begin(57600);
}

//************* LOOP *************

void loop() 
{
  if (f_wdt == 1) 
  { 
     compteur_reveil++;
    // Effectuer les mesures ici si sommeil = 8 secondes 
       Serial.print("reveil pour la  ");
       Serial.print(compteur_reveil,DEC);
       if (compteur_reveil==1){Serial.println(" ere fois");}
       else {Serial.println(" eme fois");}
   // fin des mesures ici si sommeil = 8 secondes
    if ((compteur_reveil*8)>= tempo_desiree)
    {
      compteur_travail++;
    // Effectuer les mesures ici si sommeil >8 secondes    
       Serial.print("travail car il y a eu  ");
       Serial.print((compteur_reveil*8),DEC);
       Serial.println(" secondes de sommeil ");
       Serial.print("il y a ");
       Serial.print((compteur_travail*32),DEC);
       Serial.println(" secondes de fonctionnement ");
    // fin des mesures ici si sommeil >8 secondes 
    compteur_reveil = 0;
    } 
    delay(2000); //pour laisser le temps aux "serial.print" de se fairent
    f_wdt = 0; // Ne pas oublier d'initialiser le flag, sinon pas de sommeil
    enterSleep(); //Revenir en mode veille
  } 
}

Bonjour, Pourquoi ne pas utilser un programmateur horaire 12V qui mettrai hors service l'alim de l'arduino et de tous les équipements électriques les nuits et les week-end. Ils en existent à environ 10 euros sur le site DX.com où chez les marchands de matériel solaire. La consommation en veille est faible (0.1w).Par contre de l'ordre de 3W en fonction, à toi de voir si ça passe.