ad space

Real-Time Serial Graph with P5.js

 Here we will show how to make a fast real time serial graph with p5.js. that you can use to display analog signals in real time. We will demonstrate how Arduino due can be used as arduino waveform generator that produces square wave, triangle wave, sawtooth wave and sine wave and using Arduino Uno as signal acquisition device that transfers the read analog signal serially to the PC. The received analog signal sent by Arduino Uno is then processed and displayed on serial graph in the browser in real time. This serial graph monitor can be used on a webpage to display sensor data in real time. Such serial port plotter can be helpful in Internet of Things(IoT) projects where sensor data or other kinds of data has to displayed on the internet in real time. There are many real time graphing software but this one is free and open source, can be run in browser and displayed on the web.  

This real time serial plotter was made using p5.js, p5.serial.js and chart.js Javascript libraries. With this serial plotter you can select one of the serial port, you can pause the serial plot display to view the signal, you can zoom in and zoom out and reset the zoom.  

 The following video demonstrates how the serial graph monitor works.


 What you will need 

In order to make this real time serial graph, you need to have the following software and JavaScript libraries. 

- Code Editor(Processing IDE, Visual Studio Code or any other IDE)

- JavaScript libraries(p5.js, p5.serial.js, chart.js, chartjs.plugin-zoom.min.js)

- P5 Serial Control/Monitor Application

- Signal generator & Signal Aquisition device(here Arduino Due & Arduino Uno were used)

Create a website folder or a sketch folder

Whether you use simple text editor or processing IDE(which was used here) or visual studio code IDE create a folder which has the following files in it as shown below. The top level folder contain is shown below and has mainly the index.html and sketch.js(or any other name of your project name choice). 

 


Then there is a folder called libraries which contains JavaScript files.

These files and their content is now explained and provided below.

Program Codes & HTML/CSS/Javascripts

The following is the index.html code.

<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <script language="javascript" type="text/javascript" src="libraries/p5.min.js"></script>
  <script language="javascript" type="text/javascript" src="libraries/p5.serialport.js"></script>
  <script language="javascript" type="text/javascript" src="libraries/chart.js"></script>
  <script language="javascript" type="text/javascript" src="libraries/chartjs-plugin-zoom.min.js"></script>
  <script language="javascript" type="text/javascript" src="sketch.js"></script>

<style>
body { 
padding-top: 40; 
margin: auto; 
background-color: black;
} 

#lid{
  position:absolute;
  left:75%;
  top:0%;
  color: white;
}

.chartbox{
    position: relative;
    padding:0;
    margin:auto;
    width:800px;
    height:650px;
}
  
canvas {
    position:absolute;
    background-color: black;
}

#sid {
  position: absolute;
  left:90%;
  top:0%;
}

#zoomIn {
  position: absolute;
  left:10%;
  top:55%;
}

#zoomOut {
  position: absolute;
  left:18.5%;
  top:55%;
}

#rstZoom {
  position: absolute;
  left:28.2%;
  top:55%;
}


</style>

</head>

<body>
  <div class="chartbox">
    <div id="cid"></div>
    <label id='lid'>Port Select/Pause:</label>
    <select id='sid'></select>
    <button id='zoomIn'onclick="zoomButton(1.1)">Zoom In</button>
    <button id='zoomOut'onclick="zoomButton(0.9)">Zoom Out</button>
    <button id='rstZoom'onclick="resetZoomButton()">Reset Zoom</button>
  </div>

</body>
</html>

sketch.js & JavaScript Libraries

As you can see in the above html code, we have included the following JavaScript files:

 <script language="javascript" type="text/javascript" src="libraries/p5.min.js"></script>
  <script language="javascript" type="text/javascript" src="libraries/p5.serialport.js"></script>
  <script language="javascript" type="text/javascript" src="libraries/chart.js"></script>
  <script language="javascript" type="text/javascript" src="libraries/chartjs-plugin-zoom.min.js"></script>
  <script language="javascript" type="text/javascript" src="sketch.js"></script>

 

