"Cube" 3D 3x3x7 à LED sur Arduino - partie 3 : Le code

Comme prévu, voici la troisième et dernière partie, le code Arduino de la guirlande 3D. A noter qu'il est possible de télécharger l'intégralité du code en fin d'article, avec d'avantage de commentaires pour une meilleure compréhension.

Donc, pour démarrer, la déclaration des constantes et variables :

// CONSTANTES
const int fps = 20;
const int milli_fps = 1000 / fps; // durée d'une frame en milliseconde
const int etages = 7;
const int colonnes = 9;
const int cote = sqrt(colonnes);
const int persiste = 2; //  délai d'affichage en millisecondes
const int delai_defaut = 1; // calcul la frame de l'animation toutes les x frames (à toutes les frames par défaut)
const char colonne[] = {5, 6, 10, 4, 8, 7, 9, 2, 3}; // défini les 9 PINS colonnes (+)
const char etage[] = {A1, A2, A3, A5, A4, 0, 1}; // défini les 7 PINS étages (-)
const int serpent_tour[8] = {0,1,2,5,8,7,6,3};
// VARIABLES
unsigned long last_millis = millis(); // sert à définir à quelle frame nous sommes
unsigned long programme_millis = millis(); // gère le temps pour permettre de changer d'animation
int programme_count = 0; // numéro en cours du programme
int programme_delai = 10; // durée en seconde d'une animation
int frame = 0;
int niveau = 0;
int led = 0;
bool grille[etages][colonnes] = { {0,0,0, 0,0,0, 0,0,0} };
// VARIABLES EFFETS (correspond au variables pouvant être utilisées par les différentes fonctions)
int var_init = 0; // initialisation de l'effet
int var_tmp = 0;
int var_rnd = 0;
int var_etage = 0;
int var_1 = 0;
int var_2 = 0;
int var_3 = 0;
int var_y1 = 0;
int var_y2 = 0;
int var_y3 = 0;
int var_colonne = 0;
int var_frame = 0;
int var_delai = 1;
int var_delai_count = 0;
int var_array[5];
int var_array8[8];
int var_array9[9];
int var_colonnes[cote];
int var_etages[cote];
int var_direction;


Les 3 fonctions principales qui seront utilisées pour modifier l'état des LED :

// ------------------------------------- etage (traite l'état des étages)
void etage_maj (int num = -1, bool etat = 0) { // étage à dés/activer (defaut rien), etat (defaut désactive)
    if (num > -1) // s'il y a un étage à activer
        digitalWrite(etage[num], etat == 1 ? LOW : HIGH); // dés/active l'étage
    else { // sinon
        for (int e = 0; e < etages; e++) { // désactive tous les étages
            digitalWrite(etage[e], HIGH);
            }
        }
    };
// ------------------------------------- de même pour les colonne
void colonne_maj (int num = -1, bool etat = 0) {
    if (num > -1)
            digitalWrite(colonne[num], etat == 1 ? HIGH : LOW); // inverse
    else {
        for (int c = 0; c < colonnes; c++) {
            digitalWrite(colonne[c], LOW);
            }
        }
    };
// ------------------------------------- active les leds pour afficher la frame, traitement par ETAGE
void affiche_frame () {
for (int e = 0; e < etages; e++) { // sur 7 étages (0-6)
    etage_maj(e, 1); // allumer l'étage e
    for (int c = 0; c < colonnes; c++) { // pour les 9 colonnes
        if (grille[e][c] == 1) { // si l'une est à activer
            colonne_maj(c, 1); // allume la colonne c
            }
        }
        delay(persiste); // pause persistance rétinienne
        etage_maj(e); // eteindre l'étage e
        for (int c = 0; c < colonnes; c++) { // désactive toutes les colonnes
                colonne_maj(c);
            }
        }
    };


Le Setup qui se contente d'initialiser et désactiver l'état de toutes les LED :

void setup () {
    for (int e = 0; e < etages; e++) {
        pinMode(etage[e], OUTPUT); // initialise chaque étage (-) 0 à 6
        etage_maj(e); // désactive
        }
    for (int c = 0; c < colonnes; c++) {
        pinMode(colonne[c], INPUT); // initialise chaque colonne (+) 0 à 8
        colonne_maj(c); // désactive
        }
    }


La boucle qui va activer telle ou telle fonction. Il s'agit en gros de la programmation des différentes animations :

