Archives de Catégorie: Raspberry Pi

Migration des repository du blog

Jusqu’à présent le code que j’ai pu vous présenter était hébergé chez google code. Or Google a décidé de fermer ce service.

J’ai donc décidé de migrer les repository mes-projets-arduino et mes-projets-raspberry vers gitlab. gitlab a l’avantage de permettre de créer des projets privés dans sa formule gratuite.

Pour ce faire j’ai suivi les grandes lignes de ce tutoriel.

Dans les grandes lignes j’ai rééxtrait mes projets depuis google code avec git-svn

git svn clone https://mes-projets-raspberry.googlecode.com/svn/trunk mes-projets-raspberry

j’ai vérifié que rien n’était oublié par rapport à mon répertoire local (normalement pas nécessaire, mais bon …). Pour ça j’ai utilisé l’excellent « meld ».

enfin j’ai fait un push des sources

git remote add origin git@gitlab.com:steftech/mes-projets-raspberry.git
git push -u origin master

Mes sources sont accessibles maintenant via :
https://gitlab.com/steftech/mes-projets-raspberry
https://gitlab.com/steftech/mes-projets-arduino

Cloner une télécommande Radio Fréquence (433MHz) – Part 3 Le cas Blyss

3615 Ma Life ou chronique d’une débâcle annoncée

Ce vendredi je suis passé chez Casto***** et j’ai acheté un pack de 3 prises radio commandées « Blyss » dans le but de les commandes à distance par le montage précédent.
Après avoir pris connaissance du fonctionnement normal des prises avec le montage d’acquisition (via Audacity) je me suis rendu compte que les trames envoyées changeaient à chaque envoi (tout du moins une partie …). Aye !

La cavallerie

Heureusement de bienveillants hackers (que leur noms soient glorifiés !) avaient déjà fait l’analyse du protocole :

[Hack – partie 1] Reverse engineering des interrupteurs domotique Blyss

[Hack – partie 2] Reverse engineering des interrupteurs domotique Blyss


http://forum.arduino.cc/index.php?topic=110677.0

Tout est dit dans les articles sur la méthode d’analyse et sur les format de données :

Analyse de protocole

Je copie ici le format de la trame :

- entête 2.4ms HIGH,
- code fixe : 0xFE (8 bits),
- canal global (4 bits), ( canal A = 0, canal B = 1, canal C = 2, canal D = 3 )
- adresse (16 bits), (qui identifie le bouton de la télécommande)
- sous canal (4 bits), (canal 1 = 8, canal 2 = 4, canal 3 = 2, canal 4 = 1, canal 5 = 3, tout les canaux = 0)
- état lumière (4 bits), (0 = éteins, 1 = allumé )
- rolling code (8 bits), ( 0x98 -> 0xDA -> 0x1E -> 0xE6 -> 0x67, à chaque nouvelle trame on passe au rolling code suivant  )
- timestamp (0 ~ 255), (8 bits), (en mettant n'importe quoi dedans ça semble marcher quand même, ce qui va simplifier la chose)
- footer 24ms LOW

Du Code !

Voici donc le code python correspondant pour commander une prise Blyss à partir de mon Pi.
J’ai nommé le script ci dessous Blyss.py, mais maintenant vous faites comme vous voulez 😉

#!/usr/bin/python
# -*- coding: utf-8 -*-

import RPi.GPIO as GPIO
import time
import sys

from os.path import expanduser

class Blyss :

        # Table des identifiants programmés dans les prises
        tableauBoutons = { "1" : "1000010111011010"
                                          , "2": "1000010111011011"
                                          , "3": "1000010111011100"
                                          }
        # Table des codes roulants trouvés par les hackers d'arduino.cc
        # http://skyduino.wordpress.com/2012/07/17/hack-partie-1-reverse-engineering-des-interrupteurs-domotique-blyss/
        # http://skyduino.wordpress.com/2012/07/19/hack-partie-2-reverse-engineering-des-interrupteurs-domotique-blyss/
        tableRollingCode = [ 0x67, 0x98, 0xDA, 0x1E, 0xE6]
        echantillonnage = 44100
        paramBits =     { 0 : [ [ 25, 0 ], [ 12, 1 ] ] ,
                                  1 : [ [ 12, 0 ], [ 25, 1 ] ] }
        dataPin    = 17
        nb_retry   = 7
        # Nombre de secondes d'attente entre deux retry
        attenteEntrePaquets = 0.024

        path2FileIdxLastRollingCode = expanduser("~") + "/.lastRCBlyss.idx"
        idRollingCode = 0

        def __init__(self, dataPin ):
                self.dataPin = dataPin

                # A stocker dans un fichier
                try :
                        idRollingCodeFile = open( self.path2FileIdxLastRollingCode , 'r')
                        self.idRollingCode     = int ( idRollingCodeFile.readline() )
                        self.idRollingCode    += 1
                        if self.idRollingCode >= len( self.tableRollingCode ):
                                self.idRollingCode = 0
                        idRollingCodeFile.close()
                except :
                        self.idRollingCode = 0

                GPIO.setwarnings(False)
                GPIO.setmode(GPIO.BCM)
                GPIO.setup(self.dataPin, GPIO.OUT)

        def sendDataPulse( self, dataPin, duration, value ):
                if value == 1 :
                        GPIO.output(dataPin, GPIO.HIGH)
                else:
                        GPIO.output(dataPin, GPIO.LOW)
                time.sleep( duration )

       def genereTrame( self, identifiantTelecommande, canal, sousCanal, etatLumiere ):
                trame = ""
                # empreinte 0xFE (8 bits),
                trame += "11111110"
                # canal global (4 bits),
                trame += canal
                # adresse (16 bits),
                trame += identifiantTelecommande
                # sous canal (4 bits),
                trame += sousCanal
                # état lumière (état logique) (4 bits),
                trame += etatLumiere
                # rolling code, MSBFIRST (8 bits),
                trame += "{0:08b}".format( self.tableRollingCode[ self.idRollingCode ] )
                # timestamp incrémentiel (0 ~ 255), MSBFIRST (8 bits),
                trame += "00000011"
                return trame

        def send( self, button, action ):

                if button in self.tableauBoutons:
                        identifiantTelecommande = self.tableauBoutons[ button ];
                else:
                        print "Boutton non défini"
                        exit( 1)
                # canal : a paramètrer ?
                canal = "0000"
                # sous canal : a paramètrer ?
                sousCanal = "1000"

                if action == "ON" :
                        etatLumiere = "0000"
                elif action == "OFF" :
                        etatLumiere = "0001"
                else :
                        print "l'action doit être ON ou OFF"
                        exit( 1)

                trame = self.genereTrame( identifiantTelecommande, canal, sousCanal, etatLumiere )
                print trame

                for i in range( 0, self.nb_retry ) :
                        # Envoi d'un HIGH pendant 2.4ms
                        self.sendDataPulse( self.dataPin, 0.0024 , 1 )

                        for bit in trame:
                                for paramBit in self.paramBits[ int( bit ) ]:
                                        self.sendDataPulse ( self.dataPin, 1.0 * paramBit[0]/self.echantillonnage, paramBit[1])
                        # Envoi d'un LOW pendant 0.24ms
                        self.sendDataPulse( self.dataPin, 0.00024 , 0 )

                        time.sleep( self.attenteEntrePaquets )

        def __del__(self):
                GPIO.cleanup()
                path2FileIdxLastRollingCode = open( self.path2FileIdxLastRollingCode , 'w')
                path2FileIdxLastRollingCode.writelines( str( self.idRollingCode ) )
                path2FileIdxLastRollingCode.close()

if __name__ == "__main__" :
        button = "1"
        action = "ON"

        if len(sys.argv) > 1:
                button = sys.argv[1]
                action = sys.argv[2]
        else :
                print "arguments incorrects :"
                print sys.argv[0] + "<id> <ON|OFF>"
                sys.exit(1)
        rf = Blyss( 17 )
        rf.send( button, action )

Ce programme doit être lancé en tant que root (comme toujours dés qu’on utilise le gpio sur le Pi).
Il stocke le dernier rolling code utilisé dans un fichier de config dans /root.

D’après mes tests, l’adresse est libre (vous pouvez mettre ce que vous voulez), il faut juste l’apparier avec la prise (comme pour une télécommande « normale »). Dans le code c’est « tableauBoutons » qu’il faut modifier si vous voulez mettre l’identifiant de votre télécommande (c’est de cette manière que j’ai commencé mes tests).
Utiliser plusieurs télécommandes pour une seule prise fonctionne, du coup je penses que le rolling code semble géré par adresse (d’ailleurs il y a peut être une limite au nombres de télécommandes d’une prise ?). Je ne m’explique cependant tout les tenants et les aboutissants de ce rolling code, si une prise est hors tension ou hors portée elle peut, amha, être désynchronisée (ça expliquerait pourquoi on a que 5 codes, quelques appuis sur une touche permettent de resynchroniser les compteurs).
La zone timestamp est inutile, je l’ai fixée à 00000010 et je n’y touche pas ( j’ai cru a une défaillance à cause d’une valorisation à 0, mais le problème venait d’ailleurs, mais j’ai laissé 0000010, ça marche chez moi 😉 )
La trame d’envoi doit être répétée ( la télécommande analysée le fait 7 fois !) et le temps entre deux répétitions (24ms) n’est pas neutre. Je l’ai modifié et du coup ça ne marchait plus. Donc on reste sur 24ms

Travail sous Licence Demerdenzizich

C’est un travail « brut de fonderie » que je livre ici, je ne trouve pas le système très fiable pour l’instant.
J’ai eu des trames tres déformées à un moment de la journée, sans être capable de savoir si ça venait du raspberry, du transmetteur RF ou de l’antenne.
Le fait que cela fonctionne mal si on a une attente de plus de 24ms n’est pas très rassurante, il suffit que le raspi rame pour que les temps s’envolent.
A voir si j’arrive a faire communiquer un attiny avec un raspi (envoi de la trame à envoyer via GPIO, l’attiny s’occupant de l’envoi).

L’antenne

D’ailleurs en parlant de l’antenne j’ai testé avec un fil de 17 cm. J’ai fait des tests avec une antenne d’un mettre qui n’a pas semblé améliorer les perfs, mais il faut que je refasse des tests à ce sujet, c’est peut être tombé en même temps que les problèmes généré en modifiant le temps d’attente entre réémission de deux trames (cf ci dessus).

Domoticz

Pour piloter tout cela j’ai installé le logiciel libre domoticz. Après avoir tâtonné dans leur interface( pas forcement très simple de premier abord, mais ça semble très puissant).
J’ai pu demander l’allumage et l’extinction des interrupteurs en lui faisant appeler les scripts déjà réalisés (Blyss.py et RF.py déjà décrit dans la partie 2). J’ai aussi vu pour spécifier des appels

Cloner une télécommande Radio Fréquence (433MHz) – Part 2

Après avoir vu comment récupérer une trame émise par une télécommande Radio Fréquence je vais vous montrer comment remplacer la télécommande avec un raspberry Pi.

Montage pour envoi (simplisme)

Le montage est ultra simple, le VCC de l’emetteur vers le 3,3V du raspi, le GND de l’emetteur vers le GND du raspberry et enfin le port DATA de l’emetteur vers le port 17 du raspi.
Je remets ici le diagramme des ports GPIO et leur notation (tout du moins pour la version 2 du raspberry pi)

Diagramme du port GPIO pour le Raspberry Pi version 2

Diagramme du port GPIO pour le Raspberry Pi version 2

Programme Python pour envoi code

La librairie RPi.GPIO est nécessaire, elle est dans les dépots de la raspian, pour les autres distrib veuillez vous adresser auprès de sa communauté.

 apt-get install python-rpi.gpio

La doc de cette librairie est ici : http://pythonhosted.org/RPIO/rpio_py.html#ref-rpio-py

#!/usr/bin/python
# -*- coding: utf-8 -*-

import RPi.GPIO as GPIO
import time
import sys



class RF :
	
	tableau = { "AVIDSEN" : 
				{ "echantillonnage" : 44100,
				  "bits" : 
					{ 0 : [ [ 52, 0 ], [ 22, 1 ] ] ,
					  1 : [ [ 22, 0 ], [ 52, 1 ] ] },
				  "buttons" : 
					{ "1 ON"  : "011111111111100101101",
					  "1 OFF" : "011111111111100111100",
					  "2 ON"  : "011111111111110101111",
					  "2 OFF" : "011111111111110111110",
					  "3 ON"  : "011111111111111101110",
					  "3 OFF" : "011111111111111111111",
					  "4 ON"  : "011111111111100001111",
					  "4 OFF" : "011111111111100011110",
					  
					}
				}
			}
	
	paramBits = {}
	dataPin    = 17
	tempsPerdu = 0
	nb_retry   = 4
	# Nombre de secondes d'attente entre deux retry
	attenteEntrePaquets = 0.075
	
	def __init__(self, dataPin ):
		self.dataPin = dataPin
		GPIO.setwarnings(False) 
		GPIO.setmode(GPIO.BCM)
		GPIO.setup(self.dataPin, GPIO.OUT)

	def sendDataPulse( self, dataPin, duration, value ):
		if value == 1 :
			GPIO.output(dataPin, GPIO.HIGH)
		else:
			GPIO.output(dataPin, GPIO.LOW)
		time.sleep( duration )

	def send( self, marque, button ):
		
		# Précalcul les attentes pour chaque type de bits
		for valeur in range(0, 2):
			self.paramBits[ valeur ] = [] 
			for paramBit in self.tableau[marque]["bits"][valeur]:
				tempsAttente  = 1.0 * ( paramBit[0] - self.tempsPerdu) / self.tableau[marque]["echantillonnage"]
				self.paramBits[valeur].append( [ tempsAttente , paramBit[1] ] )
		
		self.sendDataPulse( self.dataPin, 0.01 , 0 )
		for i in range( 0, self.nb_retry ) :
			for bit in self.tableau[marque]["buttons"][ button ]:
				for paramBit in self.paramBits[ int( bit ) ]:
					self.sendDataPulse ( self.dataPin, paramBit[0], paramBit[1])
			self.sendDataPulse( self.dataPin, 0.01 , 0 )
			time.sleep( self.attenteEntrePaquets )
		self.sendDataPulse( self.dataPin, 0.01 , 0 )

	def __del__(self):
		GPIO.cleanup()

if __name__ == "__main__" :
	marque = "AVIDSEN"
	button = "1 ON"
	
	if len(sys.argv) > 2:
		marque = sys.argv[1]
		button = sys.argv[2] + " " + sys.argv[3]
	else :
		print "arguments incorrects :"
		print sys.argv[0] + " <marque> <id> <position>"
		sys.exit(1)

	rf = RF( 17 )
	rf.send( marque, button )

Quelques petites choses sont à adapter pour votre télécommande, j’ai essayé de les regrouper dans la variable « tableau » ( ouh que ce nom est mal choisi !).

tableau = { "AVIDSEN" : 

AVIDSEN est le petit nom de la télécommande, j’ai donné le nom de la marque, je n’ai aucune imagination pour les noms 😉

				{ "echantillonnage" : 44100,

Le taux d’échantillonnage utilisé dans Audacity est noté ici, les temps seront notés en « samples » (il est facil d’adapter cette partie pour mettre des secondes à la place, je me tâte d’ailleurs de mettre un nombre de µs à la place).

				  "bits" : 
					{ 0 : [ [ 52, 0 ], [ 22, 1 ] ] ,
					  1 : [ [ 22, 0 ], [ 52, 1 ] ] },

C’est la partie qui me dérange le plus, car partant de bases théoriques que je pensais sûres (un nombre de points sur un enregistrement Audacity), cela ne marchait pas. Le raspberry pi n’est pas capable d’attendre un nombre de µs fiable, l’attente de 55 samples était trop long, j’ai du descendre à 52 de façon empirique (et les autres valeurs aussi). En effet nous ne sommes pas sur un système temps réel, c’est à dire que le programme d’envoi n’est pas seul à tourner sur la machine, d’autres vont le ralentir et ce de façon non prédictive. Ajoutons à ça des lib pythons qui ne sont pas forcement aussi rapides que du C du coup j’espère que le service sera viable.

				  "buttons" : 
					{ "1 ON"  : "011111111111100101101",
					  "1 OFF" : "011111111111100111100",
					}

La trame capturée à l’aide d’Audacity est stockée ici, brute de fonderie. Je n’ai pas trouvé le logique entre le bouton ON et le bouton OFF, alors j’ai pris le parti de stocker la trame complète.

Pour utiliser ce programme il fonctionne en ligne de commande

 python RF.py "AVIDSEN" 1 ON 

, mais il est aussi possible de l’utiliser comme une librairie pour un autre programme python.

Comme d’habitude cet article est un travail empirique qui peut ne pas fonctionner chez les autres (en somme il est sous licence Demerdenzizich que les Anglo Saxon appelle As-Is)

La troisième partie concernera l’utilisation d’une télécommande Blyss avec un code roulant.

Cloner une télécommande Radio Fréquence (433MHz) – Part 1 – Acquisition

La domotique arrive de plus en plus dans nos foyers, depuis des années j’avais des prises commandées par radio fréquence mais je ne les utilisaient pas car la télécommande n’était pas assez puissante (et pourtant je n’ai pas un château 😦 ). De plus j’ai toujours trouvé inconfortable de chercher la télécommande a chance fois que je veux allumer la lumière (n’avez vous jamais remarqué la timidité de ces dernières ? La preuve elles adorent se cacher derrière les coussins 😉 )

Bref j’avais dans l’idée de cloner ces télécommandes, voir d’automatiser tout ça (avec des capteurs de présence, via des « crontab », via commande vocale, …).

Les télécommandes auxquelles j’ai décidé de m’attaquer sont basées sur la fréquence 433MHz.
Coté matériel, j’ai trouvé des kits émetteur + récepteur pour un prix dérisoire via e-bay (4€ les 5 kits).
[pour ceux qui cherchent la référence on doit en trouver avec ce genre de recherche « 433Mhz RF transmitter receiver kit » ]

433Mhz emetteur-recepteur

433Mhz emetteur-recepteur

Première étape : voir à quoi on a affaire

montage

J’ai trouvé un montage d’une grand simplicité qui permet de « lire » le signal à partir d’un bête PC sans matériel coûteux.
Pour ce faire j’utilise un simple montage trouvé sur http://davehouston.net/learn.htm.
Le schéma à utiliser est le suivant :

Recepteur RF vers Line-In

Recepteur RF vers Line-In

Au niveau de l’alimentation j’utilise une alimentation variable. Du coup j’ai pu tester la réception entre 5 et 9V, je me suis fixé vers 8V ce qui affaibli le nombre de parasites reçus. Pour mes tests je n’ai pas branché d’antenne sur le récepteur, je préfères éviter de récupérer les signaux de tout le quartier 😉

Dans mes recherches je suis tombé sur une phrase concernant les jack, le fil Rouge serait toujours la voie de Droite (Right Red), si ça se confirme ça peut éviter de faire des tests pour trouver le bon câblage. (sur celui que j’ai récupéré Jaune : GND, Rouge : Droite, Bleu : Gauche. À voir si l’axiome RedRight est vérifié ailleurs ). Chez moi les deux fils (rouge et bleu) ont été joints, certains considèrent qu’on peut enregistrer deux signaux en parallèle (deux sources), moi je n’en ai pas besoin, donc on joint ;).

Audacity

Une fois le cablage vérifié (vérifiez deux fois, une boulette est si vite arrivée, je nierais avoir eu connaissance de vos actes en cas d’explosion de votre carte son) et le jack branché sur l’entré Ligne ( Line-In PAS le microphone, je ne sais pas si ça fait tout exploser mais mes sources étaient claires, ce montage est pour l’entrée Line-In !), lancez Audacity;
Ce programme est un enregistreur de son avec la possibilité de visualiser le son sous forme d’ondes. Et c’est exactement ce dont nous avons besoin ici !

Donc lançons l’enregistrement (je répète pour les deux du fonds qui discutent : en sélectionnant l’entrée Ligne ) et appuyons sur une touche de notre télécommande.
Un signal devrait apparaître, zoomez dessus :
Audacity_049

J’ai mis des 0 et des 1 pour bien comprendre ce qu’on voit. La télécommande utilise un codage (Manchester ?) où chaque bit est représenté sous forme d’un « escalier » entre un front haut et un front bas. La durée de ce front haut défini si c’est un « 1 » (front haut long) ou si c’est un « 0 » (front haut court).
Dans l’exemple pris ici nous avons une trame binaire 011111111111100101101, chaque bouton de ma télécommande envoi une trame différente.

Zoomons encore, cette fois en prenant un « 1 » et un « 0 » (idéalement, mais ça m’étonnerais que vous n’ayez pas un 1 à coté d’un 0 au moins une fois).
Le but cette fois va être de comprendre le protocole Radio Fréquence utilisé ici. Je n’ai pas la savoir pour expliquer ici les différentes méthodes existantes, mais j’ai compris qu’au moins ici c’est la durée de chaque front qui doit être mesuré.

Pour facilité l’analyse j’ai demandé à Audacity d’afficher par « sample » et non en millisecondes, un front durant quelques centaines de µs.

Mesure des temps de chaque front

Mesure des temps de chaque front


Dans l’exemple ci-dessus j’ai sélectionné le front haut du « 1 » et l’ai mesuré à 55 « échantillons ». Sachant que j’ai échantillonné à 44100Hz, il y a donc 44100 échantillons dans une seconde. Sortez vos calculettes, ce front haut dure donc ? Allez un effort … 55/44100 soit 0,001247166s soit 1.247ms. J’ai recommencé les mesures pour le front bas du « 1 » et les deux fronts du « 0 ». J’ai noté ces résultats précieusement ils seront utiles dans la partie suivante où l’on va se mettre à émettre des trames.

Suite dans la partie 2

Documentation :
http://davehouston.net/learn.htm
http://rayshobby.net/?p=3381
http://www.homautomation.org/2014/03/02/433mhtz-rf-communication-between-arduino-and-raspberry-pi-arduino-as-receiver/

Un système audio pour la maison [Part 3]

Apres avoir vu (rapidement) les avantages des squeezebox, installé le serveur et les client, je vous propose donc d’ajouter deux petites choses à notre installation :

Un DAC

pour avoir un son de meilleur qualité. En raccordant le tout à une chaîne Hifi on obtiendra un son tout a fait correct (bon … je ne suis pas un audiophile, mais il me suffit tout à fait 😉 ). Le DAC va remplacer la carte son interne du Pi, qui n’est pas exceptionnelle.

A la suite de conseils précieux j’ai commandé un mini GAMAX pour 37$ sur dhgate (made in china c’est  sûr 😉 ) , cette petite bête est de la taille du Pi et propose :

  •  une entrée USB (qu’on reliera au Pi)
  • une sortie Jack
  • une sortie coax
  • une double sortie RCA (L/R)
  • une sortie numérique
  • une entrée micro

J’alimente ce petit boitier avec une vielle alimentation 12V qui correspond pil poil à ce que veut le DAC (9-24V avec le + au milieu).

Coté reconnaissance par le Pi il n’y eu aucun soucis. J’ai branché et squeezelite permet de vérifier sa présence :

squeezelite-armv6 -l

cela donne :


Output devices:
null - Discard all samples (playback) or generate zero samples (capture)
default:CARD=Set - C-Media USB Headphone Set, USB Audio - Default Audio Device
sysdefault:CARD=Set - C-Media USB Headphone Set, USB Audio - Default Audio Device
front:CARD=Set,DEV=0 - C-Media USB Headphone Set, USB Audio - Front speakers
surround40:CARD=Set,DEV=0 - C-Media USB Headphone Set, USB Audio - 4.0 Surround output to Front and Rear speakers
surround41:CARD=Set,DEV=0 - C-Media USB Headphone Set, USB Audio - 4.1 Surround output to Front, Rear and Subwoofer speakers
surround50:CARD=Set,DEV=0 - C-Media USB Headphone Set, USB Audio - 5.0 Surround output to Front, Center and Rear speakers
surround51:CARD=Set,DEV=0 - C-Media USB Headphone Set, USB Audio - 5.1 Surround output to Front, Center, Rear and Subwoofer speakers
surround71:CARD=Set,DEV=0 - C-Media USB Headphone Set, USB Audio - 7.1 Surround output to Front, Center, Side, Rear and Woofer speakers
iec958:CARD=Set,DEV=0 - C-Media USB Headphone Set, USB Audio - IEC958 (S/PDIF) Digital Audio Output
default:CARD=ALSA - bcm2835 ALSA, bcm2835 ALSA - Default Audio Device
sysdefault:CARD=ALSA - bcm2835 ALSA, bcm2835 ALSA - Default Audio Device

A vous de choisir votre sortie préférée suivant votre DAC . Je me suis cantonné à « sysdefault:CARD=Set » que j’ai mis dans le script de démarrage que je vous avait donné précédemment (/etc/initi.d/squeezelite-arm6  : variable SLOPTIONS)

Une télécommande

Pour cela j’utiliserai lirc, le port GPIO du Raspberry Pi et un TSOP 4838 (d’autres marcheront peut être, mais je n’en sais rien 😉 )

Le matériel

Le montage à utiliser est détaillé chez Adafruit, vous n’êtes pas du tout obligé d’utiliser cette télécommande. Personnellement j’ai recyclé celle d’un vieux lecteur CD mort depuis longtemps.

Si vous suivez le montage d’adafruit il suffit de connecter GND, 3,3V et le data du TSOP (recepteur IR 38kHz) aux ports adéquats du Pi (le Data utilisé est le n°18) .

Le Système

sudo modprobe lirc_rpi

chargera le module nécessaire pour lirc.
Edit : Ce module est à rajouter au fichier /etc/modules pour qu’il soit chargé automatiquement au démarrage du Raspberry Pi

vérifiez avec un

mode2 -d /dev/lirc0

que votre Pi reçoit bien les infos venant de la télécommande (appuyez et viser 😉 )

L’apprentissage de la télécommande

Sans doute la plus grosse partie du boulot qui reste à faire.

sudo irrecord -d /dev/lirc0 /etc/lirc/lircd.conf

La première phase va permettre de reconnaitre le protocole employé par la télécommande, il ne s’agit pas spécialement d’appuyer sur toutes les touches. Les informations à l’écran sont à lire (il faut toujours lire ce qui est affiché à l’écran 😉 )

La seconde phase est plus « laborieuse », il va falloir associer les touches une par une. Le programme demande un « mnémonique » tel que KEY_PLAY, KEY_STOP, … (la liste complète est trouvable grâce à

irrecord –list-namespace

).

Une fois l’apprentissage de toutes les touches effectuées il va falloir « câbler » ça avec squeelite

Donnons des ordre à squeezelite

Pour cela je vais utiliser le fichier de configuration de irexec : /etc/lirc/lircrc


begin
prog = irexec
button = KEY_PLAY
config = /usr/local/ir2squeeze/pause
end

begin
prog = irexec
button = KEY_NEXT
config = /usr/local/ir2squeeze/next
end

begin
prog = irexec
button = KEY_PREVIOUS
config = /usr/local/ir2squeeze/previous
end

begin
prog = irexec
button = KEY_POWER
config = /usr/local/ir2squeeze/power
end

J’associe donc à chaque touche un script shell qui va être appelé, de ce fait ma télécommande peut faire plus ou moins n’importe quoi !!!!

I except you know Expect

Pour piloter squeezelite je ne vais pas faire dans la dentelle (pour le moment), un script par touche. Et pour me faire comprendre je vais directement utiliser le protocole réseau avec une commande telnet. Pour ça il y a un utilitaire magnifique que j’ai découvert il y a peu : Expect

d’abord on va l’installer

apt-get install expect

Exemple du script /usr/local/ir2squeeze/pause :

#!/usr/bin/expect

spawn telnet 192.168.X.X 9090
expect "Escape character is *"
send "AA:BB:CC:DD:EE:FF pause\n"
expect "* pause"
send "exit\n"
expect eof

où 192.168.X.X est l’adresse IP du serveur squeezebox et AA:BB:CC:DD:EE:FF l’adresse MAC de la machine cliente. Cette adresse MAC peut être trouvée dans l’écran information du serveur ou par un ifconfig -a sur le client, ça tombe bien c’est celui sur lequel vous travaillez actuellement.

Edit : Afin que lirc démarre en même temps que votre système il faut le rajouter dans le mécanisme d’init.d.

ln -s /etc/init.d/lirc /etc/rc2.d/S15lirc

Le démarrage de lirc peut se faire à l’aide de la commande classique :

sudo service lirc

Je ne suis pas rentré dans tout les détails techniques  mais cela devrait vous donner quelques pistes pour (peut-être) faire fonctionner votre Pi en tant que squeezebox 😉

Un système audio pour la maison [Part 2]

Dans le précédent article je vous ai décris (à la limite de la pub) ce que pouvait faire une squeezebox « du commerce »

Maintenant je vais vous parler de se faire sa propre squeezebox radio à base de Raspberry Pi.

Si on considère qu’on a déjà un Raspberry Pi d’installé l’opération est très simpe. Un tutoriel complet est décrit dans cet article, mais personnellement j’ai sabré bon nombre d’étapes (Wifi,..)

Dans les faits j’ai eu quelques problemes avec la distrib que j’avais installé à la base (la débian de base du Pi ? je ne sais plus), le son était mauvais. Des « clicks » se faisaient entendre. Vu que je n’avais pas grande idée sur le sujet j’ai utilisé la Raspifi que j’avais installé précédemment et appliqué la procédure ci dessus. Cette fois les « clicks » n’étaient pas présents.

A l’heure actuelle je fonctionne avec la Raspify, mais sans utiliser son interface. Il n’y a pas de conflit entre squeezelite et mpd donc je garde pour le moment 😉

Les étapes importantes à garder sont les suivante :

sudo apt-get update
sudo apt-get install libfaad2 libmad0

cd /usr/bin/
sudo wget http://squeezelite.googlecode.com/files/squeezelite-armv6
sudo chmod u+x squeezelite-armv6

A partir de ce moment là il est possible de lancer squeezelite-armv6 et de profiter du son (pour peu qu’on ai un serveur installé comme décrit dans la partie 1 … faut suivre 🙂 )

Pour lancer automatiquement le client j’utilise le script suivant, installé dans /etc/init.d/squeezelite-armv6 (j’ai pris comme base celui fourni avec le tuto cité précédemment ).

#! /bin/sh
### BEGIN INIT INFO
# Provides: squeezelite
# Required-Start:
# Required-Stop:
# Should-Start:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Squeezeslitee
# Description: Light weight streaming audio player for Logitech's Squeezebox audio server
### END INIT INFO</code>

# Author: Me
#
# Install Instructions
#
# Copy file to /etc/init.d/squeezeslite
# chmod 755 /etc/init.d/squeezeslite
# update-rc.d squeezeslitee defaults
#
# Create /etc/default/squeezeslite to override any default
# variables defined here. No not edit this file.
#
# Uninstall Instructions
#
# update-rc.d squeezeslite remove
#

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Squeezebox client"
NAME=squeezelite-armv6
DAEMON=/usr/bin/$NAME
SCRIPTNAME=/etc/init.d/$NAME
SLOPTIONS="-a 120 -o default:CARD=ALSA"
SLLOG=/var/log/squeezeslite.log
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] &amp;&amp; . /etc/default/$NAME

DAEMON_ARGS="${SLOPTIONS}"

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (&gt;= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
    if [ -f ${SLLOG} ]; then
    rm ${SLLOG}
    fi
    start-stop-daemon --start --quiet --background --exec $DAEMON -- $DAEMON_ARGS || return 2
}

#
# Function that stops the daemon/service
#
do_stop()
{
    killall $DAEMON
    RETVAL="$?"
    [ "$RETVAL" = 2 ] &amp;&amp; return 2
}

# MAIN #
case "$1" in
start)
    [ "$VERBOSE" != no ] &amp;&amp; log_daemon_msg "Starting $DESC" "$NAME"
    do_start
    case "$?" in
        0|1) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 0 ;;
        2) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 1 ;;
    esac
    ;;
