]> Mon petit coin de oueb
Au menu
Aller voir ailleurs si j'y suis
Robotique
Environnement
Systèmes embarqués
SE 2008
Technique
Communauté
Amis
Famille
Bandes Dessinées
Geeks
On finira tous à vélo ...

Une petite station météo

Ce n'est pas réellement une station météo vu qu'elle ne mesure (pour l'instant) que le vent et le soleil.

Son but est de mesurer le "gisement" de vent et de soleil disponible chez moi. Le tout, bien sûr, pour mesurer le potentiel d'énergie renouvelable locale disponible.

Le circuit est conçu autour du microcontrôleur Atmega16 et programmé en utilisant aversive de chez Droids-corp.

L'anémomètre a été commandé chez Inspeed aux États Unis, et le panneau solaire chez farnell. Le circuit est conçu pour accueillir un capteur de température sur i2c de type ds1631 et a 6 bits d'entrées numériques pour brancher une éventuelle girouette.

Tout le circuit

Le circuit est construit autour d'un atmega16, l'archive du schéma et du routage complet au format kicad se trouve ici. Toutes les connexions vers les différents capteurs (anémomètre, panneau solaire, ...) se font au moyen d'un seul connecteur DB15 (parce que j'en avais plein ;-). Il est possible de brancher des composants sur I2C comme ceux de chez Dallas (DS1621, DS1631,...) qui sont très simple à utiliser et permettent de mesurer la température ou l'humidité. Les capteurs de vent et de soleil sont décris plus bas. Pour pouvoir communiquer avec un ordinateur le port RS232 (J1) est utilisé avec une adaptation des niveaux de tension via un MAX232.

L'alimentation électrique est fournie via un régulateur de tension de 5V (le célèbre 7805) et un pont à diodes anti-boulet (qui permet de brancher l'alimentation dans n'importe quel sens).

Un connecteur HE10-10 permet quand à lui de programmer le microcontrôleur via le protocole ISP de Atmel. Le programmeur utilisé est l'USBprog, qui permet de programmer en utilisant que des logiciels libre (linux/avarice) ce qui est bien pratique.



Une photo du circuit.

La mesure du vent

Le schéma pour la mesure du vent est relativement simple. En effet l'anémomètre se présente sous la forme d'un interrupteur dont la vitesse de commutation dépend du vent. Il suffit donc de le brancher sur une entrée d'interruption (INT0) de l'Atmega, avec une résistance de tirage, pour compter les coups.


Schéma de la mesure de vent
Les broches vent1 et vent2 sont les broches de l'anémomètre et la broche vent se branche sur l'entrée d'interruption de l'atmega.

La mesure du soleil

Pour mesurer la puissance solaire on mesure le courant de court circuit ainsi que la tension de circuit ouvert du panneau photovoltaïque. On utilise pour cela un transistor MOS qui permet de commuter le panneau solaire en court circuit ou en circuit ouvert.

On mesure la tension Vv en le branchant sur un convertisseur analogique numérique de l'Atmega à travers un filtre anti-repliement. Et on mesure le courant qui traverse la résistance R1 au moyen d'un amplificateur différentiel que l'on branche ensuite sur un deuxième convertisseur analogique numérique de l'atmega.


Schéma de la mesure du soleil
Schéma de la partie de mesure du panneau solaire.

Le programme

Le code s'organise autour d'une machine d'états à 6 états :


Machine d'états représentant le fonctionnement du firmware de la carte
Machine d'états représentant le fonctionnement du firmware de la carte

Pour compter le vent il suffit d'incrémenter le compteur nbr_tours à chaque interruption INT0 par l'appel de la fonction ISR(INT1_vect){ nbr_tours++; } Et de remettre à zéro le compteur à chaque transmission des données.

Le code complet:

/*activation des ports. LED : ready !*/

//cbi (port, bit) = clear port
//sbi (port, bit) = set port
#include <aversive.h>
#include <uart.h>
#include <scheduler.h>
#include <time.h>
#include <adc.h>
#include <aversive/wait.h>

#define VENT1 2 
#define VENT2 3
#define LED_BLEUE (1<<4)
#define LED_JAUNE (1<<5)

enum etat_{
  INIT,
  COMMUTATION_COURANT,
  MESURE_COURANT,
  COMMUTATION_TENSION,
  MESURE_TENSION,
  TRANSMISSION
};

