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…
Mais une vague lueur rouge indique qu’il y a quelque chose de louche…
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
Vue schématique:
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:
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
(235)
Bravo Pascal et les participants. Ceci est très intéressant et je suis certaine qu’on vous demandera de fabriquer la version « Détecteur de fesse assis trop longtemps devant la télé ou l’ordi » d’ici peu…
Bravissimo, pour le cuscuino Prado
Selon notre point de vue, plusieurs applications possibles, entre autre pour les consignes données en gériatrie afin d’éviter de se lever pour les protections anti-chute.
Le personnel serait avisé du non respect et pourrait intervenir…
Félicitations, c’est une innovation qui servira pour tous les âges .
Démonstration convaincante de l’intérêt des acteurs au projet.
Marcel
Ah Ah Ah, bien vu! Le marché grandit!
Bravo Pascal l’invention est excellente et le vidéo bien fait, l’algorithme et le langage informatique m’a un peu perdu cependant. Félicitations! (Ça m’a fait plaisir de voir vos mousses…)