We have made use of 5 different JavaScripts files. The p5.min.js library is used mainly as a glue to the whole program that creates canvas, for plotting graph and serial communication among others. The p5.serialport.js is required for making serial communication possible with webpage and the PC serial port. The chart.js and the chartjs.plugin-zoom.min.js files are required to make the graph on the webpage canvas. The sketch.js is the main javascript file which contains our main program and that uses the p5.min.js and the other libraries.

 p5.min.js

You can download the p5.min.js from the following link.

https://p5js.org/download

or,

https://cdnjs.com/libraries/p5.js

 The javascript file can be downloaded directly from the following link.

https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js 

Once you have downloaded it you can put it in a folder libraries inside your main website folder and then include the file in the index.html webpage using the following notation.

<script language="javascript" type="text/javascript" src="libraries/p5.min.js"></script> 

Or you could include the cdn link for the p5.min.js javascript library using script tag in the index.html webpage like so.

<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script> 

P5.serial.js

 Similarly you will need to include the p5.serial.js JavaScript library in your index.html which can be downloaded from.

https://cdn.jsdelivr.net/npm/p5.serialserver@0.0.28/lib/p5.serialport.js

and then put the file into the libraries folder within your website folder and include it using the following line:

 <script language="javascript" type="text/javascript" src="libraries/p5.serialport.js"></script>

Or if you want directly put the cdn link to it in the following way.

 <script language="javascript" type="text/javascript" src="https://cdn.jsdelivr.net/npm/p5.serialserver@0.0.28/lib/p5.serialport.js"></script>

 

p5 Serial Control Application

Next you need to download and install the P5 serial control application from github link provided below.

 https://github.com/p5-serial/p5.serialcontrol/releases/tag/0.1.2

Before you launch the real time serial graph web application you have to run the P5 serial control application. 


Chart and Chart Zoom JavaScript

The chart.js and chartjs.plugin-zoom.min.js files can be downloaded from the following links.

https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.min.js

https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.1.1/chartjs-plugin-zoom.min.js

After downloading it you should put these two files within the libraries folder inside your website folder. Then you should include them in your index.html file as was shown in the index.html code above.

 sketch.js

  The sketch.js contains the main program in javascript which is as follows.

// variable to hold an instance of the serialport library:
var serial;
// HTML Select option object:
var portSelector;
// number of data points you plan to show:
let dataSize = 400;
// array to put the yvalues from the sensor
let yvalues = new Array(dataSize);
// for baudrate setting
let options = {baudRate: 9600}; 
// The chart.js chart:
let chart;
// For Time display
var ms=0;
var s =0;
var m= 0;

// data object for the chart:
const data = {
  // X axis labels
  labels: new Array(dataSize),
  datasets: [{
    label: 'Serial data',  // chart label
    data: yvalues,         // the data to chart
    backgroundColor: '#C1142B',
    borderColor: '#C1142B',
    borderWidth: 1.2,
    pointBorderColor: '#C1142B',
    pointRadius: 0.2         // point size
  }]
};

const config = {
  type: 'line',         // type of chart
  data: data,           // the data
  options: {
    animation: false,   // remove animation to speed drawing up
    responsive: true,
    maintainAspectRatio: false,
    plugins:{
      legend:{
        display: true,
      position: 'bottom',
      align: 'end'
      },
      title: {
      display: true,
      text: 'Analog Real Time Values',
      font:{
              size: 20
              },
       padding:{
                bottom: 30
              },
        color: '#fff'
      },
      zoom: {
        zoom: {
          wheel: {
            enabled: true,
          },
          pinch: {
            enabled: true
          },
          mode: 'xy',
        }
      }
    },
    scales: {           // axis ranges:
      y: {
        title: {
        display: true,
        text: 'Amplitude',
        font:{
              size: 20
              },
        color: '#fff'
        },
        ticks:{
            font: {
                  size: 14
                  },
            color: '#fff'
          },
        grid: { display: true, color: "#131c2b", borderColor: '#ffcc33' },
        min: 0,
        max: 5.5
      },
      
      x:{
          title: {
            display: true,
            text: 'Time(m:s:ms)',
            font: {
                  size: 20
                  },
            color: '#fff'
          },
          ticks:{
            font: {
                  size: 10,
                  },
            color: '#fff'
          },
          grid: { display: true, color: "#131c2b", borderColor: '#ffcc33' },
          min:0
      }
    }
  }
};

