📈 widget Graphique en temps réel sur entrée analogique (avec exemple Sonomètre)

Bonjour à tous,

il y a déjà quelques mois, j’avais eu le plaisir de vous proposer des widgets pour vos capteurs analogiques

Aujourd’hui, je vous propose d’ajouter un graphe dynamique sur votre tableau de bord.
grapheDyn

Ce type de graphe est très utile pour des valeurs analogiques qui changent très rapidement comme le niveau sonore d’une pièce, mesuré par exemple par un capteur de bruit tel que le SEN12642 de Sparkfun
image
ou encore le Capteur de Niveau Sonore Analogique Gravity par DFRobot.
image .

Ces capteurs peuvent être connectés à une entrée analogique de l’IPX800 et émettre un signal analogique proportionnel au niveau sonore relevé dans une pièce. Ce type de capteur est souvent utilisé pour la fabrication d’un système d’alarme par exemple.
Vous ne pourrez bien sûr pas entendre ce qui se passe dans la pièce, mais vous pourrez visualiser le niveau sonore, ce qui peut être très utile pour une levée de doute.

Le but de ce tuto est de vous montrer comment visualiser des échantillonnages rapides.
Pour réaliser cela, nous utiliserons la Librairie smoothie.js disponible ici: http://smoothiecharts.org/

Etape 1
Sur le dashboard, créez une source de données à taux de rafraîchissement élevé (0.5 seconde par exemple). Dans mon exemple, elle s’appellera STATUS et pointera sur le fichier Status.xml de l’IPX800 V4.
image

Etape 2
Ajoutez un widget de type HTML (hauteur 1 bloc) et collez ce code

return `
<div style="margin-top:15px;margin-left:15px;">
<span>Valeur analogique : </span>
<span id="valGraph1">0</span></div>
<script src='http://smoothiecharts.org/smoothie.js'>
`;

Ce code permet de charger la library. Pour être indépendant du web, vous pouvez télécharger le fichier javascript et l’héberger sur votre réseau local si vous avez un NAS par exemple. Il faudra adapter l’URL du script. Dans mon cas, l’ayant placée sur mon syno, ça devient
<script src='http://192.168.0.9/gce/smoothie.js'>

Etape 3
Créez un deuxième widget HTML (hauteur 3 blocs) et insérez ce code :

var mini=0;
var maxi=100;
var speed=15; //to 100

return `
<center>
<button id=bgraph onclick="createTimeline();">GRAPHIQUE</button>
<canvas id="chart" width="280" height="150"></canvas>
</center>
<style>
  div.smoothie-chart-tooltip {
  background: #444;
  padding: 1em;
  margin-top: 20px;
  font-family: consolas;
  color: white;
  font-size: 10px;
  pointer-events: none;
  }
</style>
    <script type="text/javascript">
      function createTimeline() {
         var random = new TimeSeries();
         setInterval(function() {
             random.append(Date.now(), parseFloat(document.getElementById("valGraph1").innerText));
         }, 500);
var chart = new SmoothieChart({millisPerPixel:${speed},grid:{verticalSections:0},tooltip:true,maxValue:${maxi},minValue:${mini},horizontalLines:[{color:'#ffffff',lineWidth:1,value:0}]});
         chart.addTimeSeries(random, { strokeStyle: 'rgba(0, 255, 0, 1)', fillStyle: 'rgba(0, 255, 0, 0.2)', lineWidth: 4 });
         chart.streamTo(document.getElementById("chart"), 500);
         document.getElementById("bgraph").style.visibility = 'hidden';
      }
    </script>
`;

Dans ce code, renseignez les valeurs Mini, Maxi et speed en fonction de vos besoins. Speed correspond à la vitesse de défilement du graphe, sa valeur va de 0 à 100

Après création du widget, vous pouvez voir le bouton GRAPHIQUE qui servira à initialiser le graphique.

Etape 4
Créez un troisième widget de type HTML (hauteur 1 bloc) et collez ce code :

s= (((datasources["STATUS"]["response"]["analog0"] * 0.00323)- 1.63) / 0.0326).toFixed(2);
document.getElementById("valGraph1").innerText=s; //(Math.random() * 100 ).toFixed(2);

