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/RC-LedLights - Jeu de LEDs pour modèle réduit (Arduino)

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

Introduction

Ce projet permet de commander 4 LEDs depuis un signal PPM d'une télécommande de modélisme. 4 fonctions sont disponibles:

  • GLOW (pin 3) - la LED s'allume et s'éteint progessivement
  • FLASH (pin 4) - la LED flashe
  • SWITCH1 (pin 5) et SWITCH2 (pin 6) - voies commutées ON/OFF

Les réglages intéressants:

  • FLASH_PPM, GLOW_PPM, SWITCH1_PPM, SWITCH2_PPM - longueur de la pulse à laquelle la fonction doit être activée
  • FLASH_DELAY, FLASH_LENGTH - délais ON et OFF du flash (réglé pour une LED, plus rapide que de l'incandescent)
  • GLOW_DELAY - vitesse de changement du GLOW
  • GLOW_MIN, GLOW_MAX - respectivement min. 0 et max. 255. Des valeurs au-delà permettent d'établir des délais.

Les Arduinos sont limités à 40mA par pin. Pour commander des courants plus élevés, utiliser des MOSFET (comme décrit dans le schéma). Un MOSFET IRL3705-N supporte un courant de 89A @ 10V (25 °C), et une tension de max. 55V. Il s'agit en plus d'un "Logic Gate", c'est à dire que ce MOSFET se comporte plus comme un interrupteur qu'un transistor. A 50W (~4A @ 12V), le MOSFET du GLOW chauffe à ~50 °C, sans radiateur.

Interférences

A une autre époque, on utilisait des transistors de puissance pour générer une tension variable, avec pour conséquence un fort échauffement. En électronique logique, on utilise des pulses à la tension nominale, qu'on appelle PWM (Pulse Width Modulation - modulation de la largeur d'impulsion). L'échauffement est moindre, le rendement meilleur, la logique plus simple, mais pose aussi d'autres contraintes.

Le PWM crée des interférences. L'électricité se comporte comme de l'eau. Si vous fermez brusquement un robinet sous pression, ça engendre un choc en retour. Cela se traduit par des sursauts de tension, pouvant aller jusqu'à plusieurs dizaines de volts, largement au-delà de la tolérance des composants électroniques.

Ce circuit ne contient pas de séparation galvanique. Veillez-vous de bien contrôler la réception de votre modèle avec les LEDs allumées, avant de l'envoyer en l'air.

Sur le principe (comme pour un contrôleur de moteur):

  • plus on tire de courant, plus les interférences seront élevées
  • la longueur des câbles entre l'accu et le MOSFET amplifie les interférences
  • le(s) condensateur(s) a pour fonction de lisser les interférences

Mesures effectuées:

  • 50W - sans condensateur: interférences de 2V à la source, avec parfois des pics allant jusqu'à 40V !
  • 50W - avec un condensateur de 680uF: interférences de 1V à la source, stable

Conclusions:

  • ne jamais utiliser un circuit utilisant du PWM sans placer un condensateur en parallèle à l'accu
  • un condensateur de 1uF / 50V semble adapté au besoin (2x 470uF / 50V en parallèle est encore mieux - meilleur refroidissement)
  • la tension du condensateur doit correspondre aux pics, et non pas juste à la tension de l'accu. 50V pour un accu de 12V, 100V pour un accu de 24V.
  • éviter d'utiliser l'accu qui alimente le récepteur, afin de limiter les interférences sur ce dernier

Schéma

Jeu de LEDs pour modèle réduit (Arduino).jpg Composants:
  • Arduino (Atmega 328p)
  • MOSFET IRL3705-N - n'importe quel transistor NPN ou MOSFET-N ferait l'affaire. L'IRL3705-N supporte de forts courants, est bon marché (~ 1€ / eBay), et du type "Logic Gate". Cela signifie que le MOSFET est soit ON ou OFF, mais pas dans un état intermédiaire.
  • résistance 10k par MOSFET
  • condensateur(s) 2x 470uF en parallèle ou 1x 1000 uF (testé à ~5A @ 12V)

