L'article de cette semaine est dédié aux débutants qui viennent de recevoir leur premier module Yoctopuce et qui se demandent bien quel genre de code ils vont devoir pondre pour le faire marcher. Nous vous proposons donc de discuter un peu de la structure générale d'une application qui utilise des modules Yoctopuce.
Si êtes un débutant et que vous vous voulez faire au plus rapide, vous pouvez partir des exemples de programmation fournis avec chacune des librairies de programmation. Un peu de programmation par mimétisme vous permettra probablement d'atteindre votre but. Cependant, si vous souhaitez comprendre en détails ce que font ces exemples, prenez quelques minutes pour lire la suite.
Installation de l'API
La première chose à faire consiste à installer la librairie Yoctopuce de votre choix. Vous trouverez dans la section "Tutoriels" les premiers pas à suivre pour chaque langage de programmation. Une fois cette installation terminée, vous pouvez enter dans le vif du sujet.
Initialisation de l'API Yoctopuce
L'API Yoctopuce est l'interface entre votre programme et les modules Yoctopuce. Pour pouvoir l'utiliser, il est indispensable d'initialiser cette API, faute de quoi aucun appel ne fonctionnera. La façon la plus simple consiste à faire un appel à la fonction registerHub, qui prend deux paramètres:
- url: ce paramètre défini le mode de fonctionnement de l'API, il peut soit avoir la valeur "usb" pour signifier que l'on souhaite utiliser les modules connectés par USB à la machine locale. Mais il peut être aussi l'adresse IP d'une machine distante, dans ce cas cette machine peut être soit un ordinateur normal sur lequel tourne un VirtualHub soit un YoctoHub.
- errmsg: ce paramètre contiendra au retour un éventuel message d'erreur. Dans les langages qui ne permettent pas un passage de paramètres par adresse, errmsg est en fait un simple objet contenant une chaîne de caractères.
La fonction RegisterHub() retournera la valeur YAPI.SUCCESS si tout s'est bien passé. Dans le cas contraire, errmsg contiendra une description du problème. L'erreur que vous rencontrez le plus souvent à ce stade est "Another process is already using yAPI" parce qu'il n'est pas possible de faire tourner en même temps deux applications Yoctopuce qui utilisent le mode USB sur la même machine. C'est une des raisons pour laquelle il faut initialiser l'API Yoctopuce le plus tôt possible dans la vie de votre application. Si l'initialisation de l'API échoue, cela ne vaut probablement pas la peine de continuer.
Vous pouvez parfaitement appeler RegisterHub() plusieurs fois avec des paramètres différents et ainsi accéder en même temps à des modules Yoctopuce branchés à des machines différentes. Ceci-dit, appeler plusieurs fois RegisterHub() avec la même url ne sert à rien.
Le début de votre application doit donc contenir un bout de code qui ressemble plus ou moins à ça:
{ Console.WriteLine("RegisterHub error: " + errmsg);
Environment.Exit(0);
}
Notez que RegisterHub() est une fonction bloquante, mais il existe une version non bloquante: PreregisterHub().
Accéder aux modules Yoctopuce
Une fois l'API initialisée vous avez besoin d'accéder à vos modules. Du point de vue de l'API, tous les modules Yoctopuce sont présentés sous la forme d'un ensemble de fonctionnalités indépendante les unes des autres, si cette phrase ne vous parait pas très claire, on vous recommande vivement d'aller lire cet article qui explique l'architecture logique d'un module Yoctopuce. Chaque type de fonctionnalité est codé dans une classe à part que vous aurez à inclure d'une manière ou d'une autre dans votre projet. Pour obtenir une référence sur la fonctionnalité qui vous intéresse, vous avez deux approches:
L'énumération
Vous pouvez appeler la méthode "FirstXxxxx()" de la classe correspondant à la fonctionnalité que vous cherchez et vous obtiendrez la première fonctionnalité correspondante disponible, ou encore la valeur NULL si aucune correspondance n'a été trouvée. S'il y a d'autres fonctionnalités du même type vous pourrez les énumérer en utilisant la méthode nextXxxxx() de l'objet qui vous a été retourné. C'est simple et pratique mais vous n'avez aucune garantie sur l'ordre dans lequel vous obtenez ces références.
YTemperature t = YTemperature.FirstTemperature();
if (t==null) Console.WriteLine("pas trouvé");
YTemperature t = YTemperature.FirstTemperature();
while(t!=null)
{ Console.WriteLine("trouvé "+t.get_friendlyName());
t=t.nextTemperature();
}
L'adressage par nom
S'il y a un risque d'avoir plusieurs fonctionnalités du même type dans votre système, la meilleure méthode pour en trouver une en particulier consiste à appeler la méthode FindXxxx() de la fonctionnalité en question. Cette fonction prend un paramètre qui est le numéro de série du module qui héberge la fonctionnalité suivi qu'un point et du nom de la fonctionnalité. Par exemple si vous chercher la fonction température présente sur un Yocto-Meteo-V2 dont le numéro de série est METEOMK2-12BBB2 vous écrirez:
Le numéro de série de vos modules se trouve sur l'étiquette de l'emballage, vous pouvez aussi utiliser le VirtualHub pour retrouver le numéro de série d'un module.
En lieu et place du numéro de série et du nom de la fonctionnalité, vous pouvez aussi utiliser des noms logiques que vous aurez préalablement assignés, ces noms logiques sont persistants et résistent à une perte d'alimentation. Vous pouvez utiliser le VirtualHub ou l'API pour les assigner.
Une fois que vous avez obtenu une référence sur la fonctionnalité qui vous intéresse, celle-ci reste valide pour toute la durée de l'application, vous n'avez pas besoin de rappeler FindXxxxx().
Mais il est important de comprendre que FindXxxx() renvoie toujours une référence sur un objet Xxxx valide, même si le module correspondant au nom donné en paramètre n'est pas branché ou même si ce nom ne correspond à rien. Pour tester si cet objet Xxxx correspond à une fonctionnalité qui est réellement présente dans le système, il vous faut utiliser la méthode isOnline() de cet objet.
if (!t.isOnline()) Console.WriteLine("pas connecté.");
En revanche, si vous branchez le Yocto-Meteo-V2 plus tard dans la vie de l'application, vous n'avez pas besoin de rappeler FindXxxx(), l'objet YTemperature retourné par le premier appel à FindXxxx() deviendra automatiquement utilisable.
Quelque soit le langage de programmation utilisé, vous ne devez jamais désallouer vous-même les objets renvoyés par l'API.
Interagir avec un module
Une fois que vous avez une référence sur la fonctionnalité qui vous intéresse, il ne vous reste plus qu'à l'utiliser, mais il faut être conscient que les modules Yoctopuce étant des produits USB, il y a un gros risque qu'ils soient débranchés durant la vie de l'application. Vous pouvez vous adapter à ce risque en vous assurant que la fonctionnalité qui vous intéresse est bien présente juste avant de l'utiliser avec isOnline()
{ double value = t.get_currentValue();
Console.WriteLine("valeur = "+v.toString());
}
Mais rien ne vous garanti que personne ne débranchera votre module entre le moment où vous avez fait l'appel à isOnline et get_currentValue. De plus les appels isOnline prennent du temps: comptez environ 20ms. Une alternative consiste à utiliser les exceptions pour détecter les problèmes.
try
{ value = t.get_currentValue();
Console.WriteLine("valeur = "+v.toString());
}
catch (Exception e)
{ Console.WriteLine(e.Message);
}
Si vous n'aimez pas travailler avec les exceptions, vous pouvez les désactiver et vous contenter de tester la valeur retournée
...
double value= t.get_currentValue();
if (value!= YAltitude.CURRENTRAWVALUE_INVALID)
Console.WriteLine("valeur = "+v.toString());
else
Console.WriteLine("Oups!");
Efficacité
Tant que vous n'avez à interagir avec vos modules Yoctopuce qu'occasionnellement, disons moins de 1 fois par seconde, vous pouvez de vous contenter de genre de code un peu simpliste. Mais vous devez savoir que n'importe quel appel qui interagit directement avec un module Yoctopuce peut prendre 20 à 30ms. Si vous devez interagir avec vos modules fréquemment, vous aurez à remplacer cette technique d'interrogation bête et méchante par une programmation par callbacks.
Temporisation
Il y a de bonnes chances pour que le noyau de votre application soit basé sur une boucle. Si vous avez à ajouter une temporisation dans cette boucle, évitez d'utiliser la fonction sleep() du système. Utilisez plutôt la fonction YAPI.Sleep(). Sans que vous ne vous en rendiez compte, il y a énormément de calculs qui se font en arrière plan dans l'API Yoctopuce. Et L'API a besoin de prendre la contrôle du thread principal de temps en temps pour effectuer son travail. En particulier, les différents callbacks générés par l'API Yoctopuce ne sont appelés que pendant YAPI.Sleep(), YAPI.HandleEvents() et YAPI.UpdateDeviceList().
{
try
{ value t.get_currentValue();
Console.WriteLine("valeur = "+v.toString());
}
catch (Exception e)
{ Console.WriteLine(e.Message);
}
// Thread.Sleep(1000); // Mauvaise idée
YAPI.Sleep(1000,ref errmsg); // c'est mieux.
}
Fin de l'application
Lorsque votre application se termine, il est de bon ton d'appeler YAPI.FreeAPI() qui libèrera les ressources mobilisées par l'API. Ce n'est pas formellement obligatoire dans la mesure où le système d'exploitation le fera de toutes manières, mais ne pas le faire peut avoir des conséquences inattendues. Il faut savoir que certains appels de l'API sont asynchrones, c'est pourquoi YAPI.FreeAPI() attendra que toutes ces opérations asynchrones soient terminées avant de rendre la main. Le cas typique est un programme qui effectue simplement toute une série de set_xxxx() pour configurer un module, si le programme se termine sans un appel à YAPI.FreeAPI(), il y a des chances pour que les derniers set_xxxx() n'aient pas le temps de se terminer.
Conclusion
En guise de conclusion, on vous propose un petit programme complet qui interroge un capteur Yoctopuce une fois par seconde pendant une minute. Il tire partie du fait que toutes classes qui correspondent à un capteur Yoctopuce héritent de la classe YSensor. Voici la version C#
namespace example
{ class Program
{
static void Main(string[] args)
{
string errmsg = "";
if (YAPI.RegisterHub("usb",ref errmsg) !=YAPI.SUCCESS)
{ Console.WriteLine(errmsg);
Environment.Exit(0);
}
String name = "METEOMK2-12BBB2.temperature";
YSensor sensor;
sensor = YSensor.FirstSensor();
if (sensor==null)
{ Console.WriteLine("pas de senseur disponible");
Environment.Exit(0);
}
/* variante
sensor = YSensor.FindSensor(name);
if (!sensor.isOnline())
{ Console.WriteLine("Senseur introuvable");
Environment.Exit(0);
}*/
for (int i=0;i<60;i++)
{ if (sensor.isOnline())
Console.WriteLine(sensor.get_currentValue().ToString()+ sensor.get_unit());
else
Console.WriteLine("pas connecté");
YAPI.Sleep(1000, ref errmsg);
}
YAPI.FreeAPI();
}
}
}
Et pour faire bonne figure, exactement le même code, mais en Python cette fois
# -*- coding: utf-8 -*-
import os, sys
from yocto_api import *
errmsg = YRefParam()
if YAPI.RegisterHub("usb", errmsg) != YAPI.SUCCESS:
sys.exit(errmsg.value)
name = "METEOMK2-12BBB2.temperature"
sensor = YSensor.FirstSensor()
if sensor is None:
sys.exit("pas de senseur disponible")
# variante
#sensor = YSensor.FindSensor(name)
#if (not sensor.isOnline()):
# sys.exit("Senseur introuvable")
for i in range(0,60):
if (sensor.isOnline()):
print(str(sensor.get_currentValue())+ sensor.get_unit())
else:
print("pas connecté")
YAPI.Sleep(1000, errmsg)
YAPI.FreeAPI()