Ce petit projet est parti d’un problème très concret: comment inciter nos enfants à rester assis sur leur chaise pendant toute la durée des repas? La solution imaginée est un coussin capable de rappeler à l’ordre quand on se lève trop tôt et de féliciter lorsqu’on reste assis suffisamment longtemps! Il s’agit donc bien d’un système pour détecter les fesses lorsqu’elles sont posées sur une chaise! Dans la tradition des « makers » qui s’amusent souvent à intégrer le nom des « board » utilisés dans celui du système réalisé (PanoPi avec un Raspberry Pi par exemple), le détecteur de fesses a été baptisé cuscUino pour évoquer « cuscino » (un coussin en italien) et « Arduino » qui fait référence à la carte de microcontrôleur.

Vu d’ici on dirait un coussin tout a fait ordinaire…

IMG_8369

Mais une vague lueur rouge indique qu’il y a quelque chose de louche…

Une lueur rouge à peine visible indique que ce coussin à quelquechose de spécial...

Le cuscUino est en fait très simple:

– Un détecteur fait d’un bouton poussoir placé entre 2 plaques de plastique Coroplast (encore des pancartes électorales récupérées!). Le contact est établi dès que quelqu’un s’assoit sur le détecteur. Simple et économique.

– Un buzzer piezo électrique pour faire des sons d’alarme ou de récompense.

– Un microcontroleur Arduino Nano V3 programmé pour compter le temps écoulé, lire le signal du détecteur de fesses et commander le buzzer piezo en temps voulu.

– Une batterie de 9 volts et un interrupteur marche-arrêt.

– Une petite plaquette de prototypage rapide pour souder les composants ensemble.

– Une autre plaque de Coroplast pour maintenir tous les éléments en place avec l’inévitable duct tape!

– Un coussin avec une fermeture éclair pour cacher te tout.

Deux détecteurs de fesses ont été réalisés successivement pour mes deux garçons avec la complicité de grand maman Lison qui a travaillé à modifier les coussins pour y rajouter des fermetures éclair! Le coût du projet est très raisonnable, de l’ordre de 10 ou 15$ avec des microcontrôleurs « made in China » à environ 2$US chaque (port compris!).

En cas de victoire, le premier cuscUino joue la mélodie de la marche impériale de la guerre des étoiles tandis que le second fredonne le French Can Can! Résultat des courses après 2 jours d’essais: un franc succès! Les enfants sont restés 2 à 3 fois dix minutes à chaque repas!!!! L’attrait pour le coussin ne durera certainement pas plus que quelques jours – au mieux – mais peut-être auront-ils pris de bonnes habitudes… Quel que soit le résultat ultime, il est certain que l’on se sera bien amusés en famille à imaginer, « inventer », réaliser et tester le détecteur de fesses!

Une petite démonstration par les principaux intéressés:

 

Quelques photos du « making-off » et du résultat:

 

 

Diagramme d’état:

Le programme qui passe par les états suivants: attente, démarrage, décompte, rappel à l’ordre et succès

Diagramme Etat En

Vue schématique:

Detecteur de fesses - vue schematique

Les LED1 et LED2 ainsi que les 3 résistances R1, R2 et R3 sont optionnelles et n’ont pas été utilisées dans la version 2 du coussin (la résistance de tirage R3 est en fait obsolète maintenant que le code spécifie l’activation d’une résistance de tirage interne au microprocesseur).

Platine d’essais:

Detecteur de fesses - platine essais

Code:

Voici le code dans sa version actuelle – les détails spécifiques aux branchements (ports choisis etc.) sont regroupés dans un fichier Definitions.h pour que le reste du code soit flexible et facilement portable. Le code ci-dessus comporte certaines répétitions quant à la définition des notes utilisées dans les mélodies Starwars et French Can Can.

#include <Arduino.h>
// Information about hardware connections, functions and definitions
#include "Definitions.h"

// Program tunables to be edited by user
long waiting_time=600000;  // Minimum sitting time to win game, in milliseconds 
long call_time=5000;  // Maximum duration of waiting time before recall alarm, in milliseconds
long game_over_time=30000; // Maximum duration of recall alarm before game over, in milliseconds 
long trigger_time=3000;  // Count starts if button is pressed at least for this duration

// Initialisation of program variables
long start_time=0;
long elapsed_time=0;
long pause_start_time=0;
long time_correction=0;
int game_over=0;

// Required for FrenchCanCan music from
// Rob Faludi
// http://www.faludi.com
// Additions by Christopher Stevens
// http://www.christopherstevens.cc
//referenced from http://www.phy.mtu.edu/~suits/notefreqs.html
//starting with F noteFreqArr[1]
int noteFreqArr[] = {
49.4, 52.3, 55.4, 58.7, 62.2, 65.9, 69.9, 74, 78.4, 83.1, 88, 93.2,
98.8, 105, 111, 117, 124, 132, 140, 148, 157, 166, 176, 186,
198, 209, 222, 235, 249, 264, 279, 296, 314, 332, 352, 373,
395, 419, 444, 470, 498, 527, 559, 592, 627, 665, 704, 746,
790, 837, 887, 940, 996, 1050, 1110, 1180, 1250, 1320, 1400, 1490,
1580, 1670, 1770, 1870, 1990, 2100};
long mode = 0;

void setup () {
  HardwareSetup ();
}

void CliLed1 (byte pp) {
  // Function to blink Led1
  static byte cntLed1;
  if (cntLed1++ > pp) {
    cntLed1 = 0;
    Led1Toggle;
  }
}

void CliLed2 (byte pp) {
  // Function to blink Led2
  static byte cntLed2;
  if (cntLed2++ > pp) {
    cntLed2 = 0;
    Led2Toggle;
  }
}

void CliLed3 (byte pp) {
  // Function to blink Led3
  static byte cntLed3;
  if (cntLed3++ > pp) {
    cntLed3 = 0;
    Led3Toggle;
  }
}

void Beep (long loud_duration, long loud_frequency, long silent_duration)  {   
  // Function to beep piezo - Used as recall alarm
  // All durations in milliseconds, frequency in Hz
  long nb_half_periods=loud_duration*2*loud_frequency/1000;
  long loud_half_period=1000000/(2*loud_frequency);  // microseconds
  for (int i; i<nb_half_periods; i++) { 
       HPToggle;
       delayMicroseconds (loud_half_period); // microseconds
     }
     delay (silent_duration);  // milliseconds
}