Code

/*
Date:    2014-06-13
Author:  Jean-Christophe Heger <jcheger*at*ordinoscope*net>
License: GPLv3 (http://www.gnu.org/copyleft/gpl.html)

Project desciption
------------------
Play with LEDs for RC (PPM). This project handles one PPM input, and triggers different types
of LED (or switch) behaviors. Each one can be triggered at a givven level of PPM length.

Warning
-------
This circuit use some PWM, which creates interferences with RC receivers. Take care of:
- never run such circuit without a capacitor in parallel of the battery (1uF or 2x 470uF / 50V is a good choice)
- use a separate battery is better
- do never run a model without a serious testing

Requirements
------------
- download the Timer1 library (https://code.google.com/p/arduino-timerone/)
- unzip it and copy the folder in sketchbook/libraries
- restart the Arduino IDE

Pins
----
2  - PPM input
3  - LED glow output
4  - LED flash output
5  - LED switch output (1)
6  - LED switch output (2)
13 - onboard LED PPM monitor

Limitations
-----------
- the maximum current per pin is 40mA - use a MOSFET for upper current
- the PPM reading has quite a high jitter of about 50us

PWM considerations
------------------
Pins 3 and 11 use the timer2.
Pins 5 and 6 use the timer0, conflicting with delay(), millis() and micros()
Pins 9 and 10 use the timer1, conflicting with timerIsr()

Electronic components
---------------------
- Arduino (Atmega 328p)
- MOSFETS IRL3705-N - actually any NPN transistor of MOSFET-N is allright. The IRL3705-N handles high currents,
  is cheap (~ 1€ / eBay) and is a "Logic Gate". This means the MOSFET is either ON or OFF, and not in a
  intermediate state. 
- 10k pulldown resistor per MOSFET
- 2x 470 uF or 1x 1000 uF capacitor (good for ~5A @ 12V) 

*/

#include <TimerOne.h>

// Pins settings
#define PPMIN_PIN    2     // must be an interrupt pin - do not change !
#define GLOW_PIN     3     // must be a timer2 PWM pin - do not change !
#define FLASH_PIN    4     // any digital pin
#define SWITCH1_PIN  5     // any digital pin
#define SWITCH2_PIN  6     // any digital pin
#define RXLED_PIN   13     // monitors the PPM readings (onboard LED)

// PPM settings
#define DEADBAND       50  // around 50us
#define FLASH_PPM    1900  // PPM length (1000-2000 us)
#define GLOW_PPM     1900  // PPM length (1000-2000 us)
#define SWITCH1_PPM  1400  // PPM length (1000-2000 us)
#define SWITCH2_PPM  1900  // PPM length (1000-2000 us)

// Delays and lengths
#define FLASH_DELAY  2000   // flash delay in us (OFF)
#define FLASH_LENGTH  100   // flash length in us (ON)

#define GLOW_DELAY      5   // delay between change in us
#define GLOW_MIN     -100   // minimum PWM is 0 - a value below 0 will delay the glow at 0
#define GLOW_MAX      300   // maximum PWM is 255 - a value above 255 will delay the glow at 255

// Globals
volatile boolean        flash_enable   = 0;
volatile boolean        glow_enable    = 0;
volatile boolean        switch1_enable = 0;
volatile boolean        switch2_enable = 0;
volatile unsigned long  pulse;

void setup () {           
  pinMode (PPMIN_PIN,   INPUT);
  pinMode (FLASH_PIN,   OUTPUT);
  pinMode (GLOW_PIN,    OUTPUT);
  pinMode (SWITCH1_PIN, OUTPUT);
  pinMode (SWITCH2_PIN, OUTPUT);
  pinMode (RXLED_PIN,   OUTPUT);
  attachInterrupt (0, pulseInIsr, CHANGE);    // monitor PWM input
  Timer1.initialize (1000);                   // set timer once per ms
  Timer1.attachInterrupt (timerIsr);
}
 
void loop () {
}