stop)
    [ "$VERBOSE" != no ] &amp;&amp; log_daemon_msg "Stopping $DESC" "$NAME"
    do_stop
    case "$?" in
        0|1) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 0 ;;
        2) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 1 ;;
    esac
    ;;
    status)
        status_of_proc "$DAEMON" "$NAME" &amp;&amp; exit 0 || exit $?
    ;;
    restart)
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        case "$?" in
            0|1)
                do_start
                case "$?" in
                    0) log_end_msg 0 ;;
                    1) log_end_msg 1 ;; # Old process is still running
                    *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
            *)
                # Failed to stop
                log_end_msg 1
                ;;
            esac
        ;;
        *)
            echo "Usage: $0 {start|stop|status|restart|force-reload}" &gt;&amp;2
            exit 3
        ;;
esac

N’oubliez pas de le rendre exécutable avant de faire un service squeezelite-armv6 start
Un update-rc.d squeezelite-armv6 defaults plus loin vous pouvez rebooter, il redémarrera tout seul 😉

C’est un peu brut de décoffrage , mais ça fonctionne … à suivre pour quelques raffinements

Un système audio pour la maison [Part 1]

Suite à un article trouvé chez Korben j’ai découvert la distribution Raspifi qui a pour but de lire ses mp3 à l’aide d’un rasperry pi.

