mercredi 24 décembre 2014

Porte de poulailler Part - 3 : Arduino avec RTC (horloge)

 Retour Part-2

Version arduino avec horloge RTC

Dans cette article nous allons voir comment utiliser une horloge RTC. L'idée est de fermer et d'ouvrir la porte du poulailler selon un horaire défini par le microcontrôleur. On a besoin d'une horloge (Real Time Clock) DS1307 sur batterie interne, d'une carte de commande de moteur CC avec un bridge type DRV8835 par exemple et de 2 alimentations : une pour le microcontrôleur et l'autre pour le moteur. 

La mise en oeuvre d'une horloge RTC sur bus I2C se fait en connectant  la sortie 'sda' sur le pin 4  et 'sdl' sur le pin 5 de l'arduino. Pour le branchement des autres éléments je vous invite à consulter la partie 2.

 Schéma de câblage :

Réglage de l'heure :

Lors de l'achat de votre RTC, vous voudrez certainement régler l'heure, inutile de faire un article de plus vous trouverez toutes les infos ici ! Voici le lien vers la librairie RTCLib nécessaire pour utiliser l'horloge.
Dans mon cas j'ai opté pour conserver l'heure d'hiver. 

L'étape suivante est de paramétrer les tableaux d'ouverture et de fermeture dans le code du microcontrôleur (ces tableaux seront utilisés dans le sketch de l'arduino un peu plus loin) :

Après une petite recherche sur internet, vous trouverez certainement les horaires de lever et de coucher du soleil de votre région.
http://ecole.onsevoitdemainalors.org/IMG/pdf/41_Tableau_02.pdf

Voici les tableaux que nous allons utiliser :
 
 ////////////////////////// JANV    FEV    Mars    Avr   Mai    Jun  
 int LeveSoleilHeure[12]=  {07,06, 06,06, 05,05, 04,04, 03,03, 03,03  };  
 int LeveSoleilMinute[12]= {00,50, 40,20, 40,00, 30,00, 45,15, 05,05  };  
 int CoucheSoleilHeure[12]= {18,18, 19,19, 19,20, 21,21, 21,22, 22,22  };  
 int CoucheSoleilMinute[12]={15,45, 00,30, 45,35, 00,25, 45,05, 20,25  };  



Observons les 2 tableaux suivants: LeveSoleilHeure[12] ,LeveSoleilMinute[12].

Ils représentent une demi année,  pour faire l'année complète on reprends le tableau à l'envers, ainsi juillet=juin, aout=mai etc... il y a un tableau pour les heures et un autre pour les minutes. Il y a 2 heures de défini par mois par exemple pour le mois de janvier, du [1-14] l'ouverture est programmé à 07h00 et du [15-31] janvier l'ouverture se fait à 06h50 heure d'hiver. 

Remarques : Pour économiser de l’énergie, je coupe l'alimentation de la carte de commande du moteur (pin D3), idem pour l'horloge  (pin D2, elle passera automatiquement sur pile de sauvegarde).

Les conditions initiales :
l'arduino doit-être branché de jour et la porte du poulailler est fermée.

Voici le code complet:
 /*   
  Electronic 77  
  Version 3.0 du 09/01/2014   
  L9110 or DRV8835 motor driver controlling    
  - 1 small DC motors      
  - RTC DS1307   
  */   
  #include <Wire.h>   
  #include "RTClib.h"   
  #include <avr/sleep.h>   
  // This library contains functions to set various low-power    
  // states for the ATmega328   
  // This variable is made volatile because it is changed inside   
  // an interrupt function   
  volatile int sleep_count = 0; // Keep track of how many sleep   
  // cycles have been completed.   
  const int interval = 5; // Interval in minutes between waking   
  // and doing tasks.   
  const int sleep_total = (interval*60)/8; // Approximate number    
  // of sleep cycles needed before the interval defined above    
  // elapses. Not that this does integer math.   
  /*   
  SCHEMA Utilisation d'un moteur CC   
  pin IA1 || LOW  || High   
  pin IB1 || HIGH || Low   
  Motor || backward || forward   
  PWM: 3, 5, 6, 9, 10, and 11. Provide 8-bit PWM output with the analogWrite() function.    
  */    
  const int IA1 = 9; // DIGITAL PIN 8 Bits    
  const int IB1 = 10; // DIGITAL PIN 8 Bits   
  const int TpsMontee=1200; // réglage du temps de montée   
  const int TpsDescente=880; // réglage du temps de descente   
  /*MODULE RTC SPARK FUN BOB 099   
  DS 1307 Communication I2C   
  Récupération de l'heure via I2C sur les ports 4 et 5 analogique de l'arduino NANO   
  */   
  // RTC   
  RTC_DS1307 RTC;   
  int AlimentationRTC=3;   
  //Carte Brighe Commande   
  int ALimentationCMD=2;   
  //POULAILLER    
  boolean FlagOpen=false; //porte   
  //Heure Hiver   
  //http://ecole.onsevoitdemainalors.org/IMG/pdf/41_Tableau_02.pdf   
  //matin retire 1h45    
  // soir ajoute 1h30   
  ////////////////////////// JANV FEV Mars Avr Mai Jun   
  int LeveSoleilHeure[12]= {07,06, 06,06, 05,05, 04,04, 03,03, 03,03 };   
  int LeveSoleilMinute[12]= {00,50, 40,20, 40,00, 30,00, 45,15, 05,05 };   
  int CoucheSoleilHeure[12]= {18,18, 19,19, 19,20, 21,21, 21,22, 22,22 };   
  int CoucheSoleilMinute[12]={15,45, 00,30, 45,35, 00,25, 45,05, 20,25 };   
  void setup(void) {   
   //Commande du moteur cc via le L9110H   
  pinMode(IA1, OUTPUT); // set pin to output   
  pinMode(IB1, OUTPUT); // set pin to output   
  // au branchement lance l'ouverture   
  // COndition initiales :    
  // - faire jour   
  //- Porte poulailler fermée   
  stop();   
   // Carte de commande des moteurs switch alim pour economie d'nrj   
   pinMode(ALimentationCMD, OUTPUT);    
   digitalWrite(ALimentationCMD, LOW);    
   pinMode(AlimentationRTC, OUTPUT);     
   digitalWrite(AlimentationRTC, LOW);    
   Wire.begin();   
   RTC.begin();;   
   //Serial.begin(57600);   
  watchdogOn(); // Turn on the watch dog timer.   
  // The following saves some extra power by disabling some    
  // peripherals I am not using.   
  // Disable the ADC by setting the ADEN bit (bit 7) to zero.   
  ADCSRA = ADCSRA & B01111111;   
  // Disable the analog comparator by setting the ACD bit   
  // (bit 7) to one.   
  ACSR = B10000000;   
  // Disable digital input buffers on all analog input pins   
  // by setting bits 0-5 to one.   
  DIDR0 = DIDR0 | B00111111;   
  //Serial.print("FIN init");   
  //Serial.println();   
  }   
  void loop(void) {   
  goToSleep(); // ATmega328 goes to sleep for about 8 seconds   
  // and continues to execute code when it wakes up   
  if (sleep_count == sleep_total) {   
  // CODE TO BE EXECUTED PERIODICALLY   
   digitalWrite(AlimentationRTC, HIGH);    
   delay(500);   
   DateTime now = RTC.now();   
   digitalWrite(AlimentationRTC, LOW);    
   //Les poules sont -elles levées   
   int index= getIndex(now);   
   if (FlagOpen==false && canOpen(now,index))   
   {   
   //Ouvre   
   OpenPorte();   
   FlagOpen=true;   
   }   
   if (FlagOpen && canClose(now,index))   
   {   
   //Ferme   
   ClosePorte();   
   FlagOpen=false;   
   }   
   //Serial.print(now.year(), DEC);   
   //Serial.print('/');   
   //Serial.print(now.month(), DEC);   
   //Serial.print('/');   
   //Serial.print(now.day(), DEC);   
   //Serial.print(' ');   
   //Serial.print(now.hour(), DEC);   
   //Serial.print(':');   
   //Serial.print(now.minute(), DEC);   
   //Serial.print(':');   
   //Serial.print(now.second(), DEC);   
   //Serial.println();   
   delay(5000);   
   sleep_count = 0;    
  }   
  }   
  void goToSleep()    
  {   
  // The ATmega328 has five different sleep states.   
  // See the ATmega 328 datasheet for more information.   
  // SLEEP_MODE_IDLE -the least power savings    
  // SLEEP_MODE_ADC   
  // SLEEP_MODE_PWR_SAVE   
  // SLEEP_MODE_STANDBY   
  // SLEEP_MODE_PWR_DOWN -the most power savings   
  // I am using the deepest sleep mode from which a   
  // watchdog timer interrupt can wake the ATMega328   
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode.   
  sleep_enable(); // Enable sleep mode.   
  sleep_mode(); // Enter sleep mode.   
  // After waking from watchdog interrupt the code continues   
  // to execute from this point.   
  sleep_disable(); // Disable sleep mode after waking.   
  }   
  void watchdogOn() {   
  // Clear the reset flag, the WDRF bit (bit 3) of MCUSR.   
  MCUSR = MCUSR & B11110111;   
  // Set the WDCE bit (bit 4) and the WDE bit (bit 3)    
  // of WDTCSR. The WDCE bit must be set in order to    
  // change WDE or the watchdog prescalers. Setting the    
  // WDCE bit will allow updtaes to the prescalers and    
  // WDE for 4 clock cycles then it will be reset by    
  // hardware.   
  WDTCSR = WDTCSR | B00011000;    
  // Set the watchdog timeout prescaler value to 1024 K    
  // which will yeild a time-out interval of about 8.0 s.   
  WDTCSR = B00100001;   
  // Enable the watchdog timer interupt.   
  WDTCSR = WDTCSR | B01000000;   
  MCUSR = MCUSR & B11110111;   
  }   
  ISR(WDT_vect)   
  {   
  sleep_count ++; // keep track of how many sleep cycles   
  // have been completed.   
  }   
  ///////////////////////////////////////////////////////////////////////   
  //////: GESTION POULIAILER   
  ///////////////////////////////////////////////////////////////////////   
  int getIndex(DateTime t){   
  int Index=t.month();   
   if (t.month()>6)   
 {  
   Index=13-t.month();    
   Index=(Index-1)*2;     
   if( t.day()<15)   
   Index++;   
 }  
 else   
 {  
   Index=(Index-1)*2;     
   if( t.day()>15)   
   Index++;   
 }  
   //Serial.print("Index ");   
   //Serial.print(Index, DEC);   
   //Serial.println();   
   return Index;   
  }   
  boolean canOpen(DateTime t,int index)   
  {   
   if (t.hour()<=13 && (t.hour()== LeveSoleilHeure[index] && t.minute()>= LeveSoleilMinute[index] || t.hour()> LeveSoleilHeure[index]))   
   return true;   
   else    
   return false;   
  }   
  boolean canClose(DateTime t,int index){   
   if (t.hour()<=23 && (t.hour()== CoucheSoleilHeure[index] && t.minute()>= CoucheSoleilMinute[index] || t.hour()> CoucheSoleilHeure[index]))   
   return true;   
   else    
   return false;   
  }   
  void SetALimentationCMD(boolean isAlimente)   
  {    
  if (isAlimente)   
  {   
   digitalWrite(ALimentationCMD, HIGH);     
   delay(1000);   
  }   
  else   
   digitalWrite(ALimentationCMD, LOW);     
  }   
  void ClosePorte()   
  {   
  SetALimentationCMD(true);   
  //Serial.print("Fermeture de la porte\n");   
   backward();   
   delay(TpsDescente);     
   stop();   
  SetALimentationCMD(false);    
  }   
  void OpenPorte()   
  {   
   SetALimentationCMD(true);   
   //Serial.print("Ouverture de la porte\n");   
   forward();    
   delay(TpsMontee);   
   stop();   
   SetALimentationCMD(false);    
  }   
  void stop(){   
  digitalWrite(IA1, LOW);   
  digitalWrite(IB1, LOW);   
  delay(1000);   
  }   
  void backward()   
  {   
  digitalWrite(IA1, LOW);   
  digitalWrite(IB1, HIGH);   
  }   
  void forward()   
  {   
  digitalWrite(IA1, HIGH);   
  digitalWrite(IB1, LOW);   
  }   
  ////////////////////////////////////////////////////////////////////////  


 Quel est l'avantage d'utiliser une horloge par rapport à  un capteur crépusculaire ?  

L'horloge est un système plus robuste, il ne souffre pas d'une mauvaise détection du capteur (feuille sur le capteur, etc... ), pas de rebond de détection.
Cependant la consommation comme le montage précédent ne permet pas de tenir la semaine. Malgré une mise en veille de l'arduino toute les 8 secondes.
C'est pourquoi dans la  4ème partie nous allons utiliser une horloge RTC DS3231 qui à l'avantage d'avoir un mode alarme pour mettre en veille prolongée le microcontrôleur et en plus, on va changer de microcontrôleur par un moins gourmand en énergie. J'ai choisi d'acheter un kit 'launchpad' de la famille MSP430 de chez Texas Instrument.

Lien vers la partie 4

2 commentaires:

  1. OK, c'est bien!!! mais j'utilise un interrupteur crépusculaire et j'y trouve des avantages:
    -C'est connu, les poules se couchent avec la tombée du jour et avec une horloge, il faut reprogrammer régulièrement l'heure de fermeture et d'ouverture... pas avec un inter crépusculaire.
    -Votre système reviens plus cher qu'un inter crépusculaire qui coûte à peine 10€ chez Velleman (MK125).
    -Le montage avec un inter crépusculaire est plus à la portée des petits bricoleurs qui bidouillent un peu l'électicité/électronique.
    -Quant à la mauvaise détection du capteur (feuille sur le capteur, etc... ), et le problème des rebonds de détection, je n'en ai jamais eu depuis 6 ans que j'ai installé mon système.
    Cordialement.

    RépondreSupprimer
  2. Bonjour, j'ai réalisé ma trappe auto grâce à ce tuto (merci). J'ai choisi cette version. J'ai du adapter un peu les fonctions de montée et descente pour diminuer la puissance du moteur car il tournait beaucoup trop vite (12v). Je ne laisse passer la tension que par intermittence . A adapter selon la puissance de votre moteur et poids de votre trappe.
    Par ailleurs n'ayant pas un frein moteur , j'ai appliqué le principe de contre poids avec des poulies. La ficelle fait un tour autour de la poulie du moteur pour permettre l'accroche.

    Code :

    void forward() //alimente par accoups : donne du courant 7 millisecondes puis patiente 18 ms, sinon trop puissant
    {
    for (int i = 0; i <= TpsMontee; i++) {
    digitalWrite(ALimentationCMD, HIGH);
    digitalWrite(dirPin, HIGH); //sortie de veille : donner la direction
    digitalWrite(stepPin, LOW);
    digitalWrite(led, HIGH);
    delay(7);

    digitalWrite(led, LOW);
    digitalWrite(dirPin, LOW); //Arret global avant coupure sinon pb chgmt de direction ensuite
    digitalWrite(stepPin, LOW);
    digitalWrite(ALimentationCMD, LOW);
    delay(18);
    }
    }
    void backward() //alimente par accoups : donne du courant 7 millisecondes puis patiente 18 ms, sinon trop puissant
    {
    for (int i = 0; i <=TpsDescente; i++) {
    digitalWrite(ALimentationCMD, HIGH);
    digitalWrite(dirPin, LOW); //sortie de veille : donner la direction
    digitalWrite(stepPin, HIGH);
    digitalWrite(led, HIGH);
    delay(7);

    digitalWrite(dirPin, LOW); //Arret global avant coupure sinon pb chgmt de direction ensuite
    digitalWrite(stepPin, LOW);
    digitalWrite(ALimentationCMD, LOW);
    digitalWrite(led, LOW);
    delay(18);
    }
    //remontée de qques millimetres pour eviter frottement trappe
    for (int i = 0; i <= 1; i++) {
    digitalWrite(ALimentationCMD, HIGH);
    digitalWrite(dirPin, HIGH); //sortie de veille : donner la direction
    digitalWrite(stepPin, LOW);
    digitalWrite(led, HIGH);
    delay(7);

    digitalWrite(led, LOW);
    digitalWrite(dirPin, LOW); //Arret global avant coupure sinon pb chgmt de direction ensuite
    digitalWrite(stepPin, LOW);
    digitalWrite(ALimentationCMD, LOW);
    delay(18);
    }

    RépondreSupprimer