Ce widget sert uniquement à ajouter une valeur au graphique à chaque fois que la source de données se met à jour. Nous envoyons la valeur analogique dans un élément Html, qui sera alors lu par le graphe.
Vous devrez adapter la formule selon le type de capteur utilisé.

Etape 5
Vous avez terminé, sauvegardez l’IHM puis cliquez sur le bouton GRAPHIQUE du deuxième widget :slight_smile:

Bon graph.

10 J'aimes

Deuxième partie du tutoriel
Nous allons appliquer ce qui précède au capteur de niveau sonore fabriqué par DFRobot.
image

La sortie analogique de ce capteur émet une tension linéairement proportionnelle au niveau sonore mesuré.
à 30 dBA => 0.6V
à 130 dBA => 2.6V
Nous pourrons donc connecter la sortie du capteur directement sur une entrée analogique.
image

Etape 1

  • Connectons la broche A sur l’entrée analogique n°1 de l’IPX800
  • Connectons la broche + au 3.3V de l’IPX800
  • Connectons la broche - au Gnd de l’IPX800

Etape 2
Configurons la formule du capteur sur l’entrée analogique 1
image
En fait, c’est la formule du voltmètre remultipliée par un coefficient de 50.

Etape 3
Créez le 1er Widget HTML et collez ce code

return `
<div style="margin-top:15px;margin-left:15px;">
    <span>Niveau sonore (dBA) : </span>
    <span id="valGraph1">0</span><br>
    <span id=ambiance>calme</span>
</div>
<script src='http://smoothiecharts.org/smoothie.js'>
`;

Sur ce widget, nous avons ajouté un élément nommé ambiance.

Etape 4
Créez le deuxième widget HTML

var mini=20;
var maxi=130;
var speed=15; //to 100




return `
<center>
<button id=bgraph onclick="createTimeline();">GRAPHIQUE</button>
<canvas id="chart" width="280" height="150"></canvas>
</center>
<style>
  div.smoothie-chart-tooltip {
  background: #444;
  padding: 1em;
  margin-top: 20px;
  font-family: consolas;
  color: white;
  font-size: 10px;
  pointer-events: none;
  }
</style>
    <script type="text/javascript">
      function createTimeline() {
         var random = new TimeSeries();
         setInterval(function() {
             random.append(Date.now(), parseFloat(document.getElementById("valGraph1").innerText));
         }, 500);
var chart = new SmoothieChart({millisPerPixel:${speed},grid:{verticalSections:0},tooltip:true,maxValue:${maxi},minValue:${mini},horizontalLines:[{color:'#ffffff',lineWidth:1,value:0}]});
         chart.addTimeSeries(random, { strokeStyle: 'rgba(0, 255, 0, 1)', fillStyle: 'rgba(0, 255, 0, 0.2)', lineWidth: 4 });
         chart.streamTo(document.getElementById("chart"), 500);
         document.getElementById("bgraph").style.visibility = 'hidden';
      }
    </script>
`;

Dans ce code, seules les valeurs Mini et Maxi ont été adaptées

Etape 5
Créez le troisième widget qui alimentera les données avec la valeur de l’entrée analogique n°1.

s= (((datasources["STATUS"]["response"]["analog0"] * 0.000050354 * 50))).toFixed(2);
document.getElementById("valGraph1").innerText=s; //(Math.random() * 100 ).toFixed(2);
if (s<=50){document.getElementById("ambiance").innerText="Calme";
          }else if (s >50 && s<=55) {document.getElementById("ambiance").innerText="Relativement calme";
          }else if (s>55 && s<=60) {document.getElementById("ambiance").innerText="Bruits courants";
          }else if (s>60 && s<=65) {document.getElementById("ambiance").innerText="Supportable";
          }else if (s>65 && s<=70) {document.getElementById("ambiance").innerText="Bruyant";
          }else if (s>70 && s<=75) {document.getElementById("ambiance").innerText="Très bruyant";
          }else if (s>75) {document.getElementById("ambiance").innerText="Extrêmenent bruyant";
                          }