La distrib s’installe sans trop de problème et j’ai pu lire les mp3 qui sont sur mon NAS.

N’ayant pas été convaincu par l’interface web de l’engin j’ai été aiguillé vers la solution de Logitech.

Bon disons le tout de suite, c’est une solution  propriétaire, donc qui pourrait disparaître à tout moment. Mais j’ai rarement vu une interface propriétaire qui autorise autant de possibilité, on est plutôt dans l’ère de la Pomme avec un simple bouton Play (pour les autres c’est en option 😉 ).

Cette solution réside sur une solution client/serveur et impose donc de laisser tourner un PC tout le temps. Vu que c’est déjà le cas pour moi ça ne me dérange pas ;). Il permet de lire des mp3, mais aussi des web radios (ce qui rejoint un de mes précédents articles).

Le serveur

De son petit nom « Logitech Media Server », il se télécharge sur le site du fabriquant de souris, et s’installe sous Linux (DEB ou RPM), Mac, Windows et quelques NAS. Une fois installé il faut s’enregistrer sur le site de logitech, cela permettrait théoriquement de contrôler les squezebox depuis internet. Dans les faits je n’ai jamais réussi à partir de ce site, mais ce n’est pas grave 😉 . Ensuite on lui faire indexer ses mp3, c’est assez classique. Il y a possibilité d’ajouter des plugins, des interrogations vers last.fm ou d’autre, … les possibilité sont assez étendues.