/**
 * Output loop.
 * Only Timer1 is used, based on a 1ms behavior (Timer1.initialize (1000)).
 */
void timerIsr () {
  static unsigned int flash_delay = 0;        // flash OFF increment
  static unsigned int flash_length = 0;       // flash ON increment
  
  static unsigned int glow_delay = 0;         // glow increment before next value
  static boolean      glow_dir   = 0;         // 0 = up, 1 = down
  static signed int   glow_pwm   = 0;         // glow PWM value
  

  // FLASH
  if (flash_enable) {
    if (flash_length > 0) {                   // flash is ON
      if (flash_length >= FLASH_LENGTH) {     // turn flash OFF
        digitalWrite (FLASH_PIN, LOW);
        flash_length = 0;
        flash_delay  = 0;
      } else {
        flash_length++;
      }
    } else {                                  // flash is OFF
      flash_delay++;
      if (flash_delay >= FLASH_DELAY) {       // turn flash ON
        digitalWrite (FLASH_PIN, HIGH);
        flash_length++;
        flash_delay = 0;
      }
    }
  } else {
    flash_delay  = 0;
    flash_length = 0;
    digitalWrite (FLASH_PIN, LOW);
  }
  
  // GLOW
  if (glow_enable) {
    if (glow_delay >= GLOW_DELAY) {           // glow increase/decrease PWM
      glow_delay = 0;
      if (glow_dir) {
        glow_pwm--;
      } else {
        glow_pwm++;
      }
      if (glow_pwm <= GLOW_MIN) glow_dir = 0;
      if (glow_pwm >= GLOW_MAX) glow_dir = 1;
      
      if (glow_pwm < 0) {                     // delay the glow at 0
        analogWrite (GLOW_PIN, 0);
      } else if (glow_pwm > 255) {            // delay the glow at 255
        analogWrite (GLOW_PIN, 255);
      } else {
        analogWrite (GLOW_PIN, glow_pwm);     // normal PWM
      }
    } else {                                  // glow wait (increment)
      glow_delay++;
    }
  } else {
    analogWrite (GLOW_PIN, 0);
    glow_delay = 0;
    glow_pwm   = 0;
  }

  // SWITCH1
  if (switch1_enable) {
    digitalWrite (SWITCH1_PIN, HIGH);
  } else {
    digitalWrite (SWITCH1_PIN, LOW);
  }

  // SWITCH2
  if (switch2_enable) {
    digitalWrite (SWITCH2_PIN, HIGH);
  } else {
    digitalWrite (SWITCH2_PIN, LOW);
  }
}

/**
 * Monitor the incoming PPM and set global variables if change is detected.
 * The pulse measure is not quite precise, and may jitter by about 20us.
 * The DEADBAND value is an anti flicker, but you should be advised to
 * set the trigger values about 100us below the real value.
 */
void pulseInIsr () {
  unsigned long        t = micros ();          // current time in us
  static unsigned long s;                      // previous time in us
  static boolean       rx_led = 0;             // monitors the valid PPM pulses
  pulse = t - s;                               // pulse length is us
  
  if (digitalRead (PPMIN_PIN) == LOW) {
    if (pulse < 500 || pulse > 2500) return;   // error - out of value

    rx_led = !rx_led;
    digitalWrite (RXLED_PIN, rx_led);

    if (pulse >= SWITCH1_PPM) switch1_enable = 1;
    if (pulse >= SWITCH2_PPM) switch2_enable = 1;
    if (pulse >= FLASH_PPM)   flash_enable   = 1;
    if (pulse >= GLOW_PPM)    glow_enable    = 1;
    
    if (pulse <= SWITCH1_PPM - DEADBAND) switch1_enable = 0;
    if (pulse <= SWITCH2_PPM - DEADBAND) switch2_enable = 0;
    if (pulse <= FLASH_PPM   - DEADBAND) flash_enable   = 0;
    if (pulse <= GLOW_PPM    - DEADBAND) glow_enable    = 0;
 } else {
    s = t;
  }
}