Voilà le résultat final :
graphedBA

Aller plus loin
Il est tout à fait possible de programmer des alertes par push (SMS, Mail, etc) en cas de dépassement d’un certain seuil du niveau sonore ambiant pendant votre absence.
Pour cela, mesurez le niveau sonore ambiant de fond et réglez le seuil.
Pensez à l’aspirateur robot ou au réfrigérateur ou encore à un orage qui peuvent déclencher une alerte :wink:

8 J'aimes

BRAVO et merci pour tout ton partage.

1 J'aime

bonsoir,
Pour ceux que cela intéresse, voici la dernière version du widget concernant le capteur de niveau sonore. Il ajoute les données MIN et MAX sur la période d’observation avec possibilité de réinitialisation.

widget 1


return `
<style>
.label{ 
  height: 13px;
  width: 45px;
  border-radius:15%;
  float:right;
  font-size:10px;
  font-weight:bold;
  display: block;
  text-align:center;
}
</style>

<div style="position:absolute;margin-top:15px;margin-left:15px;"><span>Niveau sonore (dBA) : </span><span id="valGraph1">0</span><br> <span id=ambiance>calme</span></div>
<span id=mini class="label" style="margin-top:5px;margin-left:180px;background-color:#28B463;">MIN</span><span id=maxi class="label" style="background-color:#e13e48;margin-top:5px;margin-left:180px;">MAX</span>
<span class=label style="margin-top:5px;margin-left:180px;background-color:#505050;" onclick='document.getElementById("mini").innerText="MIN";document.getElementById("maxi").innerText="MAX";'>RAZ</span>
<script src='http://smoothiecharts.org/smoothie.js'>
`;

Widget 2 (le graph) est inchangé (voir le script étape 4 dans le message précédent)

Widget 3
nouveau code avec la gestion des mini et maxi

s= parseInt(datasources["STATUS"]["response"]["analog0"] * 0.000050354 * 50);

document.getElementById("valGraph1").innerText=s; //(Math.random() * 100 ).toFixed(2);
if (s<=50){document.getElementById("ambiance").innerText="Calme";
          }else if (s >30 && s<=55) {document.getElementById("ambiance").innerText="Relativement calme";
          }else if (s>55 && s<=60) {document.getElementById("ambiance").innerText="Bruits courants";
          }else if (s>60 && s<=65) {document.getElementById("ambiance").innerText="Supportable";
          }else if (s>65 && s<=70) {document.getElementById("ambiance").innerText="Bruyant";
          }else if (s>70 && s<=75) {document.getElementById("ambiance").innerText="Très bruyant";
          }else if (s>75) {document.getElementById("ambiance").innerText="Extrêmenent bruyant";
                          }
if (document.getElementById("mini").innerText=="MIN"){document.getElementById("mini").innerText=s;
                                                    }else if (s<parseInt(document.getElementById("mini").innerText)) {document.getElementById("mini").innerText=s;}
if (document.getElementById("maxi").innerText=="MAX"){document.getElementById("maxi").innerText=s;
                                                    }else if (s>parseInt(document.getElementById("maxi").innerText)) {document.getElementById("maxi").innerText=s;}


image image

Bonjour,

merci pour ce partage intéressant.
Cela fonctionne très bien.
Par contre si l’on veut exploiter plusieurs entrées analogiques sur le même dashboard cela ne fonctionne plus.

Merci et cdlt

bonsoir,
la mise en place de plusieurs graphiques est abordée sur le site du développeur.
Je ne l’ai pas abordée car je pense que l’usage doit être limité. Pour des températures, le graphique par défaut de l’IPX800 suffit amplement, les variations ne sont pas assez rapides pour nécessiter un graphe en Live Stream.
cdt

Bonsoir
j’ai transcrit ce tutoriel en 2 wiki

http://gce.ovh/wiki/index.php?title=Graphique_temps_réel

http://gce.ovh/wiki/index.php?title=Le_capteur_de_niveau_sonore_DFrobot_SEN0232

cdt

5 J'aimes

bonsoir,
suite à tout ce qui précède, voici un sonomètre complet.

  • En haut du widget : descriptif de l’ambiance sonore moyenne mesurée.

  • En bas du Widget, le niveau sonore moyen, le niveau sonore instantané, le niveau sonore maximum et un descriptif

  • Le bouton image permet de réinitialiser les stats (ambiance moyenne, bruit max et ambiance)

  • Le bouton image permet de stopper et redémarrer les mesures

  • Le bouton image permet d’exporter la liste des mesures effectuées. Il suffit de copier les valeurs puis de les coller dans un fichier (possibilité de faire des graphiques sous Excel. Il faudra peut-être ajuster le séparateur décimal en fonction de vos paramètres régionaux)
    (testé sous Firefox)

image

#installation

  • ajoutez un widget HTML hauteur 3 blocs
    ajoutez le code suivant
var mini=20;
var maxi=130;
var speed=40; //to 100
return `
<style>
  div.smoothie-chart-tooltip {
  background: #444;
  padding: 1em;
  margin-top: 20px;
  font-family: consolas;
  color: white;
  font-size: 10px;
  pointer-events: none;
  }
#cadre1 {
  position:relative;
  border: 5px solid rgba(182,201,213,0.73);
  border-radius: 10px;
  height:120px;
  width:250px;
  overflow: hidden;
  background-color:0;
  }
#cadre2{
  position:absolute;
  border: 2px solid rgba(182,201,213,0.98);
  border-radius: 50%;
  background-color: #333;
  line-height:50px;
  width:50px;
  overflow: hidden;
  color:#ff0;
  font-weight:bold;
  font-size:24px;
  }

.cadre3{
  position:absolute;
  border: 2px solid rgba(182,201,213,0.98);
  border-radius: 20%;
  background-color: #333;
  line-height:30px;
  margin-top:-112px;
  width:50px;
  overflow: hidden;
  color:#aaa;
  }
.btn{
  display:inline-block;
  float:left;
  width:60px;
  line-height:20px;
  background-color:#666;
  color:#fff;
  border:2px;
  border-color:;
  border-radius: 5px;
  font-size:12px;
  text-align:middle;
}
</style>

<script src="http://cdn.rawgit.com/Mikhus/canvas-gauges/gh-pages/download/2.1.5/all/gauge.min.js"></script>
<script src='http://smoothiecharts.org/smoothie.js'></script>
<script type="text/javascript">
   function createTimeline1() {
         var random1 = new TimeSeries();
         setInterval(function() {
             random1.append(Date.now(), parseFloat(document.getElementById("valGraph1").innerText));
             }, 500);
         var chart1 = new SmoothieChart({millisPerPixel:${speed},grid:{verticalSections:0},tooltip:true,maxValue:${maxi},minValue:${mini},horizontalLines:[{color:'#ffffff',lineWidth:1,value:0}]});
         chart1.addTimeSeries(random1 ,{lineWidth:2,strokeStyle:'#3FB740'});
         chart1.streamTo(document.getElementById("chart1"), 500);
         
         document.getElementById('cadre2').onclick = function()
         {
         if (document.getElementById("isRunning").innerText=="1"){
            chart1.stop();
            document.getElementById("isRunning").innerText="0";
         }else{
            chart1.start();   
            document.getElementById("isRunning").innerText="1";
         };}
}

   window.onload=function(){
   createTimeline1();}
</script>

<center>
<div style="width:280px;position:relative;">
<div style="display:none;" id=isRunning>1</div>
  <div id=cadre1 style="height:120px;width=100%;top:45px;">
    <canvas style="position:absolute;top:1px;left:1px;" id="chart1" width="265px" height="118px">
  </div>
<span style="position:absolute;left:25px;top:10px;">Ambiance : <span id=AvgAmbiance></span></span>
<span id=chrono style="position:absolute;top:52px;clear:both;font-size:10px;left:20px;">00:00:00</span>
</center>
    </canvas>
</center>
`;

  • Ajoutez un deuxième widget de type HTML (hauteur 4 blocs)
    ajoutez le code
