Led_sDance.md 16.2 KB
Newer Older
1 2 3
---
layout: document-with-math-support
title: Led's dance
Damien OLIVIER's avatar
Damien OLIVIER committed
4
authors: Faironniers J.L_et_D
5 6 7 8 9 10 11 12 13 14 15
tags: arduino,leds,atelier
brouillon: true
image: /images/doc/ateliers/10ansBU.jpg
---

Afin de fêter dignement ses 10 ans d'existence, la bibliothèque universitaire du Havre organise une silent party. La [faironnerie ABC] a décidé de participer à l'événement en animant l'atelier **led's dance**.

> Put on your red shoes and dance the blues\\
> To the song they're playing on the radio\\
> While colour lights up your face\\
> Sway through the crowd to an empty space
Damien OLIVIER's avatar
Damien OLIVIER committed
16 17
>
> _David Bowie_
18 19 20

Nous proposons deux montages :

Damien OLIVIER's avatar
Damien OLIVIER committed
21
1. Le premier consiste à créer un pendentif avec une led clignotante que vous pourrez porter durant cette soirée silencieuse ou la mettre dans votre sapin de Noël recyclé ;
Damien OLIVIER's avatar
Damien OLIVIER committed
22
2. Le second, un sonomètre avec un arduino, un micro et un anneau de leds.
23 24 25


## Leds Aalto
Damien OLIVIER's avatar
Damien OLIVIER committed
26
René Dottelonde a permis que l'esprit du designer et architecte finlandais Alvar Aalto et son vase Savoy hante la bibliothèque universitaire du Havre. Pour les 10 ans de cette dernière, il nous a semblé amusant de réaliser un clin d'oeil intermittent. Nous allons essayer de décrire les différentes étapes.
27 28

### Matériel
Damien OLIVIER's avatar
Damien OLIVIER committed
29
Il nous faut une pile bouton 3v, un support, une led clignotante, le logo de la bibliothèque découpé dans du médium et le plus important un bout de ficelle.
30

Damien OLIVIER's avatar
Damien OLIVIER committed
31
![Matériel](/images/doc/ateliers/materiel800x600.png){:width="50%"}
32 33 34 35


### Montage

Damien OLIVIER's avatar
Damien OLIVIER committed
36
On commence par installer la pile dans le support le + côté visible.
Damien OLIVIER's avatar
Damien OLIVIER committed
37
![Pile](/images/doc/ateliers/montagePile.jpg){:width="50%"}
38 39 40

On pose ensuite un morceau de scotch double face sous le support de pile.

Damien OLIVIER's avatar
Damien OLIVIER committed
41
![Double face](/images/doc/ateliers/DoubleFace.jpg){:width="50%"}
42

Damien OLIVIER's avatar
Damien OLIVIER committed
43
On glisse la led des deux côtés de la pile bouton. **Attention** les leds sont polarisées il faut mettre la plus grande patte sur le dessus, elle correspond au plus. Elle devrait s'allumer et clignoter.
44

Damien OLIVIER's avatar
Damien OLIVIER committed
45
![Mise en place de la led](/images/doc/ateliers/PileEtLed2.jpg){:width="50%"}
46 47 48

Le moment délicat arrive, il faut plier la led. On va tout d'abord introduire la led dans le logo de la BU.

Damien OLIVIER's avatar
Damien OLIVIER committed
49
![Led dans logo](/images/doc/ateliers/LedDansBU.jpg){:width="50%"}
50 51


Damien OLIVIER's avatar
Damien OLIVIER committed
52
![Led dans logo](/images/doc/ateliers/LedDansBU2.jpg){:width="50%"}
53 54 55

On plie avec délicatesse .....

Damien OLIVIER's avatar
Damien OLIVIER committed
56
![pliage](/images/doc/ateliers/pliage.jpg){:width="50%"}
57

Damien OLIVIER's avatar
Damien OLIVIER committed
58
On enlève le tout, pour croiser les pattes de la led de façon à ce que cela fasse ressort.
59

Damien OLIVIER's avatar
Damien OLIVIER committed
60
![ressort](/images/doc/ateliers/Ressort.jpg){:width="50%"}
61

Damien OLIVIER's avatar
Damien OLIVIER committed
62
![ressort](/images/doc/ateliers/Ressort2.jpg){:width="50%"}
63 64 65

On repositionne la led. **Attention** au plus qui est maintenant la patte la moins longue horizontalement...

Damien OLIVIER's avatar
Damien OLIVIER committed
66
![remontage](/images/doc/ateliers/Remontage.jpg){:width="50%"}
67 68 69

On enlève le double face.

Damien OLIVIER's avatar
Damien OLIVIER committed
70
![](/images/doc/ateliers/DoubleFaceEnleve.jpg){:width="50%"}
71

Damien OLIVIER's avatar
Damien OLIVIER committed
72
![remontage](/images/doc/ateliers/Remontage2.jpg){:width="50%"}
73 74 75

 Et voila le travail.

Damien OLIVIER's avatar
Damien OLIVIER committed
76
![Le montage](/images/doc/ateliers/CestBo.jpg){:width="50%"}
77 78 79

J'espère que vous avez conservé la protection du double face, car cela va servir d'interrupteur.

Damien OLIVIER's avatar
Damien OLIVIER committed
80
![Interrupteur](/images/doc/ateliers/interrupteur.jpg){:width="50%"}
81 82 83

On passe une cordelette.

Damien OLIVIER's avatar
Damien OLIVIER committed
84
![Interrupteur](/images/doc/ateliers/BonAnni.jpg){:width="50%"}
85 86 87 88 89 90 91 92 93 94 95 96 97


## Sonomètre ou le baroufledomètre
L'idée consiste à réaliser un sonomètre à l'aide d'un micro amplifié et d'un anneau de led qui s'allumera plus ou moins en fonction de l'intensité sonore dans la pièce.

### Matériel
Pour réaliser le montage, il est nécessaire d'avoir
- Un [arduino nano][4681b06b] ou un "clone" compatible avec au moins une broche analogique et des broches digitales ;
- Un [micro amplifié](https://www.adafruit.com/products/1063) ou encore [celui-ci](http://www.dx.com/p/max9812-microphone-amplifier-module-red-408348?Utm_rid=32628674&Utm_source=affiliate#.WDMN9nXhCw4), par exemple ;
- Un [anneau NeoPixel](https://www.adafruit.com/products/1463) 16 x 5050 RGB LED ou bien [celui-ci](http://www.ebay.fr/itm/WS2812B-5050-RGB-LED-Ring-24Bit-RGB-LED-Integrated-Drivers-For-Arduino-CF/272387174062?_trksid=p2047675.c100005.m1851&_trkparms=aid%3D222007%26algo%3DSIC.MBE%26ao%3D1%26asc%3D39923%26meid%3D023a40b0647847e89ae2c0015b8a2142%26pid%3D100005%26rk%3D1%26rkt%3D5%26sd%3D141736103400), par exemple ;
- Sans oublier une batterie rechargeable 9v.

### Premier montage
Damien OLIVIER's avatar
Damien OLIVIER committed
98
Pour commencer nous allons mesurer l'intensité sonore à l'aide du micro amplifié. Les pins analogiques de l’Arduino renvoient un nombre entier compris entre 0 et 1023. Ces données ne sont pas directement utilisables, nous allons échantillonner durant environ 50 ms ce qui correspond à la fréquence basse perçue par l'oreille humaine (20 Hz et 20 000 Hz) et nous allons déterminer crête à crête l'amplitude.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

#### Le montage

![Nano + micro](/images/doc/ateliers/vumetre1_bb.png)


#### Le code

~~~ C
/*
 * Mesure d'un niveau sonore ambiant
 */

#define TRACEUR

#define BROCHE_MICRO   A0                // Le micro est connecté à une broche analogique

const int fenetreEchantillonnage = 50;   // Fenêtre d'échantillonnage (50 ms = 20Hz) fréquence audible
unsigned int echantillon;

void setup()
{
   Serial.begin(9600);
}


void loop()
{
   unsigned long debutCrono= millis();  // Debut d'échantillonnage
   unsigned int creteACrete = 0;        // Niveau crête à crête

   unsigned int signalMax = 0;
   unsigned int signalMin = 1024;

   // Acquisition des valeurs durant la fenêtre d'échantillonnage
   while (millis() - debutCrono < fenetreEchantillonnage)
   {
      echantillon = analogRead(BROCHE_MICRO);
      if (echantillon < 1024)           // Valeur cohèrente ?
      {
         if (echantillon > signalMax)
         {
            signalMax = echantillon;
         }
         else if (echantillon < signalMin)
         {
            signalMin = echantillon;
         }
      }
   }
   creteACrete = signalMax - signalMin;  // Amplitude crête à crête

#ifdef TRACEUR
  Serial.print(creteACrete);
  Serial.println();
#else
   Serial.print("Min   = ");
   Serial.println(signalMin);
   Serial.print("Max   = ");
   Serial.println(signalMax);
   Serial.print("Delta = ");
   Serial.println(creteACrete);
   double volts = (creteACrete * 3.3) / 1023;   // Convertion en volts
   Serial.print("Volts = ");
   Serial.println(volts);
#endif

}
~~~


![Amplitude du son](/images/doc/ateliers/Amplitude1.png)



### Deuxième montage

L'anneau de led sert ici de vumètre. On va l'allumer en fonction de l'amplitude du son. Nous allons essayer de réaliser cela progressivement.

La première chose à faire est de récupérer la bibliothèque [adafruit] correspondante et l'installer dans l'environnement de développement arduino. Il suffit pour cela d'utiliser le gestionnaire de bibliothèque. À partir du menu `croquis`, `Inclure une bibliothèque > Gérer les bibliothèques`. Au niveau de la boite de dialogue entrer `Neopixel`. Vous devez voir apparaître dans la liste `Adafruit NeoPixel by Adafruit` choisissez la dernière version et installez la.

![Installation de la bibliothèque Neopixel](/images/doc/ateliers/BibliNeopixel.png)

#### Le montage
Nous allons réaliser un montage de base extrémement simple qui consiste à connecter l'anneau de led à l'arduino nano. On prend l'alimentation sur l'arduino, attention cependant si vous utilisez un anneau plus important ou une guirlane de led, à la consommation électrique. Il faut alors adapter le montage.

![Montage NeoPixel](/images/doc/ateliers/NeoPixel1_bb.png)

#### Le code
On commence par inclure le fichier d'entête contenant les prototypes des différentes fonctions permettant d'utiliser les Neopixels.

~~~ C++
#include <Adafruit_NeoPixel.h>
~~~

Il faut déclarer ensuite une variable globale représentant votre Neopixel que vous pourrez utiliser dans vos différentes fonctions :

~~~ C++
Adafruit_NeoPixel uneVariable = Adafruit_NeoPixel(<paramètre1>, <paramètre2>, <paramètre3>);
~~~
 1. `<paramètre1>` vous précisez combien il y a de leds RGB dans votre anneau ou votre guirlande.
 2. `<paramètre2>` la broche sur laquelle votre dispositif est connecté.
 3. `<paramètre3>` une valeur indiquant le type de NeoPixels connectés, le plus souvent vous pouvez omettre cette valeur.



~~~ C++
#include <Adafruit_NeoPixel.h>

#define N_LEDS  16              // Nombre de leds RGB dans l'anneau
Damien OLIVIER's avatar
Damien OLIVIER committed
209
#define BROCHE_ANNEAU 6         // Broche sur laquelle est connecté l'anneau (Data input)
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256

// Paramètre 3 =
//   NEO_KHZ800  800 KHz fréquence du flux (le plus souvent)
//   NEO_KHZ400  400 KHz
//   NEO_GRB     cablage GRB (le plus souvent)
//   NEO_RGB     cablage RGB
Adafruit_NeoPixel anneau = Adafruit_NeoPixel(N_LEDS, BROCHE_ANNEAU, NEO_GRB + NEO_KHZ800);


void setup() {
  anneau.begin();
  anneau.show();              // Toutes les leds sont éteintes.
}

void loop() {
for(int j = 0; j < 3; j++ ) {
    // Éclat croissant
    for(int k = 0; k < 256; k++) {
      switch(j) {
        case 0: setAll(k,0,0); break;
        case 1: setAll(0,k,0); break;
        case 2: setAll(0,0,k); break;
      }
      anneau.show();
      delay(3);
    }
    // Éclat décroissant
    for(int k = 255; k >= 0; k--) {
      switch(j) {
        case 0: setAll(k,0,0); break;
        case 1: setAll(0,k,0); break;
        case 2: setAll(0,0,k); break;
      }
      anneau.show();
      delay(3);
    }
}
}

void setAll(int rouge, int vert, int bleu) {
  for(int i = 0; i < N_LEDS; i++ ) {
    anneau.setPixelColor(i, anneau.Color(rouge, vert, bleu));
  }
  anneau.show();
}
~~~

Damien OLIVIER's avatar
Damien OLIVIER committed
257
On va compléter notre programme. Nous avons joué sur le "fade" (éclat), on va maintenant utiliser la roue chromatique. Ainsi lorsque l'amplitude n'est pas trop forte les leds s'allumeront en vert, puis en jaune orange, puis en rouge. On code en RGB : `anneau.Color(rouge, vert, bleu)` et nous allons utiliser le modèle HSL, ( Hue, Saturation, Lightness ; teinte, saturation, lumière).
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
 - La teinte est mesurée par un angle autour de la roue chromatique ;
 - La saturation correspond à la pureté de la couleur. C'est une quantité en % de gris contenue dans une teinte. La saturation est mesurée du centre de la roue vers les bords en partant des couleurs neutres vers les couleurs les plus vives.
 - La luminosité indique la variation d'intensité lumineuse de la couleur.

![Roue Chromatique](/images/doc/ateliers/RoueChromatique.png)

Il va nous falloir faire varier l'angle entre 120° (vert) jusqu'à 0 (rouge). Les primitives Neopixels étant en RGB, on écrit une fonction de conversion.

~~~ C++

teinte_2_RGB( v1, v2, vH )
{
   if ( vH < 0 ) vH += 1;
   if ( vH > 1 ) vH -= 1;
   if ( ( 6 * vH ) < 1 ) return ( v1 + ( v2 - v1 ) * 6 * vH );
   if ( ( 2 * vH ) < 1 ) return ( v2 );
   if ( ( 3 * vH ) < 2 ) return ( v1 + ( v2 - v1 ) * ( ( 2 / 3 ) - vH ) * 6 );
   return ( v1 );
}

uint32_t roueChromatique(byte teinte, float saturation, float lumiere)
 if ( saturation == 0 )
 {
    rouge = lumiere * 255;
    vert  = lumiere * 255;
    bleu  = lumiere * 255;
 }
 else
 {
    if ( lumiere < 0.5 )
      var_2 = lumiere * ( 1 + saturation );
      else var_2 = ( lumiere  + saturation ) - ( saturation * lumiere );

    var_1 = 2 * lumiere - var_2;

    rouge = 255 * teinte_2_RGB( var_1, var_2, teinte + ( 1. / 3 ) );
    vert  = 255 * teinte_2_RGB( var_1, var_2, teinte );
    bleu  = 255 * teinte_2_RGB( var_1, var_2, teinte - ( 1. / 3 ) );
    return anneau.Color(rouge, vert, bleu);
 }
~~~

Damien OLIVIER's avatar
Damien OLIVIER committed
300
On écrit une fonction qui eteint `n` leds consécutives en donnant le numéro de la première et le numéro de la dernière.
301 302

~~~ C++
Damien OLIVIER's avatar
Damien OLIVIER committed
303
void eteintEntre(uint8_t ledDepart, uint8_t ledArrivee) {
304 305 306 307 308 309 310 311
  uint8_t tmp;
  if (ledDepart > ledArrivee)
  {
    tmp = ledDepart;
    ledDepart = ledArrivee;
    ledArrivee = tmp;
  }
  for (int quelleLed = ledDepart; quelleLed <= ledArrivee; quelleLed++)
Damien OLIVIER's avatar
Damien OLIVIER committed
312
    anneau.setPixelColor(quelleLed, anneau.Color(0, 0, 0));
313 314 315
}
~~~

Damien OLIVIER's avatar
Damien OLIVIER committed
316 317 318 319 320 321 322 323 324
En s'inspirant de cette dernière on pourrait écrire une fonction qui allume `n` leds consécutives en donnant le numéro de la première, le numéro de la dernière et la couleur.
Nous allons écrire une version plus sophistiquée qui allume un gradient de couleur entre les leds considérées. On utilise pour cela une fonction `map (valeur, source_limite_basse, source_limite_haute, destination_limite_basse, destination_limite_haute)` :

* `valeur` : le nombre à mettre à l'echelle ;
* `source_limite_basse` : la borne inférieure de l'échelle de la valeur ;
* `source_limite_haute` : la borne supérieure de l'échelle de la valeur ;
* `destination_limite_basse`: la borne inférieure de la nouvelle échelle ;
* `destination_limite_haute`: la borne supérieure de la nouvelle échelle.

Damien OLIVIER's avatar
Damien OLIVIER committed
325
La valeur renvoyée est la valeur mise à l'échelle. On utilise la fonction `map()`, la couleur est choisie par le numéro de led à allumer.
Damien OLIVIER's avatar
Damien OLIVIER committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391


~~~ C++
void allumeEntre(uint8_t ledDepart, uint8_t ledArrivee, unsigned int angleDepart, unsigned int angleArrivee) {
  uint8_t tmp;
  if (ledDepart > ledArrivee)
  {
    tmp = ledDepart;
    ledDepart = ledArrivee;
    ledArrivee = tmp;
  }
  for (int quelleLed = ledDepart; quelleLed <= ledArrivee; quelleLed++)
    anneau.setPixelColor(quelleLed,roueChromatique(map(ledArrivee - ledDepart + 1, 0, N_LEDS - 1, angleDepart, angleArrivee), 1, 0.5));
}
~~~

On repart de notre programme qui lisait la sortie du micro, pour une meilleure lisibilité on écrit une fonction `acquisition()` qui réalise le traitement.

~~~ C++
unsigned int acquisition()
{
  unsigned int echantillon;
  unsigned int signalMax = 0;
  unsigned int signalMin = 1024;

  unsigned long debutCrono = millis(); // Debut d'échantillonnage
  unsigned int creteACrete = 0;        // Niveau crête à crête

  while (millis() - debutCrono < fenetreEchantillonnage)
  {
    echantillon = analogRead(BROCHE_MICRO);
    if (echantillon < 1024)           // Valeur cohèrente ?
    {
      if (echantillon > signalMax)
      {
        signalMax = echantillon;
      }
      else if (echantillon < signalMin)
      {
        signalMin = echantillon;
      }
    }
  }
  creteACrete = signalMax - signalMin;  // Amplitude crête à crête
  return creteACrete;
}
~~~

Il nous reste à définir la fonction `loop()` principale :

~~~ C++
void loop() {
  unsigned int amplitude = acquisition(); // Amplitude crête à crête
  unsigned int nbAAllumer = map(amplitude, 0, 1024, 0, 17 );

#ifdef DEBUG
  Serial.print("amplitude = "); Serial.println(amplitude);
  Serial.print("nbAAllumer = "); Serial.println(nbAAllumer);
#endif

  eteintEntre(nbAAllumer, N_LEDS - 1);   // On éteint les leds en trop ...
  if (nbAAllumer > 0) allumeEntre(0, nbAAllumer - 1, 120, 0);
  anneau.show();
}
~~~

Damien OLIVIER's avatar
Damien OLIVIER committed
392 393 394 395 396 397
Le montage complet est le suivant :

![Schéma complet](/images/doc/ateliers/sonometre_bb.png)

On remarquera que l'on a été cherché une masse sur l'ISCP.
Le [code] complet.
Damien OLIVIER's avatar
Damien OLIVIER committed
398 399 400 401

## Pour aller plus loin

Notre baroufledomètre est terminé. Des améliorations sont possibles, nous traitons les mesures linéairement ce qui n'est pas correct. Nos oreilles sont sensibles à des **variations** de pression
Damien OLIVIER's avatar
Damien OLIVIER committed
402
entre 0,00002 Pa (20 μPa) et 200 Pa (la pression atmosphérique est de 101 300 Pa), l'étendue de l'échelle est donc de 10⁶ et elle est donc très grande. D'autre part c'est la **variation relative** entre deux sons que nous percevons et non pas une variation absolue. Un son exerçant une pression acoustique de 0,02 Pa relativement à 0,01 Pa est aussi fort qu'un son de 2 Pa relativement à 1 Pa. Le son a augmenté de 100% !
Damien OLIVIER's avatar
Damien OLIVIER committed
403
On utilise donc le décibel pour mesurer le son, c'est une échelle logarithmique. Dans l'usage courant, un niveau de bruit exprimé en décibels est un niveau de pression acoustique Lp = 0 dB avec comme référence 20 μPa. Cette échelle est trompeuse car non linéaire. Un bruit de 55 dB (bruit d'une conversation) ajouté à un bruit de 55 dB n'est pas un bruit de 110 db.
Damien OLIVIER's avatar
Damien OLIVIER committed
404
Pour la première source : $$L_{I1} = 10 \ln (I_1/I_0)$$ et pour la deuxième $$L_{I2} = 10 \ln (I_2/I_0)$$. Le niveau d’intensité acoustique résultant du fonctionnement des deux sources est donc $$L_{I} = 10 \ln ((I_1 + I_2)/I_0)$$ Les deux sources produisent ici la même intensité, $$I_1 = I_2$$ , $$L_{I} = 10 \ln (2 \times I_1/I_0) = L_{I1} + 10 \ln 2$$, donc environ  55 + 3 dB = 58 dB.
Damien OLIVIER's avatar
Damien OLIVIER committed
405 406


407 408 409 410


   [faironnerie ABC]: <https://faironnerie-abc.xyz/>
   [4681b06b]: https://www.arduino.cc/en/Main/ArduinoBoardNano "Arduino Nano"
Damien OLIVIER's avatar
Damien OLIVIER committed
411
   [adafruit]: <https://learn.adafruit.com>
Damien OLIVIER's avatar
Damien OLIVIER committed
412
   [code]: /attach/code/LedSDance/Sonometre/Sonometre.ino