samedi 17 janvier 2015

Porte de poulailler Part - 4 : MSP430G2452 RTC alarme

Version MSP430G2452 avec DS3231

Retour partie 3
Avant de commencer cet article, juste un petit mot pour vous dire que c'est la configuration que j'ai choisie et qui tourne depuis des mois. Avant d'aller plus loin  voici une photo du montage dans sa boite.



DS3231


Nous allons améliorer notre montage précédent en changeant de RTC, le composant DS3231 dispose de 2 alarmes et d'une sortie (pin3) INT/SQW. L'adressage du bus i2C est le même que pour le DS1307 (0x68). Avec l'arduino on peut utiliser la même librairie pour jouer avec. Utile pour mettre à l'heure l'horloge par exemple.


Pourquoi utiliser le MSP430G2452 ?

La nouvelle génération d'arduino devient intéressante avec la version 3.3 volts et la possibilité de mettre en veille le port série. Mais j'ai consulté des articles, des forums au sujet de la consommation d'énergie et dans ce domaine les MSP430 s'en sortent plutôt pas mal annonçant des modes veilles de quelques nano-ampères et en plus un réveil rapide. Sur le marché Texas instrument propose des LaunchPad pour débuter, par défaut dans la boite vous trouverez 2 micros-contrôleurs :
  • MSP430G2553
  • MSP430G2452
J'ai retenu le second car il y avait plus d'infos sur les forums et je trouve qu'il est plus facile à prendre en main. on va pouvoir utiliser son 'Timer' interne et les interruptions pour endormir et réveiller notre processeur. 

Remarques: Attention à la manipulation du processeur, les pattes sont fragiles au moment de la mise en place et du retrait. Aidez vous d'une pince à épiler à passer dessous le boitier pour le lever uniformément.


Les changements induits ?

L'alimentation du MSP430G2452 est de 3.3v, par conséquent le bus I2C doit fonctionner en 3.3v, heureusement pour nous, le DS3231 s'en sort bien il fonctionne correctement à cette tension nous ne serons pas obligé de mettre des pull-up. Idem pour la commande moteur si vous utilisez le 'DRV8885 de chez pololu' aucun changement à faire. Plutôt des bonnes nouvelles de ce côté là.

Du côté de la programmation, il existe le logiciel Energia qui est le clône d'arduino avec ces sketchs. Mais là il y a un soucis quand on s’intéresse comme moi à la consommation, on se rend compte qu'avec Energia on ne peut pas utiliser le mode veille comme on le voudrait, peut-être que cela a été changé depuis.... Alors j'ai décidé de télécharger le logiciel proposé par TI sous licence mais gratuit à condition d'avoir des projets de petites tailles ce qui est mon cas. Après avoir rempli un formulaire assez précis de l'utilisation que l'on veut en faire, on arrive enfin à télécharger "Code-composer studio 5-5'.

Les premiers pas !

Il existe des tutos divers et très bien fait sur le sujet donc je ne m'attarderai pas sur la configuration du logiciel et de la compilation du code du MSP430.



Le schéma :
Remarques : l'alimentation du DS3231 est permanente, le micro-contrôleur pourrait commander l'alim, via un 4N55 et il faudrait brancher une résistance de tirage (pull-up) sur l'alarme pour fonctionner sans alimentation extérieure ce qui permettrait de gagner encore...

Le montage du prototype :


Partie code :

J'ai transposé le code arduino, on retrouve
les temps de descente et de montée


 
static const unsigned long Timeropen=15;  
static const unsigned long Timerclose=12;  

 le tableau de réglage mois par mois.


 

 ////////////////////////////////////// JANV  FEV  Mars  Avr  Mai  Jun  
 unsigned char LeveSoleilHeure[12]=  { 7, 6, 6, 6, 5, 5, 4, 4, 4, 4, 4, 4  };  
 unsigned char LeveSoleilMinute[12]= { 0,50, 40,20, 40, 0, 30, 5, 5, 0, 0, 0  };  
 unsigned char CoucheSoleilHeure[12]= {18,18, 18,19, 19,20, 20,20, 20,20, 21,21  };  
 unsigned char CoucheSoleilMinute[12]={10,15, 30,00, 45,35, 0,25, 45,55, 15,35  }; 


