Filter results by

Web Window to MQTT

Node-RED is an impressive development tool. But once we have finalized the flow, it might be nicer for end users to view and control the edge device functions through a custom Web interface, with Node-RED running in the background.

In this section we develop our own Web page code, which we paste into Node-RED to:

  1. Interconnect and interact with edge devices through ARTIK (as both local host and MQTT broker)
  2. Display returned edge device data through an appropriate Web browser user interface
  3. Selectively allow users to provide or change certain data to send back to the network.

Prerequisites

We will build on work done in the previous MQTT tutorials:

Using Web Input to Control Edge Devices

Node-RED makes it simple to host a local Web page and request user input, as it supports standard HTTP GET and POST operations within its intuitive node structure.

  1. Create a local HTTP server that responds to GET requests with Web page HTML, within which you will give users a way to choose the Photon LED color.

    • Select an HTTP input node and drag it to the canvas. This input node will monitor browser accesses to the localhost IP address (the same as you used in previous examples).

    • Double-click it to enter /color as the URL and GET as the supported Method.

    • Select a Template node, which you will use to enter the HTML for the page you want shown to the user.

    • Double-click it and paste the following HTML code for a simple drop-down "Choose a color" selector box (you can see an image of it at the end of this section).

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
        <html>
        <body>
      
        <form method="POST" action="/color"> 
        <select name="color" onchange="this.form.submit()">
          <option>Choose a color...</option>
          <option value="RED">RED</option>
          <option value="GREEN">GREEN</option>
          <option value="BLUE">BLUE</option>
        </select>
        </form>
      
        </body>
        </html>
    • Select an HTTP Response output node.

    • Connect the input node to the template node, and the template node to the response node.

    Now you have a server flow that waits for localhost/color URL queries, and responds with the HTML code you specified.

  2. Create another local HTTP server; this one takes the user's color choice as a POST submittal from the Web browser, and then converts and publishes it as a 'color' message to the MQTT broker.

    • Select an HTTP input node; once again, this input node will monitor browser accesses to the localhost IP address.

    • Double-click it to again enter /color as the URL, but this time enter POST as the supported Method.

    • Select a Function node, where you will describe the action the server should take upon receiving the POST message.

    • Double-click it and paste the following function code, which takes the color selection made from the drop-down box and copies it as the payload of the message that will eventually be sent over MQTT.

      1
      2
      3
        var color = msg.payload.color;  
        msg.payload = color;  
        return msg;  
    • For the MQTT output, disconnect and re-use the MQTT 'color' output node you created in an earlier exercise.

    • Connect the POST input node to the function node, and the function node to the MQTT color output node.

  3. Make one connection between flows, from the POST node output to the GET template input. Without it, the browser would not consider POST requests to be complete. (You can check transaction timelines in the Chrome browser debug console, under Developer tools – Network.)

  4. Click Deploy to run this new flow set.

  5. Open a Web browser at the localhost IP address:port you have been using all along, but in the '/color' sub-folder: http://192.168.xxx.xxx:1880/color

Now you have a simple Web page that allows you to select the color message that you want to send to the Photon module. Every time you change it, the onchange= statement in the GET flow triggers a new operation in the POST flow, and a new MQTT output message is sent.

Returning Edge Device Data to Web Page

In a previous exercise, you set up a flow to receive 'brightness' messages and display them in the debug sidebar. Let's try displaying that data in a more user-friendly way.

  1. Create a short flow to store the 'brightness' messages in a file, by connecting:

    • A File output endpoint node, with a Filename of /root/brightness.txt and an Action of 'overwrite file' (leave the newline box checked)

    • Your MQTT 'brightness' input node (the one your created here).

    Now, instead of the brightness value going to the debug pane each time, it will get sent to a file. Each value replaces the previous one so only a single message is stored.

  2. Create a flow to build an HTTP message out of the 'brightness' value you are now storing in a file. Build the flow as shown using:

    • The HTTP GET brightness node you created previously
    • The "double-ended" File node, which takes the GET frame and inserts the 'brightness' payload; again give it the Filename of /root/brightness.txt

    • A Template node with the code {{payload}} pasted in

    • An HTTP Response node.

  3. Insert the following code in your original GET flow template, starting right after the existing </form> statement shown as line 1.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     </form>
    
     <p>Sensor value (updated once every 1s):</p>
     <p id="demo"></p>
    
     <script>
     var myVar = setInterval(myTimer, 1000);
     function myTimer() {
         var txtFile = new XMLHttpRequest();
         txtFile.open("GET", "/brightness", true);
         txtFile.send();
         txtFile.onreadystatechange = function() {
            if (txtFile.readyState == 4 && txtFile.status == 200) 	
              if (txtFile.responseText != "")
                 document.getElementById("demo").innerHTML = txtFile.responseText;
         }
     }
     </script>

    The XMLHTTPRequest code above may look complicated, but is a very common way to access data files. You'll find yourself using it often in these applications.

  4. Click Deploy. Your canvas should look something like this.

  5. Refresh your Web browser at localhost:1880/color to see the results.

The code now reads messages from the file once per second and updates the 'brightness' value displayed.

Troubleshooting Tip. If you are having trouble understanding the functions behind any of the nodes we have set up here, don't worry. Try attaching a Debug node anywhere to inspect what output the flow is generating at that point. Double-click the debug node and configure it to display the entire message instead of just the payload – you may find the extra detail helpful.

Summary

Generating Web pages for user interaction makes the edge device data "come alive" – you can process it and present it in a meaningful way using straightforward HTML and JavaScript code.

Moreover, with ARTIK and Node-RED relieving you of the burden of edge device support, you can focus on creating a compelling Web page. You can combine data from many diverse devices into a coherent and colorful Web presentation.

If you think you will be using a Web interface the way we've touched on in this article, you may want to consider using Node-RED as a background support program for your application. You'll only need to update the MQTT broker IP address on each restart; we'll show you how to do this automatically in an upcoming tutorial.

Extra Credit

We kept the Web page display code pretty basic, to make the concepts clear. But with a little more coding, you could make the page a lot more appealing.

For example, the LED color selector could be continuously variable, using the underlying page HTML in this example from w3schools.com. Try out the sliders!

Red Green Blue
255 0 0

Likewise, the sensor data could be displayed as a gauge, like this one from JustGage.

In this section, we show you the extra code needed for the slider function. Look it over – it's cleverly done using HTML tables (no special graphics libraries needed). This nifty code is by w3schools.com.

  1. In the Node-RED flow: add this code into the GET flow Template node, right under the </script> line in the existing code.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
     <table class="w3-table w3-border" style="width:100%">
     <tr>
     <th>Red</th>
     <th>Green</th>
     <th>Blue</th>
     </tr>
     <tr>
     <td id="valRed">255</td>
     <td id="valGreen">0</td>
     <td id="valBlue">0</td>
     </tr>
     <tr>
     <td id="red" style="height:50px;background-color:red"></td>
     <td id="green" style="background-color:green"></td>
     <td id="blue" style="background-color:blue"></td>
     </tr>
     <tr>
     <td>
     <input onchange="changeRed(this.value)" type="range" id="slideRed" name="slideRed" value="255" min="0" max="255">
     </td>
     <td>
     <input onchange="changeGreen(this.value)" type="range" id="slideGreen" name="slideGreen" value="0" min="0" max="255">
     </td>
     <td>
     <input onchange="changeBlue(this.value)" type="range" id="slideBlue" name="slideBlue" value="0" min="0" max="255">
     </td>
     </tr>
     </table>
     <div id="change" style="height:50px"></div>
     <div id="changetxt" class="w3-large w3-padding-large w3-center"></div>
     <div class="w3-center">
     </div>
    
     <script>
     changeAll();
     function changeRed(value) {
         document.getElementById('valRed').innerHTML = value;
         changeAll();
     }
     function changeGreen(value) {
         document.getElementById('valGreen').innerHTML = value;
         changeAll();
     }
     function changeBlue(value) {
         document.getElementById('valBlue').innerHTML = value;
         changeAll();
     }
     function changeAll() {
         var r = document.getElementById('valRed').innerHTML;
         var g = document.getElementById('valGreen').innerHTML;
         var b = document.getElementById('valBlue').innerHTML;
         document.getElementById('change').style.backgroundColor = "rgb(" + r + "," + g + "," + b + ")";
         document.getElementById('changetxt').innerHTML = "rgb(" + r + ", " + g + ", " + b + ")";
    
     // This is our added section for generating a POST request every time the color slider changes
     // Once again, we are using very standard POST code (similar to the GET request)
         var MQTTsite = new XMLHttpRequest();
         MQTTsite.open("POST", "/color", true);
         MQTTsite.setRequestHeader('Content-Type', 'application/json');
         color = convert(r) + convert(g) + convert(b);
         MQTTsite.send(JSON.stringify({"color":color}));
     }
     // We send 2 hex digits each for R - G - B
     function convert(integer) {
         var str = Number(integer).toString(16);
         return str.length == 1 ? "0" + str : str;
     };
     </script>
  2. In the Photon Build IDE code, make the following changes.

    • Under the header void callback(char* topic, ... add:

      1
      2
        long i;
        short int r, g, b;
    • Now remove the line RGB.color(255, 255, 255); and replace it with:

      1
      2
      3
      4
      5
      6
      7
        {
          i = strtol(message, NULL, 16);
          b = i & 0xFF;
          g = (i >> 8) & 0xff; 
          r = (i >> 16) & 0xff; 
          RGB.color(r, g, b);
        }
      

That's all there is to it. When the Photon receives a message other than RED, BLUE, or GREEN, it will try to interpret it as a string of six hex digits corresponding to R-G-B.

Last updated on: