Led multi-modes - Contrôle tactile
Sommaire
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 :
- Connecter les matériel comme indiqué ci-dessous dans la partie Schéma ;
- Télécharger le programme dans le D1 mini Wemos (cf. Code) ;
- Et c'est fini.
Schéma
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 led_delai = 0; // force la sortie des boucles "for" du mode précédent
97 break;
98
99 case 1: // couleur unie rouge
100 ledFonction = rubanCouleurRouge;
101 ruban.setBrightness(50);
102 break;
103
104 case 2: // couleur unie verte
105 ledFonction = rubanCouleurVerte;
106 break;
107
108 case 3: // couleur unie bleue
109 ledFonction = rubanCouleurBleue;
110 break;
111
112 case 4: // arc-en-ciel en boucle
113 ledFonction = arcEnCiel;
114 break;
115
116 case 5: // arc-en-ciel en boucle avec harmonisation des couleurs
117 led_delai = 0; // force la sortie des boucles "for" du mode précédent
118 ledFonction = arcEnCielCycle;
119 break;
120 }
121 }
122 }
123
124 // éteint les led
125 void rubanOff() {
126 // éteint les led en passant la luminosité à 0 et le couleur des led à 0
127 ruban.setBrightness(0);
128 rubanCouleurUnie(0);
129 ruban.show();
130
131 // passe en attente du prochain changement de mode
132 ledFonction = rubanAttente;
133 }
134
135 // ne fait rien
136 void rubanAttente() {
137 delay(1);
138 }
139
140 // Applique la même couleur à toutes les led du ruban
141 // Puis modifie la fonction ledFonction pour passer en attente
142 void rubanCouleurUnie(uint32_t couleur) {
143 for(uint16_t num_led = 0; num_led < ruban.numPixels(); num_led++) {
144 ruban.setPixelColor(num_led, couleur);
145 }
146 ruban.show();
147
148 // passe en attente du prochain changement de mode
149 ledFonction = rubanAttente;
150 }
151
152 void rubanCouleurRouge() {
153 rubanCouleurUnie(ruban.Color(255, 0, 0));
154 }
155
156 void rubanCouleurVerte() {
157 rubanCouleurUnie(ruban.Color(0, 255, 0));
158 }
159
160 void rubanCouleurBleue() {
161 rubanCouleurUnie(ruban.Color(0, 0, 255));
162 }
163
164 // fait varier la couleur des led en arc-en-ciel
165 void arcEnCiel() {
166 arcEnCiel(false);
167 }
168
169 // fait varier la couleur des led en arc-en-ciel tout en faisant en sorte que les couleurs soient équilibrées/adoucies entre-elles
170 void arcEnCielCycle() {
171 arcEnCiel(true);
172 }
173
174 // faire varier les couleurs des led
175 void arcEnCiel(bool harmonise) {
176 uint16_t num_led, id_couleur;
177
178 // paramètre le taux de rafraichissement des changements de couleurs
179 led_delai = LED_DELAI;
180
181 // si vrai, permet d'harmoniser les couleurs des led entre-elles
182 harmonise = (harmonise) ? 256 / ruban.numPixels() : 1;
183
184 // varaiation de la couleur des led
185 while(led_delai) {
186 for(id_couleur = 0; id_couleur < 256 && led_delai; id_couleur++) {
187 // applique à chaque led une nuance de couleur différente selon la couleur souhaitée
188 for(num_led = 0; num_led < ruban.numPixels() && led_delai; num_led++) {
189 ruban.setPixelColor(num_led, cycleRoue(((num_led * harmonise) + id_couleur) & 255));
190 }
191 ruban.show();
192 delay(led_delai);
193 }
194 }
195 }
196
197 // permet de boucler sur les transitions des couleurs : rouge/vert/bleu/rouge/vert/...
198 uint32_t cycleRoue(byte position_cycle) {
199 position_cycle = 255 - position_cycle;
200
201 // selon la valeur demandée, applique une nuance de couleur
202 if(position_cycle < 85) {
203 return ruban.Color(255 - position_cycle * 3, 0, position_cycle * 3);
204 }
205 if(position_cycle < 170) {
206 position_cycle -= 85;
207 return ruban.Color(0, position_cycle * 3, 255 - position_cycle * 3);
208 }
209 position_cycle -= 170;
210 return ruban.Color(position_cycle * 3, 255 - position_cycle * 3, 0);
211 }
Explications
Chargement du programme
Voici les étapes à suivre afin de pourvoir charger le programme sur le D1 mini Wemos :
- Lancez le programme Arduino IDE
- Ajoutez la carte Wemos aux cartes pouvant être gérées par l'IDE Arduino
- Ajoutez une carte supplémentaire à partir de menu Fichier > Préférences ;
- Dans l'onglet "Paramètres" cliquez sur le bouton situé en face de "URL de gestionnaire de cartes supplémentaires" :
- Ajoutez le lien suivant dans la fenêtre qui s'est ouverte et validez : https://arduino.esp8266.com/stable/package_esp8266com_index.json
- Ajoutez les librairies permettant de gérer les cartes à base d'ESP8266 (menu Outils > Carte > Gestionnaire de carte) ;
- Dans l'onglet qui s'ouvre, tapez "ESP8266" dans la barre de recherche ;
- Sélectionnez la carte "ESP8266 par ESP8266 Community" et installez-la ;
- Ajoutez la bibliothèque Neopixel permettant de contrôler le ruban LED
- Allez dans le menu Outils > Gérer les bibliothèques ;
- Dans la barre de recherche, saisissez : neopixel ;
- Sélectionnez la bibliothèque Adafruit Neopixel par Adafruit ;
- Copiez et téléversez le programme
- Copiez le code dans l'interface Arduino IDE ;
- Dans le menu "Outils > Carte", sélectionnez "esp8266 > LOLIN (WEMOS) D1 R2 & Mini", ainsi que le port série (menu Outils > Port) ;
- 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 :
- Eteint
- Couleur unie rouge
- Couleur unie verte
- Couleur unie bleue
- Arc-en-ciel
- 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 ...