void Imperial_March(){
    // Function to play imperial march on piezo - Used as a game win signal
    //tone(pin, note, duration)
    tone(bHP,LA3,Q); 
    delay(1+Q); //delay duration should always be 1 ms more than the note in order to separate them.
    tone(bHP,LA3,Q);
    delay(1+Q);
    tone(bHP,LA3,Q);
    delay(1+Q);
    tone(bHP,F3,E+S);
    delay(1+E+S);
    tone(bHP,C4,S);
    delay(1+S);
    
    tone(bHP,LA3,Q);
    delay(1+Q);
    tone(bHP,F3,E+S);
    delay(1+E+S);
    tone(bHP,C4,S);
    delay(1+S);
    tone(bHP,LA3,H);
    delay(1+H);
    
    tone(bHP,E4,Q); 
    delay(1+Q); 
    tone(bHP,E4,Q);
    delay(1+Q);
    tone(bHP,E4,Q);
    delay(1+Q);
    tone(bHP,F4,E+S);
    delay(1+E+S);
    tone(bHP,C4,S);
    delay(1+S);
    
    tone(bHP,Ab3,Q);
    delay(1+Q);
    tone(bHP,F3,E+S);
    delay(1+E+S);
    tone(bHP,C4,S);
    delay(1+S);
    tone(bHP,LA3,H);
    delay(1+H);
    
    tone(bHP,LA4,Q);
    delay(1+Q);
    tone(bHP,LA3,E+S);
    delay(1+E+S);
    tone(bHP,LA3,S);
    delay(1+S);
    tone(bHP,LA4,Q);
    delay(1+Q);
    tone(bHP,Ab4,E+S);
    delay(1+E+S);
    tone(bHP,G4,S);
    delay(1+S);
    
    tone(bHP,Gb4,S);
    delay(1+S);
    tone(bHP,E4,S);
    delay(1+S);
    tone(bHP,F4,E);
    delay(1+E);
    delay(1+E);//PAUSE
    tone(bHP,Bb3,E);
    delay(1+E);
    tone(bHP,Eb4,Q);
    delay(1+Q);
    tone(bHP,D4,E+S);
    delay(1+E+S);
    tone(bHP,Db4,S);
    delay(1+S);
    
    tone(bHP,C4,S);
    delay(1+S);
    tone(bHP,B3,S);
    delay(1+S);
    tone(bHP,C4,E);
    delay(1+E);
    delay(1+E);//PAUSE QUASI FINE RIGA
    tone(bHP,F3,E);
    delay(1+E);
    tone(bHP,Ab3,Q);
    delay(1+Q);
    tone(bHP,F3,E+S);
    delay(1+E+S);
    tone(bHP,LA3,S);
    delay(1+S);
    
    tone(bHP,C4,Q);
    delay(1+Q);
     tone(bHP,LA3,E+S);
    delay(1+E+S);
    tone(bHP,C4,S);
    delay(1+S);
    tone(bHP,E4,H);
    delay(1+H);
    
     tone(bHP,LA4,Q);
    delay(1+Q);
    tone(bHP,LA3,E+S);
    delay(1+E+S);
    tone(bHP,LA3,S);
    delay(1+S);
    tone(bHP,LA4,Q);
    delay(1+Q);
    tone(bHP,Ab4,E+S);
    delay(1+E+S);
    tone(bHP,G4,S);
    delay(1+S);
    
    tone(bHP,Gb4,S);
    delay(1+S);
    tone(bHP,E4,S);
    delay(1+S);
    tone(bHP,F4,E);
    delay(1+E);
    delay(1+E);//PAUSE
    tone(bHP,Bb3,E);
    delay(1+E);
    tone(bHP,Eb4,Q);
    delay(1+Q);
    tone(bHP,D4,E+S);
    delay(1+E+S);
    tone(bHP,Db4,S);
    delay(1+S);
    
    tone(bHP,C4,S);
    delay(1+S);
    tone(bHP,B3,S);
    delay(1+S);
    tone(bHP,C4,E);
    delay(1+E);
    delay(1+E);//PAUSE QUASI FINE RIGA
    tone(bHP,F3,E);
    delay(1+E);
    tone(bHP,Ab3,Q);
    delay(1+Q);
    tone(bHP,F3,E+S);
    delay(1+E+S);
    tone(bHP,C4,S);
    delay(1+S);
    
    tone(bHP,LA3,Q);
    delay(1+Q);
     tone(bHP,F3,E+S);
    delay(1+E+S);
    tone(bHP,C4,S);
    delay(1+S);
    tone(bHP,LA3,H);
    delay(1+H);
    
    delay(2*H);
}

