Wednesday, June 8, 2016

WIO Link and Node-RED websockets

This is an update for this old post (updated on 24-Dec-2017).

I wanted to test the Grove modules that can interact with WebSockets in Node-RED.

The WebSocket capability allows you to monitor those modules in real-time for events. As soon as an event takes place the module itself will send a notification to the SEEED exchange server and therefore to any WebSocket that is listening to those events.


We can discover how many grove modules are supported by SEED by using the API "scan drives". The result of this HTTP request will be quite long (greater than 70k bytes of text) so it is necessary to transform the JSON result with the JSONata expression on JSONata exerciser webpage:

drivers{$string(ID):GroveName}

to get this list:

{
  "0": "Grove-Moisture",
  "1": "Grove-Recorder",
  "2": "Grove - Hall Sensor",
  "3": "Grove-Servo",
  "4": "Grove-Relay",
  "5": "Grove - Loudness Sensor",
  "6": "Grove - Infrared Receiver",
  "7": "Grove - UV Sensor",
  "8": "Grove-IR Distance Interrupter",
  "9": "Grove - Luminance Sensor",
  "10": "Generic Analog Input",
  "11": "Grove - I2C ADC",
  "12": "Grove - MP3 v2.0",
  "13": "One Wire Temperature Sensor",
  "14": "Grove - Sunlight Sensor",
  "15": "Grove-I2C Motor Driver",
  "16": "Generic UART",
  "17": "Grove-Rotary Angle Sensor",
  "18": "Grove - SPDT Relay(30A)",
  "19": "Grove - Infrared Emitter",
  "20": "Grove - Gesture v1.0",
  "21": "Grove-Electromagnet",
  "22": "Grove-Barometer(BMP085/BMP180)",
  "23": "Grove - Air Quality Sensor",
  "24": "Grove-3Axis Compass",
  "25": "Grove - Temperature Sensor",
  "26": "Grove-Magnetic Switch",
  "27": "Grove - Temp&Humi&Barometer Sensor (BME280)",
  "28": "Generic Digital Input",
  "29": "Grove-3Axis Digital Acc(±1.5g)",
  "30": "Grove - 4-Digit Display",
  "31": "Grove - Encoder",
  "32": "Generic Digital Output",
  "33": "Grove-PIR Motion Sensor",
  "34": "Grove-Button",
  "35": "Grove-EL Driver",
  "36": "Grove-WS2812 LED Strip 60",
  "37": "Grove-Multichannel Gas Sensor",
  "38": "Grove - OLED Display 1.12''",
  "39": "Grove - LED Bar",
  "40": "Generic PWM/Analog Output",
  "41": "Grove-Digital Light",
  "42": "Grove-Solid State Relay",
  "43": "Grove - CO2 MH-Z16",
  "44": "Grove - Sound Sensor",
  "45": "Grove - OLED Display 0.96''",
  "46": "Grove - LCD RGB Backlight",
  "47": "Grove-Speaker",
  "48": "Grove-3-Axis Digital Gyro",
  "49": "Grove - Ultrasonic Ranger",
  "50": "Cytron 13Amp DC Motor Driver - SMD Compatible (MD13S)",
  "51": "Grove - Voltage Divider",
  "52": "Grove-Temperature&Humidity",
  "53": "Grove-Temperature&Humidity Pro",
  "54": "Grove-Dry-Reed Relay",
  "55": "Grove-Barometer(BMP280)",
  "56": "Grove - I2C FM Receiver",
  "57": "Grove - Dust Sensor"
}



Alternatively, it works on the following Node-RED flow with the following expression configured in the JSONata field in the Change node:

msg.payload.drivers{$string(ID):GroveName}




