contact bille Shield MP3

Bonjour,
je suis en train de fabriquer une sorte de petit livre sonore.
un contact à bille sur chaque page. Quand la page est tournée, la bille glisse en position ON, et déclenche le son.
parfait. j'ai fait l'éssai avec des leds à la place des sons, c'est good, ça roule!
MAIS quand je veux utiliser le shield MP3 Sparkfun (que j'ai testé, et qui fonctionne très bien),
je tourne la page 1 = le son1 est déclenché
je tourne la page 2 = je suis obligé d'attendre que le son 1 soit arrivé en fin de lecture pour qu'il lise le son 2
(j'utilise la librairie simplifié de Bill Porter SFEMP3Shield library)
je décide donc d'arrêter de faire stop en début de boucle, puis de lire le fichier son.
Mais ma boucle est infiniment courte, et il ne fait que stop lire stop lire .... c'est pas bon!!
Et comme je suis une buse, je ne sais comment faire pour que ma boucle dure suffisamment (j'ai bien éssayé avec delay mais c'est pas bon non plus il faut attendre la fin du delay ...)
je m'arrache les cheveux!! si ça vous dit quelque chose :

#include <SPI.h>

//Add the SdFat Libraries
#include <SdFat.h>
#include <SdFatUtil.h> 

//and the MP3 Shield Library
#include <SFEMP3Shield.h>

//create and name the library object
SFEMP3Shield zic;

char* tracks[11]={"numero1.mp3",  "numero2.mp3", "numero3.mp3", "numero4.mp3", "numero5.mp3", "numero6.mp3", "numero7.mp3", "numero8.mp3", "numero9.mp3", "numero10.mp3"};
//les numero ci dessus sont les sons 1,2,3,4, ...

int bille1 =44; //les contacts à bille
int bille2 =42;
int bille3 =40;
int bille4 =38;
int bille5 =36;
int bille6 =34;
int bille7 =32;
int bille8 =30;
int bille9 =28;
int bille10 =26;

boolean etat1 = false;//son1 allume ou eteinte
boolean etat2 = false;//son2 idem
boolean etat3 = false;
boolean etat4 = false;
boolean etat5 = false;
boolean etat6 = false;
boolean etat7 = false;
boolean etat8 = false;
boolean etat9 = false;
boolean etat10 = false;


void setup(){
   Serial.begin(9600);
    //boot up the MP3 Player Shield
  zic.begin();
zic.SetVolume(25,25); //VOLUME à 250 c'est off, et 0 à donf


  pinMode (bille1, INPUT);
  pinMode (bille2, INPUT);
  pinMode (bille3, INPUT);
  pinMode (bille4, INPUT);
  pinMode (bille5, INPUT);
  pinMode (bille6, INPUT);
  pinMode (bille7, INPUT);
  pinMode (bille8, INPUT);
  pinMode (bille9, INPUT);
  pinMode (bille10, INPUT);

}

void loop() {
   
 int etat1 = digitalRead(bille1);
 int etat2 = digitalRead(bille2);
 int etat3 = digitalRead(bille3);
  int etat4 = digitalRead(bille4);
  int  etat5 = digitalRead(bille5);
  int   etat6 = digitalRead(bille6);
     int etat7 = digitalRead(bille7);
     int  etat8 = digitalRead(bille8);
     int   etat9 = digitalRead(bille9);
      int   etat10 = digitalRead(bille10);
  
  if (etat1==LOW && etat2==LOW && etat3==LOW ) //PAGES OFF
  {zic.stopTrack();
   Serial.println("stop");
}

else if (etat1==HIGH && etat2==LOW ) //PAGE 1
 { zic.stopTrack();
    zic.playMP3(tracks[0]);
 Serial.println("page1");
}

else if (etat1==HIGH && etat2==HIGH & etat3==LOW) //PAGE2
 {
   zic.stopTrack();
   zic.playMP3(tracks[1]);
    Serial.println("page2");
  
}

else if (etat1==HIGH && etat2==HIGH && etat3==HIGH ) //PAGE3
 { 
   zic.stopTrack();
   zic.playMP3(tracks[2]);
    Serial.println("page3");}
   

}
[code]

[/code]

Bonjour,

À mon avis c'est typiquement le genre d'application où la gestion des interruption serait idéale
http://arduino.cc/fr/Main/AttachInterrupt
Par contre, se pose 2 problèmes:

  • Est-ce que la platine MP3 (modèle?) contient une fonction pour arrêter une lecture en cours ?
  • L'Arduino Uno n'a que deux ports gérant les interruptions. Comment faire pour en gérer plus ? (Je débute dans le domaine, et je ne sais pas si, par exemple, un registre à décalage peut s'utiliser pour gérer des entrées ?)

Sinon, considérant les deux ports disponibles, il suffirait d'attribuer l'un à "Pages suivante" et l'autre à "page précédente"
ou lire l'état de toutes les billes chaque fois que l'une d'entre elle a bougée. Dans ce cas, il faudrait que n’importe lequel des contacteurs déclenche l'interruption.

Bon, ce sont juste des idées...
XD

PS. Où trouve-t-on des contacteurs à bille ? cela peut aussi m'intéresser comme capteur de chute pour l'un de mes robots.

j'ai l'impression que tu as raison (je pige pas vraiment bien comment ça se met en place)
je suis sur mega 2560 soit 6 broches à interruptions externe (c'est trop peu)
Mais ça m'étonne qu'il n'y ait pas une autre solution beaucoup plus simple.
Oui on peut arrêter la lecture sans problème, il y a une fonction stoptrack.

les contacteurs à bille, je les ai pris chez lextronic
http://www.lextronic.fr/P13768-contact-a-bille.html
attention en soudant, le plastique fond vite ... et les billes peuvent tomber :roll_eyes:

C'est une fonction qui se déclenche quelque soit l'état en cours (ex: boucle) du reste du programme, donc excellent pour arrêter la musique.

  1. Definir une fonction à associer à page suivante (ex: Arrêter la musique en cours et jouer la suivante)
  2. Definir une fonction à associer à page précédente (l'inverse)
  3. Attacher physiquement un capteur à chaque pin lié à une interruption
  4. Déclarer quelle fonction y est lié et sur quel évènement
  attachInterrupt (pin_next, next_music, CHANGE)
  attachInterrupt (pin_prev, prev_music, CHANGE)

Normalement c'est tout: dès qu'une des pin changera d'état (si le capteur à bille bouge), la fonction désignée sera exécutée, quelque soit la partie du programme déjà en cours.
Cependant, le programme reprend en principe là où il était avant, donc il est parfois nécessaire de mettre une balise de changement d'état dans la boucle normale.

Je sais pas trop si j'exprime bien le principe, je suis pas très bon en pédagogie :fearful:
J'ai pas encore testé les fonction d'interruption sur l'Arduino, mais dans d'autres langages de programmation oui, donc ça « devrait » marcher pareil, si j'ai bien lu la doc :blush:

PS. Merci pour la référence du contacteur à bille. (Une petite astuce pour empêcher les pièces de fondre: maintenir une pince à bec sur la pièce à souder, avant la pièce plastique. Comme cela la chaleur en excédent se dissipe en grande partie dans la pince plutôt que vers la partie fragile. On peut comme cela gagner quelques secondes)

Tu dois aussi disposer d'une fonction pour savoir si le MP3 en cours est terminé.
Dans ce cas, une fois que tu as lancé un MP3, tu boucle en attendant que soit le MP3 soit terminé, soit la position des billes ait changée.
Si c'est pour la position des billes tu arrêtes la lecture en cours et tu recommence (analyse de la position des billes et lancement du MP3)

Si c'est un livre pour enfant, il va falloir que l'Arduino réagisse vite quand il v secouer le livre dans tous les sens et que les billes vont aller de partout :wink:

en fait le livre est fixé sur une table pour simplifié, je sais donc que les billes au départ sont en positions ouvert, et quand la page est tournée la bille roule en position fermée.
J'aimerai bien arrêter la lecture en cours pour lancer la lecture d'un nouveau son, mais je ne sais s'il est possible de faire une boucle qui attend infiniment, laissant le son en boucle.
ça marche très bien si par exemple :
{ zic.stopTrack(); // je stop la lecture
zic.playMP3(tracks[0]); // je lis le son 0
delay (10000); // j'attend 10000ms

MAIS tant que le delay n'a pas atteint sa fin, je peux tourner toute les pages, rien ne se passera ...
je voudrais donc l'équivalent sans delay et la je pense que ça jouera

Quelque chose du genre :

if ( bille en position page N )
{
Jouer mp3 N
While ( bille en position page N ou lecture en cour)
;
Arreter lecture
}

while! je ne sais pas encore m'en servir, il était déjà dans le code que tu avais écrit précédemment, et je n'avais pas encore saisi, mais la ça y est je crois comprendre son rôle!
j'éssaye ça, par contre il faut que j'arrête d'abord la lecture du son précédent sinon il attend la fin du son avant de lancer mon nouveau son.
merci!

pas moyen, ça tourne en boucle, comme s'il ne voyait plus ce qu'il se passait à ses entrées

 if (etat1==LOW && etat2==LOW && etat3==LOW ) //PAGES OFF
  { zic.stopTrack();

   Serial.println("stop");
}
if (etat1==HIGH && etat2==LOW )
{zic.stopTrack();
while (etat1==HIGH && etat2==LOW ) //PAGE 1
   { 
    zic.playMP3(tracks[0]);
    
 Serial.println("page1");

}
}
if (etat1==HIGH && etat2==HIGH & etat3==LOW) //PAGE2
 {zic.stopTrack();
  while (etat1==HIGH && etat2==HIGH & etat3==LOW)
   {zic.playMP3(tracks[1]);}
  
    Serial.println("page2");
  
}

if (etat1==HIGH && etat2==HIGH && etat3==HIGH ) //PAGE3
 { 

   zic.playMP3(tracks[2]);
  
    Serial.println("page3");}
   

}[code]

[/code]

C'est normal, il ne voit plus les entrées parce qu'il ne les lis plus.
Tu a lu les entrées 1 seule fois en haut de la boucle et écrit le résultat dans une variable une seule fois, puis tu continue a tester la variable

int etat1 = digitalRead(bille1);

digitalRead() est la fonction qui va aller lire l'état de la broche. Si tu veux elle prend une photo de l'état de la broche à un instant donné et ensuite tu range la photo dans le tiroir etat1

if (etat1==HIGH && etat2==LOW )

Là tu regarde l'état des photos qui sont dans les tiroirs etat1 et etat2
Comme on vient juste de les prendre ces photos, elles représente encore a peu près la réalité

Mais là

while (etat1==HIGH && etat2==LOW )

Tu ouvres les tiroirs et tu regarde toujours les mêmes photos jaunies au lieu d'aller reprendre des photos.

Vire toutes ces variables "etatx" inutiles et remplace directement par les digitalRead() correspondant

De plus tu n'as pas respecté mon algorithme et ta boucle est mal placée.
Tu dois d'abord lancer la lecture du mp3 et ensuite faire une boucle vide d'attente.
Et il manque la fonction qui vérifie si la lecture est terminée ou pas.

    zic.playMP3(tracks[0]);
    while (digitalRead(bille1)==HIGH && digitalRead(bille2)==LOW && zic.isPlaying() ) //PAGE 1
        ; // je ne fais rien, j'attends

ah MERCI, je comprends beaucoup mieux!
mais j'en reste toujours au même point en fait.
quand je tourne la page 1 ok il lit
quand je tourne la 2 il attend que la 1 soit finie puis lit la 2 (c'est pas cool)
et quand je referme la 2 il devrait lire la 1, il ne se passe rien, comme s'il ne lisait plus et si j'ouvre le monitor PAF il lit le 1 comme s'il se remettait à lire mes entrées, idem pour le 3, le comportement est étrange
et je n'ai pas la fonction pour savoir si en lecture ou pas (est ce vraiment nécéssaire)
Ce qui m'importe surtout c'est quand on tourne une page d'arrêter le son, et de lire le suivant ou précédent

tu as bien mis le zic.StopTrack() après le while() ?

Je n'avais pas tout recopié mais au final ca doit être :

if ( digitalRead(bille1)==LOW && digitalRead(bille2)==LOW && digitalRead(bille3)==LOW ) //PAGES OFF
{
  zic.stopTrack();
  Serial.println("stop");
}
else if ( digitalRead(bille1)==HIGH && digitalRead(bille2)==LOW ) //PAGE 1
 {
  zic.stopTrack();
  zic.playMP3(tracks[0]);
  Serial.println("page1");
  while (digitalRead(bille1)==HIGH && digitalRead(bille2)==LOW && zic.isPlaying() )
      ; // je ne fais rien, j'attends
  Serial.println("fin page1");
}
else if ( digitalRead(bille1)==HIGH && digitalRead(bille2)==HIGH && digitalRead(bille3)==LOW ) //PAGE2
{
  zic.stopTrack();
  zic.playMP3(tracks[1]);
  Serial.println("page2");
  while ( digitalRead(bille1)==HIGH && digitalRead(bille2)==HIGH && digitalRead(bille3)==LOW && zic.isPlaying() ) //PAGE 2
      ; // je ne fais rien, j'attends
  Serial.println("fin page2");
}
else if ( digitalRead(bille1)==HIGH && digitalRead(bille2)==HIGH && digitalRead(bille3)==HIGH ) //PAGE3
 { 
  zic.stopTrack();
  zic.playMP3(tracks[2]);
  Serial.println("page3");}
  while (digitalRead(bille1)==HIGH && digitalRead(bille2)==HIGH && digitalRead(bille3)==HIGH && zic.isPlaying() ) //PAGE 3
      ; // je ne fais rien, j'attends
  Serial.println("fin page3");
}

...si je me suis pas trompé ... pas testé bien sur... :wink:

Yep!

Ne serait-il pas plus simple de partir sur une fonction du genre :

int bille[10] = {26, 28, 30, 32, 34, 36, 38, 40, 42, 44}; // tableau de capteurs

void loop()
{
  for (i = 0; i < 10; i++) // itération des capteurs
  {
     if ((digitalRead(bille[i]) == HIGH) && (i != previous_bille)) // contrôle état et comparaison variable mémoire
     {
      lecture(i, previous_bille);               // fonction
      previous_bille = i;   // variable mémoire affecte la bille en cours
     }
  }
}

void lecture(int bille, int bille_precedente)
{
    zic.stopTrack(track[bille_precedente]);
    // delay(200);
    zic.playMP3(track[bille]);
}

Avec un petit for dans la boucle pour itérer les entrées. On pourrais même sauter deux ou trois page ici je pense :smiley:

J'ai cru appercevoir l'utilisation de pointeur ici ou là, donc le code est à prendre comme une hypothèse de simplification. D'ailleurs, on peut trés bien comparer le capteur/titre en cours avec le -1 ou le +1. (L'autre solution avec le même principe est d'envoyer le titre directement à la fonction.)

En positionnant correctement des flags, il y a moyen de s'en sortir.

Par contre, je n'ai pas trop compris l'interêt de conserver l'etat du/des capteur/s precedent ??? (je ne connais pas du tout ce système de bille) Mais d'aprés ce que j'ai compris, une fois la page tournée, on ne peut que continuer de l'avant ???

Aprés, on peut partir sur un tableau à deux dimensions, sur des fronts montants (page suivante), front descendant (page précedente)...3 capteurs max à surveiller, -1 | 0 | +1

@+

Zoroastre.

EDIT1 : Pffff !!! Je suis naze et j'ecrit n'imp !!!
EDIT2 : 2/3 corrections, plus cohérent désormais !!!

YEEE!!! Barbudor c'est super ça roule!! j'étais pas loin, je n'avais juste pas pris au sérieux la fonction zic.isplaying je pensais que tu m'avais indiqué ça comme ça, je pensais qu'elle n'existait pas dans la librairie, et en cherchant à résoudre mon problème, j'ai vu qu'elle était réelle ... honte à moi :blush:
et donc c'est parfait!
pour rendre la chose encore plus efficace, connais tu une façon de lisser le signal, je veux dire que le contacteur quand la page se tourne passe plusieurs fois de 0 à 1 suivant comment on tourne (rebond, roulement de la bille), j'ai éssayé en rajoutant un delay juste après zic.playMP3, mais il fait le contraire, il attend avant de passer au suivant
Merci Zoroastre, il n'y a pas dans cette librairie possibilité de choisir quel track tu arrêtes, c'est un stop général, je vais me pencher plus longtemps sur ton code, tes pistes de simplifications m'intéressent, enfin j'y vais tout doucement avant de courir, j'arrive à peine à mettre un pied devant l'autre ...
Encore merci à vous

le filtrage anti rebond peut être fait de plusieurs façon :

  • en hard avec un filtre RC (résistance plus capa)
  • en soft mais dans ce cas il faut décorréler la gestion des entrées et leur filtrage de la gestion des pages

Le code ci-dessous devrait fonctionner pour faire un filtrage anti-rebond.
la fonction anti-rebond() traite 1 broche d'entrée
La fonction lecture_toutes_billes() va toutes les lires
Une fois que la lecture est effectuée, les tesst doivent être appliqués sur la variable EtatBille[] qui contient le résultat après filtrage

Je n'ai pas repris le reste du code mais tous les test if et while doivent être effectués sur la variable EtatBille[] et non plus directement sur la bille.
On appelle donc la fonction lecture_toutes_billes() en début de loop pour faire les tests if
Mais il faut aussi la rappeller dans la boucle while() afin de mettre à jour EtatBille[].

#define NB_BILLES   3
// Liste des broches pour chaque bille
uint8_t PinBille[NB_BILLES] = { 6,7,8 };
// Etat courant "logiiciel" après filtrage anti-rebond - a utiliser dans les tests (if et while)
uint8_t EtatBille[NB_BILLES] = { 0, };
// Mémorise qu'un changement d'état est en cours
uint8_t ChangeEtatBille[NB_BILLES] = { 0, };
// Sert à compté le temps de la transition. On accepte le changement qu'au bout de TEMPO_DEBOUNCE
unsigned long TimerBille[NB_BILLES] = { 0, };

#define TEMPO_DEBOUNCE      100

void anti_rebond( uint8_t bille )
{
  // lecture de l'état courant de la pin
  uint8_t etatPin = digitalRead( PinBille[bille] );
  // lecture du timer "maintenant"
  unsigned long now = millis();
  // Est que l'état de la broche correspond à l'état actuellement mémorisé
  if ( etatPin == EtatBille[bille] )
  {
    // Si oui, il n'y a pas de changement en cours
    ChangeEtatBille[bille] = false;
  }
  else
  {
    // Est-ce qu'on avait déjà noté qu'un changement était en cours ?
    if ( ChangeEtatBille[bille] )
    {
      // Oui mais est-on resté suffisamment longtemps dans le nouvel état pour l'accepter ?
      if ( (now - TimerBille[bille]) > TEMPO_DEBOUNCE )
      {
        // oui, on accepte le nouvel état, et il n'y a plus de changement en cours
        ChangeEtatBille[bille] = false;
        EtatBille[bille] = etatPin;
      }
      else
      {
        // changement en cours mais pas depuis assez longtemps,
        // alors on continue a attendre l'expiration de TEMPO
      }
    }
    else
    {
      // Il n'y avait pas encore de changement en cours,
      // Donc on note et on mémorise la date de début du changement
      ChangeEtatBille[bille] = true;
      TimerBille[bille] = now;
    }
  }
}

void lecture_toutes_billes()
{
  for ( int bille = 0 ; bille < NB_BILLES ; ++bille )
    anti_rebond( bille );
}


void setup()
{
  for ( int bille = 0 ; bille < NB_BILLES ; ++bille )
    pinMode( PinBille[bille], INPUT );
}


void loop()
{
  lecture_toutes_billes();
  
  // refaire les tests en utilisant la variable EtatBille[...] au lieu de digitalRead()
  // dans la boucle while, appeller lecture_toutes_billes()
  //   while ( EtatBille[0] && EtatBille[1] && zic.isPlaying() )
  //      lecture_toutes_billes();
}

Est-ce que c'est clair ?

N'hésite pas poser des questions sinon.

ça c'est de la réponse! incollable! je comprends mieux "god member"..., encore merci Barbudor pour tes réponses hyper complète, précise, et très pédagogique. je n'ai vu que trop tard ton post et n'ai pas encore pris le temps de bien comprendre et l'appliquer au livre. ça restera un code référence pour mon apprentissage.

Salut,

Si tu veut faire un anti-rebond "software" tu as une librairie sur le playground pour cela :
http://www.arduino.cc/playground/Code/Bounce

Elle marche suivant le principe que barbudor t'as présenté mais a l'avantage de rendre le code (un peu) plus propre (tout est dans la librairie).

merci Skywodd pour la librairie effectivement ça doit rendre le code plus clair.
Barbudor, je ne comprend pas la syntaxe : uint8_t EtatBille[NB_BILLES] = { 0, }; (pourquoi 0?)
sinon je crois qu'il manque qq chose ici non? :

else
      {
        // changement en cours mais pas depuis assez longtemps,
        // alors on continue a attendre l'expiration de TEMPO
      }
[code]

j'ai essayé sans succés,
j'ai aussi éssayé d'adapter avec la librairie de Skywodd, ... pas moyen ...

[/code]

uint8_t EtatBille[NB_BILLES] = { 0, }; (pourquoi 0?)

Pas défaut les EtatBille sont initialisés à 0.
Ils passent à 1 si le code d'anti-rebond décide que ca passe à 1.
Normalement il faut mettre 0, 0, ... autant que NB_BILLES.
Ou alors dans setup() tu mets

memset( EtatBille, 0, sizeof(EtatBille) );

Non il ne manque rien.
Si on a noté un changement, il faut attendre TEMPO pour confirmer le changement d'état.

Barbudor, saurais tu trouver l'erreur dans le code ci dessous? ... :blush:
je l'ai fais pour l'instant, pour un test plus facile, sur 3 leds, et 3 button

#define led1 22
#define led2 24
#define led3 26


boolean etat1 = false;//son1 allume ou eteinte
boolean etat2 = false;//son2 idem
boolean etat3 = false;

#define NB_BILLES   3
// Liste des broches pour chaque bille
uint8_t PinBille[NB_BILLES] = { 20,19,18 };
// Etat courant "logiiciel" après filtrage anti-rebond - a utiliser dans les tests (if et while)
uint8_t EtatBille[NB_BILLES] = { 0,0,0 };
// Mémorise qu'un changement d'état est en cours
uint8_t ChangeEtatBille[NB_BILLES] = { 0,0,0};
// Sert à compté le temps de la transition. On accepte le changement qu'au bout de TEMPO_DEBOUNCE
unsigned long TimerBille[NB_BILLES] = { 0,0,0 };

#define TEMPO_DEBOUNCE      100




void setup(){
   Serial.begin(9600);
   {
  for ( int bille = 0 ; bille < NB_BILLES ; ++bille )
    pinMode( PinBille[bille], INPUT );
}
   
    //boot up the MP3 Player Shield

pinMode (led1, OUTPUT);
pinMode (led2, OUTPUT);
pinMode (led3, OUTPUT);

  /*pinMode (bille1, INPUT);
  pinMode (bille2, INPUT);
  pinMode (bille3, INPUT);
  */


}

void loop() {
  
lecture_toutes_billes();
  
   
if ( EtatBille [1] ==LOW &&  EtatBille [2]==LOW &&  EtatBille [3]==LOW ) //PAGES OFF
{
 Serial.println("stop");
 stopled ();
  
}
else if (  EtatBille [1]==HIGH &&  EtatBille [2]==LOW ) //PAGE 1
 {
 stopled ();
   digitalWrite (led1, HIGH);
  Serial.println("page1");
  while ( EtatBille [1]==HIGH &&  EtatBille [2]==LOW )
      ; // je ne fais rien, j'attends
      lecture_toutes_billes();
  Serial.println("fin page1");
}


else if ( EtatBille [1]==HIGH && EtatBille [2]==HIGH && EtatBille [3]==LOW ) //PAGE2
{ 
   stopled ();
   digitalWrite (led2, HIGH);
  Serial.println("page2");
  while (EtatBille [1]==HIGH && EtatBille [2]==HIGH && EtatBille [3]==LOW) //PAGE 2
      ; // je ne fais rien, j'attends
      lecture_toutes_billes();
      
  Serial.println("fin page2");
}
else if (EtatBille [1]==HIGH && EtatBille [2]==HIGH && EtatBille [3]==HIGH ) //PAGE3
 { 
   stopled ();
   digitalWrite (led3, HIGH);
  Serial.println("page3");
  while (EtatBille [1]==HIGH && EtatBille [2]==HIGH && EtatBille [3]==HIGH ) //PAGE 3
      ; // je ne fais rien, j'attends
      lecture_toutes_billes();
  Serial.println("fin page3");
}

}
void stopled () { digitalWrite (led1, LOW);
   digitalWrite (led2, LOW);
    digitalWrite (led3, LOW);}
  


void anti_rebond( uint8_t bille )
{
  // lecture de l'état courant de la pin
  uint8_t etatPin = digitalRead( PinBille[bille] );
  // lecture du timer "maintenant"
  unsigned long now = millis();
  // Est que l'état de la broche correspond à l'état actuellement mémorisé
  if ( etatPin == EtatBille[bille] )
  {
    // Si oui, il n'y a pas de changement en cours
    ChangeEtatBille[bille] = false;
  }
  else
  {
    // Est-ce qu'on avait déjà noté qu'un changement était en cours ?
    if ( ChangeEtatBille[bille] )
    {
      // Oui mais est-on resté suffisamment longtemps dans le nouvel état pour l'accepter ?
      if ( (now - TimerBille[bille]) > TEMPO_DEBOUNCE )
      {
        // oui, on accepte le nouvel état, et il n'y a plus de changement en cours
        ChangeEtatBille[bille] = false;
        EtatBille[bille] = etatPin;
      }
      else
      {
        // changement en cours mais pas depuis assez longtemps,
        // alors on continue a attendre l'expiration de TEMPO
      }
    }
    else
    {
      // Il n'y avait pas encore de changement en cours,
      // Donc on note et on mémorise la date de début du changement
      ChangeEtatBille[bille] = true;
      TimerBille[bille] = now;
    }
  }
}

void lecture_toutes_billes()
{
  for ( int bille = 0 ; bille < NB_BILLES ; ++bille )
    anti_rebond( bille );
}



[code]

[/code]