1. Introduction

Le sommeil fait partie intégrante de notre vie quotidienne, et le réveil vient avec. Les expressions telles que "se lever du mauvais pied" ou si la fortune vient en dormant, ça n'empêche pas les emmerdements de venir au réveil foisonnent, et rappellent que le réveil peut s'avérer désagréable, c'est en tout cas cette perception qui a initié ce projet. On se demande alors quelle alternative trouver à un réveil pour que celui-ci ne fasse pas de bruit, mais qu'il réveille quand même. La solution proposée ici est un simulateur d'aube, c'est-à-dire un réveil dont l'allumage progressif reproduirait le lever du jour. Cette technique de réveil est utilisée notamment en thérapie pour les personnes souffrant de dépression hivernale, mais s'avère améliorer la qualité subjective du sommeil. Une étude récente (https://www.ncbi.nlm.nih.gov/pubmed/14577838) menée en Finlande a ainsi montré que les sujets remarquaient une amélioration de leur qualité de sommeil dès six jours après le début de l'utilisation de leur réveil.

Une fois le projet choisi, des objectifs ont été fixés; le réveil doit réveiller le dormeur mais ne pas le déranger pendant le reste de son sommeil, il doit être facile et intuitif à régler, le tout doit avoir un coût minimal et l'ensemble du système ne doit pas être encombrant (on part du principe que le réveil sera posé sur une table de nuit). Le but, savoir la programmation et le montage du réveil, est atteint si le programme final fonctionne et permet d'une part d'utiliser l'interface pour régler l'heure et d'autre part de réveiller le dormeur à l'heure programmée.

Le projet est réalisé avec un RaspberryPi comme support. Il s'agit d'un petit ordinateur bon marché qui fonctionne avec Raspbian, un OS basé sur Linux. Il dispose de connexions USB, d'un port pour la technologie Ethernet, d'un port HDMI et d'une prise jack. Le RaspberryPi dispose également d'une série de 40 pins nommée le GPIO (pour General Purpose Input and Output). Il permet au RaspberryPi de recevoir ou d'envoyer de l'information depuis/à des composants électroniques. Pour pouvoir l'utiliser, la librairie GPIO est nécessaire. Selon que les pins du GPIO recevront ou enverront de l'information, ils doivent être initialisés: GPIO.setup(p,GPIO.OUT) ou GPIO.setup(p,GPIO.IN) respectivement pour un pin p envoyant de l'information ou un pin p recevant de l'information. Pour lire cette dernière, deux constantes propres au GPIO sont définies, GPIO.HIGH lorsque le pin émet ou lit une tension, GPIO.LOW lorsque la tension émise ou captée est nulle ou très proche de zéro.

Pour simuler l'aube, un allumage progressif est nécessaire. Or, le GPIO ne possède que deux constantes, il faut utiliser un procédé particulier pour envoyer un signal partiel, savoir le signal à modulation de largeur d'onde ( ou PWM, abréviation anglaise pour Pulse Width Modulation). Le GPIO émet alors un signal carré qui alterne entre GPIO.LOW et GPIO.HIGH. Lorsque la fréquence est suffisamment élevée on peut ramener le signal carré à un courant moyen correspondant à la moyenne du temps où la tension est HIGH et où la tension est LOW. En modulant la largeur d'une onde, on peut donc contrôler un allumage progressif.

On devra se servir pour le projet d'un convertisseur analogique en digital fonctionnant avec un protocole SPI. Le protocole SPI est un protocole de communication entre deux machines, dont le rapport est bâti sur le modèle maître-esclave: l'un ordonne, l'autre fait. Dans notre cas, c'est le Raspberry le maître et le convertisseur analogique en digital est l'esclave. Un convertisseur analogique en digital (anglais analog to digital converter, ADC) est un composant électronique qui transforme un signal analogique (par exemple, une intensité lumineuse ou une résistance variable) en données binaires qu'un ordinateur peut exploiter.

Le Python est un langage de programmation puissant et rapide de la famille des C. C'est le langage de programmation utilisé dans le cadre de ce projet. Ce langage nécessite que des librairies soient importées lorsqu'il s'agit de commander le GPIO ou un protocole SPI, et des modules peuvent aussi être importés lorsque le langage a besoin d'une extension. Comme il s'agit d'un réveil, ce projet a besoin d'une instance qui est capable de générer et gérer des objets de date et d'heure. Le module time sera importé à cet effet.

2. Matériel et méthodes

2.1. Matériel

2.1.1. Électronique

  • Un bouton
  • 25 fils, dont une douzaine de mâle-femelle
  • 4 résistances 330 Ω pour les LEDs et le bouton
  • 4 LEDs mm blanches
  • 1 LED 5 mm jaune
  • Une breadboard
  • Un Pi wedge SparkFun et un câble nappe: connectent le GPIO du RaspberryPi à la breadboard
  • Deux potentiomètres U103
  • Un convertisseur analogique en digital (ADC) MCP3008 disponible chez Adafruit ()

2.1.2. Mécanique

  • Deux cadrans de 180°, l'un gradué de 0 à 23 et l'autre de cinq en cinq de 0 à 55
  • Deux aiguilles, en l'occurrence des cure-dents
  • Du carton a été utilisé lors de la conception du prototype

DSC_0044.JPG

2.1.3. Software

  • Un RaspberryPi
  • Un autre ordinateur avec une connexion internet pour travailler via un logiciel de versions
  • Un écran, une souris et un clavier permettent également de travailler directement sur le RaspberryPi. Il faut que celui-ci dispose de plus d'une connexion internet pour pouvoir enregistrer le travail dans un logiciel de versions non local comme c'était le cas lors de la réalisation de ce projet.

2.1.4. Autres

  • Un pistolet à colle
  • Une équerre à rapporteur
  • Un compas
  • Une paire de ciseaux

2.2. Méthode

Le réveil fonctionne de la sorte: Lorsque le programme est lancé, il lit l'heure indiquée sur les cadrans grâce au convertisseur analogique en digital et la définit comme heure de réveil. Si l'utilisateur veut modifier cette heure, il déplace les aiguilles sur les cadrans puis appuie sur un bouton. Le réveil définit alors la nouvelle heure indiquée comme l'heure de réveil et indique que le changement a été validé en allumant une LED. Pour connaître le moment du réveil, celui-ci compare l'heure définie avec l'heure actuelle. Lorsque les deux sont égales, le réveil allume des LEDs progressivement avec un signal PWM. Lorsque le bouton est pressé les LEDs sont éteintes à nouveau.

2.2.1. La manipulation d'objets de temps

Sur les différentes fonctions comprises dans le module permettant de traiter des objets de temps, time.strftime(format,[t]) est celle qui était la plus judicieuse dans le cas du projet car elle permet de sélectionner uniquement les données nécessaires et de les renvoyer sous un format défini. Ce n'est par exemple pas le cas de la fonction time.time qui renvoie le nombre de secondes écoulées depuis l'epoch Unix. La fonction est utilisée de la sorte: now = time.strftime("%H:%M") où l'argument %H définit l'heure au moment t et %M les minutes au moment t. Le moment n'étant pas spécifié, c'est le temps actuel qui sera mesuré. La variable now ainsi renvoyée est une chaîne de caractères (par exemple, minuit correspond à "00:00"). Les autres variables liées au changement de l'heure sont définies d'une manière spécifiée dans la partie 2.2.4.

Corollaire de l'installation de ce module, la fonction time.sleep(t), qui met le programme en attente pendant t secondes, est plutôt récurrente. Celle-ci est notamment utilisée lors de la phase de réveil en créant un allumage progressif.

2.2.2. Le contrôle du GPIO

Le GPIO a trois utilités dans ce projet, il permet à l'aide du protocole SPI de communiquer avec l'ADC (voir 2.2.3), d'allumer des LEDs - normalement et en PWM - et de lire l'information depuis un bouton, c'est-à-dire si celui-ci est activé ou non. Il est nécessaire de configurer les pins selon leur utilisation (entrée/sortie) en début de programme. On utilise la fonction GPIO.setup(p, GPIO.OUT) ou GPIO.IN pour initialiser le pin p respectivement comme envoyant ou recevant de l'information.

Allumer une LED normalement se fait en émettant une tension GPIO.HIGH depuis un pin déclaré comme pin de sortie. Cette façon de faire est appliquée pour la LED témoin indiquant que l'heure a été modifiée.

Pour la simulation d'aube, un signal à modulation de largeur d'onde est utilisé. Avant de pouvoir démarrer un PWM, il faut le déclarer: alm_LEDs = GPIO.PWM(LEDS,55) avec LEDS le port de sortie et 55 la fréquence en Hertz. Lorsque le moment du réveil est venu, le code suivant s'éxécute:

if wakeTime == now:
alm_LEDs.start(0)
for i in range (1800):
if GPIO.input == (PUSH, GPIO.HIGH):
alm_LEDs.stop()
break
if i%18 == 0:
alm_LEDs.ChangeDutyCycle(i/18)
time.sleep(1)

Pour savoir si le bouton est activé ou non, il faut relier celui-ci à un pin déclaré comme pin d'entrée. Il faut également faire une mise à zéro pour le pin (voir schéma), car s'il ne capte rien le GPIO ne fera pas de mesure précise et la valeur oscillera entre GPIO.HIGH et GPIO.LOW aléatoirement.

C

2.2.3. Le convertisseur analogique en digital

Pour lire la position du potentiomètre, il était nécessaire de passer par un convertisseur analogique en digital car le GPIO du RaspberryPi ne dispose pas de canaux d'entrée analogiques. Le composant utilisé est le MCP3008 d'Adafruit, qui utilise un protocole SPI pour communiquer avec le RaspberryPi. Il s'agit d'un convertisseur 10-bits à huit canaux.

Le convertisseur analogique en digital est géré par une librairie d'Adafruit, et à la fois le protocole SPI et la lecture des données sont déjà programmés, il a suffi de sélectionner la configuration désirée. Après des essais infructueux avec la configuration d'un SPI software, la configuration hardware s'est imposée et fonctionnait sans encombre. Cette configuration dépend cependant de pins définis sur le GPIO, tandis que la lecture se fait depuis n'importe quel pin si on utilise une configuration software.

Les deux potentiomètres sont connectés aux canaux 0 et 1. Pour lire la valeur d'un canal on utilise la commande mcp.read_adc(0). Ici le canal où l'information sera lue est le canal 0.

2.2.4. Le réglage de l'heure

sethours = mcp.read_adc(1)
setmins = mcp.read_adc(0)
for h in range(24):
if sethours < MCP_H_VALUES[h]:
hour = '{:02d}'.format(h)
break
for m in range(12):
if setmins < MCP_M_VALUES[m]:
mins = '{:02d}'.format(m*5)
break
wakeTime = hour + ":" + mins
GPIO.output(BLINK,GPIO.HIGH)
alm_LEDs.stop()
time.sleep(1) GPIO.output(BLINK,GPIO-LOW)

Lorsque le programme démarre, une boucle infinie est lancée (non visible ci-dessus). Les premières instructions exécutées sont la lecture de la position des deux potentiomètres et la conversion de cette position en chaîne de caractères du même format que celui évoqué en 2.2.1. Lorsque le bouton est appuyé, la boucle attente ou alarme (voir 2.2.5.) est interrompue et la boucle principale recommence, relisant et ainsi modifiant l'heure de réveil. Les potentiomètres n'étant pas exactement linéaires, les constantes MCP_H_VALUES et MCP_M_VALUES sont deux listes à autant d'items qu'il y a de positions sur chaque cadran. Chaque item correspond à la valeur faisant office de limite avant de changer d'heure. Lors du réglage, on place par conséquent chaque aiguille en face d'une zone et non sur une position unique. De la sorte, chaque valeur de l'ADC est associée à une heure et les erreurs causées par le manque de précision du potentiomètre ou de la personne réglant l'heure sont considérablement diminuées, la plage étant suffisamment large.

Le code ci-dessus comprend des boucles for. Ces boucles se répètent un certain nombre de fois en itérant une variable à chaque début de boucle. Cette variable est utilisée pour sélectionner l'item de la liste à comparer et pour définir l'heure correspondant à la valeur lue. Le problème rencontré lors de la définition de l'heure est que le format de la variable ne correspond pas toujours à celui de l'heure: Si le réveil est réglé pour 6h00, les variables m et h vaudront respectivement 0 et 6. Le format nécessaire étant 06:00, l'outil utilisé "{:02d}".format(h) formate la variable h en ajoutant des zéros devant jusqu'à ce que le nombre ait atteint la longueur requise (ici 2), et indique que c'est un nombre entier.

2.2.5. La boucle attente ou alarme

  while GPIO.input(PUSH)== GPIO.LOW:
now = time.strftime("%H:%M")
if wakeTime == now:
alm_LEDs.start(0) #Get Up, Stand Up
for i in range (1800): #30 minutes
if GPIO.input(PUSH) == GPIO.HIGH:
alm_LEDs.stop()
break
if i % 18 == 0:
alm_LEDs.ChangeDutyCycle(i/18)
time.sleep(1)
else:
time.sleep(1)
else:
break

Cette boucle tourne aussi longtemps que le bouton n'est pas activé. Lorsque c'est l'heure du réveil, le signal PWM est allumé et augmente graduellement l'intensité des LEDs pendant une demi-heure. Dans la boucle d'attente comme dans la boucle for de l'alarme, le temps de pause est de 1 seconde alors qu'il serait plus économe en quantité d'instructions de calculer le temps qui doit passer entre le moment actuel et le moment du réveil et de mettre le programme en pause pendant cette durée ou respectivement de mettre le temps de pause à 18 secondes pour l'allumage graduel puisqu'il n'augmente que toutes les 18 secondes. Cependant, lorsque le programme est en pause celui-ci ne reçoit pas l'information si le bouton est appuyé ou non. L'état du bouton est ainsi contrôlé une fois par seconde, ce qui suffit et ne demande pas de garder le doigt appuyé trop longtemps. De plus, on peut dire que le programme n'est de toute façon pas surchargé par les instructions, celles-ci étant relativement simples par rapport à la puissance du langage.

2.2.6. La fabrication du support

Une aiguille a été collée sur chaque potentiomètre à la colle chaude. Les cadrans d'essai sont découpés dans du carton. Un orifice au centre du demi-cercle sert à tenir le potentiomètre en place. Les cadrans ont été gradués à l'aide d'un rapporteur et d'une équerre.

C

Si le support final n'a pas été réalisé, des croquis de l'ensemble et des plans des pièces ont été élaborés.

3. Résultats

Un prototype de réveil a été réalisé. Il ne peut pas réveiller un dormeur et le support final n'a pas pu être terminé. Le programme est cependant fonctionnel et définitif.

DSC_0045.JPG

C

4. Discussion

Le problème le plus significatif lors de la réalisation de ce projet était le manque de prudence avec les composant électroniques. En effet, quatre LEDs ont subi un court-circuit (dont une sans qu'on en ait trouvé la cause). Deux LEDs ont été montées en parallèle afin d'évaluer si cela était une bonne idée pour le projet, et sans qu'on place de résistance sur le circuit. La troisième a aussi subi un court-circuit car elle se trouvait sur un circuit sans résistance, mais cela s'est produit à cause d'une erreur de branchement. Il a aussi été constaté que le pin n°27 redémarrait le Raspberry lorsqu'il était soumis à une trop haute tension. La leçon qu'il faut tirer de ces divers incidents est qu'il est important de vérifier ses circuits avant de les mettre sous tension, et par le calcul si besoin.

Le programme du réveil n'étant pas d'une grande complexité et il était facile de retrouver ses erreurs. Une stratégie utilisée au cours du projet pour repérer des erreurs était d'insérer des instructions print qui affichent à l'écran un texte ou une valeur. Le plus souvent celles-ci se rapportaient à une variable, ce qui permettait de comprendre son traitement et de repérer une anormalité. Cependant, cela n'a pas marché à chaque fois, par exemple dans le cas du réglage de l'heure (minutes comme heures):

       setmins = mcp.read_adc(0)
           for m in range (12):
if setmins < MCP_M_VALUES\ m\:
mins = M_CONV\ m\
else:
print "mins = none"
wakeTime = hour + ":" + mins
print wakeTime

Si la valeur vérifie la condition en troisième ligne, la condition sera vérifiée toutes les itérations suivantes. Les minutes ou les heures sont définies selon la liste de valeurs M_CONV ou H_CONV, qui ont par la suite été remplacées par '{:02d}'.format(). Le problème est que la boucle recommence après une itération, et l'heure enregistrée n'est alors plus la même. Si aucun arrêt de boucle n'a été programmé, on lira "23:55" à chaque fois. Les instructions print n'étaient donc pas placées au bon endroit pour qu'on voie l'erreur.

Le dernier problème qui n'a pas de vraie solution est le signal PWM. Il a été très difficile d'obtenir un signal stable, et l'allumage graduel ne fonctionne pas tout à fait. Une possibilité aurait été d'utiliser un PWM hardware, mais celui-ci est venu à notre connaissance trop tard pour qu'on puisse y remédier.

Le résultat final est incomplet par rapport au but de départ. Cela est dû au fait que le temps a mal été géré. De plus, les composants sont du matériel de prêt et il n'en a pas été commandé. On ne peut pas dire que les objectifs fixés en début de projet aient servi de ligne de conduite et, par conséquent, ils n'ont pas été remplis. Toutefois, l'utilisateur peut régler l'heure de manière intuitive, ce qui était un des buts du projet. Si le programme agit comme il le devrait, le réveil n'est pas utilisable car il ne fait pas assez de lumière pour réveiller.

La principale amélioration envisageable est d'utiliser une LED plus puissante avec une alimentation externe pour que le dormeur soit réveillé. On pourrait aussi concevoir un support plus compact afin de rendre le produit véritablement commercialisable. On peut aussi imaginer ajouter un haut-parleur, bien que l'idée de départ soit de faire un réveil silencieux, pour diffuser des bruits d'ambiance ou de la musique, par exemple du reggae.

La carte Arduino est un microprocesseur qui permet de faire de l'éléctronique. La carte Arduino dispose également d'entrées analogiques et de plusieurs canaux PWM hardware, il aurait donc été avantageux pour la partie électronique de passer par ce type de microprocesseur. Il n'a cependant pas été choisi car il ne dispose pas d'une horloge, c'est ce pour quoi le RaspberryPi a été privilégié. Il est aussi tout à fait envisageable de connecter les deux et de gérer séparément ce en quoi chaque partie est la meilleure, ce qui serait une solution optimale en ce qui concerne la performance.

5. Conclusion

On peut dire que ce projet était une découverte au sens où il a permis le développement d'un artefact du début à la fin et en travaillant en indépendance. Le réveil en lui-même n'est pas un achèvement mais reste un succès de par le savoir acquis tout au long de sa réalisation. Une partie des buts ont été atteints, mais certains objectifs n'ont pas été pris en compte et n'ont pu faire l'objet d'une discussion.

6. Remerciement

Je tenais particulièrement à remercier Léonie Courbat qui a largement et généreusement participé à l'élaboration du design du support, bien qu'il n'ait pas été réalisé.

Références