struct pv_{
  /* Mesure de tension et nombre de mesures */
  uint32_t tension;
  uint16_t tension_cnt;

  /* Mesure de courant et nombre de mesures */
  uint32_t courant;
  uint16_t courant_cnt;
};

volatile static uint16_t nbre_tours;
volatile static uint16_t demi_secondes;

/* Incrementation sur front montant de INT1 */
ISR(INT1_vect){
  nbre_tours++;
}

void temps(void * stuff){
  demi_secondes++;
}

int main(void)
{
  enum etat_ etat;
  struct pv_ soleil;
  char unemesure;

  /******************************************************************/
  /* initialisations                                                */
  /******************************************************************/
  unemesure = 0;
  soleil.tension=0;
  soleil.tension_cnt=0;
  soleil.courant=0;
  soleil.courant_cnt=0;


  uart_init();
  scheduler_init();
  adc_init();
  time_init(200);
  sei();
  PORTB |= (1<<3);

  /* pour les interruptions externes sur INT1 */
  MCUCR |=0x0f; // front montant
  GICR  = 0x80; // INT1 enable

  // truc pour faire marcher printf 
  fdevopen((int(*)(char))uart0_send,(int(*)(void))uart0_recv,0);

  /* incrementation du temps toutes les 0,5 secondes 244*2048us */
  scheduler_add_periodical_event(temps,NULL,244);

  DDRD = LED_BLEUE | LED_JAUNE;   //pind 4 et 5 en sortie (led)
  DDRB |= 1<<3;         //portb 3 en sortie (PWM)

  nbre_tours=0;
  PORTD = LED_BLEUE | LED_JAUNE;   // extinction des leds
  adc_get_value(ADC_REF_AVCC | MUX_ADC0); // la première valeur est toujours fausse
  etat = INIT;


  printf("StationMeteo a bootee\n");

  /******************************************************************/
  /*Debut de la boucle infinie                                      */
  /******************************************************************/
  while(1){
    /* gestion des actions */
    switch(etat){
      case INIT:  nbre_tours = 0;
                  soleil.courant = 0;
                  soleil.courant_cnt = 0;
                  soleil.tension = 0;
                  soleil.tension_cnt = 0;
                  unemesure = 0;
                  demi_secondes = 0;
                  break;

      case COMMUTATION_COURANT : PORTB |= (1<<3);
                                 if(!unemesure)
                                   adc_get_value(ADC_REF_AVCC | MUX_ADC0);
                                 unemesure++;
                                 break;
      case MESURE_COURANT : soleil.courant += adc_get_value(ADC_REF_AVCC | MUX_ADC0);
                            wait_ms(100);
                            soleil.courant_cnt++;
                            break;
      case COMMUTATION_TENSION : PORTB &= ~(1<<3);
                                 if(!unemesure)
                                   adc_get_value(ADC_REF_AVCC | MUX_ADC1);
                                 unemesure++;
                                 break;
      case MESURE_TENSION : soleil.tension += adc_get_value(ADC_REF_AVCC | MUX_ADC1);
                            soleil.tension_cnt++;
                            wait_ms(100);
                            break;
      case TRANSMISSION :printf("%d %ld %d %ld %d\n",soleil.courant_cnt
                                ,soleil.courant
                                ,soleil.tension_cnt
                                ,soleil.tension
                                ,nbre_tours);
                         break;
    }

    /* gestion des transition */
    switch(etat){
      case INIT : etat = COMMUTATION_COURANT;
                  break;
      case COMMUTATION_COURANT :if(demi_secondes > 1){
                                  etat = MESURE_COURANT;
                                  demi_secondes = 0;
                                }
                                break;
      case MESURE_COURANT :if(demi_secondes >=28){
                             etat = COMMUTATION_TENSION;
                             demi_secondes = 0;
                             unemesure =0;
                           }
                           break;
      case COMMUTATION_TENSION :if(demi_secondes >1){
                                  etat = MESURE_TENSION;
                                  demi_secondes = 0;
                                }
                                break;
      case MESURE_TENSION :if(demi_secondes >=28){
                             etat = TRANSMISSION;
                             unemesure =0;
                           }
                           break;
      case TRANSMISSION : etat = INIT;
                          break;
      default : etat = INIT;
    }

  }
  return 0;
}



L'anémomètre et le panneau solaire perchés au dessus de l'antenne à laver le cerveau.