Projet enseigne Led géant

Bonjour à tous,
Je suis nouveau ici alors avant tout bonjour à tous. Je suis régisseur lumière et j'ai un projet de lettrage géant à monter en haut d'un chapiteau chaque lettre faisant environ 70 80 cm de haut et étant contourné d'un ruban LED WS2812B, environ 2500 led au final.
J'arrive déjà à faire briller mon ruban est à utiliser différents effets mais au fur et à mesure que j'ajoute des lettres dans mon lettrage et donc des led ceci devient de plus en plus compliqué... Forcément ma question est donc dans un premier temps de savoir quel méthodologie utilisée pour mon code j'ai l'impression que ça va devenir vraiment chaotique autrement :

  • Est il possible de définir chaque lettre à un certains nombres de LED dès le départ et ensuite de mettre couleur effet et modifications directement à la lettre (correspondant à un groupe de led) plutôt que d'avoir à taper à chaque fois le nombre de Led ?
  • Est il plus interessant de faire différentes sorties data une sortie datapin par lettre par exemple ?
  • En bref quelles est la méthode la plus pratique pour travailler et tester efficacement et pour définir au mieux mon espace de travail ? en fait j'ai plusieurs groupe de led correspondant chacun à une lettre et sur lesquels j'aimerais appliquer différents effets soit séparément soit tous ensemble.

voici un exemple de code de ce que j'aimerais faire, ici fait avec seulement 3 lettres sachant qu'au final j'en aurais 14. Donc l'idée et de pouvoir faire ce genre de chose de façon bien moins fastidieuse.

#include <FastLED.h>
#include <Adafruit_NeoPixel.h>

//FIXED GLOBAL VARS
#define DATA_PIN 6  //Define led data pin in
#define LED_TYPE WS2812B  //define type of led
#define NUM_LEDS 454  //num of leds in strip

struct CRGB leds[NUM_LEDS];  // Initialize LED array

void setup() {
  // put your setup code here, to run once:
  delay(2000); //for safe startup
  FastLED.addLeds<LED_TYPE, DATA_PIN>(leds, NUM_LEDS);
}