void playNote(long noteInt, long length, long mode, long breath = 25) {
  length = length - breath;
 
  long noteInt2 = noteInt + 12; //1 octave up
  long noteInt3 = noteInt + 24; //2 octaves up
  long noteInt4 = noteInt + 36; //3 octaves up
 
  long playLoop = length / 100; //divide length by 4, for use in play sequence
 
  if(mode == 0) { //mode 0 sequence
    for (long i=0; i < playLoop; i++){
      buzz(bHP, noteFreqArr[noteInt], 20);
      delay(5);
      buzz(bHP, noteFreqArr[noteInt2], 20);
      delay(5);
      buzz(bHP, noteFreqArr[noteInt3], 20);
      delay(5);
      buzz(bHP, noteFreqArr[noteInt4], 20);
      delay(5);
    }
  } else if(mode == 1) { //mode 1 sequence
    for (long i=0; i < playLoop; i++){
      buzz(bHP, noteFreqArr[noteInt3], 20);
      delay(5);
      buzz(bHP, noteFreqArr[noteInt4], 20);
      delay(5);
      buzz(bHP, noteFreqArr[noteInt3], 20);
      delay(5);
      buzz(bHP, noteFreqArr[noteInt4], 20);
      delay(5);
    }
  } else if(mode == 2) { //mode 2 sequence
    for (long i=0; i < playLoop; i++){
      buzz(bHP, noteFreqArr[noteInt3], 20);
      delay(5);
      buzz(bHP, noteFreqArr[noteInt3], 20);
      delay(5);
      buzz(bHP, noteFreqArr[noteInt3], 20);
      delay(5);
      buzz(bHP, noteFreqArr[noteInt2], 20);
      delay(5);
    }
  } else if(mode == 3) { //mode 3 sequence
    for (long i=0; i < playLoop; i++){ buzz(bHP, noteFreqArr[noteInt4], 40); delay(5); buzz(bHP, noteFreqArr[noteInt2], 40); delay(5); } } if(breath > 0) { //take a short pause or 'breath' if specified
    delay(breath);
  }
}

void FrenchCanCan() {
  //main course
  playNote(12, 500, mode);
 
  playNote(5, 1000, mode);
  playNote(7, 250, mode);
  playNote(10, 250, mode);
  playNote(9, 250, mode);
  playNote(7, 250, mode);
  playNote(12, 500, mode);
  playNote(12, 500, mode);
  playNote(12, 250, mode);
  playNote(14, 250, mode);
  playNote(9, 250, mode);
  playNote(10, 250, mode);
  playNote(7, 500, mode);
  playNote(7, 500, mode);
  playNote(7, 250, mode);
  playNote(10, 250, mode);
  playNote(9, 250, mode);
  playNote(7, 250, mode);
  playNote(5, 250, mode);
  playNote(17, 250, mode);
  playNote(16, 250, mode);
  playNote(14, 250, mode);
  playNote(12, 250, mode);
  playNote(10, 250, mode);
  playNote(9, 250, mode);
  playNote(7, 250, mode);
 
  playNote(5, 1000, mode);
  playNote(7, 250, mode);
  playNote(10, 250, mode);
  playNote(9, 250, mode);
  playNote(7, 250, mode);
  playNote(12, 500, mode);
  playNote(12, 500, mode);
  playNote(12, 250, mode);
  playNote(14, 250, mode);
  playNote(9, 250, mode);
  playNote(10, 250, mode);
  playNote(7, 500, mode);
  playNote(7, 500, mode);
  playNote(7, 250, mode);
  playNote(10, 250, mode);
  playNote(9, 250, mode);
  playNote(7, 250, mode);
  playNote(5, 250, mode);
  playNote(12, 250, mode);
  playNote(7, 250, mode);
  playNote(9, 250, mode);
  playNote(5, 250, mode);
  delay(250);
 
  if(mode == 0) {
    mode = 1;
  } else if(mode == 1) {
    mode = 2;
  } else if(mode == 2) {
    mode = 3;
  } else if(mode == 3) {
    mode = 0;
  }
}

