Migration du server/client domotique de “Select” vers “twisted”

Le serveur domotique hébergé sur mon RaspberryPi déclenche certaines actions en fonction d’évènements. Par exemple lorsqu’ un détecteur de présence est déclenché on allume la lumière de la même pièce. Dans cet exemple l’évènement sera reçût par le port USB du RaspberryPi (car il proviendra de l’Arduino Leonardo). Il existe d’autre évènements qui arriveront sur le RaspberryPi par d’autre moyen (TCP pour le site web de commande par exemple).

Jusqu’ a présent le serveur été code en python et attendait les évènements grâce au module python « select » qui me permettait de monitorer a la fois la liaison USB et TCP. Avec ce modèle je ne pouvais cependant pas faire du processing en arrière-plan (déclencher un évènement a une certaine heure par exemple) puisque le « select » est bloquant. J’ai donc choisit de migrer mon code vers Twisted qui correspond exactement a ce que je veux faire « Twisted is an event-driven networking engine written in Python and licensed under the open source »

La documentation est très bien faite est disponible sur le site officiel : http://twistedmatrix.com/trac/

Dans mon cas je veux que twisted monitor 2 « récepteurs » :

  • La liaison USB qui communique avec l’Arduino Leonardo est reçoit donc les messages des capteurs (capteur présence, détection incendie) qui peuvent être trigger à tout moment. Cette liaison est aussi utilisée pour envoyer des ordres aux capteurs/actionneurs du réseau (par exemple pour allumer la lumière)
  • La liaison TCP qui communique avec un client python fortement couple a un script PHP qui reçoit les ordres de la GUI HTML. Cette liaison est utilisée dans les 2 sens (le serveur répond au client qui affiche la réponse sur la page web).

La création de ses 2 « handlers » se fait assez facilement à condition de trouver le bon protocole duquel il faut hériter. Pour la liaison TCP il faut hériter de « twisted.internet.protocol. Protocol » (il existe déjà pas mal d’exemple pour ce cas de figure). Pour la liaison USB avec L’Arduino Leonardo on doit hériter de « twisted.protocols.basic. LineReceiver ». Ce protocole permet de recevoir les messages du port USB à condition qu’il soit bien envoyé comme une ligne complète. Il ne faut pas se tromper avec un autre protocole sinon les données seront tronquées.

Chacun de ses « handlers » a une méthode qui sera appelé lors de la réception de message : « lineReceived » pour l USB et « dataReceived » pour le TCP. Voilà à quoi ressemblent les 2 handlers

class UsbHandler(LineReceiver):
    """protocol handling class for USB """

    def __init__(self,iBrain,iRegisteredDevice):
        self.brain = iBrain
        self.registeredDevices = iRegisteredDevice

    def lineReceived(self, line):
        logging.info("USB Handler created to process : " + str(line))
        self.brain.HandleUsbInput(line,self.registeredDevices)

class TcpHandler(Protocol):
    """protocol handling class for TCP """

    def __init__(self,iBrain,iRegisteredDevice):
        self.brain = iBrain
        self.registeredDevices = iRegisteredDevice

    def dataReceived(self, data):
        logging.info("Tcp Handler created to process : " + str(data))
        if "READ" in str(data):
            logging.info("READ command")
            aRest = aBrain.ReadDeviceStatus2(data,aRegisterDevices)
            logging.info("READ command res " + str(aRest))
            self.transport.write(str(aRest))
        elif str(data) == "STOP": 
            logging.info("STOP command")
            aBrain.stop()
            reactor.stop()
        else:
            logging.info("Write command")
            aBrain.SendMessage(data,aRegisterDevices)
            self.transport.write("ACK")

 

Ensuite on attache ses 2 protocoles au “reacteur” qui est le Cœur de twisted. Pour le TCP on lui ajoute une surcouche au travers une factory (je ne sais pas pkoi il ne faut pas faire la même chose pour l USB…)

reactor.listenTCP(50007, TcpHandlerFactory(aBrain,aRegisterDevices))
SerialPort(UsbHandler(aBrain,aRegisterDevices), '/dev/ttyACM0', reactor, 9600)

 

Avec ce design je me retrouve dans la même configuration que celle de mon “framework – Select” fait maison. On va donc ajouter une boucle qui sera capable de gérer la logique background (celle qui ne dépend pas d’un évènement particulier).

Pour cela on créer une méthode (dans notre cas elle se contente d’appeler la vrai méthode de processing) :

def tired_task(iBrain):
    #logging.info("I want to run slowly" + str (datetime.datetime.now()))
    iBrain.smartProcessing2(aRegisterDevices)

et on l ajoute au “reactor” avec une frequence :

lc2 = LoopingCall(tired_task, aBrain)
 lc2.start(5)