void loop () {
    if (millis() - last_millis >= milli_fps) {
        if (millis() - programme_millis >= programme_delai * 1000) {
            programme_millis = millis();
            programme_count++;
            var_init = 0; // réinitialise la variable pour indiquer à la fonction si une nouvelle fonction commence ou si une fonction continue d'être traitée
            }
        switch(programme_count) { // programmation à moduler en fonction des envies
            case 0:
                programme_count = 7;
            break;
            case 7:
                programme_delai = 5; // change la durée d'affichage de l'animation en fonction de cette dernière. Peut être fixe ou aléatoire
                anim_etage(1, 1); // inverse
            break;
            case 8:
                programme_delai = 15;
                anim_sin(3);
            break;
            case 9:
                programme_delai = 5;
                anim_etage(2);
            break;
            case 10:                
                programme_delai = 10;
                anim_point_aleatoire(3, 2); // nombre de point alétoires
            break;
            case 11:
                programme_delai = 5;
                anim_point_aleatoire(1, 5);    
            break;
            case 12:
                programme_delai = 10;
                anim_point_aleatoire(10, 18);    
            break;
            case 13:
                programme_delai = 7;
                anim_empli_rand(1, 0);
            break;
            case 14:
                programme_delai = 2;
                anim_vide_rand(1);
            break;
            case 15:
                anim_empli_rand(1, 0); // 0 = ne vide pas le contenu au départ
            break;
            case 16:
                anim_vide_rand(1);
            break;
            case 17:
                anim_empli_rand(1, 0);
            break;
            case 18:
                programme_delai = 7;
                anim_vide_rand(1);
            break;
            case 19:
                programme_count++;
            break;
            case 20:
                programme_delai = 10;
                anim_rand_haut_bas(3);
            break;
            case 21:
                anim_point_aleatoire(1, 5);    
            break;
            case 22:
                programme_delai = 5;
                anim_tourne(3, 3); //    3 colonnes
            break;
            case 23:
                programme_count++;
            break;
            case 24:
                programme_delai = 10;
                anim_pluie(2); // de bas en haut                
            break;
            case 25:
                programme_delai = 15;
                anim_points_haut_bas(1);
            break;
            case 26:
                programme_delai = 5;
                anim_clignote_plein_vide(10);
            break;
            default:
                programme_delai = 10;
                programme_count = 0;
            }
        var_frame++; // frame suivante
        if (var_frame >= fps) // dernière frame
            var_frame = 0; // revient à la première
        last_millis = millis(); // raffraichit time pour la prochaine frame
        }
    affiche_frame(); // à toutes les boucles, active, désactive sans cesse les leds de la frame (array grille)
    }


Enfin, les différentes fonctions d'animations avec une petite description pour chacune. La variable delai indique le temps d'affichage d'une frame de l'animation en fonction des frames à afficher par seconde (par défaut à 2, soit 5 par seconde)


Fait clignoter l'ensemble des LED, jour, nuit, jour, nuit... :

void anim_clignote_plein_vide (int delai = delai_defaut) {
    
    if (var_init == 0) {
        var_init = 1;
        var_delai = delai;
        var_delai_count = 0;
        var_1 = 0;
        for (int e = 0; e < etages; e++) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = {0};
                }
            }
        }
        
    if (var_delai_count >= var_delai) {
        for (int e = 0; e < etages; e++) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = var_1 == 1 ? 1 : 0; // active ou desactive
                }
            }
        var_1 = var_1 == 1 ? 0 : 1; // change var plein -> vide, vide -> plein pour next
        var_delai_count = 0; // remet à zéro, fin du code joué toutes les x frames
        }
    
    var_delai_count++;
    };


Eclaire une étage à la fois de haut en bas, ou inversement :

void anim_etage (int delai = delai_defaut, bool inverse = 0) {
    
    if (var_init == 0) {
        var_init = 1;
        var_etage = 0;
        if (inverse == 1)
            var_etage = colonnes-1;
        var_delai = delai;
        var_delai_count = 0;
        }
        
    if (var_delai_count >= var_delai) {
        for (int e = 0; e < etages; e++) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = 0; // désactive
                }
            }
        for (int c = 0; c < colonnes; c++) {
            grille[var_etage][c] = 1; // active toutes
            }
        if (inverse == 1)
            var_etage--;
        else
            var_etage++; // étage suivant (vers le bas)
        
        if (inverse == 0 && var_etage >= etages) {
            var_etage = 0; // boucle l'animation
            }
        else if (inverse == 1 && var_etage < 0) {
            var_etage = colonnes-1;
            }
        var_delai_count = 0;
        }
    var_delai_count++;

    };


Neuf points se déplacent aléatoirement et tour à tour de haut en bas :

void anim_points_haut_bas (int delai = delai_defaut) {

    if (var_init == 0) {
        var_init = 1;
        var_etage = var_colonne = var_direction = 0;
        var_delai = delai;
        for (int e = 0; e < etages; e++) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = 0;
                }
            }
        for (int c = 0; c < colonnes; c++) {
            var_1 = random(0, 2);
            var_1 = var_1 == 1 ? etages-1 : 0; // si 1, etage max
            var_array9[c] = var_1; // renseigne l'étage de la colonne 0 (haut) ou 6 (bas)
            grille[var_1][c] = 1; // met à jour la grille
            }
        }
        
    if (var_delai_count >= var_delai) {
        if (var_direction == 0) { // 0 = pas de déplacement de point en cours
            var_colonne = random(0, colonnes); // choisi une des 9 colonnes
            var_etage = var_array9[var_colonne]; // récupère son étage
            var_direction = var_etage == 0 ? 1 : -1; // 1 descend, -1 monte en conséquence
            }
        else { // sinon, déplace -1 bas ou +1 haut
            grille[var_etage][var_colonne] = 0; // eteint la précédente
            if (var_etage+var_direction >= 0 && var_etage+var_direction <= etages-1) { // s'il peut encore se déplacer
                var_etage += var_direction; // passe à 'étage suivant
                var_array9[var_colonne] = var_etage; // change l'étage du point fini d'etre déplacé 0 haut ou 6 bas
                }
            else // sinon, arrivé tout en bas/haut
                var_direction = 0; // remet à zéro pour passer à un autre point
            grille[var_etage][var_colonne] = 1; // allume la suivante (ou rallume la derniere si au bout)
            }
        var_delai_count = 0;
        }
    
    var_delai_count++;
    };


Trois points se suivent et se déplacent alétoirement le long des colonnes extérieures à la manière du fameux jeu Nokia Snake :

void anim_serpent (int delai = delai_defaut) {

    if (var_init == 0) {
        var_init = 1;
        var_delai = delai;
        for (int e = 0; e < etages; e++) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = 0;
                }
            }
        var_tmp = random(0, colonnes); // un point au hasard
        var_3 = var_2 = var_1 = var_tmp;
        var_y3 = var_y2 = var_y1 = random(0, etages);        
        var_direction = random(1, 5);
        }

    if (var_delai_count >= var_delai) {
        var_rnd = random(0, 4); // 1 fois sur 4
        if (var_rnd == 0) {
            var_tmp = random(1, 5); // direction random
            if ((var_tmp - 2 != var_direction) || (var_tmp + 2 != var_direction)) { // vérifie qu'il ne s'agit pas d'un demi-tour
                var_direction = var_tmp;
                }
            }
        grille[var_y3][serpent_tour[var_3]] = grille[var_y2][serpent_tour[var_2]] = grille[var_y1][serpent_tour[var_1]] = 0;
        var_3 = var_2;
        var_2 = var_1;
        var_y3 = var_y2;
        var_y2 = var_y1;
        if (var_direction == 1) {
            var_tmp--;
            if (var_tmp < 0)
                var_tmp = 7;
            var_1 = var_tmp;
            }
        else if (var_direction == 3) {
            var_tmp++;
            if (var_tmp > 7)
                var_tmp = 0;
            var_1 = var_tmp;
            }
        else if (var_direction == 2) { // étage haut
            var_y1--;
            if (var_y1 < 0)
                var_y1 = etages - 1;
            }
        else if (var_direction == 4) { // étage bas
            var_y1++;
            if (var_y1 >= etages)
                var_y1 = 0;
            }
        grille[var_y3][serpent_tour[var_3]] = grille[var_y2][serpent_tour[var_2]] = grille[var_y1][serpent_tour[var_1]] = 1;
        var_delai_count = 0;
        }
    
    var_delai_count++;
    };


Des points apparaissent alétoirement et tombent tour à tour comme une pluie :

void anim_pluie (int delai = delai_defaut, bool inverse = 0) {
    
    if (var_init == 0) {
        var_init = 1;
        var_etage = var_colonne = var_direction = 0;
        var_delai = delai;
        for (int e = 0; e < etages; e++) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = 0;
                var_array9[c] = 0;
                if (inverse == 1)
                    var_array9[c] = etages-1;
                }
            }
        }
        
    if (var_delai_count >= var_delai) {
        if (var_direction == 0) {
            var_colonne = random(0, colonnes);
            var_etage = var_array9[var_colonne];
            var_direction = 1;
            if (inverse == 1)
                var_direction = -1;    
            grille[var_etage][var_colonne] = 1;
            }
        else {
                grille[var_etage][var_colonne] = 0;    
            if (var_etage+var_direction >= 0 && var_etage+var_direction <= etages-1) {
                var_etage += var_direction;
                grille[var_etage][var_colonne] = 1;    
                }
            else
                var_direction = 0;
            }
        var_delai_count = 0;
        }
    
    var_delai_count++;
    };


Une à plusieurs colonnes extérieurs s'allument en tournant :

void anim_tourne (int delai = delai_defaut, int longueur = 1) {
    
    if (var_init == 0) {
        var_init = 1;
        var_delai = delai;
        for (int e = 0; e < etages; e++) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = 0;
                }
            }
        var_1 = 0; // premiere sequence relative à serpent_tour
        var_2 = 1;
        var_3 = 2;
        }
    
    if (var_delai_count >= var_delai) {
        for (int e = 0; e < etages; e++) {
            grille[e][serpent_tour[var_1]] = 0;
            if (longueur > 1)
                grille[e][serpent_tour[var_2]] = 0;
            else if (longueur > 2)
                grille[e][serpent_tour[var_3]] = 0;
            }
        var_1++;
        if (var_1 > 7) // si dépasse array8
            var_1 = 0;
        var_2++;
        if (var_2 > 7)
            var_2 = 0;
        var_3++;
        if (var_3 > 7)
            var_3 = 0;
        for (int e = 0; e < etages; e++) {
            grille[e][serpent_tour[var_1]] = 1;
            if (longueur > 1)
                grille[e][serpent_tour[var_2]] = 1;
            else if (longueur > 2)
                grille[e][serpent_tour[var_3]] = 1;
            }        
        var_delai_count = 0;
        }
    
    var_delai_count++;
    };


Allume des LED aléatoirement :

void anim_empli_rand (int delai = delai_defaut, bool vide = 1) {
    
    if (var_init == 0) {
        var_init = 1;
        var_delai = delai;
        if (vide == 1) {
            for (int e = 0; e < etages; e++) {
                for (int c = 0; c < colonnes; c++) {
                    grille[e][c] = 0;
                    }
                }
            }
        }
    
    if (var_delai_count >= var_delai) {
        grille[random(0, etages)][random(0, colonnes)] = 1;
        var_delai_count = 0;
        }
    
    var_delai_count++;
    };


Eteint des LED aléatoirement :

void anim_vide_rand (int delai = delai_defaut) {
    
    if (var_delai_count >= var_delai) {
        grille[random(0, etages)][random(0, colonnes)] = var_delai_count = 0;
        }
        
    var_delai_count++;
    };


Effet de pluie :

void anim_rand_haut_bas (int delai = delai_defaut) {
    
    if (var_init == 0) {
        var_init = 1;
        var_etage = var_colonne = var_direction = 0;
        var_delai = delai;
        for (int e = 0; e < etages; e++) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = var_array9[c] = 0;
                }
            }
        }

    if (var_delai_count >= var_delai) {
        for (int e = etages-1; e >= 0; e--) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = e == 0 ? 0 : grille[e-1][c]; // déplace vers le bas
                }
            }
        grille[0][random(0, colonnes)] = 1;
        var_delai_count = 0;
        }
    var_delai_count++;
    
    };


Effet de vague diagonale défilante :

void anim_sin (int delai = delai_defaut) {
    
    if (var_init == 0) {
        var_init = 1;
        var_etage = var_colonne = var_direction = 0;
        var_delai = delai;
        
        for (int e = 0; e < etages; e++) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = var_array9[c] = 0;
                }
            }
        var_1 = 0; // etapes
        }
    
    if (var_delai_count >= var_delai) {
        for (int e = etages-1; e >= 0; e--) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = e == 0 ? 0 : grille[e-1][c];
                }
            }
        for (int c = 0; c < colonnes; c++) {
                grille[0][c] = 0;
                }
        switch(var_1) {
            case 0: case 8:
                grille[0][0] = 1;
            break;
            case 1: case 7:
                grille[0][1] = grille[0][3] = 1;
            break;
            case 2: case 6:
                grille[0][2] = grille[0][4] = grille[0][6] = 1;
            break;
            case 3: case 5:
                grille[0][5] = grille[0][7] = 1;
            break;
            case 4:
                grille[0][8] = 1;
            break;
            default:
                var_1 = 0;
            }
        var_1++;
        if (var_1 >= 8)
            var_1 = 0;
            
        var_delai_count = 0;
        }
    var_delai_count++;
    };


Allume et éteint une ou plusieurs LED aléatoirement :

void anim_point_aleatoire (int delai = delai_defaut, int nombre = 1) {
    
    if (var_init == 0) {
        var_init = 1;
        var_delai = delai;
        }
    
    if (var_delai_count >= var_delai) {
        for (int e = 0; e < etages; e++) {
            for (int c = 0; c < colonnes; c++) {
                grille[e][c] = 0;
                }
            }
        for (int e = 0; e < nombre; e++) {
            grille[random(0, etages)][random(0, colonnes)] = 1;
            }
        var_delai_count = 0;
        }
    
    var_delai_count++;
    }


Beaucoup d'autres animations sont possibles (tetris, lettrage, effets de scintillement en jouant sur la durée d'affichage etc), mais c'est déjà une base suffisante pour commencer à égayer vos apéros !

Télécharger le code commenté


Et pour conclure, une vidéo en situation réelle avec le programmation en Loop, qui permet de visualiser le comportement des différentes fonctions :

Voila pour ce premier projet qui a été mené à terme et sans trop d'accroc.

Pour le prochain, je souhaite m'attaquer à un coin pusher, dit pousse-pièce ou cascade ou plus vulgairement : une machine à sous de forain, mais de comptoir et toujours original.
Projet qui fera certainement l'objet de plusieurs articles s'il se concrétise... à suivre !

 

Partie 1 : Le prototype
Partie 2 : Le premier modèle
Partie 3 : Le code (vous êtes ici)

 

"Cube" 3D 3x3x7 à LED sur Arduino - partie 2 : Premier modèle

Déçu du rendu avec les LED disponibles sur le prototype, et vu que je pouvais aller plus loin, j'ai investi dans des LED 5mm bleues.

Objectif

  • Les bonnes LED et mieux alignées et orientées
  • La guirlande pourra être rétractée pour occuper moins de place
  • de fait, les jonctions entre les étages seront souples, mais tendues une fois ouvert
  • La partie electronique sera en bas (pas besoin de poid et ports plus accessibles)
  • EDIT : la partie électronique sera en faite en haut : La guirlande pourrait être utilisée dans un sens comme dans l'autre, mais avoir la partie large en haut rendra mieux

Matériel

  • 1 Arduino Nano ou équivalent (ici de marque Izokee)
  • 100 LED (63 pour 7 étages, 91 pour 9)
  • 9 résistances, ce coup-ci le jus passera par les colonnes
  • quelques cables pour faciliter les branchements
  • un cul de peinture noire en bombe ou en pot
  • EDIT : une batterie "de secours" USB pour alimenter le tout