void buzz(int targetPin, long frequency, long length) {
  long delayValue = 1000000/frequency/2; // calculate the delay value between transitions
  //// 1 second's worth of microseconds, divided by the frequency, then split in half since
  //// there are two phases to each cycle
  long numCycles = frequency * length/ 1000; // calculate the number of cycles for proper timing
  //// multiply frequency, which is really cycles per second, by the number of seconds to
  //// get the total number of cycles to produce
  for (long i=0; i < numCycles; i++){ // for the calculated length of time...
    digitalWrite(targetPin,HIGH); // write the buzzer pin high to push out the diaphram
    delayMicroseconds(delayValue); // wait for the calculated delay value
    digitalWrite(targetPin,LOW); // write the buzzer pin low to pull back the diaphram
    delayMicroseconds(delayValue); // wait againf or the calculated delay value
  }
}

enum {Sleep,Start,Check,Count,Call,Success} condition=Sleep;
void loop () {
  delay (20);
  switch (condition) {
  case Sleep:
    //Initialize variables (required on second run)
    start_time=0;
    elapsed_time=0;
    pause_start_time=0;
    time_correction=0;
    game_over=0;
    Led2Off;
    Led3Off;
    CliLed1(1000/25);
    if (Pous1On) { 
      condition = Start; 
    }
    break;
  case Start:
    start_time=millis();
    //Beep(50,1661,1);
    condition = Count;
    break;
  case Count:
    Led1Off;
    CliLed2(100/25);
    CliLed3(100/25);
    elapsed_time = millis()-start_time-time_correction;
    if (!Pous1On & elapsed_time<trigger_time) { condition = Sleep; } if (!Pous1On & elapsed_time>=trigger_time) { 
      Led2Off;
      Led3Off;
      condition = Call;
      pause_start_time=millis();
    }
    if (elapsed_time>waiting_time){
      condition=Success;
    }
    break;
  case Call:
    while(!Pous1On){
      if (millis()-pause_start_time>call_time){
        Beep(200,1000,3000);
        if (millis()-pause_start_time>call_time+game_over_time){
          game_over=1;
          for (int i=27;i>=-27;i--){
            Beep(200,int(440.0*pow(2.0,float(i/12.0))),1);
          }
          Beep(3000,int(440.0*pow(2.0,float(-27.0/12.0))),1);
          break;
        }
      }
    }
    if (game_over==1){
      condition=Sleep;
    }
    else {
      condition = Count;
    }
    time_correction=time_correction+millis()-pause_start_time;
    break;
  case Success:
    Led1On;
    Led2On;
    Led3On;
    //Imperial_March();
    FrenchCanCan();
    FrenchCanCan();
    FrenchCanCan();
    FrenchCanCan();
    condition = Sleep;
    break;
  }
}


Definitions.h

// Definition of ports used and corresponding names
#include <Arduino.h>

#define bHP 6 // PORTD
#define   HPOn   bitSet  (PORTD,bHP) 
#define   HPOff  bitSet  (PORTD,bHP)
#define   HPToggle  PORTD ^= (1<<bHP)

#define bL1  4
#define  Led1On  bitSet   (PORTD,bL1) 
#define  Led1Off bitClear (PORTD,bL1)
#define  Led1Toggle  PORTD ^= 1<<bL1

#define bL2  5
#define  Led2On  bitSet   (PORTD,bL2)
#define  Led2Off bitClear (PORTD,bL2)
#define  Led2Toggle PORTD ^= (1<<bL2)

#define bL3  13
#define  Led3On  bitSet   (PORTB,5)
#define  Led3Off bitClear (PORTB,5)
#define  Led3Toggle PORTB ^= (1<<5)

#define bP1  2 // PORTD  
#define   Pous1On  (!digitalRead(bP1)) 


