Led multi-modes - Contrôle tactile

De Les Fabriques du Ponant
Révision datée du 13 mai 2024 à 13:47 par François-Régis (discussion | contributions) (Choix du Wemos)
Aller à : navigation, rechercher

Description

Contrôler une led RGB, un led ring ou ici un ruban led (led strip), grâce à un bouton tactile. A chaque contact détecté par le bouton tactile, le mode de contrôle des led est changé.

Liste du matériel

Électronique

  • 1 x Module Wifi D1 Mini Wemos ou 1 x Arduino UNO ou équivalent
  • 1 x Ruban de LED 1 mètre/60 LED
  • Câbles Dupont M-F et M-M
  • 1 x Bouton tactile TTP223
  • 1 x mini breadboard

Coût

Pour l'ensemble du matériel et en étant large, il faut compter moins de 30€.

Réalisation du projet

La réalisation de ce projet est relativement simple. Elle se résume en 3 étapes :

  1. Connecter les matériel comme indiqué ci-dessous dans la partie Schéma ;
  2. Télécharger le programme dans le D1 mini Wemos (cf. Code) ;
  3. Et c'est fini.

Schéma

Schéma circuit Led Touch.jpeg

Code

Certaines lignes sont surlignées en jaune. Ces lignes identifient les paramètres qui peuvent être modifiés sans risque afin de s'adapter aux spécificités et choix faits pour votre projet.

  1 #include <Adafruit_NeoPixel.h>
  2 
  3 // définition d'un type pour faciliter la déclaration des fonctions liées aux interruptions
  4 typedef void (*LED_MODE_FUNC)();
  5 
  6 // broche du wemos sur laquelle est connectée le ruban de led
  7 #define PIN_RUBAN_LED       D3
  8 // broche du wemos sur laquelle est connecté le bouton tactile TP223
  9 #define PIN_BOUTON_TACTILE  D2
 10 // nombre de led sur le ruban
 11 #define NB_LED              60
 12 
 13 // permet de suivre le mode dans lequel sont configurées les led (couleur unique, arc-en-ciel, etc.)
 14 /************************************/
 15 /* par défaut 6 modes :             */
 16 /*     - 0 : éteint                 */
 17 /*     - 1 : couleur unique (rouge) */
 18 /*     - 2 : couleur unique (vert)  */
 19 /*     - 3 : couleur unique (bleu)  */
 20 /*     - 4 : arc-en-ciel            */
 21 /*     - 5 : arc-en-ciel harmonisé  */
 22 /************************************/
 23 volatile byte led_mode = 0;
 24 // permet de switcher de mode d'affichage selon le mode actif
 25 volatile LED_MODE_FUNC ledFonction = NULL;
 26 
 27 // permet de forcer les fonctions contrôlant les led à sortir de leurs boucles,
 28 // et à contrôler les temps de bascule entre deux changements de couleur dans les modes arc-en-ciel
 29 volatile byte led_delai = 0;
 30 // délai par défaut entre deux changements de couleur en mode arc-en-ciel
 31 const byte LED_DELAI = 20;
 32 // définit la durée d'attente entre 2 changements de mode en milliseconde
 33 const unsigned long dureeAntiRebond = 1000;
 34 
 35 // initialise le ruban led
 36 // paramètre 1 = nombre de led dans le ruban
 37 // paramètre 2 = numéro de la broche sur laquelle est connecté le ruban
 38 // paramètre 3 = configuration des led du ruban
 39 //   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
 40 //   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
 41 //   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
 42 //   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
 43 //   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
 44 Adafruit_NeoPixel ruban = Adafruit_NeoPixel(NB_LED, PIN_RUBAN_LED, NEO_GRB + NEO_KHZ800);
 45 
 46 /***************************************************************************************/
 47 /* Pour réduire le risque d'endommager le ruban led :                                  */
 48 /*   - ajouter un condensateur de 1000µF entre le vcc et la masse alimentant le ruban  */
 49 /*   - ajouter un résistance de 300/500 ohms entre le wemos et le câble data du ruban  */
 50 /*   - réduire au maximum la distance entre le wemos et le ruban led                   */
 51 /*   - si branchement à chaud, connecter en 1e la masse du ruban led                   */
 52 /***************************************************************************************/
 53 
 54 void setup() {
 55   ruban.begin();
 56   // force la luminosité du ruban à 0 (ruban éteint)
 57   ruban.setBrightness(0);
 58   ruban.show();
 59   // initialise la fonction paramétrant les led du ruban à rubanOff
 60   ledFonction = rubanOff;
 61   // initialise la broche du bouton tactile en pull up afin d'éviter les faux signaux
 62   pinMode(PIN_BOUTON_TACTILE, INPUT_PULLUP);
 63   // lie le bouton tactile à une interruption qui déclenche la fonction configLedMode
 64   attachInterrupt(digitalPinToInterrupt(PIN_BOUTON_TACTILE), configLedMode, FALLING);
 65 }
 66 
 67 void loop() {
 68   // exécute la fonction liée au mode d'affichage sélectionné par l'utilisateur
 69   ledFonction();
 70 }
 71 
 72 // fonction déclenchée par interruption via le bouton tactile
 73 // ICACHE_RAM_ATTR : force le stockage de cette fonction dans la RAM,
 74 // sinon elle est stockée dans la mémoire flash, et forcément ça plante
 75 ICACHE_RAM_ATTR void configLedMode() {
 76   // stocke la dernière fois que le mode a été effectivement modifié
 77   // pour mémoire 1 changement de mode par seconde au maximum (cf. ligne 33 : dureeAntiRebond)
 78   static unsigned long date_mode_precedent = 0;
 79 
 80   // récupère l'instant actuel
 81   unsigned long date = millis();
 82   // si dernier changement de mode depuis plus d'une seconde alors on traite le signal
 83   if ((date - date_mode_precedent) > dureeAntiRebond) {
 84     // enregistre l'instant actuel
 85     date_mode_precedent = date;
 86     // incrémente le mode
 87     led_mode++;
 88 
 89     // modifie la fonction ledFonction selon le mode choisi
 90     switch(led_mode)
 91     {
 92       default: // mode hors porté, alors repasse le mode à 0
 93         led_mode = 0;
 94       case 0: // ruban led éteint
 95         ledFonction = rubanOff;
 96       break;
 97 
 98       case 1: // couleur unie rouge
 99         ledFonction = rubanCouleurRouge;
100         ruban.setBrightness(50);
101       break;
102 
103       case 2: // couleur unie verte
104         ledFonction = rubanCouleurVerte;
105       break;
106 
107       case 3: // couleur unie bleue
108         ledFonction = rubanCouleurBleue;
109       break;
110 
111       case 4: // arc-en-ciel en boucle
112         ledFonction = arcEnCiel;
113       break;
114 
115       case 5: // arc-en-ciel en boucle avec harmonisation des couleurs
116         led_delai = 0;  // force la sortie des boucles "for" du mode précédent
117         ledFonction = arcEnCielCycle;
118       break;
119     }
120   }
121 }
122 
123 // éteint les led
124 void rubanOff() {
125   // permet de forcer la sortie des boucles arc-en-ciel
126   led_delai = 0;
127 
128   // éteint les led en passant la luminosité à 0 et le couleur des led à 0
129   ruban.setBrightness(0);
130   rubanCouleurUnie(0);
131   ruban.show();
132 
133   // passe en attente du prochain changement de mode
134   ledFonction = rubanAttente;
135 }
136 
137 // ne fait rien
138 void rubanAttente() {
139   delay(1);
140 }
141 
142 // Applique la même couleur à toutes les led du ruban
143 // Puis modifie la fonction ledFonction pour passer en attente
144 void rubanCouleurUnie(uint32_t couleur) {
145   for(uint16_t num_led = 0; num_led < ruban.numPixels(); num_led++) {
146     ruban.setPixelColor(num_led, couleur);
147   }
148   ruban.show();
149 
150   // passe en attente du prochain changement de mode
151   ledFonction = rubanAttente;
152 }
153 
154 void rubanCouleurRouge() {
155   rubanCouleurUnie(ruban.Color(255, 0, 0));
156 }
157 
158 void rubanCouleurVerte() {
159   rubanCouleurUnie(ruban.Color(0, 255, 0));
160 }
161 
162 void rubanCouleurBleue() {
163   rubanCouleurUnie(ruban.Color(0, 0, 255));
164 }
165 
166 // fait varier la couleur des led en arc-en-ciel
167 void arcEnCiel() {
168   arcEnCiel(false);
169 }
170 
171 // fait varier la couleur des led en arc-en-ciel tout en faisant en sorte que les couleurs soient équilibrées/adoucies entre-elles
172 void arcEnCielCycle() {
173   arcEnCiel(true);
174 }
175 
176 // faire varier les couleurs des led
177 void arcEnCiel(bool harmonise) {
178   uint16_t num_led, id_couleur;
179 
180   // paramètre le taux de rafraichissement des changements de couleurs
181   led_delai = LED_DELAI;
182 
183   // si vrai, permet d'harmoniser les couleurs des led entre-elles
184   harmonise  = (harmonise) ? 256 / ruban.numPixels() : 1;
185 
186   // varaiation de la couleur des led
187   while(led_delai) {
188     for(id_couleur = 0; id_couleur < 256 && led_delai; id_couleur++) {
189       // applique à chaque led une nuance de couleur différente selon la couleur souhaitée
190       for(num_led = 0; num_led < ruban.numPixels() && led_delai; num_led++) {
191         ruban.setPixelColor(num_led, cycleRoue(((num_led * harmonise) + id_couleur) & 255));
192       }
193       ruban.show();
194       delay(led_delai);
195     }
196   }
197 }
198 
199 // permet de boucler sur les transitions des couleurs : rouge/vert/bleu/rouge/vert/...
200 uint32_t cycleRoue(byte position_cycle) {
201   position_cycle = 255 - position_cycle;
202 
203   // selon la valeur demandée, applique une nuance de couleur
204   if(position_cycle < 85) {
205     return ruban.Color(255 - position_cycle * 3, 0, position_cycle * 3);
206   }
207   if(position_cycle < 170) {
208     position_cycle -= 85;
209     return ruban.Color(0, position_cycle * 3, 255 - position_cycle * 3);
210   }
211   position_cycle -= 170;
212   return ruban.Color(position_cycle * 3, 255 - position_cycle * 3, 0);
213 }

Explications

Chargement du programme

Voici les étapes à suivre afin de pourvoir charger le programme sur le D1 mini Wemos :

  1. Lancez le programme Arduino IDE
  2. Ajoutez la carte Wemos aux cartes pouvant être gérées par l'IDE Arduino
    1. Ajoutez une carte supplémentaire à partir de menu Fichier > Préférences ;
    2. Dans l'onglet "Paramètres" cliquez sur le bouton situé en face de "URL de gestionnaire de cartes supplémentaires" :
      Config ide arduino esp8266.jpg
    3. Ajoutez le lien suivant dans la fenêtre qui s'est ouverte et validez : https://arduino.esp8266.com/stable/package_esp8266com_index.json
    4. Ajoutez les librairies permettant de gérer les cartes à base d'ESP8266 (menu Outils > Carte > Gestionnaire de carte) ;
    5. Dans l'onglet qui s'ouvre, tapez "ESP8266" dans la barre de recherche ;
    6. Sélectionnez la carte "ESP8266 par ESP8266 Community" et installez-la ;
  3. Ajoutez la bibliothèque Neopixel permettant de contrôler le ruban LED
    1. Allez dans le menu Outils > Gérer les bibliothèques ;
    2. Dans la barre de recherche, saisissez : neopixel ;
    3. Sélectionnez la bibliothèque Adafruit Neopixel par Adafruit ;
  4. Copiez et téléversez le programme
    1. Copiez le code dans l'interface Arduino IDE ;
    2. Dans le menu "Outils > Carte", sélectionnez "esp8266 > LOLIN (WEMOS) D1 R2 & Mini", ainsi que le port série (menu Outils > Port) ;
    3. Compilez et téléversez vers le D1 Mini Wemos.

Fonctionnement général

Par défaut, 6 modes de fonctionnement sont programmés pour contrôler l'éclairage des led :

  1. Eteint
  2. Couleur unie rouge
  3. Couleur unie verte
  4. Couleur unie bleue
  5. Arc-en-ciel
  6. Arc-en-ciel v2

Pour basculer d'un mode à l'autre, il suffit d'appuyer sur le bouton tactile. Si l'on appuie lorsque le dernier mode est en cours d'exécution, alors le programme boucle sur le 1e mode (ici Eteint).

Choix du Wemos

De prime abord, le choix d'un contrôleur type Wemos n'est pas celui qui viendrait naturellement à l'esprit. Cependant, ce choix a été fait d'une part pour son prix et d'autre part pour sa petite taille. L'origine de ce projet est d'intégrer ce système de contrôle de ruban led dans un boîtier imprimé en 3D créant un jeu d'ombres chinoises. Aussi, fallait-il que le contrôleur soit le plus petit possible, et le Wemos (sans ses broches) était ce qu'il y avait de plus petit dans les tiroirs. Dans la partie Améliorations/Evolutions, une piste alternative, autre qu'Arduino, est proposée.

Spécificités du code C++

Interruption

Dans ce programme, on utilise le système d'interruptions pour basculer d'un mode d'éclairage à un autre.

