Comment débuter en Node.js avec des modules Yoctopuce

Comment débuter en Node.js avec des modules Yoctopuce

Nous vous proposons cette semaine de découvrir comment faire vos premiers pas avec les modules Yoctopuce en JavaScript, et en particulier dans le contexte d'un serveur Web écrit avec node.js. L'intérêt de ce scénario est qu'il permet de faire des applications serveur qui collectent des données de plein de capteurs Yoctopuce dispersés dans la nature, qui transmettent chacun indépendamment leurs données par Internet via le WiFi, le réseau GSM, etc.


Avant de commencer, précisons qu'on aurait aussi pu vous présenter une exemple en JavaScript très similaire à l'exemple Python présenté il y a quelques semaines: la librairie JavaScript/EcmaScript fonctionne aussi bien dans un browser Web que dans simple un programme Node.js ou sur un hébergement serveur Node.js. Mais cette librairie a la particularité d'offrir un maximum de possibilités pour interroger à distance des modules Yoctopuce depuis un serveur Web. C'est donc ce que nous allons vous montrer.

On supposera que vous avez un minimum de connaissances de JavaScript et de Node.js, et que vous avez installé une version vraiment récente de Node.js sur votre machine de développement. En effet, la librairie Yoctopuce est basée sur des communications réseau, par nature asynchrones en JavaScript. Nous avons donc pris le parti d'utiliser la nouvelle syntaxe asynchrone standardisée dans la dernière version du langage, appelée EcmaScript 2017. Toutes les versions de Node.js à partir de la v7.6 reconnaissent cette syntaxe. Au besoin, faites la mise à jour de votre Node.js, vous ne risquez rien: la compatibilité arrière est très bonne.

Installation de la librairie Yoctopuce


Cette librairie Yoctopuce est disponible sur npm sous le nom yoctolib-es2017. On vous recommande de ne plus utiliser les anciennes versions yoctolib et yoctolib-es, nous ne les avons préservées que pour ne pas casser les dépendances de projets existants, mais elles ne présentent plus aucun intérêt depuis l'apparition de la version es2017.

Si vous commencez un nouveau projet à zéro, commencez comme d'habitude par créer le fichier package.json qui servira à lister les dépendances du projet, à l'aide de la commande

npm init


Vous pouvez alors ajouter la librairie Yoctopuce à votre projet avec la commande:

npm install yoctolib-es2017 --save



Squelette du programme


Cet exemple très simple tient dans un seul fichier source. On va créer un serveur Web capable de recevoir d'une part les connexion des YoctoHubs qui transmettent les données des capteurs Yoctopuce, et d'autre part les connexions Web de l'utilisateur qui vient récupérer ces données.

La structure sera donc la suivante:

require('yoctolib-es2017/yocto_api.js');

YAPI.LogUnhandledPromiseRejections();

async function HttpCallbackHandler(request, response) {
    // Les requêtes HTTP des browsers Web arrivent ici
    response.writeHead(200, {'Content-Type': 'text/html'});
    response.write('Hello Browser<br>\n');
    response.end();
}

async function WebSocketCallbackHandler(ws)
{
    // Les connexions WebSocket des YoctoHubs arrivent ici
    ...
}

// On crée un serveur HTTP...
var http = require('http');
http.createServer(HttpCallbackHandler).listen(8080);

// ... et on crée un serveur WebSocket
var WebSocketServer = require('ws').Server;
let wss = new WebSocketServer({ port: 8044 });
wss.on('connection', WebSocketCallbackHandler);

console.log('Node.js Web HTTP server running on http://...:8080/');
console.log('Node.js Websocket server running on http://...:8044/');



La première ligne require(...) sert à charger les fonctions de base de la librairie Yoctopuce. Remarquez qu'il n'est pas nécessaire d'énumérer explicitement les symboles à importer, nous avons pris l'option d'exporter automatiquement les classes essentielles de notre librairie: YAPI, YModule, YSensor, etc.

La deuxième ligne LogUnhandledPromiseRejections sert à faciliter le debugging: elle s'assure que les erreurs sur les opérations asynchrones qui ne sont pas explicitement attrapées par un try..catch soient affichées sur la console.

Suivent deux fonctions. La première gère les connexions par HTTP faites par un browser usuel. La seconde gère les connexions WebSocket faites par les YoctoHubs pour communiquer avec le serveur Web. L'intérêt d'une connexion WebSocket par rapport à une simple connexion HTTP est qu'elle est bidirectionnelle, et permet donc une interrogation complète des capteurs, alors qu'une connexion HTTP ne permet pas au serveur de poser des questions au client HTTP.

Les dernières lignes du programme lancent les serveurs HTTP et WebSocket, et les lient aux fonctions définies précédemment.

Le gestionnaire de callbacks WebSocket


La partie la plus intéressante est celle qui gère la connexion effectuée par les YoctoHubs vers le serveur WebSocket. Cette fonction est appelée une fois pour chaque connexion entrante, ce qui signifie que plusieurs exécutions peuvent se dérouler simultanément si plusieurs YoctoHubs se connectent en même temps. Les exécutions partagent toutes le même espace mémoire global, mais bien entendu les variables locales à la fonction sont propres à chaque exécution.

La première chose à faire pour utiliser la librairie Yoctopuce est de l'initialiser, en indiquant où les modules doivent être cherchés. Dans notre cas, c'est bien entendu sur l'appelant WebSocket que se trouvent les modules. Pour éviter que notre callback se mélange les pinceaux entre les différentes connexions, à la place d'utiliser la fonction globale YAPI.RegisterHub, on instancie un contexte YAPI spécifique à chaque connexion:

    console.log('Incoming WebSocket connection!');
    let errmsg = new YErrorMsg();
    let yctx = new YAPIContext();
    if(await yctx.RegisterHubWebSocketCallback(ws, errmsg) != YAPI.SUCCESS) {
        console.log('WebSocket callback error: '+errmsg);
        yctx.FreeAPI();
        return;
    }



Ensuite, on peut par exemple énumérer les capteurs connectés au YoctoHub, et les configurer pour qu'ils envoient des mesures toutes les 10 secondes;

    await yctx.UpdateDeviceList(errmsg);
    let sensor = YSensor.FirstSensorInContext(yctx);
    while(sensor) {
        console.log('Sensor: ' + (await sensor.get_hardwareId()));
        await sensor.set_reportFrequency("6/m");
        await sensor.registerTimedReportCallback(sensorCallback);
        sensor = sensor.nextSensor();
    }



Il ne reste alors plus qu'à attendre les données, en détectant une éventuelle déconnexion:

    while(ws.readyState != ws.CLOSED) {
        await yctx.Sleep(1000);
    }
    await yctx.FreeAPI();



La fonction sensorCallback est automatiquement appelée dès que les capteurs envoient leurs données périodiques. Pour cet exemple, nous nous contenterons de stocker les valeurs dans un tableau, mais c'est là que vous pourriez mettre du code pour stocker les mesures dans une base de données par exemple:

var Data = [];

async function sensorCallback(sensor, measure)
{
    Data.push({
        "name": await sensor.get_hardwareId(),
        "time" : await measure.get_endTimeUTC(),
        "value" : await measure.get_averageValue()
    });
}



Le gestionnaire de callbacks HTTP pour le browser


Plutôt que d'afficher Hello Browser comme dans le squelette de code ci-dessus, vous pouvez maintenant afficher les mesures en mémoire, qui regrouperont les transmissions de tous les hubs puisque les mesures ont été stockées dans un tableau global:

    response.write('time;sensor;value<br>\n');
    for(let i = 0; i < Data.length; i++) {
        response.write(Data[i].time + ";" +
                       Data[i].name + ";" +
                       Data[i].value + "<br>\n");
    }



Lancement du serveur node.js en local


Une fois ces fragments de code mis ensemble dans un fichier demo.js (vous en trouverez une copie dans les exemples de la librairie sous le nom Prog-WebSocketCallback), vous pouvez lancer votre serveur avec la commande:

node demo.js


Vous devriez alors pouvoir diriger votre navigateur sur l'adresse http://127.0.0.1:8080/ et obtenir une liste de mesures vides (juste l'entête): c'est notre gestionnaire HTTP qui vous a répondu.

Configurez maintenant un YoctoHub-Ethernet ou un YoctoHub-Wireless-g pour qu'il aille poster des données sur ce serveur. Dans la fenêtre de configuration, cliquez à droite de Callback URL sur le bouton edit:


Puis reproduisez la configuration ci-dessous. Prenez bien garde à changer le type d'URL de http:// à ws:// pour que la connexion se fasse par WebSocket:


Si vous cliquez sur le bouton Test, vous devriez avoir une ligne de message qui apparaît sur la console de votre process node.js, et les valeurs des capteurs qui s'enregistrent chaque 10 secondes et qui peuvent alors être listées dans le browser.

Lancement du serveur sur un hébergement public


Une fois que votre serveur fonctionne en local, vous pouvez l'installer sur un serveur public visible sur Internet, ce qui vous permettre de le contacter par exemple depuis un YoctoHub-GSM-3G-EU. Pour cela, vous devrez choisir un hébergeur Internet qui supporte la technologie Node.js. On trouve deux types d'hébergeurs Node.js:

Certains hébergeurs comme Heroku vous fournissent un environnement d'exécution Node.js fermé, avec des outils pour effectuer la configuration, charger vos fichier sources et récupérer les logs à distance.

D'autres hébergeurs comme Infomaniak vous fournissent un serveur Cloud virtuel avec une connexion ssh. Vous vous connectez directement sur la machine pour activer la version désirée de Node.js, à l'aide de NVM par exemple, et vous lancez votre programme avec la même ligne de commande qu'en local. Le module npm forever est dans ce cas très utile pour permettre une exécution continue du serveur, indépendamment de la durée de vie de la session ssh. Sur ce genre d'hébergement, il peut être nécessaire d'autoriser, via une console de management, le port TCP entrant correspondant à votre application pour qu'elle soit joignable depuis Internet.

Conclusion


Vous savez maintenant comment faire un serveur Web en Node.js pour collecter les données de capteurs Yoctopuce. L'utilisation des WebSocket offre des possibilités presque illimitées d'interaction avec les modules, et permet par exemple aussi la récupération de mesures stockées sur le data logger interne à chaque capteur Yoctopuce, de sorte à garantir un enregistrement des mesures sans interruption.

Vous pouvez bien entendu aussi enrichir le serveur HTTP pour fournir un contenu différent selon la page demandée. Vous trouverez des exemples pour cela dans la documentation de Node.js.

Il existe aussi des modules npm facilitant la publication de contenu statique et dynamique sur un serveur HTTP en Node.js, et qui permettent d'utiliser le même port TCP pour les requêtes HTTP et les requêtes WebSocket. L'un des plus connu est ExpressJS, et vous trouverez dans la documentation du package ws un exemple d'intégration d'un serveur WebSocket au sein d'un serveur ExpressJS.

Commenter aucun commentaire Retour au blog












Yoctopuce, get your stuff connected.