A node.js server that receives IoT sensor data and broadcast it via web

A node.js server that receives IoT sensor data and broadcast it via web

Introduction

I constantly strive to easily access real-time, high-frequency data streams, or remotely deploy sensor data. Following data acquisition, the convenience of analyzing or visualizing this sensor data through algorithms is essential.

This is a basic project that allows the Sensor_driven_by_MCU submit the data on to the nodejs_driven_server and allows the user record the data via web page that receives data from the nodejs_driven_server via WebSocket.

see the diagram below:

                       +-----------------------+                              
                       |   Server by Node.js   |                              
                       |                       | \                            
                     > |                       |  \ WebSocket                 
       Http/Https  -/  |                       |   \                          
                 -/    +-----------------------+    \                         
                /                                    v                        
+--------------+                                   +-------------------------+
|              |                                   |Browser                  |
| MCU client   |                                   |+----------------------+ |
|              |                                   || Web for Sensor data  | |
| +---------+  |                                   ||                      | |
| | Sensors |  |                                   || data: 123.45 ...     | |
| +---------+  |                                   |+----------------------+ |
+--------------+                                   +-------------------------+
                                                                              

About Node.js

node.js is a server framework that allows js coder build a backend logic. It can be also used for developing the desktop app.

2.1 Code for Server

cmd: npm install express

Javascript code for app.js:

const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

const port = 8080; //port

const clients = new Set();  // for storing the connected WebSocket client

wss.on('connection', (ws) => {
  clients.add(ws);
  console.log('Client connected');

  ws.on('close', () => {
    clients.delete(ws);
    console.log('Client disconnected');
  });
});

app.use(express.static(path.join(__dirname, 'public')));

app.use(express.json());

app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.post('/esp-data', (req, res) => {
  const data = req.body;  // obtain the data sent from micro chip
  console.log('Received data from IoT data acquisition unit:', data.sensorValue);

  /* broadcast the data to all the connected clients */
  for (const client of clients) {
    if (client.readyState === WebSocket.OPEN) {
      client.send(JSON.stringify(data));
    }
  }

  res.send('Data received successfully and broadcasted via webSocket.');
});

server.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

Run: node app.js

2.2 Code for IoT Sensor MCU client:

The C code with Arduino as SDK for esp32 chip is:

#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>

#define USE_SERIAL Serial

/*
remember to configure your server IP and wifi passCode before download this firmwear!
*/

WiFiMulti wifiMulti;
float sensorValue = 123.45;
float bias = 1;

void setup() {

    USE_SERIAL.begin(115200);

    USE_SERIAL.println();
    USE_SERIAL.println();
    USE_SERIAL.println();

    for(uint8_t t = 4; t > 0; t--) {
        USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
        USE_SERIAL.flush();
        delay(1000);
    }

    wifiMulti.addAP("UR_WIFI_SSID", "UR_WIFI_PASSCODE");

}

void loop() {

    if (sensorValue > 126){
        bias = -1;
    }

    if (sensorValue < 120){
        bias = 1;
    }

    // wait for WiFi connection
    if((wifiMulti.run() == WL_CONNECTED)) {

        HTTPClient http;

        USE_SERIAL.print("[HTTP] begin...\n");
        // configure traged server and url
        //http.begin("https://www.howsmyssl.com/a/check", ca); //HTTPS
        http.begin("http://YOUR_SERVER_IP:8080/esp-data"); //HTTP
        http.addHeader("Content-Type", "application/json");

        // read sensor data
        sensorValue = sensorValue + bias;
      
        //You can use `arduinojson` lib to assemble the json string:
        String data = "{\"sensorValue\":" + String(sensorValue) + "}";

        // post data to server
        USE_SERIAL.print("[HTTP] POST...\n");
        int httpCode = http.POST(data);
        if (httpCode > 0) {
            Serial.printf("[HTTP] POST request code: %d\n", httpCode);
            if(httpCode == HTTP_CODE_OK) {
                String payload = http.getString();
                USE_SERIAL.println(payload);
            }
        } 
        else {
            Serial.printf("[HTTP] POST request failed.\n");
            Serial.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());
        }

        /* // HTTP GET:
        USE_SERIAL.print("[HTTP] GET...\n");
        // start connection and send HTTP header
        int httpCode = http.GET();

        // httpCode will be negative on error
        if(httpCode > 0) {
            // HTTP header has been send and Server response header has been handled
            USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode);

            // file found at server
            if(httpCode == HTTP_CODE_OK) {
                String payload = http.getString();
                USE_SERIAL.println(payload);
            }
        } else {
            USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
        } 
        */
        http.end();
    }
    delay(2000);
}

2.3 Code for front-end Web page:

The ./public/index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Sensor Data Display</title>
</head>
<body>
  <h1>Sensor Data Display:</h1>

  <div id="sensorDataDisplay"></div>
  <script src="main.js"></script>
</body>
</html>

and The ./public/main.js file:

const sensorDataDisplay = document.getElementById('sensorDataDisplay');
let paragraph; // Declare a global variable to store the <p> element

const socket = new WebSocket(`ws://${window.location.hostname}:8080`);

socket.addEventListener('message', (event) => {
  const newData = JSON.parse(event.data);
  const timestamp = new Date().toLocaleTimeString();
  const sensorValue = newData.sensorValue;

  // If the paragraph element is already created, update its content
  if (paragraph) {
    paragraph.textContent = `Timestamp: ${timestamp}, Sensor Value: ${sensorValue}`;
  } else {
    // If the paragraph element doesn't exist, create it and append it to sensorDataDisplay
    paragraph = document.createElement('p');
    paragraph.textContent = `Timestamp: ${timestamp}, Sensor Value: ${sensorValue}`;
    sensorDataDisplay.appendChild(paragraph);
  }

  console.log(newData); // Optional: Log the newData to the console for debug
});



/**
 * If it is in append mode, i.e., the server sends a piece of data through WebSocket, 
 * and a new line is appended for each data entry, you can use the following main.js code.
 *
 * This is suitable for scenarios where you want to start recording sensor data when 
 * the webpage is opened.
 */

/*

const sensorDataDisplay = document.getElementById('sensorDataDisplay');
const socket = new WebSocket(`ws://${window.location.hostname}:8080`);

socket.addEventListener('message', (event) => {
  const newData = JSON.parse(event.data);

  const timestamp = new Date().toLocaleTimeString();
  const sensorValue = newData.sensorValue;

  // Create a new paragraph element to display the data
  const paragraph = document.createElement('p');
  paragraph.textContent = `Timestamp: ${timestamp}, Sensor Value: ${sensorValue}`;

  // Append the paragraph to the sensorDataDisplay div
  sensorDataDisplay.appendChild(paragraph);

  console.log(newData); // Optional: Log the newData to the console
});

*/

Note: Remember to firewall-allow the port used by the web app.

Subsequent work based on this proj.

  • use Echarts framework for data visualization
  • Record the data in file or database
  • more function for specific scenarios
Published At
comments powered by Disqus