How to create simple web application to view the Particle Stream Events

As we know, we can monitor the published event via the Particle console https://console.particle.io/

But say I would like to build a simple web application that would present this stream of events in user friendly way, can someone give me a kick start advice?

As an example, say I have a light sensor attached to Particle device. When the light in the room lit, it will publish and event and the web app via some GUI will indicate so.

Thank you!

The best way to do that would be via the Particle API JS particularly getEventStream()

But any SSE implementation you can find online will work as long you can authenticate with the cloud endpoint.

You can even look at the raw event stream with a browser like Chrome like this
view-source:https://api.particle.io/v1/devices/<deviceID>/events/?access_token=<accessToken>

Thank you @ScruffR, just what I need!

Do you happen to know if there is open source sample project for this? Just for my reference.

Unfortunately not. There is only one example in the GitHub repo example folder for logging in.

However, I think there are multiple threads for various intents in this forum.
And there are also individual call samples in the docs I linked above
e.g.

There are a bunch of examples of using the SSE event stream from node.js in sse-examples.

2 Likes

Hi,
here is my approach to use particle.getEventStream
I just listen for event named “DEBUG” and then display on innerHTML

here is Photon code:

#include <PietteTech_DHT.h>
#include "Particle.h"

#define DHTTYPE DHT11 // Sensor type DHT11/21/22/AM2301/AM2302
#define DHTPIN D3 // Digital pin for communications



PietteTech_DHT DHT(DHTPIN, DHTTYPE);



char status[] = "{\"debug\":%s}" ;

char results[] = "{\"humidity\":%.02f,\"temperature_f\":%.02f,\"temperature_k\":%.02f,\"temperature_c\":%.02f,\"dewpoint\":%.02f,\"dewpointslow\":%.02f}" ;

char msg_st[sizeof(status) + 16];

char msg_res[sizeof(results) + 32 ];

double humidity = 0.0;
double temperature_k = 0.0;
double temperature_c = 0.0;
double temperature_f = 0.0;
double dewpoint = 0.0;
double dewpointslow = 0.0;

unsigned long interval = 0;

void setup()
{

Serial.begin(9600);

Particle.variable("data", msg_res);


DHT.begin();

}

void loop()
{


 int result = DHT.acquireAndWait(1000); // wait up to 1 sec (default indefinitely)

    switch (result) {
        case DHTLIB_OK:{
           
           snprintf(msg_st, sizeof(msg_st),status,  "\"ok\"");
           Particle.publish("DEBUG", msg_st, 60, PRIVATE);
           
        }break;

        case DHTLIB_ERROR_CHECKSUM:{
          
          snprintf(msg_st, sizeof(msg_st),status, "\"Checksum error\"");
          Particle.publish("DEBUG", msg_st, 60, PRIVATE);
          
        }break;

    case DHTLIB_ERROR_ISR_TIMEOUT:{
          
          snprintf(msg_st, sizeof(msg_st), status, "\"ISR time out error\"");
          Particle.publish("DEBUG", msg_st, 60, PRIVATE);
          
        }break;

    case DHTLIB_ERROR_RESPONSE_TIMEOUT:{
         
          snprintf(msg_st, sizeof(msg_st), status, "\"Response time out error\"");
          Particle.publish("DEBUG", msg_st, 60, PRIVATE);
          
        }break;

    case DHTLIB_ERROR_DATA_TIMEOUT:{
       
        snprintf(msg_st, sizeof(msg_st), status, "\"Data time out error\"");
        Particle.publish("DEBUG", msg_st, 60, PRIVATE);
         
        }break;

    case DHTLIB_ERROR_ACQUIRING:{
       
        snprintf(msg_st, sizeof(msg_st), status, "\"Acquiring\"");
        Particle.publish("DEBUG", msg_st, 60, PRIVATE);
       
       }break;
    
    case DHTLIB_ERROR_DELTA:{
        
        snprintf(msg_st, sizeof(msg_st), status, "\"Delta time to small\"");
        Particle.publish("DEBUG", msg_st, 60, PRIVATE);
        
        }break;

    case DHTLIB_ERROR_NOTSTARTED:{
      
        snprintf(msg_st, sizeof(msg_st), status, "\"Not started\"");
        Particle.publish("DEBUG", msg_st, 60, PRIVATE);
        
       }break;

    default:{
        
       
        snprintf(msg_st, sizeof(msg_st), status, "\"Unknown error\"");
        Particle.publish("DEBUG", msg_st, 60, PRIVATE);
        
        }break;

}

if (millis() - interval > 1000) {
  interval = millis();
  
 humidity = (double)DHT.getHumidity();

 temperature_c = (double)DHT.getCelsius();

 temperature_f = (double)DHT.getFahrenheit();
 
 dewpoint = (double)DHT.getDewPoint();
 
 temperature_k = (double)DHT.getKelvin();
  
 dewpointslow = (double)DHT.getDewPointSlow();
 
 snprintf(msg_res, sizeof(msg_res), results
         ,humidity
         ,temperature_f
         ,temperature_k
         ,temperature_c
         ,dewpoint
         ,dewpointslow 
         );
 Particle.publish("ALL_DATA", msg_res, 60, PRIVATE);
}

}

