Web Socket Help

I'm a total novice to coding and just can't get my head around the web socket.

I'm sure this is super simple.....

I have connect PLC to node red successfully and have data logging to a mysql database. The next thing I wanted to do is feed the current realtime value to a website. I have a website setup which is hosted on the same server via apache2.

I have tried a couple of examples that create a simple webpage and updates every five seconds etc. I got that working no problem, I think because it is hosted directly from node red web server. I tried to transfer this to my own hosted web page and I can't get it to connect.

After reading more I realised I didn't understand it enough. Am I correct to think that you need a server and a client to pass data? In my instance to I need to setup the node red side as the server and then code at my website site side as the client?

My server is 192.168.1.50 and the website hosted 192.168.1.50/index.html. I don't really understand how to point them to each other to establish a connection.

Node red is hosted 192.168.1.50:1880.

Apologies if this is really simple.....I'm really after understanding should the website be the client and is there some simple code I can implement to get this going.....as a starter.

Thanks for the help,
Ant

You may wish to share some code as it is generally easier for us to work out what you are doing.

If you want "realtime" communication between your webpage and Node-RED, you are correct in thinking that websockets are a good way to go. And Node-RED (of course) supports them well. Indeed, if you've played with Dashboard, that is how it communicates.

... sorry Ocado delivery arrives ... back in a bit ...

I'll share the code, but the only bit i have that works, is the S7 Siemens connection and logging into a Mysql db.

The web socket is the problem, I'd done a few tutorials, but can't get it working on my own web server.

I'm after a starter for setting up the web socket i.e. which side is the server and client....and how do I set it up properly to work?

Thanks for reply,
Ant

OK, sorry, post delivery :smiley:

Node-RED is the server in this case. The client is the browser. The fact that your Apache server serves up the web page is not relevant unless you need to configure CORS security. Or if you are using Apache as a reverse proxy for Node-RED.

So you have to code your page to link to ws:<server-name-or-ip>:1880/ e.g. the Node-RED server. Websockets uses the same IP Ports as http.

You set up a websocket in and out node in Node-RED to handle the server comms.

Incidentally, I've assumed here that you are keeping all traffic local to your LAN. If not, then you will need to set up TLS for everything and the ws: URI becomes wss:.

Also, you probably want to do things like automatically handle connection drops and reconnection. So it is sensible to use a client-side websocket library. Make sure you use one that handles reconnections though.

Any idea of a simple example that I can try and integrate into in the website, it's half the battle.....I'm not sure what i set each if the websocket nodes, cause I need to set one as the server.

Ant

There is a simple weboscket example in the Flow libraries

Simple Web page with live data updates via websocket

1 Like

Thanks for the help all.....with a bit more reading and playing around I have got the web socket working. I was having problems with the value appending each time rather then replacing / refreshing. But I worked out to clear the value before the new one is displayed.

Just a second question; I want to feed 3 "real time" values to the webpage; at the moment I have got one working. Do I setup different websockets for each value or should I be sending them all through one connection? If one connection how do I differentiate the values; currently its just the value sent.

Cheers,
Ant

You use the same websocket connection. In the function where you handle the incoming event, you do the filtering. I do this myself, I can show you an example...unless somebody else is faster than me (now time for a coffebreak with family)

So in this example taken from my own system, it might be a bit "raw" but I'll explain how it works. Please note this is taken from a template node where I have both html and javascript code. If you use a dashboard template node, this is done a bit differently, using the available scope

In there, I have this javascript funtion to initialize the websocket connection. I also do some other stuff like changing background colour

    function init() {
		// event handlers for WebSocket:
        inProgress = true;
        wSocket = null;
        prot = "";
        ip = location.host;

        if (location.protocol == "https:") {
            prot = "wss://";
        }
        else {
            prot = "ws://";
        }

        url = prot + ip + "/ws/location"; 
        wSocket = new WebSocket(url);
        wSocket.onopen = function() {
            document.getElementById("theBody").style.backgroundColor ="green"; //Green
            doSend('statuses_publish');   
        };
        wSocket.onmessage = wsMessage;
        wSocket.onclose = function() {
            document.getElementById("theBody").style.backgroundColor ="#C0C0C0"; //Grey
        };
        inProgress = false;
    }

As you can see in the above, I have the line "wSocket.onmessage = wsMessage;". This means that when a message arrives, a call is made to a function named "wsMessage". This is the (very much reduced) code for it. As example, when a message holding the "Indoortemperature", I decode it, save the value to a variable that is used to update the specific element in the html code. A similar example is for the spotlights

    function wsMessage(event) {
        //alert(event.data);
        //if (event.data.search("Ping")==-1) {
        //  writeToScreen(event.data);
        //}
        inProgress = true;
        document.getElementById("theBody").style.backgroundColor ="black"; //Black
        pingOK = true;

        if (event.data.match("Indoortemp")) { //BV Vardagsrum
            m = event.data.split(":");
            temp_i = parseFloat(m[1]);
            temp_i = temp_i.toPrecision(3);
            //alert(temp_i);
        }

        if (event.data.search("sunUp.")!=-1) {
            m = event.data.split(".");
            sunUP = m[1];
            sunDOWN = m[3];
            //alert(m);
            banner0 = createBanner();
            //document.getElementById("mrq1").style.color ="white";
            document.getElementById("mrq1").style.fontFamily ="arial";
            document.getElementById("mrq1").style.fontSize ="40";
            document.getElementById("mrq1").innerHTML = banner0;
        }

        if (event.data.match('garden spotlights turnon')) {
            document.getElementById("p14").style.backgroundColor="#FBF14D";
            document.getElementById("p14").style.color ="black";
            document.getElementById("p14").className = "on";
            document.getElementById('i14').src = "lamp-on.png";
            document.getElementById("p14").style.fontSize ="25";
        }
        if (event.data.match('garden spotlights turnoff')) {
            document.getElementById("p14").style.backgroundColor="black";
            document.getElementById("p14").style.color ="white";
            document.getElementById("p14").className = "off";
            document.getElementById('i14').src = "lamp-off.png";
            document.getElementById("p14").style.fontSize ="25";
        }

   }

To get "the things going", I have the following code in the html part that is executed when the page loads. I hope this gives you some "meat on the bones"

<body id="theBody" onLoad="init()" bgcolor=black text=white link=white vlink=white alink=white/>

Thanks again all for the help; I'll admit.....I'm no coder and am stuck now.

So I originally had the websocket set to payload; now I connect two different nodes and change to entire message. Some how I need to split the message to the TEMP_AM1 & TEMP_AM3 then split them to the message & message3 that I can include in the html.
The screenshot shows the full message, when the websocket is set to payload it does display the value.

Cheers,
Ant

<script type="text/javascript">
  
  let socket = new WebSocket("ws://192.168.1.50:1880/ws/request");

// message received - show the message in div#messages
socket.onmessage = function(event) {
  let message = event.data;

  let messageElem = document.createElement('div');
  messageElem.textContent = message;
  document.getElementById('messages3').innerHTML = "";
  document.getElementById('messages3').append(messageElem);
}
 
