Accueil / Domotique / Node-RED+MySensors : stocker des données dans une base MongoDB

Node-RED+MySensors : stocker des données dans une base MongoDB

Il est très facile d’enregistrer dans une base de données MongoDB des mesures provenant d’un capteur connecté à l’aide de Node-RED. Lorsqu’on bricole ses objets connectés, il est assez difficile d’imaginer la structure de la base de données adaptée à ses projets. MongoDB permet de résoudre ce problème car on stocke directement les données sous la forme d’un document (au format JSON clé:valeur) sans avoir à se soucier des évolutions futures (nouvelle mesures, suppression d’un capteur…). Pour illustrer le fonctionnement de MongoDB, nous allons récupérer dans un flow Node-RED les mesures d’une sonde de température DHT22 reliée à un Raspberry PI par ondes radio.

Si vous découvrez MongoDB, vous pouvez commencer par ces articles d’introduction.

Préparer une sonde de température avec MySensors

Pour réaliser ce tutoriel, vous aurez besoin

  • D’une sonde de température à base de DHT11 ou DHT22. Suivez les étapes rapides ci-dessous ou lisez cet article plus détaillé si vous débutez.
  • Une gateway réseau MySensors (LAN Gateway) ou une gateway série. Pour cet article, nous utiliserons une gateway réseau ESP8266. Pour en savoir plus lisez cet article.

MySensors v2 : décoder/envoyer des messages avec Node-RED

Matériel nécessaire

arduino nano v3 atmega328p Arduino Nano (conseillé)
rflink nrf24l01 Module radio nRF24L01

Portée standard ou longue portée (1000m ou environnement difficile)

adaptateur cablage nRF24L01 Adaptateur avec régulateur de tension 3V3 pour module antenne nRF24L01 ou nRF24L01 + PA + LNA
Broches DHT22 Capteur de température et d’humidité

DHT11 ou DHT22

jumper dupont Jumper Dupont
breadboard Breadboard

Circuit

mysensors arduino micro dht22 nrf24l01

Code

Téléversez le code suivant dans l’Arduino Nano.

/**
 * The MySensors Arduino library handles the wireless radio link and protocol
 * between your home built sensors/actuators and HA controller of choice.
 * The sensors forms a self healing radio network with optional repeaters. Each
 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
 * network topology allowing messages to be routed to nodes.
 *
 * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
 * Copyright (C) 2013-2015 Sensnology AB
 * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
 *
 * Documentation: http://www.mysensors.org
 * Support Forum: http://forum.mysensors.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 *******************************
 *
 * REVISION HISTORY
 * Version 1.0 - PROJETS DIY
 * 
 * DESCRIPTION
 * Example sketch showing how to measue light level using a LM393 photo-resistor 
 * http://www.mysensors.org/build/light
 */

// Active le mode debug / Enable debug prints to serial monitor
#define MY_DEBUG 

// Mode Radio / Enable and select radio type attached
#define MY_RADIO_NRF24
//#define MY_RADIO_RFM69

#define MY_RF24_PA_LEVEL RF24_PA_LOW

// Noeud de l'objet / Object Node
#define MY_NODE_ID 3

#include <SPI.h>
#include <MySensors.h>  
#include <DHT.h> 

#define CHILD_ID_TEMP 0  
#define CHILD_ID_HUM 1
#define SLEEP_NODE true // Mettre à True pour activer la mise en sommeil (Sleep Mode) / True to activate Sleep Mode
unsigned long SLEEP_TIME = 10 * 1000; // Temps de mise en sommeil (en ms) / Sleep time between reads (in milliseconds)

#define DHTPIN 3        // Broche sur laquelle est branché le DHT / what pin we're connected to
#define DHTTYPE DHT22   // DHT 22  (AM2302), DHT11

float lastTemp;
float lastHum;
boolean metric = true; 
MyMessage msgHum(CHILD_ID_HUM, V_HUM);
MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);

void setup() { 
}

void presentation()  {
  // Présenttion du sketch / Send the sketch version information to the gateway and Controller
  sendSketchInfo("Sonde Temp/Hum DHT22", "1.0");

  // Déclaration des capteurs attachés au noeud
  present(CHILD_ID_TEMP, S_TEMP);
  present(CHILD_ID_HUM, S_HUM);
}

void loop()      
{    
  DHT dht(DHTPIN, DHTTYPE,3); // Creation d'un objet dht dans la loop sinon les valeurs ne sont pas mesurées au réveil
  float temperature = dht.readTemperature();
  float humidity = dht.readHumidity();
  if (isnan(temperature)) {
      Serial.println("Impossible de lire la temperature sur le DHT");
  } else {
    if (temperature != lastTemp) {
      lastTemp = temperature;
      if (!metric) {
        temperature = dht.readTemperature(true);
      }
      
      Serial.print("T: ");
      Serial.print(temperature);
      Serial.print(" | H: ");
      Serial.println(humidity);
      send(msgTemp.set(temperature, 1));
      send(msgHum.set(humidity, 1));
    } else {
      Serial.println("Temperature identique => on force l'envoi");  
      Serial.print("T: ");
      Serial.print(temperature);
      Serial.print(" | H: ");
      Serial.println(humidity);
      send(msgTemp.set(temperature, 1));
      send(msgHum.set(humidity, 1));
    }
  }  
  
  if (SLEEP_NODE) {
    Serial.println("Sleep");
    sleep(SLEEP_TIME);
  } else {
    delay (SLEEP_TIME);
  }
}

Connexion à la gateway MySensors

node-red tcp mysensors lan gateway Ajoutez un Node TCP et configurez la connexion ainsi :

  • Type : Connect to
  • Port : 5003 par défaut (à adapter à votre configuration)
  • at host : l’adresse ip de la gateway réseau.
  • output : stream of … String
  • Donnez un nom
  • Enregistrez avec Done

node-red tcp mysensors lan gateway settings parametres

Décoder les messages envoyés par la sonde MySensors

Maintenant collez ce flow dans votre projet et reliez le à la sortie de la gateway pour pouvoir décoder les messages envoyés par la sonde DHT22 (http://flows.nodered.org/flow/fa02078c160cb3e00e09f4980b534490). En sortie ce Node renvoi un objet JSON plus facile à manipuler.

[{"id":"f4fd4940.32ba8","type":"function","z":"eb4ead14.fd77f","name":"Decode MySensor Message","func":"/* MySensors v2 Message Decoder\n*  Payload : JSON object\n*  www.projetsdiy.fr - oct. 2016\n*/\nvar mySensorsMessage = {}\nvar newPayload = {};\nvar message = msg.payload.toString();\nmessage = message.replace(/(\\r\\n|\\n|\\r)/gm, \"\");\nvar tokens = message.split(\";\")\nif(tokens.length == 6)\n{\n    mySensorsMessage.nodeId =       parseInt(tokens[0]);\n    mySensorsMessage.childSensorId= parseInt(tokens[1]);\n    mySensorsMessage.messageType =  parseInt(tokens[2]);\n    mySensorsMessage.ack =          parseInt(tokens[3]);\n    mySensorsMessage.subType =      parseInt(tokens[4]);\n    mySensorsMessage.value =        Number(tokens[5]);\n\n    var messageType = mySensorsMessage.messageType;\n    var subType = mySensorsMessage.subType;\n    var labelPresentation = [\"S_DOOR\",\"S_MOTION\",\"S_SMOKE\",\"S_LIGHT\",\"S_BINARY\",\"S_DIMMER\",\"S_COVER\",\"S_TEMP\",\"S_HUM\",\"S_BARO\",\"S_WIND\",\"S_RAIN\",\"S_UV\",\"S_WEIGHT\",\"S_POWER\",\"S_HEATER\",\"S_DISTANCE\",\"S_LIGHT_LEVEL\",\"S_ARDUINO_NODE\",\"S_ARDUINO_REPEATER_NODE\",\"S_LOCK\",\"S_IR\",\"S_WATER\",\"S_AIR_QUALITY\",\"S_CUSTOM\",\"S_DUST\",\"S_SCENE_CONTROLLER\",\"S_RGB_LIGHT\",\"S_RGBW_LIGHT\",\"S_COLOR_SENSOR\",\"S_HVAC\",\"S_MULTIMETER\",\"S_SPRINKLER\",\"S_WATER_LEAK\",\"S_SOUND\",\"S_VIBRATION\",\"S_MOISTURE\",\"S_INFO\",\"S_GAS\",\"S_GPS\",\"S_WATER_QUALITY\"];\n    var labelSet = [\"V_TEMP\",\"V_HUM\",\"V_STATUS\",\"V_LIGHT\",\"V_PERCENTAGE\",\"V_DIMMER\",\"V_PRESSURE\",\"V_FORECAST\",\"V_RAIN\",\"V_RAINRATE\",\"V_WIND\",\"V_GUST\",\"V_DIRECTION\",\"V_UV\",\"V_WEIGHT\",\"V_DISTANCE\",\"V_IMPEDANCE\",\"V_ARMED\",\"V_TRIPPED\",\"V_WATT\",\"V_KWH\",\"V_SCENE_ON\",\"V_SCENE_OFF\",\"V_HVAC_FLOW_STATE\",\"V_HVAC_SPEED\",\"V_LIGHT_LEVEL\",\"V_VAR1\",\"V_VAR2\",\"V_VAR3\",\"V_VAR4\",\"V_VAR5\",\"V_UP\",\"V_DOWN\",\"V_STOP\",\"V_IR_SEND\",\"V_IR_RECEIVE\",\"V_FLOW\",\"V_VOLUME\",\"V_LOCK_STATUS\",\"V_LEVEL\",\"V_VOLTAGE\",\"V_CURRENT\",\"V_RGB\",\"V_RGBW\",\"V_ID\",\"V_UNIT_PREFIX\",\"V_HVAC_SETPOINT_COOL\",\"V_HVAC_SETPOINT_HEAT\",\"V_HVAC_FLOW_MODE\",\"V_TEXT\",\"V_CUSTOM\",\"V_POSITION\",\"V_IR_RECORD\",\"V_PH\",\"V_ORP\",\"V_EC\",\"V_VAR\",\"V_VA\",\"V_POWER_FACTOR\"]\n    var labelInternal = [\"I_BATTERY_LEVEL\",\"I_TIME\",\"I_VERSION\",\"I_ID_REQUEST\",\"I_ID_RESPONSE\",\"I_INCLUSION_MODE\",\"I_CONFIG\",\"I_FIND_PARENT\",\"I_FIND_PARENT_RESPONSE\",\"I_LOG_MESSAGE\",\"I_CHILDREN\",\"I_SKETCH_NAME\",\"I_SKETCH_VERSION\",\"I_REBOOT\",\"I_GATEWAY_READY\",\"I_REQUEST_SIGNING\",\"I_GET_NONCE\",\"I_GET_NONCE_RESPONSE\",\"I_HEARTBEAT\",\"I_PRESENTATION\",\"I_DISCOVER\",\"I_DISCOVER_RESPONSE\",\"I_HEARTBEAT_RESPONSE\",\"I_LOCKED\",\"I_PING\",\"I_PONG\",\"I_REGISTRATION_REQUEST\",\"I_REGISTRATION_RESPONSE\",\"I_DEBUG\"]\n    \n    switch (messageType) {\n        case 0:     // Presentation\n            \n            newPayload.mode =       \"Presentation\";\n            newPayload.type =       labelPresentation[subType];\n            break;\n        case 1:     // Set\n            newPayload.nodeId=      mySensorsMessage.nodeId;\n            newPayload.sensorId=    mySensorsMessage.childSensorId;\n            newPayload.mode=        \"Set\";\n            newPayload.type=        subType;\n            newPayload.typeLabel=   labelSet[subType];\n            newPayload.value=       mySensorsMessage.value;\n            break;\n        case 2:     // Req\n            newPayload.nodeId=      mySensorsMessage.nodeId;\n            newPayload.sensorId=    mySensorsMessage.childSensorId;\n            newPayload.mode=        \"Req\";\n            newPayload.type=        subType;\n            newPayload.typeLabel=   labelSet[subType];\n            newPayload.value=       mySensorsMessage.value;\n            break;  \n        case 3:     // Internal\n            newPayload.nodeId=      mySensorsMessage.nodeId;\n            newPayload.sensorId=    mySensorsMessage.childSensorId;\n            newPayload.mode=        \"Internal\";\n            newPayload.type=        subType;\n            newPayload.typeLabel=   labelInternal[subType];\n            newPayload.value=       mySensorsMessage.value;\n            break;    \n        case 4:     // Stream - OTA firmware update\n            newPayload.nodeId=      mySensorsMessage.nodeId;\n            newPayload.mode=        \"stream\";\n            break;\n        default:\n            break;\n    }\n\n    msg.payload = newPayload; \n} else {\n    msg.payload = \"Error! Nothing to decode\"\n}  \n\nreturn msg;","outputs":1,"noerr":0,"x":394.5555419921875,"y":279.5555725097656,"wires":[["22cb3401.ef7e24","2d12c5a2.7304aa","fd30fa09.244ce8"]]}]
node-red mysensors lan gateway message decode
Node-RED est connecté à la Gateway MySensors réseau. Les messages sont décodés en temps réel.

Les modules MongoDB pour Node-RED disponibles

node-red library mongodbModules MongoDB disponibles dans la librairie au moment de l’écriture de l’article.

Il existe plusieurs modules MongoDB pour Node-RED :

  • node-red-node-mongodb : le connecteur Node-RED pour MongoDB par défaut
  • node-red-contrib-mongodb2 : un autre connecteur (non testé)
  • node-red-contrib-objectid : permet de générer un identifiant unique permettant d’attribuer son propre _id plutôt que de laisser MongoDB le faire. Ca peut être utile pour lier deux documents entre eux.
  • node-red-bluemix-node : un connecteur pour le cloud IBM Bluemix qui comporte un connecteur MongoDB permettant de réaliser les opérations courantes :
    • Save, insert, update ou remove
    • Find, count, aggregate

Pour ce tutoriel, vous aurez besoin d’installer le module node-red-node-mongodb. Depuis la version 0.15.1, on peut installer et désinstaller les modules (vous pouvez lire cet article pour en savoir plus).

node-red install mongodb module

Présentation du module MongoDB : exemple

Après installation (et actualisation du navigateur), la palette Storage contient deux nouveaux Nodes. Le premier permet d’écriture dans la base de données. Le seconde de lire. C’est tout.
node-red-storage-mongodb-palette

Enregistrer un document (Node Out)

Ajoutez un Node out sur le flow et éditez le. Appuyez sur le crayon à coté de Server pour ouvrir le panneau de configuration puis saisissez vos paramètres :

  • Host : l’adresse ip de la base MongoDB ou 127.0.0.1 si c’est une base locale (sur le même ordinateur)
  • Port : par défaut c’est le 27017
  • Database : le nom de la base de données sur laquelle on va enregistrer les données. Si elle n’existe pas, elle sera automatiquement créée par MongoDB au premier enregistrement (insert, save).
  • Username, password : si vous avez mis en place un accès sécurisé à la base
  • Name : un nom au Node

node-red mongodb configure connexion host

Enregistrez la nouvelle connexion en appuyant sur Update (ou Done).

node-red-mongodb-operation-save-insert-update-removeIl existe 4 opérations possibles

  • Save
  • Insert
  • Update
  • Remove

 

Nous n’allons pas voir en détail la différence entre ces opérations, je vous renvoi vers cet article qui décortique en détail les principales commandes MongoDB.

Comme on souhaite stocker chaque nouvelle mesure envoyée par la sonde, nous utiliserons l’opération Insert. Pour séparer les types de mesure (température, humidité…), on indique une collection. Dans ce cas, la collection temperature contiendra tous les mesures de température reçues. Cochez Only store msg.payload object pour n’enregistrer que la sortie de la fonction (msg.payload).

node-red mongodb out node insert msg payload only

Terminez en cliquant sur Done.

Exemple : enregistrer la température publiée par une sonde DHT22 MySensors

Il ne nous reste plus qu’à extraire les mesures de température en provenance d’une sonde MySensors et l’envoyer vers le Node MongoDB Out.

Ajoutez un Node function et collez ce code.

if (msg.payload.nodeId == 3 && msg.payload.type === 0) {
    msg.payload = {
        date : msg.payload.timestamp,
        value: msg.payload.value
    }    
    return msg;
}

node-red mongodb filtre temperature dht22 mysensors

Que fait ce code ?

On rentre dans la condition uniquement si le nodeId correspond au capteur désiré (ici 3) et que si le type de mesure est 0 (V_TEMP, température).

On créé alors un objet JSON contenant :

  • La date : recopiée de msg.payload.timestamp
  • La mesure (value) :  recopiée de msg.payload.value

En sortie on récupère un objet JSON qu’il suffit de brancher au Node MongoDB pour l’insérer dans la collection temperature.

https://i2.wp.com/www.projetsdiy.fr/wp-content/uploads/2016/11/11-node-red-mongodb-flow-complet.png?resize=660%2C198
Déployez le Flow et attendez la réception des premières mesures qui s’afficheront dans le debug.

node-red insert document debug

Code du flow

Collez directement ce code pour récupérer directement cet exercices dans votre Flow.

[{"id":"e8737ba4.f46b78","type":"tcp in","z":"adaea2ae.4fd6e","name":"MySensors Gateway","server":"client","host":"192.168.1.20","port":"5003","datamode":"stream","datatype":"utf8","newline":"","topic":"","base64":false,"x":537,"y":414,"wires":[["996a086d.8218e8"]]},{"id":"996a086d.8218e8","type":"function","z":"adaea2ae.4fd6e","name":"Decode MySensor Message","func":"/* MySensors v2 Message Decoder\n*  Payload : JSON object\n*  www.projetsdiy.fr - oct. 2016\n*/\nvar mySensorsMessage = {}\nvar newPayload = {};\nvar timestamp = new Date();\nvar message = msg.payload.toString();\nmessage = message.replace(/(\\r\\n|\\n|\\r)/gm, \"\");\nvar tokens = message.split(\";\")\nif(tokens.length == 6)\n{\n    mySensorsMessage.nodeId =       parseInt(tokens[0]);\n    mySensorsMessage.childSensorId= parseInt(tokens[1]);\n    mySensorsMessage.messageType =  parseInt(tokens[2]);\n    mySensorsMessage.ack =          parseInt(tokens[3]);\n    mySensorsMessage.subType =      parseInt(tokens[4]);\n    mySensorsMessage.value =        Number(tokens[5]);\n\n    var messageType = mySensorsMessage.messageType;\n    var subType = mySensorsMessage.subType;\n    var labelPresentation = [\"S_DOOR\",\"S_MOTION\",\"S_SMOKE\",\"S_LIGHT\",\"S_BINARY\",\"S_DIMMER\",\"S_COVER\",\"S_TEMP\",\"S_HUM\",\"S_BARO\",\"S_WIND\",\"S_RAIN\",\"S_UV\",\"S_WEIGHT\",\"S_POWER\",\"S_HEATER\",\"S_DISTANCE\",\"S_LIGHT_LEVEL\",\"S_ARDUINO_NODE\",\"S_ARDUINO_REPEATER_NODE\",\"S_LOCK\",\"S_IR\",\"S_WATER\",\"S_AIR_QUALITY\",\"S_CUSTOM\",\"S_DUST\",\"S_SCENE_CONTROLLER\",\"S_RGB_LIGHT\",\"S_RGBW_LIGHT\",\"S_COLOR_SENSOR\",\"S_HVAC\",\"S_MULTIMETER\",\"S_SPRINKLER\",\"S_WATER_LEAK\",\"S_SOUND\",\"S_VIBRATION\",\"S_MOISTURE\",\"S_INFO\",\"S_GAS\",\"S_GPS\",\"S_WATER_QUALITY\"];\n    var labelSet = [\"V_TEMP\",\"V_HUM\",\"V_STATUS\",\"V_LIGHT\",\"V_PERCENTAGE\",\"V_DIMMER\",\"V_PRESSURE\",\"V_FORECAST\",\"V_RAIN\",\"V_RAINRATE\",\"V_WIND\",\"V_GUST\",\"V_DIRECTION\",\"V_UV\",\"V_WEIGHT\",\"V_DISTANCE\",\"V_IMPEDANCE\",\"V_ARMED\",\"V_TRIPPED\",\"V_WATT\",\"V_KWH\",\"V_SCENE_ON\",\"V_SCENE_OFF\",\"V_HVAC_FLOW_STATE\",\"V_HVAC_SPEED\",\"V_LIGHT_LEVEL\",\"V_VAR1\",\"V_VAR2\",\"V_VAR3\",\"V_VAR4\",\"V_VAR5\",\"V_UP\",\"V_DOWN\",\"V_STOP\",\"V_IR_SEND\",\"V_IR_RECEIVE\",\"V_FLOW\",\"V_VOLUME\",\"V_LOCK_STATUS\",\"V_LEVEL\",\"V_VOLTAGE\",\"V_CURRENT\",\"V_RGB\",\"V_RGBW\",\"V_ID\",\"V_UNIT_PREFIX\",\"V_HVAC_SETPOINT_COOL\",\"V_HVAC_SETPOINT_HEAT\",\"V_HVAC_FLOW_MODE\",\"V_TEXT\",\"V_CUSTOM\",\"V_POSITION\",\"V_IR_RECORD\",\"V_PH\",\"V_ORP\",\"V_EC\",\"V_VAR\",\"V_VA\",\"V_POWER_FACTOR\"]\n    var labelInternal = [\"I_BATTERY_LEVEL\",\"I_TIME\",\"I_VERSION\",\"I_ID_REQUEST\",\"I_ID_RESPONSE\",\"I_INCLUSION_MODE\",\"I_CONFIG\",\"I_FIND_PARENT\",\"I_FIND_PARENT_RESPONSE\",\"I_LOG_MESSAGE\",\"I_CHILDREN\",\"I_SKETCH_NAME\",\"I_SKETCH_VERSION\",\"I_REBOOT\",\"I_GATEWAY_READY\",\"I_REQUEST_SIGNING\",\"I_GET_NONCE\",\"I_GET_NONCE_RESPONSE\",\"I_HEARTBEAT\",\"I_PRESENTATION\",\"I_DISCOVER\",\"I_DISCOVER_RESPONSE\",\"I_HEARTBEAT_RESPONSE\",\"I_LOCKED\",\"I_PING\",\"I_PONG\",\"I_REGISTRATION_REQUEST\",\"I_REGISTRATION_RESPONSE\",\"I_DEBUG\"]\n    \n    switch (messageType) {\n        case 0:     // Presentation\n            newPayload.timestamp = timestamp;\n            newPayload.mode =       \"Presentation\";\n            newPayload.type =       labelPresentation[subType];\n            break;\n        case 1:     // Set\n            newPayload.timestamp = timestamp;\n            newPayload.nodeId=      mySensorsMessage.nodeId;\n            newPayload.sensorId=    mySensorsMessage.childSensorId;\n            newPayload.mode=        \"Set\";\n            newPayload.type=        subType;\n            newPayload.typeLabel=   labelSet[subType];\n            newPayload.value=       mySensorsMessage.value;\n            break;\n        case 2:     // Req\n            newPayload.timestamp = timestamp;\n            newPayload.nodeId=      mySensorsMessage.nodeId;\n            newPayload.sensorId=    mySensorsMessage.childSensorId;\n            newPayload.mode=        \"Req\";\n            newPayload.type=        subType;\n            newPayload.typeLabel=   labelSet[subType];\n            newPayload.value=       mySensorsMessage.value;\n            break;  \n        case 3:     // Internal\n            newPayload.timestamp = timestamp;\n            newPayload.nodeId=      mySensorsMessage.nodeId;\n            newPayload.sensorId=    mySensorsMessage.childSensorId;\n            newPayload.mode=        \"Internal\";\n            newPayload.type=        subType;\n            newPayload.typeLabel=   labelInternal[subType];\n            newPayload.value=       mySensorsMessage.value;\n            break;    \n        case 4:     // Stream - OTA firmware update\n            newPayload.timestamp = timestamp;\n            newPayload.nodeId=      mySensorsMessage.nodeId;\n            newPayload.mode=        \"stream\";\n            break;\n        default:\n            break;\n    }\n\n    msg.payload = newPayload; \n} else {\n    msg.payload = \"Error! Nothing to decode\"\n}  \n\nreturn msg;","outputs":1,"noerr":0,"x":722.6666717529297,"y":503.6278991699219,"wires":[["955ef8ed.fc3088"]]},{"id":"955ef8ed.fc3088","type":"function","z":"adaea2ae.4fd6e","name":"Filtre : temperature noeud 3","func":"if (msg.payload.nodeId == 3 && msg.payload.type === 0) {\n    msg.payload = {\n        date : msg.payload.timestamp,\n        value: msg.payload.value\n    }    \n    return msg;\n}    ","outputs":1,"noerr":0,"x":1010,"y":573,"wires":[["7bdfb6cf.eb6908","5389b2c9.73042c"]]},{"id":"7bdfb6cf.eb6908","type":"mongodb out","z":"adaea2ae.4fd6e","mongodb":"30911156.5127be","name":"Insert to MongoDB","collection":"temperature","payonly":true,"upsert":false,"multi":false,"operation":"insert","x":1276.5,"y":617,"wires":[]},{"id":"5389b2c9.73042c","type":"debug","z":"adaea2ae.4fd6e","name":"-> MongoDB","active":true,"console":"false","complete":"payload","x":1252,"y":547,"wires":[]},{"id":"30911156.5127be","type":"mongodb","z":"","hostname":"127.0.0.1","port":"27017","db":"mysensors","name":""}]

Rechercher des documents (Node In)

Avant d’utiliser le Node In, voyons un peu se qui se passe du coté de MongoDB. Ouvrez un Terminal et ouvrez un client avec la commande mongo.

On change pour la base de données mysensors.

> use mysensors
switched to db mysensors
>

Maintenant on récupère les 5 derniers documents insérés dans la collection temperature. Explication de la commande :

  • .find() : méthode de recherche
  • .limit(5) : ne retourne que 5 enregistrements au maximum
  • .sort({« date »:-1}) : trie du plus récent au plus ancien, 1 pour inverser le trie
  • .pretty() : pour un affichage JSON déplié
> db.temperature.find().limit(5).sort({"date":-1})
{ "_id" : ObjectId("58204ca0d35cec114c0066d8"), "date" : ISODate("2016-11-07T09:42:56.171Z"), "value" : 21.1 }
{ "_id" : ObjectId("58204c95d35cec114c0066d7"), "date" : ISODate("2016-11-07T09:42:45.727Z"), "value" : 21.1 }
{ "_id" : ObjectId("58204c8bd35cec114c0066d6"), "date" : ISODate("2016-11-07T09:42:35.282Z"), "value" : 21.1 }
{ "_id" : ObjectId("58204c80d35cec114c0066d5"), "date" : ISODate("2016-11-07T09:42:24.733Z"), "value" : 21.1 }
{ "_id" : ObjectId("58204c76d35cec114c0066d4"), "date" : ISODate("2016-11-07T09:42:14.290Z"), "value" : 21.2 }
>

Maintenant, faisons de même avec Node-RED.

Pour extraire à intervalle régulier les derniers enregistrement, nous aurons besoins d’un Node Inject pour déclencher à intervalle régulier une requête à la base de données. Voici un exemple de configuration qui permet d’extraire les derniers enregistrements toutes les 5 secondes :

node-red mngodb inject interval repeatIl faut passer par une fonction pour configurer plusieurs paramètres (obligatoires) de la méthode find(). Dans cet exemple, on limite la récupération à 5 documents, triés du plus récent au plus ancien. On de passe pas les premiers enregistrements trouvés

msg.limit = 5; //obligatoire
msg.skip = 0;
msg.sort = {"date":-1}
return msg;

node-red mongodb limit skip sort function find

node-red-mongodb-find-cound-aggregateEnfin on relie la fonction à un Node In. Le Node In se configure comme le Node Out. On choisit la connexion au serveur puis le type d’opération :

  • find()
  • count()
  • aggregate()

node-red mongodb in find

Ce qui donne ce flow une fois terminé.

node-red mongodb find flow last 5 records every 5 secondes

Branchez un Node debug pour visualiser les documents trouvés.

node-red mongodb debug 5 derniers enregistrementsCode du flow

[{"id":"9fb085ad.672348","type":"debug","z":"adaea2ae.4fd6e","name":"","active":true,"console":"false","complete":"false","x":1120.5,"y":866,"wires":[]},{"id":"830c5513.e05c68","type":"inject","z":"adaea2ae.4fd6e","name":"","topic":"","payload":"","payloadType":"date","repeat":"5","crontab":"","once":false,"x":542.4999694824219,"y":723,"wires":[["25ac7592.17678a"]]},{"id":"25ac7592.17678a","type":"function","z":"adaea2ae.4fd6e","name":"Last 5 records","func":"msg.limit = 5;\nmsg.skip = 0;\nmsg.sort = {timestamp:-1}\nreturn msg;","outputs":1,"noerr":0,"x":677.5,"y":788,"wires":[["5e5af488.06fd6c"]]},{"id":"5e5af488.06fd6c","type":"mongodb in","z":"adaea2ae.4fd6e","mongodb":"30911156.5127be","name":"find In MongoDB","collection":"temperature","operation":"find","x":892.5,"y":839,"wires":[["9fb085ad.672348"]]},{"id":"30911156.5127be","type":"mongodb","z":"","hostname":"127.0.0.1","port":"27017","db":"mysensors","name":""}]