Ce site contient essentiellement des notes de travail. Le contenu est en constante évolution, et loin d'être achevé. (+ d'infos)
La plupart des documentations informatiques sont orientées Debian / Ubuntu.

Electronique/Projets/BattLevelRelay1 (Arduino)

De Ordinoscope.net
Sauter à la navigation Sauter à la recherche

Introduction

Je possède une Fiat Stilo, une voiture détestable qui vibre de partout (diesel) et qui m'a amené autant d'ennuis que toutes mes voitures précédentes réunies. Pour couronner le tout, j'ai changé d'autoradio, lequel me descend la batterie en moins de 3 jours. La raison est assez simple. L'autoradio de base était commandé par un signal CAN (OBDII), mais aucun témoin d'enclenchement n'est disponible dans la torche (+12V commuté). Je pourrais tirer un fil depuis un allume-cigare, mais la coupure ne fonctionne pas dans le 100% des cas, et il me faudrait passer des heures à démonter des caches (j'ai d'ailleurs essayé, mais sans succès jusque là).

L'autoradio se sert d'un fil témoin de l'enclenchement du véhicule (+12V). Voici les consommations relevées:

Description Fil témoin Courant Puissance
Autoradio enclenché +12V 0.75A 9W
Autoradio éteint +12V 0.16A 1.9W
Autoradio éteint 0V 0.002A 0.02W
Avec le circuit décrit ci-après (sans mode sleep) +12V / 0V 0.02A 0.25W
Avec le circuit décrit ci-après (avec mode sleep) +12V / 0V 0.006A 0.07W

Conclusion: le circuit décrit ci-après permet de consommer 27x moins de courant que l'extinction manuelle de l'autoradio avec le témoin actif.

Concept

Le principe du circuit est basé sur le concept des commutateurs de batteries de camping-car. Quant le moteur est éteint, la tension nominale est de 12V (batterie). Quand l'alternateur tourne, la tension monte alors à 14V. Le circuit mesure la tension d'entrée, et commute un relais si elle dépasse 13.5V. La mesure varie un peu selon les composants et nécessite une calibration (hardcodée).

Le relais s'enclenche instantanément si la tension dépasse 13.5V (mesure 1x/s en mode veille). Si la tension mesurée pendant une période est inférieure à 13.5V, le relais se déclenche. Cela évite un flip-flap, dû à des appareils qui peuvent être assez gourmands dès le démarrage du moteur, surtout au ralenti.

D'autre part, l'autoradio + un Arduino Pro Mini (utilisé dans le prototype), consomme 0.02A @ 12V lorsque le relais est déclenché, ce qui fait déjà env. 8x moins que l'autoradio seul. Mais on peut faire encore mieux en mettant l'Arduino en veille, ce qui l'amène à 0.006A @ 12V, soit 27x moins en tout.

Le code est une adaptation de l'exemple décrit ici.

N.B. l'économie d'énergie ne fonctionne que sur un circuit épuré. Les convertisseurs USB-sériel inclus sur les Arduinos sont assez gourmands en énergie (~0.7W). Mon choix s'est porté sur un Arduino Pro Mini, sans USB.

Circuit

CarRelay
Schéma fait avec Fritzing
Le prototype une fois monté
Prototype

