Conception et fabrication d’un poulailler connecté

Après la folle aventure du potager connecté, nous voici repris de connectivité aiguë !

Voici une visite guidée de 7 minutes : ( Vous pouvez vous abonner à ma chaîne : https://www.youtube.com/channel/UCtMHR7ng1EaqPY_8dGJW2TA )

Nous avons tenté de vivre en symbiose avec 5 poules pendant 6 mois, mais le bilan n’a pas été bon :

  • le partage de l’espace n’a pas été équitable, l’herbe ne s’en est pas remise et les rebords de fenêtre non plus !
  • la chasse aux œufs a un certain charme à Pâques, mais tous les jours, c’est lassant

Les contraintes liées aux poules sont :

  • L’eau
  • L’alimentation (céréales & restes)
  • La protection / isolation physique
  • La récupération des œufs

Après un tour du marché, j’ai trouvé celui ci : https://fr.eggs-iting.com/ qui actuellement ressemble plus à de la R&D qu’à un réel produit. De plus, l’algorithme de détection des œufs ne m’a pas convaincu.

Pour gérer ces contraintes, nous leur avons construit un espace clos et avons automatisé une partie des contraintes.

La construction du poulailler

Élagage de l’arbre, mise à niveau de la terre, suppression de l’ancienne barrière.
Réalisation d’un plancher pour la partie humaine du poulailler. Les deux poutrelles de béton sont un recyclage de l’ancienne cuve à fuel de la maison.
Gestion des contraintes arboricoles
Titouan à l’oeuvre
Fixation de la paroi verticale
La prison prend forme
La bâche, c’est pour nous protéger du soleil.

Les contraintes automatisées sont :

  • la distribution des céréales
  • la distribution de l’eau
  • la détection de la présence d’œufs (on ne va pas se mentir, plus qu’un besoin, c’était surtout un défi technique)

L’alimentation en céréales

Nos poules ont tendance à sortir 5 grains de la gamelle quand elles en mangent un, l’idée était donc de réguler quotidiennement la quantité de céréales dans leur gamelle tout en profitant d’une grande autonomie grâce à une réserve de plusieurs dizaines de kilos de céréales à l’abri. Le passage de la réserve se fait par une vis sans fin imprimée, et contrôlée par un Raspberry Pi zéro.

Impression 3D d’un tronçon de vis sans fin
Les 4 tronçons assemblés sur une tige filetée
Test de rotation dans un tuyau PVC 50mm
Validation de la motorisation
Réalisation d’un trou oblong « gros doigt »… pour le passage des céréales
Erwan toujours partant pour ce genre de projet !
Fixation du moto-réducteur à l’aide de 6 vis
Utilisation d’une pièce de jonction en aluminium permettant de régler les problèmes d’alignement d’axes.
Construction du silo à céréales coté appentis
Vue du haut. La fente donnera sur le trou oblong du tuyau de PVC
Perçage de la cloison de l’appentis, qui donne directement dans la gamelle des poules
Même trou, vu de l’intérieur de l’appentis
et vu de la mangeoire

Contrôle du poulailler via un Raspberry Pi

Comme pour le potager connecté, je suis passé par un raspberry pi, mais un modèle zéro, pour tester.

Le boitier « récup » réalisé par Titouan
Le boitier installé dans le poulailler : reste à lui faire une protection…
Quand votre poulailler ping en local en ipv6 !

Pour mettre en musique tout ça, j’ai utilisé Domoticz. J’ai détaillé son installation pour le poulailler.

Le raspberry pi est alimenté par l’énergie solaire. Un contrôleur de charge gère le panneau solaire et la batterie. 12V pour le moteur et la lampe, 5V pour le raspberry pi.

La distribution de l’eau

Pour l’alimentation en eau, je me suis contenté d’exploiter le réseau d’eau potable de ma maison avec un abreuvoir à mouton. Le bouchon de purge permet de nettoyer très facilement (mais manuellement) l’abreuvoir.

Le mécanisme est comparable au mécanisme d’une chasse d’eau.

La détection de la présence des œufs

Que diriez vous d’un poulailler qui vous prévient quand un œuf a été pondu ?

Cette fonctionnalité est plus un défi technique qu’un réel besoin, en effet, mes poules pondent le matin autour de 10:30 et chantent après avoir pondu.

Voici les principales étapes :

  • Découverte d’un service de machine learning en ligne
  • Codage de la chaîne complète
  • Notification

Nous avons observé que les poules pondent les œufs toujours au même endroit. Une caméra Pi fixe jouera parfaitement le jeu du début de la chaîne.

L’API Vision de Google dispose d’une interface web permettant de valider l’idée :

Et l‘API object-localizer permet de localiser les éléments identifiés :

Avec cette API, j’ai tout ce qu’il faut pour implémenter ma détection des œufs.

Voici les différentes parties de mon script Python :

  • authentification à GCP
  • allumage de la lumière
  • prise de la photo
  • extinction de la lumière
  • transmission de la photo à l’API de Google
  • réception de la réponse (flux Json ce dessus)
  • parsing du flux (analyse) pour savoir si des œufs ont été découverts (mots clés : food & egg) et si oui, combien et où
  • ajout des zones sur la photo où des œufs ont été découverts
  • mise à jour du capteur « nombre d’œufs » dans Domoticz à des fins statistiques
  • envoi de la notification par Pushbullet (photo + nombre d’œufs)

Et le résultat en image :

Si par hasard, une poule est en train de pondre au moment de la prise de la photo, alors je suis aussi prévenu :

Pour améliorer la reconnaissance, j’ai essayé une caméra infra-rouge, mais les mots clés associés à une photo noir et blanc ne sont pas adaptés au poulailler connecté !

J’ai donc réglé le problème en ajoutant une lampe LED qui s’allume comme un flash.

Dans Domoticz, j’ai créé des capteurs correspondant au nombre d’œufs :

"""
Script inspire des ressources suivantes : 
Google Vision API Tutorial with a Raspberry Pi and Raspberry Pi Camera.  See more about it here:  https://www.dexterindustries.com/howto/use-google-cloud-vision-on-the-raspberry-pi/
This script uses the Vision API's logo detection.

Prerequis :
 - chmod 750 poulailler.py (donner les droits d'execution sur ce script)
 - installer python
    - apt-get update && sudo apt-get install python3-picamera
    - apt-get install python3-pip
 - installer le client pour Google cloud vision : https://cloud.google.com/vision/docs/libraries
 - sudo apt-get update && sudo apt-get install google-cloud-sdk
 - sudo pip install --upgrade pip
 - sudo pip install --upgrade google-api-python-client
 - sudo apt-get install python-picamera
 - sudo pip install --upgrade google-cloud-vision
 - sudo pip install --upgrade oauth2client
 - sudo pip install pushbullet.py
 
 - configuration materielle : 
   - sudo raspi-config / enable Camera
 
 - copie de la font arialbd.ttf
 
execution : 
python poulailler.py

"""

import argparse
import base64
import picamera
import os
import json

from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
from pushbullet import Pushbullet

import requests

from PIL import Image, ImageDraw, ImageFont

import time
import datetime

from RPi import GPIO

def is_time_between(begin_time, end_time, check_time=None):
    # If check time is not given, default to current UTC time
    check_time = check_time or datetime.datetime.utcnow().time()
    if begin_time < end_time:
        return check_time >= begin_time and check_time <= end_time
    else: # crosses midnight
        return check_time >= begin_time or check_time <= end_time

def takephoto():
    camera = picamera.PiCamera()
    camera.capture('image.jpg')
    #os.system('fswebcam -r 1280x720 --save image_temp.jpg')

def main():
    #On desactives les  warnings et on lui precise que l on travaille avec les numeros des gpio et non le numero des pins
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)

    #ON precise que le gpio 14 est en mode sortie, elle sert ici a envoyer du courant
    GPIO.setup(18, GPIO.OUT)

    #Le code meme du programme, LOW correspond a eteindre et HIGH a allumer
    GPIO.output(18, GPIO.HIGH)

    dateiso =  datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S')

    takephoto() # First take a picture
    """Run a label request on a single image"""

    credentials = GoogleCredentials.get_application_default()
    service = discovery.build('vision', 'v1', credentials=credentials)


    #ouverture de l'image
    im = Image.open("image.jpg")

    with open('image.jpg', 'rb') as image:
        image_content = base64.b64encode(image.read())
        service_request = service.images().annotate(body={
            'requests': [{
                'image': {
                    'content': image_content.decode('UTF-8')
                },
                'features': [{
                    'type': 'OBJECT_LOCALIZATION'
                }]
            }]
        })
        response = service_request.execute()

	#save json in file
	with open('data' + dateiso + '.json', 'w') as outfile:
		json.dump(response, outfile)
		
		jsonr = json.dumps(response, indent=4, sort_keys=True)	     
		input_dict = json.loads(jsonr)
		
	nbOeufs = 0

	try:
		for k in input_dict['responses'][0]['localizedObjectAnnotations']:
			name=k['name']
			if name == 'Egg' or name == 'Food':

				coordonnees = k['boundingPoly']['normalizedVertices']
				for j in coordonnees: 
					rx1 = coordonnees[0]['x']
					rx2 = coordonnees[1]['x']
					ry1 = coordonnees[0]['y']
					ry2 = coordonnees[2]['y']
							
					x1 = rx1 * im.size[0]
					x2 = rx2 * im.size[0]
					y1 = ry1 * im.size[1]
					y2 = ry2 * im.size[1]

					if name == "Egg" or name == "Food":                
						color = 500 #rouge   #20000 #vert
					elif name == "Fauna" or  name == "Bird":                
						color = 99900000 #violet
					else:                
						color = 1 #noir

					draw = ImageDraw.Draw(im)
					draw.line((x1, y1, x2, y1), fill=color, width=3)
					draw.line((x2, y1, x2, y2), fill=color, width=3)
					draw.line((x2, y2, x1, y2), fill=color, width=3)
					draw.line((x1, y2, x1, y1), fill=color, width=3)

					font = ImageFont.truetype("/home/pi/arialbd.ttf", 20)
					draw.text((x1 + 3, y1+3),name,color, font)

					del draw
					
				nbOeufs+=1
	except:
		nbOeufs = 0

	# write to stdout
	im.save("image.jpg")

	if nbOeufs >0:
		pb = Pushbullet('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
		push = pb.push_note("Cot cot", "Nb d'oeufs : " + str(nbOeufs) )

		with open("image.jpg", "rb") as pic:
			file_data = pb.upload_file(pic, "picture.jpg")
			push = pb.push_file(**file_data)

	print nbOeufs
	fichier = open("log.txt", "a")
	fichier.write("\n" + dateiso +  " " + str(nbOeufs))
	fichier.close()

	#copy nb vers Domoticz
	if is_time_between(datetime.time(10,55), datetime.time(11,05)):
		requete="http://127.0.0.1/json.htm?type=command&param=udevice&idx=3&svalue="+str(nbOeufs)
		r=requests.get (requete)

		requete="http://127.0.0.1/json.htm?type=command&param=udevice&idx=5&svalue="+str(nbOeufs)
		r=requests.get (requete)

	try:
		label = response['responses'][0]['logoAnnotations'][0]['description']
	except:
		label = "No response."        
	#print label

	#copy image
	nom, ext = os.path.splitext("image.jpg")
	os.rename("image.jpg", nom + '_' + dateiso + ext)

	#on eteint la lampe
	GPIO.output(18, GPIO.LOW)

if __name__ == '__main__':
    main()

Réalisation d’une étagère murale style Steampunk

Nous avons profité des vacances de noël pour refaire la peinture de la « salle télé » avec une inspiration « suédoise » pour la déco et les couleurs. J’ai conservé une lampe nue accrochée au bout de 2 mètres de fils électrique, sans trop savoir comment l’exploiter : la créativité vient des contraintes !

Je scanne Internet à la recherche d’une solution d’éclairage murale et je tombe sur plusieurs idées d’étagères équipées de lampe.

Après plusieurs soirées de pérégrination sur Pinterest et Etsy, le style Steampunk ressort, assez loin du style suedois… L’étagère prend forme dans ma tête.

On passe aux croquis et à la validation familiale.

Le choix est fait : ce sera une étagère « plomberie » diamètre 20/27 avec des étagères en chêne clair.

J’ai commandé la plupart des articles sur Etsy, dont certains assez étonnants, comme une vanne hydraulique qui fonctionne comme un interrupteur électrique.

Le matériel arrive au compte-goutte.
Montage provisoire pour se projeter.
Passage de passe câble pour permettre aux fils électriques de passer par le tuyau. J’ajoute des feutres pour ne pas marquer le mur pendant le montage.

Montage de la première platine.
Ensuite, tout s’enchaîne logiquement
Première victoire : la première lampe s’allume. Le câble électrique pour raccorder la deuxième ampoule est déjà passé mais pas encore raccordé !
Passage d’un autre câble électrique pour permettre une commande future à partir de la fameuse vanne électrique.
L’avantage de la plomberie décorative, c’est qu’elle n’a pas besoin d’être étanche. On bénéficie donc d’une certaine souplesse au montage
Deuxième victoire : la deuxième ampoule !
Test sur les deux lampes
Débits pour les étagères, puis dégauchissage, rabotage et sciage
Test de placement des étagères

Reste à faire :

  • vernir et fixer les trois étagères
  • réceptionner manomètre et vanne et les fixer
  • créer la table basse dans le même style : L’article
  • aménager le placard à gauche de cette étagère (L’article) et exploiter la vanne…
  • mettre à jour cet article 😉

Réception et fixation d’un thermomètre, histoire de joindre l’utile à l’agréable.
Structure de la table dans le même style

Bétonnage de la cave

Qui dit nouvelle maison, dit nouvelle cave.

Comme dans l’ancienne maison, la moitié de la cave était déjà bétonnée.

Par contre cette fois :

– la première partie est bétonnée en pente…, mais je ne vais pas y revenir, trop compliqué

+ la partie non bétonnée est quasi décaissée.

Après un micro décaissage de 600Kg, j’ai passé des gaines pour alimenter en électricité mes machines à bois sans que des rallonges traînent par terre.

J’ai utilisé un rouleau de film alvéolé le long des murs pour permettre à la sous dalle de respirer en évitant les remontées d’humidité.

puis une bâche plastique sous dalle pour éviter que le BAN (Béton auto nivelant) ne sèche trop vite.

Puis, fabrication d’une rampe pour éviter que le béton ne troue la bâche à son arrivée :

Ensuite, le grand jour, la livraison (après deux décalages pour cause de neige…)

 

Après pulvérisation d’un produit de cure, toujours pour éviter la sur-évaporation créatrice de fissures.

Prochaine étape, fabrication d’un meuble sur mesure Acier / Bois pour répondre à plusieurs besoins :

– rangement des caisses de matériel dans la partie basse

– établi de 15 mètres de long et 50cm de profondeur, pour les machines stationnaires (touret, scie à chant tourner…)

– rangement vertical, via des planches à clous

– rangement en hauteur pour les planches et autre matière première encombrante.

Voici un croquis.

J’ai déjà fait un meuble similaire dans la cave de l’ancienne maison.