Utiliser Node-RED pour créer une passerelle entre MySensors et Blynk (serveur local) • Domotique et objets connectés à faire soi-même

zch1kgpkr3f3zvxk9nru-4857133

Blynk est génial pour utiliser et commander ses objets connectés depuis son smartphone iOS ou Android. Si vous avez développé des objets connectés à l’aide de la librairie MySensors pour communiquer à longue distance par ondes radio, il est très facile de renvoyer les données à l’aide de Node-RED. Ce tutoriel ne traite que de l’envoi de données en provenance d’un sonde MySensors. Nous verrons dans un prochain tutoriel comment piloter des actionneurs MySensors depuis l’application Blynk.

Avant de commencer

Si vous ne disposez pas encore de Node-RED, suivez le tutoriel qui correspondant à votre environnement :

Vous pouvez également lire ce tutoriel pour apprendre comment gérer simplement les modules.

Installer le module node-red-contrib-blynk-websockets pour Node-RED

Ouvrez le gestionnaire de palette et faites une recherche sur le mot clé Blynk. Vous devriez obtenir 2 modules :

Le premier plugin (node-red-contrib-blynk) est maintenant obsolète. Il fonctionne toujours mais ne recevra plus de mise à jour. Installez le 2nd plugin node-red-contrib-blynk-websockets. Une fois l’installation terminée, vous aurez une nouvelle palette composée de 4 outils

  • read event : permet de récupérer les événements de lecture
  • write event : permet de récupérer les événements d’écriture
  • write : permet de publier quelque chose sur Blynk
  • email : permet d’utiliser un email par l’intermédiaire du serveur Blynk

Au moment de l’écriture de cet article, le plugin n’est pas encore documenté. J’ai du un peu chercher. Il faudra m’excuser s’il y a des erreurs. En tout cas n’hésitez pas à partager si vous avez d’autres infos que les miennes.

Utiliser Node-RED comme passerelle entre MySensors et Blynk

Node-RED se prête très bien à ce jeu. A part décoder les messages (le flow se trouve plus bas) en provenance de la Gateway MySensors, il n’y aura aucune programmation. Voici comment nous allons faire :

  • Le capteur transmet les mesures par ondes radio (2.4GHz) à la Gateway MySensors (suivez ce tutoriel pour fabriquer une gateway réseau WiFi).
  • On se connecte à la gateway MySensors réseau depuis Node-RED à l’aide du flow TCP/IP.
  • On décode les messages MySensors
  • On envoi chaque mesure sur le Pin Virtuels du projet Blynk
  • Le serveur Blynk reçoit les données
  • L’application mobile se connecte au serveur Blynk pour récupérer les mesures

Connexion à la Gateway MySensors

Pour se connecter à une passerelle réseau, nous allons utiliser le Node TCP

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

Décoder les messages MySensors

Dans ce tutoriel précédent, nous avons vu comment décoder à la volée les messages reçus par la gateway MySensors. Nous allons l’utiliser de nouveau car on doit envoyer les données individuellement à Blynk. Collez le flow de décodeur dans votre projet Node-RED et connectez le àau Node TCP précédemment créé.

[{"id":"61bbd468.bddf9c","type":"tcp in","z":"eb4ead14.fd77f","name":"MySensors Gateway","server":"client","host":"192.168.1.20","port":"5003","datamode":"stream","datatype":"utf8","newline":"","topic":"","base64":false,"x":169.88886260986328,"y":226.92767333984375,"wires":[["f4fd4940.32ba8"]]},{"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"]]},{"id":"fd30fa09.244ce8","type":"debug","z":"eb4ead14.fd77f","name":"","active":true,"console":"false","complete":"false","x":628.5,"y":235,"wires":[]}]

Mise en place de la passerelle

Maintenant que les messages sont décodés, on va pouvoir extraire ce qui nous intéresse et l’envoyer à Blynk. Pour cela, vous devez récupérer les informations suivantes :

  • Le Token du projet sur lequel vous voulez publier les données
  • Les Pins virtuels sur lesquels envoyer les données. Ici, ce sera V0 pour la température et V1 pour l’humidité
  • L’adresse IP du serveur Blynk et le port websocket

