Mini-fête foraine avec Arduino - 2

Pour l'exposition des passionnés du train au Manet à Montigny-le-Bretonneux, les 12-13 Octobre 2019, nous avions envie de surprendre nos visiteurs avec une nouveauté mais aussi d'attirer leur attention avec une véritable animation: la fête foraine a fait alors son apparition. Dans cet article, qui est la suite logique de la première partie, nous allons survoler la partie logicielle réalisée par François et faire un retour d'expérience des essais réalisés. Attention, c'est compliqué.

Vue de l'intérieur du module de commande

Description générale du logiciel

Les fonctions à réaliser par le logiciel sont les suivantes:
  • Piloter et animer les bandeaux lumineux,
  • Sonoriser la mini-fête, 
  • Piloter une petite loco avec commande d’accélération, freinage et arrêt d'urgence,
  • Détecter les actions sur les boutons de commande pour le public,
  • Commander/arrêter les moteurs des manèges.

Le logiciel est composé d'un boucle principale qui répète des actions d'animation lumineuse et sonore et attend des actions du public sur les boutons pour déclencher les manèges et de nouveaux sons et lumières.
L'environnement de développement est l'IDE Arduino et le sketch est exécuté sur la carte Arduino MEGA.
Regardons un peu plus en détail la description de chaque fonction.

Piloter et animer les bandeaux lumineux

Les bandeaux lumineux sont à LED et commandés grâce à l'utilisation d'une librairie spéciale : FastLED.
Dans le sketch l'appel à cette librairie se fera simplement en utilisant l'instruction suivante: #include 

Nous avions déjà utilisé cette librairie et après discussion/essais, il y a une meilleure façon de l'utiliser en évitant les longues déclarations des couleurs dans une table d'environ 100 éléments.

La première chose à faire est d'initialiser et d'éteindre les bandeaux et LED dans la partie setup() du sketch. La déclaration du tableau de LED de type CRGB (R=Red, G=Green, B=Blue, les trois couleurs fondamentales) se fait comme suit:

CRGB leds1[NUM_LEDS1];

Les instructions suivantes permettent d'affecter longueur de bandeaux de LED à un tableau de LED que l'on pourra facilement manipuler.

   // FastLED.addLeds(leds, NUM_LEDS);
    // Mât double (40)
    FastLED.addLeds(leds1, NUM_LEDS1);
   // Palais des glaces/mirage + couronne grande roue
    FastLED.addLeds(leds2, NUM_LEDS2);   
    // Grande Roue gauche
    FastLED.addLeds(leds3, NUM_LEDS3); 
    // Grande Roue droite
    FastLED.addLeds(leds4, NUM_LEDS4);
    // Petit bandeau 
    FastLED.addLeds(leds5, NUM_LEDS5);
    // Grand bandeau
    FastLED.addLeds(leds6, NUM_LEDS6);
    // Petit mât
    FastLED.addLeds(leds7, NUM_LEDS7);

L'extinction des bandeaux se fait au moyen d'une boucle avec la couleur noir (BLACK) et de l'instruction show de la librairie FastLED:

    for (uint8_t i=0; i < NUM_LEDS1; i++) {
        leds1[i] = CRGB::Black;
    }
    FastLED.show();

Avant de lancer les animations dans la boucle principale, il est bienvenu de faire un petit affichage test sur chaque bandeau. Les couleurs "Rouge, Vert, Bleu" sont très souvent utilisées comme test. Concrètement, cela donne un tableau avec les valeurs suivantes:

    leds2[0] = CRGB::Red;
    leds2[1] = CRGB::Green;
    leds2[2] = CRGB::Blue;
    leds2[3] = CRGB::White;
    leds2[4] = CRGB::White;

Pour le reste du bandeau, on peut éclairer en blanc ou tout simplement éteindre toutes les autres LED.

