MySensors objects communicate with each other in the form of small text messages sent by radio waves. It is very easy to decode messages in following the technical specifications of the API available on the series official website of the project. The MySensors library allows to facilitate the development of applications of all kinds (mobile, robotics, home automation) without having to manage the communication between the devices layer. We use Node-RED to decode the messages received by a gateway network or series.
Structure of a Message MySensors
MySensors communicates with small messages of 32 bytes (the maximum size of messages sent by a chip nRF24L01). Header (header) of the message is 7 bytes. It is composed thus:
node-id ; child-sensor-id ; message-type ; ack ; sub-type ; payload \n
- node-id: number of the node.
- child-sensor-id: the identifier of a child. For example the temperature measured by a sensor DHT22.
- message-type: type of message
- 0. Presentation of the node. It is sent during execution of the function presentation() at the start of the node
- 1. Set. Sending data by a sensor
- 2. Req. Request to an actuator to execute the requested command
- 3. Internal. Internal message
- 4. Stream. Used to update wireless (OTA)
- ack: request acknowledgement receipt (or acknowledgement)
- sub-type: the subtype depends on the message type
- payload: content of the message. It is limited to 25 bytes.
25 bytes may seem small, but the nRF24L01 is not designed to transmit large messages, States or physical measures.
Connect to a gateway MySensors with Node-RED
There are two types of gateway MySensors. The serial gateway that plugs into the USB of a computer or a Raspberry. It is convenient to start and read the debugging messages sent on the serial port (for example with the monitor series of the Arduino IDE). The network gateway is now very simple (and cheap) to make using an ESP8266 (explanations in article). You can install it (hide it!) in place or radio reception is better and it doesn’t hog a USB port of the Raspberry Pi.
Serial gateway on USB
To connect to a gateway series, add a Node (in Input) serial
Open to set it up and press the pencil to configure the serial port. Press on the magnifying glass and choose the USB port on which the gateway is connected. For example /dev/ttyUSB0.
The speed of communication with the gateway defaults to 115200 baud. Leave the default configuration. 8-bit Data, Parity None, Stop Bits 1.
Save the configuration by pressing Done .
LAN Gateway
To connect to a network gateway, we will use a Node TCP
Configure the connection as well:
- Type: Connect to
- Port: 5003 by default (to adapt to your configuration)
- at host : the ip address of the gateway resea.
- output: stream of… String
- name
- save with Done
The plugin node-red-contrib-mysensors developed by Thomas Mørch allows to decode messages MySensors and send to a node. He moved with this command.
npm install node-red-contrib-mysensors
After installation, restart Node-RED with the following commands and refresh the browser.
node-red-stop node-red-start
You now have a new range called mysensors. It is composed of 3 Nodes
- mysdecenc : encoder / decoder of messages MySensors
- mysencap : prepares the message to send to the node
- mysdebug : makes readable messages MySensors
Function mysdebug
Let’s go by the end for once. The function mysdebug allows to decode and make it more readable messages in sent by the gateway. Each code is replaced by its meaning.
Decoded messages displayed in the console. Everything is not correctly decoded (in the current version of the plugin).
This function allows to decode messages MySensors. to use, simply plug it into a gateway (series or network). Output (payload), we get directly the payload of each message. Every piece of available information is published on a different stream:
- msg.payload : (payload) message sent by the node content
- msg.nodeId : node from which the message
- msg.childSensorId : Id of the child attached to the node
- msg.messageType : type of message
- msg.ack : request for acknowledgement
- msg.subType : subtype of message
This architecture present advantages and disadvantages. We get directly output the payload of each message, but this can quickly become complicated to filter the data from any sensor.
Decoded messages displayed in the console.
Code of the flow
[{"id":"62351421.a2a7ec","type":"serial in","z":"eb4ead14.fd77f","name":"MySensors Serial Gateway","serial":"219cc581.f9b252","x":145.6666717529297,"y":134.66666412353516,"wires":[["2027984.714eb68","d8174c63.f752a"]]},{"id":"893c2411.1181e8","type":"debug","z":"eb4ead14.fd77f","name":"","active":false,"console":"false","complete":"payload","x":559,"y":35,"wires":[]},{"id":"d8174c63.f752a","type":"mysdecenc","z":"eb4ead14.fd77f","name":"","x":372.5,"y":36,"wires":[["893c2411.1181e8","80842fe1.74a72"]]},{"id":"80842fe1.74a72","type":"debug","z":"eb4ead14.fd77f","name":"","active":false,"console":"false","complete":"nodeId","x":559.5,"y":80 ,"wires":[]},{"id":"2027984.714eb68","type":"mysdebug","z":"eb4ead14.fd77f","name":"","x":374.5,"y":135,"wires":[["ed9596a0.eaa2b8"]]},{"id":"ed9596a0.eaa2b8","type":"debug","z":"eb4ead14.fd77f","name":"","active":false,"console":"false","complete":"false","x":561.5,"y":140,"wires":[]},{"id":"219cc581.f9b252","type":"serial-port","z":"","serialport":"/dev/ttyUSB0","serialbaud":"115200","databits":"8","parity":"none","stopbits":"1","newline":"\n","bin":"false","out":"char","addchar":false}]
Decoding function that returns a JSON object
I find it easier and clearer to handle a JSON object. I Developed a small function That You can add to your Node-RED projects. It Decodes Each post and created a JSON object to the key: value format.
The JSON object returns the following information:
- nodeId : Id of the node issuing the message
- sensorId : Id of the child
- mode : presentation, set, req, internal, stream
- type: number of the subtype of data (depending on mode)
- typeLabel : language of the type of data
- value : content of the message (payload)
You can also install this flow from the official website of Node-RED http://flows.nodered.org/flow/fa02078c160cb3e00e09f4980b534490
/ * MySensors v2 Message Decoder * Payload: JSON object * www.projetsdiy.fr - oct. 2016 * / var mySensorsMessage = {} var newPayload = {}; var messaGE = msg.payload.toString (); message = message.replace(/(rn|n|r)/gm, ""); var tokens = message.split(";") if(tokens.length == 6) {mySensorsMessage.nodeId = parseInt(tokens[0]); mySensorsMessage.childSensorId = parseInt(tokens[1]); mySensorsMessage.messageType = parseInt(tokens[2]); mySensorsMessage.ack = parseInt(tokens[3]); mySensorsMessage.subType = parseInt(tokens[4]); mySensorsMessage.value = Number(tokens[5]); var messageType = mySensorsMessage.messageType; var subType = mySensorsMessage.subType; 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"]; 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"] 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"] switch (messageType) {case 0: / / Presentation newPayload.mode = "Presentation";} newPayload.type = labelPresentation [subType]; break; case 1: / / Set newPayload.nodeId = mySensorsMessage.nodeId; newPayload.sensorId = mySensorsMessage.childSensorId; newPayload.mode = "Set"; newPayload.type = subType; newPayload.typeLabel = labelSet [subType]; newPayload.value = mySensorsMessage.value; break; case 2: / / Req newPayload.nodeId = mySensorsMessage.nodeId; newPayload.sensorId = mySensorsMessage.childSensorId; newPayload.mode = "Req"; newPayload.type = subType; newPayload.typeLabel = labelSet [subType]; newPayload.value = mySensorsMessage.value; break; case 3: / / Internal newPayload.nodeId = mySensorsMessage.nodeId; newPayload.sensorId = mySensorsMessage.childSensorId; newPayload.mode = "Internal"; newPayload.type = subType; newPayload.typeLabel = labelInternal [subType]; newPayload.value = mySensorsMessage.value; break; box 4: / / Stream - OTA firmware update newPayload.nodeId = mySensorsMessage.nodeId; newPayload.mode = "stream"; break; default: break; } msg.payload = newPayload; } else {msg.payload = "Error! Nothing to decode"} return msg;
Here’s what you get with the service. Each message is now explicit and formatted in the form of a JSON object easier to exploit in a project.
You can modify according to your needs.
Code of the flow
[{"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 Decodern * Payload: JSON objectn * www.projetsdiy.fr - oct."} 2016n * / nvar mySensorsMessage = {nvar newPayload = {}; message = msg.payload.toString();nmessage = message.replace(/(\r\n|\n|\r)/gm, "");nvar = message.split(";")nif(tokens.length tokens nvar is 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]); nn 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 box} 0: / / Presentationn n newPayload.mode = "Presentation";n newPayload.type = labelPresentation [subType]; n break; n case 1: / / Setn newPayload.nodeId = mySensorsMessage.nodeId; newPayload.sensorId = n 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: / / Reqn newPayload.nodeId = mySensorsMessage.nodeId; newPayload.sensorId = n 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: / / Internaln newPayload.nodeId = mySensorsMessage.nodeId; newPayload.sensorId = n mySensorsMessage.childSensorId; n newPayload.mode = "Internal";n newPayload.type = subType; n newPayload.typeLabel = labelInternal [subType]; n newPayload.value = mySensorsMessage.value; n break; {n box 4: / / Stream - OTA firmware updaten newPayload.nodeId = mySensorsMessage.nodeId; newPayload.mode = "stream";n n break; n default:n break; n} nn msg.payload = newPayload; n} else {n msg.payload = "Error! Nothing to decode"n} nnreturn 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":[]}]
The function mysencap
The last function mysencap proposed by the plugin to format a message before publishing it to a node of the network MySensors. We can send you want dans le Limits of 25 bytes. We shoulds aussi add a return line (n) before you inject it into the function. For example, you can retrieve the State of a switch node created with the plugin-red-contrib-ui to turn on or turn off an LED (or a lamp by a relay). We will see this plugin in detail in a future tutorial.
Add a switch Node
Give it a name. For the output value, enter 1 and 0 for Off. It’s not very important, it may very well leave True/False. It will take just to test the good value in the node MySensors Arduino code.
Go to the page of interface at the address
http://IP_NODE-RED:1880/ui
You now have a two-State switch.
It remains more to prepare the message. Add a function and paste this code to add a return to the line.
msg.payload = msg.payload + "\n" return msg;
Then add a mysencap Node and specify the parameters of the destination of the message node. The type of message must be Request. Here is an example:
It remains more to inject into a Node mysdecenc before you send the message on a gateway (series or network) MySensors.
Add this code on the MySensor node to intercept messages sent to this object.
void receive(const MyMessage & message) { Serial.print("Message recu pour le capteur:"); Serial.print(message.sensor); Serial.print(", Nouveau statut:"); Serial.println(message.getBool()); }
And, if the switch is pressed, one receives good messages on the node from the Node-RED flow.
Code of the flow
[{"id":"f34523e0.fa94a","type":"mysdecenc","z":"e1e0f04d.8c404","name":"","x":546.388916015625,"y":276,"wires":[["a9d71461.a61de8"]]},{"id":"45e9c56a.b8cdec","type":"mysencap","z":"e1e0f04d.8c404","name":"","nodeid":"3","childid":"0","subtype":"2","internal":0,"ack":false,"msgtype":"2","presentation":false,"presentationtype":0,"presentationtext":"","fullpresentation":false,"firmwarename":"","firmwareversion":"","x":400,"y":220.3333282470703,"wires":[["f34523e0.fa94a"]]},{"id":"a9d71461.a61de8","type":"tcp out","z":"e1e0f04d.8c404","host":"192.168.1.20","port":"5003","beserver":"client","base64":false ,"end":false,"name":"to MySensors","x":738,"y":232.3333282470703,"wires":[]},{"id":"d253489c.087998","type":"function","z":"e1e0f04d.8c404","name":"slash n","func":"msg.payload = msg.payload + "\n"nreturn msg;","outputs":1,"noerr":0,"x":274,"y":276.3333282470703,"wires":[["45e9c56a.b8cdec"]]},{"id":"af4ddcee.88eaf","type":"ui_switch","z":"e1e0f04d.8c404","tab":"6df01db6.d8538c","name":"Switch","topic":"","group":"","order":1,"onvalue":"1","offvalue":"0","x":149,"y":224.3333282470703,"wires":[["d253489c.087998"]]},{"id" [{"": "6df01db6.d8538c", "type": "ui_tab", "z": "","name": "Show", "icon": "store", "order": "1"}]