function resetZoomButton(){
  chart.resetZoom();
}

function zoomButton(zom){
  chart.zoom(zom);
}

function setup() {
  // set up the canvas:
  createCanvas(800, 650).parent("cid");
  // new instance of the serialport library:
  serial = new p5.SerialPort();
 

  // callback function for serialport:
  serial.on('list', printList);
  
  //// list the serial ports:
  serial.list();
   //serial.open(portName, options);
   
   serial.on('data', serialEvent);
  
  
  // instantiate the chart:
  chart = new Chart(
    this,   // context: this sketch
    config  // config from the global variables
  );
}


function draw() {
  // update the chart:
  chart.update();
}

//make a serial port selector object:
function printList(portList) {
  // create a select object:
  portSelector = select('#sid');
  // portList is an array of serial port names
  for (var i = 0; i < portList.length; i++) {
    // add this port name to the select object:
    portSelector.option(portList[i]);
  }
  // set an event listener for when the port is changed:
  portSelector.changed(mySelectEvent);
}

function mySelectEvent() {
  let item = portSelector.value();
  // give it an extra property for hiding later:
  portSelector.visible = true;
  // if there's a port open, close it:
  if (serial.serialport != null) {
    serial.close();
  }
  // open the new port:
  serial.open(item, options);
}

function keyPressed() {
  if (portSelector) {
    if (portSelector.visible) {
      portSelector.hide();
      portSelector.visible = false;
    } else {
      portSelector.show();
      portSelector.visible = true;
    }
  }
}


function serialEvent() {
  
   ms++;
  if(ms == 1000){
    s++;
    ms = 0;
    if(s == 60){
      m++;
      s = 0;
    }
  }
  let t = m + ":" + s + ":" + ms;
  
  let serialData = Number(serial.readStringUntil('\n'));
  potData = map(serialData,0, 1023, 0, 5);
  if (potData) {
    yvalues.push(potData);
    data.labels.push(t);
    
    if (yvalues.length > dataSize) {
      yvalues.shift();
      data.labels.shift();
    }
  }
}


The sketch.js contains mainly the following code parts.

- codes to configure and open serial port

- codes to create charts with zoom capabilities

- code to retrieve serial data and append and shift data into the chart

Signal Generator and Signal Aquisition

In order to make use of the serial graph we have used Arduino Due as signal generator which generates square wave, triangle wave, sine wave and sawtooth wave. The selection of this signal is done using a push button. There is also a potentiometer which is used to change the frequency of the signal. The signal generated is available the DAC0 pin of Arduino Due. This signal is fed into Arduino Uno via the analog A1 pin. 

The picture below shows how Arduino Due is connected to 10Kohm potentiometer, push button and Arduino Uno.

Arduino Due signal generator Arduino Uno

The schematic electrical diagram is as shown below. The resistor is of 10KOhm and the potentiometer is of 10Kohm.

schematic diagram Arduino Due signal generator

Arduino Due and Arduino Uno Program Codes

The Arduino Due is the signal generator which generates square wave, triangle wave, sine wave and sawtooth wave by pressing the push button as shown in the above schematic diagram. Also the frequency of the generated wave can be varied using the potentiometer. The program code for Arduino Due is as follows.

Arduino Due Code

signalgenerator.ino

#include "Waveforms.h"

#define sampleTime 1000000/maxSamplesNum  // sample for the 1Hz signal expressed in microseconds

const int button = 2;
volatile int wave = 0;

int i = 0;
int sample;

void setup() {
  analogWriteResolution(12);  // set the analog output resolution to 12 bit (4096 levels)
  attachInterrupt(button, waveSelect, RISING);  // Interrupt attached to the button connected to pin 2
}

void loop() {
  sample = map(analogRead(A0), 0, 4095, 0, sampleTime);

  analogWrite(DAC0, waveformsTable[wave][i]);  // write the selected waveform on DAC0

  i++;
  if(i == maxSamplesNum)  // Reset the counter to repeat the wave
    i = 0;

  delayMicroseconds(sample);  // Hold the sample value for the sample time
}

// function that gets executed by the interrupt on digital pin 2
void waveSelect() {
  wave++;
  if(wave == 4)
    wave = 0;
}

 The four types of signals are stored in Waveforms.h header file provided below.

Waveforms.h

#ifndef _Waveforms_h_
#define _Waveforms_h_

#define maxWaveform 4
#define maxSamplesNum 120

static int waveformsTable[maxWaveform][maxSamplesNum] = {
  // Sin wave
  {
    0x7ff, 0x86a, 0x8d5, 0x93f, 0x9a9, 0xa11, 0xa78, 0xadd, 0xb40, 0xba1,
    0xbff, 0xc5a, 0xcb2, 0xd08, 0xd59, 0xda7, 0xdf1, 0xe36, 0xe77, 0xeb4,
    0xeec, 0xf1f, 0xf4d, 0xf77, 0xf9a, 0xfb9, 0xfd2, 0xfe5, 0xff3, 0xffc,
    0xfff, 0xffc, 0xff3, 0xfe5, 0xfd2, 0xfb9, 0xf9a, 0xf77, 0xf4d, 0xf1f,
    0xeec, 0xeb4, 0xe77, 0xe36, 0xdf1, 0xda7, 0xd59, 0xd08, 0xcb2, 0xc5a,
    0xbff, 0xba1, 0xb40, 0xadd, 0xa78, 0xa11, 0x9a9, 0x93f, 0x8d5, 0x86a,
    0x7ff, 0x794, 0x729, 0x6bf, 0x655, 0x5ed, 0x586, 0x521, 0x4be, 0x45d,
    0x3ff, 0x3a4, 0x34c, 0x2f6, 0x2a5, 0x257, 0x20d, 0x1c8, 0x187, 0x14a,
    0x112, 0xdf, 0xb1, 0x87, 0x64, 0x45, 0x2c, 0x19, 0xb, 0x2,
    0x0, 0x2, 0xb, 0x19, 0x2c, 0x45, 0x64, 0x87, 0xb1, 0xdf,
    0x112, 0x14a, 0x187, 0x1c8, 0x20d, 0x257, 0x2a5, 0x2f6, 0x34c, 0x3a4,
    0x3ff, 0x45d, 0x4be, 0x521, 0x586, 0x5ed, 0x655, 0x6bf, 0x729, 0x794
  }
  ,

  // Triangular wave
  {
    0x44, 0x88, 0xcc, 0x110, 0x154, 0x198, 0x1dc, 0x220, 0x264, 0x2a8,
    0x2ec, 0x330, 0x374, 0x3b8, 0x3fc, 0x440, 0x484, 0x4c8, 0x50c, 0x550,
    0x594, 0x5d8, 0x61c, 0x660, 0x6a4, 0x6e8, 0x72c, 0x770, 0x7b4, 0x7f8,
    0x83c, 0x880, 0x8c4, 0x908, 0x94c, 0x990, 0x9d4, 0xa18, 0xa5c, 0xaa0,
    0xae4, 0xb28, 0xb6c, 0xbb0, 0xbf4, 0xc38, 0xc7c, 0xcc0, 0xd04, 0xd48,
    0xd8c, 0xdd0, 0xe14, 0xe58, 0xe9c, 0xee0, 0xf24, 0xf68, 0xfac, 0xff0,
    0xfac, 0xf68, 0xf24, 0xee0, 0xe9c, 0xe58, 0xe14, 0xdd0, 0xd8c, 0xd48,
    0xd04, 0xcc0, 0xc7c, 0xc38, 0xbf4, 0xbb0, 0xb6c, 0xb28, 0xae4, 0xaa0,
    0xa5c, 0xa18, 0x9d4, 0x990, 0x94c, 0x908, 0x8c4, 0x880, 0x83c, 0x7f8,
    0x7b4, 0x770, 0x72c, 0x6e8, 0x6a4, 0x660, 0x61c, 0x5d8, 0x594, 0x550,
    0x50c, 0x4c8, 0x484, 0x440, 0x3fc, 0x3b8, 0x374, 0x330, 0x2ec, 0x2a8,
    0x264, 0x220, 0x1dc, 0x198, 0x154, 0x110, 0xcc, 0x88, 0x44, 0x0
  }
  ,

  // Sawtooth wave
  {
    0x22, 0x44, 0x66, 0x88, 0xaa, 0xcc, 0xee, 0x110, 0x132, 0x154,
    0x176, 0x198, 0x1ba, 0x1dc, 0x1fe, 0x220, 0x242, 0x264, 0x286, 0x2a8,
    0x2ca, 0x2ec, 0x30e, 0x330, 0x352, 0x374, 0x396, 0x3b8, 0x3da, 0x3fc,
    0x41e, 0x440, 0x462, 0x484, 0x4a6, 0x4c8, 0x4ea, 0x50c, 0x52e, 0x550,
    0x572, 0x594, 0x5b6, 0x5d8, 0x5fa, 0x61c, 0x63e, 0x660, 0x682, 0x6a4,
    0x6c6, 0x6e8, 0x70a, 0x72c, 0x74e, 0x770, 0x792, 0x7b4, 0x7d6, 0x7f8,
    0x81a, 0x83c, 0x85e, 0x880, 0x8a2, 0x8c4, 0x8e6, 0x908, 0x92a, 0x94c,
    0x96e, 0x990, 0x9b2, 0x9d4, 0x9f6, 0xa18, 0xa3a, 0xa5c, 0xa7e, 0xaa0,
    0xac2, 0xae4, 0xb06, 0xb28, 0xb4a, 0xb6c, 0xb8e, 0xbb0, 0xbd2, 0xbf4,
    0xc16, 0xc38, 0xc5a, 0xc7c, 0xc9e, 0xcc0, 0xce2, 0xd04, 0xd26, 0xd48,
    0xd6a, 0xd8c, 0xdae, 0xdd0, 0xdf2, 0xe14, 0xe36, 0xe58, 0xe7a, 0xe9c,
    0xebe, 0xee0, 0xf02, 0xf24, 0xf46, 0xf68, 0xf8a, 0xfac, 0xfce, 0xff0
  }
  ,

  // Square wave
  {
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
  }

};

#endif


 Arduino Uno Code

The Arduino Uno is used here for signal acquisition. The Arduino Due DAC0 pin is connected to Arduino Uno A1 analog pin. The analog signal acquired is sent by Arduino Uno to PC serially over the USB. The following is the Arduino Uno code.


void setup() {
Serial.begin(9600);
}

void loop() {
int pot= analogRead(A1);
Serial.println(pot);
delay(1);
}


Running the serial graph

Once the codes and program is ready, you should start the p5 serial control application then run the program which will open in browser with url of localhost followed by some port number such as http://127.0.0.1:8373. 

You should first select the COM port to which Arduino Uno is connected to. Here it is COM3. Then you should see the signal waveform on the graph. By pressing the button, you can select one of the four waveform described above. You can pause the graph by selecting any other port than the currently used port. Then you can zoom in, zoom out and reset the zoom to restore the signal waveform. You can also hover to waveform signal to see the amplitude value and time to make signal measurement. You can restart the signal view in real time by again selecting the com port.

Whats Next?

In the tutorial How to host website from home we showed how one can host website or web application from home for free that is available on the internet. The real time serial graph shown in this tutorial can be made available online from home. The tutorial How to control device over Internet (IoT) and How to display Sensor data in real time on Web describes the process of putting web application online. 

Post a Comment

Previous Post Next Post