Dans la boucle principale (loop) du sketch on fait appel à des scénarios dédiés à l'animation. Le concept est typique de la programmation dite objet. Cela permet de manipuler des objets plus ou moins complexes, en faisant abstraction des détails. On se concentre sur le scénario et cela rend notre programmation bien plus facile.

Un scénario est composé d'une:
- fonction init qui doit tout initialiser,
- fonction action qui est appelée régulièrement et qui retourne le délai en millisecondes (ms) avant qu'on la rappelle,
- une variable cycle qui stocke un état qui permet à la fonction action de savoir ce qu'elle doit faire,

Par exemple pour la led_string1:

// Mas de cocagne de 40 LEDS [0..39] / led1

#define NUM_LEDS11 40
//la fonction init qui éteint toutes les LEDs est la suivante:
void init11 (uint8_t &cycle){
  for (uint8_t i=0;  i < NUM_LEDS11; i++) {
    leds1[i] = CRGB::Black;
  }
  cycle=0;
}
//une fonction action qui met à jour les LED
uint32_t update_led11(uint8_t &cycle) {
switch (cycle) {
  case 0:
            break;
  // Si le cycle est à 1, on allume les LEDs en blanc avec CRGB:White
  case 1:
            for (uint8_t i=0;  i < NUM_LEDS11; i++) {
               leds1[i] = CRGB::White;

            }
   // On met la variable cycle à 2
   cycle = 2;
  // cette fonction sera rappelée dans 1000 ms
  return 1000;
  default:
 //Ici cycle est égal à 2, on met le LED à Yellow/Red/Orange en fonction du registre TCNT0 (plus ou moins aléatoire)
  CRGB color;
  switch (TCNT0 &0xf) {
    case 0 ... 4 : color = CRGB::Yellow; break;
    case 5 ... 10 : color = CRGB::Red; break;
    case 11 ... 14 : color = CRGB::Orange; break;
    default : color = CRGB::Blue; break;
  }
  for (uint8_t i=0; i < NUM_LEDS11; i++) {
    leds1[i] = color;
  }
  cycle = 1;
  return 1000;
  // et on reviendra dans 1000ms avec cycle = 1 ...
  break;
}
return 0;
}

scenario led_string11(&init11, &update_led11);
//Cette ligne déclare un scenario led_string11 ou la fonction init est init11, et la fonction action est update_led11

Bon comme dit François : "Un grand verre d'aspirine et une bonne nuit et tu comprendras :-) "

Mon conseil est de s'inspirer de l'exemple et de commencer avec un seul scénario et un bandeau.

La musique


Pour le circuit qui fait la musique, c'est un petit circuit MP3 player YX5300 utilisé avec la librairies disponibles ici.

 MP3 player YX5300 - entrées à gauche, sortie audio à droite et microSD en bas

Le plus long est de trouver de bons fichiers sons que l'on peut utiliser librement. Vous pouvez aussi faire des enregistrements dans les fêtes foraines avec votre smartphone. Une bonne occasion de s'amuser !
Les enregistrements peuvent être retouchés avec un logiciel spécialisé comme Audacity. Les fichiers sont ensuite sauvegardés sur une mémoire micro-SD.

D'un point de vue programmation, nous allons utiliser une librairie dédiée à la carte.
#include "MD_YX5300.h";

A l'initialisation dans le setup, on règle le volume avec l'instruction suivante:
mp3.volume(MID_VOLUME);

Le volume est compris entre 10 et 30.
Les fonctions sont appelées dans la boucle principale. Ainsi, nous avons la fonction check, qui doit être appelée régulièrement pour recevoir et traiter les messages de la carte.

mp3.check();

Les principales autres fonctions permettant de manipuler les fichiers audio de la carte microSD sont:
playStart
playTrack
playPause
playStop
playNext
playPrev
repeat
equalizer

Il y a également des fonctions plus spécifiques mais ce sera pour un prochain article.
 
