1. Introduction

Comme son nom l’indique, le but de ce projet est de créer une machine qui arrose les plantes toute seule, pour les gens têtes en l’air comme moi qui ont une forte tendance à oublier d’arroser leurs plantes. Mais comme les elles apprécient aussi la compagnie, elles auront droit en plus à une berceuse sur pression d'un bouton. L’idée est donc de placer un "détecteur d’humidité" dans la terre (deux câbles plantés), qui permet de détecter quand la terre devient trop sèche et a besoin d’arrosage. Pour ré-humidifier la terre, on active une pompe qui prend l’eau du réservoir pour la mettre dans le pot. Une LED permet également de prévenir lorsque le réservoir est vide, pour ne pas oublier de le remplir. Un haut-parleur permet de jouer la musique, et deux boutons servent à enclencher la musique ou l’arrosage à volonté. Le tout est relié au RaspberryPi qui permet de tout gérer par code.

2. Matériel et méthode

2.1. Matériel

  • 1 Pot de fleur (rempli...)
  • 1 Cache-pot ou autre soucoupe, assiette ou bol dans lequel mettre le pot pour ne pas mettre de l'eau partout
  • 1 Pompe
  • 1 Tuyau 60 cm
  • 1 Haut-parleur
  • 1 (ou deux pendant la période test) Réservoir (n'importe quel saladier ou seau peut faire l'affaire, tant qu'il est assez grand pour que la pompe soit recouverte d'eau et assez petit pour que les câbles puissent atteindre la pompe en utilisant une quantité d'extensions raisonnable)
  • 25 Câbles
  • 6 Extensions pour les câbles
  • Scotch/Étiquettes (pour différencier les câbles de détection)
  • 4 Résistances 330 Ohms
  • 2 Boutons de couleurs différentes
  • 2 LEDs de couleurs différentes
  • 1 Rallonge non-nécessaire à votre survie (parce qu'elle ne va pas conserver son intégrité physique bien longtemps)
  • 1 Raspberry
  • 1 Breadboard de taille moyenne
  • 1 SparkFun Beefcake Relay Control Kit (achetable ici: https://www.sparkfun.com/products/13815)
  • 1 Convertisseur analogue/digital MCP3008
  • 1 Sparkfun Pi Wedge (un gros T rouge qui sert d'intermédiaire entre le Raspberry et la breadboard)
  • 1 Multimètre pour aider au débugage

2.2. Méthodes

Le plus simple pour mettre à bien un projet de ce type, et au final n'importe quel projet, est de le découper en plus petits projets à mener à bien les uns après les autres avent de les rassembler pour constituer le vrai projet. Cela comporte plusieurs avantages: déjà cela aide à conserver sa motivation durant le projet, car on a beaucoup moins l'impression de n'arriver à rien, et toutes les étapes réussies apportent leur lot de satisfaction, mais cela aide aussi d'un point de vue pratique pour le débugage. En effet, si le programme individuel pour allumer une LED fonctionne, si dans le programme pour allumer une LED avec un bouton elle ne s'allume pas, ce n'est pas la faute de la LED. Et en cas de doute, on peut utiliser un multimètre ou un voltomètre pour déterminer où le courant passe. J'ai donc séparé mon projet en plusieurs étapes:

A.

D'abord j'ai vérifier que je savais monter une LED sur la breadboard. C'est simple à faire, donc tout en haut de la liste. J'ai commencé par connecter une LED bleue à la breadboard, la relier par son côté + à la pin G27, connecter une résistance de l'autre côté de la LED, puis la connecter au ground à l'aide d'un câble. Pour allumer la LED, il faut expliquer dans le programme qu'il s'agit d'une sortie connectée à la pin G27 et qu'il faut lui envoyer du courant. Pour ce faire, voici ce qu'on peut écrire:

import RPi.GPIO as GPIO
GPIO.setmode (GPIO.BCM)

try:  
       # main code
       LED=27
       GPIO.setup(LED, GPIO.OUT)
		
       while True:
               GPIO.output (LED, GPIO.HIGH)

except KeyboardInterrupt:  
       # ctrl+c
       pass
 
except Exception as error: 
       # error
       print error

finally:  
       # always clean up before leaving
       GPIO.cleanup()
B.

La prochaine étape est d'activer la LED grâce à un bouton. Le bouton permet au courant de passer uniquement lorsque l'on appuie dessus. Je l'ai donc branché à la breadboard, l'ai connecté sur du 3.3V et l'ai relié à la pin G17. J'ai également mis une résistance connectée au ground du côté du câble connecté à la pin. Voici le code que j'ai utilisé:

import RPi.GPIO as GPIO  
from time import sleep
# use microcontroller numbering 
GPIO.setmode(GPIO.BCM)

try:  
       # main code
       LED=27
       BUTTON=17
       GPIO.setup(LED, GPIO.OUT)
       GPIO.setup(BUTTON, GPIO.IN)
       
       while True:
               if GPIO.input(BUTTON) == GPIO.HIGH : 
                       print("bouton high")
                       GPIO.output (LED, GPIO.HIGH)
               else: 
                       print("bouton low")
                       GPIO.output (LED, GPIO.LOW)
                       sleep(0.1)
except KeyboardInterrupt:  
        # ctrl+c
       pass
       
except Exception as error: 
       # error
       print error
 
finally:  
       # always clean up before leaving
       GPIO.cleanup()

La fonction "print" sert principalement à vérifier que le tout fonctionne, elle permet de voir lorsque le bouton s'enclenche. La fonction "sleep" quant à elle permet au raspberry de se reposer un peu, c'est-à-dire de ne pas devoir renouveler continuellement l'état affiché.

C.

Ensuite, l'étape la plus simple de toutes (oui, même plus simple que que la première, là il fallait comprendre ce qu'on faisait pour s'habituer au principe du GPIO): refaire exactement la même chose qu'avant pour l'avoir à double. En tant que tel le programme n'est pas très utile, mais si on part du principe qu'au lieu d'allumer une LED le programme doit jouer une musique, et qu'il suffit de rajouter un câble relié au relais pour activer la pompe, on réalise que le résultat est en réalité assez proche du projet souhaité. Bien sûr il faut utiliser d'autres pins dans le code et le montage, et leur donner d'autres noms. Voici mon code:

import RPi.GPIO as GPIO  
from time import sleep 
# use microcontroller numbering 
GPIO.setmode(GPIO.BCM)
 
try:  
       # main code
       LEDW=27
       LEDM=26
       BWATER=17
       BMUSIC=16
       GPIO.setup(LEDW, GPIO.OUT)
       GPIO.setup(LEDM, GPIO.OUT)
       GPIO.setup(BWATER, GPIO.IN)
       GPIO.setup(BMUSIC, GPIO.IN)

       while True:
               if GPIO.input(BWATER) == GPIO.HIGH : 
                       print("bouton water high")
                       GPIO.output (LEDW, GPIO.HIGH)
               else: 
                       print("bouton water low")
                       GPIO.output (LEDW, GPIO.LOW)
               if GPIO.input(BMUSIC) == GPIO.HIGH :
                       print ("bouton music high")
                       GPIO.output(LEDM, GPIO.HIGH)
               else:
                       print ("bouton music low")
                       GPIO.output(LEDM, GPIO.LOW)
               sleep(0.1)
except KeyboardInterrupt:  
       # ctrl+c
       pass
 
except Exception as error: 
       # error
       print error
 
finally:  
       # always clean up before leaving
       GPIO.cleanup()
D.

Maintenant, une partie un peu moins drôle: récupérer un code pour lire les informations reçues par le convertisseur analogue digital (MCP3008). En gros il faut expliquer dans le programme comment comprendre les données, c'est pas simple. Du coup, on peut "s'inspirer" d'un code écrit par quelqu'un d'autre, en l'occurence j'ai pris le mien sur ce site: https://learn.adafruit.com/reading-a-analog-in-and-controlling-audio-volume-with-the-raspberry-pi?view=all Evidemment, j'ai ignoré toute la partie en rapport avec le potentiomètre, puisqu'il n'en est pas question dans mon projet. J'ai également connecté de façon identique la puce au GPIO, seuls les canaux utilisés sont différents. Chez moi ce sont les canaux 0 et 7. Voilà à quoi ressemble le code que j'ai récupéré:

#!/usr/bin/env python

# Written by Limor "Ladyada" Fried for Adafruit Industries, (c) 2015
# This code is released into the public domain

import time
import os
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)
DEBUG = 1

# read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7)
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
       if :
               return -1
       GPIO.output(cspin, True)

       GPIO.output(clockpin, False)  # start clock low
       GPIO.output(cspin, False)     # bring CS low

       commandout = adcnum
       commandout |= 0x18  # start bit + single-ended bit
       commandout <<= 3    # we only need to send 5 bits here
       for i in range(5):
               if (commandout & 0x80):
                       GPIO.output(mosipin, True)
               else:
                       GPIO.output(mosipin, False)
               commandout <<= 1
               GPIO.output(clockpin, True)
               GPIO.output(clockpin, False)
       
       adcout = 0
       # read in one empty bit, one null bit and 10 ADC bits
       for i in range(12):
               GPIO.output(clockpin, True)
               GPIO.output(clockpin, False)
               adcout <<= 1
               if (GPIO.input(misopin)):
                       adcout |= 0x1

       GPIO.output(cspin, True)
       
       adcout >>= 1       # first bit is 'null' so drop it
       return adcout

# change these as desired - they're the pins connected from the
# SPI port on the ADC to the Cobbler
SPICLK = 18
SPIMISO = 23
SPIMOSI = 24
SPICS = 25

# set up the SPI interface pins
GPIO.setup(SPIMOSI, GPIO.OUT)
GPIO.setup(SPIMISO, GPIO.IN)
GPIO.setup(SPICLK, GPIO.OUT)
GPIO.setup(SPICS, GPIO.OUT)

# 10k trim pot connected to adc #0
potentiometer_adc = 0;

last_read = 0       # this keeps track of the last potentiometer value
tolerance = 5       # to keep from being jittery we'll only change
                    # volume when the pot has moved more than 5 'counts'@@
E.

A partir de là, il faut définir les canaux utilisés, et tenter de lire les valeurs fournies par le MCP3008. Pour ce faire, j'ai choisis d'appeler les canaux water_adc et humidity_adc, et j'ai demandé au programme d'afficher la valeur qu'il lisait depuis ces canaux, en utilisant à nouveau la commande print. Le code est presque le même qu'avant, mais les dernières lignes sont maintenant adaptés à mes besoins:

import time
import os
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)
DEBUG = 1

# read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7)
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
       if :
               return -1
       GPIO.output(cspin, True)

       GPIO.output(clockpin, False)  # start clock low
       GPIO.output(cspin, False)     # bring CS low

       commandout = adcnum
       commandout |= 0x18  # start bit + single-ended bit
       commandout <<= 3    # we only need to send 5 bits here
       for i in range(5):
               if (commandout & 0x80):
                       GPIO.output(mosipin, True)
               else:
                       GPIO.output(mosipin, False)
               commandout <<= 1
               GPIO.output(clockpin, True)
               GPIO.output(clockpin, False)

       adcout = 0
       # read in one empty bit, one null bit and 10 ADC bits
       for i in range(12):
               GPIO.output(clockpin, True)
               GPIO.output(clockpin, False)
               adcout <<= 1
               if (GPIO.input(misopin)):
                       adcout |= 0x1

       GPIO.output(cspin, True)
       
       adcout >>= 1       # first bit is 'null' so drop it
       return adcout

# change these as desired - they're the pins connected from the
# SPI port on the ADC to the Cobbler
SPICLK = 18
SPIMISO = 23
SPIMOSI = 24
SPICS = 25

# set up the SPI interface pins
GPIO.setup(SPIMOSI, GPIO.OUT)
GPIO.setup(SPIMISO, GPIO.IN)
GPIO.setup(SPICLK, GPIO.OUT)
GPIO.setup(SPICS, GPIO.OUT)

# 10k trim pot connected to adc #0
water_adc = 0;
humidity_adc = 7;

while True:
       print readadc(water_adc, SPICLK, SPIMOSI, SPIMISO, SPICS)
       print readadc(humidity_adc, SPICLK, SPIMOSI, SPIMISO, SPICS)
       
       #do nothing for a half second
       time.sleep(0.5)
F.

Maintenant, if faut trouver comment enclencher une musique. Pour que le RaspberryPi puisse lire un fichier mp3, il faut installer le matériel nécessaire en utilisant cette commande:

sudo apt-get install alsa-utils mpg123

On peut désormais jouer un son grâce à cette commande:

os.system('mpg123 -q <nom du fichier audio> &')

Le fichier audio doit se trouver au même endroit que le programme pour que celui-ci puisse le trouver.

G.

À présent, il faut préparer le relai qui permettra de contrôler la pompe depuis le RaspberryPi. Pour ce faire, il faut prendre la rallonge, dénuder un bout au milieu, ouvrir la phase (le câble où passe le courant, brun-gris en tout cas dans mon cas), dénuder quelques millimètres des deux côtés, entortiller les bouts, dévissez les petites vis dans les parties bleues, insérer les deux bouts dans les trous du côté LOAD, revisser. Ensuite on peut glisser un câble dans chacun des trous de l'autre côté et revisser de la même manière. Voilà une image du relai une fois branché:

H.

L'étape finale: le rassemblage, tant du code que des montages. Il faut donc rassembler les morceaux de codes préexistants et les coordonner pour qu'il accomplissent leurs tâches respectives. La LED qui simulait à la base la musique (LEDM) disparaît du code, son pin est maintenant utilisé par le LED d'alerte signalant que le réservoir est vide, et le bouton active à présent réellement la musique. Il faut encore ajouter à la LED qui simulait la présence de la pompe le relais la contrôlant , c'est-à-dire brancher le câble CNTRL du relais entre la branche + de la LED et le câble qui vient du pin G27.Il ne faut pas oublier d'installer le tuyau sur la pompe, la mettre au fond du réservoir et placer le bout du tuyau dans le pot. On doit également étiqueter les câbles détecteurs d'humidité et d'eau, et les placer respectivement dans le pot et le réservoir. On place égalemt un câble 3.3V dans chacun des deux récipients pour pouvoir capter du courant. J'ai également placé les conditions pour l'activation de chaque élément du programme et rajouté l'activation de la LED rouge d'alerte dans le cas où le réservoir doit être rempli suivie de la commande "continue", qui fait en sorte que le Raspberry passe directement à la fin de la boucle, et donc ne puisse pas activer la pompe.

Voici le code final:

#!/usr/bin/env python

# Code controlling MCP3008 written by Limor "Ladyada" Fried 
# for Adafruit Industries, (c) 2015

 
# import some libraries
import time
import os
import RPi.GPIO as GPIO  
from time import sleep

# use microcontroller numbering 
GPIO.setmode(GPIO.BCM)

DEBUG = 1

# read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7)
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
       if :
               return -1
       GPIO.output(cspin, True)

       GPIO.output(clockpin, False)  # start clock low
       GPIO.output(cspin, False)     # bring CS low

       commandout = adcnum
       commandout |= 0x18  # start bit + single-ended bit
       commandout <<= 3    # we only need to send 5 bits here
       for i in range(5):
               if (commandout & 0x80):
                       GPIO.output(mosipin, True)
               else:
                       GPIO.output(mosipin, False)
               commandout <<= 1
               GPIO.output(clockpin, True)
               GPIO.output(clockpin, False)

       adcout = 0
       # read in one empty bit, one null bit and 10 ADC bits
       for i in range(12):
               GPIO.output(clockpin, True)
               GPIO.output(clockpin, False)
               adcout <<= 1
               if (GPIO.input(misopin)):
                       adcout |= 0x1

       GPIO.output(cspin, True)
       
       adcout >>= 1       # first bit is 'null' so drop it
       return adcout

# Pins connected from the SPI port on the ADC to the Cobbler
SPICLK = 18
SPIMISO = 23
SPIMOSI = 24
SPICS = 25

# set up the SPI interface pins
GPIO.setup(SPIMOSI, GPIO.OUT)
GPIO.setup(SPIMISO, GPIO.IN)
GPIO.setup(SPICLK, GPIO.OUT)
GPIO.setup(SPICS, GPIO.OUT)

# Wires connected to adc, water: 0 humidity: 7
water_adc = 0;
humidity_adc = 7;

try:  
       # main code
       LEDW=27
       LEDFILL=26
       BWATER=17
       BMUSIC=16
       GPIO.setup(LEDW, GPIO.OUT)
       GPIO.setup(LEDFILL, GPIO.OUT)
       GPIO.setup(BWATER, GPIO.IN)
       GPIO.setup(BMUSIC, GPIO.IN)
        	
       while True:
               print readadc(humidity_adc, SPICLK, SPIMOSI, SPIMISO, SPICS)
               sleep(0.5)

               # if there's no contact in the water, it's empty 
               # -> stop everything and light up the LED to be filled
               if readadc(water_adc, SPICLK, SPIMOSI, SPIMISO, SPICS) < 1000 :
                       GPIO.output (LEDFILL, GPIO.HIGH)
                       continue
               else:
                       GPIO.output (LEDFILL, GPIO.LOW)
       
               # check humidity, if low -> activate test led: LEDW 
               # (will be replaced by pump when relay ready)
               if readadc(humidity_adc, SPICLK, SPIMOSI, SPIMISO, SPICS) < 800 :
                       GPIO.output (LEDW, GPIO.HIGH)
                       sleep (5)
               GPIO.output (LEDW, GPIO.LOW)
       
               # button to activate pump at will, test led for the moment: LEDW
               if GPIO.input(BWATER) == GPIO.HIGH : 
                       print("button water high")
                       GPIO.output (LEDW, GPIO.HIGH)
               else: 
                       GPIO.output (LEDW, GPIO.LOW)
               # button to play music at will
               if GPIO.input(BMUSIC) == GPIO.HIGH :
                       print ("button music high")
                       os.system('mpg123 -q  okami_orcas_themes.mp3 &')

except KeyboardInterrupt:  
       # ctrl+c
       pass
       
except Exception as error: 
       # error
       print error

finally:  
       # always clean up before leaving
       GPIO.cleanup()

Et maintenant le montage final:

Montage_final_arroseur.fzz

Je n'ai pas trouvé le convertisseur sur le logiciel que j'ai utilisé pour faire le schéma, je l'ai donc remplacé par la puce marquée du numéro 556 puisqu'elle avait la bonne forme. Pour la même raison les câbles bruns qui sortent de la breadboard symbolisent la connection au relai. Les câbles bleu clair quant à eux sont les détecteurs d'eau et d'humidité.

Voilà une idée de ce à quoi ressemblait mon projet en phase de test, pour ne pas mettre de l'eau partout, les câbles détecteur d'eau sont plantés dans du papier ménage imbibé d'eau et la pompe n'est pas dans le réservoir mais juste posée sur la table:

3. Résultats

Lorsque l'on appuie sur le bouton jaune, la musique s'enclenche. Lorsque l'on appuie sur le bouton bleu, la LED bleue s'allume et la pompe s'enclenche tant que le bouton est enfoncé. Si le courant entre les deux câbles détecteurs d'eau est trop faible, le programme se bloque et une LED rouge s'allume jusqu'à ce que le courant passe de nouveau. Si le courant entre les deux câbles détecteurs d'humidité est trop faible, la LED bleue s'allume est la pompe s'enclenche pour 5 secondes.

4. Discussion

Malgré un départ quelque peu difficile, j'ai réussi à mener à bien la plupart de mes idées initiales. Toutefois je voulais à la base rajouter une fonction qui jouait la musique trois fois par jour en plus du moment où l'on appuie sur le bouton. Cela c'est avéré être inutilement compliqué et au final assez peu utile pour l'ensemble du projet, le but principal étant d'arroser une plante. Cela a donc été abandonné. Toutefois, en réalisant ce projet je suis devenue beaucoup plus à l'aise avec le fonctionnement du RaspberryPi et des circuits électriques. Un défaut assez important de mon montage est qu'il est assez désordonné, et il n'est pas forcément facile de trouver à quoi sert chaque câble au premier coup d'œil. J'ai tenté d'utiliser un code couleur pour les mieux voir la fonction de chaque câble, mais il s'est avéré que je n'en avait pas assez de chaque couleur pour un résultat efficace, et j'ai jugé inutile de racheter des câbles uniquement dans ce but. La LED bleue servait à la base à simuler la présence de la pompe, et j'aurai donc du en théorie la retirer une fois celle-ci obtenue, mais il se trouve qu'avoir une LED pour signifier que la pompe est active est assez pratique, cela permet de ne pas se fier uniquement aux sons ou aux vibrations pour savoir si elle fonctionne lors des phases de tests.

5. Conclusion

L'ensemble du projet est, du moins selon moi, une réussite. Le but premier -arroser une plante uniquement lorsqu'elle en a besoin- a été atteint. Réaliser ce projet m'a permis de mieux comprendre la théorie vue en cours, et d'apprendre à intégrer du code trouvé sur internet à mon propre code.