Télécommande universelle du pauvre

Bogoss attitude dans une documentation technique

Bogoss attitude dans une documentation technique

Un nouveau jouet

Je me suis procuré récemment un Avocent SPC810. Waah ça en jette comme ça ! Mais… c’est quoi ? C’est un appareil qui permet de gérer la puissance d’équipements dans un datacenter. Il possède 8 prises et on peut par exemple éteindre et allumer 8 serveurs à distance. Pour la gestion, on peut nommer des ports, leur donner un état au démarrage (“lorsque le courant revient, j’allume ce port ou le laisse éteint ?”), voir le courant total consommé mais aussi créer des utilisateurs, donner des droits d’accès à des utilisateurs sur des ports,…

Prises de sortie du SPC810, moniteur d'ampérage et port série

Prises de sortie du SPC810, moniteur d’ampérage et port série

Pour se connecter dessus, on utilise un port série en RS232 qui le plus souvent se trouve sur une prise DB-9. Or ici, comme c’est de coutume dans les datacenter, ils n’utilisent pas de prises DB-9, mais des câbles RJ45 pour causer en série. Les appareils récent font tout en IP via le réseau de gestion du datacenter. Le fabricant de cet appareil a prévu de vendre un appareil (chez Avocent ils appellent ça un CPS) qui fait du “serial over IP” : d’un côté de ce boîtier CPS il y a du réseau RJ45, de l’autre des ports Serial RJ45. Ce qui est fun avec ces gens qui font du série en RJ45 (Avocent, Cisco et d’autres) c’est qu’ils ne se sont pas mis d’accord pour le pinout. Un ami m’a aidé après un peu de trial and error à retrouver le pinout, et on a pu fabriquer un adaptateur DB9-RJ45 spécifique que je peux planter dans un port série DB9-USB sur le PC.

Adaptateur DB-9 RJ45

Adaptateur DB-9 vers RJ45

pinout de la prise RJ45 du SPC

pinout de la prise RJ45 du SPC

Adaptateur C13 vers T13 (suisse)

Adaptateur C13 vers T13 (suisse)

Infrared Power

Voilà mon idée avec ce machin : dans ma chambre, j’ai une télécommande infrarouge qui commande une lampe RGB autour de mon lit et ma lampe de chevet ( c’est basé sur ce projet, je ne vais pas y revenir en détails ). But : utiliser la même télécommande pour d’autres appareils dans ma chambre comme l’éclairage de mon bureau.
Et pour ce faire, je vais utiliser… un Banana Pi que j’ai sous la main. Le Banana Pi est un clone du Raspberry Pi original mais sous stéroïdes. A l’heure actuelle, il est un peu dépassé mais il possède un capteur infrarouge. Je vais m’en servir pour envoyer des commandes au SPC810.

Banana Pi dans son boîtier imprimé en 3D

Banana Pi dans son boîtier imprimé en 3D

Configuration de l’infrarouge sur le Banana Pi

Pour commencer, on vérifie que le capteur IR est reconnu sur le Banana Pi :

cat /proc/bus/input/devices
I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="sunxi-ir"
P: Phys=RemoteIR/input1
S: Sysfs=/devices/virtual/input/input0
U: Uniq=
H: Handlers=sysrq rfkill kbd event0
B: PROP=0
B: EV=3
B: KEY=ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe

On voit une référence à event0, on se référera par la suite à ce capteur via : /dev/input/event0

Pour trouver une télécommande qui est reconnue (là c’est la loterie mais en général ça fonctionne) on utilise evtest :

apt-get install evtest
evtest /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100
Input device name: "sunxi-ir"
Supported events:
[...]
Properties:
Testing ... (interrupt to exit)
Event: time 1457262850.435187, type 1 (EV_KEY), code 5 (KEY_4), value 1
Event: time 1457262850.435194, -------------- EV_SYN ------------
Event: time 1457262850.686531, type 1 (EV_KEY), code 5 (KEY_4), value 0
Event: time 1457262850.686536, -------------- EV_SYN ------------
Event: time 1457262852.554246, type 1 (EV_KEY), code 9 (KEY_8), value 1
Event: time 1457262852.554255, -------------- EV_SYN ------------
Event: time 1457262852.796536, type 1 (EV_KEY), code 9 (KEY_8), value 0
Event: time 1457262852.796542, -------------- EV_SYN ------------

So far so good, il reconnait des touches de ma télécommande ! Installons lirc qui va s’occuper le lire et décoder les signaux de la télécommande :

apt-get install lirc

Edition de /etc/lirc/hardware.conf pour qu’il ressemble à :

# /etc/lirc/hardware.conf
#
# Arguments which will be used when launching lircd
LIRCD_ARGS=""

#Don't start lircmd even if there seems to be a good config file
#START_LIRCMD=false

#Don't start irexec, even if a good config file seems to exist.
#START_IREXEC=false

#Try to load appropriate kernel modules
LOAD_MODULES=true

# Run "lircd --driver=help" for a list of supported drivers.
DRIVER="devinput"
# usually /dev/lirc0 is the correct setting for systems using udev
DEVICE="/dev/input/event0"
#MODULES="sunxi-ir"

# Default configuration files for your hardware if any
LIRCD_CONF=""
LIRCMD_CONF=""

Maintenant il faut configurer LIRC avec cette télécommande spécifique. Il existe une BDD de télécommandes du marché avec LIRC mais la mienne n’y figure pas, je configure quelques touches à la main. Je récupère pour commencer la configuration d’une télécommande générique :

wget http://lirc.sourceforge.net/remotes/generic/NEC.conf

Puis je lance irrecord qui est l’utilitaire de configuration :