<center>
<div style="width:280px;position:relative;">
<div id=cadre1 style=div id=cadre1 style="margin-top:10px;height:120px;width:99%;color:#fff;">
<canvas id=sono3 data-type="radial-gauge"
data-width="250"
data-height="250"
data-units="Km/h"
data-min-value="30"
data-start-angle="110"
data-ticks-angle="140"
data-color-major-ticks="#3FB740"
data-color-minor-ticks="#3FB740"
data-color-numbers='["#3FB740","#3FB740","#3FB740","#3FB740","#3FB740","#3FB740","#3FB740","#a64a37","#a64a37","#a64a37","#a64a37"]'
data-value-box="false"
data-max-value="130"
data-major-ticks="30,40,50,60,70,80,90,100,110,120,130"
data-minor-ticks="2"
data-stroke-ticks="true"
data-highlights='[{"from": 95, "to": 130, "color": "#a64a37"}]'
data-border-shadow-width="0"
data-borders="false"
data-needle-type="arrow"
data-needle-width="2"
data-needle-circle-size="7"
data-needle-circle-outer="true"
data-needle-circle-inner="false"
data-animation-duration="1500"
data-animation-rule="linear"
data-value="60"
data-title="dB"
data-color-plate="transparent"
></canvas>
</div>
<span id=Start style="display:none;">-</span>
<span id=cpt style="display:none;"/>
<span id=Noises style="display:none;"/>
<span id=NoisesList style="display:none;"/>   
<div id=cadre1 style="margin-top:10px;height:90px;">
<span style="position:absolute;top:5px;left:3px;color:#3FB740;font-size:12px;">AVG</span>
<span style="position:absolute;top:5px;left:220px;color:#3FB740;font-size:12px;">MAX</span><br>
<span id=avg style="width:65px;left:3px;position:absolute;top:20px;color:#3FB740;font-size:28px;text-align:left;">- -.-</span>
<span id="valGraph1" style="width:115px;left:68px;position:absolute;top:28px;color:#a64a37;font-size:42px;text-align:center;">- -.-</span>
<span id=maxi style="width:65px;left:178px;position:absolute;top:20px;color:#3FB740;font-size:28px;text-align:right;">- -.-</span>
<span id=ambiance style="left:3px;width:250px;position:absolute;top:70px;color:#a64a37;font-size:12px;text-align:center;"></span>
</div>
<span class=cadre3 style="margin-top:-123px;margin-left:20px;" >
    <span style="font-size:14px;left:8px;top:3px;" class="glyphicons glyphicons-list-alt" onclick="var msg=getElementById('NoisesList');var msg1=msg.innerText;var table=msg1.split('|');alert(table.join('\\n'));"></span></span>
<span class=cadre3 style="margin-top:-123px;margin-left:-70px;">
    <span style="font-size:14px;top:3px;left:5px;" class="glyphicons glyphicons-refresh" onclick="document.getElementById('Noises').innerText='';document.getElementById('maxi').innerText='- -.-';document.getElementById('avg').innerText='- -.-';document.getElementById('Start').innerText=Date.now();document.getElementById('NoisesList').innerText='';"/></span>
    <span id=cadre2 style="margin-top:-132px;margin-left:-25px;"><span  style="position:relative;top:7px;left:6px;" class="glyphicons glyphicons-retweet"></span></span>
</div>
</div>
</center>

  • créez un troisième widget html (hauteur 1 bloc)
    collez ce code dans le widget
var a= parseFloat(datasources["STATUS"]["response"]["analog0"] * 0.000050354 * 50).toFixed(1);
var h="00";var h1="00";
var m="00";var m1="00";
var s="00";var s1="00";
var tauxDS=1;
var calibration=0;
var sommeDBA=0;
var i=0;
var seuilsB=[0 ,40 ,60 ,80 ,100 ,130];
var bruits=["Bruits légers","Bruits courants","Bruits fatigants","Bruits dangereux","Bruits assourdissants"];
var seuilsA=[0,50,55,60,65,70,75,130];
var ambiances=["Calme","Relativement calme","Bruits courants","Bruits supportables","Bruyant","Très bruyant","Etrêmement bruyant"];