Le principe est presque identique à la version Arduino.
Au démarrage la porte du poulailler doit être fermée. Lors de la mise sous tension, la porte s'ouvre (cela permet de vérifier que tout est correctement branché), puis une fois que l'alarme est programmée je mets en veille le MSP430G2452 il est réveillé par interruption sur la pin 1.4 lorsque l'alarme se déclenche. Le micro-contrôleur procède alors à l'ouverture ou fermeture selon l'heure et programme la prochaine alarme sur le DS3231 et se rendort gentiment...

La fonction arduino "delay()" est remplacée par l'utilisation du TimerA avec interruption.

J'ai utilisé et adapté la librairie I2C développé par 
* Kerr D. Wong  
  * http://www.kerrywong.com  

extrait du fichier principal "main.c"

 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
 // Commande d'un moteur pour l'ouverture et la fermeture d'un poulailler  
 // ELECTRONIC77  
 // 26/03/2014  
 // Matériel :  
 // - MSP430G2452  
 // - RTC DS3232 I2C  
 // - Carte de commande bidirectionnelle d'un moteur CC  
 // - 4N35 optocoupleur  
 // - Transistor de puissance BD233  
 // - Résistance 220 Ohms  
 // - 1 Moteur CC  
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
 /* Branchement  
  * 2 alimentation VCC 3.3 et VCC2 5V  
  *  
  *      Clock   MSP430G2452  
  *      SQW               P1.4  
  *      SQL      P1.6  
  *      SDA      P1.7  
  *                 VCC      3.3V  
  *                 GND      GND  
  *  
  *                4N35        MSP430G2452  
  *                1   220 Ohms  P1.0  
  *                2   GND  
  *                3  
  *                4   BD233 patte 3  
  *                5   VCC2  
  *                6  
  *  
  *                BD233  
  *                1 Carte de commande VCC  
  *                2 VCC2  
  *                3 4N35 4  
  *  
  *                Carte de commande  
  *                VCC BD233 1  
  *                GND GND2  
  *                IB1 MSP430G2452 P1.1  
  *                IA1 MSP430G2452 P1.2  
  *  
  *               MSP430G2452  
  *               RST résistance de 47kOmhs Vcc + condo de 100nano vers GND  
  *  
  */  
 #include <msp430.h>  
 #include "DS3231.h"  
 ///////////////////////////////////////////////////  
 //Header  
 unsigned char getIndex(Date t);  
 void initCommandeMoteur();  
 void ouvre();  
 void fermeture();  
 void SetAlarm();  
 void stop();  
 //////////////////////////////////////////////////  
 static const unsigned long Timeropen=15;  
 static const unsigned long Timerclose=12;  
 //////////////////////////////////////////////////  
 //Heure Hiver  
 //http://ecole.onsevoitdemainalors.org/IMG/pdf/41_Tableau_02.pdf  
 //matin retire 1h45  
 // soir ajoute 1h30  
 ////////////////////////////////////// JANV  FEV  Mars  Avr  Mai  Jun  
 unsigned char LeveSoleilHeure[12]=  { 7, 6, 6, 6, 5, 5, 4, 4, 4, 4, 4, 4  };  
 unsigned char LeveSoleilMinute[12]= { 0,50, 40,20, 40, 0, 30, 5, 5, 0, 0, 0  };  
 unsigned char CoucheSoleilHeure[12]= {18,18, 18,19, 19,20, 20,20, 20,20, 21,21  };  
 unsigned char CoucheSoleilMinute[12]={10,15, 30,00, 45,35, 0,25, 45,55, 15,35  };  
 /**  
  * Kerr D. Wong  
  * http://www.kerrywong.com  
  *  
  * D3231 RTC Example Using MSP430G2452  
  * sans pull up p1.6=SCL p1.7=sda  
  */  
 volatile unsigned long iCompteur=0;  
 unsigned char isAlarmSet;  
 Date dt;  
 Date dtAlarm;  
 Date dtSetAlarm;  
 /**  
  * Stop the Watch Dog Timer  
  */  
 void inline stopWatchDogTimer() {  
   WDTCTL = WDTPW + WDTHOLD;  
 }  
 void init()  
 {  
      stopWatchDogTimer();  
 /*  
      P1DIR = 0xFF;               // All P1.x outputs  
      P1OUT = 0;                // All P1.x reset  
      P2DIR = 0xFF;               // All P2.x outputs  
      P2OUT = 0;  
 */  
 }  
 void StartInterruptClock(){  
       P1DIR = 0x01;               // P1.0 output, else input  
       P1OUT = 0x10;              // P1.4 set, else reset  
       P1REN |= 0x10;              // P1.4 pullup  
       P1IE |= 0x10;               // P1.4 interrupt enabled  
       P1IES |= 0x10;              // P1.4  High to Low Edge  
       P1IFG &= ~0x10;              // P1.4 IFG cleared  
       _BIS_SR(LPM4_bits+GIE); // Enter low power mode 4 le plus bas)  
 }  
 // Port 1 interrupt service routine  
 #pragma vector=PORT1_VECTOR  
 __interrupt void Port_1(void){  
      turnOffAlarm(0x01);  
      //P1OUT ^=0x01; // P1.0 =toggle ^ XOR // POUR DEBUG  
      P1IFG &=~0x10; //P1.4 IFG Cleared (flag)  
      //Ouvre ou ferme la porte du poulailler  
      initCommandeMoteur();  
      //Tempo  
      unsigned char i=0;  
      for (i=0;i<255;i++);  
      if (dtAlarm.hour<12)  
      {  
           //lever  
           ouvre();  
      }  
      else  
      {  
           //Coucher  
           fermeture();  
      }  
      stop();  
      //Programmer la prochaine alarme  
      SetAlarm();  
 }  
 void SetAlarm(){  
      setupRTC3232();  
      ///////////////////////////////////  
      turnOffAlarm(0x01);  
      checkIfAlarm(0x01);  
      //////////////////////////////////  
      //setDate3232(); // ToDO A virer c'est pour le reglage de l'heure  
      //Lecture de l'heure  
      dt=getDateDS3232();  
      /*unsigned char c=dt.second;  
                c=dt.minute;  
                c=dt.hour ;  
                c=dt.dayOfWeek;  
                c=dt.dayOfMonth;  
                c=dt.month ;  
                c=dt.year ;*/  
      //recherche l'index de la date d'alarme dans la matrice  
      unsigned char index=getIndex(dt);  
      signed char Lever=CompareTimeHM(dt,LeveSoleilHeure[index],LeveSoleilMinute[index]);  
      signed char Coucher=CompareTimeHM(dt,CoucheSoleilHeure[index],CoucheSoleilMinute[index]);  
      if (Lever==0){  
           // Si l'heure correspond à l'alarme du lever alors on programme l'alarme du couche  
           dtAlarm.dayOfWeek=dt.dayOfWeek;  
           dtAlarm.minute=CoucheSoleilMinute[index];  
           dtAlarm.hour=CoucheSoleilHeure[index];  
           dtAlarm.second=0;  
      }  
      else if(Coucher==0 || Coucher>0){  
           // Si l'heure correspond à l'alarme du coucher alors on programme l'alarme du lever le lendemain  
           // Si L'heure est superieur alarme du coucher on programme l'alarme du lever  
           dtAlarm.dayOfWeek=dt.dayOfWeek+1;  
           if (dtAlarm.dayOfWeek>6) dtAlarm.dayOfWeek=0;  
           dtAlarm.minute=LeveSoleilMinute[index];  
           dtAlarm.hour=LeveSoleilHeure[index];  
           dtAlarm.second=0;  
      }  
      else if (Lever>0 && Coucher<0)  
      {  
           //On entre le lever et le coucher (par exemple 7h00 vers 19h00) On programme le coucher  
           dtAlarm.dayOfWeek=dt.dayOfWeek;  
           dtAlarm.minute=CoucheSoleilMinute[index];  
           dtAlarm.hour=CoucheSoleilHeure[index];  
           dtAlarm.second=0;  
      }  
      else if (Lever<0){  
           //L'heure est plus petite que le lever on programme le lever  
           dtAlarm.dayOfWeek=dt.dayOfWeek;  
           dtAlarm.minute=LeveSoleilMinute[index];  
           dtAlarm.hour=LeveSoleilHeure[index];  
           dtAlarm.second=0;  
      }  
      //RAPPEL ERRROR ATTENTION DIFFERENT unsigned char A1MH=0x52;//t.minute;  
      turnOnAlarm(1);  
      isAlarmSet=checkAlarmEnabled(1);  
      setA1Time(dtAlarm.dayOfWeek,dtAlarm.hour,dtAlarm.minute,dtAlarm.second,0x08,0x01,0x00,0x00); //une fois par seconde  
      dtSetAlarm=getA1Time();  
      //repete la transmission de l'alarme en cas de pbl  
      unsigned char i=0;  
      for (i=0;i<5;i++)  
      {  
           if (CompareTimeHM(dtSetAlarm,dtAlarm.hour,dtAlarm.minute)!=0)  
           {  
                setA1Time(dtAlarm.dayOfWeek,dtAlarm.hour,dtAlarm.minute,dtAlarm.second,0x08,0x01,0x00,0x00); //une fois par seconde  
                dtSetAlarm=getA1Time();  
           }  
      }  
 }  
 void main(void) {  
      init();  
      // La premiere fois ouvre  
      // refermer à la main si nécessaire  
      initCommandeMoteur();  
      ouvre();  
      stop();  
      SetAlarm();  
      StartInterruptClock(); // Enter low power mode 4 le plus bas)  
 }  
 ////////////////////////////////////////////////////:  
 ///////////////DELAY  /////////////////////////////:  
 ////////////////////////////////////////////////////:  
 void StartInterruptTimerA(){  
        CCTL0 = CCIE;               // CCR0 interrupt enabled  
       CCR0 = 50000;                                    // 50000 SMCLK cycles  
       TACTL = TASSEL_2 + MC_2;         // SMCLK, contmode  
       //_BIS_SR(LPM0_bits + GIE);         // Enter LPM0 w/ interrupt  
       _BIS_SR( GIE);         // Enter LPM0 w/ interrupt  
 }  
 // Timer A0 interrupt service routine  
 #pragma vector=TIMER0_A0_VECTOR  
 __interrupt void Timer_A (void)  
 {  
  //P1OUT ^= 0x01;              // Toggle P1.0  
  //CCR0 += 50000;              // Add Offset to CCR0  
      //Arrete le timer et on averti que c'est bon  
      TACTL = TASSEL_2 + MC_0; // Stop Le timer  
      CCTL0 &= ~ CCIE; // disable interrupt  
 }  
 void delay(unsigned long aDelay){  
      iCompteur=0;  
      for (iCompteur=0;iCompteur<aDelay;iCompteur++){  
           StartInterruptTimerA();  
           while(CCTL0 & CCIE)//FlagTimerA==0)  
           {  
                //P1OUT ^=0x01; // P1.0 =toggle ^ XOR // POUR DEBUG  
           }  
      }  
 }  
 ///////////////////////////////////////////////////////////////////////  
 //////: GESTION POULAILLER  
 ///////////////////////////////////////////////////////////////////////  
 unsigned char getIndex(Date t){  
  unsigned char Index=t.month;  
   if (t.month>6)  
        {  
        // on redescend dans le tableau Juillet -> decembre  
        Index=13-t.month;  
        Index=(Index-1)*2;  
          if( t.dayOfMonth<15)  
               Index++;  
        }  
   else  
   {  
        // Janvier vers Juin  
        Index=(Index-1)*2;  
        if( t.dayOfMonth>15)  
             Index++;  
   }  
   return Index;  
 }  
 void initCommandeMoteur()  
 {  
      // ALIMENTATION  
      P1DIR |= 0x01;          //P1.0 output (alimentation Carte moteur)  
      // Commande Moteur 1  
      P1DIR |= 0x02;          //P1.1 output  
      // Commande Moteur 2  
      P1DIR |= 0x04;           //P1.2 Output  
      // Active l'alimentation  
      P1OUT |= 0x01;           //set High Alimentation du moteur  
 }  
 void ouvre(){  
      P1OUT |= 0x02;  
      P1OUT &=~0x04;  
      delay(Timeropen);  
 }  
 void fermeture(){  
      P1OUT |= 0x04;  
      P1OUT &=~0x02;  
      delay(Timerclose);  
 }  
 void stop(){  
      //Arrete tout  
      P1OUT &=~0x04;  
      P1OUT &=~0x02;  
      P1OUT &=~0x01; // coupe l'alimentation du DS3231  
 }  