irrecord -H devinput -d /dev/input/event0 NEC.conf

Après avoir identifié quelques touches, je me retrouve avec un fichier NEC.conf.conf (sic) qui contient des trucs du genre :

          KEY_POWER2               0x01000900000001 0x00000000000000
          KEY_POWER                0x01000500000001 0x00000000000000
          KEY_F10                  0x01000E00000001 0x00000000000000
          KEY_F11                  0x01001200000001 0x00000000000000
          KEY_F12                  0x01000A00000001 0x00000000000000

J’enlève à la main le deuxième code 0x00000000000000 et enregistre sous le nom /etc/lirc/lircd.conf
J’édite le fichier /etc/lirc/lircrc, c’est lui qui enverra les signaux vers mon script python plus tard. Je crée des blocs du style pour chaque touche que je compte utiliser :

begin
        prog = myProg
        button = KEY_F10
        config = key1
        repeat = 0
end

Voilà, lirc est configuré, on peut le redémarrer pour appliquer les changements :

service lirc restart

Test avec irw :

irw
0001000e00000001 00 KEY_F10 rgbremote
0001001200000001 00 KEY_F11 rgbremote
0001000a00000001 00 KEY_F12 rgbremote
0001000900000001 00 KEY_POWER2 rgbremote
0001000500000001 00 KEY_POWER rgbremote

Yay ! Passons à la partie Python qui fera le lien entre la télécommande et SPC810

Python, IR et Série

Python discute avec l’IR par le biais de la librairie pylirc :

apt-get install python-pylirc

et discute en série via le module PySerial.
Pour utiliser le serial, je branche mon adaptateur serial dans l’USB et regarde ce que me dit dmesg :

[   15.738687] usbserial: USB Serial Driver core
[   15.781537] usbcore: registered new interface driver ftdi_sio
[   15.809006] USB Serial support registered for FTDI USB Serial Device
[   15.830876] ftdi_sio 4-1:1.0: FTDI USB Serial Device converter detected
[   15.850186] usb 4-1: Detected FT232BM
[   15.864004] usb 4-1: Number of endpoints 2
[   15.878486] usb 4-1: Endpoint 1 MaxPacketSize 64
[   15.893360] usb 4-1: Endpoint 2 MaxPacketSize 64
[   15.908012] usb 4-1: Setting MaxPacketSize 64
[   15.928439] usb 4-1: FTDI USB Serial Device converter now attached to ttyUSB0
[   15.947323] ftdi_sio: v1.6.0:USB FTDI Serial Converters Driver

ah, il faudra donc utiliser le port /dev/ttyUSB0

Bon, on a enfin tout configuré, l’infrarouge, le serial, installé les librairies il est temps de pondre un petit code qui fait le lien entre tout ça :

import pylirc
import serial
import time

LSonAllume = False
LBureauAllume = False
sonAllume = False

#
#initialisation du serial
#
ser = serial.Serial(
        port='/dev/ttyUSB0',
        baudrate=9600,
        parity=serial.PARITY_ODD,
        stopbits=serial.STOPBITS_TWO,
        bytesize=serial.SEVENBITS
)
ser.close()
ser.open()
if ser.isOpen():
        print 'Serial connection done...'

#
#initialisation de l'infrarouge
#
pylirc.init("myProg","/etc/lirc/lircrc")

def read():
        out = ''
        while ser.inWaiting() > 0:
                out += ser.read(1)
        print out
        return out

def login():
        ser.write('\r\r')
        time.sleep(1)
        read()
        #login par défaut, j'attends le hacker
        #qui rentre dans ma chambre, se branche sur
        #le SPC, éteint la lumière et repart en ricanant
        ser.write('Admn\r')
        time.sleep(0.5)
        read()
        ser.write('admn\r') 
        time.sleep(0.5)
        read()

def toggleLSon():
        global LSonAllume
        if LSonAllume:
                ser.write('off port lson\r')
        else:
                ser.write('on port lson\r')
        time.sleep(0.5)
        read()
        LSonAllume = not LSonAllume

def toggleLBureau():
        global LBureauAllume
        if LBureauAllume:
                ser.write('off port lbureau\r')
        else:
                ser.write('on port lbureau\r')
        time.sleep(0.5)
        read()
        LBureauAllume = not LBureauAllume

def toggleSon():
        global sonAllume
        if sonAllume:
                ser.write('off port son\r')
        else:
                ser.write('on port son\r')
        time.sleep(0.5)
        read()
        sonAllume = not sonAllume

def offAll():
        global sonAllume
        global LBureauAllume
        global LSonAllume
        ser.write('off ports all\r')
        time.sleep(0.5)
        read()
        sonAllume = False
        LBureauAllume = False
        LSonAllume = False

login()
while True:
        list = pylirc.nextcode()
        if list is not None:
                for code in list:
                        if code == "key1":
                                toggleLSon()
                        if code == "key2":
                                toggleLBureau()
                        if code == "key3":
                                toggleSon()
                        if code == "powerOff":
                                offAll()

        #garder la connexion active après 5 min
        if ser.inWaiting() > 0:
                time.sleep(1)
                if(read().find('ended') != -1) : #session ended
                        print 'Reconnecting...'
                        time.sleep(1)
                        login()

A l’aide de ce script, j’ai configuré 4 nouveaux boutons sur ma télécommande : un qui allume/éteint ma sono, un qui fait la lumière au dessus de mes platines, un qui fait la lumière sur mon bureau et un OFF qui éteint tout en même temps. Combiné à l’arduino qui possède son propre capteur vers mon lit, ma télécommande est configurée comme suit :

boutons de ma télécommande multifonctions

boutons de ma télécommande multifonctions