Freeboard et Node-Red

Freeboard est un projet de tableau de bord gratuit et open source avec des abonnements hébergés optionnels, facile à intégrer à diverses sources de données. Ce projet a été développé par Bug Labs, les créateurs d’un puissant service appelé dweet , un service de messagerie conçu pour l’Internet des Objets. Il existe un nœud Node-Red qui permet de mettre en relation un flux et le tableau de bord freeboard. Nous allons voir ici comment l’installer et l’utiliser.

Contrôle d’accès par lecture de plaques d’immatriculation (ALPR)

Ce système permet d’autoriser l’accès à un parking par lecture des plaques d’immatriculation. Il s’appuie sur une caméra IP associée à une carte Raspberry Pi et le logiciel de reconnaissance de plaques openALPR. Une base de donnée mongoDB contient la liste des plaques autorisées. Le middleware est assuré par un “flow” node-red. (liaison openALPR / mongoDB, déclenchement de l’ordre d’ouverture et IHM pour la gestion de la base)

Diagramme de cas d’utilisation

La lecture des plaques d’immatriculation va s’intégrer dans le cas d’utilisation « Contrôle d’accès ». L’ajout et la mise à jour de nouveaux véhicules sera lié au cas d’utilisation « Mise à jour de la base données ». Le système autorise l’accès au parking aux véhicules dont le numéro de plaque d’immatriculation est inscrit dans la base de données.

Constitution du système

  • Une caméra IP, elle sera positionnée afin de capter la plaque d’immatriculation du véhicule lorsque ce dernier de présentera à l’entrée du parking.
  • Une barrière automatisée. Elle est fermée par défaut et s’ouvre si la plaque du véhicule est reconnue.
  • Un ordinateur qui contient la base de données des plaques autorisées, l’IHM web permettant la maintenance de la BDD, un logiciel de reconnaissance de plaques en relation avec la caméra IP et un interlogiciel (middleware) permettant de faire communiquer les différentes applications.

Topologie du système

Solution retenue

Matériels

  • Une carte Raspberry (2 ou 3) / 16GB raspbian JESSIE (nov 2016) -> serveur
  • Une carte Arduino avec le firmware Firmata Standard -> extension E/S
  • Une caméra IP. On doit pouvoir accéder à l’image via le web. Pour l’exemple nous utiliserons une petite caméra motorisée db power clone d’une caméra FOSCAM FI8908W[1]. Sur cette caméra, d’après la documentation[2], l’accès au flux s’effectue à l’URL : http://<IP_CAMERA>:<PORT_CAMERA>//videostream.cgi?user=admin&pwd=&resolution=32&rate=0
  • Une barrière automatique pilotée par un signal logique (« 1 » ouverture, « 0 » fermeture). La barrière gère elle-même la sécurité de passage.

Logiciels

  • openALPR pour Raspberry : logiciel open source de reconnaissance de plaques d’immatriculation.
  • MongoDB et mongoExpress : serveur de base de données et client web.
  • NODE-RED avec node-red-contrib-gpio, node-red-dashboard, node-red-contrib-mongoDB2, ObjectID et mongoDB2 : logiciel de programmation orienté flux, permettant l’interconnexion d’applications hétérogènes devant échanger des données.

Installation Logicielle.

Node-red

dans une console lancer node-red : node-red-start puis à l’aide d’un navigateur  accéder à http://<ip raspberry>:1880  . Si le noeud Johnny5 est visible, il n’y a rien à faire sinon voir Réinstallation de node-red avec Johnny5

OpenAlpr

voir openAlpr sur Raspberry Pi

Base de données mongoDB

voir installation de mongoDB et mongo-express

Mise en oeuvre.

Test de openALPR avec la caméra

Compléter le fichier /etc/openalpr/alprd.conf et indiquer le chemin d’accès au flux vidéo de la caméra IP

; This configuration file overrides the default values specified 
; in /usr/local/share/openalpr/config/alprd.defaults.conf
[daemon]

; country determines the training dataset used for recognizing plates. Valid values are us, eu
country = eu
pattern = fr

; text name identifier for this location
site_id = rpi01

; Declare each stream on a separate line
; each unique stream should be defined as stream = [url]
; adresse de la camera IP accessible par le web
stream = http://192.168.5.23:8080/videostream.cgi?user=admin&pwd=&resolution=32&rate=0

; topn is the number of possible plate character variations to report
topn = 5

; Determines whether images that contain plates should be stored to disk
store_plates = 0
store_plates_location = /home/pi/plateimages/

; upload address is the destination to POST to
upload_data = 1
upload_address = http://localhost:1880/plate/

Lancer la commande alprd -fet placer une image comportant une plaque d’immatriculation devant la caméra

Observer la console :

pi@Raspberry Pipi:~ $ alprd -f
INFO - Running OpenALPR daemon in the foreground.
INFO - Using: /etc/openalpr/alprd.conf for daemon configuration
Missing config value for company_id
Missing config value for pattern
INFO - Using: /home/pi/plateimages/ for storing valid plate images
INFO - country: fr -- config file: /etc/openalpr/openalpr.conf
INFO - pattern:
INFO - Stream 1: http://192.168.5.23:8080/videostream.cgi?user=admin&pwd=&resolution=32&rate=0
INFO - Starting camera 1
INFO - Video stream connecting...
INFO - Video stream connected
DEBUG - Writing plate AV865XG (rpi01-cam1-1483036354101) to queue.

Video de Guy TechLab : mise en oeuvre de ce tuto.

OpenALPR et node-red : lecture et affichage d’une plaque.

Node-red permet d’interconnecter des applications hétérogènes devant échanger des informations. On parle d’échange de messages. Aplrd permet d’envoyer une requête POST à un serveur web lors de la lecture d’une plaque. Nous allons donc créer un flux qui intercepte cette requête POST, va en extraire la donnée et publier cette dernière dans une fenêtre de débogage.

Vérification du fichier fichier [shell]/etc/openalpr/alprd.conf[/shell]

Vérifier que les 3 dernières lignes du fichier alprd.cong correspondent à :

; upload address is the destination to POST to
upload_data = 1 ; « 1 » = envoi des données
upload_address = http://localhost:1880/plate/ ; adresse du serveur web où sont envoyées les requêtes POST

Ce qui impliquera que les données seront postées à l’adresse http://localhost:1880/plate

Flux node-red

Flux composé d’un node « http in », « http response » et « debug »

Lors de la reconnaissance d’une plaque on obtient un objet msg.payload en sortie du node « http in » qui une fois remis en forme ressemble à ceci :

{
    "version": 2,
    "data_type": "alpr_results",
    "epoch_time": 1483090959429,
    "img_width": 640,
    "img_height": 480,
    "processing_time_ms": 395.173157,
    "regions_of_interest": [],
    "results": [
        {
            "plate": "AV865XG",
            "confidence": 90.980667,
            "matches_template": 0,
            "plate_index": 0,
            "region": "",
            "region_confidence": 0,
            "processing_time_ms": 171.828781,
            "requested_topn": 5,
            "coordinates": [
                {…

On peut observer qu’il s’agit d’un objet JSON dont la propriété « results » est un tableau contenant les plaques reconnues avec leur score « confidence ».

On peut donc interroger l’objet msg.payload afin d’en extraire le numéro de plaque. Pour cela on introduit un script

 

On peut alors voir le numéro de la plaque apparaitre dans la fenêtre de débogage

Utilisation de mongoDB et ALPR dans node-red

L’objectif est de comparer la plaque lue par alprd avec une plaque contenue dans la base de données.

Ajout du node mongoDB2 à la palette de nodes :

Sélectionner le Menu de Node-red -> Manage palette

Sélectionner l’onglet « Install »

Dans l’espace de recherche taper « mongo »

On voit alors apparaitre les nodes disponibles. Cliquer sur le bouton « install » du node « node-red-contrib-mongoDB2 »

Le nœud s’installe est devient alors disponible dans la palette des nodes.

Installer aussi le node « node-red-contrib-objectid », il permet de gérer les propriétés « _id » des documents.

Paramétrage du nœud mongoDB2 et test de connexion

Faire glisser un node mongoDB2 dans l’espace de travail et l’éditer.

Sélectionner « External service » puis cliquer sur le petit crayon afin d’ajouter une connexion au serveur mongoDB Ajouter un serveur, l’URI est normalement : mongodb://localhost:27017/db
Une fois le serveur paramétré, sélectionner « db » dans l’option « Server », compléter l’option « Collection » avec le nom de la collection « LicencePlate » enfin dans « Operation » sélectionner « findOne » et cliquer sur « Done » Ajouter un nœud « Inject » dans l’espace de travail et le paramétrer de la manière suivante :

Compléter le flow de la manière suivante : ajouter un node « json » et un node « debug ». Lier l’ensemble.

Quand on clique sur le bouton du nœud inject, on voit apparaitre le document au format JSON dans la fenêtre de debug

code du Flow (à importer via le clipboard)
[{"id":"fcc8de73.b9a8e","type":"mongodb2 in","z":"de8a40ec.19323","service":"_ext_","configNode":"1d02b15c.4cf227","name":"","collection":"LicencePlate","operation":"findOne","x":1234.7665710449219,"y":157.85000610351562,"wires":[["c37c79dd.bbd448"]]},{"id":"5934a9db.1c3ee8","type":"json","z":"de8a40ec.19323","name":"","x":996.7665710449219,"y":158.01666259765625,"wires":[["fcc8de73.b9a8e"]]},{"id":"c37c79dd.bbd448","type":"debug","z":"de8a40ec.19323","name":"","active":true,"console":"false","complete":"false","x":1474.7666015625,"y":157.01666259765625,"wires":[]},{"id":"fea87bf7.46af38","type":"inject","z":"de8a40ec.19323","name":"","topic":"","payload":"{\"nom\":\"Hollande\"}","payloadType":"str","repeat":"","crontab":"","once":false,"x":774.7666320800781,"y":158.38333129882812,"wires":[["5934a9db.1c3ee8"]]},{"id":"1d02b15c.4cf227","type":"mongodb2","z":"","uri":"mongodb://localhost:27017/db","name":"db","options":"","parallelism":"-1"}]

 

Nous venons d’effectuer une requête dans la base mongoDB

Reconnaissance d’une plaque d’immatriculation.

En associant la gestion de la requête POST fournit par le logiciel alprd et une requête dans la base de données, on peut déterminer si l’accès est autorisé.

code du Flow (à importer via le clipboard)
[{"id":"cd7d103b.308a78","type":"mongoDB2 in","z":"1ee78c77.4f0c84","service":"_ext_","configNode":"d3816939.e303e8","name":"","collection":"LicencePlate","operation":"count","x":1201,"y":144,"wires":[["ae24a545.fb9ea","3fc0b1.16e58f5"]]},{"id":"ae24a545.fb9ea","type":"debug","z":"1ee78c77.4f0c84","name":"","active":true,"console":"false","complete":"false","x":1477.7666015625,"y":66.71665954589844,"wires":[]},{"id":"d96d3dcc.a4c3a8","type":"http in","z":"1ee78c77.4f0c84","name":"","url":"/plate","method":"post","swaggerDoc":"","x":697.7666015625,"y":147.18333435058594,"wires":[["e36dbbe7.4063c8"]]},{"id":"e36dbbe7.4063c8","type":"function","z":"1ee78c77.4f0c84","name":"extraction num plaque","func":"var plaque={}; //objet vide\nplaque.immatriculation=msg.payload.results[0].plate;\nmsg.payload=plaque;\nreturn msg;","outputs":1,"noerr":0,"x":939.7666015625,"y":145.9499969482422,"wires":[["cd7d103b.308a78","b918e1ce.d2721"]]},{"id":"b918e1ce.d2721","type":"debug","z":"1ee78c77.4f0c84","name":"","active":true,"console":"false","complete":"false","x":1184.7666015625,"y":61.716644287109375,"wires":[]},{"id":"3fc0b1.16e58f5","type":"http response","z":"1ee78c77.4f0c84","name":"","x":1457.7666015625,"y":142.4166717529297,"wires":[]},{"id":"d3816939.e303e8","type":"mongoDB2","z":"","uri":"mongoDB://localhost:27017/db","name":"db","options":"","parallelism":"-1"}]

 

Le script dans le node function (extraction num plaque) est le suivant :

var plaque={}; 
plaque.immatriculation=msg.payload.results[0].plate;
msg.payload=plaque;
return msg;

Le message de sortie sera donc de la forme : { "immatriculation": "AV865XG" }. Ce message sert d’argument d’entrée à la requête « count » du node mongoDB2. Autrement dit le node mongoDB2 va compter les documents qui contiennent un champ « immatriculation » ayant pour valeur « AV865XG ». Le message de sortie du node mongoDB sera un nombre. Si ce dernier vaut 0, c’est que le numéro de plaque n’est pas enregistré dans la base. Reste à commander l’ouverture de la barrière en fonction de cette information.

Pour cela nous allons utiliser une carte Arduino qui sera pilotée par la carte Raspberry.

Compléter le flow de la manière suivante en ajoutant les nœuds « switch », « trigger » et « gpio ».

code du Flow (à importer via le clipboard)
[{"id":"7ec1525b.cd9fac","type":"trigger","z":"1ee78c77.4f0c84","op1":"1","op2":"0","op1type":"str","op2type":"str","duration":"2000","extend":false,"units":"ms","reset":"","name":"","x":1270.7666015625,"y":243.18331909179688,"wires":[["1a99c7a1.ed34b8"]]},{"id":"1a99c7a1.ed34b8","type":"gpio out","z":"1ee78c77.4f0c84","name":"arduino","state":"OUTPUT","pin":"13","i2cDelay":"0","i2cAddress":"","i2cRegister":"","outputs":0,"board":"4a53a7ab.eb3e7","x":1475.7666015625,"y":242.11666870117188,"wires":[]},{"id":"fd89d95c.482bb","type":"switch","z":"1ee78c77.4f0c84","name":"","property":"payload","propertyType":"msg","rules":[{"t":"gte","v":"1","vt":"num"}],"checkall":"true","outputs":1,"x":1043.7666015625,"y":243.76663208007812,"wires":[["7ec1525b.cd9fac"]]},{"id":"4a53a7ab.eb3e7","type":"nodebot","z":"","name":"","username":"","password":"","boardType":"firmata","serialportName":"/dev/ttyUSB0","connectionType":"local","mqttServer":"","socketServer":"","pubTopic":"","subTopic":"","tcpHost":"","tcpPort":"","sparkId":"","sparkToken":"","beanId":"","impId":"","meshbluServer":"https://meshblu.octoblu.com","uuid":"","token":"","sendUuid":""}]

 

Le switch sera paramétré de la manière suivante :

Le trigger :

Le nœud gpio quant à lui sera paramétré pour piloter une broche digital d’une carte Arduino. La carte Arduino sera programmée avec le programme Firmata : (Menu fichier > Exemples > Firmata > StandardFirmata ) puis connectée à la carte Raspberry Pi par un câble USB.

Editer le nœud gpio puis cliquer sur le crayon afin de définir la cible :
Cliquer sur la loupe afin de trouver la carte Arduino connectée à la carte Raspberry (dans l’exemple c’est /dev/tty/USB0, mais cela peut être autre chose…). Configurer le reste comme suit :
Compléter alors le nœud gpio comme suit :

Si la configuration est correcte la broche 13 (et la led qui y est rattachée) passe à l’état logique « 1 » pour une durée de 2 sec lorsqu’une plaque est reconnue. Reste à interconnecter l’ensemble à la vraie barrière…

Outil d’administration de la base de donnée

voir : Gestion de mongoDB à l’aide de jsGrid et node-red

code du Flow (à importer via le clipboard)

il est disponible ici : http://flows.nodered.org/flow/e32f2b942bce77ef6079c0642b93c036

Ce flow fabrique une interface homme machine qui permet de maintenir la base de données des utilisateurs autorisés à accéder au parking. Il s’appuie sur un template html et le plugin jsGrid (dépend de jQuery). Son fonctionnement est dit « RESTfull » c’est-à-dire que la grille utilise une requête ajax de type POST pour insérer des valeurs dans la base, une requête de type PUT pour effectuer la mise à jour et une requête DELETE pour effacer un document. Pour fonctionner ce flow a besoin du node « ObjectId », qu’il faut installer si ce n’est pas déjà fait.

capture

Le format des documents doit être :

{
    "_id": ObjectID("585a4732ac11400192a60b85"),
    "nom": "nameOfUser",
    "prenom": "FirstNameOfUser",
    "immatriculation": "123456",
    "heure": "13",
    "minute": "30"
}

La grille est accessible à l’adresse : http://<node-red>:<port>/plates

Le code du node template est le suivant :

<!DOCTYPE html>
<html lang="fr">
<head>
    <title>Licence Plate</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-timepicker/0.5.2/css/bootstrap-timepicker.min.css" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.css" />
    <link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid-theme.min.css" />
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.js"></script>

    <script type="text/javascript">
        $(function () {
            var db = {{#payload}}{{{.}}}{{/payload}};
                $("#jsgrid").jsGrid({
                    width: "100%",
                    inserting: true,
                    editing: true,
                    sorting: true,
                    paging: true,

                    data: db,

                    fields: [
                        { title:"Nom", name: "nom", type: "text", width: 50 },
                        { title:"Prénom", name: "prenom", type: "text", width: 50 },
                        { title:"Plaque", name: "immatriculation", type: "text", width: 50 },
                        { title:"Heure", name: "heure", type:"number", width: 25},
                        { title:"Minute", align:"left",  name: "minute", type:"number" , width: 25},
                        { type: "control" }
                    ],
               
                    controller: {
                        insertItem: function(item) {
                            return $.ajax({
                                type: "POST",
                                url: "/insert",
                                data: item
                            });
                        },
                        updateItem: function(item) {
                            return $.ajax({
                                type: "PUT",
                                url: "/update",
                                data: item
                            });
                        },
                        deleteItem: function(item) {
                            return $.ajax({
                                type: "DELETE",
                                url: "/delete",
                                data: item
                            });
                        }
                    }   
                });
            });
    
  </script>
</head>
<body class="container">
    <section class="row">

        <div class="col-md-6"></div>
        <div class="col-md-6" id="jsgrid">
        </div>
    </section>
</body>
</html>

La ligne var db = {{#payload}}{{{.}}}{{/payload}};utilise la notation « mustache » c’est la notation qui permet de à la page web de récupérer les informations contenues dans le message d’entrée du node. Dans notre cas msg.payload est le résultat de la requête effectuée dans la base de données. Il s’agit d’un tableau d’objets JSON le format est le suivant :

[
    {
        "nom": "TestName",
        "prenom": "Toto",
        "immatriculation": "AV865XG",
        "_id": "58667499d93f5401a2b4d2d5"
    },
    {
        "_id": "586666b73a3b9e2011963257",
        "nom": "Hollande",
        "prenom": "Francois",
        "immatriculation": "HE123LY"
    }
]

Le moteur de template mustache va itérer le tableau et passer les objets qu’il contient au rendu de la page web

Lors du rendu de la page, le texte du template

…
        $(function () {
            var db = {{#payload}}{{{.}}}{{/payload}};
                $("#jsgrid").jsGrid({
                    width: "100%",
                    inserting: true,
…

est remplacé par

…
        $(function () {
            var db = [{"nom":"TestName","prenom":"Toto","immatriculation":"AV865XG","_id":"58667499d93f5401a2b4d2d5"},{"_id":"586666b73a3b9e2011963257","nom":"Hollande","prenom":"Francois","immatriculation":"HE123LY"}];
                $("#jsgrid").jsGrid({
                    width: "100%",
                    inserting: true,
…

{{{.}}}(Avec triple « mustache ») signifie que l’objet entier doit être passé de manière brute, c’est-à-dire sans être mise au format html

Avec une {{.}}(double mustache) le texte serait remplacé par

var db = [{&quot;nom&quot;:&quot;TestName&quot;,&quot;prenom&quot…

c’est-à-dire que les « ” » sont remplacés par leur code html « &quot ; » de ce fait la variable « db » n’est plus un tableau JSON au vu du script.

Voir https://github.com/janl/mustache.js/#non-empty-lists et https://github.com/janl/mustache.js/#variables pour plus d’informations.

  1. http://www.gadgetvictims.com/2008/08/foscam-fi8908w-firmware-history-page.html
  2. https://docs.google.com/fileview?id=0B9kFXSWngqLWYjBlYjcyOWUtYzgxYy00MWYxLThhYjAtOTgwODgzOTRmOGM5&hl=en

Instrumentation modbus

L’instrumentation consiste ici à équiper un véhicule électrique de sondes afin de mesurer la vitesse, la température du moteur ainsi que sa vitesse de rotation. Les sondes sont construites autour de cartes Arduino. Les cartes Arduino sont reliées à un bus RS485. Une application Node-red embarquée dans un Raspberry Pi permet de communiquer via le protocole modBus avec les cartes Arduino. Les données recueillies sont affichées sur un tableau de bord : une page web développée elle aussi avec Node-red équipée de jauges.

Une Carte Raspberry équipée de node-red se comporte comme un maitre modBus. Une carte Arduino effectue une mesure et se comporte comme un esclave modBus. Le maitre interroge cycliquement la carte Arduino qui envoie alors ses résultats. La trame modBus est constituée d’un paquet de 7 données (voir le tableau dans le code Arduino). Node-red récupère ces données et les encapsule dans un objet json qui est stocké dans le contexte du flux. Une page web est fabriquée à l’aide de node-red cette page affiche une jauge grâce au script sonicGauge. Des requêtes Ajax à intervalles réguliers récupèrent l’objet stocké dans le contexte, ce qui permet la mise à jour de la jauge en temps réel.

Vidéos

La playlist suivante contient 4 vidéos :

  1. Présentation du projet
  2. Intégration du nœud Slave Modbus, extraction des données et sauvegarde dans le contexte de flux.
  3. Création d’une page web exploitant le contexte de flux à l’aide d’AJAX.
  4. Intégration de sonicGauge pour afficher les valeurs.

Code Arduino

La bibliothèque ModbusRtu.h est accessible ici : https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino

Le code Arduino simule une acquisition de température allant de 0 à 42°C ce manière cyclique.

#include <ModbusRtu.h>
#include <SoftwareSerial.h>
#define SERIAL 0
#define MODBUS_ADR 1
#define TXEN 3
#define ID 0xCAFE
#define UNITE 0x09 // 
#define TYPE 0x03
#define MIN 0
#define MAX 100 
// data array for modbus network sharing
// format : {Id_appareil,id_unite,id_type,min,max,valEntiere,valDeci}
// 
// id_appareil : numéro unique
/* 

╔═════════╦════════════════╦══════════╦═════════╦═════╦═════╦════════════╦═════════╗
║  index  ║       0        ║    1     ║    2    ║  3  ║  4  ║     5      ║    6    ║
╠═════════╬════════════════╬══════════╬═════════╬═════╬═════╬════════════╬═════════╣
║ trame   ║ id_appareil    ║ id_unite ║ id_type ║ min ║ max ║ valEntiere ║ valDeci ║
╠═════════╬════════════════╬══════════╬═════════╬═════╬═════╬════════════╬═════════╣
║ exemple ║ 51966 (0xCAFE) ║ 9        ║ 3       ║ 5   ║ 95  ║ 33         ║ 90      ║
╚═════════╩════════════════╩══════════╩═════════╩═════╩═════╩════════════╩═════════╝

tableau des unité :
╔══════════╦══════╦═════╦══════╦═══╦════╦═══╦═══╦═════════╦═══════════╦════╗
║ id_unite ║  0   ║  1  ║  2   ║ 3 ║ 4  ║ 5 ║ 6 ║    7    ║     8     ║ 9  ║
╠══════════╬══════╬═════╬══════╬═══╬════╬═══╬═══╬═════════╬═══════════╬════╣
║ unité    ║ sans ║ m/s ║ Km/h ║ m ║ km ║ s ║ h ║ Tours/s ║ Tours/min ║ °C ║
╚══════════╩══════╩═════╩══════╩═══╩════╩═══╩═══╩═════════╩═══════════╩════╝
tableau des types :
╔═════════╦══════╦══════╦═════╦═════════╗
║ id_type ║  0   ║  1   ║  2  ║    3    ║
╠═════════╬══════╬══════╬═════╬═════════╣
║ Type    ║ Char ║ Byte ║ Int ║ decimal ║
╚═════════╩══════╩══════╩═════╩═════════╝
*/
// min :                       valeur minimum
// max :                       valeur max                             
// valEntiere:                 valeur entière
// valDeci :                   valeur decimale
 
uint16_t mesure[] = { ID, UNITE, TYPE, MIN, MAX, 0, 0 };
/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  u8serno : serial port (use 0 for Serial)
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
Modbus slave(MODBUS_ADR,SERIAL,TXEN); // this is slave @1 and RS-232 or USB-FTDI
unsigned long previousMillis = 0;        // will store last time LED was updated
 
// constants won't change :
const long interval = 75;
void setup() {
  slave.begin( 19200 ); // baud-rate at 19200
}
 
void loop() {
        static long temp = 0;
        unsigned long currentMillis = millis();
 
        if (currentMillis - previousMillis >= interval) {
                // save the last time you blinked the LED
                previousMillis = currentMillis;
                temp += 10;
                int entier = temp / 100;
                int decimal = temp - (entier *100);
                mesure[5] = entier;
                mesure[6] = decimal;
                if (temp>4200)
                {
                        temp = 0;
                }
 
        }
 
  slave.poll( mesure,sizeof(mesure) );
}

Fichier JS du nœud “data2obj”

/* 

╔═════════╦════════════════╦══════════╦═════════╦═════╦═════╦════════════╦═════════╗
║  index  ║       0        ║    1     ║    2    ║  3  ║  4  ║     5      ║    6    ║
╠═════════╬════════════════╬══════════╬═════════╬═════╬═════╬════════════╬═════════╣
║ trame   ║ id_appareil    ║ id_unite ║ id_type ║ min ║ max ║ valEntiere ║ valDeci ║
╠═════════╬════════════════╬══════════╬═════════╬═════╬═════╬════════════╬═════════╣
║ exemple ║ 51966 (0xCAFE) ║ 9        ║ 3       ║ 5   ║ 95  ║ 33         ║ 90      ║
╚═════════╩════════════════╩══════════╩═════════╩═════╩═════╩════════════╩═════════╝

tableau des unité :
╔═══════╦══════╦═════╦══════╦═══╦════╦═══╦═══╦═════════╦═══════════╦════╗
║ index ║  0   ║  1  ║  2   ║ 3 ║ 4  ║ 5 ║ 6 ║    7    ║     8     ║ 9  ║
╠═══════╬══════╬═════╬══════╬═══╬════╬═══╬═══╬═════════╬═══════════╬════╣
║ unité ║ sans ║ m/s ║ Km/h ║ m ║ km ║ s ║ h ║ Tours/s ║ Tours/min ║ °C ║
╚═══════╩══════╩═════╩══════╩═══╩════╩═══╩═══╩═════════╩═══════════╩════╝
tableau des types :
╔═══════╦══════╦══════╦═════╦═════════╗
║ index ║  0   ║  1   ║  2  ║    3    ║
╠═══════╬══════╬══════╬═════╬═════════╣
║ Type  ║ Char ║ Byte ║ Int ║ decimal ║
╚═══════╩══════╩══════╩═════╩═════════╝
par exemple data = [ 51966, 9, 3, 5, 95, 33, 90 ]
id = 51966 = 0xCAFE
unité = 9 => °C
type = 3 => decimal
min = 5 => minimum indiqué sur la gauge
max = 95 => max indiqué sur la gauge
valEntiere = 33 et vaDeci = 90 => tempétature = 33.90°C

format de l'objet de sortie :
4 propriétés : unite, valeur, min et max 
{ "unite": "°C", "valeur": 33.9, "min": 5, "max": 95 }
*/
var data = msg.payload;
var obj={};
var unite = ['','m/s','km/h','m','km','s','h','tours/s','tours/min','°C'];

if(data[0]==0xCAFE)
{
    obj.unite=unite[data[1]];
    if(data[2]==3){
        obj.valeur= (data[5]*100 + data[6])/100.0;
    }
    obj.min=data[3];
    obj.max=data[4];
}
flow.set('message',obj);
msg.payload=obj;
return msg;

Fichier html du noeud “html”

<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script>{{{script}}}</script>
<div id='thermometre' class="gauge"></div>
<script>
    var thermometre= $('#thermometre').SonicGauge();
    
    $.getJSON(
    "/data",
    function(data){
        var options ={};
        options.label= data.unite;
        options.start = {angle: -225, num: data.min};
        options.end		= {angle: 45, num: data.max};
        thermometre.SonicGauge('setOptions', options);
        thermometre.SonicGauge ('draw');
    });
                
                
setInterval(function () {
$.getJSON(
    "/data",
    function(data){
        thermometre.SonicGauge('val',data.valeur);
    });
},200);


</script>

Script SonicGauge à coller dans le nœud “script”

/**
 * Sonic Gauge jQuery Plugin v0.3.0
 * jQuery plugin to create and display SVG gauges using RaphaelJS
 * 
 * Copyright (c) 2013 Andy Burton (http://andyburton.co.uk)
 * GitHub https://github.com/andyburton/Sonic-Gauge
 * 
 * Licensed under the MIT license (http://andyburton.co.uk/license/mit.txt)
 */

(function(b){var a={init:function(c){if(!this.length){return this}this.options=b.extend(true,{},b.fn.SonicGauge.defaultOptions);this.settings={};this.SonicGauge("setOptions",c);this.SonicGauge("draw");return this},setOptions:function(c){if(c){b.extend(true,this.options,c)}this.settings.canvas_d=this.options.diameter;this.settings.canvas_r=this.settings.canvas_d/2;this.settings.speedo_d=this.settings.canvas_d-this.options.margin*2;this.settings.speedo_r=this.settings.speedo_d/2;this.settings.increment=(this.options.end.angle-this.options.start.angle)/(this.options.end.num-this.options.start.num);return this},draw:function(){var f=this;this.width(this.settings.canvas_d);this.height(this.settings.canvas_d);this.gauge=Raphael(this.attr("id"),this.settings.canvas_d,this.settings.canvas_d);var e=this.gauge.circle(this.settings.canvas_r,this.settings.canvas_r,this.settings.speedo_r).attr(this.options.style.outline);var i=this.settings.canvas_r;var h=this.settings.canvas_r-(this.settings.canvas_r/4);if(typeof this.options.label=="object"){if(this.options.label.margin_x){i+=this.options.label.margin_x}if(this.options.label.margin_y){h+=this.options.label.margin_y}}else{if(typeof this.options.label=="string"){this.options.label={value:this.options.label}}}if(this.options.label){var d=this.gauge.text(i,h,this.options.label.value).attr(this.options.style.label)}this.sectors=[];b.each(this.options.sectors,function(n){this.style=b.extend(true,f.options.style.sector,this.style);if(!(isNaN(this.start)||isNaN(this.end))){var p=f.settings.increment*(this.start-f.options.start.num)+f.options.start.angle;var m=f.settings.increment*(this.end-f.options.start.num)+f.options.start.angle;var j=this.radius?this.radius:f.settings.speedo_r;var q=Math.PI/180;var l=f.settings.canvas_r+j*Math.cos(p*q),k=f.settings.canvas_r+j*Math.cos(m*q),t=f.settings.canvas_r+j*Math.sin(p*q),s=f.settings.canvas_r+j*Math.sin(m*q);var o=f.gauge.path(["M",f.settings.canvas_r,f.settings.canvas_r,"L",k,s,"A",j,j,0,+(m-p>180),0,l,t,"z"]).attr(this.style);f.sectors.push(o)}});var g=[];b.each(this.options.markers,function(){if(this.line){if(!this.line.width){this.line.width=10}if(!this.line.height){this.line.height=1}var v=f.gauge.rect(f.settings.canvas_r+f.settings.speedo_r-this.line.width,f.settings.canvas_r-Math.floor(this.line.height/2)).attr(this.line).hide()}var n=1;while(this.gap<1){n*=10;this.gap*=10}var j=f.options.start.num*n;var l=f.options.end.num*n;for(var o=j;o<=l;o+=this.gap){var k=n>1?o/n:o;if(this.toFixed){k=k.toFixed(this.toFixed)}if(this.toPrecision){k=k.toPrecision(this.toPrecision)}var t=f.settings.increment*(k-j)+f.options.start.angle;if(t+Math.abs(f.options.start.angle)>=360){t=(t+Math.abs(f.options.start.angle))%360+f.options.start.angle}if(b.inArray(t,g)>=0){continue}g.push(t);if(this.line){var p=v.clone().rotate(t,f.settings.canvas_r,f.settings.canvas_r)}if(this.text){if(!this.text.space){this.text.space=0}var m=k;if(typeof this.value=="object"){if(this.value.divide){m/=this.value.divide}if(this.value.multiply){m*=this.value.multiply}}var q=t.toRadians();var s=f.settings.canvas_r+(this.text.space+f.settings.speedo_r)*Math.cos(q);var r=f.settings.canvas_r+(this.text.space+f.settings.speedo_r)*Math.sin(q);var u=f.gauge.text(s,r,m).attr(this.text)}}if(this.line){v.remove()}});if(this.options.digital){this.digital=b("<div>").addClass("digital").css({"margin-top":Math.ceil(this.settings.speedo_r/2),width:"20%","font-family":"Arial","font-size":20,color:"#fff","text-align":"center",border:"2px solid #777","border-radius":10,padding:5,"background-color":"#111"}).css(this.options.digital).text(this.options.default_num).appendTo(this).center()}this.needles=[];b.each(this.options.needles,function(k){if(!this.default_num){this.default_num=f.options.default_num}this.style=b.extend(true,f.options.style.needle,this.style);var m=this.default_num-f.options.start.num;if(typeof this.value=="object"){if(this.value.divide){m/=this.value.divide}if(this.value.multiply){m*=this.value.multiply}}var j=f.settings.increment*m+f.options.start.angle;var l=f.gauge.rect(f.settings.canvas_r,f.settings.canvas_r,f.settings.speedo_r).attr(this.style).transform("r"+j+","+f.settings.canvas_r+","+f.settings.canvas_r);f.needles.push(l)});if(typeof this.options.style.center=="object"){var c=this.gauge.circle(this.settings.canvas_r,this.settings.canvas_r,this.options.style.center.diameter).attr(this.options.style.center)}this.trigger("drawn");return this},val:function(e){if(this.digital){var c=e;if(this.options.digital_toFixed){c=c.toFixed(this.options.digital_toFixed)}if(this.options.digital_toPrecision){c=c.toPrecision(this.options.digital_toPrecision)}this.digital.text(c)}var d=this;b.each(this.needles,function(h){var f=e;if(typeof d.options.needles[h].value=="object"){var g=d.options.needles[h].value;if(g.divide){f/=g.divide}if(g.multiply){f*=g.multiply}}this.animate({transform:"r"+(d.settings.increment*(f-d.options.start.num)+d.options.start.angle)+","+d.settings.canvas_r+","+d.settings.canvas_r},d.options.animation_speed)});this.trigger("update",e);return this}};b.fn.SonicGauge=function(c){if(a[c]){return a[c].apply(this,Array.prototype.slice.call(arguments,1))}else{if(typeof c==="object"||!c){return a.init.apply(this,arguments)}else{b.error("Method "+c+" does not exist on jQuery.SonicGauge")}}};b.fn.SonicGauge.defaultOptions={margin:35,diameter:350,start:{angle:-225,num:0},end:{angle:45,num:100},default_num:0,animation_speed:1000,digital:{},digital_toFixed:0,needles:[{}],sectors:[{}],markers:[{gap:10,line:{width:20,stroke:"none",fill:"#eeeeee"},text:{space:22,"text-anchor":"middle",fill:"#333333","font-size":18}},{gap:5,line:{width:8,stroke:"none",fill:"#999999"}}],style:{outline:{fill:"#333333",stroke:"#555555","stroke-width":8},center:{fill:"#eeeeee",diameter:10},needle:{height:1,stroke:"none",fill:"#cc0000"},label:{"text-anchor":"middle",fill:"#fff","font-size":16}}}})(jQuery);if(typeof(Number.prototype.toRadians)==="undefined"){Number.prototype.toRadians=function(){return this*Math.PI/180}}if(typeof(Number.prototype.decimalPlaces)==="undefined"){Number.prototype.decimalPlaces=function(){return(this.toFixed(20)).replace(/^-?\d*\.?|0+$/g,"").length}}if(typeof(jQuery.fn.center)==="undefined"){jQuery.fn.center=function(a){if(typeof a==="undefined"){a=this.parent()}a.css("position","relative");return this.css("position","absolute").css({top:Math.max(0,((a.height()-this.outerHeight())/2)+a.scrollTop()),left:Math.max(0,((a.width()-this.outerWidth())/2)+a.scrollLeft())})}};

Le code complet du flow node-red : http://flows.nodered.org/flow/f0d416178add2b981ac21afeaf0bc0f7

Lecteur WebRadio avec node-red, MPD et MPC

Cet article propose de créer une IHM pour piloter un lecteur de radio internet. Le système est embarqué dans une carte Raspberry Pi équipée de node-red. Nous verrons d’abord comment installer MPD et MPC qui permettent de transformer la carte en lecteur audio. Nous verrons ensuite comment créer une interface web à l’aide de node-red qui permette de piloter le lecteur.

MPD est un lecteur média qui fonctionne comme un service (daemon). Il met en relation des sources de musiques, des codecs et une API de pilotage via le réseau. En fait il se comporte comme un serveur auquel on envoie des commandes de type « play », « stop »,… afin qu’il les exécute.
MPD / MPC[3]
Nous allons installer MDP (Music Player Daemon) qui va servir de lecteur de musique embarqué, ainsi que MPC (Music Player Command) qui servira à piloter MPD (telle une télécommande) :
MPD
Pré-requis
Vérifier que le driver audio est correctement installé :

[shell]
lsmod[/shell]

Il doit apparaître le driver snd_bcm2835 dans le résultat. Si ce n’est pas le cas, essayer :

[shell]sudo modprobe snd_bcm2835[/shell]

Forcer la sortie son sur la prise casque :

[shell]amixer cset numid=3 1
sudo alsactl store
[/shell]

Pour que la sortie audio soit toujours la prise Jack (au lieu de la prise HDMI), éditer le fichier /boot/config.txt et dé-commenter la ligne  hdmi_drive=2 en enlevant le dièse en début de ligne.

[shell]
# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
hdmi_drive=2
[/shell]

Installation

Sous Raspbian stretch, l’installation est facile (après mise à jour des dépôts) :

sudo apt update
sudo apt install mpc mpd

Il faut ensuite éditer le fichier /etc/mpd.conf et retirer le commentaire de la ligne “device” dans la section “audio_output”

# An example of an ALSA output:
#
audio_output {
    type		"alsa"
    name		"My ALSA Device"
    device		"hw:0,0"	# optional

il faut ensuite redémarrer MPD :

sudo /etc/init.d/mpd restart

Télécharger le fichier radio.m3u. Ce fichier contient la liste de quelques radios, il peut être édité. Pour ajouter d’autres radios rendez-vous à : www.ecouter-en-direct.com/
Coller le fichier playlist radio.m3u dans /var/lib/mpd/playlists/
Le fichier de configuration de mdp est /etc/mdp.conf. On pourra y paramétrer les dossiers où MPD ira chercher la musique et les playlist par exemple. Une fois les réglages effectués, redémarrer le service : [shell]sudo /etc/init.d/mpd restart[/shell]

charger le fichier playlist : [shell]mpc load radio[/shell]

lancer la diffusion : [shell]mpc play 1[/shell]

Vidéos

La playlist suivante contient 5 vidéos :

  1. installation de MPD et MPC
  2. Affichage de la liste des radio (DahBoard node-red)
    Remarque : bien décocher les cases “Pass through…” et “Add output…” dans le template  de la liste des radios.
  3. Déclenchement de la lecture au clic sur un élément de la liste.
  4. Ajout de boutons de commandes
  5. Affichage du nom de la radio et du titre en cours

Annexe

Le fichier css pour la mise en forme du tableau:

table {
    color: #fff;
    font-family: Helvetica, Arial, sans-serif;
    width: 100%;
    border-collapse: collapse;
    border-spacing: 0;
}

td, th {
    border: 1px solid transparent;
    /* No more visible border */
    height: 30px;
    transition: all 0.3s;
    /* Simple transition for hover effect */
}

th {
    background: #DFDFDF;
    /* Darken header a bit */
    font-weight: bold;
}

td {
    background: #3a3a3a;
    text-align: center;
}

/* Cells in even rows (2,4,6...) are one color */

tr:nth-child(even) td {
    background: #313131;
}

/* Cells in odd rows (1,3,5...) are another (excludes header cells)  */

tr:nth-child(odd) td {
    background: #3e3e3e;
}

tr td:hover {
    background: #F1F1F1;
    color: #000;
}

 

  1. Voir : http://www.peyregne.info/wp/radio-internet-sur-raspberry-pi-partie-1-mpdmpc/

Interface de gestion de base de données

jsGrid est une bibliothèque jQuery qui permet de gérer un tableau de données et offre les options de CRUD (Create Read Update Delete) au travers de requêtes AJAX. L’objet de l’article consiste à mettre en œuvre ce composant en liaison avec une collection mongoDB, à travers l’interface de développement Node-Red.

Exemple de grille obtenue avec jsGrid

vidéos

La playlist suivante contient 5 vidéos :

  • Installation en local des fichiers scripts et CSS
  • Mise en œuvre de jsGrid à partir de l’exemple fournit sur le site de jsGrid dans une page structurée avec le framework BootStrap
  • Utilisation de la base de données afin de remplir la grille jsGrid avec les documents d’une collection.
  • Insertion et effacement d’un nouveau document dans la collection
  • Mise à jour d’un  document.

 

Mongodb et Node-red

Mongodb est un moteur de base de données noSQL (not only SQL) orienté documents. Ce genre de base de données est structurée autour de classeurs qu’on nomme “collections”, chaque collection contenant des documents. Les documents sont indexés afin de pouvoir y accéder rapidement. Les bases de données noSQL favorisent la vitesse d’accès aux données, au détriment parfois de la compacité. Nous verrons ici comment installer mongoDB sur Raspberry Pi, puis comment l’utiliser au sein de Node-Red.

Installation de mongoDB sur raspberry pi

(voir le document complet)

MongoDB et node-red

Installation du nœud mongodb2 et insertion d’un document dans une collection par injection :

Création d’un formulaire d’insertion de données à l’aide des nœuds Dashboard.

 

Installation de mongoDB et mongo-express

Pour mongoDB c’est plutôt simple :

sudo apt-get install mongodb

On peut vérifier que cela fonctionne :
Démarrage du service mongodb

$ sudo /etc/init.d/mongodb start

Le serveur mongo db démarre normalement à l’adresse 127.0.0.1 :27017

$ mongo

Ça lance une console

> use myDB
> db.LicencePlate.insert({})

Confirmation de l’existence de la base et de la collection :

> show dbs
> show collections

Mongo-express est une interface utilisateur permettant de manipuler mongoDB de manière visuelle. Il faut que node-red et npm soit installés (voir le document d’installation de node-red avec Johnny5)

sudo npm install -g mongo-express

Par défaut le login et le mot de passe sont admin et pass cela peut être changé en modifiant le fichier de configuration. Sur raspberry le fichier de configuration se trouve dans /usr/lib/node_modules/mongo-express

Il faut recopier en le renommant config.js  puis l’éditer:

sudo cp /usr/lib/node_modules/mongo-express/config.default.js /usr/lib/node_modules/mongo-express/config.js
sudo nano /usr/lib/node_modules/mongo-express/config.default.js

Si la base myDB a été créée (voir l’installation de mongodb et les commandes shell), il faut modifier le fichier config.js :

// Accessing Bluemix variable to get MongoDB info 
if (process.env.VCAP_SERVICES) {
    var dbLabel = 'mongodb-2.4';
    var env = JSON.parse(process.env.VCAP_SERVICES);
    if (env[dbLabel]) {
        mongo = env[dbLabel][0].credentials;
    }
} else {
    mongo = {
        db: 'myDB',
        host: 'localhost',
        // password: 'pass', 
        port: 27017,
        ssl: false,
        url: 'mongodb://localhost:27017/db',
        // username: 'admin',
    };
}

 

Si on veut accéder à l’interface utilisateur depuis un autre ordinateur que le raspberry, dans la section « site »,  il faut renseigner l’adresse IP du raspberry à la place de localhost et modifier le login et le mot de passe:

site: {
        // baseUrl: the URL that mongo express will be located at - Remember to add the forward slash at the start and end! 
        baseUrl: process.env.ME_CONFIG_SITE_BASEURL || '/',
        cookieKeyName: 'mongo-express',
        cookieSecret: process.env.ME_CONFIG_SITE_COOKIESECRET || 'cookiesecret',
        host: process.env.VCAP_APP_HOST || '0.0.0.0', //connexion quelle que soit l’adresse de la carte Raspberry 
        port: process.env.VCAP_APP_PORT || 8081, //port du serveur web requestSize
        Limit: process.env.ME_CONFIG_REQUEST_SIZE || '50mb',
        sessionSecret: process.env.ME_CONFIG_SITE_SESSIONSECRET || 'sessionsecret',
        sslCert: process.env.ME_CONFIG_SITE_SSL_CRT_PATH || '',
        sslEnabled: process.env.ME_CONFIG_SITE_SSL_ENABLED || false,
        sslKey: process.env.ME_CONFIG_SITE_SSL_KEY_PATH || '',
    },
    //set useBasicAuth to true if you want to authenticate mongo-express loggins 
    //if admin is false, the basicAuthInfo list below will be ignored 
    //this will be true unless ME_CONFIG_BASICAUTH_USERNAME is set and is the empty string 
    useBasicAuth: process.env.ME_CONFIG_BASICAUTH_USERNAME !== '',
    //useBasicAuth: false, 
    basicAuth: {
        username: process.env.ME_CONFIG_BASICAUTH_USERNAME || 'admin',
        password: process.env.ME_CONFIG_BASICAUTH_PASSWORD || 'pass',
    },

Ensuite on peut lancer mongo-express :

cd /usr/lib/node_modules/mongo-express/ && node app.js

ou plus simplement

$ mongo-express

Lancer un navigateur http://<adresse_IP_Raspberry>:8081 puis s’authentifier avec le l’identifiant et le mot de passe choisit précédemment :

Page d’accueil de mongo express Collections de la base myDB(cliquer sur « View » sur la page d’accueil pour voir les collections)
Documents de la collection « LicencePlate », on voit qu’une plaque d’immatriculation est inscrite (AV865XG) Cliquer sur « New Document » pour créer une nouvelle plaque d’immatriculation : ajouter “immatriculation”:”XX123YY”, avant “_id”: ObjectID() puis cliquer sur Save.

En cliquant sur le numéro de plaque on peut modifier le document, en cliquant sur le bouton rouge représentant une poubelle, on peut effacer ce document.

openALPR sur raspberry

OpenALPR est un logiciel de reconnaissance de plaque d’immatriculation. Ce logiciel s’appuie sur la bibliothèque openCV et le logiciel de reconnaissance de caractères tesseract.

Raspbian Jessie

On trouve sur les forums tout ce qu’il faut pour télécharger, compiler et installer ce package depuis les sources. L’inconvénient est que la compilation sur raspberry est très longue. Nous proposons ici une version packagée de openALPR, ce qui réduit considérablement le temps d’installation.

sudo apt-get update
sudo apt-get install liblog4cplus-1.0-4 beanstalkd libgtk2.0-0 libtiff5 libdc1394-22 libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev
wget https://www.rgot.org/wp-content/uploads/2016/12/openalpr.tgz
tar -zxvf openalpr.tgz
sudo dpkg -i openalpr_20161219-1-complet_armhf.deb
rm openalpr_20161219-1-complet_armhf.deb
rm openalpr.tgz

Pour déinstaller :

sudo dpkg -r openalpr

Pour Raspbian Stretch c’est plus simple :

il suffit de suivre les instructions disponibles sur le github de openalpr

sudo apt-get update && sudo apt-get install -y openalpr openalpr-daemon openalpr-utils libopenalpr-dev

Réinstallation de NODE-RED, RASPI-IO et JOHNNY-FIVE sur Raspberry Pi

La version intégrée de node-red permet de s’initier mais ne permet pas d’utiliser la bibliothèque raspi-io utilisée par le raspberry pour communiquer avec l’extérieur (problème de droits d’accès aux broches du raspi).
Pour pouvoir communiquer avec l’extérieur (capteurs, afficheurs, leds…) il faut donc réinstaller node.js et node-red.

Télécharger le fichier install_johnny5 , le rendre exécutable puis le lancer:

wget https://www.rgot.org/wp-content/uploads/2016/12/install_johnny5.txt
sudo chmod 755 install_johnny5.txt
./install_johnny5.txt

autre méthode : exécuter les lignes suivantes dans une console. (démarche issue de la note d’installation de node-red : http://nodered.org/docs/hardware/raspberrypi )

sudo apt-get update
sudo apt-get remove nodered
sudo apt-get remove nodejs nodejs-legacy
curl -sL https://deb.nodesource.com/setup_4.x | sudo bash -
sudo apt-get install -y build-essential python-rpi.gpio nodejs
sudo npm cache clean
sudo npm install -g --unsafe-perm node-red
sudo apt-get update && sudo apt-get install python-rpi.gpio
sudo wget https://raw.githubusercontent.com/node-red/raspbian-deb-package/master/resources/nodered.service -O /lib/systemd/system/nodered.service
sudo wget https://raw.githubusercontent.com/node-red/raspbian-deb-package/master/resources/node-red-start -O /usr/bin/node-red-start
sudo wget https://raw.githubusercontent.com/node-red/raspbian-deb-package/master/resources/node-red-stop -O /usr/bin/node-red-stop
sudo chmod +x /usr/bin/node-red-st*
sudo systemctl daemon-reload
sudo npm i -g npm@2.x

Pour les raspi 2 et 3 il faut aussi exécuter les lignes suivantes pour installer raspi-io (gestion des entrées/sorties) et johnny5 (bibliothèque permettant la programmation d’automates)

sudo npm install -g johnny-five --unsafe-perm --force
sudo npm install -g raspi-io --unsafe-perm --force
sudo npm install -g node-red-contrib-gpio --unsafe-perm --force
sudo npm install serialport --unsafe-perm --build-from-source
sudo npm i -g oled-js oled-font-5x7

Éditer le fichier /lib/systemd/system/nodered.serviceet remplacer USER=pi par USER=root
ainsi la commande node-red-start lancera node-red en mode root (dans le dossier /root/.node-red )

Activer SSH au premier boot d’une carte Raspberry Pi

Depuis la dernière version de Raspbian Jessie (nov 2016) ssh est déactivé par défaut. Il faut donc se connecter via l’interface graphique et activer ssh dans la configuration de raspberry ( bouton framboise -> Préférences -> configuration du Raspberry Pi puis dans l’onglet “Interfaces” cocher SSH)
Si on possède une carte Raspberry qui n’est pas connectée à un ensemble clavier/souris/écran, il semble impossible de se connecter…
La solution consiste à ajouter un fichier (vide) nommé ssh dans la partition boot (c’est la partition visible lorsque la carte SD est introduite dans un PC Windows).
Au premier boot Raspberry détecte le fichier ssh, active le protocole ssh et efface le fichier ssh sur la partition boot.