Lien vers le code source complet : Commande de portillon avec MSP430 G 2452


Cette version est plus économe en énergie, environ 3mA. Si on regarde en détail on perd  1mA par le régulateur, si on dispose d'un accu 3v alors on peut le supprimer. Autre piste  la carte DS3231 qui consomme 1mA avec une petite led sur la carte qui reste allumée que l'on pourrait supprimer. Pour ma part je me contente de cette version suffisamment optimisée.
Il ne reste plus qu'à l'alimenter avec un petit kit solaire (l'autonomie est alors largement suffisante > 1 mois ). 

Je vais écrire un nouvel article sur la détection crépusculaire, j'ai trouvé une led crépusculaire intéressante proche de la sensibilité de l'oeil humain et des poules.




Part 5

4 commentaires:

  1. Bonjour,
    C'est sympa de partager votre expérience.
    On a eu la même idée.
    Votre système mécanique est astucieux. J'avais opté pour le même principe, mais avec des contacts fin de course.
    Ayant eu pas mal de problème et pour éviter qu'un intrus réussisse à forcer la porte, j'ai opté pour une deuxième poulie et ficelle toujours tendue.
    Ça fonctionne bien aussi. http://moncastel.free.fr/forum/trappePoule/

    L'arduino Uno consomme 7 mA en veille, mais le pro mini peut descendre beaucoup plus bas http://forum.arduino.cc/index.php?topic=290236.0

    Je vais quand même faire l'essai d'alimenter le UNo en basse tension sans passer par le régulateur pour voir demain ?

    RépondreSupprimer
  2. Le DS3231 me plait bien, j'en ai commandé un. On devrais pouvoir commuter directement l'alimentation de l'arduino, comme ça il ne consommerait plus rien en période de veille ;)

    RépondreSupprimer
  3. Désolé pour la réponse tardive,
    votre remarque est judicieuse, un intrus avec son museau pourra facilement soulever un peu la porte pour peu que cette dernière ne soit pas descendue complétement en buté.
    Concernant l'alimentation, il me semble que la broche Vin accepte jusqu' a 12 V.
    Bon bricolage !

    RépondreSupprimer
  4. bravo, j'avais suivi votre projet arduino et comme je me questionne sur le TI msp340, fortuitement je vous ai retrouvé. Je m'amuse avec un timer de ce genre, prenant en compte aussi les calculs de lever et coucher du soleil et de la lune. J'en suis encore à Arduino, mais tout a basculé il y a quelques semainesavec la découverte de l'horloge RV1805-C3
    https://cdn.sparkfun.com/assets/1/8/9/e/2/RV-1805-C3.pdf

    qui peut déconnecter le msp430 ou Arduino( éteint au lieu d'en veille), fonctionne avec un supercondensateur, et qui ne consomme que 22 NANOAMPERES. Bonus, 512 k de RAM . Par conséquent je revois tout mon programme. Bravo et je vais me mettre au msp340.

    RépondreSupprimer