Construire une application IoT avec IBM Cloudet Node.js pour Raspberry Pi

Tout d’abord, il vous faudra :

C’est parti.

Pour en savoir plus : IBM Cloud : www.ibm.com/fr-fr/marketplace/cloud-platform

1 Le référentiel Github

Si vous êtes déjà un bon connaisseur de IBM Cloud et que vous souhaitez simplement tester l’application IoT, récupérez le code sur le répertoire Github : 

git clone https://github.com/nicolefinnie/iot-nodejs-tutorial

Dans ce code source, nous avons plusieurs capteurs implémentés (accéléromètre, capteur de mouvement, température / humidité, etc.) sur 4 cartes Raspberry Pi, le tout connecté à la plateforme IoT :

git clone https://github.com/nicolefinnie/iot-disaster

Commençons le tutoriel !

2 Faire fonctionner les capteurs de sa Pi

Un des aspects les plus intéressants de l’IoT concerne le câblage et le montage de la Raspberry Pi. Un capteur PIR est un excellent choix pour comprendre comment fonctionne un capteur. C’est pourquoi nous allons l’utiliser. Ce type de capteur peut détecter un mouvement jusqu’à 7 mètres. Les broches de droite à gauche correspondent à :

  • alimentation
  • High et Low (c’est à dire actif ou inactif)
  • GND (Ground)

C’est un schéma classique. Vous pouvez vérifier que le votre est similaire :

https://www.mpja.com/download/31227sc.pdf

Dans notre montage, le fil bleu est branché sur la broche 5V, le jaune sur le GND et le vert sur le GPIO 4. Le schéma suivant résume le branchement :

https://www.raspberrypi.org/documentation/usage/gpio/

Installez maintenant la librairie Wiring Pi qui va vous faciliter la gestion et le contrôle des GPIO :

sudo apt-get update
sudo apt-get upgrade

git clone git://git.drogon.net/wiringPi
cd wiringPi
./build

Puis, installez le package python et la librairie IBM IoT sur votre Raspberry Pi :

sudo apt-get install python-pip
sudo pip install ibmiotf

Avant de connecter votre montage à la plateforme IoT, vérifiez que le matériel fonctionne bien. Vous pouvez tester le bon comportement du PIR avec des simples scripts :

motionSensor.py

#!/usr/bin/python
# Define setup() that initializes GPIO, and sample() that samples data
import RPi.GPIO as GPIO

gpioPort = 0

def setup(inputPort):
   global gpioPort
   gpioPort = inputPort
   GPIO.setmode(GPIO.BCM)
   GPIO.setup(gpioPort, GPIO.IN, GPIO.PUD_DOWN)

def sample():
   global gpioPort
   data = {}
   currentState = GPIO.input(gpioPort)
   data['motionDetected'] = currentState
   return data

testMotionSensor.py

#!/usr/bin/python
# It samples the data from the motion sensor every half second

import motionSensor
import json
import time

gpioPort = 4
motionSensor.setup(gpioPort)

while True:
 time.sleep(0.5)
 try:
     motionData = motionSensor.sample()
     jsonData = json.dumps(motionData)
     print "Current motion sensor data:", jsonData
 except Exception as e:
     print "Uh oh, exception!", e

Exécutez le script de test en suivant les commandes ci-dessous. Le script retournera l’état de détection toutes les 0,5 secondes :

pi@hhbear:~/iot-tutorial $ sudo python testMotionSensor.py 

Current motion sensor data: {"motionDetected": 0}
Current motion sensor data: {"motionDetected": 0}
Current motion sensor data: {"motionDetected": 1}
Current motion sensor data: {"motionDetected": 1}

3 référencer la Raspberry Pi sur la plateforme IoT

Faire communiquer une Pi et la plateforme IoT n’est pas très compliqué. Il existe de nombreux exemples sur le site developperWorks. Nous allons utiliser un de ces tutoriels pour référencer rapidement notre carte sur la plateforme IoT :

https://developer.ibm.com/recipes/tutorials/raspberry-pi-4/

Une fois toutes les étapes du tutoriel effectuées, testons le résultat. Au préalable, téléchargez la librairie IoT iot_1.0-1_armhf.deb :