N'oubliez pas de mettre une enceinte avec un amplificateur en sortie de la carte!

Contrôle du train

Pour le contrôleur de moteur pour le train, c'est un L9110S.
Par contre, il faut enlever quelques composants sur la carte pour que ça fonctionne correctement pour notre application. La carte a des résistances de pull-up vers le VCC (donc 12V) et cela n'est pas compatible avec un signal logique de 3,3V. Donc on enlève les résistances R2,R3,R4,R5.

Il y a aussi un condensateur en sortie qui vaut mieux enlever (C3,C4).

L9110S modifié

Le train est un modèle en HOe qui fonctionne bien en 9V. En 12V, ça fonctionne aussi mais cela donne plutôt l'allure d'un TGV!
Nous avions d'abord prévu de commander la vitesse par des boutons. Un premier bouton était dédié à l'arrêt d'urgence, le second et le troisième étaient utilisés pour augmenter et diminuer la vitesse.
Ici aussi, on fait appel à l'approche objet et nous utilisons les deux fonctions suivantes:
train_control.init(train_cycle);
train_control.run(train_cycle);
 

La vitesse est paramétrable et cela permet de l'augmenter ou la diminuer par pas.
Nous allons détailler l'interface avec les boutons dans le chapitre suivant.

Détecter les actions sur les boutons de commande pour le public

Le principe de détection des actions est basé sur des boutons connectés à un bus I2C. Une petite carte d'interface PCF8574 permet de relier les boutons au bus. En fait, nous utilisons ce type de carte pour interfacer les 8 relais et les 10 boutons. Chaque carte a une capacité de piloter 8 objets avec une adresse matérielle sur 3 bits fixée par les cavaliers jaunes sur la carte. Les cartes sont raccordées les unes aux autres via 4 fils : VCC, GND, SDA, SCL, c'est le bus I2C.

 carte PCF8574

Pour notre application, nous avons donc une carte pour connecter le ARDUINO aux relais dans le boîtier de commande, puis cette carte est connectée à autre carte, située à l'extérieur sur le module, gérant 5 boutons, qui est elle-même connectée à la carte suivante gérant les 5 derniers boutons.

Les 10 boutons de commandes sur le module

Nous utilisons encore une librairie qui gère les lectures et écritures sur le bus I2C. Pour cela on inclut la librairie:  #include "i2c_port.h"

Les 10 boutons ont des adresses sur le bus I2C. Il faut aussi initialiser les boutons pour en faire des entrées comme ceci:

     for( uint16_t i=0; i < 5; i++) {
          I2C_pinMode(10+i, INPUT);
          I2C_pinMode(20+i, INPUT);
      }

Dans le programme principal loop(), on lit régulièrement tous les boutons avec I2C_digitalRead et suivant l'état du bouton, une action est effectuée:

         if (I2C_digitalRead(10+i) == 0) {
            //Affichage sur la console du numéro du bouton
            Serial.print(F("Button "));
            Serial.println(10+i);
            //Changement de musique
            mp3.playTrackRepeat(i+6);
        }

 Dans le programme vous trouverez un tableau qui permet de manipuler les boutons et cela se traduit par exemple par:

i2c_ports[1].read_value

On fait de nouveau appel à l'approche objet et nous utilisons les fonctions suivantes:

op_button1.init(op_button1_cycle);
op_button1.enable(op_button1_cycle);
op_button1.run(op_button1_cycle); 

Cela simplifie la manipulation dans le programme principal.

Commander/arrêter les moteurs des manèges

Le commande des manèges est réalisée avec des relais qui sont commandés aussi par le bus I2C.Vous pourrez trouver assez facilement ces barrettes de relais (2, 4, 8, ...). Il faut impérativement vérifier la tension maximale de sortie et le courant supportés par les relais. Dans notre cas les tensions des moteurs des manèges sont en alternatif et peuvent monter à 25V/1,5A.

Relais

Les actions pour commander les relais sont simples. Voici un exemple:

    I2C_digitalWrite(R_BALANCELLE, LOW);
    I2C_digitalWrite(R_TOUR, LOW);
    I2C_digitalWrite(R_ROUE, LOW);
    I2C_digitalWrite(R_CHAISES, LOW);
    I2C_digitalWrite(R_WATERPARK, LOW);

Retour d'expérience 

La réalisation de l'ensemble matériel n'est pas compliqué bien que cela soit dense. Il faut par contre beaucoup de patience et d'organisation pour réaliser des câblages propres. Si cela n'est pas repéré ni séparé vous n'aurez aucune chance de vous y retrouver et ce sont des heures perdues à la recherche de fils qui vous attendent. Votre effort au début sera largement récompensé lorsqu'il faudra réparer ou modifier.

Les boutons étaient une fausse bonne idée. Premièrement, cela ne fonctionne pas aussi bien en exposition qu'au club. Le bus I2C s'est vu très perturbé par l'environnement dans lequel on trouve des émetteurs/récepteurs radio, des moteurs, ... Les déclenchements du train n'étaient pas garantis et cela nous a conduit a mettre une alimentation de secours pour faire circuler le petit train de la fête foraine.
Deuxièmement, le public a tendance à appuyer fort sur le module, ce qui peut décaler légèrement celui-ci et perturber la circulations des trains sur les voies principales.

La musique et l'éclairage ont fonctionné à merveille. Vous pouvez donc réutiliser sans aucun problème la partie matérielle et logicielle.

La commandes des moteurs n'a pas posé de problème car l'interface I2C est située dans le boîtier de commande qui le protège. Nous avions tout de même prévu de pouvoir connecter directement les manèges à l'alimentation des manèges grâce aux connecteurs avec un brochage identique. Cela nous a servi pour la mise au point mais pas pendant l'expo.

Le manège pirate est superbe mais il a des fuites! Heureusement que nous avions décidé de ne rien mettre ni en-dessous, ni au voisinage de ce manège... Suivez notre exemple et mettez bien votre électronique de commande dans un boîtier le plus loin possible de l'eau.

Il ne vous reste plus qu'à lire le programme simplifié et à faire votre marché!

Le programme

Le programme est accessible sous un drive ici. Il sera prochainement mis sous Github par François.

Voici le programme principal simplifié qui peut vous servir d'exemple:
#include "stdint.h";
#include "Wire.h";
#include "SPI.h";
#include "FastLED.h";
#include "MD_YX5300.h";
#include "dcc_timer.h";
#include "i2c_port.h"
#include "scenario.h"


#define CURRENT_DETECT 41
// How many leds in your strip?
// Mas de cocagne 24+16
#define NUM_LEDS1 40
// Palais des mirages (2) / Palais des glaces (3) + couronne grande roue (12)
#define NUM_LEDS2 17
// grande roue gauche 
#define NUM_LEDS3 26
// Grande roue droite 
#define NUM_LEDS4 26
// Petit bandeau - 57
#define NUM_LEDS5 56
// Grand bandeau 144
#define NUM_LEDS6 144 
// Mas de cocagne 2 12
#define NUM_LEDS7 12

#define FET1 45
#define FET2 44
#define FET3 43
#define FET4 42

// For led chips like Neopixels, which have a data line, ground, and power, you just
// need to define DATA_PIN.  For led chipsets that are SPI based (four wires - data, clock,
// ground, and power), like the LPD8806 define both DATA_PIN and CLOCK_PIN
#define DATA_PIN1 22 // Grande Roue Gauche
#define DATA_PIN2 23 // Palais glace / mirage
#define DATA_PIN3 24 // Grande Roue Droite
#define DATA_PIN4 25 // Petit mas
#define DATA_PIN5 26 // Grand mas
#define DATA_PIN6 27 // Bandeau 1
#define DATA_PIN7 28 // Bandeau 2