</script>```

You did not look at my example?

Share the complete webpage, not only the script part, show also your html-code, share the complete html file. You need to selectively update elements, for that you need unique element id's

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="apple-touch-icon" sizes="76x76" href="../assets/img/apple-icon.png">
  <link rel="icon" type="image/png" href="../assets/img/favicon.png">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <title>
   PQT_2110 Machine Data
  </title>
  <meta content='width=device-width, initial-scale=1.0, shrink-to-fit=no' name='viewport' />
  <!--     Fonts and icons     -->
  <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Roboto+Slab:400,700|Material+Icons" />
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css">
  <!-- CSS Files -->
  <link href="../assets/css/material-dashboard.css?v=2.1.2" rel="stylesheet" />
  
</head>

<script type="text/javascript">
  
  let socket = new WebSocket("ws://192.168.1.50:1880/ws/request");

// message received - show the message in div#messages
socket.onmessage = function(event) {
  let message = event.data;

  let messageElem = document.createElement('div');
  messageElem.textContent = message + '&#8482;';
  document.getElementById('messages3').innerHTML = "";
  document.getElementById('messages3').append(messageElem);
}

  
</script>


<body>
  <div class="wrapper ">
    <div class="sidebar" data-color="azure" data-background-color="white" data-image="../assets/img/sidebar-1.jpg">
      <!--
        Tip 1: You can change the color of the sidebar using: data-color="purple | azure | green | orange | danger"

        Tip 2: you can also add an image using data-image tag
    -->
      <div class="logo"><a href="#" class="simple-text logo-normal">
          PQT_2110 Dashboard
        </a></div>
      <div class="sidebar-wrapper">
        <ul class="nav">
          <li class="nav-item active  ">
            <a class="nav-link" href="./dashboard.html">
              <i class="material-icons">dashboard</i>
              <p>Dashboard</p>
            </a>
          </li>
          <li class="nav-item ">
            <a class="nav-link" href="./user.html">
              <i class="material-icons">person</i>
              <p>User Profile</p>
            </a>
          </li>
          <li class="nav-item ">
            <a class="nav-link" href="./tables.html">
              <i class="material-icons">content_paste</i>
              <p>Table List</p>
            </a>
          </li>
          <li class="nav-item ">
            <a class="nav-link" href="./typography.html">
              <i class="material-icons">library_books</i>
              <p>Typography</p>
            </a>
          </li>
          <li class="nav-item ">
            <a class="nav-link" href="./icons.html">
              <i class="material-icons">bubble_chart</i>
              <p>Icons</p>
            </a>
          </li>
          <li class="nav-item ">
            <a class="nav-link" href="./map.html">
              <i class="material-icons">location_ons</i>
              <p>Maps</p>
            </a>
          </li>
          <li class="nav-item ">
            <a class="nav-link" href="./notifications.html">
              <i class="material-icons">notifications</i>
              <p>Notifications</p>
            </a>
          </li>
          <li class="nav-item ">
            <a class="nav-link" href="./rtl.html">
              <i class="material-icons">language</i>
              <p>RTL Support</p>
            </a>
          </li>
        </ul>
      </div>
    </div>








    <div class="main-panel">
      <div class="container-fluid">
          <div class="row">
            <div class="col-lg-3 col-md-6 col-sm-6">
              <div class="card card-stats">
                <div class="card-header card-header-success card-header-icon">
                  <div class="card-icon">
                    <i class="material-icons">arrow_circle_down</i>
                  </div>
                  <p class="card-category">Central Heating</p>
                  <h3 class="card-title"><div id="messages"></div> </h3>
                </div>
                <div class="card-footer">
                  <div class="stats">
                    <i class="material-icons">autorenew</i> Flow
                  </div>
                </div>
              </div>
            </div>
            <div class="col-lg-3 col-md-6 col-sm-6">
              <div class="card card-stats">
                <div class="card-header card-header-danger card-header-icon">
                  <div class="card-icon">
                    <i class="material-icons">arrow_circle_up</i>
                  </div>
                  <p class="card-category">Central Heating</p>
                  <h3 class="card-title"><div id="messages3"></div> </h3>
                </div>
                <div class="card-footer">
                  <div class="stats">
                    <i class="material-icons">autorenew</i> Return
                  </div>
                </div>
              </div>
            </div>
            <div class="col-lg-3 col-md-6 col-sm-6">
              <div class="card card-stats">
                <div class="card-header card-header-success card-header-icon">
                  <div class="card-icon">
                    <i class="material-icons">arrow_circle_down</i>
                  </div>
                  <p class="card-category">Hot Water Tank</p>
                  <h3 class="card-title"><div id="messages"></div> </h3>
                </div>
                <div class="card-footer">
                  <div class="stats">
                    <i class="material-icons">autorenew</i> Flow
                  </div>
                </div>
              </div>
            </div>
            <div class="col-lg-3 col-md-6 col-sm-6">
              <div class="card card-stats">
                <div class="card-header card-header-danger card-header-icon">
                  <div class="card-icon">
                    <i class="material-icons">arrow_circle_up</i>
                  </div>
                  <p class="card-category">Hot Water Tank</p>
                  <h3 class="card-title"><div id="messages"></div> </h3>
                </div>
                <div class="card-footer">
                  <div class="stats">
                    <i class="material-icons">autorenew</i> Return
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div class="row">
            <div class="col-md-4">
              <div class="card card-chart">
                <div class="card-header card-header-success">
                  <div class="ct-chart" id="dailySalesChart"></div>
                </div>
                <div class="card-body">
                  <h4 class="card-title">Daily Sales</h4>
                  <p class="card-category">
                    <span class="text-success"><i class="fa fa-long-arrow-up"></i> 55% </span> increase in today sales.</p>
                </div>
                <div class="card-footer">
                  <div class="stats">
                    <i class="material-icons">access_time</i> updated 4 minutes ago
                  </div>
                </div>
              </div>
            </div>
            <div class="col-md-4">
              <div class="card card-chart">
                <div class="card-header card-header-warning">
                  <div class="ct-chart" id="websiteViewsChart"></div>
                </div>
                <div class="card-body">
                  <h4 class="card-title">Email Subscriptions</h4>
                  <p class="card-category">Last Campaign Performance</p>
                </div>
                <div class="card-footer">
                  <div class="stats">
                    <i class="material-icons">access_time</i> campaign sent 2 days ago
                  </div>
                </div>
              </div>
            </div>
            <div class="col-md-4">
              <div class="card card-chart">
                <div class="card-header card-header-danger">
                  <div class="ct-chart" id="completedTasksChart"></div>
                </div>
                <div class="card-body">
                  <h4 class="card-title">Completed Tasks</h4>
                  <p class="card-category">Last Campaign Performance</p>
                </div>
                <div class="card-footer">
                  <div class="stats">
                    <i class="material-icons">access_time</i> campaign sent 2 days ago
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div class="row">
            <div class="col-lg-6 col-md-12">
              <div class="card">
                <div class="card-header card-header-tabs card-header-primary">
                  <div class="nav-tabs-navigation">
                    <div class="nav-tabs-wrapper">
                      <span class="nav-tabs-title">Tasks:</span>
                      <ul class="nav nav-tabs" data-tabs="tabs">
                        <li class="nav-item">
                          <a class="nav-link active" href="#profile" data-toggle="tab">
                            <i class="material-icons">bug_report</i> Bugs
                            <div class="ripple-container"></div>
                          </a>
                        </li>
                        <li class="nav-item">
                          <a class="nav-link" href="#messages" data-toggle="tab">
                            <i class="material-icons">code</i> Website
                            <div class="ripple-container"></div>
                          </a>
                        </li>
                        <li class="nav-item">
                          <a class="nav-link" href="#settings" data-toggle="tab">
                            <i class="material-icons">cloud</i> Server
                            <div class="ripple-container"></div>
                          </a>
                        </li>
                      </ul>
                    </div>
                  </div>
                </div>

From what I see you do not have unique id's for each "card". You have it for the one with id="messages3"
and I understand that it works when you modify it's content with the line document.getElementById('messages3').append(messageElem);
But you need to have "message1", "message2" etc etc if you want to modify those other "cards" as well in similar way with lines like (as examples)
document.getElementById('messages1').append(messageElemOne);
document.getElementById('messages2').append(messageElemTwo);

In your function for incoming messages you have the line
let message = event.data;
Thats fine but here you then need to setup a filter. Either using if statements like in my example or a switch so you can update the correct html element. Filtering on some specific and unique part of the message data. If there is no such "keys" you can use, you may have to consider adding this when you create the message in the sourcing unit. But this is pure javascript, you can study my example how I did it

1 Like

Appreciate the help; I genuinely have tried for hours, but I have got no where on this. My lack of knowledge in programming seems to have beat me on this one. I don't have enough JavaScript understanding to get it working. I've tried inserting bits of the example code.....but no luck at all. Is there any apps / website that the code can be put in to test the messages coming through; because its hard to workout what I'm doing wrong.

Below is the JSON message coming through the websocket. What I need to filter on is the "topic":"TEMP_AM1" and return the payload....to one 'message1' and then need to the look for the other topic "TEMP_AM3" and return the payload relevant into 'message2'.

Honestly I need help on this one.

Regards,
Ant

{"payload":54,"topic":"TEMP_AM1","_msgid":"f278750f.4e7a88","_event":"node:80f7e615.dfbe08"}
{"payload":42,"topic":"TEMP_AM3","_msgid":"127ac951.804707","_event":"node:e5aa700e.decb7"}
{"payload":54,"topic":"TEMP_AM1","_msgid":"a99d0035.255c2","_event":"node:80f7e615.dfbe08"}
{"payload":42,"topic":"TEMP_AM3","_msgid":"42b3741a.99e9dc","_event":"node:e5aa700e.decb7"}
{"payload":54,"topic":"TEMP_AM1","_msgid":"7e73ca82.05b584","_event":"node:80f7e615.dfbe08"}
{"payload":42,"topic":"TEMP_AM3","_msgid":"41a14cee.f07194","_event":"node:e5aa700e.decb7"}
{"payload":54,"topic":"TEMP_AM1","_msgid":"1fd2d253.d9272e","_event":"node:80f7e615.dfbe08"}
{"payload":42,"topic":"TEMP_AM3","_msgid":"a2648242.d903a","_event":"node:e5aa700e.decb7"}
{"payload":53,"topic":"TEMP_AM1","_msgid":"a7cb87d7.3baa68","_event":"node:80f7e615.dfbe08"}
{"payload":42,"topic":"TEMP_AM3","_msgid":"495f77db.b904a8","_event":"node:e5aa700e.decb7"}
{"payload":53,"topic":"TEMP_AM1","_msgid":"39babf59.10ffd","_event":"node:80f7e615.dfbe08"}
{"payload":42,"topic":"TEMP_AM3","_msgid":"c5530d7a.bea7f","_event":"node:e5aa700e.decb7"}
{"payload":53,"topic":"TEMP_AM1","_msgid":"182181f4.57b68e","_event":"node:80f7e615.dfbe08"}
{"payload":42,"topic":"TEMP_AM3","_msgid":"8a8e4541.e2ff58","_event":"node:e5aa700e.decb7"}

Hi .. if you want to check the value of a javascript variable at any point in your code you usually do a

console.log(<nameOfVariable>)

and then you open your browser devtools (F12) to see whats being logged.

For example you have this code :

// message received - show the message in div#messages
socket.onmessage = function(event) {
  let message = event.data;

console.log(message)   // log whats being received

 // let messageElem = document.createElement('div');
 // messageElem.textContent = message + '&#8482;';
//  document.getElementById('messages3').innerHTML = "";
//  document.getElementById('messages3').append(messageElem);
}

In order to make your code a bit more dynamic
It would be better if you renamed the ids of your divs (that will hold the msg payload value) to match the topic of the msg being sent from NR

For example <div id="messages3"></div> to <div id="TEMP_AM3"></div>
and your javascript can be

// message received - show the message in div#messages
socket.onmessage = function(event) {
  let message = event.data;

  document.getElementById(message.topic).innerHTML = message.payload;

}

ofcourse its best to implement a few error checks with IF statements to check if the div really exists for that topic .. but see how it goes for now.

[EDIT]

Check this Javascript introductory tutorial out by Brad Traversy
JavaScript Crash Course For Beginners

Come to this a bit late - are you using Dashboard? Or some other webpage? If some other webpage, what is serving that page?

The reason I ask is that this would all be a lot easier with a suitable framework. Dashboard uses Angular v1. If you are using a separate webpage then if you use uibuilder, you would get the scaffolding for the communications done for free and all you need is a simple function in the front-end script.

Either way, you wouldn't need to mess with manual DOM manipulations of the page.

As for a direct answer to the immediate question, something like:

// assuming your incoming websocket msg is in a variable called `msg`
if ( msg.topic === 'TEMP_AM1') {
    message1 = msg.payload
} else if ( msg.topic === 'TEMP_AM2') {
    message2 = msg.payload
} else {
    // do something or nothing, whatever you want
}

Thanks for the response....really helpful!

Your comment about frameworks etc. this has been a debate in my head for months. I have just setup a "internal" website using apache2 from a linux server. Its running on my own internal server not internet hosted. Its a simple dashboard; I had downloaded a starter and then used bootstrap; general html and JavaScript. I'd appreciate some guidance, whatever you can offer if you think I could go about this much simpler.

Thanks,
Ant

What is the Dashboard? Do you have a link to the framework you are describing?

Cheers,
Ant

Dashboard is the main user interface tool for Node-RED. Check out node-red-dashboard.

Both Dashboard and uibuilder provide integrated websocket connections.

Dashboard is highly opinionated. It does a lot for you but the downside is that you don't get as much flexibility and have to live with the bits of Angular v1 that it lets you get to. But it also gives you a very easy to start was of building a data-driven user interface.

uibuilder is unopinionated. you can use any framework you like. It comes with VueJS and bootstrap-vue with a default template but you certainly don't have to use that and it has been tested with various frameworks including jQuery, Moon, REACT and others. It helps but doesn't get in the way. Downside is that you have to write your own front-end code, no site builder as yet.

So both give you data-driven web interfaces, you don't need a separate web server since Node-RED supplies that for you. Though you can use one to front Node-RED if you like, that can provide performance gains and can add a security layer.

There you go, hopefully that puts things into context for you. Use Dashboard if you want to get going quickly but don't mind some layout limitations. Use uibuilder if you want full control and are prepared to do some coding.

So I got the code to display in two different locations; but it is the same data. I seem unable to read what the topic is and then pass the payload to another variable.

I've attached the code......to see if have got something totally wrong.

Cheers,
Ant

<script type="text/javascript">
  
  let sockPet = new WebSocket("ws://192.168.1.50:1880/ws/request");

// message received - show the message in div#messages
let socket = new WebSocket("ws://192.168.1.50:1880/ws/request");

// message received - show the message in div#messages
socket.onmessage = function(event) {
  let message = event.data;

  console.log(message)    // log whats being received

  // assuming your incoming websocket msg is in a variable called `msg`
if (message.topic === "TEMP_AM3") 
  {
    message3 = (message.payload)
    let messageElemthree = document.createElement('div');
  messageElemthree.textContent = message3;
  document.getElementById('messages3').innerHTML = "";
  document.getElementById('messages3').append(messageElemthree)
  }


else (message.topic === "TEMP_AM1") 
{
  message1 = (message.payload)
  let messageElemone = document.createElement('div');
  messageElemone.textContent = message1;
  document.getElementById('messages1').innerHTML = "";
  document.getElementById('messages1').append(messageElemone);
}




}

  
</script>