Dans mon cas cette méthode va mettre a jour les capteurs automatiquement quand nécessaire. Par exemple si le dernier relevé de température date de plus de 5 minutes dans le salon et que la config de ce capteur autorise un relevé toutes les 5 minutes….on le met à jour. Cette méthode va également vérifier l’ensemble des capteurs stable de l’appartement et les updater si nécessaire. Par exemple on va éteindre la lampe de l’entrée si personne n’a été détecté depuis 10 minutes.

    def smartProcessing2(self,iListOfDevice):
        '''Une boucle qui a lieu regulierement pour prendre des decision. Elle va verifier les detecteur et en fonction triger certains evenements.
        Par ex si qq un est detecte dans l entree on decide d allumer la lumiere.
        Cette classe va aussi verifier si certaines autres actions (non lie a la detection de personne) peuvent etre prise.
        Par ex : si on a pas eut de detection de personne depuis un moment ds l entree et que la lumiere est allume....on etient
        Enfin elle va aussi mettre a jour tous les capteurs en fonction de leur refresh rate
        Par exemple si ca fait trop longtemps qu on a pas updater la T alors on la met a jour'''
        logging.info("Begining of a smart loop")

        #Step 1 : Verifier tous les detecteurs (interupteurs stables) pour voir si ils ont ete actives et prendre les actions correspondantes avant de les reset
        #Par exemple si le detecteur de fumee a ete active alors on va envoyer un mail 
        logging.info("Checking all possible event")
        for aOneDevice in iListOfDevice.registeredDevices:
            logging.debug("checking event : " + str(aOneDevice.id))
            if ((aOneDevice.id == 2) and (aOneDevice.currentStatus=="unstable")):
                sendEmailFireDetected()
            elif ((aOneDevice.id == 10) and (aOneDevice.currentStatus=="unstable")):
                self.PeopleDetectedEntree(iListOfDevice)
            elif ((aOneDevice.id == 9) and (aOneDevice.currentStatus=="unstable")):
                self.PeopleDetectedCharlesRoom(iListOfDevice)
            aOneDevice.reset()

        #Setp 2 : On reset les actions resultantes des detections passe
        #Par exemple si la lumiere de l entree ete ON car qq un avait ete detecte depuis 10 minutes mais qu il y a plus eu de detection depuis 10 min....on eteind
        logging.info("Reseting all previous automatic actions")
        for aOneDevice in iListOfDevice.registeredDevices:
            logging.debug("checking states : " + str(aOneDevice.id))
            if ((aOneDevice.id == 9) and ((iListOfDevice.getDevice(3)).currentStatus=="on") and (datetime.datetime.now() - aOneDevice.LastTMeaureDate > datetime.timedelta (seconds = 600))):
                self.TurnCharlesLightOff(iListOfDevice)
            elif ((aOneDevice.id == 10) and ((iListOfDevice.getDevice(8)).currentStatus=="on") and (datetime.datetime.now() - aOneDevice.LastTMeaureDate > datetime.timedelta (seconds = 180))):
                self.TurnEntreeLightOff(iListOfDevice)

        #Setp 3 : On force un refresh des capteurs periodiques
        logging.info("Force the auto refresh of capteur")
        for aOneDevice in iListOfDevice.registeredDevices:
            logging.debug("checking autoupdate : " + str(aOneDevice.id))
            if ( (aOneDevice.stateCanBeRefresh == True) and (aOneDevice.refreshOngoing == False)and (datetime.datetime.now() - aOneDevice.LastTMeaureDate > datetime.timedelta (minutes = aOneDevice.refreshRatemin) ) ):
                logging.debug("We can refresh : " + str(aOneDevice.id))
                self.refreshCapteur(aOneDevice,iListOfDevice)

Grace à cette nouvelle boucle intelligente la mise à jour des capteurs ne se fait plus dans la crontab avec :

# toutes les heures + 7 min on refresh la emperature entree
 7 * * * * /home/pi/Usb_Arduino_Leonardo/PythonWrapperWebArduinoUsbS.py -o CRONTAB -s 30

La mise à jour des capteurs fait maintenant partie intégrante de l’application.

J’ai également supprimé la base de données qui été interrogée par le site web et remplit par le serveur. Maintenant le site web interroge le serveur pour récupérer l’état T de n’importe quel capteur (en utilisant le format JSON).

case "CMD_READ" :
 $aCommandToExecute = WRAPPER2 . "-o " . getenv(REMOTE_ADDR) . " -s " . $_REQUEST["iCmdToExecute"] . " -t READ";
 $output = array();
 exec($aCommandToExecute, $output);
 print(json_encode($output));
 break;

renvoit :