// 22 : Couroune derriere la grande roue + Grande roue droite
// 23 : grande roue gauche

// define which button control which manège
#define B_BALANCELLE 10 // Balancelle
#define B_TOUR 20 // Tour
#define B_ROUE 12 // Grande roue
#define B_CHEVEAUX 13 // Petit chevaux - musique seule
#define B_TRAIN   14
#define B_CHAISES 11 // Chaises
#define B_WATERPARK 21
#define B_EMERG 24
// define relay 
#define R_BALANCELLE 5 // Balancelle
#define R_TOUR 4 // Tour
#define R_ROUE 3 // Grande Roue
#define R_WATERPARK 1 // Pirates
#define R_CHAISES 7 // Chaises

// Operator buttons on the back
#define OP_BUTTON1 30
#define OP_BUTTON2 31
#define OP_BUTTON3 32
#define OP_BUTTON4 33
#define MANEGE_DURATION 30000
#define HIGH_VOLUME 30
#define MID_VOLUME 20
#define LOW_VOLUME 10

MD_YX5300 mp3(0,0);
bool bUseCallback = true; // use callbacks?
bool bUseSynch = false;   // use synchronous?
uint8_t mp3_status;
uint8_t requested_music;
uint8_t stop_request;

#define TRACK_BALANCELLE 4
#define TRACK_TOUR 2
#define TRACK_ROUE 6
#define TRACK_CHEVAUX 8
#define TRACK_CHAISES 3
#define TRACK_WATERPARK 5

#define MP3_DEFAULT 1 // Default track to play ...
// Define 3 I2C extender
#define NB_I2C_PORT 4
I2c_Port i2c_ports[NB_I2C_PORT]= {I2c_Port(0x20), I2c_Port(0x21), I2c_Port(0x22), I2c_Port(0x23)};

uint8_t led11_cycle;
uint8_t led12_cycle;
uint8_t led21_cycle;
uint8_t led22_cycle;
uint8_t led34_cycle;
uint8_t led5_cycle;
uint8_t led6_cycle;
uint8_t train_cycle;
uint8_t manege1_cycle;
uint8_t manege2_cycle;
uint8_t manege3_cycle;
uint8_t manege4_cycle;
uint8_t manege5_cycle;
uint8_t manege6_cycle;
uint8_t op_button1_cycle;
uint8_t op_button2_cycle;
uint8_t op_button3_cycle;
uint8_t op_button4_cycle;
uint8_t mp3_cycle;

// Define the array of leds
CRGB leds1[NUM_LEDS1];
CRGB leds2[NUM_LEDS2];
CRGB leds3[NUM_LEDS3];
CRGB leds4[NUM_LEDS4];
CRGB leds5[NUM_LEDS5];
CRGB leds6[NUM_LEDS6];
CRGB leds7[NUM_LEDS7];