En l’état le système ne fonctionne pas, on a pas la possibilité de lire de mp3, pour cela il faut un (ou plusieurs clients)

Le client (payant)

Logitech ne faisant pas ça gratuitement compte quand même vendre du matos,en l’occurrence il s’agit des Squeezebox Radio. A priori la gamme était plus vaste il y a quelques années mais la marque a recentré sa stratégie 😦

J’ai donc acheté une Squeezebox Radio pour la cuisine. La petite bête est sympathique avec son Wifi et du son mono de bonne qualité. Disponible aux alentours de 130€ ça peut refroidir, mais je ne regrette pas du tout mon achat.

L’ergonomie de la petite bête est très agréable et dispose d’un excellent WAF ce qui fait qu’elle a été adoptée par madame de façon surprenante.

Le client (gratuit)

Là où la chose est beaucoup plus intéressante, c’est qu’il est possible d’installer squeezelite sur son ordinateur (ou sur un raspberry pi, mais j’y viendrais plus tard). à partir de ce moment là il est possible de streamer de la musique sur l’ordinateur depuis le serveur.

Et il peut y avoir plusieurs clients en même temps. Par exemple un serveur familiale avec la musique et des raspberry dans les autres pièces  raccordés aux enceintes.

D’ailleurs cette multitude de client a un fonctionnalité que je n’avais jamais vu avant c’est la « synchonisation » des clients. Par un menu il est possible d’indiquer que deux clients vont diffuser les mêmes programmes, idéal quand on se déplace dans la maison, une sorte de radio d’intérieure en somme.