Liste des composants:

  • Régulateur de tension LM7805
  • Condensateur 470uF / 25V (ce que j'avais sous la main)
  • Diode 1N4048 (anti-retour pour le relais)
  • diviseur de tension
    • Résistance 470k (R1 - VCC-mesure)
    • Résistance 100k (R2 - GND-mesure)
  • Relais 5V/1A (suffisant pour le témoin de l'autoradio)

L'Arduino Pro Mini a une entrée "RAW", qui mène à un régulateur de tension. Malheureusement je ne sais pas de quel régulateur de tension il s'agit, et les spécifications affichent une tension d'entrée max de 12V. Or, l'alternateur fournit du 14V. J'ai donc opté pour un régulateur externe, un LM7805, qui d'ailleurs chauffe un peu lorsque le relais est tiré. J'en conclus qu'il est plus sage de gérer la régulation de tension en-dehors de la solution fournie par l'Arduino Pro Mini.

Code

/*
BattLevelRelay

To say it shortly, this circuit will power on the radio at the moment the motor is running, and will power it
off 10 seconds after the motor did stop.

I'm owning a Fiat Stilo and have a car radio that kills the battery in less than 3 days. This car has no cable
to indicate to the radio is the contact is on or off. Actually, they are 2 extra cables to send this information
though an CAN signal (ODB-II). This circuit is designed to monitor the battery level and turn the radio on or off.

It will monitor the battery level once per second, in sleep mode for power saving. At the moment the voltage goes
over 13.5V (~14V while charging), the relay switches on. When a car warms up, a lot of extra equipment can make
this voltage vary a lot, especially while idleing. If the voltage goes under 13.5V, it will increase an OFF
counter, which will stop the relay after the timeout. Every time the voltage goes over 13.5V, the OFF counter is
reset.
*/

// User settings
float calib =       36.25; // Voltage divisor calibration
#define RELAY_PIN   13     // PIN for relay
#define BATT_PIN    A0     // PIN for voltage divisor
#define TIMEOUT     10     // timeout in seconds
#define TRIGGER     13.5   // Voltage trigger
#define SHORT_DELAY 100    // delay in ms when off is increasing
#define LONG_DELAY  1000   // normal delay
#define DEBUG            // comment out to debug (serial output)

// Internal calculation
int min_off = TIMEOUT * 1000 / SHORT_DELAY;

// Sleep code
#include <avr/sleep.h>
#include <avr/wdt.h>
#ifndef cbi
#  define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#  define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
 
volatile boolean f_wdt=1;
 
ISR(WDT_vect) {
  f_wdt=1;
}
 
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms, 6=1s, 7=2s, 8=4s, 9=8s
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;
  MCUSR &= ~(1<<WDRF);
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  WDTCSR = bb;
  WDTCSR |= _BV(WDIE);
}
 
void system_sleep () {
  cbi (ADCSRA,ADEN);
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  sleep_enable ();
  sleep_mode ();
  sleep_disable ();
  sbi (ADCSRA,ADEN);
}
 
void setup() {
  cbi (SMCR,SE);
  cbi (SMCR,SM0);
  sbi (SMCR,SM1);
  cbi (SMCR,SM2);
  setup_watchdog (6);

  pinMode (BATT_PIN, INPUT);
  digitalWrite(RELAY_PIN, LOW);
  pinMode (RELAY_PIN, OUTPUT);
  
  #ifdef DEBUG
  Serial.begin (115200);
  Serial.println ("begin");
  Serial.print ("LONG_DELAY: ");
  Serial.println (LONG_DELAY);
  Serial.print ("SHORT_DELAY: ");
  Serial.println (SHORT_DELAY);
  Serial.print ("min_off: ");
  Serial.println (min_off);
  #endif
}
 
void loop() {
  if (f_wdt==1) f_wdt=0;   

  static int off = 0;
  float v = (float) analogRead (BATT_PIN) / (float) calib;

  #ifdef DEBUG
  Serial.println (v);
  #endif

  if (v >= (float) TRIGGER) {           // power on instantly
      off = 0;                          // reset off counter every time that the voltage is equal or above TRIGGER
      digitalWrite (RELAY_PIN, HIGH);
      delay (LONG_DELAY);
  } else {
    if (off < min_off) {
      off++;
      #ifdef DEBUG
      Serial.print ("off=");
      Serial.println (off);
      #endif
      delay (SHORT_DELAY);
    } else {
      if (off >= min_off) {             // timeout - shut down
        digitalWrite (RELAY_PIN, LOW);  // Let's not take any risk. Better to state the port every time than killing the battery.
        system_sleep ();                // sleep for 1s - better power saving
      } else {
        delay (LONG_DELAY);
      }
    }
  }
}