Files
2026-10-08 11:03:41 +02:00

388 lines
14 KiB
Arduino
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* =============================================================================
* 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 <FastLED.h>
// =============================================================================
// 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<WS2812B, PIN_DONNEES, GRB>(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
}