/* * ============================================================================= * MATRICE LED WS2812B 8 x 32 (256 pixels) — Arduino Uno + FastLED * Workshop « Vitrine pixels » * ============================================================================= * * Avant de téléverser ce programme, lisez le README.md de ce dossier * et vérifiez le branchement avec schema_branchement.svg. * * * CE QUI CHANGE PAR RAPPORT À LA MAQUETTE 3 x 3 * --------------------------------------------- * Sur la maquette 3 x 3, l'Arduino devait allumer lui-même chaque ligne, * très vite, l'une après l'autre (le multiplexage). Il fallait 12 fils. * * Ici, chaque LED (WS2812B) contient une minuscule puce électronique. * Les 256 LED sont reliées les unes aux autres en une longue CHAÎNE, * et l'Arduino ne leur parle que par UN SEUL FIL : le fil de données. * * Imaginez une file de 256 personnes qui se passent un paquet de lettres : * - l'Arduino envoie une longue suite de couleurs dans le fil ; * - la 1re LED garde la 1re couleur pour elle, et transmet le reste ; * - la 2e LED garde la couleur suivante, et transmet le reste ; * - … et ainsi de suite jusqu'à la 256e. * Puis chaque LED garde sa couleur affichée jusqu'au prochain envoi. * Plus besoin de balayage : chaque LED « se souvient » de sa couleur. * * L'avantage : un seul fil de données, et chaque LED peut avoir n'importe * laquelle de 16 millions de couleurs (256 niveaux de rouge × 256 de vert * × 256 de bleu), au lieu des 8 couleurs « tout ou rien » de la maquette. * * * BIBLIOTHÈQUE NÉCESSAIRE : FastLED * --------------------------------- * Dans l'IDE Arduino : menu Outils → Gérer les bibliothèques… * → chercher « FastLED » → Installer. * FastLED s'occupe d'envoyer les couleurs dans le fil, au bon rythme * (800 000 bits par seconde !). Nous, on se contente de remplir un tableau * de couleurs, puis de dire « envoie » avec FastLED.show(). * * * ORGANISATION DU PROGRAMME * ------------------------- * 1. Réglages : broche, taille, luminosité, limite de courant. * 2. Le tableau de couleurs (une case par LED). * 3. La fonction XY() : trouver le numéro d'une LED à partir de sa * position (x, y) sur la matrice. * 4. Les outils de dessin. * 5. Les animations. ← c'est ici que vous créez ! * 6. setup() et loop(). */ // On charge la bibliothèque FastLED. #include // ============================================================================= // 1. RÉGLAGES // ============================================================================= // Broche de l'Arduino reliée au fil VERT (DIN) de la matrice, // à travers une résistance de 330 Ω. #define PIN_DONNEES 6 // Taille de la matrice : 32 colonnes (largeur) × 8 lignes (hauteur). #define LARGEUR 32 #define HAUTEUR 8 #define NB_LEDS (LARGEUR * HAUTEUR) // = 256 // LUMINOSITÉ générale, de 0 (éteint) à 255 (maximum). // Ces LED sont TRÈS lumineuses : 40 suffit largement en intérieur, // et ça ménage les yeux… et l'alimentation. #define LUMINOSITE 40 // SÉCURITÉ : courant maximum autorisé, en milliampères (1000 mA = 1 A). // Une LED en blanc à fond consomme environ 60 mA. // 256 LED × 60 mA = 15 360 mA = plus de 15 A ! // L'alimentation d'établi en fournit beaucoup moins (souvent 3 à 5 A). // FastLED calcule à chaque image ce que les LED vont consommer et baisse // automatiquement la luminosité si on dépasse cette limite. // → Réglez cette valeur UN PEU EN DESSOUS de la limite de courant de // l'alimentation (ex. : alimentation limitée à 3 A → 2500 mA ici). #define COURANT_MAX_MA 2500 // ORIENTATION de la matrice. // Selon la façon dont vous posez la matrice, la LED n° 0 peut se trouver // dans n'importe quel coin. Lancez l'animation animTestCoins() : le pixel // ROUGE doit être en HAUT À GAUCHE et le VERT en HAUT À DROITE. // - Si rouge et vert sont inversés (gauche/droite) → INVERSER_X à true // - Si rouge et vert sont en bas au lieu d'en haut → INVERSER_Y à true // (true = vrai, false = faux) #define INVERSER_X false #define INVERSER_Y false // ============================================================================= // 2. LE TABLEAU DE COULEURS // ============================================================================= // // leds[] est une liste de 256 cases, une par LED, numérotées de 0 à 255 // dans l'ORDRE DE LA CHAÎNE (l'ordre dans lequel passe le fil de données). // // Chaque case est de type CRGB : une couleur faite de trois nombres, // rouge, vert, bleu, chacun de 0 à 255. // CRGB(255, 0, 0) = rouge vif // CRGB(255, 128, 0) = orange // CRGB(20, 0, 40) = violet sombre // CRGB::Black = noir (éteint) — FastLED connaît aussi les noms // CRGB::White, CRGB::Red, CRGB::Gold, CRGB::Pink… (noms anglais) // // Mémoire : 256 LED × 3 octets = 768 octets, sur les 2048 du Uno. CRGB leds[NB_LEDS]; // ============================================================================= // 3. DE LA POSITION (x, y) AU NUMÉRO DE LED // ============================================================================= // // Pour dessiner, on préfère penser en coordonnées, comme sur une feuille // quadrillée : // x = colonne, de 0 (à gauche) à 31 (à droite) // y = ligne, de 0 (en haut) à 7 (en bas) // // Mais les LED, elles, sont numérotées dans l'ordre de la chaîne. Et cette // chaîne fait des ZIGZAGS (on dit « en serpentin ») : elle descend la // 1re colonne, remonte la 2e, redescend la 3e, etc. // // x=0 x=1 x=2 x=3 // y=0 0 15 16 31 … // y=1 1 14 17 30 // y=2 2 13 18 29 // … … … … … // y=7 7 → 8 23 → 24 // ↓ ↑ ↓ ↑ // // Les colonnes PAIRES (0, 2, 4…) descendent, les colonnes IMPAIRES // (1, 3, 5…) remontent. La fonction XY() fait ce calcul pour nous. // (L'animation animOrdreDesLeds() permet de VOIR ce zigzag.) uint16_t XY(int x, int y) { // Si la matrice est posée « à l'envers », on retourne les coordonnées. if (INVERSER_X) x = (LARGEUR - 1) - x; if (INVERSER_Y) y = (HAUTEUR - 1) - y; if (x % 2 == 0) { // Colonne paire : la chaîne DESCEND. // On saute les x colonnes précédentes (x × 8 LED), puis on descend de y. return x * HAUTEUR + y; } else { // Colonne impaire : la chaîne REMONTE. // Même chose, mais on compte depuis le bas : (7 - y). return x * HAUTEUR + (HAUTEUR - 1 - y); } } // ============================================================================= // 4. LES OUTILS DE DESSIN // ============================================================================= // // Ces fonctions modifient le tableau leds[], mais n'allument rien tout de // suite ! Comme un peintre qui prépare sa toile en coulisses : on dessine // toute l'image, puis on la montre d'un coup avec FastLED.show(). // Colore un pixel. Si (x, y) est en dehors de la matrice, on ne fait rien : // on peut donc dessiner des formes qui « dépassent » sans risque // (pratique pour faire défiler une image qui entre et sort de l'écran). void pixel(int x, int y, CRGB couleur) { if (x >= 0 && x < LARGEUR && y >= 0 && y < HAUTEUR) { leds[XY(x, y)] = couleur; } } // Éteint toute la matrice (dans le tableau seulement). void effacer() { fill_solid(leds, NB_LEDS, CRGB::Black); // fill_solid = tout remplir } // Affiche réellement l'image : envoie les 256 couleurs dans le fil. // Ça prend environ 8 millisecondes. void montrer() { FastLED.show(); } // ============================================================================= // 5. LES ANIMATIONS // ============================================================================= // // Comme pour la maquette 3 x 3, une animation est une suite d'images : // effacer / dessiner → montrer() → attendre (delay) → recommencer. // // À VOUS DE JOUER : copiez une animation, renommez-la, modifiez-la, // et ajoutez son nom dans loop() tout en bas. // --- Animation 1 : test des coins (orientation) --- // Un pixel de couleur dans chaque coin : // ROUGE en haut à gauche VERT en haut à droite // BLEU en bas à gauche BLANC en bas à droite // Si ce n'est pas le cas, changez INVERSER_X / INVERSER_Y (partie 1). void animTestCoins() { effacer(); pixel(0, 0, CRGB::Red); pixel(LARGEUR - 1, 0, CRGB::Green); pixel(0, HAUTEUR - 1, CRGB::Blue); pixel(LARGEUR - 1, HAUTEUR - 1, CRGB::White); montrer(); delay(3000); // 3 secondes pour regarder } // --- Animation 2 : l'ordre de la chaîne --- // Allume les LED une par une, dans l'ordre où le signal les traverse // (n° 0, 1, 2… 255), SANS passer par XY(). On voit le serpentin ! void animOrdreDesLeds() { effacer(); for (int i = 0; i < NB_LEDS; i++) { leds[i] = CRGB::Orange; // la tête du serpent montrer(); leds[i] = CRGB(10, 3, 0); // laisse une trace très faible delay(15); } } // --- Animation 3 : arc-en-ciel qui défile --- // Ici on utilise une autre façon de décrire une couleur : CHSV. // H (hue, la TEINTE) : de 0 à 255, fait le tour de l'arc-en-ciel // 0 = rouge, 32 = orange, 64 = jaune, 96 = vert, 128 = cyan, // 160 = bleu, 192 = violet, 224 = rose… puis retour au rouge. // S (saturation) : 0 = gris/blanc, 255 = couleur pure. // V (valeur, la LUMINOSITÉ) : 0 = noir, 255 = plein. // C'est souvent plus intuitif que RGB pour créer des dégradés. void animArcEnCiel() { for (int temps = 0; temps < 256; temps++) { for (int x = 0; x < LARGEUR; x++) { for (int y = 0; y < HAUTEUR; y++) { // La teinte dépend de la colonne (x × 8) et du temps qui passe. // Un byte revient tout seul à 0 après 255 : la teinte boucle. byte teinte = x * 8 + temps * 2; pixel(x, y, CHSV(teinte, 255, 255)); } } montrer(); delay(20); } } // --- Animation 4 : la pluie --- // Des gouttes tombent au hasard et laissent une traînée qui s'estompe. void animPluie() { for (int image = 0; image < 200; image++) { // fadeToBlackBy = « assombrir un peu » toutes les LED (ici de 25 % // environ, 64 sur 256). Les gouttes laissent ainsi une traînée. fadeToBlackBy(leds, NB_LEDS, 64); // Faire descendre toute l'image d'une ligne : on recopie chaque ligne // dans celle du dessous, en partant du bas pour ne rien écraser. for (int y = HAUTEUR - 1; y > 0; y--) { for (int x = 0; x < LARGEUR; x++) { leds[XY(x, y)] = leds[XY(x, y - 1)]; } } // La ligne du haut s'éteint… for (int x = 0; x < LARGEUR; x++) { pixel(x, 0, CRGB::Black); } // … et de nouvelles gouttes apparaissent au hasard. // random(LARGEUR) = une colonne au hasard entre 0 et 31. for (int n = 0; n < 3; n++) { pixel(random(LARGEUR), 0, CRGB(0, 120, 255)); // bleu clair } montrer(); delay(60); } } // --- Animation 5 : un dessin en pixel art qui défile --- // Le dessin est écrit comme du texte : une ligne de caractères par ligne // de pixels. Chaque lettre est une couleur (voir couleurDeLettre()), // et le point « . » est un pixel éteint. // → Dessinez le vôtre ! Gardez 8 lignes, et la même largeur partout. #define DESSIN_LARGEUR 9 const char* dessin[HAUTEUR] = { "..RR.RR..", ".RBRRRRR.", ".RRRRRRR.", ".RRRRRRR.", "..RRRRR..", "...RRR...", "....R....", ".........", }; // Traduit une lettre du dessin en couleur. CRGB couleurDeLettre(char lettre) { switch (lettre) { case 'R': return CRGB::Red; case 'O': return CRGB::Orange; case 'J': return CRGB::Yellow; case 'V': return CRGB::Green; case 'C': return CRGB::Cyan; case 'B': return CRGB::White; // B comme blanc case 'L': return CRGB::Blue; // L comme bLeu (B est déjà pris) case 'M': return CRGB::Magenta; case 'P': return CRGB::DeepPink; // P comme rose Pink default: return CRGB::Black; // « . » et tout le reste = éteint } } // Dessine le dessin avec son coin haut-gauche en (departX, 0). void dessinerDessin(int departX) { for (int y = 0; y < HAUTEUR; y++) { for (int x = 0; x < DESSIN_LARGEUR; x++) { char lettre = dessin[y][x]; // la lettre en ligne y, colonne x if (lettre != '.') { pixel(departX + x, y, couleurDeLettre(lettre)); } } } } void animDessinQuiDefile() { // Le dessin entre par la droite (x = 32) et sort par la gauche // (x = -9, complètement hors de l'écran). for (int x = LARGEUR; x >= -DESSIN_LARGEUR; x--) { effacer(); dessinerDessin(x); montrer(); delay(70); } } // --- Animation 6 : étincelles --- // Des pixels de couleur au hasard s'allument puis s'éteignent doucement. void animEtincelles() { for (int image = 0; image < 300; image++) { fadeToBlackBy(leds, NB_LEDS, 20); // tout s'estompe doucement // random(256) = une teinte au hasard. pixel(random(LARGEUR), random(HAUTEUR), CHSV(random(256), 200, 255)); montrer(); delay(15); } } // ============================================================================= // 6. SETUP ET LOOP // ============================================================================= void setup() { // On présente la matrice à FastLED : // - WS2812B : le modèle de LED // - PIN_DONNEES : la broche du fil de données // - GRB : l'ordre des couleurs attendu par ces LED (Vert, Rouge, Bleu // — eh oui, pas RGB ! Si vos rouges sortent verts, c'est ici.) FastLED.addLeds(leds, NB_LEDS); FastLED.setBrightness(LUMINOSITE); FastLED.setMaxPowerInVoltsAndMilliamps(5, COURANT_MAX_MA); // Graine du hasard (voir la maquette 3 x 3 : on lit le bruit // électrique d'une broche analogique non branchée). randomSeed(analogRead(A0)); effacer(); montrer(); } // La « playlist » des animations, jouée en boucle. // Mettez // devant une ligne pour sauter une animation. void loop() { animTestCoins(); // 1. orientation (à retirer une fois réglée) animOrdreDesLeds(); // 2. le serpentin de la chaîne animArcEnCiel(); // 3. arc-en-ciel animPluie(); // 4. pluie animDessinQuiDefile(); // 5. pixel art animEtincelles(); // 6. étincelles }