The general scheme of the DC motor position control is shown in the next figure. The developed system will enable us to study different control algorithms.
Hardware view on the breadboard is shown in the next figure. It's a combination of Example 11 and 12. You should follow the position of the elements which should match above and below figure. This is important to compare the structure of the control system as abstract scheme and as real system.
Example of the element positions in the realised system:
The starting example for writing the code is Example 11 with two pots reading. We therefore rename Example 11 to Example 13 (example13.js and example13.html). First we add additional variable in the .js file which will be used in control algorithm:
var factor = 0.1; // proportional factor that determines the speed of aproaching toward desired value
The additional pins 2, 3, 4 should be enabled:
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
We should add function controlAlgorithm () (in .js file)
function controlAlgorithm () {
pwm = factor*(desiredValue-actualValue);
if(pwm > 255) {pwm = 255}; // to limit the value for pwm / positive
if(pwm < -255) {pwm = -255}; // 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));
};
... and function function startControlAlgorithm () which will call control algorithm on 30ms:
function startControlAlgorithm () {
setInterval(function() {controlAlgorithm(); }, 30); // na 30ms call
console.log("Control algorithm started")
};
We should put in the code for starting the controlAlgorithm (after board.analogRead):
startControlAlgorithm();
Next, we should check:
1. If the shafts of the motor and feedback pot are UNCOUPLED.
- start the system
2. If the motor rotation corresponds to the settings of input pot and output pot. Here we should first set both potentiometers to central position. Then we should move input potentiometerswith desired value for example left. The motor should start to spin right. By hand, we should now move the output potentiometer (with pointer) to the right and the motor should stop spinning. We should do the same for the opposite directions. If this works, the system is put together correctly. Otherwise, the HW and SW should be checked.
Demonstration of the rotation check is shown in the following video: https://youtu.be/CVL5jE_tkEo
Demonstration of the coupling of the motor shaft and potentiometer: https://youtu.be/n30-R1-u6dc
The output on the GUI should be as shown in the next figure:
Differences between example09.js and example13.js -> The .html code is the same.
var http = require("http").createServer(handler); // on req - hand
var fs = require("fs"); // variable for file system for providing html
var firmata = require("firmata");
const WebSocket = require('ws'); // for permanent connection between server and client
const wss = new WebSocket.Server({port: 8888}); // websocket port is 8888
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
};
wss.sendToLastWs = function sendToLastWs(data) {
if (wss.clients[wss.clients.length-1] !== undefined) { // if websocket exist
if (wss.clients[wss.clients.length-1].readyState === WebSocket.OPEN) { // if it is connected
wss.clients[wss.clients.length-1].send(data); // send to the last connected client - to only one client's websocket
}
}
};
var messageJSON;
console.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 + "/example13.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 var
var actualValue = 0; // variable for actual value (output value)
var pwm;
var factor = 0.1; // proportional factor that determines the speed of aproaching toward desired value
http.listen(8080); // server will listen on port 8080
board.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
});
startControlAlgorithm(); // to start control alg.
wss.on('connection', function (ws, req) { // start of wss code
messageJSON = {"type": "message", "content": "Srv connected, board OK"};
ws.send(JSON.stringify(messageJSON));
setInterval(sendValues, 40); // on 40ms we send message to client
}); // end of sockets.on connection
}); // end of board.on(ready)
function startControlAlgorithm () {
setInterval(function() {controlAlgorithm(); }, 30); // na 30ms call
console.log("Control algorithm started")
};
function controlAlgorithm () {
pwm = factor*(desiredValue-actualValue);
if(pwm > 255) {pwm = 255}; // to limit the value for pwm / positive
if(pwm < -255) {pwm = -255}; // 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));
};
function sendValues () {
//wss.sendToLastWs(JSON.stringify({"type": "clientReadValues", "desiredValue": desiredValue}));
wss.broadcast(JSON.stringify({"type": "clientReadValues", "desiredValue": desiredValue, "actualValue": actualValue}));
};
<!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>
</div>
<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 potentiometer
var x1 = new Array(); // array for x1
var y1 = new Array(); // array for y1
var potValue2 = 0; // value of second potentiometer
var x2 = new Array(); // array for x2 variable
var y2 = new Array(); // array for y2 variable
var canvas1;
var ctx1;
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 it
var 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";
// 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
};
};
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 "clientReadValues":
potValue1 = msg.desiredValue;
potValue2 = msg.actualValue;
// Draw first graph *****************************************
ctx1.clearRect(0, 0, canvas1.width, canvas1.height); // clear the canvas
ctx1.lineWidth = "1";
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***********************************
// 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 ************************************
log(potValue1 + "|" + potValue2);
break;
}
};
</script>
</body>
</html>
Example setup from the desk in Lecture Room 313: