Comment débuter en TypeScript avec les modules Yoctopuce

Comment débuter en TypeScript avec les modules Yoctopuce

Depuis cette semaine, vous pouvez utiliser les modules Yoctopuce directement depuis un programme écrit en TypeScript. Nous venons en effet de publier une nouvelle librairie de programmation pour ce langage de plus en plus populaire. C'est donc l'occasion de proposer un tutoriel pour les nouveaux utilisateurs de modules Yoctopuce avec un petit exemple simple mais réaliste. Comme d'habitude, on supposera que vous avez des notions de programmation en Typescript, mais on vous expliquera le reste en détail.

Comme application d'exemple, on vous propose de contrôler un Yocto-PowerRelay-V3 en fonction de la température donnée par un Yocto-Temperature. On imagine que vous avez lu avec attention les précédents articles de notre série pour les débutants en particulier celui sur la structure logique des modules Yoctopuce.

On veut changer l'état du Yocto-PowerRelay-V3 en fonction de la température mesurée par le Yocto-Temperature
On veut changer l'état du Yocto-PowerRelay-V3 en fonction de la température mesurée par le Yocto-Temperature



Installation

Pour cet exemple, nous allons écrire un programme destiné à s'exécuter avec Node.js en ligne de commande. Si vous vous intéressez plutôt à une utilisation dans un navigateur Web, vous trouverez des explications complémentaires dans la documentation et des exemples dans le fichier zip de notre librairie... en attendant un autre tutorial dédié spécifiquement à ce sujet.

La première chose à faire consiste donc à installer sur votre machine Node.js et TypeScript si ce n'est pas déjà fait. Vous pourrez charger la dernière version de Node.js gratuitement sur http://nodejs.org, et l'installer. Une fois fait, pour installer TypeScript globalement sur votre machine, il suffit de lancer la commande:

npm install -g typescript



Comme JavaScript - et donc TypeScript - ne dispose pas d'accès direct au port USB, l'accès aux modules USB de Yoctopuce doit se faire à travers l'application VirtualHub, que vous devrez donc aussi télécharger depuis notre site et lancer avant de commencer.

Nous allons maintenant construire à partir de zéro notre projet de test en TypeScript pour Node.js. Ouvrez un shell (une fenêtre de commande) dans un répertoire vide, par exemple appelé tutorial. Pour commencer, nous allons créer le fichier de définition du projet pour NPM, avec la commande:

npm init -y


Ensuite, nous allons indiquer à NPM que notre projet a besoin de la librairie Yoctopuce, sous forme de module CommonJS:

npm install yoctolib-cjs


Finalement, nous allons faire créer un fichier de configuration pour le compilateur TypeScript, en indiquant que nous voulons que le code généré utilise le standard ECMAScript 2017.

tsc --init --target es2017


Il ne reste plus qu'à écrire le code TypeScript. Pour cela, vous pouvez bien sûr utiliser n'importe quel éditeur de texte, mais si vous utilisez un éditeur qui supporte spécifiquement TypeScript comme WebStorm ou Visual Studio Code, vous bénéficierez en prime d'une assistance durant l'écriture de code, incluant la documentation instantanée des paramètres des méthodes de la librairie Yoctopuce.

Utilisation de la librairie Yoctopuce pour TypeScript

Créez un nouveau fichier appelé index.ts et inserez-y les lignes suivantes:

import { YAPI, YErrorMsg } from 'yoctolib-cjs/yocto_api_nodejs.js'

async function runDemo(): Promise<void>
{
    let errmsg: YErrorMsg = new YErrorMsg;
    await YAPI.RegisterHub("127.0.0.1", errmsg);

    console.log('Connecté au VirtualHub !');

    await YAPI.FreeAPI();
}

runDemo()


Ces quelques lignes demandent un certain nombre d'explications, mais rassurez-vous, une fois que vous aurez compris ce qui se cache derrière, tout le reste deviendra presque trivial :-)

1. L'import de yocto_api

import { YAPI, YErrorMsg } from 'yoctolib-cjs/yocto_api_nodejs.js'


Les fonctions de base de notre librairie sont traditionnellement inclues en important le fichier yocto_api. Or la librairie TypeScript peut être aussi bien utilisée dans un navigateur HTML que dans un environnement Node.js, mais forcément on ne passe pas par les même librairies réseau dans les deux cas. Nous avons donc dû nous résoudre à proposer deux points d'import séparés: yocto_api_nodejs et yocto_api_html. Choisissez donc celui que vous incluez en fonction de la librairie réseau que vous désirez utiliser. Si une partie de votre code doit fonctionner dans les deux modes, il peut n'inclure que yocto_api. Mais au moins un des fichiers de votre projet doit inclure l'un des deux points d'entrée liés à une couche réseau.

Notez que le nom de la librairie que nous importons est yoctolib-cjs, où cjs est l'abréviation de CommonJS, le standard de modules utilisé habituellement avec Node.js. Il n'y a pas de mention explicite de TypeScript dans le nom de la librairie, car par définition un module TypeScript ne peut être importé que sous forme compilée, donc en pur JavaScript. Notre nouvelle librairie peut donc aussi être utilisée par du code écrit en pur JavaScript. Mais en plus, à l'intérieur se trouvent aussi les fichiers de définition de type .d.ts permettant son utilisation en bénéficiant de tous les avantages du typage fort de TypeScript.

Notez que si vous préférez intégrer la librairie en code source TypeScript directement dans votre projet, plutôt que d'importer un module compilé, vous trouverez tous les fichiers source dans un fichier zip sur notre page listant toutes les librairies de Yoctopuce.

2. Fonction async et objets Promise

async function runDemo(): Promise<void>


Vous aurez remarqué le mot-clé async précédant la déclaration de notre fonction runDemo. Si vous n'êtes pas encore familier avec l'utilisation des fonctions asynchrones avec async / await, vous ne couperez pas à vous y mettre pour utiliser la librairie Yoctopuce... Ce n'est pas directement une spécificité de Yoctopuce puisque c'est une syntaxe introduite dans le standard ECMAScript en 2017, mais notre librairie l'utilise presque partout. Elle permet d'éviter de devoir passer en paramètre des fonctions de callback pour chaque appel d'entrée/sortie potentiellement bloquant, comme c'était fait par le passé en JavaScript, mais ce qui tendaient à fragmenter terriblement le code.

A la place de cela, en déclarant une fonction async, on indique au compilateur que le résultat de la fonction sera différé par l'utilisation d'un callback implicite. A l'intérieur de la fonction, on ajoute le mot clé await avant l'appel de toutes les fonctions asynchrones, ce qui crée implicitement une fonction de callback avec la suite du code, qui sera appelée automatiquement après l'exécution de la commande asynchrone. Au final, le code est presque aussi simple que si on était dans un environnement multi-threads préemptifs, sans devoir gérer de mutex. Sauf que...

Sauf qu'il faut se méfier que l'utilisation d'une fonction asynchrone est contagieuse: puisque l'appel de la fonction est différé, l'appelant doit lui-aussi être asynchrone, ou tout du moins utiliser la méthode .then() avec un callback pour récupérer le résultat de l'objet Promise.

3. Initialisation de l'API

let errmsg: YErrorMsg = new YErrorMsg;
await YAPI.RegisterHub("127.0.0.1", errmsg);


Pour utiliser la librairie Yoctopuce, il faut l'initialiser en lui indiquant sur quelles machines se trouvent les modules. C'est le rôle de l'appel à YAPI.RegisterHub. L'adresse 127.0.0.1 correspond à la machine locale, sur laquelle vous avez précédemment installé le VirtualHub pour accéder aux modules branchés par USB.

Comme mentionné précédemment, on précède l'appel du mot clé await pour que l'exécution du code ne continue qu'une fois les opérations asynchrones terminées.

Si le VirtualHub n'a pas été lancé sur la machine, l'appel à RegisterHub va insister un moment, puis lancer une exception. Vous pouvez dans ce cas retrouver des détails sur l'erreur dans l'objet errmsg. Si vous désirez éviter les exceptions, vous pouvez aussi désactiver l'utilisation des exceptions dans la librairie, et récupérer le résultat sous forme de valeur de retour. Vous trouverez plus d'informations à ce sujet dans la documentation.

4. Libération de l'API

await YAPI.FreeAPI();


L'appel à RegisterHub a ouvert un canal de communication WebSocket pour permettre d'interroger les modules Yoctopuce. Si vous désirez que votre programme se termine, il faut fermer ce canal et terminer toutes les opérations asynchrones qui lui sont liées. C'est à cela que sert l'appel à la méthode asynchrone YAPI.FreeAPI().

Un exemple un peu plus utile

Maintenant que le décor est planté, nous allons écrire un petit programme qui bascule le relais quand la température passe au dessus d'une certaine température, disons 25°C. Reprenons par étapes.

1. Les imports

La première chose à faire consiste à inclure les bons imports de la librairie. En plus de l'indispensable "yocto_api_nodejs.js", comme on compte utiliser un capteur de température et un relais, on aura aussi besoin de "yocto_temperature.js" et "yocto_relay.js". Nous commencerons donc par:

import { YAPI, YErrorMsg } from 'yoctolib-cjs/yocto_api_nodejs.js'
import { YTemperature } from 'yoctolib-cjs/yocto_temperature.js'
import { YRelay } from 'yoctolib-cjs/yocto_relay.js'



2. Récupération des fonctionnalités

Comme vous avez lu l'article sur la structure logique des modules Yoctopuce vous savez que le Yocto-Temperature et le Yocto-PowerRelay-V3 hébergent différentes fonctionnalités parmi lesquelles on trouve temperature et relay. Ce sont ces deux fonctionnalités qui nous intéressent. Pour les récupérer il y a plusieurs manières.

FirstXXX

Si vous êtes sûr de n'avoir qu'une seule fonctionnalité temperature et une seule fonctionnalité relay disponible, vous pouvez faire au plus simple et utiliser la méthode FirstXXX des classes YTemperature et YRelay.

let temp: YTemperature | null = YTemperature.FirstTemperature();
if(!temp) {
    console.error("No temperature sensor found");
    return;
}
let relay: YRelay | null = YRelay.FirstRelay();
if(!relay) {
    console.error("No relay found");
    return;
}


Ces méthodes renvoient la première occurrence de la fonctionnalité cherchée, ou null si rien ne correspond.

FindXXX et numéro de série

Vous pouvez aussi utiliser les méthodes FindXXX des classes YTemperature et YRelay avec le numéro de série des modules. Supposons que le numéro de série du Yocto-Temperature soit TMPSENS1-3EA8F et que le numéro de série du Yocto-PowerRelay-V3 soit RELAYHI3-56FC0, vous pouvez utiliser:

let temp: YTemperature = YTemperature.FindTemperature("TMPSENS1-3EA8F.temperature")
if(!await temp.isOnline()) {
    console.error("Temperature sensor not connected");
}
let relay: YRelay = YRelay.FindRelay("RELAYHI3-56FC0.relay1")
if(!await relay.isOnline()) {
    console.error("Relay not connected");
}


A la différence de FirstXXX, FindXXX renvoie toujours un objet valide, c'est pourquoi il faut tester si il correspond bien à un module connecté avec la méthode isOnline(). Cela permet d'instancier des objets qui correspondent à des modules qui pourront être connectés plus tard dans la vie du programme.

FindXXX et noms logique

A la place du numéro de série, vous pouvez utiliser un nom logique que vous aurez par exemple affecté à l'aide du VirtualHub.

Affection d'un nom logique au relais du Yocto-PowerRelay
Affection d'un nom logique au relais du Yocto-PowerRelay


Mettons que vous ayez appelé le relay "myRelay" et capteur de température "myTemp". Il vous suffira alors d'écrire:

let temp: YTemperature = YTemperature.FindTemperature("myTemp");
if(!await temp.isOnline()) {
    console.error("Temperature sensor 'myTemp' not found");
}
let relay: YRelay = YRelay.FindRelay("myRelay");
if(!await relay.isOnline()) {
    console.error("Relay 'myRelay' not found");
}


L'intérêt des noms logiques est double: d'une part ils permettent de s'y retrouver plus facilement dans les systèmes complexes composés de nombreux modules Yoctopuce et d'autre part ils permettent de fabriquer des systèmes en plusieurs exemplaires sans avoir à adapter le code aux numéro de séries des modules de chaque exemplaire.

3. La boucle principale

La boucle principale de notre programme consiste à lire la température du capteur et à ajuster la position du relais en fonction de cette température:

while(await temp.isOnline() && await relay.isOnline()) {
    let value: number = await temp.get_currentValue();
    if(value > 25) {
        await relay.set_state(YRelay.STATE_B);
    } else if(value < 24) {
        await relay.set_state(YRelay.STATE_A);
    }
}


Vous remarquerez qu'on passe le relais à l'état B si la température passe au dessus de 25 alors qu'on ne revient a l'état A que si la température passe en dessous de 24. Cela s'appelle un schmitt trigger et évite que le relais ne se mette à osciller quand la température se trouve juste à la limite de déclenchement.

On pourrait s'arrêter là, mais il n'est pas forcément nécessaire de faire tourner cette boucle à la vitesse maximale. De toutes manières la fréquence de rafraîchissement du Yocto-temperaturey est 10Hz. On peut donc insérer une petite pause de 100ms dans la boucle sans que cela n'affecte la réactivité du système.

await YAPI.Sleep(100,errmsg)



Conclusion

Cet article est un peu long, on en convient. Mais si vous mettez bout à bout tous les morceaux de code qu'on a décrit, vous vous rendrez compte que l'utilisation de modules Yoctopuce dans un programme TypeScript ne nécessite en fin de compte quelques lignes de code:

import { YAPI, YErrorMsg } from 'yoctolib-cjs/yocto_api_nodejs.js'
import { YTemperature } from 'yoctolib-cjs/yocto_temperature.js'
import { YRelay } from 'yoctolib-cjs/yocto_relay.js'

async function runDemo(): Promise<void>
{
    let errmsg: YErrorMsg = new YErrorMsg;
    await YAPI.RegisterHub("127.0.0.1", errmsg);

    let temp: YTemperature = YTemperature.FindTemperature("myTemp");
    if(!await temp.isOnline()) {
        console.error("Temperature sensor 'myTemp' not found");
    }
    let relay: YRelay = YRelay.FindRelay("myRelay");
    if(!await relay.isOnline()) {
        console.error("Relay 'myRelay' not found");
    }

    while(await temp.isOnline() && await relay.isOnline())
    {
        let value: number = await temp.get_currentValue();
        if(value > 25) {
            await relay.set_state(YRelay.STATE_B);
        } else if(value < 24) {
            await relay.set_state(YRelay.STATE_A);
        }
        await YAPI.Sleep(100,errmsg)
    }

    await YAPI.FreeAPI();
}

runDemo()



Pour finir, sachez que vous trouverez une documentation interactive de l'API Yoctopuce pour TypeScript dans le répertoire Documentation de la libraire et sur notre site web.

Commenter aucun commentaire Retour au blog












Yoctopuce, get your stuff connected.