curl -LO https://github.com/ibm-messaging/iot-raspberrypi/releases/download/1.0.2/iot_1.0-1_armhf.deb

Un « device id » (un identifiant uniquement) est généré à partir de l’adresse MAC grâce à la librairie IoT qui s’exécute directement sur votre Raspberry Pi :

b827eb80403e (cet ID est un exemple)

Un auth-token (jeton d’authentification) est aussi généré lorsque vous avez ajouté votre Raspberry à la plateforme IoT : 

auth-token= abcdefg_12345 (auth-token généré)  

Voilà maintenant, un service IoT a été créé. Nous voyons dans le tableau de bord que le périphérique référé est une raspberry pi. Dans notre exemple, nous en avons 4.

Vous avez maintenant votre montage enregistré sur IBM Cloud. Et vous voyez aussi l’ID (identifiant) de la Raspberry :

Occupons-nous du fichier device.cfg qui a été créé sur votre Raspberry Pi. 

Nous utilisons pour illustrer notre device.cfg. Il faudra le remplir avec vos informations de configuration. Ce fichier permet à la plateforme IoT de se connecter et de communication, grâce au auth-token.

org = 273pdc                         # société / organisation de notre service IoT
type = raspberrypi                   # le type de matériel tel que défini lors de l’ajout de celui-ci
id = b827eb80403e                    # identifiant de notre matériel
auth-method = token
auth-token = abcdefg_12345           # auth-token généré par la plateforme IoT quand le matériel a été ajouté

4 Générer une clé API depuis la plateforme IoT

Nous avons vu à l’étape 3 comment le service IoT a détecté votre Raspberry. Cependant, votre carte doit communiquer avec le service IoT. Pour ce faire, il faut générer un clé API depuis la plateforme IoT. Elle fournira un clé d’authentification et le jeton d’authentification. Les deux éléments sont importants car ils vont permettre de savoir que c’est bien votre Raspberry et pas une autre. 

Depuis le tableau de bord IoT, cliquez sur le bouton Generate API key :

C’est tout ! En quelques secondes, IBM Cloud a généré la clé et le jeton. Vous pouvez rajouter un nom à votre clé dans le champ commentaire. N’oubliez pas ces informations : copiez-les ou imprimez-les. Maintenant, il nous faut partager la clé et le jeton avec notre Raspberry.

5 créez une application Node.js avec IBM Cloud 

Maintenant il est temps de créer l’application Node.js sur IBM Cloud. Une fois créée, vous devez la lier à notre service IoT que nous avons créé au début de notre tutoriel. 

Et voilà, l’application Node fonctionne.

Nous sautons quelques étapes pour aller directement à l’ajout d’un répertoire Git pour éditer notre code. Si vous avez le moindre problème, n’hésitez pas à regarder ce tutoriel : 

https://hub.jazz.net/tutorials/jazzeditor/

Nous vous recommandons d’installer le plug-in Eclipse pour IBM Cloud et d’importer l’application comme un projet Git dans votre Eclipse. Pour plus de détails sur la configuration d’Eclipse : 

https://hub.jazz.net/docs/gitclient/#eclipse_using_egit

6 Construire une application web Node.js en utilisant la librairie ibmiotf

La librairie ibmiotf est une librairie node.js implémentée par IBM pour envoyer des commandes MQTT. Elle facilite la souscription de multiples matériels et pour publier les événements à ces matériels. Notre application utilise cette librairie. 

Pour plus de détails, regardez la documentation : http://iotf.readthedocs.io/en/latest/applications/libraries/nodejs.html

app.js est notre script de démarrage. Dans le fichier package.json, nous avons l’importation de librairie ibmiotf. IBM Cloud l’a installé pour moi durant le déploiement.

{
 "name": "NodejsStarterApp",
 "version": "0.0.1",
 "private": true,
 "scripts": {
 "start": "node app.js"
 },
 "dependencies": {
 "express": "4.13.x",
 "cfenv": "1.0.x",
 "ibmiotf":"~0.2.11"
 },
 "repository": {},
 "engines": {
 "node": "4.2.x"
 }
}

Connecter un IoT est une opération très simple. Premièrement, nous avons besoin de récupérer les informations d’identifications de notre service IoT (iotCredentialsIdentifier, org, apiKey, apiToken). Ces données sont stockées dans IBM Cloud et sont très similaires aux variables d’environnement (Environment Variables VCAP_SERVICES).

La configuration dans IBM Cloud est un peu différente (voir ci-dessous). Nous avons ajouté deux manières différentes pour parser les informations d’identifications dans notre app.js. L’une des méthodes analyse le VCAP-SERVICES pour tester l’application et le service IoT. La seconde méthode est l’analyse de la configuration interne pour exécuter l’application sur IBM Cloud. Nous allons prendre la seconde méthode. 

Configuration interne de IBM Cloud (la structure JSON diffère du VCAP-SERVICES de la capture d’écran précédente) :

{
    "iot-raspberrypi":
 {
    "name": "iot-raspberrypi",
    "label": "iotf-service",
    "plan": "iotf-service-free",
    "credentials": {
      "iotCredentialsIdentifier": "xxxxxxx",
      "mqtt_host": "273pdc.messaging.internetofthings.ibmcloud.com",
      "mqtt_u_port": 1883,
      "mqtt_s_port": 8883,
      "base_uri": "https://273pdc.internetofthings.ibmcloud.com:443/api/v0001",
      "http_host": "273pdc.internetofthings.ibmcloud.com",
      "org": "273pdc",
      "apiKey": "xxxxxxxxxxxxxxxxx",
      "apiToken": "xxxxxxxxxxxxxxxx"
    }
 }
}

app.js : récupération des informations d’identification

//@attention iot-raspberrypi est le nom du service IOT dans notre exemple. A remplacer si nécessaire 

var iotPlatformServiceName = 'iot-raspberrypi';

// IBM IoT service
var Client = require('ibmiotf').IotfApplication;

// informations d’authentifications de notre service IoT
var iotCredentials;

//Loop through configuration internally defined in IBM Cloud and retrieve the credential from the IoT service
var baseConfig = appEnv.getServices(iotPlatformServiceName);
iotCredentials = baseConfig[iotPlatformServiceName];

var iotAppConfig = {
 "org" : iotCredentials.credentials.org,
 "id" : iotCredentials.credentials.iotCredentialsIdentifier,
 "auth-method" : "apikey",
 "auth-key" : iotCredentials.credentials.apiKey,
 "auth-token" : iotCredentials.credentials.apiToken
}

app.js – connexion à notre objet connecté et souscription aux événements publiés par l’application client.py installé sur la Raspberry Pi

var appClient = new Client(iotAppConfig);

appClient.connect();
console.log("Successfully connected to our IoT service!");

// subscribe to input events
appClient.on("connect", function () {
 console.log("subscribe to input events");
 appClient.subscribeToDeviceEvents("raspberrypi");
});

var motionSensorData = {"motionPayload":{}};

// deviceType "raspberrypi" and eventType "motionSensor" are published by client.py on RaspberryPi
appClient.on("deviceEvent", function(deviceType, deviceId, eventType, format, payload){
 if (eventType === 'motionSensor'){
   motionSensorData.motionPayload = JSON.parse(payload);
 }
}

7 Connecter la Raspberry Pi à la plateforme IoT

Maintenant que nous avons toutes les données d’authentification nécessaires, nous pouvons connecter notre Raspberry à la plateforme IoT dans IBM Cloud. Nous créons le fichier de configuration à l’emplacement suivant : 

/home/pi/device.cfg 

Ce fichier permettra de faire communiquer les deux entités.

[application]org=273pdc                          # même référence que dans /etc/iotsample-raspberrypi/device.cfg
id=b827eb80403e                                  # ID de mon matériel le même que dans /etc/iotsample-raspberrypi/device.cfg
auth-method=apikey
auth-key=a-273pdc-idj1eozloh                     # la clé généré précédemment
auth-token=zEu*MfYu@XGZSdxjjA                    # le jeton généré précédemment
type=raspberrypi                                 # le type de matériel, le même que dans /etc/iotsample-raspberrypi/device.cfg

La Raspberry Pi est prête à se connecter et communiquer avec notre service IoT sur IBM Cloud. Le code python suivant (client.py) est un exemple d’appels sample() définis dans le fichier motionSensor.py. Et ensuite, on connecte la Raspberry à notre service IoT et on publie les événements générés, en format JSON.

#!/usr/bin/python
# This is only executed on the device client e.g. Raspberry Pi
import time
import os, json
import ibmiotf.application
import uuid
import motionSensor

client = None

motionSensorGPIOPort = 4

try:
  options = ibmiotf.application.ParseConfigFile("/home/pi/device.cfg")
  options["deviceId"] = options["id"]  options["id"] = "aaa" + options["id"]  client = ibmiotf.application.Client(options)
  print "try to connect to IoT"
  client.connect()
  print "connect to IoT successfully"

  motionStatus = False
  motionSensor.setup(motionSensorGPIOPort)

  while True:
    motionData = motionSensor.sample()
    jsonMotionData = json.dumps(motionData)
    client.publishEvent("raspberrypi", options["deviceId"], "motionSensor", "json", jsonMotionData)

    if motionData['motionDetected'] != motionStatus:
      motionStatus = motionData['motionDetected']      print "Change in motion detector status, motionDetected is now:", motionStatus
 
    time.sleep(0.2)
 
except ibmiotf.ConnectionException as e:
 print e

Si nous regardons le tableau de bord de notre service IoT, nous voyons normalement plusieurs événements motionSensor : 

8 Utiliser les API RESTful pour retourner les données du capteur

Après avoir souscrit aux événements , nous devons référencer les API RESTful. C’est une exemple GET pour suivre notre client et d’envoyer une requête GET à notre application. Dans l’API GET, nous retournons les données du PIR (les événements souscrits comme précédemment vu).

app.js

app.get('/sensordata', raspberryPiServer.returnCurrentSensorData(motionSensorData)); 

Cette implémentation d’API est en JavaScript. Elle ne fait que retourner les données du capteur. 

raspberryPiServer.js

/*eslint-env node */

/**
 * Server side - express JS - where you should define REST APIs of the server side.
 */
// client asks server for the sensor data, and server sends back the sensor data
exports.returnCurrentSensorData = function(sensorData){
 return function(req, res){
   res.send(sensorData);
 };
};

9 Client (view) demande les données du capteur

La partie client est écrite en angular, très populaire quand on fait du front-end web. Cet exemple de code exécute à intervalle régulier une tâche. Le code demande à recevoir les données du capteur par un appel GET API comme nous venons de le voir. Quand un mouvement est détecté, le client (la vue) est averti.

home.client.html

<section>
<div ng-controller="HomeController" >
 <div class="switch">
 <label>
 No Motion
 <input id="switch" type="checkbox">
 <span class="lever"></span>
 Motion Detected
 </label>
 </div>
</div>
</section>

client.controller.js

/** Client side - angularJS - where controllers are defined.
 * HTML talks to these controllers.
 */

var homeControllers = angular.module('HomeControllers', []);

homeControllers.controller('HomeController', ['$scope', '$rootScope', '$http', '$interval',
function ($scope, $rootScope, $http, $interval) {
 
 // Set of all tasks that should be performed periodically
 var runIntervalTasks = function() {
   $http({
      method: 'GET',
      url: '/sensordata'
   }).then(function successCallback(response) {
 
     if (response.data.motionPayload !== undefined) {
     // switch on when motion is detected
     if(Object.keys(response.data.motionPayload).length > 0){
        var payload = JSON.parse(response.data.motionPayload);
        $('#switch').prop('checked', payload.motionDetected);
     }
   }
 }, function errorCallback(response) {
      console.log("failed to listen to sensor data");
 });
 };
}
]);

10 Test réel !

Il est temps de tester en condition réel notre IoT, le service cloud et notre application. Connectez-vous à la Raspberry Pi et exécutez client.py

sudo python client.py 

Pour le moment, aucun mouvement n’est détecté !

Soudain, un intrus est détecté ! Avec cette application d’intrusion, vous vous sentez plus en sécurité.

Bien entendu, cet exemple est très simple. Vous pouvez l’étendre, améliorer l’interface et rajouter des capteurs.

IBM Cloud : www.ibm.com/fr-fr/marketplace/cloud-platform