Lecture de capteurs USB par callbacks

Lecture de capteurs USB par callbacks

La plupart des exemples d'utilisation de la librairie Yoctopuce que nous présentons pour interagir avec les capteurs USB utilisent des appels successifs de fonctions (approche procédurale, par polling). C'est ce que nous recommandons initialement, parce que c'est en général plus intuitif et moins risqué. Il existe pourtant une alternative, parfois bien pratique: l'approche événementielle, par callback. C'est la nouvelle interface que nous allons présenter aujourd'hui.


Nous allons illustrer cette technique sur un exemple simple, qui consiste à fabriquer une console avec trois potentiomètres pour changer la couleur de toutes les leds RGB Yocto-Color connectées. On utilisera un potentiomètre par composante de couleur (rouge, vert et bleu). Nous verrons comment implémenter cet exemple à l'aide de callbacks, et comment cette solution peut parfaitement s'étendre au cas de modules connectés via un réseau IP. Nous utiliserons aussi un nouveau produit qui devrait être disponible sur le shop d'ici quelques semaines: le Yocto-PowerColor.

Matériel
Commençons par le matériel dont nous aurons besoin:

- 3 potentiomètres standard (nous utilisons un modèle trouvé chez Distrelec, mais n'importe quel autre potentiomètre fonctionnerait).
- 1 Yocto-Knob pour brancher les 3 potentiomètres.
- au moins une Yocto-Color (ou une Yocto-PowerColor, disponible prochainement).
- du fil électrique.

Montage
Pour rendre cet exemple un peu plus présentable, nous avons découpé un petit support en Plexiglass pour monter les potentiomètres. Une fois monté, on branche les trois potentiomètres à un module Yocto-Knob. Il suffit de souder du fil sur deux contacts adjacents du potentiomètre et de brancher l'un de deux sur le fil commun du Yocto-Knob et l'autre sur l'une des 5 entrées. Comme nous pouvons calibrer les entrées en software, vous n'avez pas besoin de vous soucier de la valeur de vos potentiomètre ni de rajouter d'autres composants.

voilà nos trois potentiomètre dans leur écrin
voilà nos trois potentiomètre dans leur écrin



Configuration
Nous avons donc un Yocto-Knob, avec trois potentiomètres branchés sur les trois premières entrée AnButton du module. Dans les exemples précédents, nous avons référencé nos modules soit par leur numéro de série, soit en utilisant une des fonctions yFirstXXXX(). Cette fois-ci, nous attribuerons un nom logique à chaque bouton ("Red", "Green" et "Blue"), et les référencerons par nom dans le programme. Commençons donc par lancer le VirtualHub pour configurer le nom de de chaque bouton et calibrer sa course utilisable.

Fenêtre de configuration du Yocto-Knob
Fenêtre de configuration du Yocto-Knob



Le code
Retroussons nous les manches et commencer par coder un objet ColorMixer qui s'occupera de mettre à jour toutes les ColorLed trouvées avec la couleur choisie, et implémenter trois callback qui seront utilisés par les trois potentiomètres pour mettre à jour seulement la composante correspondante au potentiomètre.

callback
Toutes les fonctions d'un module Yoctopuce contiennent un attribut appelé "valeur publiée". Il s'agit de l'état courant de la fonction, par exemple la valeur mesurée pour un capteur, la position active pour un relais ou la couleur pour une led RGB. Ainsi, pour toute fonction il est possible de définir un callback qui sera appelé par la librairie à chaque changement de la valeur publiée. Un callback doit être compatible avec le prototype suivant (exemple du C++) :

    void callback(YFunction *func, const string& functionValue);



Le paramètre func pointe sur la fonction dont la valeur à changé, et functionValue est une chaîne décrivant la nouvelle valeur. De plus, pour simplifier l'utilisation du callback, toutes les fonctions possèdent un attribut appelé userData, qui n'est pas touché par l'API mais permet de passer implicitement un contexte au callback.

Pour vous éviter les problèmes de concurrence et des corruptions, il est garanti que les callback ne sont pas appelés quand vous ne vous y attendez pas. Ils ne seront appelés que dans deux cas:

- pendant un appel à yHandleEvents
- pendant un appel à ySleep()

Il est cependant important d'appeler l'une de ces deux fonctions périodiquement pour vous assurer que les callback ne soient pas appelé trop tard. Si vous n'avez pas peur de la concurrence, si vous travaillez dans une environnement multi-thread et si vous désirez recevoir les callbacks à tout moment, faites simplement un thread qui appelle ySleep en boucle.

Dans notre cas le callback sera implémenté comme suit:

  static void redCallback(YAnButton *button, const string& calibratedValue)
    {
        // Compute the red component by scaling the calibratedValue
        // from 0..1000 to 0..255
        unsigned char red  =  ( atoi(calibratedValue.c_str()) * 255 / 1000 ) & 0xff;
       
        // Use the userData to get the pointer to the instance of ColorMixer
        ColorMixer *mixer  =  (ColorMixer*) button->get_userData();
       
        // Change the color
        mixer->changeRed(red);
    }



La fonction principale

Tout d'abord nous allons enregistrer tous les hubs qui sont spécifiés sur la ligne de commande

    for (i=0; i< argc ;i++) {
        if(yRegisterHub(argv[i], errmsg) != YAPI_SUCCESS) {
            cerr << "Unable to get access to devices on " << argv[i] << endl;
            cerr << "error: " << errmsg << endl;
            return 1;
        }
    }



Il faut ensuite instancier notre objet ColorMixer et lui attribuer les trois fonctions AnButton choisies, en les identifiant par leur nom. Nous allons aussi enregistrer toutes les ColorLed qui sont accessibles.

    // Create our ColorMixer Object
    ColorMixer mixer = ColorMixer();

    // Get pointers to the 3 knobs, using the logical names
    // that we have configured using the VirtualHub
    YAnButton* knobRed  = yFindAnButton("Red");
    YAnButton* knobGreen = yFindAnButton("Green");
    YAnButton* knobBlue  = yFindAnButton("Blue");

    // Register the 3 knobs in the mixer
    mixer.assignRedButton( knobRed );
    mixer.assignGreenButton( knobGreen );
    mixer.assignBlueButton( knobBlue );

    // Register all leds connected to the "network"
    YColorLed* led = yFirstColorLed();
    while(led){
        mixer.addLED(led);
        nbled++;
        led = led->nextColorLed();
    }



Une fois que l'initialisation à été faite, il ne reste plus qu'à appeler ySleep() régulièrement.

// never-ending loop that will.. while(1) { // 1. handle all event during 1000 ms, without using much CPU ... ySleep(1000, errmsg); // 2. handle device hotplug/unplug if needed yUpdateDeviceList(errmsg); } return 0; }



Si vous désirez l'exemple au complet, téléchargez la dernière libraire C++ ici. Vous trouverez ce code dans le répertoire Examples.

Démo avec une seule machine

Pour commencer, on va vérifier que tout fonctionne correctement en local avec les leds et les potentiomètres branchés directement sur la machine de test. Comme les modules sont connectés directement en USB sur la même machine, nous n'avons pas besoin de lancer de VirtualHub. Il suffit de lancer l'exécutable avec "usb" comme argument. L'appel à yRegisterHub(argv[i],errmsg); va s'occuper de faire la detection USB sur la machine courante et initialiser l'API. Dès que nous bougerons nos potentiomètres, le Yocto-Knob enverra la nouvelle valeur par USB et notre callback sera appelé.

Tout est branché directement en USB sur la même machine
Tout est branché directement en USB sur la même machine


  



Comme on le voit dans la vidéo, ça marche. Mais sans changer une ligne de code, on peu faire encore un peu plus sympa...

Démo avec plusieurs machines

Imaginons maintenant que nous voulions avoir par exemple les 3 potentiomètres sur une machine à un endroit précis, les leds branchées à une autre machine dans une autre pièce et le code code de contrôle sur une troisième machine dans une salle serveur. Est-ce encore possible ?
Pour nous amuser, nous allons brancher les potentiomètres sur une machine Mac OS X, la led RGB de puissance Yocto-PowerColor sur une machine Linux et le programme de contrôle sur une machine Windows (les rôles sont entièrement interchangeables). On pourrait penser qu'un tel changement nécessiterait d'ouvrir des sockets réseau ou au moins de modifier notre programme, mais non! Il suffit pour cela de
- lancer le VirtualHub sur les deux machines distantes
- passer les addresses IP (ou le nom d'hôte) de ces deux machines en paramètre à notre exécutable (à la place de "usb")

Les potentiomètres sont branchés sur une deuxième machine, et la LED sur un troisième
Les potentiomètres sont branchés sur une deuxième machine, et la LED sur un troisième



Tout comme dans l'exemple précédent, yRegisterHub(argv[i],errmsg); va faire une énumération et initialiser l'API. Au lieux de communiquer directement en USB, l'API va communiquer en TCP avec le VirtualHub, qui communiquera lui avec le module en USB. L'encapsulation du traffic USB sur TCP est totalement transparente pour le programme utilisateur. Il n'y a pas besoin de configuration réseau compliquée, il suffit que la machine qui exécute le programme puisse contacter la machine distante par TCP. Ce mécanisme n'utilise qu'un seul port TCP (4444 par défaut) et toutes le communication sont initiées par la machine qui execute le programme (ce qui permet de fonctionner à travers un routeur NAT). Dès que les potentiomètres bougent, le Yocto-Knob envoie la nouvelle valeur par USB qui sera transférée par TCP à l'API, déclenchant le callback.

  



Vous vous demandez peut-être pourquoi nous avons ajouté la ligne yUpdateDeviceList(errmsg); dans notre boucle principale. Si nous l'enlevons, le programme fonctionnera toujours dans un environnement statique, mais si on débranche le Yocto-Knob et qu'on le rebranche les potentiomètres ne sont plus détectés. En ajoutant cette ligne, qui effectue une détection périodique des modules, nous pouvons débrancher le module en cours de fonctionnement et le rebrancher plus tard sans problème. Nous pouvons même le débrancher d'une machine et le rebrancher sur une autre!

Chaque machine a une LED, et les potentiomètres peuvent être connectés n'importe où
Chaque machine a une LED, et les potentiomètres peuvent être connectés n'importe où


  



L'API de programmation par callback, telle que présentée dans cet article, sera disponible aujourd'hui pour C++, Objective-C et C#. Elle sera disponible pour tous les languages supportés d'ici quelques jours, le temps de mettre à jour la librairie. Comme d'habitude, si vous avez des question, n'hésitez pas nous contacter!



Commenter aucun commentaire Retour au blog












Yoctopuce, get your stuff connected.