Node-Red – Display Real Time Arduino Sensor Readings thru WebSocket

This flow will show you how to display real-time Arduino sensor readings on our Node-Red application using WebSocket.

It displays the sensor readings in a custom HTML/CSS/Javascript user interface that I have written.

Companion Writeup

Node-Red – Display Arduino Sensor Readings thru WebSocket

Featured Image - Node-Red - Display Arduino Sensor Readings thru WebSocket

[{"id":"3de7f87250c169e2","type":"websocket in","z":"b93e9b260d53649d","name":"","server":"8dbef215b4efa8a7","client":"","x":400,"y":600,"wires":[["ce592e4dc2db096b"]]},{"id":"2a88ff5bce73144b","type":"debug","z":"b93e9b260d53649d","name":"debug 5","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":920,"y":680,"wires":[]},{"id":"2d0866385f48fa75","type":"http in","z":"b93e9b260d53649d","name":"","url":"/sensorReadings","method":"get","upload":false,"swaggerDoc":"","x":380,"y":240,"wires":[["f14f34c54fcfaed0"]]},{"id":"3f31af6081e79e4b","type":"inject","z":"b93e9b260d53649d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":500,"y":500,"wires":[["ad3e1c6852897a8a"]]},{"id":"ad3e1c6852897a8a","type":"websocket out","z":"b93e9b260d53649d","name":"","server":"8dbef215b4efa8a7","client":"","x":930,"y":500,"wires":[]},{"id":"ce592e4dc2db096b","type":"function","z":"b93e9b260d53649d","name":"Clear Session","func":"msg._session = \"\";\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":680,"y":600,"wires":[["2a88ff5bce73144b","ad3e1c6852897a8a"]]},{"id":"f14f34c54fcfaed0","type":"template","z":"b93e9b260d53649d","name":"Javascript","field":"payload.script","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"const sensorValues = document.querySelector(\"#sensor-values\");\n\nconst sensorData = [];\n\n/*\n  Plotly.js graph and chart setup code\n*/\nvar sensorChartDiv = document.getElementById(\"sensor-chart\");\n\n// History Data\nvar sensorTrace = {\n  x: [],\n  y: [],\n  name: \"LDR/Photoresistor\",\n  mode: \"lines+markers\",\n  type: \"line\",\n};\n\nvar sensorLayout = {\n  autosize: false,\n  width: 800,\n  height: 500,\n  colorway: [\"#05AD86\"],\n  margin: { t: 40, b: 40, l: 80, r: 80, pad: 0 },\n  xaxis: {\n    gridwidth: \"2\",\n    autorange: true,\n  },\n  yaxis: {\n    gridwidth: \"2\",\n    autorange: true,\n  },\n};\nvar config = { responsive: true };\n\nPlotly.newPlot(sensorChartDiv, [sensorTrace], sensorLayout, config);\n\n// Will hold the sensor reads\nlet newSensorXArray = [];\nlet newSensorYArray = [];\n\n// The maximum number of data points displayed on our scatter/line graph\nlet MAX_GRAPH_POINTS = 50;\nlet ctr = 0;\n\nfunction updateChart(sensorRead) {\n  if (newSensorXArray.length >= MAX_GRAPH_POINTS) {\n    newSensorXArray.shift();\n  }\n  if (newSensorYArray.length >= MAX_GRAPH_POINTS) {\n    newSensorYArray.shift();\n  }\n  newSensorXArray.push(ctr++);\n  newSensorYArray.push(sensorRead);\n\n  var data_update = {\n    x: [newSensorXArray],\n    y: [newSensorYArray],\n  };\n\n  Plotly.update(sensorChartDiv, data_update);\n}\n\n// WebSocket support\nvar targetUrl = `ws://${location.host}/ws/readings`;\nvar websocket;\nwindow.addEventListener(\"load\", onLoad);\n\nfunction onLoad() {\n  initializeSocket();\n}\n\nfunction initializeSocket() {\n  console.log(\"Opening WebSocket connection Node-Red Server...\");\n  websocket = new WebSocket(targetUrl);\n  websocket.onopen = onOpen;\n  websocket.onclose = onClose;\n  websocket.onmessage = onMessage;\n}\nfunction onOpen(event) {\n  console.log(\"Starting connection to WebSocket server..\");\n}\nfunction onClose(event) {\n  console.log(\"Closing connection to server..\");\n  setTimeout(initializeSocket, 2000);\n}\nfunction onMessage(event) {\n  console.log(\"WebSocket message received:\", event);\n  updateValues(event.data);\n  updateChart(event.data);\n}\n\nfunction sendMessage(message) {\n  websocket.send(message);\n}\n\nfunction updateValues(data) {\n  sensorData.unshift(data);\n  if (sensorData.length > 20) sensorData.pop();\n  sensorValues.value = sensorData.join(\"\\r\\n\");\n}\n","output":"str","x":580,"y":340,"wires":[["ce0f6f510b40daa3"]]},{"id":"ce0f6f510b40daa3","type":"template","z":"b93e9b260d53649d","name":"CSS","field":"payload.style","fieldType":"msg","format":"handlebars","syntax":"mustache","template":":root {\n  --color-white: #fff;\n  --color-dark-variant: #f3f5f8;\n  --border-radius-1: 0.4rem;\n}\n\n* {\n  margin: 0;\n  padding: 0;\n  outline: 0;\n}\n\nbody {\n  width: 100vw;\n  height: 100vh;\n  overflow-x: hidden;\n  background: var(--color-dark-variant);\n}\nh1 {\n  margin-top: 0.4rem;\n  margin-left: 1.6rem;\n}\n\n.container {\n  display: grid;\n  width: 96%;\n  margin: 0 auto;\n  gap: 1.8rem;\n  grid-template-columns: 14rem auto;\n}\naside {\n  margin-top: 1.4rem;\n  height: 100vh;\n  background: var(--color-white);\n  border-radius: var(--border-radius-1);\n  padding: 0.4rem;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n}\n.values {\n  text-align: center;\n  resize: none;\n  height: 100%;\n  font-size: 1.5rem;\n  font-weight: bold;\n}\n\nmain {\n  margin-top: 1.4rem;\n  background: var(--color-white);\n  padding: 0.4rem;\n  /* border: 1px solid red; */\n  text-align: center;\n}\n\n.wrapper {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n","output":"str","x":730,"y":240,"wires":[["0f0def2d6e7cb078"]]},{"id":"0f0def2d6e7cb078","type":"template","z":"b93e9b260d53649d","name":"HTML","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n    <title>Node-Red WebSocket</title>\n    <style>{{{payload.style}}}</style>\n    <link rel=\"icon\" href=\"./favicon.ico\" type=\"image/x-icon\" />\n    <script src=\"https://cdn.plot.ly/plotly-2.16.1.min.js\"></script>\n  </head>\n  <body>\n    <h1>Node-Red WebSocket</h1>\n    <div class=\"container\">\n      <aside>\n        <h2>Real time values</h2>\n        <textarea\n          id=\"sensor-values\"\n          name=\"\"\n          class=\"values\"\n          cols=\"10\"\n          rows=\"20\"\n        ></textarea>\n      </aside>\n      <main class=\"chart\">\n        <h2>LDR/Photoresistor</h2>\n        <div class=\"wrapper\">\n          <div id=\"sensor-chart\" class=\"sensor-chart\"></div>\n        </div>\n      </main>\n    </div>\n    <script>{{{payload.script}}}</script>\n  </body>\n</html>\n","output":"str","x":870,"y":340,"wires":[["b48f6f14310d044c"]]},{"id":"b48f6f14310d044c","type":"http response","z":"b93e9b260d53649d","name":"","statusCode":"","headers":{},"x":990,"y":240,"wires":[]},{"id":"d24e9af23649e742","type":"comment","z":"b93e9b260d53649d","name":"Web Application","info":"","x":380,"y":180,"wires":[]},{"id":"cc3002e58ce69bc3","type":"comment","z":"b93e9b260d53649d","name":"WebSocket Server","info":"","x":390,"y":440,"wires":[]},{"id":"8dbef215b4efa8a7","type":"websocket-listener","path":"/ws/readings","wholemsg":"false"}]

Flow Info

Created 2 years, 4 months ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • comment (x2)
  • debug (x1)
  • function (x1)
  • http in (x1)
  • http response (x1)
  • inject (x1)
  • template (x3)
  • websocket in (x1)
  • websocket out (x1)
  • websocket-listener (x1)

Tags

  • websocket
  • arduino
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option