[“‘{\”py\/object\”: \”Deipara_Objects.CapteurMesure\”, \”InPossibleCmd\”: {\”15\”: \”recoit Nouvelle T\”}, \”physicalLocation\”: \”\”, \”ActionsCommands\”: {\”15\”: \”self.currentStatus=aData\\\\nself.LastTMeaureDate=datetime.datetime.now()\\\\nself.refreshOngoing = False\”}, \”OutPossibleCmd\”: {\”15\”: \”recoit Nouvelle T\”}, \”porteuse\”: \”GATEWAY\”, \”stateCanBeRefresh\”: true, \”type\”: \”CapteurMesure\”, \”LastRefreshDate\”: {\”py\/repr\”: \”datetime\/datetime.datetime(2013, 4, 7, 22, 13, 48, 269822)\”}, \”refreshRatemin\”: 3, \”refreshOngoing\”: false, \”id\”: 15, \”currentStatus\”: 209.0, \”Reset\”: \”\”, \”LastTMeaureDate\”: {\”py\/repr\”: \”datetime\/datetime.datetime(2013, 4, 8, 15, 31, 38, 381700)\”}, \”PossibleStates\”: {}, \”description\”: \”\”}'”]

Au site Web et les informations nécessaires pour la page seront utilisés. Toute la logique est en train de migrer vers le serveur python pour qu’il coordonne l’ensemble des actions. Je clarifie également toutes les interfaces pour utiliser des formats standard (JSON plutôt que des select de colonnes dans une base).

L’ensemble du code est toujours dispo ICI

Création d’un daemon python capable de communiquer avec le port USB et un socket

Actuellement quand je clique sur un lien qui nécessite l’envoie d’un message a un capteur dans l’appartement comme par exemple « actualiser la température » :

ScreenShot001

Le site web envoie une requête AJAX qui appel un script python pour communiquer par USB depuis le RaspberryPi vers l’Arduino Leonardo. Le script monitor ensuite le port USB pour la réponse de l’Arduino Leonardo pendant 10s pour récupérer la réponse du capteur et la transmettre au site web et à l’utilisateur.

TemperatureRequestV1

Ce design fonctionne plutôt bien mais bug de temps en temps quand plusieurs personne tentent de rafraichir la température et l’humidité en même temps car une réponse « humidité » peut arriver avant une réponse « température » et les réponses seront inverse. De plus cela ne me permet pas de mettre des capteurs  qui ne nécessite pas d’être appelé pour envoyer une information tel qu’un détecteur de fumée 😉
J’ai donc décidé de revoir le script python pour lui permettre de recevoir des infos de capteur a tout moment sans interaction avec l’utilisateur. Ce daemon doit cependant toujours être capable de recevoir les demandes des utilisateurs du site web…
Le daemon est donc en constante attente soit de message provenant de l’Arduino Leonardo au travers du port USB, soit de message de l’utilisateur (site web) provenant d’un client léger python au travers d’un socket réseau.

TemperatureRequestV2Server

Le monitoring simultané du port USB et d’un socket se fait grâce au module python « select » qui est un appel direct à la fonction « select » disponible sous linux. C’est le moyen le plus simple que j’ai trouvé pour faire du multiplexing sur mes 2 entrées (port USB et socket). Le code est donc plutôt simple puisque l’on attend juste que « select » nous donne la main pour tester quelle « entrée » a reçue quelque chose. En fonction de l’entrée on procéder au traitement. Voilà le code avec quelque commentaire pour mieux comprendre 😉

Un gros changement a également eut lieu dans la chaine de réponse puisque le process est maintenant asychrone. Il faut donc inclure plus d’information dans la réponse tel que l’ID du capteur pour savoir a quoi correspond la valeur reçue sur le port USB. Voilà un nouvel exemple de réponse de l’Arduino  Leonardo
La réponse comporte plusieurs champ/valeur tel que l’ID du capteur. Cet ID sera également inséré dans la DB.

Bonus :

Comme il m’arrive de temps en temps de redémarrer mon Raspberry Pi et que le daemon n’est pas lance au démarrage le system pourrai ne plus fonctionner….
J’ai donc légèrement modifie mon client qui est appelé par le serveur web pour détecter si le daemon est bien démarrer sur le RaspberryPi. Si ce n’est pas le cas le Server se charge de démarrer le daemon. On ne pourra donc pas répondre a la requête utilisateur mais au moins les suivantes fonctionneront bien.

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    s.connect((HOST, PORT))
except:
    print("no server available")
    s.close()
    print("starting server")
    subprocess.call("python PythonWrapperWebArduinoUsbD.py", shell=True)
    #on va rester la a l infini

Exemple :

On lance le script python serveur avec les arguments tel qui l serait lancer par le site web a la réception d’une requête client (les arguments utilisés simulant un Ping avec réponse hardcode ‘456’ d’un capteur.

pi@raspberrypi ~/Usb_Arduino_Leonardo $ python PythonWrapperWebArduinoUsbS.py -s J -i 20 -o TOTO
no server available
starting server

Le client détecte bien qu’il n y a pas de serveur et en lance donc un. Dans un autre terminal on relance le client

Client :

pi@raspberrypi ~/Usb_Arduino_Leonardo $ python PythonWrapperWebArduinoUsbS.py -s J -i 20 -o TOTO
Received 'MSG:J_ID:20_ORIGIN:TOTO'
pi@raspberrypi ~/Usb_Arduino_Leonardo $

Server :

pi@raspberrypi ~/Usb_Arduino_Leonardo $ python PythonWrapperWebArduinoUsbS.py -s J -i 20 -o TOTO
no server available
starting server
handle the server socket
handle all other sockets
input is : MSG:J_ID:20_ORIGIN:TOTO
Writting input to USB port and sending back to sender
Log line : DATE: 2013-03-11 21:02:08.518208 ORIGIN: TOTO CMD: J ID: 20
handle all other sockets
input is :
Closing socket

Le serveur à bien reçu la demande du client et à forwarder la demande a l’Arduino Leonardo par le port USB. Ensuite il a repris sa boucle et quelque secondes plus tard il a reçu la réponse ‘456’ sur le port USB. La réponse a été décodé et stocke dans la DataBase pour ensuite être utilise sur le site Web.

Wake up by LAN using Raspberry Pi

J’ai décidé d’ajouter une fonctionnalité à mon ensemble domotique pour pouvoir allumer mon PC fixe depuis mon site web (hébergé sur mon raspberry Pi).

Rien de plus simple car il suffit d’utiliser le binaire linux etherwake disponible sur Raspbian

sudo apt-get install etherwake

puis :

/usr/sbin/etherwake 20:cf:30:ca:8a:50 (avec l'adresse MAC de la carte réseau du PC fixe).

Il faut aussi penser à bien configurer le BIOS du PC fixe pour supporter le WOL. Pour la partie WEB j’ai simplement ajouté un cas dans la passerelle :

case "CMD_WOL" :
            $aCommandToExecute = 'sudo /usr/sbin/etherwake 20:cf:30:ca:8a:50';
            echo exec($aCommandToExecute);
            break;

et ajouté un bouton :

et la callback :

$("#Bopen").click(function() 
        {
            $.ajax(
            {
                type: "POST",
                   url: "http://82.227.228.35/Sender/XbeeWrapper.php",
                   data: ({iCmdType : "CMD_WOL"}),
                   cache: false,
                   dataType: "text",
                   success: onSuccess
            });
        });
...
<input id="Bopen" type="button" name="open" value="Wake up PC"/>

Lumière automatique Arduino

Projet rapide pour ajouter un peu de lumière a un meuble de l’entrée de l’appartement. La lumière s’allumera automatiquement pendant 10s quand quelqu’un passe à proximité.

Pour le moment j’utilise une simple LED mais je la remplacerai par un ruban de LED bientôt. Le détecteur de présence a déjà été utilisé avant dans ce projet.

La seule nouveauté est l’utilisation d’une nouvelle librairie Arduino pour gérer les tempos et la présence d’une gestion d’interruption. La librairie de gestion du timer “MsTimer2” est détaillé sur la page officiel et est également téléchargeable sur le playground.

L’utilisation est assez simple… Il suffit d’inclure le fichier d’entete :

#include <MsTimer2.h>

ensuite on initialise la durée de la tempo et la fonction qui sera appelée à la fin de la temporisation (dans le setup)

MsTimer2::set(10000, InterruptTimer2);

et de définir notre fonction :

void InterruptTimer2() 
{
  digitalWrite(_OutPinRelay, LOW);
}

Voilà le programme final :

#include <MsTimer2.h>

const int _InPinButton = 6;
const int _InPinIrDetector = 7;
const int _OutPinLed = 11;
const int _OutPinRelay = 10;
int aLightState = 0;

void InterruptTimer2() 
{
  digitalWrite(_OutPinRelay, LOW);
}

void setup()
{
  //defined IO
  pinMode(_InPinButton,INPUT);
  pinMode(_InPinIrDetector,INPUT);
  pinMode(_OutPinLed, OUTPUT);
  pinMode(_OutPinRelay, OUTPUT);
  MsTimer2::set(10000, InterruptTimer2);
}

void flashPin(int pin, int times, int wait) 
{
  for (int i = 0; i < times; i++) {
    digitalWrite(pin, HIGH);
    delay(wait);
    digitalWrite(pin, LOW);

    if (i + 1 < times) {
      delay(wait);
    }
  }
}

void loop()
{
  //Read button status
  int aInputDigitalValue = digitalRead(_InPinIrDetector);

  if (aInputDigitalValue == HIGH)
  {
    //IR detector see something
    digitalWrite(_OutPinRelay, HIGH);
    MsTimer2::start(); // active Timer 2 
  }
}

Le résultat final avec la barrette de LED et le montage cache sous le meuble :

20130217_131435

Merge !

Il est temps de mettre tous les projets en commun :

pour obtenir un système domotique :

Le système repose sur un site web “Domos” hébergé sur un raspberry pi qui communique a divers équipement de la maison par différents média (ZigBee, Ethernet, X10, …) au travers une passerelle Arduino.

Systeme domotique x10 zigbee xbee arduino

L’ensemble des équipements peut être commande depuis le site web. Ce dernier est construit autour du framework JqueryMobile pour un affichage optimal sur les smartphone (le site web doit être accessible depuis n’importe ou pour pouvoir visualiser l’état du système a tout moment.

Le site comprend plusieurs section (correspondant aux différentes pièces de l’appartement et quelque “actions” disponible sur la page d’accueil.

 

 

Linux Udev pour setter les permissions sur les peripheriques USB

Suite à mon article précédant ou mon Raspberry Pi communique avec une Arduino Leonardo grâce à la lib libusb… j’ai voulu lancer mon programme avec un utilisateur non root et j’obtiens l’erreur suivante :

libusb couldn't open USB device /dev/bus/usb/002/005: Permission denied

Après quelques recherches sur Internet :

  • http://ubuntuforums.org/showthread.php?t=1939231
  • https://groups.google.com/forum/#!msg/ultra-cheap-sdr/bNn5naF4nwg/u_FjO7xC3rcJ
  • https://wiki.archlinux.org/index.php/Map_Custom_Device_Entries_with_udev

Je me rends compte que le problème vient de Udev qui crée le périphérique lors du branchement avec un accès en écriture uniquement pour le root. Pour pallier ce problème une solution simple et de créer une règle UDEV pour changer la création du périphérique à la volée. Il existe déjà beaucoup de documentation sur UDEV et je n’ai pas l’intention de faire du copier-coller 😉

Voilà la règle ajoute dans mon cas pour changer les permissions du périphérique Arduino Leonardo :

pi@raspberrypi /var/www $ sudo cat /etc/udev/rules.d/10-arduinoLeo.rules
SUBSYSTEMS=="usb", ACTION=="add", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="8036", MODE:="0777"

Fini les problèmes 😉

Bonus : Protéger le site web par mot de passe (serveur lighttpd) :
http://www.cyberciti.biz/tips/lighttpd-setup-a-password-protected-directory-directories.html

Arduino Leonardo : USB Device

Il est aujourd’hui possible d’émuler un périphérique USB HID avec les carte Arduino UNO grâce a au framework LUFA. Néanmoins cela nécessite de flasher la carte et donc un programmateur externe et cela conduit également a la perte du bootloader Arduino.
Des informations sur le framework LUFA ICI et un exemple d’utilisation avec une Arduino Uno ICI

La nouvelle carte Arduino Leonardo possède également une gestion USB HID. Celle-ci est intégrée dans l’IDE. Nous allons l’utiliser pour communique avec serveur Raspberry Pi fonctionnant sous Raspbian. Cela permet de créer des périphériques HID sans devoir flasher le firmeware comme c’est le cas pour la librairie LUFA et d’utilise l’IDE de DEV Arduino.

Un thread intéressant à propos de LUFA et l’Arduino Leonardo :

It’s disappointing to see Arduino move away from LUFA in only one generation of hardware
revisions, but I understand that they need to more tightly integrate the USB code with their custom platform to make it more useful to the masses, and portable to the new expanded range of Arduino architectures.

On peut donc penser qu’il y aura de plus en plus de projet USB grâce à la nouvelle génération de carte Arduino Leonardo. Il existe déjà quelque exemple dans l’IDE 1.01 (dans le menu Fichier -> Exemple -> Leonardo) pour la gestion d’un clavier et d’une souris.

Je vais utiliser un des exemple “clavier” et la lib libusb pour essayer de communiquer entre mon raspberry pi et mon arduino Leonardo. Voilà le code Leonardo qui se contente de faire clignoter une LED s’il reçoit qq chose sur le port USB :

/* 
 Blink LED when receive something from USB
 */

//Define the pin number
const int _OutPinLedTest = 7;
const int PIN_BOUTON_ON = 4;
const int PIN_BOUTON_OFF = 3;

int _ledState=0;

void setup() {
  //defined input button
  pinMode(_OutPinLedTest,OUTPUT);
  pinMode(PIN_BOUTON_ON,INPUT);
  pinMode(PIN_BOUTON_OFF,INPUT);
  //Active resistor as pull up
  digitalWrite(PIN_BOUTON_ON,HIGH);
  digitalWrite(PIN_BOUTON_OFF,HIGH);
  // open the serial port:
  Serial.begin(9600);
  // initialize control over the keyboard:
  Keyboard.begin();
}

void flashPin(int pin, int times, int wait) 
{
  for (int i = 0; i < times; i++) {
    digitalWrite(pin, HIGH);
    delay(wait);
    digitalWrite(pin, LOW);

    if (i + 1 < times) {
      delay(wait);
    }
  }
}

void switchLedStatus(int pin) 
{
  if(_ledState == 0) {
    digitalWrite(pin, HIGH);
    _ledState = 1;
    delay(100);
  }else{
    digitalWrite(pin, LOW);
    _ledState = 0;
    delay(100);

  }
}

void loop() {
  // check for incoming serial data:
  if (Serial.available() > 0) {
    // read incoming serial data:
    char inChar = Serial.read();
    // Type the next ASCII value from what you received:
    flashPin(_OutPinLedTest, 10, 600);
    //Keyboard.write(inChar+1);
  }  
  //Read button status
  int aInputDigitalValue = digitalRead(PIN_BOUTON_ON);
  //Reset the counter if button is press
  if (aInputDigitalValue == LOW)
  {
    flashPin(_OutPinLedTest, 10, 600);
  }
  aInputDigitalValue = digitalRead(PIN_BOUTON_OFF);
  //Reset the counter if button is press
  if (aInputDigitalValue == LOW)
  {
    switchLedStatus(_OutPinLedTest);
  }
}

Cote Raspberry Pi avant de connecter la carte :

pi@raspberrypi ~ $ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 007: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB
Bus 001 Device 008: ID 04f2:1013 Chicony Electronics Co., Ltd

Après connexion avec la Carte Leonardo (sur un HUB USB externe) :

pi@raspberrypi ~ $ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 007: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB
Bus 001 Device 008: ID 04f2:1013 Chicony Electronics Co., Ltd
Bus 001 Device 010: ID 2341:8036 Arduino SA

La carte Leonardo est détecte ! Voyons voir dans le détail :

Bus 001 Device 010: ID 2341:8036 Arduino SA
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0x2341 Arduino SA
  idProduct          0x8036
  bcdDevice            1.00
  iManufacturer           1
  iProduct                2
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength          100
    bNumInterfaces          3
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    Interface Association:
      bLength                 8
      bDescriptorType        11
      bFirstInterface         0
      bInterfaceCount         2
      bFunctionClass          2 Communications
      bFunctionSubClass       2 Abstract (modem)
      bFunctionProtocol       1 AT-commands (v.25ter)
      iFunction               0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         2 Communications
      bInterfaceSubClass      2 Abstract (modem)
      bInterfaceProtocol      0 None
      iInterface              0
      CDC Header:
        bcdCDC               1.10
      CDC Call Management:
        bmCapabilities       0x01
          call management
        bDataInterface          1
      CDC ACM:
        bmCapabilities       0x06
          sends break
          line coding and serial state
      CDC Union:
        bMasterInterface        0
        bSlaveInterface         1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0010  1x 16 bytes
        bInterval              64
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      0
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.01
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength     101
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x84  EP 4 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1

Pour mieux comprendre à quoi correspondent ces informations vous pouvez jeter à la présentation de la norme USB disponible ICI. Le schéma suivant permet de voir les différents “composants” d’un périphérique USB :

Et si on adapte a l’Arduino Leonardo :

Si ma mémoire est bonne (la dernière fois que j’ai utilisé la lib libusb été à l’époque où j’utilisai les microcontroleur PIC avec une communication Linux USB – PIC 18f4550) il nous faut ces 2 informations :

idVendor           0x2341 Arduino SA
idProduct          0x8036

Cote Raspbian

J’ai trouvé un vieux code C++ que j’utilisai pour communiquer avec les PIC 18f4550 qui fonctionne avec la lib libusb. On doit donc commencer par l’installer :

pi@raspberrypi ~/USB_Leonardo $ sudo apt-get install libusb-1.0-0-dev

Ensuite….un petit tour sur la doc de la lib disponible ICI pour vérifier les dernières mise a jour/info. Ensuite j’ai mis à jour mon vieux programme (ca n’a pas bcp change en deux…) pour que l’utilisateur puisse changer d’interface et de configuration sans devoir tout recompiler. Voilà le programme :

#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
//C++ migration
#include <iostream>

#include <stdlib.h>
#include <libusb-1.0/libusb.h>

#define VERSION "0.1.0"

#define VENDOR_ID 9025
//CWA
#define LINUX
#define PRODUCT_ID 32822

// HID Class-Specific Requests values. See section 7.2 of the HID specifications
#define HID_GET_REPORT                0x01
#define HID_GET_IDLE                  0x02
#define HID_GET_PROTOCOL              0x03
#define HID_SET_REPORT                0x09
#define HID_SET_IDLE                  0x0A
#define HID_SET_PROTOCOL              0x0B
#define HID_REPORT_TYPE_INPUT         0x01
#define HID_REPORT_TYPE_OUTPUT        0x02
#define HID_REPORT_TYPE_FEATURE       0x03

#define CTRL_IN                LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE
#define CTRL_OUT       LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE

const static int PACKET_CTRL_LEN=2; 

const static int PACKET_INT_LEN=2;
const static int INTERFACE=2;
const static int ENDPOINT_INT_IN=0x81; /* endpoint 0x81 address for IN */
const static int ENDPOINT_INT_OUT=2; /* endpoint 1 address for OUT */
const static int TIMEOUT=5000; /* timeout in ms */

void bad(const char *why)
{
    fprintf(stderr,"Fatal error> %s\n",why);
    exit(17);
}

static struct libusb_device_handle *devh = NULL;

static int find_lvr_hidusb(void)
{
    devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
    return devh ? 0 : -EIO;
}

int main(void)
{
    //Store the interface number to use
    int aInterface;
    std::cout << "Please enter an interface ID (integer value): ";
    std::cin >> aInterface;
    std::cout << "The interface used will be : " << aInterface <<std::endl;

    //Store the configuration number to use
    int aConfiguration;
    std::cout << "Please enter an configuration ID (integer value): ";
    std::cin >> aConfiguration;
    std::cout << "The configuration used will be : " << aConfiguration <<std::endl;

    int r = 1;
    unsigned char *data = new unsigned char[1]; //data to write
    data[0]=0x00; //some dummy values
    //for (int compt=0; compt<63;compt++)
    //data[compt]=0x00; //some dummy values
    int actual; //used to find out how many bytes were written
    int actual2; //used to find out how many bytes were written

    r = libusb_init(NULL);
    if (r < 0) {
        fprintf(stderr, "Failed to initialise libusb\n");
        exit(1);
    }
    printf("Successfully initialise libusb\n");

    libusb_set_debug(NULL,4);

    r = find_lvr_hidusb();
    if (r < 0) {
        fprintf(stderr, "Could not find/open LVR Generic HID device\n");
        goto out;
    }
    printf("Successfully find the LVR Generic HID device\n");

#ifdef LINUX
    printf("LINUX - we need to see if a kernel driver is active on the interface.\nIf yes we have to detach it to be able to claim the interface\n");

    r = libusb_kernel_driver_active(devh, aInterface);

    if (r==0)
    {
        printf("No Kernel Driver active on this interface\n");
    }
    else
    {
        printf("Kernel Driver already active on this interface - we have to detach it to be able to claim the interface\n");
        r = libusb_detach_kernel_driver(devh, aInterface);  
        if (r < 0) 
        {
            fprintf(stderr, "libusb_detach_kernel_driver error %d\n", r);
            goto out;
        }
        printf("Kernel Driver detach\n");
    }

#endif

    /*r =  libusb_reset_device(devh);
    if (r < 0) {
        fprintf(stderr, "libusb_set_configuration error %d\n", r);
        goto out;
    }
    printf("Successfully reset usb \n");*/

    r = libusb_set_configuration(devh, aConfiguration);
    if (r < 0) {
        fprintf(stderr, "libusb_set_configuration error %d\n", r);
        goto out;
    }
    printf("Successfully set usb configuration 1\n");

    r = libusb_claim_interface(devh, aInterface);
    if (r < 0) {
        fprintf(stderr, "libusb_claim_interface error %d\n", r);
        goto out;
    }
    printf("Successfully claimed interface\n");
    r = libusb_bulk_transfer(devh, ENDPOINT_INT_OUT, data, 1, &actual2, 0);  
    if(r == 0 && actual2 == 1) //we wrote the 4 bytes successfully 
    {
        fprintf(stderr, "Successfully perform bulk transfer \n");
    }
    else
    {
    fprintf(stderr, "libusb_bulk_transfer error %d\n", r);
        goto out;
    }

    //r = libusb_bulk_transfer(devh, ENDPOINT_INT_OUT, data, 64, &actual, 0); 
    r = libusb_interrupt_transfer(devh, ENDPOINT_INT_OUT, data, 1, &actual, 0); 
    if(r == 0 && actual == 1) //we wrote the 4 bytes successfully 
    {
        fprintf(stderr, "Successfully perform interupt transfer \n");
    }
    else
    {
    fprintf(stderr, "libusb_interrupt_transfer error %d\n", r);
        goto out;
    }

    libusb_release_interface(devh, 0);
out:
    //     libusb_reset_device(devh);
    libusb_close(devh);
    libusb_exit(NULL);
    return r >= 0 ? r : -r;

}

Voilà ce que ça donne au final :

pi@raspberrypi ~/USB_Leonardo $ sudo ./test
Please enter an interface ID (integer value): 1
The interface used will be : 1
Please enter an configuration ID (integer value): 1
The configuration used will be : 1
Successfully initialise libusb
Successfully find the LVR Generic HID device
LINUX - we need to see if a kernel driver is active on the interface.
If yes we have to detach it to be able to claim the interface
No Kernel Driver active on this interface
Successfully set usb configuration 1
Successfully claimed interface
Successfully perform bulk transfer
[timestamp] [threadID] facility level [function call] <message>
--------------------------------------------------------------------------------
[ 0.000000] [000007ca] libusbx: error [submit_bulk_transfer] submiturb failed error -1 errno=22
libusb_interrupt_transfer error -1

et une led qui clignote 😉

Migration Arduino Uno vers Arduino Leonardo

Cet article traite de la migration de l’application CPL décrite dans un article précédant “Application domotique avec Arduino et CPL ” accessible ici vers une plateforme Arduino Leonardo.

La carte Arduino Leonardo (présentation ICI) est la première carte Arduino à utiliser une “vrai” liaison USB :
The Leonardo differs from all preceding boards in that the ATmega32u4 has built-in USB communication, eliminating the need for a secondary processor. This allows the Leonardo to appear to a connected computer as a mouse and keyboard, in addition to a virtual (CDC) serial / COM port. It also has other implications for the behavior of the board; these are detailed on the getting started page.

Un des premiers changements est de changer la liaison série par défaut de l Arduino Uno vers la liaison série non USB de l’Arduino Leonardo (car la liaison série par défaut de l’Arduino Uno est l’USB). La librairie Xbee doit donc être modifie (merci a http://arduino.cc/forum/index.php/topic,111354.0.html) en remplaçant la ligne:

_serial = &Serial;

par :

#if defined(USBCON)
   _serial = &Serial1;
#else
   _serial = &Serial;
#endif

Il faut aussi modifier la création de l’objet X10 pour ne pas utiliser une interruption “custom” car elles ne fonctionnent pas sur l’Arduino Leonardo voir http://vort.org/tag/bugs/ et http://code.google.com/p/arduino/issues/detail?id=714

On va donc utilise une interruption standard sur le pin 2 grâce au code suivant :

X10ex x10ex = X10ex(
  1, // Zero Cross Interrupt Number (2 = "Custom" Pin Change Interrupt)
  2, // Zero Cross Interrupt Pin (Pin 4-7 can be used with interrupt 2)
  9, // Power Line Transmit Pin 
  10, // Power Line Receive Pin
  true, // Enable this to see echo of what is transmitted on the power line
  powerLineEvent, // Event triggered when power line message is received
  1, // Number of phases (1 = No Phase Repeat/Coupling)
  50 // The power line AC frequency (e.g. 50Hz in Europe, 60Hz in North America)
);

au lieu de

X10ex x10ex = X10ex(
  2, // Zero Cross Interrupt Number (2 = "Custom" Pin Change Interrupt)
  4, // Zero Cross Interrupt Pin (Pin 4-7 can be used with interrupt 2)
  5, // Power Line Transmit Pin 
  6, // Power Line Receive Pin
  true, // Enable this to see echo of what is transmitted on the power line
  powerLineEvent, // Event triggered when power line message is received
  1, // Number of phases (1 = No Phase Repeat/Coupling)
  50 // The power line AC frequency (e.g. 50Hz in Europe, 60Hz in North America)
);

Après ça… tout fonctionne 😉

J’upload ici le REPO complet fonctionnel : ED2

Raspberry Pi : installation de Raspbian

Contexte

Mon Raspberry Pi est enfin arrivé ce week-end :

Grosso modo il s’agit d’un mini PC sur une architecture ARM avec un port Ethernet…pour 40E ! Idéal pour un petit serveur web communicant avec mes montages Arduino 😉 Plus d’info sur le Raspberry Pi sur la page officiel ICI.

Hardware

Tout d’abord le matériel… il suffit de 3 choses :

  • Câble alimentation micro usb capable de fournir plus de 700 mA (la majorité des chargeurs de portable feront l’affaire MAIS pas un port USB de PC dont le courant est limite (200mA si ma mémoire est bonne)),
  • Cable ethernet,
  • Carte SD de 4Go et classe 4 au minimum

Le clavier, la souris, et la sortie HDMI sont inutile puisque j’utilise putty pour me connecter sur le Pi (même la première fois).

Software

Installation

J’ai choisi d’installer une distribution spécialement dédié au Pi (base sur Debian) : Raspbian. Il s’agit d’une des distributions conseillées sur le site officiel dont l’image et la présentation sont disponible ICI.

La création de la carte SD est assez simple et clairement détaillé ICI.

Une fois la carte terminée est mise en place… le Raspberry peut effectuer son premier boot. Le Pi va obtenir en adresse IP grâce au serveur DHPC présent sur notre réseau. Cette adresse nous servira pour pouvoir se logger sur le Pi en SSH avec Putty. Il est maintenant possible de se connecter sur le Pi en utilisant putty (attendre qq minutes après la mise sous tension pour laisser le temps au boot de se terminer).

Le login par défaut est “user:pi // pass:raspberry).

Bonus :
Dans mon cas je souhaite que mon Raspberry garde toujours la même adresse IP donc je vais modifier la table d’adressage DHCP de mon serveur pour que l’adresse MAC de mon Pi soit toujours associe à l’IP 192.168.0.6.Pour cela il faut l’adresse MAC que l’on obtient avec ifconfig :

pi@raspberrypi ~ $ ifconfig
eth0      Link encap:Ethernet  HWaddr b8:27:eb:df:0e:2e
          inet addr:192.168.0.9  Bcast:192.168.0.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:158 errors:0 dropped:0 overruns:0 frame:0
          TX packets:132 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:13571 (13.2 KiB)  TX bytes:15732 (15.3 KiB)

Puis il suffit d’ajouter cette adresse dans le serveur DHCP avec lP 192.168.0.6

 Mise à jour

La version de Raspbian disponible au moment où j’écris cet article présente certains bugs en particulier pour le module lighttpd (dont on aura besoin pour le serveur WEB) qui empêche l’installation. On va donc mettre a jour la version avec APT:

pi@raspberrypi ~ $ sudo apt-get update
...
pi@raspberrypi ~ $ sudo apt-get upgrade
...

Installation du serveur Web

Rien de plus simple….a condition d’avoir mis a jour sa version !

pi@raspberrypi ~ $ sudo apt-get instal lighttpd

et si tout se passe bien vous devriez être capable d’ouvrir une page web sur l’IP de votre Pi qui devrait ressemble à :

Il faut ensuite activer la gestion du php en commençant par :

pi@raspberrypi /var/www $ sudo apt-get install php5-cgi

il suffit ensuite d’activer le support des modules ds lighttpd avec :

pi@raspberrypi /var/www $ sudo lighttpd-enable-mod fastcgi
Enabling fastcgi: ok
Run /etc/init.d/lighttpd force-reload to enable changes
pi@raspberrypi /var/www $ sudo lighttpd-enable-mod fastcgi-php
Enabling fastcgi-php: ok
Run /etc/init.d/lighttpd force-reload to enable changes

On peut tester le résultat avec la page d’exemple suivante :

pi@raspberrypi /var/www $ cat test.php
<html>
<head>
<title>PHP Test</title>
</head>
<body>
<?php phpinfo(); ?>
</body>
</html>

 Installation du serveur FTP

Pour le serveur FTP j’ai choisi vsftpd pour sa légèreté et rapidité. L’installation se déroule sans pb avec APT :

pi@raspberrypi ~ $ sudo apt-get install vsftpd
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  vsftpd
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 149 kB of archives.
After this operation, 329 kB of additional disk space will be used.
Get:1 http://mirrordirector.raspbian.org/raspbian/ wheezy/main vsftpd armhf 2.3.5-3 [149 kB]
Fetched 149 kB in 5s (27.8 kB/s)
Preconfiguring packages ...
Selecting previously unselected package vsftpd.
(Reading database ... 54581 files and directories currently installed.)
Unpacking vsftpd (from .../vsftpd_2.3.5-3_armhf.deb) ...
Processing triggers for man-db ...
Setting up vsftpd (2.3.5-3) ...
Starting FTP server: vsftpd.

Apres l’installation il n’est pas encore possible de se connecter sur le Pi. On obtient pour le moment l’erreur suivante (avec le user pi) :

Statut :    Connexion à 192.168.0.9:21...
Statut :    Connexion établie, attente du message d'accueil...
Réponse :    220 (vsFTPd 2.3.5)
Commande :    USER pi
Réponse :    530 This FTP server is anonymous only.
Erreur :    Impossible d'établir une connexion au serveur

Alors que la connexion fonctionne bien pour les utilisateurs anonymes. On va donc changer la configuration du daemon pour interdire les connections anonyme et autoriser les utilisateurs a se logger:

pi@raspberrypi ~ $ grep local_enable /etc/vsftpd.conf
#local_enable=YES
devient 
pi@raspberrypi ~ $ grep local_enable /etc/vsftpd.conf
local_enable=YES

et

pi@raspberrypi ~ $ grep anonymous_en /etc/vsftpd.conf
anonymous_enable=YES
devient
pi@raspberrypi ~ $ grep anonymous_en /etc/vsftpd.conf
anonymous_enable=NO

la connexion anonyme est maintenant interdite et il est possible de se logger avec l’utilisateur Pi:

L’installation est terminée 😉

Application domotique avec Arduino et CPL

Le but de ce projet et de pouvoir commander certains équipements depuis un site web hébergé sur un raspberry pi (je l’attends toujours…) qui communique avec un arduino relié à une passerelle CPL. Les ordres sont transmis aux différents équipements au travers du CPL en utilisant le protocole X10.

Je vous conseille vivement de lire le document de Patrick Abati qui présente le protocole X10 (le meilleur que j’ai pu trouver pour comprendre le fonctionnement) disponible ICI ou x10 presentation.

Les différents modules présents sur le réseau électrique sont :

J’utilise une librairie déjà existante qui peut être téléchargé ici pour dialoguer avec la passerelle X10 de marmitek : https://code.google.com/p/arduino-x10/

La communication se fait également avec une liaison Xbee avec une carte mère (la même que cette utilise dans l’article sur la station météo). De plus je vais ajouter une liaison USB dès que j’aurai reçu mon Arduino Leonardo (avec une vrai gestion USB et non une liaison série émulé). Cette liaison USB servira a communique avec un Raspberry Pi pour commander les équipements depuis un site Web (en plus de la carte mère).

Voilà le programme complet:

//Xbee library
#include <XBee.h>
//X10
#include <X10ex.h>

#define POWER_LINE_MSG "PL:"
#define POWER_LINE_BUFFER_ERROR "PL:_ExBuffer"
#define SERIAL_DATA_MSG "SD:"
#define SERIAL_DATA_THRESHOLD 1000
#define SERIAL_DATA_TIMEOUT "SD:_ExTimOut"
#define MODULE_STATE_MSG "MS:"
#define MSG_DATA_ERROR "_ExSyntax"
//Next

XBee _Xbee = XBee(); // create Xbee object to control a Xbee
ZBRxResponse _ZbRxResp = ZBRxResponse(); //Create reusable response objects for responses we expect to handle
int _ServoPosition = 90;

//Define the pin number
const int _OutPinLedTest = 8;

// Fields used for serial and byte message reception
unsigned long sdReceived;
char bmHouse;
byte bmUnit;
byte bmCommand;
byte bmExtCommand;
int _CmdReceived = 0;

// X10 Power Line Communication Library
X10ex x10ex = X10ex(
  2, // Zero Cross Interrupt Number (2 = "Custom" Pin Change Interrupt)
  4, // Zero Cross Interrupt Pin (Pin 4-7 can be used with interrupt 2)
  5, // Power Line Transmit Pin 
  6, // Power Line Receive Pin
  true, // Enable this to see echo of what is transmitted on the power line
  powerLineEvent, // Event triggered when power line message is received
  1, // Number of phases (1 = No Phase Repeat/Coupling)
  50 // The power line AC frequency (e.g. 50Hz in Europe, 60Hz in North America)
);

void setup()
{
  //defined input button
  pinMode(_OutPinLedTest,OUTPUT);

  // start serial
  _Xbee.begin(9600);
    // Remember to set baud rate in Serial Monitor or lower this to 9600 (default value)
  Serial.begin(9600);
  // Start the Power Line Communication library
  x10ex.begin();
}

void flashPin(int pin, int times, int wait) 
{
  for (int i = 0; i < times; i++) {
    digitalWrite(pin, HIGH);
    delay(wait);
    digitalWrite(pin, LOW);

    if (i + 1 < times) {
      delay(wait);
    }
  }
}

void loop()
{
  _Xbee.readPacket();
  //Reset command to 0
  _CmdReceived = 0;

  if (_Xbee.getResponse().isAvailable()) {
    // got something
    Serial.println("We have something on the serial");
    Serial.print("ApiId: 0x");
    Serial.println(_Xbee.getResponse().getApiId(), HEX);

    if (_Xbee.getResponse().getApiId() == ZB_RX_RESPONSE) {
      Serial.println("This is a ZB response");

      // now fill our zb rx class
      _Xbee.getResponse().getZBRxResponse(_ZbRxResp);
      flashPin(_OutPinLedTest, 3, 200);
      _CmdReceived = _ZbRxResp.getData(0);

    }  
  } 
  //Process the command
  //Test if we have an action to do 
  if(_CmdReceived==5)
  {
    x10ex.sendCmd('A', 4, CMD_ON, 1);
  }
  else if(_CmdReceived==6)
  {
    x10ex.sendCmd('A', 4, CMD_OFF, 1);
  }
  else if(_CmdReceived==7)
  {
    x10ex.sendCmd('A', 5, CMD_ON, 1);
  }
  else if(_CmdReceived==8)
  {
    x10ex.sendCmd('A', 5, CMD_OFF, 1);
  }
  else if(_CmdReceived==9)
  {
    x10ex.sendCmd('A', 7, CMD_ON, 1);
  }
  else if(_CmdReceived==10)
  {
    x10ex.sendCmd('A', 7, CMD_OFF, 1);
  }
  else if(_CmdReceived==11)
  {
    x10ex.sendCmd('A', 7, CMD_ON, 1);
    x10ex.sendCmd('A', 5, CMD_ON, 1);
  }
  else if(_CmdReceived==12)
  {
    x10ex.sendCmd('A', 7, CMD_OFF, 1);
    x10ex.sendCmd('A', 5, CMD_OFF, 1);
  }
}

void printX10Message(const char type[], char house, byte unit, byte command, byte extData, byte extCommand, int remainingBits)
{
  printX10TypeHouseUnit(type, house, unit, command);
  // Ignore non X10 commands like the CMD_ADDRESS command used by the IR library
  if(command <= 0xF)
  {
    Serial.print(command, HEX);
    if(extCommand || (extData && (command == CMD_STATUS_ON || command == CMD_STATUS_OFF)))
    {
      printX10ByteAsHex(extCommand);
      printX10ByteAsHex(extCommand == EXC_PRE_SET_DIM ? extData & B111111 : extData);
    }
  }
  else
  {
    Serial.print("_");
  }
  Serial.println();
}

void printX10TypeHouseUnit(const char type[], char house, byte unit, byte command)
{
  Serial.print(type);
  Serial.print(house);
  if(
    unit &&
    unit != DATA_UNKNOWN/* &&
    command != CMD_ALL_UNITS_OFF &&
    command != CMD_ALL_LIGHTS_ON &&
    command != CMD_ALL_LIGHTS_OFF &&
    command != CMD_HAIL_REQUEST*/)
  {
    Serial.print(unit - 1, HEX);
  }
  else
  {
    Serial.print("_");
  }
}

void printX10ByteAsHex(byte data)
{
  Serial.print("x");
  if(data <= 0xF) { Serial.print("0"); }
  Serial.print(data, HEX);
}

byte charHexToDecimal(byte input)
{
  // 0123456789  =>  0-15
  if(input >= 0x30 && input <= 0x39) input -= 0x30;
  // ABCDEF  =>  10-15
  else if(input >= 0x41 && input <= 0x46) input -= 0x37;
  // Return converted byte
  return input;
}

// Process messages received from X10 modules over the power line
void powerLineEvent(char house, byte unit, byte command, byte extData, byte extCommand, byte remainingBits)
{
  printX10Message(POWER_LINE_MSG, house, unit, command, extData, extCommand, remainingBits);
}