In order to see what is the control signal, that is driving DC motor, we should plot the pwm values on the client side.
In the .js file, we should only change the pwm write:
board.analogWrite(3, Math.round(Math.abs(pwm)));In .html file we add additional canvas2:
<br><canvas id="canvas2" width = "200" height = "100" style="border: 1px dashed #00c3c3;"></canvas>Then we add variables for the new graph:// variable for the second graph on canvas2var x3 = new Array(); // array for x3 variablevar y3 = new Array(); // array for y3 variablevar canvas2;var ctx2;We initialize the canvas:
// initialization for canvas2canvas2 = document.getElementById("canvas2");ctx2 = canvas2.getContext("2d");Then we initialize variable arrays in for loop:
// initialize third graph arrays for (var i=0; i<200; i++) { x3[i] = i; // for x values are 0, 1, 2, ... y3[i] = 0; // for y values are 0};At receiving the message from the WebSocket, we should add (after potValue2 = msg.actualValue;):
pwm = parseInt((Number(msg.pwm)).toFixed(0), 10);Then we add legend for the graph:
// add legendctx1.font = "11px Arial";ctx1.fillText("Desired",70,10);ctx1.strokeStyle = "#ff0000";ctx1.beginPath(); // beginning of legend line drawctx1.lineTo(50, 6);ctx1.lineTo(65, 6);ctx1.stroke(); // end of legend line drawctx1.font = "11px Arial";ctx1.fillText("Actual",140,10);ctx1.strokeStyle = "#00ff00";ctx1.beginPath(); // beginning of legend line drawctx1.lineTo(120, 6);ctx1.lineTo(135, 6);ctx1.stroke(); // end of legend line draw Adding axis labels:
// add axis labels
ctx1.font = "10px Arial"; // x-axis label ctx1.fillText("<-"+ 0 + "|" + 200 + "->", 155, 100-5); // display number of points on x-axis // min on y-axis label: ctx1.fillText(1023,5,11); // sicer zaokrozimo na dve decimalni mesti ctx1.fillText(0,5,100-5);Adding code for third graph:
// Draw third graph **********************************************
ctx2.clearRect(0, 0, canvas2.width, canvas2.height); // clear canvas only ONCE! // draw line at coordinate 0 ctx2.lineWidth = "1"; ctx2.strokeStyle = "#add8e6"; ctx2.beginPath(); // beginning of the line ctx2.lineTo(0, 50); // starting point ctx2.lineTo(200, 50); // ending point ctx2.stroke(); // to put it on the graph
// put on the legend
ctx2.font = "11px Arial"; ctx2.fillText("PWM",70,10); ctx2.strokeStyle = "#0000ff"; ctx2.beginPath(); // beginning of legend line draw ctx2.lineTo(50, 6); ctx2.lineTo(65, 6); ctx2.stroke(); // end of legend line draw // draw PWM ctx2.lineWidth = "1"; ctx2.strokeStyle = "#0000ff"; // set blue color ctx2.beginPath(); // to start drawing new line y3.splice(0, 1); // on the position 0 in the field y3 we erase one value y3[199] = pwm; // new value is added at the end for (var i=0; i<200; i++) { ctx2.lineTo(x3[i], (100 - (255 + y3[i]) / 510 * 100)); // 0,0 x,y coordinate is in upper left corner }; ctx2.stroke(); // to draw the line // add axis labels ctx2.font = "10px Arial"; // x-axis label ctx2.fillText("<-"+ 0 + "|" + 200 + "->", 155, 100-5); // display number of points on x-axis // min on y-axis label: //ctx2.textBaseline="top"; // to print font 5 px from top ctx2.fillText(255,5,11); // sicer zaokrozimo na dve decimalni mesti ctx2.fillText(-255,5,100-5); // Draw third graph END ************************************GUI with pwm graph and button for algorithm select is shown in the next figure:
Comparison between the file example16.js and example17a.js (the only change is in the file name from 16 to 17a)
Comparison between example16.html and example17a.html
var http = require("http").createServer(handler); // on req - handvar fs = require("fs"); // variable for file system for providing htmlvar firmata = require("firmata");const WebSocket = require('ws'); // for permanent connection between server and clientconst wss = new WebSocket.Server({port: 8888}); // websocket port is 8888wss.broadcast = function broadcast(data) { wss.clients.forEach(function each(client) { if (client.readyState === WebSocket.OPEN) { client.send(data); } });};var messageJSON;var controlAlgorithmStartedFlag = 0; // variable for indicating weather the Alg has benn sta.var intervalCtrl; // var for setInterval in global scopeconsole.log("Starting the code");var board = new firmata.Board("/dev/ttyACM0", function(){ console.log("Connecting to Arduino"); console.log("Enabling analog Pin 0"); board.pinMode(0, board.MODES.ANALOG); // analog pin 0 board.pinMode(1, board.MODES.ANALOG); // analog pin 1 board.pinMode(2, board.MODES.OUTPUT); // direction of DC motor board.pinMode(3, board.MODES.PWM); // PWM of motor i.e. speed of rotation board.pinMode(4, board.MODES.OUTPUT); // direction DC motor});function handler(req, res) { fs.readFile(__dirname + "/example17a.html", function (err, data) { if (err) { res.writeHead(500, {"Content-Type": "text/plain"}); return res.end("Error loading html page."); } res.writeHead(200); res.end(data); })}var desiredValue = 0; // desired value varvar actualValue = 0; // variable for actual value (output value)var Kp1 = 0.55; // proportional factor of PID controllervar Ki1 = 0.008; // integral factor of PID controllervar Kd1 = 0.15; // differential factor of PID controllervar factor = 0.3; // proportional factor that determines speed of resonsevar pwm = 0; // set pwm as global variablevar pwmLimit = 254; // to limit value of the pwm that is sent to the motorvar err = 0; // errorvar errSum = 0; // sum of errors as integralvar dErr = 0; // difference of errorvar lastErr = 0; // to keep the value of previous error to estimate derivativehttp.listen(8080); // server will listen on port 8080board.on("ready", function() { board.analogRead(0, function(value){ desiredValue = value; // continuous read of analog pin 0 }); board.analogRead(1, function(value) { actualValue = value; // continuous read of pin A1 }); wss.on('connection', function (ws, req) { // start of wss code messageJSON = {"type": "message", "content": "Srv connected, board OK"}; ws.send(JSON.stringify(messageJSON)); messageJSON = {"type": "staticMsgToClient", "content": "Srv connected, board OK"}; ws.send(JSON.stringify(messageJSON)); setInterval(sendValues, 40); // on 40ms we send message to client ws.on("message", function (msgString) { // message comes as string -> msgString var msg = JSON.parse(msgString); // string from ws which comes as a string is put to JSON switch(msg.type) { case "startControlAlgorithm": startControlAlgorithm(msg); // msg has all values, alg. type and parameters break; case "stopControlAlgorithm": stopControlAlgorithm(); break; } }); // end of wss.on code }); // end of sockets.on connection}); // end of board.on(ready)function controlAlgorithm (msg) { // the parameter in the argument holds ctrlAlgNo and param. values if(msg.ctrlAlgNo == 1) { pwm = msg.pCoeff*(desiredValue-actualValue); if(pwm > pwmLimit) {pwm = pwmLimit}; // to limit the value for pwm / positive if(pwm < -pwmLimit) {pwm = -pwmLimit}; // to limit the value for pwm / negative if (pwm > 0) {board.digitalWrite(2,1); board.digitalWrite(4,0);}; // določimo smer če je > 0 if (pwm < 0) {board.digitalWrite(2,0); board.digitalWrite(4,1);}; // določimo smer če je < 0 board.analogWrite(3, Math.abs(pwm)); console.log(Math.round(pwm)); } if(msg.ctrlAlgNo == 2) { err = desiredValue - actualValue; // error as difference between desired and actual val. errSum += err; // sum of errors | like integral dErr = err - lastErr; // difference of error pwm = msg.Kp1*err+msg.Ki1*errSum+msg.Kd1*dErr; // PID expression lastErr = err; // save the value of error for next cycle to estimate the derivative if(pwm > pwmLimit) {pwm = pwmLimit}; // to limit the value for pwm / positive if(pwm < -pwmLimit) {pwm = -pwmLimit}; // to limit the value for pwm / negative if (pwm > 0) {board.digitalWrite(2,1); board.digitalWrite(4,0);}; // determine direction if > 0 if (pwm < 0) {board.digitalWrite(2,0); board.digitalWrite(4,1);}; // determine direction if < 0 board.analogWrite(3, Math.abs(pwm)); };}function startControlAlgorithm (msg) { if (controlAlgorithmStartedFlag == 0) { // reset parameters pwm = 0; // Reset Pulse Width Modulation value err = 0; // Reset error errSum = 0; // Reset sum of errors as integral dErr = 0; // Reset difference of error lastErr = 0; // Reset value whih keeps the value of previous error to estimate derivative controlAlgorithmStartedFlag = 1; intervalCtrl = setInterval(function(){controlAlgorithm(msg);}, 30); // call the alg. on 30ms console.log("Control algorithm has been started."); messageJSON = {"type": "staticMsgToClient", "content": " No. " + msg.ctrlAlgNo + " started | " + json2txt(msg)}; wss.broadcast(JSON.stringify(messageJSON)); }};function stopControlAlgorithm () { clearInterval(intervalCtrl); // clear the interval of control algorihtm board.analogWrite(3, 0); controlAlgorithmStartedFlag = 0; pwm = 0; console.log("Control algorithm has been stopped."); messageJSON = {"type": "staticMsgToClient", "content": "Stopped."}; wss.broadcast(JSON.stringify(messageJSON));};function sendValues () { wss.broadcast(JSON.stringify({"type": "clientReadValues", "desiredValue": desiredValue, "actualValue": actualValue, "error": (desiredValue - actualValue), "pwm": (pwm).toFixed(0)}));};function json2txt(obj) // function to print out the json names and values{ var txt = ''; var recurse = function(_obj) { if ('object' != typeof(_obj)) { txt += ' = ' + _obj + '\n'; } else { for (var key in _obj) { if (_obj.hasOwnProperty(key)) { txt += '.' + key; recurse(_obj[key]); } } } }; recurse(obj); return txt;}<!DOCTYPE html><meta charset = utf8><html><head><title>Example with potentiometers</title></head> <body onload="load()";> <div><canvas id="canvas1" width ="200" height = "100" style="border: 1px dashed #00c3c3;"></canvas><br><canvas id="canvas2" width = "200" height = "100" style="border: 1px dashed #00c3c3;"></canvas><p></p></div>pCoeff: <input id="pCoeff" value="0.1" size="5" /><button id="buttonStartControlAlgorithm1" onClick="startControlAlgorithm1();">Start Ctrl Alg1</button><button id="buttonStopControlAlgorithm" onClick="stopControlAlgorithm();">Stop Ctrl Alg</button><p></p>Kp: <input id="Kp1" value="0.15" size = "5" />Ki: <input id="Ki1" value="0.0055" size = "5" />Kd: <input id="Kd1" value="0.25" size = "5" /><button id="buttonStartControlAlgorithm2" onClick="startControlAlgorithm2();">Start Ctrl Alg2</button><button id="buttonStopControlAlgorithm" onClick="stopControlAlgorithm();">Stop Ctrl Alg</button><div id="divForStaticPrint"> </div><p></p> <div id="print1"></div> <br> <script type="text/javascript"> "use strict"; // in order to use clasess var divElement = document.getElementById("print1"); // var for div el.var numberOfLinesBeforeScroll = 10;var linesPrintCounter = 0; var potValue1 = 0; // value of the first potentiometervar x1 = new Array(); // array for x1var y1 = new Array(); // array for y1var potValue2 = 0; // value of second potentiometervar x2 = new Array(); // array for x2 variablevar y2 = new Array(); // array for y2 variablevar pwm = 0;var canvas1;var ctx1;// variable for the second graph on canvas2var x3 = new Array(); // array for x3 variablevar y3 = new Array(); // array for y3 variablevar canvas2;var ctx2;var pCoeff = 0;var Kp1 = 0;var Ki1 = 0;var Kd1 = 0; function log(msg) { var node=document.createElement("tr"); // we create the variable node as the a table row (tr) var textnode=document.createTextNode(linesPrintCounter + " | " + msg); // we create element with the text adding the counter node.appendChild(textnode); // adding text to "node", i.e. table row divElement.insertBefore(node, divElement.childNodes[0]); // inserting into variable node if (linesPrintCounter > numberOfLinesBeforeScroll-1) { // if the lines are more than limit -> start with scroll divElement.removeChild(divElement.childNodes[numberOfLinesBeforeScroll]); // we remove the oldest printout } linesPrintCounter++; // increasing the number of printouts} let ws = new WebSocket("ws://192.168.254.149:8888"); // create socket - connect to itvar messageJSON;function load() { // function that is started, when we open the page canvas1 = document.getElementById("canvas1"); ctx1 = canvas1.getContext("2d"); // initialization of graph with points ctx1.lineWidth = "1"; ctx1.strokeStyle = "#ff0000"; // initialization for canvas2 canvas2 = document.getElementById("canvas2"); ctx2 = canvas2.getContext("2d"); // initializaction of first time series for (var i=0; i<200; i++) { x1[i] = i; // for x values are 0, 1, 2, ... y1[i] = 0; // for y values are 0 }; // initializaion of second graph for (var i=0; i<200; i++) { x2[i] = i; // x2 values are 0, 1, 2, ... y2[i] = 0; // for y2 values are 0 }; // initialize third graph arrays for (var i=0; i<200; i++) { x3[i] = i; // for x values are 0, 1, 2, ... y3[i] = 0; // for y values are 0 };};ws.onmessage = function(event) { var msg = JSON.parse(event.data); // string from ws is put to JSON switch(msg.type) { case "message": log(msg.content); // add msg to div break; case "staticMsgToClient": document.getElementById("divForStaticPrint").innerHTML = "Status: Ctrl. Alg. " + msg.content; // we print to div break; case "clientReadValues": potValue1 = msg.desiredValue; potValue2 = msg.actualValue; pwm = parseInt((Number(msg.pwm)).toFixed(0), 10); // Draw first graph ***************************************** ctx1.clearRect(0, 0, canvas1.width, canvas1.height); // clear the canvas ctx1.lineWidth = "1"; // add legend ctx1.font = "11px Arial"; ctx1.fillText("Desired",70,10); ctx1.strokeStyle = "#ff0000"; ctx1.beginPath(); // beginning of legend line draw ctx1.lineTo(50, 6); ctx1.lineTo(65, 6); ctx1.stroke(); // end of legend line draw ctx1.font = "11px Arial"; ctx1.fillText("Actual",140,10); ctx1.strokeStyle = "#00ff00"; ctx1.beginPath(); // beginning of legend line draw ctx1.lineTo(120, 6); ctx1.lineTo(135, 6); ctx1.stroke(); // end of legend line draw // END of add legend // add axis labels ctx1.fillText("<-" + 0 + "|" + 200 + "->", 150, 100-5); // display no. of points on x-axis // min on y-axis label ctx1.fillText(1023,5,11); ctx1.fillText(0,5,100-5); ctx1.strokeStyle = "#ff0000"; ctx1.beginPath(); // to start drawing new line y1.splice(0, 1); // on the position 0 in the field y1 we erase one value y1[199] = potValue1; // new value is added at the end for (var i=0; i<200; i++) { ctx1.lineTo(x1[i], (100 - (y1[i] / 1023) * 100)); // 0,0 x,y coordinate is in upper left corner }; ctx1.stroke(); // to draw the line // End of draw graph 1st *********************************** // Draw second graph ********************************************** ctx1.lineWidth = "1"; ctx1.strokeStyle = "#00ff00"; ctx1.beginPath(); // to start drawing new line y2.splice(0, 1); // on the position 0 in the field y2 we erase one value y2[199] = potValue2; // new value is added at the end for (i=0; i<200; i++) { ctx1.lineTo(x2[i], (100 - (y2[i] / 1023) * 100)); // 0,0 x,y coordinate is in upper left corner }; ctx1.stroke(); // to draw the line // Draw second graph graph END ************************************ // Draw graph No3 on canvas2 ***************************************** ctx2.lineWidth = "1"; ctx2.clearRect(0, 0, canvas2.width, canvas2.height); // clear the canvas // draw centerline at 0 ctx2.strokeStyle = "#add8e6"; ctx2.beginPath(); // draw centerline at 0 ctx2.lineTo(0, 50); // starting point ctx2.lineTo(200, 50); // ending point ctx2.stroke(); // add legend to graph ctx2.font = "11px Arial"; ctx2.fillText("PWM", 70, 10); ctx2.strokeStyle = "#0000ff"; ctx2.beginPath(); // beginning of the short line for the legend ctx2.lineTo(50, 6); ctx2.lineTo(65, 6); ctx2.stroke(); ctx2.strokeStyle = "#0000ff"; ctx2.beginPath(); // to start drawing new line y3.splice(0, 1); // on the position 0 in the field y we erase one value y3[199] = pwm; // new value is added at the end for (var i=0; i<200; i++) { ctx2.lineTo(x3[i], (100 - (255 + y3[i]) / 510 * 100)); // 0,0 x,y coordinate is in upper left corner }; ctx2.stroke(); // to draw the line // add axis labels ctx2.fillText("<-" + 0 + "|" + 200 + "->", 150, 100-5); // display no. of points on x-axis // min on y-axis label ctx2.fillText(255,5,11); ctx2.fillText(-255,5,100-5); // End of draw No3 on canvas2 *********************************** log(potValue1 + "|" + potValue2 + "|" + msg.error + "|" + msg.pwm); break; }};function startControlAlgorithm1() { stopControlAlgorithm(); // just in case, if it is not started pCoeff = document.getElementById("pCoeff").value; // read the value of coeff from input field messageJSON = {"type": "startControlAlgorithm", "ctrlAlgNo": 1, "pCoeff": pCoeff}; ws.send(JSON.stringify(messageJSON)); // we have to stringify JSON to send it over websocket}function startControlAlgorithm2() { stopControlAlgorithm(); // just in case, if it is not started Kp1 = document.getElementById("Kp1").value; // read the value of coeff from input field Ki1 = document.getElementById("Ki1").value; // read the value of coeff from input field Kd1 = document.getElementById("Kd1").value; // read the value of coeff from input field messageJSON = {"type": "startControlAlgorithm", "ctrlAlgNo": 2, "Kp1": Kp1, "Ki1": Ki1, "Kd1": Kd1}; ws.send(JSON.stringify(messageJSON)); // we have to stringify JSON to send it over websocket }function stopControlAlgorithm () { messageJSON = {"type": "stopControlAlgorithm"}; ws.send(JSON.stringify(messageJSON)); // we have to stringify JSON to send it over websocket } </script></body></html>After completing Assignment 5 and 6, the following changes should be made to html file (.js file should only be renamed to example17b.js and new reference should be made to example17b.html):
CodeDiff between example17a.html without class and with class example17b.html:
<!DOCTYPE html><meta charset = utf8><html><head><title>Example with potentiometers</title></head> <body onload="load()";> <div><canvas id="canvas1" width ="200" height = "100" style="border: 1px dashed #00c3c3;"></canvas><br><canvas id="canvas2" width = "200" height = "100" style="border: 1px dashed #00c3c3;"></canvas><p></p></div>pCoeff: <input id="pCoeff" value="0.1" size="5" /><button id="buttonStartControlAlgorithm1" onClick="startControlAlgorithm1();">Start Ctrl Alg1</button><button id="buttonStopControlAlgorithm" onClick="stopControlAlgorithm();">Stop Ctrl Alg</button><p></p>Kp: <input id="Kp1" value="0.15" size = "5" />Ki: <input id="Ki1" value="0.0055" size = "5" />Kd: <input id="Kd1" value="0.25" size = "5" /><button id="buttonStartControlAlgorithm2" onClick="startControlAlgorithm2();">Start Ctrl Alg2</button><button id="buttonStopControlAlgorithm" onClick="stopControlAlgorithm();">Stop Ctrl Alg</button><div id="divForStaticPrint"> </div><p></p> <div id="print1"></div> <br> <script type="text/javascript">"use strict"; // in order to use clasess var divElement = document.getElementById("print1"); // var for div el.var numberOfLinesBeforeScroll = 10;var linesPrintCounter = 0; var potValue1 = 0; // value of the first potentiometervar potValue2 = 0; // value of second potentiometervar pwm = 0;var pCoeff = 0;var Kp1 = 0;var Ki1 = 0;var Kd1 = 0;var graph1; // variable for graph objectvar graph2; // variable for graph object function log(msg) { var node=document.createElement("tr"); // we create the variable node as the a table row (tr) var textnode=document.createTextNode(linesPrintCounter + " | " + msg); // we create element with the text adding the counter node.appendChild(textnode); // adding text to "node", i.e. table row divElement.insertBefore(node, divElement.childNodes[0]); // inserting into variable node if (linesPrintCounter > numberOfLinesBeforeScroll-1) { // if the lines are more than limit -> start with scroll divElement.removeChild(divElement.childNodes[numberOfLinesBeforeScroll]); // we remove the oldest printout } linesPrintCounter++; // increasing the number of printouts} let ws = new WebSocket("ws://192.168.254.149:8888"); // create socket - connect to itvar messageJSON;function load() { // function that is started, when we open the page graph1 = new Graph("canvas1", 0, 200, 0, 1023, ["red", "green"], ["desired", "actual"], ["0", "200", "0", "1023"]); // arguments: Arg1: canvasId, Arg2: maxX, Arg3: maxY, Arg4: [vector of colors]; this determines the size of yValue matrix (if we state one color as eg. ["red"], we assume only one time series, ["red", "green", "blue"] -> three time series) graph2 = new Graph("canvas2", 0, 200, -254, 254, ["orange"], ["PWM"], ["0", "200", "-254", "254"]);};class Graph { constructor(canvasId, minGraphX, maxGraphX, minGraphY, maxGraphY, color, legend, axisDescription) { // pri konstruktorju moramo podati ID platna, ki ga sicer ustvarimo v html-ju this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext("2d"); this.canvasWidth = this.canvas.width; // mind capital W at Width this.canvasHeight = this.canvas.height; // mind capital H at Height this.x = new Array(); // create new Array x this.y = new Array(); this.rangeX = maxGraphX - minGraphX; this.rangeY = maxGraphY - minGraphY; // create y array (size) according to the color vector (could have multiple rows i.e. 2d) for( var i=0; i<color.length; i++ ) { this.y.push([]); // example of three row array init. would be: this.y = [[],[],[]]; } this.minGraphX = minGraphX; this.maxGraphX = maxGraphX; this.minGraphY = minGraphY; this.maxGraphY = maxGraphY; this.color = color; // color of the graph this.legend = legend; this.axisDescription = axisDescription; // fill x vector; vector y is filled in real-time for (var i=0; i<this.maxGraphX+1; i++) { this.x[i] = i; // values for the x coordinate; 0, 1, 2, ... } } addValueOrCutAndAdd(yValue) { if (this.y[0].length == this.maxGraphX+1) { // if canvas size is 10x10 we have 11x11 points (starting with 0 and ending with 10) for (var i = 0; i < yValue.length; i++) { // v zanki gremo po polju yInput in na mestu 0 eno vrednost odrežemo, na koncu pa eno mesto dodamo - zapišemo novo vrednost yInput this.y[i].splice(0, 1); // on the position 0 in the vector y we cut one value this.y[i][this.maxGraphX] = yValue[i]; // at the end of the array the new value is added } } else { for (var i = 0; i < yValue.length; i++) { // z zanko gremo po vseh vrsticah za matrike y this.y[i].push(yValue[i]); // if the array is not yet full, we push the new value in the array / vrednost v oklepaju [] t.j. index je za ena večji; npr., če imamo eno vrednost je indeks [0], length pa 1 } } } plot(yValue) { this.addValueOrCutAndAdd(yValue); this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); // clear the canvas for (var i=0; i < yValue.length; i++) { // zanka, ki gre po vrsticah y matrike this.ctx.strokeStyle = this.color[i]; // determine color this.ctx.beginPath(); // for the start of the line for (var j=0; j<this.y[0].length; j++) { this.ctx.lineTo(this.x[j]/this.rangeX*this.canvasWidth, (this.canvasHeight - ((this.y[i][j]-this.minGraphY)/this.rangeY) * this.canvasHeight)); // for y values we multiply with canas height, eg. 0.25 * 100 = 25 } this.ctx.stroke(); } // add legend for( var i=0; i<this.legend.length; i++ ) { // legend and color should be of the same size this.ctx.font = "11px Arial"; this.ctx.fillText(this.legend[i], 49+i*54, 10); this.ctx.strokeStyle = this.color[i]; this.ctx.beginPath(); // beginning of the short line for the legend this.ctx.lineTo(37+i*54, 6); this.ctx.lineTo(46+i*54, 6); this.ctx.stroke(); } // add axis descritions this.ctx.fillText("<-" + this.axisDescription[0] + "|" + this.axisDescription[1] + "->", 150, 95) this.ctx.fillText(this.axisDescription[2], 5, 95); this.ctx.fillText(this.axisDescription[3], 5, 11); }}ws.onmessage = function(event) { var msg = JSON.parse(event.data); // string from ws is put to JSON switch(msg.type) { case "message": log(msg.content); // add msg to div break; case "staticMsgToClient": document.getElementById("divForStaticPrint").innerHTML = "Status: Ctrl. Alg. " + msg.content; // we print to div break; case "clientReadValues": potValue1 = msg.desiredValue; potValue2 = msg.actualValue; pwm = parseInt((Number(msg.pwm)).toFixed(0), 10); graph1.plot([potValue1, potValue2]); // desired Vs actual graph graph2.plot([pwm]); // plot pwm log(potValue1 + "|" + potValue2 + "|" + msg.error + "|" + msg.pwm); break; }};function startControlAlgorithm1() { stopControlAlgorithm(); // just in case, if it is not started pCoeff = document.getElementById("pCoeff").value; // read the value of coeff from input field messageJSON = {"type": "startControlAlgorithm", "ctrlAlgNo": 1, "pCoeff": pCoeff}; ws.send(JSON.stringify(messageJSON)); // we have to stringify JSON to send it over websocket}function startControlAlgorithm2() { stopControlAlgorithm(); // just in case, if it is not started Kp1 = document.getElementById("Kp1").value; // read the value of coeff from input field Ki1 = document.getElementById("Ki1").value; // read the value of coeff from input field Kd1 = document.getElementById("Kd1").value; // read the value of coeff from input field messageJSON = {"type": "startControlAlgorithm", "ctrlAlgNo": 2, "Kp1": Kp1, "Ki1": Ki1, "Kd1": Kd1}; ws.send(JSON.stringify(messageJSON)); // we have to stringify JSON to send it over websocket }function stopControlAlgorithm () { messageJSON = {"type": "stopControlAlgorithm"}; ws.send(JSON.stringify(messageJSON)); // we have to stringify JSON to send it over websocket } </script></body></html>Let us also add class for printing in the div (example17c.html - .js is the same except the reference to .html file):
<!DOCTYPE html><meta charset = utf8><html><head><title>Example with potentiometers</title></head> <body onload="load()";> <div><canvas id="canvas1" width ="200" height = "100" style="border: 1px dashed #00c3c3;"></canvas><br><canvas id="canvas2" width = "200" height = "100" style="border: 1px dashed #00c3c3;"></canvas><p></p></div>pCoeff: <input id="pCoeff" value="0.1" size="5" /><button id="buttonStartControlAlgorithm1" onClick="startControlAlgorithm1();">Start Ctrl Alg1</button><button id="buttonStopControlAlgorithm" onClick="stopControlAlgorithm();">Stop Ctrl Alg</button><p></p>Kp: <input id="Kp1" value="0.15" size = "5" />Ki: <input id="Ki1" value="0.0055" size = "5" />Kd: <input id="Kd1" value="0.25" size = "5" /><button id="buttonStartControlAlgorithm2" onClick="startControlAlgorithm2();">Start Ctrl Alg2</button><button id="buttonStopControlAlgorithm" onClick="stopControlAlgorithm();">Stop Ctrl Alg</button><div id="divForStaticPrint"> </div><p></p> <div id="print1"></div> <br> <script type="text/javascript">"use strict"; // in order to use clasess var potValue1 = 0; // value of the first potentiometervar potValue2 = 0; // value of second potentiometervar pwm = 0;var pCoeff = 0;var Kp1 = 0;var Ki1 = 0;var Kd1 = 0;var graph1; // variable for graph objectvar graph2; // variable for graph objectvar divPrint1; // var for object div print let ws = new WebSocket("ws://192.168.254.149:8888"); // create socket - connect to itvar messageJSON;function load() { // function that is started, when we open the page graph1 = new Graph("canvas1", 0, 200, 0, 1023, ["red", "green"], ["desired", "actual"], ["0", "200", "0", "1023"]); // arguments: Arg1: canvasId, Arg2: maxX, Arg3: maxY, Arg4: [vector of colors]; this determines the size of yValue matrix (if we state one color as eg. ["red"], we assume only one time series, ["red", "green", "blue"] -> three time series) graph2 = new Graph("canvas2", 0, 200, -254, 254, ["orange"], ["PWM"], ["0", "200", "-254", "254"]); // create divPrint1 object to output the numbers divPrint1 = new LogDiv("print1", 10); // as the argument at the creation of the new object we declare the div name into which the printout of the log will be performed and the number of lines before the scroll};class Graph { constructor(canvasId, minGraphX, maxGraphX, minGraphY, maxGraphY, color, legend, axisDescription) { // pri konstruktorju moramo podati ID platna, ki ga sicer ustvarimo v html-ju this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext("2d"); this.canvasWidth = this.canvas.width; // mind capital W at Width this.canvasHeight = this.canvas.height; // mind capital H at Height this.x = new Array(); // create new Array x this.y = new Array(); this.rangeX = maxGraphX - minGraphX; this.rangeY = maxGraphY - minGraphY; // create y array (size) according to the color vector (could have multiple rows i.e. 2d) for( var i=0; i<color.length; i++ ) { this.y.push([]); // example of three row array init. would be: this.y = [[],[],[]]; } this.minGraphX = minGraphX; this.maxGraphX = maxGraphX; this.minGraphY = minGraphY; this.maxGraphY = maxGraphY; this.color = color; // color of the graph this.legend = legend; this.axisDescription = axisDescription; // fill x vector; vector y is filled in real-time for (var i=0; i<this.maxGraphX+1; i++) { this.x[i] = i; // values for the x coordinate; 0, 1, 2, ... } } addValueOrCutAndAdd(yValue) { if (this.y[0].length == this.maxGraphX+1) { // if canvas size is 10x10 we have 11x11 points (starting with 0 and ending with 10) for (var i = 0; i < yValue.length; i++) { // v zanki gremo po polju yInput in na mestu 0 eno vrednost odrežemo, na koncu pa eno mesto dodamo - zapišemo novo vrednost yInput this.y[i].splice(0, 1); // on the position 0 in the vector y we cut one value this.y[i][this.maxGraphX] = yValue[i]; // at the end of the array the new value is added } } else { for (var i = 0; i < yValue.length; i++) { // z zanko gremo po vseh vrsticah za matrike y this.y[i].push(yValue[i]); // if the array is not yet full, we push the new value in the array / vrednost v oklepaju [] t.j. index je za ena večji; npr., če imamo eno vrednost je indeks [0], length pa 1 } } } plot(yValue) { this.addValueOrCutAndAdd(yValue); this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); // clear the canvas for (var i=0; i < yValue.length; i++) { // zanka, ki gre po vrsticah y matrike this.ctx.strokeStyle = this.color[i]; // determine color this.ctx.beginPath(); // for the start of the line for (var j=0; j<this.y[0].length; j++) { this.ctx.lineTo(this.x[j]/this.rangeX*this.canvasWidth, (this.canvasHeight - ((this.y[i][j]-this.minGraphY)/this.rangeY) * this.canvasHeight)); // for y values we multiply with canas height, eg. 0.25 * 100 = 25 } this.ctx.stroke(); } // add legend for( var i=0; i<this.legend.length; i++ ) { // legend and color should be of the same size this.ctx.font = "11px Arial"; this.ctx.fillText(this.legend[i], 49+i*54, 10); this.ctx.strokeStyle = this.color[i]; this.ctx.beginPath(); // beginning of the short line for the legend this.ctx.lineTo(37+i*54, 6); this.ctx.lineTo(46+i*54, 6); this.ctx.stroke(); } // add axis descritions this.ctx.fillText("<-" + this.axisDescription[0] + "|" + this.axisDescription[1] + "->", 150, 95) this.ctx.fillText(this.axisDescription[2], 5, 95); this.ctx.fillText(this.axisDescription[3], 5, 11); }}class LogDiv { constructor(divId, numberOfLinesBeforeScroll) { this.divElement = document.getElementById(divId); // name of div where values will be printed this.numberOfLinesBeforeScroll = numberOfLinesBeforeScroll; // number of lines which print before scroll this.linesPrintCounter = 0; } log(msg) { // function for printout of the messages with scroll functionality var node=document.createElement("tr"); // we create the variable node as the a table row (table row) var textnode=document.createTextNode(this.linesPrintCounter + " | " + msg); // we create element with the text node.appendChild(textnode); // adding text to "node", t.j. vrstici tabele this.divElement.insertBefore(node, this.divElement.childNodes[0]); // inserting into variable node if (this.linesPrintCounter > this.numberOfLinesBeforeScroll-1) { // if the lines are more than limit -> start with scroll this.divElement.removeChild(this.divElement.childNodes[this.numberOfLinesBeforeScroll], this.divElement.childNodes[this.numberOfLinesBeforeScroll]); // we remove the oldest printout } this.linesPrintCounter++; // increasing the number of printouts } } ws.onmessage = function(event) { var msg = JSON.parse(event.data); // string from ws is put to JSON switch(msg.type) { case "message": divPrint1.log(msg.content); // add msg to div break; case "staticMsgToClient": document.getElementById("divForStaticPrint").innerHTML = "Status: Ctrl. Alg. " + msg.content; // we print to div break; case "clientReadValues": potValue1 = msg.desiredValue; potValue2 = msg.actualValue; pwm = parseInt((Number(msg.pwm)).toFixed(0), 10); graph1.plot([potValue1, potValue2]); // desired Vs actual graph graph2.plot([pwm]); // plot pwm divPrint1.log(potValue1 + "|" + potValue2 + "|" + msg.error + "|" + msg.pwm); break; }};function startControlAlgorithm1() { stopControlAlgorithm(); // just in case, if it is not started pCoeff = document.getElementById("pCoeff").value; // read the value of coeff from input field messageJSON = {"type": "startControlAlgorithm", "ctrlAlgNo": 1, "pCoeff": pCoeff}; ws.send(JSON.stringify(messageJSON)); // we have to stringify JSON to send it over websocket}function startControlAlgorithm2() { stopControlAlgorithm(); // just in case, if it is not started Kp1 = document.getElementById("Kp1").value; // read the value of coeff from input field Ki1 = document.getElementById("Ki1").value; // read the value of coeff from input field Kd1 = document.getElementById("Kd1").value; // read the value of coeff from input field messageJSON = {"type": "startControlAlgorithm", "ctrlAlgNo": 2, "Kp1": Kp1, "Ki1": Ki1, "Kd1": Kd1}; ws.send(JSON.stringify(messageJSON)); // we have to stringify JSON to send it over websocket }function stopControlAlgorithm () { messageJSON = {"type": "stopControlAlgorithm"}; ws.send(JSON.stringify(messageJSON)); // we have to stringify JSON to send it over websocket } </script></body></html>