Modifications

Aller à : navigation, rechercher

CellarKare

9 001 octets ajoutés, 4 juillet 2016 à 11:39
m
Révocation des modifications de Admin (discussion) vers la dernière version de Quentin
* Documenter les différentes étapes (tutoriel), et fournir les fichiers sources nécessaires aux différentes étapes.
= Mark (1): Le matos et un premier proto Arduino=
Le premier prototype sera relativement simple: la station affichera la température et l'humidité ambiante sur un écran LCD, le tout piloté par un Arduino Uno.
Sur le schéma du capteur DHT, la résistance de 10K sert de [https://fr.wikipedia.org/wiki/R%C3%A9sistance_de_rappel "résistance pull-up"].
'''Cette résistance n'est pas nécessaire dans le cas du capteur AM2302''' (la version avec câble et boitier en plastique, comme sur la photo). Cette version [http://www.adafruit.com/product/393 intègre une résistance de 5.1K dans le boîtier], qui connecte déjà VCC et DATA, merci Patrick pour l'info ;-).
<div style="overflow: hidden">[[Fichier:Cellarkare mark1 bb.png|500px|thumb|noneleft]][[Fichier:Cellarkare_mark1.jpg|500px|thumb|noneright]]</div>
== Le code ==
La suite: la même chose avec un Raspberry pi, pour bénéficier de sa faciliter à se connecter au réseau. Ce serait quand même cool de pouvoir lire ces valeurs sur un smartphone ;-)
= Mark (2): la même chose avec un Raspberry Pi =
Le but de cette nouvelle itération: réaliser un montage équivalent avec un [https://www.raspberrypi.org/ Raspberry Pi] (RPi en abrégé). Pourquoi un RPi ?
* Parce que j'en avais un sous la main ;-)
wpa-ssid "Le_nom_(SSID)_de_votre_wifi"
wpa-psk "Le_mot_de_passe_de_votre_wifi"
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf</nowiki>
Ensuite un redémmarrage de la carte avec <code>$ sudo ifdown wlan0</code> suivi de <code>sudo ifup wlan0</code> et ça devrait être OK. On peut maintenant se passer de câble ethernet!
sudo python setup.py install</nowiki>
Au niveau du montage, rien de nouveau: le capteur doit toujours être alimenté en 3.3V ou en 5V, et le câble de lecture à une pin d'entrée/sortie (GPIO: General Purpose Input Output), ce qui donne:
<div style="overflow: hidden">[[Fichier:Cellarkare mark2 bb.png|500px400px|thumb|noneleft]][[Fichier:Cellarkare_mark2.jpg|500px|thumb|noneright]]</div>
Un test en ligne de commande permet de vérifier qu'on a une lecture sur le capteur (dans mon cas un AM2302 raccordé sur la pin GPIO 4):
<nowiki>
== Le montage ==
Au final, le montage a été fait pour gagner un maximum de place sur mon breadboard. La pin data du DHT a été mise déplacée sur la pin 5, et le reste des pin pour le LCD sont documentées dans le code.<div style="overflow: hidden">[[Fichier:Cellarkare mark2_2 bb.png|500px|thumb|noneleft]][[Fichier:Cellarkare_mark2_2.jpg|500px|thumb|noneright]]</div>
== Le code ==
* Pas de gros problème ou obstacles majeurs pour convertir le montage et le code Arduino en son équivalent RPi. Mais heureusement que l'équipe d'Adafruit a déjà programmé les bibliothèques Python qui vont bien!
* Au niveau du breadboard, ça commence à prendre de la place... Une solution: passer en [https://fr.wikipedia.org/wiki/I2C "I2C"] pour le capteur et le LCD, ce qui limite drastiquement le nombre de pins nécessaires pour le montage, et facilite la programmation. Merci Patrick pour les infos, ce sera sûrement utile pour le prochain montage ;-) !
** Par exemple, pour la température: un capteur I2C comme le [https://www.adafruit.com/products/1638 SHT15], et pour l'écran, [http://www.electrodragon.com/w/index.php?title=1602_LCD_I2C/SPI_Module la version I2C du même 1602].
** Par contre dans ce cas, il faut faire gaffe au fait que le passage en 3.3V pour la logique du RPi, le lcd reste en 5V et il faudra ajouter un [http://www.adafruit.com/products/757 convertisseurs de niveau] pour le bus si utilisé avec un capteur qui ne supporte pas 5V.
* le langage Python, c'est pas plus compliqué que le simili-C de l'Arduino une fois qu'on est rentré dedans.
= Mark (3): Start, Stop , and say Cheese =
== StartSart/Stop ==Un "problème " du RPi, c'est qu'il n'a pas de bouton ON/OFF simple, et par conséquent, l'éteindre proprement avec <code>sudo halt</code> ou <code>sudo poweroff</code> est impossible sans un accès physique (clavier et écran connectés) ou une connection SSH (connection à distance). Pas pratique pour un boitier qui sera installé dans une cave à vin...
Autrement dit, mes besoins:
# Nec plus ultra: avoir un fonctionnement similaire à un bouton ON/OFF d'un laptop par exemple: si éteint, le bouton allume, si allumé, le bouton éteint proprement le RPi, et coupe l'alimentation USB.
Bon après quelques recherches sur le net, voici le résultat:# C'est simple, il suffit de configurer le "init script" du RPi pour lancer automatiquement le programme pythonau démarrage
# Un montage simple permet d'écouter sur une des pins du GPIO, et de lancer la procédure propre d'arrêt (shutdown). Soit un bouton OFF
# Le RPi (à partir du modèle B rev2, soit Brev2, B+ et 2) possède un pinout "reset", qui n'est pas soudé de base sur la carte. Si on raccorde ces deux pins, le RPi redémarre électriquement (hard reboot). Donc sans s'éteindre proprement. L'intérêt, c'est que si le RPi est éteint mais encore sous tension, ce bouton permet de le ré-allumer. Donc un bouton ON/RESET. Voir par exemple:
#* Un montage en anglais à base de micro contrôleur (pas simple donc) sur [http://www.instructables.com/id/Raspberry-Pi-Shutdown-Button/ Instructables]
#* Un article très complet en anglais reprenant toutes les options, dont un montage assez simple de bouton unique [http://www.raspberry-pi-geek.com/Archive/2013/01/Adding-an-On-Off-switch-to-your-Raspberry-Pi sur raspberry-pi-geek.com]
#* Des solutions commerciales toutes faites commes comme [https://www.pi-supply.com/product/pi-supply-raspberry-pi-power-switch/ PiSupply] ou [http://www.mausberrycircuits.com/collections/frontpage MausberryCircuits]
Dans un premier temps, ce sera pour moi la combinaison le choix suivant: un bouton OFF, qui lors d'un appui court long éteint proprement le RPi, et lors s'un appui long active le reset. En gros la solution 2.
Mais dans un premier temps, il faut configurer le RPi pour lancer le programme au démarrage...
Il y a certainement 36 façons d'arriver au même résultat (après tout, le RPi tourne sous Linux ;-), mais dans mon cas, la solution qui me convient bien: un script d'init lance par défaut le script python localisé en <code>/usr/local/bin/pystartup.py</code>. L'intérêt de la chose: <code>pystartup.py</code> peut être un lien symbolique vers n'importe quel autre script.
Pour éviter d'encombrer le wiki, jje n'ai pas repris le script ici, mais il est dispo sur [https://gitlab.com/qberten/cellarkare/blob/master/mark3_camera/initscript_pystartup GitLab]. Pour l'installation, en ligne de commande (connecté sur le RPi):
On télécharge le script, on le déplace au bon endroit et on le rend exécutable
<nowiki>
sudo update-rc.d pystartup defaults</nowiki>
Il est ensuite possible de lancer son programme en arrière plan comme n'importe quel autre service avec <code>sudo service pystartup start</code>, de l'arrêter avec <code>sudo service pystartup stop</code>, etc
== Pinout RESET ==
Activer le "hard" reset sur le Rpi suppose un peu de soudure, mais rien de bien effrayant, 2 points suffisent. Un fois les connecteur pinout soudésoudés, quand on raccorde les deux pins (par exemple avec un interrupteur), le RPi redémarre ("hard" reset, donc attention à la carte SD...), ou s'allume (si il était raccordé au secteur).Quelques J'ai soudé les 2 pins et testé la fonction, mais comme il s'agit d'un redémarrage "hard", je n'ai finalement pas vraiment vu l'utilité dans ce projet: autant directement débrancher / rebrancher le câble d'alimentation dans ce cas. Mais à toutes fins utiles, les photos (à venir)de l'opération, avant/après.<div style="overflow: hidden">[[Fichier:Rpi_reset_before.jpg|500px|thumb|left]][[Fichier:Rpi_reset_after.jpg|500px|thumb|right]]</div>
== Bouton OFF ==
In workRien de très compliqué: on raccorde le bouton à une entrée, on configure la résistance pull-up/down interne du Rpi, on s'assure de récupérer les évènements dans le codeLes parties utiles du code: <syntaxhighlight lang="python"># La fonction qui effectuer l'arrêt propre du RPi</syntaxhighlight><syntaxhighlight lang="python" class="mw-collapsible mw-collapsed">def btn_proper_halt(pin): global off_state, cur_millis, off_millis # On lit le nombre de millisecondes courant cur_millis = time.time()*1000 # Utilise la variable globale off_state off_state = not off_state # DEBUG logging.debug("Bouton OFF_PIN pressé, son état est maintenant %s" % str(off_state)) if off_state == True: off_millis = cur_millis elif off_state == False: # DEBUG logging.debug("Bouton OFF_PIN pressé pendant {0:0.0f} ms".format(cur_millis - off_millis)) if (cur_millis - off_millis) > OFF_INTERVAL: # On a relâché le bouton avec un écart long => shutdown logging.debug("Appui long sur le bouton OFF_PIN") logging.info("Arrêt du Raspberry PI, attendre encore 10 sec avant de débrancher") subprocess.call('halt', shell=False) else: # Appuis court => on ne fait rien logging.debug("Appui court sur le bouton OFF_PIN")</syntaxhighlight> <syntaxhighlight lang="python"># Fonction de terminaison du programme (appelée lorsque le script est arrêté</syntaxhighlight><syntaxhighlight lang="python" class="mw-collapsible mw-collapsed">def terminate(): logging.debug("Terminate function started") # efface l'écran LCD lcd_dim_backlight(0.0, 0.2) lcd.clear() logging.debug("Terminate function ended")</syntaxhighlight> <syntaxhighlight lang="python"># Fonction d'initialisation</syntaxhighlight><syntaxhighlight lang="python" class="mw-collapsible mw-collapsed">def initialize(): global lcd logging.debug("Initialize function started") # Initialisation du LCD avec les variables ci-dessus lcd = LCD.Adafruit_CharLCD(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7, LCD_COLUMNS, LCD_ROWS, LCD_BACKLIGHT, invert_polarity = False, enable_pwm = True, initial_backlight = lcd_backlight) # Initialisation des autres GPIO's GPIO.setmode(GPIO.BCM) # On utilise la numérotation BCM pour les PIN # On définit la pin OFF_PIN comme entrée, et on active la résistance pull-down interne du RPi GPIO.setup(OFF_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Configure une interruption qui va détecter les mouvements up et down sur le bouton GPIO.add_event_detect(OFF_PIN, GPIO.BOTH, callback=btn_proper_halt, bouncetime=20) ...</syntaxhighlight> == Say "cheese" == Cette partie du projet s'attache à pouvoir prendre rapidement et facilement un photos des étiquettes des bouteilles entrées ou sorties de la cave. Ben oui, j'ai pas forcément une bonne mémoire, et se donner les moyens d'enregistrer facilement et rapidement l'info est le meilleur moyen de suivre son stock ;-) Cette partie est super simple à réaliser avec le rpi: on raccorde la caméra, on s'assure qu'elle est activée avec <code>sudo raspi-config</code>, et on peut ensuite l'utiliser en ligne de commande ou en python. Le site web [https://www.raspberrypi.org/documentation/usage/camera/README.md raspberrypi.org (en anglais)] explique cela très bien. On ajoute un deuxième bouton sur le breadboard, qui déclenche la prise d'une photo, et le prototype est maintenant fonctionnel. Les photos sont sauvegardées dans le répertoire courant du RPi, avec la date et l'heure pour nom de fichier. Au niveau du code, les parties utiles sont:<syntaxhighlight lang="python"># Fonction de prise d'une photo</syntaxhighlight><syntaxhighlight lang="python" class="mw-collapsible mw-collapsed">def btn_take_picture(pin): # DEBUG logging.info("Bouton CAM_PIN pressé, on prend un photo") # Construction du chemin pour la photo now = datetime.datetime.now() pic_filename = "/home/qb/pic_" + str(now) + ".jpg" # Initialisation de la camera. On ne le fait pas dans initialize() pour éviter au maximum de la laisser # allumée (et consommer) en continu. En plus, une fois activée, les lectures sur le DHT sont beaucoup # plus souvent ratées. with PICAM.PiCamera() as cam: cam.capture(pic_filename) logging.debug("Photos sauvée " + str(cam.resolution) + " dans " + pic_filename)</syntaxhighlight> == Pimp my screen == Pour ce qui est d'allumer ou éteindre l'écran, la bibliothèque CharLCD permet la modulation PWN du rétroéclairage. De quoi avoir un chouette effet de fondu au noir lorsque l'écran s'allume et s'éteint. Au niveau du code, la partie utile:<syntaxhighlight lang="python"># Module l'intensité du rétro éclairage de l'écran lcd</syntaxhighlight><syntaxhighlight lang="python" class="mw-collapsible mw-collapsed">def lcd_dim_backlight(backlight_value, time_step): global lcd global lcd_backlight # calcule l'incrément à avoir entre lcd_backlight et backlight_value stp = (lcd_backlight > backlight_value) * -2 +1 brightnesses = map(lambda x: x/10.0, range(int(lcd_backlight*10), int(backlight_value*10)+1, stp)) for bright in brightnesses: lcd_backlight = bright logging.debug("loop on bright= " + str(bright)) lcd.set_backlight(bright) time.sleep(time_step)</syntaxhighlight> == Le code et le montage == Le code source complet pour [https://gitlab.com/qberten/cellarkare/blob/master/mark3_camera/cellarkare_cam.py Mark 3 est disponible sur gitlab]. Pour ce qui est du montage, ben pas de schéma de raccordement disponible, j'ai oublié de le documenter en temps utile avant de passer à Mark 4! Donc il faudra attendre la suite... = Mark (4): We are (almost) done = == Pimp my screen, reloaded ==Une dernière petite touche intéressante: l'appareil est destiné à être mis dans une cave à vin, endroit où il va faire noir la plupart du temps. En conséquence, l'écran LCD n'a pas vraiment besoin d'être allumé en permanence, pourquoi ne pas l'allumer automatiquement quand la lumière du local dépasse un certain seuil. Une photorésistance permet de réaliser rapidement la mesure d'une intensité lumineuse, pourquoi s'en priver... Oui mais... le RPi n'a pas d'entrée analogique, comment convertir simplement la mesure sur la photorésistance en une valeur analogique, alors que le RPi n'a que des entrées digitales. Après un peu de recherche, un montage simple à base d'une capacité dont le temps de charge sera directement proportionnel à la mesure sur la photo-résistance. Tout cela est expliqué ici (en anglais): [http://www.raspberrypi-spy.co.uk/2012/08/reading-analogue-sensors-with-one-gpio-pin/] (TODO - continuer la documentation) == Buttons, buttons, we want more == Dernier petit détail, et non des moindre, Pour avoir un projet réellement fonctionnel, il va falloir travailler un peu l'interface homme-machine, et permettre un peu plus d'interaction avec le Rpi, pour par exemple permettre d'entrer le nombre de bouteilles couvertes par l'entrée ou la sortie photographiée, quitter/valider une action, ... (TODO - continuer la documentation) == We want leds == Toujours dans l'optique d'améliorer l'interaction avec l'appareil, pourquoi ne pas intégrer une led de status sur l'appareil, pour indiquer visuellement différents états, comme par exemple: une température trop basse ou trop haute est enregistrée, il n'y a pas de connection réseau, ... (TODO - à implémenter tout court ;-) == All your data are belong to us == Enfin, c'est bien beau d'afficher la température et l'humidité, c'est encore mieux de l'enregistrer quelque part pour pouvoir tracer l'historique, et intéragir plus facilement avec ces données. Après quelques investigations, c'est [http://thingspeak.com Thingspeak] qui a retenu mon attention. (TODO - à implémenter tout court ;-)
[[Catégorie:Projets]]

Menu de navigation