if(document.getElementById("isRunning").innerText=="1")
    {
//maj des jauges
if (a<30){a=30;}
gauge3 = document.gauges.get('sono3');
gauge3.value=a;

//chronomètre
if (document.getElementById("Start").innerText=="-"){document.getElementById("Start").innerText=Date.now();}
var millis=Date.now()-parseInt(document.getElementById("Start").innerText);
var temps=new Date();
temps.setTime(millis);
h="00"+(temps.getHours()-1).toString();
m="00"+(temps.getMinutes()).toString();
s="00"+(temps.getSeconds()).toString();
document.getElementById("chrono").innerText=h.substring(h.length - 2, h.length)+":"+m.substring(m.length - 2, m.length)+":"+s.substring(s.length - 2, s.length);

//niveau sonore moyen
document.getElementById("Noises").innerText+=";" + (Math.pow(10,(a + calibration) / 10));
var Noises=(document.getElementById("Noises").innerText).split(";");
document.getElementById("cpt").innerText=Noises.length;
var Flen=Noises.length-1;
for (i=1;i<Flen;i++){
    sommeDBA += parseFloat(Noises[i]);
}
sommeDBA = (sommeDBA * tauxDS)/Flen;
var avgDBA = 10 * Math.log10(sommeDBA);
if (Flen>2){document.getElementById("avg").innerText=avgDBA.toFixed(1);
           }else{document.getElementById("avg").innerText="- -.-";}

//liste horodatée pour export
var myTime=new Date();
h1="00"+(myTime.getHours()-1).toString();
m1="00"+(myTime.getMinutes()).toString();
s1="00"+(myTime.getSeconds()).toString();
document.getElementById("NoisesList").innerText+= h1.substring(h1.length - 2, h1.length)+":"+m1.substring(m1.length - 2, m1.length)+":"+s1.substring(s1.length - 2, s1.length) + ';' + a + '|';

//Bruits
var i=seuilsB.findIndex(seuil => seuil > a)
document.getElementById("ambiance").innerText=bruits[i-1];

//Ambiance
var i=seuilsA.findIndex(seuil => seuil > avgDBA)
document.getElementById("AvgAmbiance").innerText=ambiances[i-1];

if (document.getElementById("maxi").innerText=="- -.-"){document.getElementById("maxi").innerText=a;
   }else if (a>parseFloat(document.getElementById("maxi").innerText)) {document.getElementById("maxi").innerText=a;}

document.getElementById("valGraph1").innerText=a;
    };

Sauvegardez l’IHM.

bonne soirée

7 J'aimes

bonjour,
wiki sur SEN0232 complété avec le sonomètre complet.
http://gce.ovh/wiki/index.php?title=Le_capteur_de_niveau_sonore_DFrobot_SEN0232
cdt

3 J'aimes

Bonjour et félicitation pour ce très beau travail !

Je suis intéressé par un widget température simple avec le mini, le maxi et le bouton pour réinitialiser.
Est-il possible de m’aiguiller sur la démarche à suivre sans trop alourdir le Dashboard ?

Merci d’avance.

Romain.

Bonjour @rom1 ,
attention au hors sujet :wink:
il y a déjà des exemples sur le forum


cdt

Oui c’est vrai @fgtoul mais je ne suis pas complétement hors sujet !

En effet la partie du forum « Widget température HTML » indique des valeurs min et max écritent en dur. Ce n’est pas du tout ce que je recherche. Je souhaite comme pour le sonomètre récupérer les vrais valeurs min et max et utiliser un bouton reset pour remettre à zéro.

Est-ce possible avec les températures?

Romain.

Les min max du sonomètre ne fonctionnent pas comme ça. Ces valeurs sont déterminées uniquement sur la plage de mesure effectuée pendant l’affichage du widget.
Vos températures mini maxi quant à elles doivent être déterminées sur 24h, ce que fait déjà l’ipx800.

Essayez le widget graphique natif de l’ipx800 v4.

Le titre de ce post concerne un graphique temps réel, donc vous êtes bien hors sujet :wink: