Oscilloscopio digitale con SlimLine

Scopo del programma

Il programma ScopeMeter trattato in questo articolo (Download programma) dimostra come scambiare dati in formato JSON con il modulo CPU Cortex da browser. Il programma realizza le funzioni minime di oscilloscopio digitale ed i dati acquisiti sono visualizzabili graficamente da un browser. Utilizzando uno degli ingressi analogici presenti sul modulo CPU possiamo acquisire segnali nel range da 0 a 10 volts, i segnali acquisiti sono memorizzati in un array e tramite il web server integrato nel modulo passati in una stringa JSON al browser per la visualizazione grafica.

Il programma si basa sul FB DataSampling visibile a lato di cui viene dato il codice sorgente, proprio come un oscilloscopio è possibile indicare il valore di base tempi, il livello del trigger, la percentuale di valori da visualizzare in pre trigger oltre all’indirizzo del buffer di memorizzazione degli stessi. Come tutti gli oscilloscopi digitali ho introdotto anche l’algoritmo per il calcolo della frequenza del segnale acquisito. Il FB deve essere posto in un POU unitamente alla acquisizione analogica del segnale ed eseguito in task Fast ogni 1 mS. Può essere usato come punto di partenza per essere modificato adattandolo alle proprie esigenze.

VAR_INPUT
  DSampling: BOOL; //Data sampling, attiva campionamento dati
  TMode: UINT; //Trigger mode, modo triger 0:Free Run, 1:Triggered
  IValue: REAL; //Input value, Valore analogico in ingresso (0-10 V)
  TBase: UDINT; //Time base (mS), Base tempi
  TLevel: REAL; //Trigger level, Livello di trigger (0-10 V)
  PTrigger: REAL; //Pre trigger percentage (%), Percentuale di eventi visualizzati prima del trigger
  DBAddress: @REAL; //Sampling data buffer address, Indirizzo buffer dati
  DBSize: UDINT; //Sampling data buffer size, Dimensione buffer dati
END_VAR

VAR_OUTPUT
  DCatched: BOOL; //Data catched, Dati catturati e trasferiti in buffer dati
  Frequency: REAL; //Signal frequency (Hz); Frequenza segnale
END_VAR

POU DataAcquisition

Programma sviluppato in linguaggio FBD eseguito nella task Fast ad ogni 1 mS che gestisce l’acquisizione analogica ed il FB DataSampling. Quando un campionamento di dati è stato eseguito viene settata la variabile DCatched che informa la parte di programma eseguita in task Back di copiare i dati campionati nell’array di memoria allocato in DB100 che sarà poi acquisito dal browser.

FB DataSampling

Il FB è sviluppato in linguaggio ST (Structured Text), verso la fine si trova la parte che gestisce il campionamento, ogni tempo impostato viene salvato il valore del’ingresso nel buffer shiftando tutti gli altri valori acquisiti, in questo modo si ha una storia continua dell’andamento del segnale in ingresso. All’inizio del programma c’è la una parte realizzata utilizzando lo statement CASE che gestisce il trigger, quando il segnale in ingresso supera il valore impostato di trigger inizia il conteggio dei campioni da memorizzare. Il numero di campioni è calcolato in base alla percentuale impostata di pretrigger in modo da mantenere in visualizzazione i dati acquisisti prima della soglia di trigger. Di seguito la parte che si occupa del campionamento dei dati.

  // Controllo soglia base tempi.

  TMCounter:=TMCounter+1; //Time base counter
  IF (TMCounter < TBase) THEN RETURN; END_IF;
  TMCounter:=0; //Time base counter

  // Eseguo storicizzazione valori acquisiti.

  i:=Sysmemmove(DBAddress, DBAddress+4, DBSize-4);
  i:=Sysmemmove(DBAddress+(DBSize-4), ADR(IValue), 4);
  
  // Controllo se terminato acquisizione.

  IF (OnSampling) THEN
    DSCounter:=DSCounter-1; //Data sampling counter
    IF (DSCounter = 0) THEN
      DCatched:=TRUE; //Data catched
      CaseNr:=0; //Case programma
    END_IF;
  END_IF;

POU Auxiliaries

Programma sviluppato in linguaggio ST, nella parte inziale provvede (Se non è già presente) a generare il file Values.htm che contiene i TAGs con gli indirizzi dei valori da visualizzare. Come si vede dal file in realtà i valori che il browser acquisirà leggendo il file sono in formato JSON.

  IF (SysFirstLoop) THEN
    IF (Sysfilelength('C:/Web/Chart/Values.htm') = -1) THEN

      // Apertura file in write, in questo modo ci si posiziona in testa.

      Fp:=Sysfopen('C:/Web/Chart/Values.htm', 'w'); (* File pointer *)
      IF (Fp = NULL) THEN RETURN; END_IF;

      // ----------------------------------------------------------------
      // SCRITTURA VALORI
      // ----------------------------------------------------------------
      // Inizializzo stringa JSON.

      i:=SysVarfprintf(Fp, '{"%s":[', STRING_TYPE, ADR('Values'));

      // Eseguo loop scrittura indirizzo variabili.
    
      FOR IDx:=0 TO ((SIZEOF(DBuffer)/4)-2) DO
        VIDx:=(IDx*4); //Variable index
        i:=SysVarfprintf(Fp, '<!--["%%.3f", REAL, %d]-->, ', UDINT_TYPE, ADR(VIDx));
      END_FOR;

      // Scrittura ultimo valore e chiudo stringa JSON.

      VIDx:=(IDx*4); //Variable index
      i:=SysVarfprintf(Fp, '<!--["%%.3f", REAL, %d]-->]}', UINT_TYPE, ADR(VIDx));
      i:=Sysfclose(Fp); //Eseguo chiusura file
    END_IF;
  END_IF;

=========================================================================
Sorgente file Values.htm:
{"Values":[<!--["%.3f", REAL, 0]-->, <!--["%.3f", REAL, 4]-->, ....}

Dati letti dal browser visualizzando il file:
{"Values":[7.446, 9.514, 9.928, 8.529, 5.859, 2.937, ...}

Nella parte finale viene eseguito il trasferimento dei dati campionati nel buffer allocato in DB100. Il trasferimento avviene quando la variabile DCatched è attiva.

  IF (DCatched) THEN
    i:=Sysmemmove(ADR(ChartData), ADR(DBuffer), SIZEOF(ChartData));
    DCatched:=FALSE; //Data catched
  END_IF;

Sorgente pagina web

Per la visualizzazione nel browser viene utilizzata la libreria Chart.js, tutta la parte che riguarda la visualizzazione và trasferita nella cartella C:/Web/Chart. La pagina Chart.htm gestisce la visualizzazione dell’oscilloscopio, è una pagina responsiveche utilizza le librerie Bootstrap e JQuery presenti nel server web del modulo CPU. Tralascio la spiegazione della parte htm e mi soffermo sulla parte Javascript che gestisce la visualizzazione.

// -----------------------------------------------------------------------------
// DICHIARAZIONE GRAFICO
// -----------------------------------------------------------------------------
// Dichiaro le opzioni del grafico 

var data=
{
  labels: [],
  datasets: [{label: "Demo", data: [], fill: false, borderColor: '#009933'},{label: "aaa", data: [], fill: false, borderColor: '#009955'}]
}; 
  
var options=
{
  maintainAspectRatio: false,
  scales: {xAxes: [{ticks: { fontColor: "#989898", display:false, },}], yAxes: [{ticks: { fontColor: "#989898", beginAtZero:true,  display:true, },}] }, // Opzioni scale
  legend: {labels: {fontColor: "#989898", fontSize: 16, fontStyle: 'bold',},}, // Opzioni della label
};

// Creazione grafico.

CreateChart( new Chart(document.getElementById("myChart"), { type: 'line', data: data, options: options}) );

// -----------------------------------------------------------------------------
// VALORIZZAZIONE GRAFICO
// -----------------------------------------------------------------------------

GetValues(); //Valorizzo alla apertura
setInterval('GetValues()', 2000); //Imposto refresh automatico

// Questa funzione invia allo SlimLine i valori impostati e riceve i dati
// da visualizzare dal file "Values.htm" in formato JSON.

function GetValues(){$.post( "Values.htm", {'UINT 4000':+$("#Selection").val(), 'UINT 4002':+$("#TBase").val(), 'REAL 4004':+$("#TLevel").val(), 'REAL 4008':+$("#PTrigger").val()}, function(PContent) {ChartDraw(PContent)}, 'json');};

L’aggiornamento del grafico è gestito dalla funzione GetValues che invia la selezione del modo trigger, i valori di base tempi, soglia trigger e percentuale pretrigger e riceve l’array di valori da visualizzare nel grafico. La funzione è eseguita al caricamento pagina ed automaticamente ogni 2 secondi per ottenere l’aggiornamento automatico del grafico.

Conclusioni

In questo articolo abbiamo realizzato un semplice oscilloscopio, ma grazie alla libreria Chart.js possiamo visualizzare in forma grafica qualsiasi sequenza di valori. Così se eseguissimo la lettura di valori di temperatura potremmo visualizzare l’andamento della stessa nel tempo, ed ipotizzando di campionare ogni 15 minuti avremo 96 campionamenti in una intera giornata.

Was this article helpful?