and here HTML:

<html>
  <head>

<style>
  html {
    background-color: black; 
}
  					
  </style>

 <body>
    
    
    <p style="color: white;">Debug info: <span id="MY_DEBUG"></span></p>

    <div style="display: inline-block; margin: 50px; ">
      <div id="chart_div" style="width: 1200px; height: 360px; opacity: 0.9"></div>
      <div id="chart_div2" style="width: 1200px; height: 360px; opacity: 0.9"></div>
    </div>
  </body>
  
   <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
   <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/particle-api-js@8/dist/particle.min.js"></script>

   <script type="text/javascript">


     var accessToken = "XXXXXXXXXXXXXXXXXXXX"; //not display for security
     var deviceID = "XXXXXXXXXXXXXXXXX" //not display for security

      var dane = 0; 
      var VarName = "data";
      var D_info = document.getElementById("MY_DEBUG");
      D_info.innerHTML = "wait for DEBUG";
      google.charts.load('current', {'packages':['gauge']});
      google.charts.setOnLoadCallback(drawChart);
      var particle = new Particle();


    function get_data(){
         
            particle.getVariable({ deviceId: deviceID , name: VarName, auth: accessToken }).then(

            function(data){

               console.log('Device variable retrieved successfully:', JSON.parse(data.body.result));
               dane = JSON.parse(data.body.result);
              
               
            }, 
            function(err) {
               console.log('An error occurred while getting attrs:', err);
           });
         }
    
 

   function get_event(){
     
       particle.getEventStream({ name: 'DEBUG', auth: accessToken }).then(function(stream) {
             stream.on('event', function(data) {
             console.log("Event: ", JSON.parse(data.data).debug); 
             D_info.innerHTML = JSON.parse(data.data).debug;
             
           });
          });


     }

        
      function drawChart() {
         
        var data = google.visualization.arrayToDataTable([
          ['Label', 'Value'],
          ['temp_k. [K]', 0],
          ['temp_c. [C]', 0],
          ['temp_f. [F]', 0]
        ]);
        
        var options = {
          width: 1200, height: 360,
          redFrom: 90, redTo: 100,
          yellowFrom: 40, yellowTo: 90,
          greenFrom: 0, greenTo: 40,
          minorTicks: 10,
          majorTicks: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
          easing: 'inAndOut'
          
        };        
        var data2 = google.visualization.arrayToDataTable([
          ['Label', 'Value'],
          ['humi. [%]', 0],
          ['devp. [%]', 0],
          ['devpsl. [%]', 0]         
        ]);

        var options2 = {
          width: 1200, height: 360,
          redFrom: 900, redTo: 1200,
          yellowFrom:300, yellowTo: 900,
          greenFrom: 0, greenTo: 300,
          minorTicks: 10,
          majorTicks: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
          min: 0,
          max: 1200,
          easing: 'inAndOut',
           
        };

        var chart = new google.visualization.Gauge(document.getElementById('chart_div'));
        var chart2 = new google.visualization.Gauge(document.getElementById('chart_div2'));

        chart.draw(data, options);
        chart2.draw(data2, options2);

        setInterval(function() {

          get_data();
          if(dane != 0){
          //console.log("from_s_interval",dane);  
          data.setValue(0, 1, dane.temperature_k.toFixed(2));
          data.setValue(1, 1, dane.temperature_c.toFixed(2));
          data.setValue(2, 1, dane.temperature_f.toFixed(2));
          chart.draw(data, options);
          data2.setValue(0, 1, dane.humidity.toFixed(2));
          data2.setValue(1, 1, dane.dewpoint.toFixed(2));
          data2.setValue(2, 1, dane.dewpointslow.toFixed(2));
          chart2.draw(data2 ,options2);
          }   
        }, 1000);        
      }

     window.addEventListener('load',function() {
	/* hack to prevent firing the init script before the window object's values are populated */
	
     setTimeout(get_event, 200);
     

    },false);

    </script>
  </head>
 
</html>
2 Likes

thank you so much @dreamER :grinning:

Can I use this method to make a live graph in HTML? :thinking:

Yes, you can! I have made live graphs using HTML5 canvas tags. You add an event handler that plots data in a graph–you have full control. There are also lots of pre-made javascript widgets that you use.

Here’s a screenshot of a simple example (I only let it run for a few datapoints–it is self ranging in the y-axis):
Screenshot 2021-05-14 175607

1 Like

Ah nice. Let me get on it :muscle: Thanks @bko

2 Likes

You can even use Plotly.js and MQTT and be as fast as possible :grin:
Like here:

:wink:

Thanks a lot @dreamER. :laughing:

Sure :wink: no problemo here is the code
With No autorange for layouts and with pause button. Photon is streaming data as fast as every 200ms.


<html>
<head>
  <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript"></script>
  <meta charset="utf-8"/>
</head>
<style>  

 #MQ2 { 
    float:left;  
    width:33%; 
                 
 } 
 #altitude_m{ 
    float:left;               
    width:33%; 
                
 } 
 #altitude_ft{ 
    float:left;             
    width:33%;                
 } 
 #humidity{ 
    float:left;                 
    width:33%;                
 } 
 #pressure_hPa{ 
    float:left;              
    width:33%;                 
 } 
 #temperature_c{ 
   float:left;                
   width:33%;                
 } 

.btn-fs {
  display: inline-block;
  top: 20px;
  left: 10px;
  padding: 5px;
  color: black;
  border: 3px solid green;
  border-radius: 7px;
  z-index: 1;
  cursor: pointer;
  font-size: 55px;
}
             
</style> 
<body>
<div>
<span id="info" style="color:red; font-size:35px">info</span>
</div>
<div>
<div class="btn-fs" id="btnFS">
    Pause
</div>
</div>

 <div id="temperature_c" ></div>
 <div id="humidity" ></div>
 <div id="pressure_hPa" ></div>
 <p>
 <div id="MQ2" ></div>
 <div id="altitude_m" ></div>
 <div id="altitude_ft" ></div>

<script>
var DataArrays = [[],[],[],[],[],[]]; 
var layouts = [[{ colorway: ['green'], width: 400, height: 500, xaxis: { range: [0, 100] }, yaxis: {range: [0, 100]}}], [{ colorway: ['red'], width: 400, height: 500, xaxis: { range: [0, 100] }, yaxis: {range: [0, 100]}}],[{ colorway: ['blue'], width: 400, height: 500, xaxis: { range: [0, 100] }, yaxis: {range: [0, 1100]}}],[{ colorway: ['orange'], width: 400, height: 500, xaxis: { range: [0, 100] }, yaxis: {range: [0, 25]}}],[{ colorway: ['aqua'], width: 400, height: 500, xaxis: { range: [0, 100] }, yaxis: {range: [0, 4200]}}],[{ colorway: ['black'], width: 400, height: 500, xaxis: { range: [0, 100] }, yaxis: {range: [0, 1280]}}] ];
var samples = 0;
var get_ok = false;
var paused = false;
var Main_data = {};
var data_in_status = 0;
var info = document.getElementById('info'); 
info.innerHTML = "info: initial page opened wait for new event....";
var client = new Paho.MQTT.Client('your_mqtt_broker_ip_address_here', Number(1884), 'jSclient'); 

var fs = document.getElementById('btnFS'); 
fs.addEventListener('click', pause.bind(null), false);

client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
client.connect({onSuccess:onConnect});

function pause(){
if(!paused){
 
  paused = true;
  return;
}else{
  
  paused = false;
  return;

}
}

function plot(res){
   for(key in res){
       DataArrays[data_in_status].splice(0,0,res[key]); 
 
   var data = [
     {
       type: "indicator",
       mode: "number+delta",
       value: res[key],
       number: { font: { size: 80, color: 'blue'}},
      delta: { reference: DataArrays[data_in_status][DataArrays[data_in_status].length-1], valueformat: ".0f" },
       ticker: { showticker: false },
       domain: { y: [0.25, 0.75], x: [0.25, 0.75] },
       title: { text: key }
      },
  
    {
       y: DataArrays[data_in_status]
     }
   ];

   Plotly.newPlot(key, data, layouts[data_in_status][0]);  

   if(DataArrays[data_in_status].length >=100){
      DataArrays[data_in_status].pop();
   }
   data_in_status++;

   if(data_in_status == 6){
      data_in_status = 0;
      }
   }
}
//called when the client connects
function onConnect() {
  console.log("onConnect_success");
  client.subscribe("all_data");  
}
// called when a message arrives
function onMessageArrived(message) {
  var res = JSON.parse(message.payloadString);
   if(paused){
      get_ok = false;
      samples ++;
      info.innerHTML = "warning: " + samples +" new samples omited !!!"
  }else{
   get_ok = true;
   samples = 0;
        info.innerHTML = "info: new data detected !!!"
  }    
  Main_data = res.all_data[0].data[0];
}
// called when the client loses its connection
function onConnectionLost(responseObject) {
  if (responseObject.errorCode !== 0) {
    console.log("onConnectionLost:"+responseObject.errorMessage);
    get_ok = false;
    info.innerHTML = "warning: connection lost !!!";
    client.connect({onSuccess:onConnect});
  }
}

setInterval(function() {
   if(get_ok){
      plot(Main_data);
      //info.innerHTML = "info: new sensors data event " + samples + " detected !!!"; 
    }
}, 200);
</script>
</body>
</html>

2 Likes