Node-RED va communiquer en Websocket avec le serveur Blynk. Pour une liaison non sécurisé, le port par défaut est le 8082.

ws://IP_BLYNK_SERVER:8082/websocket

Si vous disposez de votre propre serveur Blynk local, remplacez IP_BLYNK_SERVER par l’adresse IP. Si vous utilisé votre compte Blynk officiel, le serveur est cloud.blynk.cc .

Enfin si vous avez mis en place un certificat SSL, vous pouvez sécuriser les échanges avec Node-RED en vous connectant sur le websocket

wss://IP_BLYNK_SERVER:9443/websocket

Vous pouvez modifier les ports dans le fichier de configuration server.properties de Blynk

Dans la palette Node-RED, faites un recherche sur le mot clé Blynk. Placez le Node write sur le flow

Appuyez sur le crayon à droite de la liste de sélection pour ajouter une connexion

  • Url : l’url du websocket
  • Project key : le token vers lequel les données doivent être envoyées
  • Name : donnez un nom à la connexion
  • Enregistrez la configuration

De retour dans le Node write :

  • Sélectionnez la connexion dans la liste.
  • Indiquez le Pin virtuel (juste le nombre).
  • Donnez un nom
  • Enregistrez

Faites de même pour l’humidité. Branchez le Node Write sur le filtre d’extraction de la mesure. Voici ce que vous devez obtenir

Le code complet du flow

[{"id":"eb4ba8fd.56e7b8","type":"tcp in","z":"ae5a00f5.9de7a","name":"MySensors Gateway","server":"client","host":"192.168.1.20","port":"5003","datamode":"stream","datatype":"utf8","newline":"","topic":"","base64":false,"x":110,"y":120,"wires":[["6172d989.fe7678","c29897bf.f9dfc8"]]},{"id":"6172d989.fe7678","type":"function","z":"ae5a00f5.9de7a","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":380,"y":120,"wires":[["7652f827.9cb588","e74cb85e.1cd2f8"]]},{"id":"7652f827.9cb588","type":"function","z":"ae5a00f5.9de7a","name":"Filter: temperature ","func":"if (msg.payload.nodeId == 3 && msg.payload.type === 0) {\n    var msg;\n    msg.payload = msg.payload.value;\n    msg.topic = \"Temperature\"\n    return msg;\n}    ","outputs":1,"noerr":0,"x":630,"y":80,"wires":[["78896415.6e454c"]]},{"id":"e74cb85e.1cd2f8","type":"function","z":"ae5a00f5.9de7a","name":"Filter: humidity","func":"if (msg.payload.nodeId == 3 && msg.payload.type === 1) {\n    var msg;\n    msg.payload = msg.payload.value;\n    msg.topic = \"Humidity\"\n    return msg;\n}    ","outputs":1,"noerr":0,"x":620,"y":140,"wires":[["aa8a8b27.16b158"]]},{"id":"78896415.6e454c","type":"blynk-websockets-out-write","z":"ae5a00f5.9de7a","name":"Blynk -> V0 (Temperature)","pin":0,"client":"d94baf97.38207","x":870,"y":80,"wires":[]},{"id":"aa8a8b27.16b158","type":"blynk-websockets-out-write","z":"ae5a00f5.9de7a","name":"Blynk -> V1 (Humidity)","pin":"1","client":"d94baf97.38207","x":860,"y":140,"wires":[]},{"id":"d94baf97.38207","type":"blynk-websockets-client","z":"","name":"Blynk Local Server","path":"ws://192.168.1.24:8082/websocket","key":"ed151c7d8547497ea951d3c7ad87ebe8"}]

Déployez le flow et ouvrez l’application Blynk sur votre smartphone iOS ou Android. Vous recevez maintenant les données en provenance de vos objets MySensors sans aucune modification du code de ces dernières.

Avez-vous aimé cet article ?