Les interfaces

Afin de piloter la lecture il y a aussi foultitude de possibilité, on peu se connecter sur le site web de logitech (même si ça ne marche pas chez moi), on peu se connecter au serveur (et choisir sur quel client la lecture se passera). Il existe aussi des interfaces pour Android (même topo), la squeezebox radio est aussi autonome de ce coté là (mais peut aussi être pilotée par le réseau. Enfin il est aussi possible de piloter via le protocole réseau qui est disponible sur le Net (j’en reparlerais plus tard).

Et ceci n’est que le commencement … à suivre

Faire de son Raspberry Pi une Web Radio

Pour faire une web Radio j’ai utilisé les capacités du lecteur « mpd » (je ne détaille pas l’explication de l’installation de mpd, « apt-get install mpd mpc »).

Pour commencer j’ai ajouté une web radio :
mpc add http://streaming.radio.rtl2.fr:80/rtl2-1-44-128

Ensuite j’ai réalisé un petit montage pour ajouter un écran LCD et quelques boutons.

Maestro

Câblage d’un écran LCD et de 4 boutons sur un Raspberry Pi

J’ai trouvé un bout de code Python permettant de gérer un écran LCD de type « HD44780 » (disponible sur ebay pour quelques euros) sur zem.fr . J’ai juste mis le code dans un module Python indépendant (LCD.py).

Les codes sources sont disponible dans mon nouveau repository dédié au Raspberry Pi (vu que je développe directement dessus, j’ai préféré le séparer du repository Arduino). Le Programme s’appelle Maestro et utilise pour le moment quatre boutons, l’un pour faire Pause/Play le second pour Next (changement de station) et les deux derniers pour la gestion du volume.

Il est tout à fait possible d’ajouter ou d’enlever des boutons dans le code, chaque bouton est un objet qui est défini par le port GPIO à utiliser pour le câblage et une fonction de callback.
Dans l’init du GUI, je défini un bouton. Ce bouton restera en attente de ce qui se passe sur un port GPIO. Il a son propre Thread pour la surveillance. A voir si augmenter le nombre de bouton dégrade les performances.

self.bouton4 = Button( 22, self.Button4Pressed )
self.bouton4.start()

Et la fonction de callback qui va augmenter le volume

 def Button4Pressed(self):
  os.system("mpc volume +10")

Le code est volontairement simpliste et ne fait appel qu’a mpc pour le contrôle. Je pars du principe qu’on ne va pas « spammer » les boutons et que le raspberry sera peu ou prou dédié à cette tache.

Je ne sais pas encore si j’utiliserais ce « mini projet », mais s’il s’avère que c’est le cas j’ai dans l’idée d’ajouter un bouton qui ferait passer du mode radio à un mode playlist. Cela permettrait de « meubler » quand la radio diffuse une pub ou une chanson qui ne me plait pas.

Edit : Dans les faits j’utilise effectivement un raspberry Pi pour lire de la musique au salon mais en utilisant un autre système que je décris ici

Brochages Arduino et Raspberry Pi

Un petit article bookmark pour citer deux liens particulièrement utiles.

[*] Les brochages de l’arduino, des Atmega328 mais aussi des attiny, à garder à portée de main

http://www.semageek.com/pratique-des-diagrammes-de-pinout-pour-arduino-et-atmega/

[*] Un petit PDF à découper et à enficher sur le port GPIO de votre Raspberry Pi, un must have pour ceux qui n’ont pas de Pi Cobbler

http://www.doctormonk.com/2013/02/raspberry-pi-and-breadboard-raspberry.html