uint32_t current_time;
uint8_t current_color; 
#include "helpers.h" 
 
 void setup() { 
    uint8_t i,ret;
    
    Wire.begin();
    Serial.begin(230400);

    // Turn Off Relays
    Wire.beginTransmission(0x20); // transmit to PCF8574
    Wire.write(0x00);
    Wire.endTransmission();

    pinMode(CURRENT_DETECT, INPUT);     // set pin to input for current detector
    digitalWrite(CURRENT_DETECT, HIGH); // turn on pullup resistors

    delay(2000);

    timer3.end();
    // MP3 init
    mp3.begin();
    setCallbackMode(bUseCallback);
    setSynchMode(bUseSynch);

    Serial.println(F("\nScanning I2C bus"));
    for (i=1; i<127; i++) {
        // scan I2C bus
        Wire.beginTransmission(i); // transmit to PCF8574
        ret = Wire.endTransmission();
        if (ret == 0) {
            Serial.print(F("I2C dev at address : 0x"));
            Serial.println(i,16);
        }
    }
    Serial.println(F("Done"));

    FastLED.addLeds<WS2812B, DATA_PIN5, GRB>(leds1, NUM_LEDS1);  // Mas double (40)
    FastLED.addLeds<WS2812B, DATA_PIN3, GRB>(leds2, NUM_LEDS2);  // Palais glace/mirage + courone grande roue 
    FastLED.addLeds<WS2812B, DATA_PIN2, GRB>(leds3, NUM_LEDS3);  // Grande Roue gauche
    FastLED.addLeds<WS2812B, DATA_PIN1, GRB>(leds4, NUM_LEDS4);  // Grande Roue droite
    FastLED.addLeds<WS2812B, DATA_PIN7, GRB>(leds5, NUM_LEDS5);  // petit bandeau
    FastLED.addLeds<WS2812B, DATA_PIN6, GRB>(leds6, NUM_LEDS6);  // Grand bandeau OK
    FastLED.addLeds<WS2812B, DATA_PIN4, GRB>(leds7, NUM_LEDS7);  // Petit mas OK

    for (uint8_t i=0; i< NUM_LEDS1; i++) {
        leds1[i] = CRGB::Black;
    }
    for (uint8_t i=0; i< NUM_LEDS2; i++) {
        leds2[i] = CRGB::Black;
    }
    for (uint8_t i=0; i< NUM_LEDS3; i++) {
        leds3[i] = CRGB::Black;
    }
    for (uint8_t i=0; i< NUM_LEDS4; i++) {
        leds4[i] = CRGB::Black;
    }
    for (uint8_t i=0; i< NUM_LEDS5; i++) {
        leds5[i] = CRGB::Black;
    }
    for (uint8_t i=0; i< NUM_LEDS6; i++) {
        leds6[i] = CRGB::Black;
    }
    for (uint8_t i=0; i< NUM_LEDS7; i++) {
        leds7[i] = CRGB::Black;
    }

    FastLED.show();
    delay(500);

    leds1[0] = CRGB::Red;
    leds1[1] = CRGB::Green;
    leds1[2] = CRGB::Blue;
    leds1[3] = CRGB::White;
    
    leds2[0] = CRGB::Red;
    leds2[1] = CRGB::Green;
    leds2[2] = CRGB::Blue;
    leds2[3] = CRGB::White;
    leds2[4] = CRGB::White;
    
    leds3[0] = CRGB::Red;
    leds3[1] = CRGB::Green;
    leds3[2] = CRGB::Blue;
    leds3[3] = CRGB::White;
    leds3[4] = CRGB::White;
    leds3[5] = CRGB::White;
    
    leds4[0] = CRGB::Red;
    leds4[1] = CRGB::Green;
    leds4[2] = CRGB::Blue;
    leds4[3] = CRGB::White;
    leds4[4] = CRGB::White;
    leds4[5] = CRGB::White;
    leds4[6] = CRGB::White;
    
    leds5[0] = CRGB::Red;
    leds5[1] = CRGB::Green;
    leds5[2] = CRGB::Blue;
    leds5[3] = CRGB::White;
    leds5[4] = CRGB::White;
    leds5[5] = CRGB::White;
    leds5[6] = CRGB::White;
    leds5[7] = CRGB::White;
    
    leds6[0] = CRGB::Red;
    leds6[1] = CRGB::Green;
    leds6[2] = CRGB::Blue;
    leds6[3] = CRGB::White;
    leds6[4] = CRGB::White;
    leds6[5] = CRGB::White;
    leds6[6] = CRGB::White;
    leds6[7] = CRGB::White;
    leds6[8] = CRGB::White;
    
    leds7[0] = CRGB::Red;
    leds7[1] = CRGB::Green;
    leds7[2] = CRGB::Blue;
    leds7[3] = CRGB::White;
    leds7[4] = CRGB::White;
    leds7[5] = CRGB::White;
    leds7[6] = CRGB::White;
    leds7[7] = CRGB::White;
    leds7[8] = CRGB::White;
    leds7[9] = CRGB::White;
    
    FastLED.show();

    delay(1000);

#if 0
    for( uint16_t i=0; i<NUM_LEDS1; i++) {
        leds1[i] = CRGB::Blue;
    }
     for( uint16_t i=0; i<NUM_LEDS2; i++) {
        leds2[i] = CRGB::Red;
      }
    for( uint16_t i=0; i<NUM_LEDS3; i++) {
        leds3[i] = CRGB::Green;
        leds4[i] = CRGB::Yellow;
    }
    for( uint16_t i=0; i<NUM_LEDS5; i++) {
        leds5[i] = CRGB::Orange;
    }
    for( uint16_t i=0; i<NUM_LEDS6; i++) {
        leds6[i] = CRGB::Purple;
    }
    for( uint16_t i=0; i<NUM_LEDS7; i++) {
        leds7[i] = CRGB::Pink;
    }

    FastLED.show();

   delay(5000);

#endif

     current_color = 0;
     FastLED.setBrightness(0xFF);
     /* Set button as input ports */
     for( uint16_t i=0; i<5; i++) {
          I2C_pinMode(10+i, INPUT);
          I2C_pinMode(20+i, INPUT);
          I2C_pinMode(30+i, INPUT);
      }

     for (uint8_t i = 0; i < NB_I2C_PORT; i++) {
         i2c_ports[i].set_input_i2c();
     }
    I2C_digitalWrite(R_BALANCELLE, LOW);
    I2C_digitalWrite(R_TOUR, LOW);
    I2C_digitalWrite(R_ROUE, LOW);
    I2C_digitalWrite(R_CHAISES, LOW);
    I2C_digitalWrite(R_WATERPARK, LOW);
    

     current_time=0;

     led_string11.init(led11_cycle);
     led_string12.init(led12_cycle);
     led_string21.init(led21_cycle);
     led_string22.init(led22_cycle);
     led_string34.init(led34_cycle);
     led_string5.init(led5_cycle);
     led_string6.init(led6_cycle);
     train_control.init(train_cycle);
     op_manege1.init(manege1_cycle);
     op_manege2.init(manege2_cycle);
     op_manege3.init(manege3_cycle);
     op_manege4.init(manege4_cycle);
     op_manege5.init(manege5_cycle);
     op_manege6.init(manege6_cycle);
     op_button1.init(op_button1_cycle);
     op_button2.init(op_button1_cycle);
     op_button3.init(op_button1_cycle);
     op_button4.init(op_button1_cycle);
     op_mp3.init(mp3_cycle);

     led_string11.enable(led11_cycle);
     led_string12.enable(led12_cycle);
     led_string21.enable(led21_cycle);
     led_string22.enable(led22_cycle);
     led_string34.enable(led34_cycle);
     led_string5.enable(led5_cycle);
     led_string6.enable(led6_cycle);
     train_control.enable(train_cycle);
     op_manege1.enable(manege1_cycle);
     op_manege2.enable(manege2_cycle);
     op_manege3.enable(manege3_cycle);
     op_manege4.enable(manege4_cycle);
     op_manege5.enable(manege5_cycle);
     op_manege6.enable(manege6_cycle);
     op_button1.enable(op_button1_cycle);
     op_button2.enable(op_button1_cycle);
     op_button3.enable(op_button1_cycle);
     op_button4.enable(op_button1_cycle);
     op_mp3.enable(mp3_cycle);

     for (uint8_t hue=0; hue < 255; hue++) {
         FastLED.showColor(CHSV(hue, 255, 180));
         delay(10);
     }
     /* Turn on FET */
     pinMode(FET1,OUTPUT);
     pinMode(FET2,OUTPUT);
     pinMode(FET3,OUTPUT);
     pinMode(FET4,OUTPUT);
     digitalWrite(FET1, LOW);
     digitalWrite(FET2, HIGH);
     digitalWrite(FET3, LOW);
     digitalWrite(FET4, HIGH);
     mp3.volume(MID_VOLUME);

     delay (1000);
}


