In this example the fuzzy controller will be implemented with three rules. The GUI will show only the desired and actual value; user sets the desired value by pot. Figure of the GUI:
In this example socket.io library is used.
var http = require("http").createServer(handler); // on req - handvar io = require("socket.io").listen(http); // socket libraryvar fs = require("fs"); // variable for file system for providing htmlvar firmata = require("firmata");console.log("Starting the code");var board = new firmata.Board("/dev/ttyACM0", function(){ console.log("Connecting to Arduino"); board.pinMode(0, board.MODES.ANALOG); // enable analog pin 0 board.pinMode(1, board.MODES.ANALOG); // enable analog pin 1 board.pinMode(2, board.MODES.OUTPUT); // direction of DC motor board.pinMode(3, board.MODES.PWM); // PWM of motor board.pinMode(4, board.MODES.OUTPUT); // direction of DC motor});function handler(req, res) { fs.readFile(__dirname + "/example28.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; // actual value varvar factor = 0.1; // proportional factor that deterimes speed of res.var pwm = 0; // set pwm as global variablehttp.listen(8080); // server will listen on port 8080var sendValueViaSocket = function(){}; // var for sending messagesboard.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 analog pin 1});startControlAlgorithm();io.sockets.on("connection", function(socket) { socket.emit("messageToClient", "Srv connected, board OK"); setInterval(sendValues, 40, socket); // on 40ms trigerr func. sendValues }); // end of sockets.on connection}); // end of board.on ready////////////////////////////////////////////////fuzzyvar NFuzzyVars = 2;var NFuzzyOutputs = 1;var NFuzzySets = new Array(NFuzzyVars);NFuzzySets[0] = 3;NFuzzySets[1] = 3;var FSvalues = new Array(NFuzzyVars);for (var i=0;i!=NFuzzyVars;i++){ FSvalues[i] = new Array(NFuzzySets[i]); for (var j=0;j!=NFuzzySets[i];j++) { FSvalues[i][j] = new Array(3); }}var NRules = 3;var RBase = new Array(NRules);for (var i=0;i!=NRules;i++){ RBase[i] = new Array(NFuzzyVars);}var ValuesForFuzzy = new Array(NFuzzyVars);ValuesForFuzzy[0] = 0;ValuesForFuzzy[1] = 0;var AlphaCut = new Array(NFuzzySets[0]);var RN = 0;RBase[RN][0] = 0; RBase[RN][1] = 0; RN++;RBase[RN][0] = 1; RBase[RN][1] = 1; RN++;RBase[RN][0] = 2; RBase[RN][1] = 2; RN++;FSvalues[0][0][0] = -100; // number of var, num of fuzzy sets, number of points, that define fsFSvalues[0][0][1] = -100;FSvalues[0][0][2] = 0; FSvalues[0][1][0] = -100;FSvalues[0][1][1] = 0;FSvalues[0][1][2] = 100;FSvalues[0][2][0] = 0;FSvalues[0][2][1] = 100;FSvalues[0][2][2] = 100;FSvalues[1][0][0] = -15;FSvalues[1][0][1] = -15;FSvalues[1][0][2] = 0; FSvalues[1][1][0] = -15;FSvalues[1][1][1] = 0;FSvalues[1][1][2] = 15;FSvalues[1][2][0] = 0;FSvalues[1][2][1] = 15;FSvalues[1][2][2] = 15;function getMRFromFSvalues(value,NumOfVar,NumOfSet){ if (NumOfSet == 0) { if (value < FSvalues[NumOfVar][NumOfSet][0]) { return 1; } else if (value > FSvalues[NumOfVar][NumOfSet][2]) { return 0; } else { return (FSvalues[NumOfVar][NumOfSet][2] - value) / (FSvalues[NumOfVar][NumOfSet][2] - FSvalues[NumOfVar][NumOfSet][1]); } } else if (NumOfSet == NFuzzySets[NumOfVar] - 1) { if (value < FSvalues[NumOfVar][NumOfSet][0]) { return 0; } else if (value > FSvalues[NumOfVar][NumOfSet][2]) { return 1; } else if (value < FSvalues[NumOfVar][NumOfSet][1]) { return (value - FSvalues[NumOfVar][NumOfSet][0]) / (FSvalues[NumOfVar][NumOfSet][1] - FSvalues[NumOfVar][NumOfSet][0]); } } else { if (value < FSvalues[NumOfVar][NumOfSet][0]) { return 0; } else if (value > FSvalues[NumOfVar][NumOfSet][2]) { return 0; } else if (value < FSvalues[NumOfVar][NumOfSet][1]) { return (value - FSvalues[NumOfVar][NumOfSet][0]) / (FSvalues[NumOfVar][NumOfSet][1] - FSvalues[NumOfVar][NumOfSet][0]); } else { return (FSvalues[NumOfVar][NumOfSet][2] - value) / (FSvalues[NumOfVar][NumOfSet][2] - FSvalues[NumOfVar][NumOfSet][1]); } }}function getDesiredValuesFuzzy(){ for (var j=0;j!=NFuzzySets[0];j++) { AlphaCut[j] = 0; } for (var i=0;i!=NRules;i++) { var tempMR = 0; var tempMinMR = 1; for (var CurrentVar = NFuzzyOutputs;CurrentVar!=NFuzzyVars;CurrentVar++) { tempMR = getMRFromFSvalues(ValuesForFuzzy[CurrentVar],CurrentVar,RBase[i][CurrentVar]); if (tempMR < tempMinMR) tempMinMR = tempMR; } for (var CurrentVar = 0;CurrentVar!=NFuzzyOutputs;CurrentVar++) { if (tempMinMR > AlphaCut[RBase[i][CurrentVar]]) AlphaCut[RBase[i][CurrentVar]] = tempMinMR; } } for (var CurrentVar = 0;CurrentVar!=NFuzzyOutputs;CurrentVar++) { var FuzzyRange = FSvalues[CurrentVar][NFuzzySets[CurrentVar]-1][2] - FSvalues[CurrentVar][0][0]; var NIntervals = 100; var CoordinateMassSumm = 0; var MassSumm = 0; for (var i=0;i!=NIntervals;i++) { var TempCoordinate = FSvalues[CurrentVar][0][0] + FuzzyRange/NIntervals*i; var TempMass1 = 0; var TempMass2 = 0; for (var j=0;j!=NFuzzySets[CurrentVar];j++) { TempMass2 = getMRFromFSvalues(TempCoordinate,CurrentVar,j); if (TempMass2 > AlphaCut[j]) TempMass2 = AlphaCut[j]; if (TempMass2 > TempMass1) TempMass1 = TempMass2; } MassSumm += TempMass1; CoordinateMassSumm += TempCoordinate * TempMass1; } if (MassSumm != 0) ValuesForFuzzy[CurrentVar] = CoordinateMassSumm / MassSumm; if (ValuesForFuzzy[CurrentVar].isNaN) ValuesForFuzzy[CurrentVar] = 0; }}////////////////////////////////////////////////unfuzzyfunction controlAlgorithm () { ValuesForFuzzy[1] = desiredValue-actualValue; console.log(ValuesForFuzzy[1]); getDesiredValuesFuzzy(); pwm = ValuesForFuzzy[0]; console.log(pwm); if (pwm > 255) {pwm = 255}; // to limit pwm values if (pwm < -255) {pwm = -255}; // to limit pwm values if (pwm > 0) {board.digitalWrite(2,1); board.digitalWrite(4,0);}; // direction if > 0 if (pwm < 0) {board.digitalWrite(2,0); board.digitalWrite(4,1);}; // direction if < 0 board.analogWrite(3, Math.abs(pwm));};function startControlAlgorithm () { setInterval(function(){controlAlgorithm();}, 30); // call the alg. on 30ms console.log("Control algorithm has been started.");};function sendValues (socket) { socket.emit("clientReadValues", { "desiredValue": desiredValue, "actualValue": actualValue });};<!DOCTYPE html><meta charset = utf8><html><head> <title>Example with potentiometer</title></head><body onload="load();"> <div><canvas id="canvas1" width ="200" height = "100" style="border: 1px dashed #00c3c3;"></canvas> </div><p></p><div id="divForPrint"></div><br><script type="text/javascript" src="/socket.io/socket.io.js"></script><script type="text/javascript">var potValue1 = 0; // value of the first potentiometervar potValue2 = 0; // value of the second potentiometervar x1 = new Array(); // array for x1var y1 = new Array(); // array for y1var x2 = new Array(); // array for x2var y2 = new Array(); // array for y2var canvas1;var ctx1;function load() { // function that is started, when we open the page canvas1 = document.getElementById("canvas1"); ctx1 = canvas1.getContext("2d"); // initialization of first graph 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 };};var divForPrint = document.getElementById("divForPrint");// var for printing messagesvar numberOfLinesInLog = 20; // variable for the number of lines in log divvar counterOfLogs = 0; // variable for counting the logsfunction log(msg) { // function to print messages to div with implemented scroll var node=document.createElement("tr"); // we create variable node as tr (table row) var textnode=document.createTextNode(counterOfLogs + " | " + msg); // create elem. with text node.appendChild(textnode); // add to "node", i.e. table row divForPrint.insertBefore(node, divForPrint.childNodes[0]); // insert into variable divForPrint -> document.getElementById("divForPrint"); if (counterOfLogs > numberOfLinesInLog-1) { // if there are more numbers as e.g. 10 divForPrint.removeChild(divForPrint.childNodes[numberOfLinesInLog]); // remove the oldest printout } counterOfLogs = counterOfLogs + 1; // increase the counter of logs}var socket = io.connect("192.168.254.149:8080"); // connect via socketsocket.on("messageToClient", function (msg){ log(msg); // add msg to div});socket.on("clientReadValues", function(value) { potValue1 = value.desiredValue; potValue2 = value.actualValue; // Draw graph No1 ***************************************** ctx1.lineWidth = "1"; ctx1.strokeStyle = "#ff0000"; ctx1.clearRect(0, 0, canvas1.width, canvas1.height); // clear the canvas 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 No1 *********************************** // Draw graph No2 ***************************************** ctx1.strokeStyle = "#00ff00"; // green line 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 (var 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 // End of draw graph No2 *********************************** log(value.desiredValue + "|" + value.actualValue);})socket.on("disconnect", function(){ log("Disconnected from the server"); // we print status of disconn. to div}); </script> </body> </html>