[{"id":"f9e0cd3a.9a47c","type":"inject","z":"a9e35387.ccf05","name":"email+password","topic":"","payload":"email=XXX&password=YYY","payloadType":"str","repeat":"","crontab":"","once":false,"x":120,"y":80,"wires":[["89dc065f.b354e8"]]},{"id":"6174bb14.39d664","type":"debug","z":"a9e35387.ccf05","name":"","active":true,"console":"false","complete":"true","x":850,"y":80,"wires":[]},{"id":"89dc065f.b354e8","type":"function","z":"a9e35387.ccf05","name":"User Login","func":"msg.url=\"https://us.wio.seeed.io/v1/user/login?\"+msg.payload;\nmsg.payload=\" \";\nmsg.method=\"POST\";\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":80,"wires":[["e02efc95.ab5d4"]]},{"id":"e02efc95.ab5d4","type":"http request","z":"a9e35387.ccf05","name":"HTTP Request","method":"use","ret":"obj","url":"","x":496.9971466064453,"y":80.88922119140625,"wires":[["102e5dd3.016402"]]},{"id":"b1fd43af.51f1b","type":"function","z":"a9e35387.ccf05","name":"User Scan Drivers","func":"msg.url=\"https://us.wio.seeed.io/v1/scan/drivers\"; \nmsg.method=\"GET\";\nmsg.headers = {\"Authorization\": global.get(\"user_token\")};\nmsg.payload=\"\";\nreturn msg;","outputs":1,"noerr":0,"x":290,"y":200,"wires":[["3aea3549.07b8da"]]},{"id":"3aea3549.07b8da","type":"http request","z":"a9e35387.ccf05","name":"HTTP Request","method":"use","ret":"obj","url":"","tls":"","x":501.1419677734375,"y":199.21304321289062,"wires":[["9772c89e.680898"]]},{"id":"102e5dd3.016402","type":"function","z":"a9e35387.ccf05","name":"Store user_token","func":"global.set(\"user_token\",msg.payload.token)\nreturn msg;","outputs":1,"noerr":0,"x":694.8493804931641,"y":79.5823974609375,"wires":[["6174bb14.39d664"]]},{"id":"23344e4c.1a8f42","type":"inject","z":"a9e35387.ccf05","name":"Fire","topic":"","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"x":97.14491271972656,"y":200.14199829101562,"wires":[["b1fd43af.51f1b"]]},{"id":"5911271b.728448","type":"debug","z":"a9e35387.ccf05","name":"","active":true,"console":"false","complete":"true","x":730,"y":200,"wires":[]},{"id":"9772c89e.680898","type":"change","z":"a9e35387.ccf05","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"msg.payload.drivers{$string(ID):GroveName}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":340,"wires":[["5911271b.728448"]]}]






Now to filter only those who support WebSockets, we use the following JSONata filter on JSONata exerciser webpage::

drivers[HasEvent]{GroveName:HasEvent}


The outcome shows that there are 9 of them (excluding the generic one) supporting WebSockets.



{
  "Grove - Hall Sensor": true,
  "Grove - Infrared Receiver": true,
  "Grove-IR Distance Interrupter": true,
  "Generic UART": true,
  "Grove - Gesture v1.0": true,
  "Grove-Magnetic Switch": true,
  "Generic Digital Input": true,
  "Grove - Encoder": true,
  "Grove-PIR Motion Sensor": true,
  "Grove-Button": true
}




I have only one module in my bench that works with WebSockets: the Grove Button.

So I will use it for testing the WebSocket node from Node-RED.


Explanation of the Node-RED flow:

The inject node will hold the node token in the payload field:


This inject node will be wired to an output web socket node. This node will handle the handshake to open the WebSocket on the server and will send the payload with the node token. The field Type has to be configured as " Connect to". You will click on the pencil icon on the URL field to add the following WebSocket endpoint:

wss://iot.seeed.cc/v1/node/event


Update: 24-Dec-2017

Notice that the exchange server changed from iot.seed.cc to  us.wio.seeed.io




Then you will add an input Node-RED node that will be configured exactly like the previous one. This node will receive notifications from the exchange server.

Finally, there will be a debug node to show the results in the debug pane of FRED.


Testing:

Of course, you have first to visually program the setup in your app (Android or IOS) and load the firmware.

Whenever you press the button on your board you will see a debug message in Node-RED displaying the alert that the bottom has been pressed.

Just showing "button pressed" in the debug of Node-RED is too simplistic. Maybe you want to send to an MQTT broket or Dweet the event or build some logic in Node-REd that trigger some other action. It is up to your imagination.







Here is the flow to copy and paste to your Node-RED:



[{"id":"356f667e.5f3afa","type":"websocket-client","z":"2f43f52c.5f5eba","path":"wss://iot.seeed.cc/v1/node/event","wholemsg":"false"},{"id":"7b2c2f4e.22df","type":"websocket out","z":"378ba411.6c39fc","name":"","server":"","client":"356f667e.5f3afa","x":468.09942626953125,"y":73.09091186523438,"wires":[]},{"id":"3cea87fa.149c38","type":"inject","z":"378ba411.6c39fc","name":"","topic":"","payload":"a1ad1dfa6dc7726333d49962dde5599a","payloadType":"str","repeat":"","crontab":"","once":false,"x":136.09661865234375,"y":71.51419067382812,"wires":[["7b2c2f4e.22df"]]},{"id":"8ad40f70.4ab6c","type":"websocket in","z":"378ba411.6c39fc","name":"","server":"","client":"356f667e.5f3afa","x":230.08810424804688,"y":171.59945678710938,"wires":[["5c703398.b865bc"]]},{"id":"5c703398.b865bc","type":"debug","z":"378ba411.6c39fc","name":"","active":true,"console":"false","complete":"true","x":533.0880432128906,"y":169.64773318393156,"wires":[]}]