J'ai pris 2 modèles de LED bleues 5mm, 120° pour voir ce qui correspondrait le mieux :

  • têtes plates, 800 mcd, 3,2 V à 25 mA (20 avec l'Arduino) (bleues sur la photo)
  • têtes rondes, 200 mcd, entre 3,2 et 3,4 V à 20 mA (blanches sur la photo)

J'ignore directement les 1500 mcd et plus ; le but étant d'avoir des points lumineux sans être aveuglé.

Et ici, les LED bleues sont parfaites, diffuses, elles font un beau point et sont visibles même de jour, sans éblouir.

Assemblage

Avec des bouts de planches et 2 demi-tasseaux je découpe les pièces qui me semblent nécessaires à l'aide de mes croquis clairs et limpides

collage, ponçage et premiers trous des éléments haut et bas.

Arrivé de dernière minute : la batterie USB, pour briller en extérieur.

Une petite touche de peinture et les éléments sont prêts à être assemblés.

Ce coup-ci je me suis fait un gabarit pour la matrice des étages afin de positionner les LED équitablement.

Pour les colonnes, j'ai utilisé des brins de cuivre d'un cable Hi-Fi, par 3 (pas comme sur la photo).

Une centaine de soudures plus tard, je peux ajouter la carte et effectuer les branchements.

Je suis très mauvais en soudure (niveau collège en techno). du coup c'est du paté par endroit, mais on me dit dans l'oreillette, qu'on s'en bat les couilles, parce qu'une fois assemblé :

Ouverture de la guirlande/lampion 3D :

Côté code, je défini l'ordre des colonnes. Ici du fond vers l'avant, de gauche à droite.
const char colonne[] = {5, 6, 10, 4, 8, 7, 9, 2, 3};
Les étages, de haut en bas. Les A6, A7 n'éclairent pas mes derniers niveaux, j'ai donc utiliser les D0 et D1.
const char etage[] = {A1, A2, A3, A5, A4, 0, 1};
J'ai simplement repris le code du proto en modifiant les pins pour vérifier le bon fonctionnement.

Dans le noir :

Le résultat est beaucoup mieux. A suivre, la partie code !

 

Partie 1 : Le prototype
Partie 2 : Le premier modèle (vous êtes ici)
Partie 3 : Le code
 

"Cube" 3D 3x3x7 à LED sur Arduino - partie 1 : Prototype

Pour prendre en main cette carte, j'ai glané quelques idées, je cherchais juste un truc sympa à réaliser. Et si comme moi vous avez vu des démos de cubes 8x8x8 et plus, comme celles-ci :

C'est parfaitement stylé et inutile !

De nombreux tutoriels proposent la création de ces cubes en 2, 3 et 4^3 avec ce modèle de carte ou équivalent, comme ici ou . Un nombre plus élevé de LED peut être géré, mais inclue la gestion de pièces supplémentaires et une plus grosse expérience. Exemple en 8x8x8.

Objectif

L'idée est de faire un premier jet sans moyen, à suspendre comme un lampion.
avec 20 pins, je soustrais le nombre de colonnes pour obtenir le nombre d'étages possible : 9 colonnes, 11 étages ; 12 colonnes, 8 étages ; 16 colonnes, 4 étages.
Je souhaite quand même garder de la place pour un futur bouton ou autre. Aussi, il devra être plus grand qu'une enceinte PC : en effet, la plupart des modèles produit sont "de bureau" et font cheap au final

3x3 est idéal pour moi. A 20mA par LED et par étage, je ne dépasse théoriquement pas les 190mA. Pour d'avantage il faudra peut-être procéder à un affichage LED à LED/groupées ou d'une alimentation externe.
J'ai donc opté pour un format atypique, un Parallélépipède 3x3x7 que je trouve plus intéressant à exploiter qu'un 4x4x4

Matériel

  • Le nouveau joujou : 1 Arduino Uno
  • et déja sous la main : du fil souple et rigide
  • 7 résistances, une par étage
  • 3x3x7 leds pourraves
  • deux bouts de carton

Assemblage

L'assemblage des étages se fait un peu à l'oeil, à l'aide de tiges presque droites.
Pas grand chose à ajouter, tous les tutos vus plus haut expliquent globalement la même chose, si ce n'est qu'ils se contentent souvent des pattes des LED.

Ici, les étages sont simplement entrecrochés pour pouvoir être démontés ou en ajouter. L'écart entre les LED est de 6 cm. Normalement les colonnes devaient finir par des poids, pour tendre l'ensemble de la structure, d'où l'aspect lendemain de cuite.

Vient la partie code. Pour simplement tester, voici un petit code qui va allumer une à une les LED, une fois les étages et colonnes définis.

const int colonnes = 9;
const int etages = 7;
const char colonne[] = {A2, A1, A0, 6, 7, 10, 5, 8, 9};
const char etage[] = {2, 3, 4, 0, 1, A4, A5};

void to_led (int num = 5) {  // milieu par defaut
	for (int t = 0; t < colonnes; t++) {
		digitalWrite(colonne[t], LOW);
		}
	digitalWrite(num, HIGH);
	};

void to_etage (int num = 0) {
	for (int t = 0; t < etages; t++) {
		digitalWrite(etage[t], HIGH);
		}
	digitalWrite(num, LOW);
	};

void setup () {
	for (int t = 0; t < etages; t++) {
		pinMode(etage[t], OUTPUT);
		digitalWrite(etage[t], HIGH);
		}
	for (int t = 0; t < colonnes; t++) {
		pinMode(colonne[t], INPUT);
		digitalWrite(colonne[t], LOW);
		}
	}

void loop () {
	for (int t = 0; t < etages; t++) {
		to_etage(etage[t]);
		for (int u = 0; u < colonnes; u++) {
			to_led(colonne[u]);
			delay(40);
			}
		}
	}

Et voila ! C'est bien de la daube... la caméra plus la faible luminosité des LED, il faut se concentrer pour les voir, mais ça donne un aperçu et surtout, ça fonctionne.

A suivre, le premier modèle !

 

Partie 1 : Le prototype (vous êtes ici)
Partie 2 : Le premier modèle
Partie 3 : Le code

plan dynamique à partir d'une photo

Je vous propose étape par étape la réalisation d'un plan vidéo animé à partir d'une simple photo. Cet effet que l'on retrouve dans de nombreux documentaires pouvant apporter du dynamisme et de la vie à des images statiques, tel que des photos anciennes, tableaux de maîtres et autres...

Le choix de la photo

J'ai ici récupéré une photo de 1897, prise lors de la célèbre ruée vers l'or, le long du Klondike, estampillée Domaine Public. L'image est de très moyenne qualité, mais suffisante pour l'intégration dans une vidéo documentaire. Le format de la photo se prêtera à un travelling vertical.

Le découpage des plans

Il convient de déterminer la position des éléments dans l'espace en commençant évidemment par les premiers. Dans mon exemple, je n'irais pas jusqu'à évider la flaque d'eau (qui semble surtout refléter le ciel entouré de la masse de gens), mais il est tout à fait faisable d'y apporter un effet ondulé qui apportera toujours plus de réalisme. Le détourage reste sommaire, tant la photo manque de netteté. J'ai volontairement multiplié les plans, mais on aurait pu se contenter de trois quatre.

 

L'extension des fonds

Pour permettre le glissement des différents éléments lors du montage vidéo, il est essentiel de couvrir tous les détourages inférieurs aux premiers plans, voir d'avantage. Ici le sol boueux et le ciel, à coup de tampon, pansement ou autre, faites comme vous voulez ! On pourra y revenir ultérieurement suivant le travelling pour ne travailler que les zones nécessaires. Les premiers plans glisseront d'avantage que ceux du fond, effet de perspective oblige. Ainsi, plus nous reculons dans les plans, moins ces retouches seront nécessaires.

Nous verrons plus bas qu'en faisant partir ces élements du haut vers le bas, nous nous économiserons pas mal de travail en excluant les zones latérales. Exemple avec la façade de l'hôtel, ou j'ai entre autre utilisé le point de fuite pour répliquer la fenêtre. Sans être baclé, nous ne nous attarderons par sur les détails. La séquence ne faisant que quelques secondes, l'oeil n'aura pas vraiment le temps d'en apprécier tous les détails. L'objectif étant de ne laisser aucune zone vide.

J'intègre également aux éléments qui en nécessitent, le sol à part, en y appliquant un masque aux larges dégradés : il est essentiel que les objets et personnages n'ai pas cette illusion de flotter lors de leurs déplacements et donc, de les ancrer au sol.

Ceci fait, le rendu des différents calques superposés est évidemment très proche de l'original à quelques détails près.
Je peux maintenant séparer mes 9 plans pour pouvoir les intégrer au montage vidéo. J'utilise ici Vegas, mais cela reste appliquable à votre logiciel favori.

Le montage vidéo

Importés et superposés, j'applique un déplacement à chacun du haut vers le bas. Comme dit plus haut, les premiers plans devraient avoir un déplacement plus prononcé que ceux de derrière, le fond restant fixe. Dans mon exemple, l'écart est régulier et le résultat reste correct.

Dans le même temps, la caméra fera défiler l'ensemble de bas en haut comme pour dynamiser d'avantage et justifier tout ça. On aurait également pu ajouter un léger dé-zoom, un effet sépia... à votre bon coeur m'sieurs, dames.

Le rendu

Un autre exemple