void loop() { 
    uint16_t i;
    delay(20);

    /* All the background processing */
    current_time = millis();
    // MP3 player
    mp3.check();

    // Update I2C ports vales
    for (i=0; i<NB_I2C_PORT; i++) {
        i2c_ports[i].read_i2c();
        i2c_ports[i].write_i2c();
    }
#if 0
    // Simulate Button Press with character received over Serial
    if (Serial.available() > 0) {
        // read the incoming byte:
        i = Serial.read();
        switch (i) {
        case '1':
            i2c_ports[1].read_value &= 0xfe; // Button 1 pressed
            break;
        case '2':
            i2c_ports[1].read_value &= 0xfd; // Button 2 pressed
            break;
        case '3':
            i2c_ports[1].read_value &= 0xfb; // Button 3 pressed
            break;
        case '4':
            i2c_ports[1].read_value &= 0xf7; // Button 4 pressed
            break;
        case '5':
            i2c_ports[1].read_value &= 0xef; // Button 5 pressed
            break;
        case '6':
            i2c_ports[2].read_value &= 0xfe; // Button 6 pressed
            break;
        case '7':
            i2c_ports[2].read_value &= 0xfd; // Button 7 pressed
            break;
        case '8':
            i2c_ports[2].read_value &= 0xfb; // Button 8 pressed
            break;
        case '9':
            i2c_ports[2].read_value &= 0xf7; // Button 9 pressed
            break;
        case '0':
            i2c_ports[2].read_value &= 0xef; // Button 10 pressed
            break;
        case 'A':
            i2c_ports[3].read_value &= 0xfe; // Button 0 pressed
            break;
        case 'B':
            i2c_ports[3].read_value &= 0xfd; // Button 0 pressed
            break;
        case 'C':
            i2c_ports[3].read_value &= 0xfb; // Button 0 pressed
            break;
        case 'D':
            i2c_ports[3].read_value &= 0xf7; // Button 0 pressed
            break;
        default:
            break;
        }
    }
#endif
    if (Serial.available() > 0) {
        // read the incoming byte:
        i = Serial.read();
        switch (i) {
        case '1'...'9':
            requested_music = i-'0';
            break;
        case 'A':
            i2c_ports[3].read_value &= 0xfe; // Button 0 pressed
            break;
        case 'B':
            i2c_ports[3].read_value &= 0xfd; // Button 0 pressed
            break;
        case 'C':
            i2c_ports[3].read_value &= 0xfb; // Button 0 pressed
            break;
        case 'D':
            i2c_ports[3].read_value &= 0xf7; // Button 0 pressed
            break;
        default:
            break;
        }
    }

    FastLED.show();

    /* end of "background tasks */
    led_string11.run(led11_cycle); // Grand mas de cocagne
    led_string12.run(led12_cycle);
    led_string21.run(led21_cycle);
    led_string22.run(led22_cycle);
    led_string34.run(led34_cycle);
    led_string5.run(led5_cycle);
    led_string6.run(led6_cycle);
    train_control.run(train_cycle);
    op_manege1.run(manege1_cycle);
    op_manege2.run(manege2_cycle);
    op_manege3.run(manege3_cycle);
    op_manege4.run(manege4_cycle);
    op_manege5.run(manege5_cycle);
    op_manege6.run(manege6_cycle);
    op_button1.run(op_button1_cycle);
    op_button2.run(op_button1_cycle);
    op_button3.run(op_button1_cycle);
    op_button4.run(op_button1_cycle);
    op_mp3.run(mp3_cycle);
    }
}