// Notes below from http://www.instructables.com/id/How-to-easily-play-music-with-buzzer-on-arduino-Th/
// NB: ALL NOTES DEFINED WITH STANDARD ENGLISH NAMES, EXCEPT FROM "A" 
//THAT IS CALLED WITH THE ITALIAN NAME "LA" BECAUSE A0,A1...ARE THE ANALOG PINS ON ARDUINO.
// (Ab IS CALLED Ab AND NOT LAb)
#define	C0 16.35
#define	Db0	17.32
#define	D0	18.35
#define	Eb0	19.45
#define	E0	20.60
#define	F0	21.83
#define	Gb0	23.12
#define	G0	24.50
#define	Ab0	25.96
#define	LA0	27.50
#define	Bb0	29.14
#define	B0	30.87
#define	C1	32.70
#define	Db1	34.65
#define	D1	36.71
#define	Eb1	38.89
#define	E1	41.20
#define	F1	43.65
#define	Gb1	46.25
#define	G1	49.00
#define	Ab1	51.91
#define	LA1	55.00
#define	Bb1	58.27
#define	B1	61.74
#define	C2	65.41
#define	Db2	69.30
#define	D2	73.42
#define	Eb2	77.78
#define	E2	82.41
#define	F2	87.31
#define	Gb2	92.50
#define	G2	98.00
#define	Ab2	103.83
#define	LA2	110.00
#define	Bb2	116.54
#define	B2	123.47
#define	C3	130.81
#define	Db3	138.59
#define	D3	146.83
#define	Eb3	155.56
#define	E3	164.81
#define	F3	174.61
#define	Gb3	185.00
#define	G3	196.00
#define	Ab3	207.65
#define	LA3	220.00
#define	Bb3	233.08
#define	B3	246.94
#define	C4	261.63
#define	Db4	277.18
#define	D4	293.66
#define	Eb4	311.13
#define	E4	329.63
#define	F4	349.23
#define	Gb4	369.99
#define	G4	392.00
#define	Ab4	415.30
#define	LA4	440.00
#define	Bb4	466.16
#define	B4	493.88
#define	C5	523.25
#define	Db5	554.37
#define	D5	587.33
#define	Eb5	622.25
#define	E5	659.26
#define	F5	698.46
#define	Gb5	739.99
#define	G5	783.99
#define	Ab5	830.61
#define	LA5	880.00
#define	Bb5	932.33
#define	B5	987.77
#define	C6	1046.50
#define	Db6	1108.73
#define	D6	1174.66
#define	Eb6	1244.51
#define	E6	1318.51
#define	F6	1396.91
#define	Gb6	1479.98
#define	G6	1567.98
#define	Ab6	1661.22
#define	LA6	1760.00
#define	Bb6	1864.66
#define	B6	1975.53
#define	C7	2093.00
#define	Db7	2217.46
#define	D7	2349.32
#define	Eb7	2489.02
#define	E7	2637.02
#define	F7	2793.83
#define	Gb7	2959.96
#define	G7	3135.96
#define	Ab7	3322.44
#define	LA7	3520.01
#define	Bb7	3729.31
#define	B7	3951.07
#define	C8	4186.01
#define	Db8	4434.92
#define	D8	4698.64
#define	Eb8	4978.03
// DURATION OF THE NOTES 
#define BPM 120    //  you can change this value changing all the others
#define H 2*Q //half 2/4
#define Q 60000/BPM //quarter 1/4 
#define E Q/2   //eighth 1/8
#define S Q/4 // sixteenth 1/16
#define W 4*Q // whole 4/4


void HardwareSetup () {
  DDRD |= 0b01110010; //HP L1 L2 out
  pinMode(bP1,INPUT);
  digitalWrite(bP1,HIGH);  // Configure built-in pullup resitor
}

Améliorations futures:

Si elle existe un jour, la prochaine version pourrait inclure les changements suivants:

  • Mise en veille automatique au bout de quelques minutes sans activation du bouton poussoir (pour économiser la batterie)
  • Harmonisation des méthodes utilisés pour coder les mélodies. Rendre cela plus simple (notes dans un tableau?).
  • Rajouter plusieurs mélodies de victoire et choisir aléatoirement laquelle est utilisée.

Code Arduino

Le code est disponible sur Github:

https://github.com/pepelepoisson/

N’hésitez pas à le réutiliser pour l’améliorer puis à partager vos réussites!

Contact

dsc01824

(137)