void loop() {
for (int i = 0; i < 118; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 119; i < 272; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 273; i < 454; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
FastLED.show();       //start the leds
delay(100);

for (int i = 0; i < 118; i++) {
  leds[i] = CRGB::White;  //set the all leds to blue
}
for (int i = 119; i < 272; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 273; i < 454; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
FastLED.show();       //start the leds
delay(100);

for (int i = 0; i < 118; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 119; i < 272; i++) {
  leds[i] = CRGB::white;  //set the all leds to blue
}
for (int i = 273; i < 454; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
FastLED.show();       //start the leds
delay(100);

for (int i = 0; i < 118; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 119; i < 272; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 273; i < 454; i++) {
  leds[i] = CRGB::white;  //set the all leds to blue
}
FastLED.show();       //start the leds
delay(100);
}

J'aurais beaucoup aimé trouver tout ça par moi même mais malheureusement je réalise toutes les possibilités d'arduino et j'ai peur de me perdre dans pleins de choses et essayer par curiosité alors que quelqu'un ici peut sans doute m'apporter la méthodologie que je recherche sans que j'y passe trop de temps de manière archaïque.

Merci d'avance et bonne soirée à vous.

peut-être cette bibliothèque peut vous aider:

leds_NeoMatrix

Merci pour ton retour et pour le lien.
Alors je n'ai pas été très clair dans ma description, je ne cherche pas à faire du matricage avec un écran doté de plusieurs LED mais juste piloté des rubans led qui détoure de grosses lettres en plastique.
Voici des photos pour être plus clair :

Avec les adresses des Leds.

L'idée serait de pouvoir dire rapidement les Led de ... à .... correspondent à la lettre et ensuite pouvoir appeler la lettre et mettre une couleur un effet ou autre directement.
Merci d'avance
Jérémy

Rebonsoir,
Encore une chose je suis en train de bricoler sur un enchainement d'effets. Mais j'ai un problème surement un truc qui traine dans mon code car à la première lecture ça marche bien puis au passage du theater chase. Le relecture du loop d'après s'éteint entre chaque step... je penses que cela vient de "strip.begin" mais je n'arrive pas à l'arrêter pour relancer la loop sans cela. Car j'en ai besoin pour effectuer correctement le theater chase.

#include <FastLED.h>
#include <Adafruit_NeoPixel.h>
#include <FastLED_NeoPixel.h>

//FIXED GLOBAL VARS
#define DATA_PIN 6  //Define led data pin in
#define LED_TYPE WS2812B  //define type of led
#define NUM_LEDS 454  //num of leds in strip
#define BRIGHTNESS 50

// Adafruit_NeoPixel strip(NUM_LEDS, DATA_PIN, NEO_GRB);  // <- Adafruit NeoPixel version
FastLED_NeoPixel<NUM_LEDS, DATA_PIN, NEO_GRB> strip;      // <- FastLED NeoPixel version



struct CRGB leds[NUM_LEDS];  // Initialize LED array

void setup() {
  // put your setup code here, to run once:

  delay(2000); //for safe startup
  FastLED.addLeds<LED_TYPE, DATA_PIN>(leds, NUM_LEDS);


}

void loop() {
 FastLED.clear();
for (int i = 0; i < 118; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 119; i < 272; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 273; i < 454; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
FastLED.show();       //start the leds
delay(1000);

for (int i = 0; i < 118; i++) {
  leds[i] = CRGB::White;  //set the all leds to blue
}
for (int i = 119; i < 272; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 273; i < 454; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
FastLED.show();       //start the leds
delay(1000);

for (int i = 0; i < 118; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 119; i < 272; i++) {
  leds[i] = CRGB::White;  //set the all leds to blue
}
for (int i = 273; i < 454; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
FastLED.show();       //start the leds
delay(1000);

for (int i = 0; i < 118; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 119; i < 272; i++) {
  leds[i] = CRGB::Green;  //set the all leds to blue
}
for (int i = 273; i < 454; i++) {
  leds[i] = CRGB::White;  //set the all leds to blue
}
FastLED.show();       //start the leds
delay(1000);

 for (int i = 0; i < 454; i++) {
  leds[i] = CRGB::Black;  //set the all leds to blue
}
for (int i = 0; i < NUM_LEDS; i++) {
  leds[i] = CRGB::White;  //set the led to Pink
  FastLED.show();       //start the leds
  leds[i] = CRGB::Red;  //clear the led
  delay(10);          //Wait before moving to next let 
 }
for (int i = 0; i < 118; i++) {
  leds[i] = CRGB::Red;  //set the all leds to blue
}
for (int i = 119; i < 272; i++) {
  leds[i] = CRGB::Red;  //set the all leds to blue
}
for (int i = 273; i < 454; i++) {
  leds[i] = CRGB::Red;  //set the all leds to blue
}
FastLED.show();       //start the leds
delay(1000);

  strip.begin();

theaterChase(strip.Color(0, 255, 255), 100, 3, 5);  // cyan
strip.clear();
}

void theaterChase(uint32_t color, unsigned long wait, unsigned int groupSize, unsigned int numChases) {
  for (unsigned int chase = 0; chase < numChases; chase++) {
    for (unsigned int pos = 0; pos < groupSize; pos++) {
      strip.clear();  // turn off all LEDs
      for (unsigned int i = pos; i < strip.numPixels(); i += groupSize) {
        strip.setPixelColor(i, color);  // turn on the current group
      }
      strip.show();
      delay(wait);
    }
  }
}

Si quelqu'un peux m'aider ce serait top.
Merci d'avance

Jérémy

Vous pourriez utiliser plusieurs contrôleurs, un par lettre

Pour commencer, de la même façon que tu as déclarer un tableau de struct CRGB, il faudrait créer une structure définissant tes lettres.
Puis de faire des fonctions pour tes animations.

un truc du genre, j'ai testé uniquement dans un simulator.

#include <FastLED.h>
#include <Adafruit_NeoPixel.h>

//FIXED GLOBAL VARS
#define DATA_PIN_L 6  //Define led data pin in
#define DATA_PIN_E 7  //Define led data pin in
#define DATA_PIN_S 5  //Define led data pin in
#define LED_TYPE WS2812B  //define type of led
#define NUM_LEDS 6  //num of leds in strip


struct CRGB leds[NUM_LEDS];  // Initialize LED array

struct lettre {
  int nbLED;
  struct CRGB* leds;
};
struct lettre L, E, S;

void setup() {
  L.nbLED = 6;
  L.leds = new CRGB[6];
  E.nbLED = 11;
  E.leds = new CRGB[11];
  S.nbLED = 11;
  S.leds = new CRGB[11];
  // put your setup code here, to run once:
  delay(2000); //for safe startup
  FastLED.addLeds<LED_TYPE, DATA_PIN_L>(L.leds, L.nbLED);
  FastLED.addLeds<LED_TYPE, DATA_PIN_E>(E.leds, E.nbLED);
  FastLED.addLeds<LED_TYPE, DATA_PIN_S>(S.leds, S.nbLED);
}

void unisColor(struct lettre currentLetter, CRGB color) {
  for (int i = 0; i < currentLetter.nbLED; i++) {
    currentLetter.leds[i] = color;  //set the all leds to blue
  }
}

void loop() {

  unisColor(L,CRGB::Green);
  unisColor(E,CRGB::Red);
  unisColor(S,CRGB::CRGB::Orange);
  FastLED.show();       //start the leds
  delay(1000);


  unisColor(L,CRGB::CRGB::Yellow);
  unisColor(E,CRGB::Green);
  unisColor(S,CRGB::CRGB::CRGB::Red);
  FastLED.show();       //start the leds
  delay(1000);

  unisColor(L,CRGB::CRGB::Orange);
  unisColor(E,CRGB::Yellow);
  unisColor(S,CRGB::CRGB::CRGB::Green);
  FastLED.show();       //start the leds
  delay(1000);
  unisColor(L,CRGB::Red);
  unisColor(E,CRGB::Orange);
  unisColor(S,CRGB::CRGB::CRGB::Yellow);
  FastLED.show();       //start the leds
  delay(1000);
}

je n'ai pas trop compris ce que tu décris pour l'utilisation de theaterChase

Salut Merci pour ton retour et désole du temps de réponse j'ai été pris par plein d'autres trucs ces derniers temps mais je m'y remet enfin. Alors c'est plutôt ça l'idée effectivement mais le problème est que je n'ai qu'une seule data. Je vais tout de même essayer de trouver en partant de ton code mais comment définir tel lettre va de tel led à tel led ?

pourquoi ne pouvez vous pas changer cela ? vous aviez posé la question

et au fait quel est votre Arduino ? parce que pour commander 2500 Leds il faut pas mal de mémoire


sinon voici un exemple pour que les lettres soient pilotées depuis une seule pin à condition bien sûr que les lettres soient connectées entre elles.

vous définissez les lettres dans un tableau de structures par exemple dans le code j'ai

t_lettre lesLettres[] = {
  {'A', 0, 40},
  {'B', 40, 20},
  {'C', 60, 30},
  {'D', 90, 35},
};

ça veut dire que la lettre A commence au pixel 0 et compte 40 Leds, la lettre 'B' commence au pixel 40 et compte 20 Leds etc...

ensuite j'ai fait des fonctions qui prennent la lettre en paramètre et des couleurs par exemple. Le code cherche cette lettre dans le tableau (on pourrait bien sûr faire autrement si les lettres peuvent se répéter et mettre un identifiant ou juste dire l'indice dans le tableau) et applique une fonction de la bibliothèque fastLed sur ce segment. Pour cela on va taper "au milieu" du tableau en prenant comme adresse de début celle de la première case correspondant à la lettre trouvée et le bon nombre de LEDs

c'est la base, pour ensuite faire des animations il faudra bosser un peu car vous ne pouvez pas appeler une fonction bloquante sur juste une lettre sinon les autres ne seraient pas animées ==> il faudra gérer vous même la construction de l'animation par lettre puis faire l'appel à show()

le code pour référence;

#include <FastLED.h>

const uint16_t ledCount = 40 + 20 + 30 + 35;
const byte dataPin = 2;

CRGB leds[ledCount];

struct t_lettre {
  char caractere;
  uint16_t debut;   // N° Led de début
  uint16_t nombre;  // nombre de Leds
};

t_lettre lesLettres[] = {
  {'A', 0, 40},
  {'B', 40, 20},
  {'C', 60, 30},
  {'D', 90, 35},
};

void remplir(char c, uint32_t couleur1 ) {
  for (auto& l : lesLettres) {
    if (l.caractere == c) {
      Serial.print(F("remplir : ")); Serial.print(c); Serial.print(F(" => de "));
      Serial.print(l.debut);Serial.print(F(" pour x")); Serial.println(l.nombre);
      fill_solid(&(leds[l.debut]), l.nombre, couleur1);
      FastLED.show();
      break;
    }
  }
}

void gradient(char c, uint32_t couleur1, uint32_t couleur2 ) {
  for (auto& l : lesLettres) {
    if (l.caractere == c) {
      Serial.print(F("gradient : ")); Serial.print(c); Serial.print(F(" => de "));
      Serial.print(l.debut);Serial.print(F(" pour x")); Serial.println(l.nombre);
      fill_gradient_RGB(&(leds[l.debut]), l.nombre, couleur1, couleur2);
      FastLED.show();
      break;
    }
  }
}

void eteindre() {
  fill_solid(leds, ledCount, CRGB::Black);
  FastLED.show();
}

void setup() {
  Serial.begin(115200);

  FastLED.addLeds<WS2812B, dataPin, GRB>(leds, ledCount);
  FastLED.setBrightness(50);

  remplir('A', CRGB::Red);

  delay(200);
  remplir('B', CRGB::Blue);
  
  delay(200);
  remplir('C', CRGB::Green);
  
  delay(200);
  gradient('D', CRGB::Blue, CRGB::Yellow);
  
  delay(5000);
  eteindre();
}

void loop() {}

Effectivement désolé c’était ma question au départ mais depuis j’ai fait toute les led avec la même data… mais mon problème majeur est que j’avais besoin de tirer ma carte en bas du chapiteau pour continuer à faire des modifications donc compliqué de descendre 15 data car nécessitant trop de câble.
Désolé pour cette contreinfo que je n’avais pas ré précisé.
Ça marche pour piloter 2500 leds même si ça ralentit un peu sur certains effet, un peu moins de fluidité . Ma carte est une teensy 2++.
Merci beaucoup pour tes retours rapide en tout cas.

j'ai posté un petit code d'exemple ci dessus pour un exemple de pilotage de différentes parties d'un bandeau

Wahoo merci beaucoup !! Je vais essayer ça tout de suite. J’ai l’impression de comprendre à peu près le principe mais je demanderais peut être des explications de certaines lignes même si ça fonctionne par besoin de tout comprendre.
Mais merci vraiment.
Je te redis dès que j’ai fait les premiers essais.

C’est juste ces lignes là dans le setup :


remplir('A', CRGB::Red);
  delay(200);
  remplir('B', CRGB::Blue);  
  delay(200);
  remplir('C', CRGB::Green);````

Elle ne devrait pas se retrouver dans le loop ?

(Désolé je suis sur mon téléphone j’espère que mon message apparaîtra correctement)

je les ai mises dans le setup pour faire cela une seule fois, en guise de démo

(sur mon iPhone aussi :slight_smile: )

Bon ça marche super mais il me manque une fonction essentielles, je n'arrive pas à appeler ma lettre pour lui appliquer des effets.

for ('L') 
 {
    leds[i] = CRGB::Red;
    FastLED.show();
    delay(100);
    leds[i] = CRGB::Black;
}

Désolé c'est certainement pas ça du tout qu'il fallait mettre mais j'avais commencé à capter des choses en appelant les numéros de Led et avec ta méthode c'est nettement plus simple mais ça change tout la méthode de saisi. Je veux bien un petit coup de main.

Et d'ailleurs tu parles des fonctions bloquantes et je me dis que c'est également un autre problème pour moi qui pourrait venir de là je n'arrive pas à faire partir la fonction de défilement des lettres sur plusieurs lettre en même temps. Est il possible de faire un défilement des Leds depuis plusieurs point de départ mais qui partent tous ensemble ?

Merci d'avance

Et est il possible de mettre plusieurs lettre dans une même fonction, si je l'écris comme j'aurais envie de faire ça ferait :

  remplir('L' and 'E', CRGB::Red);

Ce qui ne marche bien entendu pas du tout mais il y a surement une façon de faire.

Oui ce n’est pas cela et il ne faut pas être bloquant ni mettre de délais

Dans l’absolu il faut continuer à enrichir le code pour associer dans la structure décrivant une lettre des éléments d’une animation ou pas et faire que la loop fasse un pas d’une animation quand il faut. Vous ne pourrez pas utiliser les fonctions existantes d’animation il faudra vous en inspirer mais les récrire

Si j’ai le temps aujourd’hui je vous mettrai un exemple mais va falloir coder :technologist:

Bonjour,

Et sinon, passer par QLC+, définir chaque lettre comme une machine...?

Je ne connais pas mais ça semble à explorer

à tester:

Le code a évolué, il y a le .ino pour le code principal et le reste se passe dans l'onglet Lettre.hpp dans lequel j'ai crée deux classes : la classe Phrase qui englobe un certain nombre d'instance de la classe Lettre

on définit la Phrase en lui donnant le tableau de leds et le nombre ainsi qu'entre chevrons le nombre de lettres dans la phrase

par exemple

Phrase<5> phrase(leds, nombreTotalDeLeds); // 5 lettres dans notre phrase

ensuite dans le setup on crée les lettres en donnant la position de la lettre dans dans la phrase (commence à 0) le caractère (purement indicatif) représenté, l'indice de départ dans le bandeau et le nombre de leds pour cette lettre ➜ par exemple ma phrase de 5 lettres pourrait être

  phrase.ajouterLettre(0, 'A',   0, 40);
  phrase.ajouterLettre(1, 'B',  40, 20);
  phrase.ajouterLettre(2, 'C',  60, 30);
  phrase.ajouterLettre(3, 'D',  90, 35);
  phrase.ajouterLettre(4, 'E', 125, 50);

voilà une fois la phrase configurée on peut accéder aux lettres par leur position phrase[0] par exemple et on peut configurer un effet statique ou dynamique

effets statiques:

phrase[x]->allumer(couleur, immediatement)
phrase[x]->eteindre(immediatement)
phrase[x]->gradient(couleur, couleur2, immediatement)
phrase[x]->arcEnCiel(nombreArcEnCiel, immediatement)

si immediatement est true alors l'appel de la fonction fait un rafraîchissement du bandeau, sinon les pixels sont configurés mais l'affichage n'est pas fait. ça permet de préparer toutes les lettres puis de faire un affichage d'un coup à la fin avec FastLED.show();

effets dynamiques:
Pour un effet dynamique il faut configurer l'effet par l'appel de la fonction effet(type, immediatement, ... ) puis dans la loop s'assurer d'appeler tick() sur la phrase le plus souvent possible. les effets possibles sont AUCUN, REMPLISSAGE, DEPLACEMENT, CHASE, CYCLECOULEUR

la fonction effet() prend comme premier paramètre le type d'effet et si on veut un affichage immédiat puis ensuite des paramètres en fonction de l'effet voulu. Attention le passage d'une durée ou d'une couleur se fait toujours en uint32_t

les effets (inspirés de ceux qu'on voit ici mais adaptés pour être non bloquants)

effet(REMPLISSAGE, immediatement, couleur, uint32_t (duree));
effet(DEPLACEMENT, immediatement, couleur, uint32_t (duree));
effet(CHASE, immediatement, couleur, uint32_t (duree));
effet(CYCLECOULEUR, immediatement, uint32_t (duree));            

REMPLISSAGE remplit d'une couleur unie la lettre à la vitesse définie par la durée entre 2 pixels
DEPLACEMENT est un effet type "Running Lights"
CHASE comme dans le lien donné plus haut pour "Theatre Chase"
CYCLECOULEUR comme dans le lien donné plus haut pour "Rainbow Cycle"

j'ai codé cet exemple que je ne peux tester que sur le simulateur n'ayant pas accès à du matériel en ce moment, donc c'est plus pour vous donner une idée de ce qui est possible


pour mémoire

le .ino

#include <FastLED.h>
#include "Lettre.hpp"

const uint16_t nombreTotalDeLeds = 40 + 20 + 30 + 35 + 50;
const byte dataPin = 2;
CRGB leds[nombreTotalDeLeds];

Phrase<5> phrase(leds, nombreTotalDeLeds);

void setup() {
  Serial.begin(115200); Serial.println();
  FastLED.addLeds<WS2812B, dataPin, GRB>(leds, nombreTotalDeLeds);
  FastLED.setBrightness(50);
  phrase.ajouterLettre(0, 'A',   0, 40);
  phrase.ajouterLettre(1, 'B',  40, 20);
  phrase.ajouterLettre(2, 'C',  60, 30);
  phrase.ajouterLettre(3, 'D',  90, 35);
  phrase.ajouterLettre(4, 'E', 125, 50);

  phrase[0]->allumer(CRGB::Red, false); // false pour effet différé par l'appel à FastLED.show()
  phrase[1]->arcEnCiel(1, false);
  phrase[2]->gradient(CRGB::Blue, CRGB::Green, false);   
  FastLED.show();

  delay(1000);
  phrase.toutEteindre();

  phrase[0]->effet(CYCLECOULEUR, false, uint32_t(100));             // uint32_t() très important pour forcer unsigned long
  phrase[1]->effet(REMPLISSAGE, false, CRGB::Red, uint32_t(100));   // uint32_t() très important pour forcer unsigned long`Preformatted text`
  phrase[2]->effet(DEPLACEMENT, false, CRGB::Blue, uint32_t(50));
  phrase[3]->effet(CHASE, false, CRGB::Blue, uint32_t(50));

}

void loop() {
  phrase.tick();
  FastLED.show();
}

et à mettre dans le même répertoire du sketch

Lettre.hp

#ifndef __LETTRE_H__
#define __LETTRE_H__
#include <Arduino.h>
#include <FastLED.h>

enum t_effet {AUCUN, REMPLISSAGE, DEPLACEMENT, CHASE, CYCLECOULEUR  };


class Lettre {
  public:
    Lettre() {}

    void begin(CRGB * l, char c, uint16_t p, uint16_t n)  {
      leds = l;
      symbole = c;
      indexPremiereLed = p;
      nombreDeLeds = n;
      effetLumineux = AUCUN;
    }

    char caractere() {
      return symbole;
    }

    // effets instantannés, sans animation

    void allumer(uint32_t couleur, bool immediatement = true) {
      Serial.print(F("allumer : ")); Serial.println(symbole);
      fill_solid(&(leds[indexPremiereLed]), nombreDeLeds, couleur);
      if (immediatement) FastLED.show();
    }

    void eteindre(bool immediatement = true) {
      Serial.print(F("éteindre : ")); Serial.println(symbole);
      fill_solid(&(leds[indexPremiereLed]), nombreDeLeds, CRGB::Black);
      if (immediatement) FastLED.show();
    }

    void gradient(uint32_t couleur1, uint32_t couleur2, bool immediatement = true) {
      Serial.print(F("gradient : ")); Serial.println(symbole);
      fill_gradient_RGB(&(leds[indexPremiereLed]), nombreDeLeds, couleur1, couleur2);
      if (immediatement) FastLED.show();
    }

    void arcEnCiel(uint16_t nombreArcEnCiel = 1, bool immediatement = true) {
      Serial.print(F("arcEnCiel : ")); Serial.println(symbole);
      fill_rainbow(&(leds[indexPremiereLed]), nombreDeLeds, 0, (nombreArcEnCiel * 255) / nombreDeLeds);
      if (immediatement) FastLED.show();
    }

    // effets avec animation
    void effet(t_effet e, bool immediatement, ...) {
      va_list args;
      t0 = millis();
      indexActif = 0;
      effetImmediat = immediatement;
      effetLumineux = e;
      switch (e) {
        case AUCUN: break;

        case REMPLISSAGE: // effet immédiat couleur et vitesse en paramètres
          va_start(args, 2);
          couleurAnimation = va_arg(args, uint32_t);
          periodeAnimation = va_arg(args, uint32_t);
          va_end(args);
          break;

        case DEPLACEMENT: // Running Lights
          va_start(args, 2);
          couleurAnimation = va_arg(args, uint32_t);
          periodeAnimation = va_arg(args, uint32_t);
          va_end(args);
          break;

        case CHASE:
          va_start(args, 2);
          couleurAnimation = va_arg(args, uint32_t);
          periodeAnimation = va_arg(args, uint32_t);
          va_end(args);
          break;

        case CYCLECOULEUR:
          va_start(args, 1);
          periodeAnimation = va_arg(args, uint32_t);
          va_end(args);
          break;
      }
    }

    void tick() {
      switch (effetLumineux) {
        case AUCUN: break;

        case REMPLISSAGE:
          if (millis() - t0 >= periodeAnimation) {
            leds[indexPremiereLed + indexActif++] = couleurAnimation;
            if (indexActif >= nombreDeLeds) effetLumineux = AUCUN;

            t0 = millis();
            if (effetImmediat) FastLED.show();
          }
          break;

        case DEPLACEMENT:
          if (millis() - t0 >= periodeAnimation) {
            for (uint16_t i = 0; i < nombreDeLeds; i++) {
              leds[indexPremiereLed + i] = ((sin(i + indexActif) * 127 + 128) / 255) * couleurAnimation;
            }
            if ( ++indexActif >= 2 * nombreDeLeds) indexActif = 0;
            t0 = millis();
            if (effetImmediat) FastLED.show();
          }
          break;

        case CHASE:
          if (millis() - t0 >= periodeAnimation) {
            for (int i = 0; i < nombreDeLeds; i = i + 3)
              leds[indexPremiereLed + i + indexActif / 2] = (indexActif & 0b1) ? CRGB::Black : couleurAnimation;
            if ( ++indexActif >= 6) indexActif = 0;
            t0 = millis();
            if (effetImmediat) FastLED.show();
          }
          break;

        case CYCLECOULEUR:
          if (millis() - t0 >= periodeAnimation) {

            for (int i = 0; i < nombreDeLeds; i++) {
              uint8_t* c = roueCouleur(((i * 256 / nombreDeLeds) + indexActif) & 0xFF);
              leds[indexPremiereLed + i] = CRGB(*c, *(c + 1), *(c + 2));
            }
            if ( ++indexActif >= 256) indexActif = 0;

            t0 = millis();
            if (effetImmediat) FastLED.show();
          }
          break;
      }
    }


  private:
    CRGB *      leds;
    char        symbole;
    uint16_t    indexPremiereLed;     // N° Led de début
    uint16_t    nombreDeLeds;         // nombreDeLeds de Leds

    t_effet     effetLumineux;
    bool        effetImmediat;
    uint32_t    t0;                   // dernière animation
    uint32_t    couleurAnimation;
    uint32_t    periodeAnimation;
    uint16_t    indexActif;

    uint8_t * roueCouleur(byte position) {
      static uint8_t c[3];

      if (position < 85) {
        c[0] = position * 3;
        c[1] = 255 - position * 3;
        c[2] = 0;
      } else if (position < 170) {
        position -= 85;
        c[0] = 255 - position * 3;
        c[1] = 0;
        c[2] = position * 3;
      } else {
        position -= 170;
        c[0] = 0;
        c[1] = position * 3;
        c[2] = 255 - position * 3;
      }

      return c;
    }

};

template <uint8_t N>
class Phrase {
  public:
    Phrase(CRGB * l, uint16_t n) : leds(l), nombreDeLeds(n), nombreDeLettres(N) {};

    bool ajouterLettre(uint8_t position, char c, uint16_t p, uint16_t n) {
      if (position < nombreDeLettres) {
        lesLettres[position].begin(leds, c, p, n);
        return true;
      }
      return false;
    }

    Lettre* operator [] (uint8_t position) {
      if (position < nombreDeLettres) return &(lesLettres[position]);
      Serial.print(F("erreur - caractère ")); Serial.print(c); Serial.println(F(" inexistant."));
      return nullptr;
    }

    void toutEteindre(bool immediatement = true) {
      Serial.println(F("tout éteindre"));
      fill_solid(leds, nombreDeLeds, CRGB::Black);
      if (immediatement) FastLED.show();
    }

    void tick() {
      for (auto & l : lesLettres) l.tick();
    }

  private:
    CRGB *      leds;
    uint16_t    nombreDeLeds;
    uint8_t     nombreDeLettres;
    Lettre      lesLettres[N];
};

// fonctions générales hors classe


#endif