Dans notre cas, on utilise le système des interruptions afin que lorsque l'on appuie sur le bouton tactile le basculement du mode d'éclairage au suivant soit instantané. Effectivement, si l'on n'utilisait pas les interruptions, cela signifierait que les appuis sur le bouton tactile ne seraient pas toujours pris en compte. La lecture de l'état du bouton tactile interviendrait uniquement au moment où l'instruction correspondante est exécutée. Dans cette hypothèse, si l'utilisateur appuie au moment où le programme rentre dans une boucle un peu longue, alors l'appui ne sera pas détecté. Ainsi, afin de pallier cet écueil, les changements d'état du bouton tactile sont associés au déclenchement d'une interruption. Dans ce cas, dès qu'un changement d'état est opéré au niveau du bouton tactile, cela déclenche une interruption qui met en pause le fonctionnement normal du programme pour aller exécuter prioritairement la partie du code associée à ladite interruption avant de reprendre l'exécution normale du programme.

Pointeur de fonctions

Afin de faciliter et simplifier l'écriture de ce code, il a été choisi d'utiliser un pointeur de fonction. Sans entrer dans les détails, en C++ un pointeur de fonction se comporte comme une variable à laquelle il est possible d'assigner des valeurs. Ici, les valeurs assignées seront des fonctions. Ces fonctions correspondent individuellement à un mode d'éclairage. Ainsi, dans la boucle principale du programme une seule instruction est exécutée qui correspond à l'appel de la fonction ledFonction(). Cette fonction un peu particulière est en fait le pointeur de fonction qui pointera in fine vers le code à exécuter associé au mode de fonctionnement sélectionné.

Améliorations/Evolutions

Améliorations

En termes d'amélioration, quelques recommandations doivent être appliquées (non mises en œuvre dans ce projet). Celles-ci concernent la réalisation du câblage et plus particulièrement la protection du ruban led contre les risques d'endommagement :

  • ajouter un condensateur de 1000µF entre le vcc et la masse alimentant le ruban ;
  • ajouter un résistance de 300/500 ohms entre le wemos et le câble data du ruban ;
  • réduire au maximum la distance entre le wemos et le ruban led ;
  • si branchement à chaud, connecter en 1e la masse du ruban led.

Evolutions

Afin de travailler sur la miniaturisation de l'électronique, il serait intéressant d'étudier son portage sur un microcontrôleur type ATTiny85.

A vous de jouer ...