diff --git a/lib/index.js b/lib/index.js index 7aa5c6b..9124b50 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,255 +9,258 @@ */ "use strict"; -var PID = function(Input, Setpoint, Kp, Ki, Kd, ControllerDirection) { - this.input = Input; - this.mySetpoint = Setpoint; - this.inAuto = false; - this.setOutputLimits(0, 255); // default output limits - this.SampleTime = 100; // default Controller Sample Time is 0.1 seconds - this.setTunings(Kp, Ki, Kd); - this.setControllerDirection(ControllerDirection); - this.lastTime = this.millis() - this.SampleTime; - - this.ITerm = 0; - this.myOutput = 0; -}; +class PID { + constructor(Input, Setpoint, Kp, Ki, Kd, ControllerDirection) { + this.input = Input; + this.mySetpoint = Setpoint; + this.inAuto = false; + this.setOutputLimits(0, 255); // default output limits + this.SampleTime = 100; // default Controller Sample Time is 0.1 seconds + this.setTunings(Kp, Ki, Kd); + this.setControllerDirection(ControllerDirection); + this.lastTime = this.millis() - this.SampleTime; -// Constants for backward compatibility -PID.AUTOMATIC = 1; -PID.MANUAL = 0; -PID.DIRECT = 0; -PID.REVERSE = 1; + this.ITerm = 0; + this.myOutput = 0; + } -PID.prototype.setInput = function(current_value) { - this.input = current_value; -}; + setInput(current_value) { + this.input = current_value; + } -PID.prototype.setPoint = function(current_value) { - this.mySetpoint = current_value; -}; + setPoint(current_value) { + this.mySetpoint = current_value; + }; -PID.prototype.millis = function() { - var d = new Date(); - return d.getTime(); -}; + millis() { + var d = new Date(); + return d.getTime(); + } -/** - * Compute() - * This, as they say, is where the magic happens. this function should be called - * every time "void loop()" executes. the function will decide for itself whether a new - * pid Output needs to be computed. returns true when the output is computed, - * false when nothing has been done. - */ -PID.prototype.compute = function() { - if (!this.inAuto) { - return false; - } - var now = this.millis(); - var timeChange = (now - this.lastTime); - if (timeChange >= this.SampleTime) { - - // Compute all the working error variables - var input = this.input; - var error = this.mySetpoint - input; - this.ITerm += (this.ki * error); - var dInput = input - this.lastInput; - // Compute PID Output - var output = (this.kp * error + this.ITerm - this.kd * dInput) * this.setDirection; - - if (output > this.outMax) { - output = this.outMax; - } - else if (output < this.outMin) { - output = this.outMin; - } - this.myOutput = output; - - // Remember some variables for next time - this.lastInput = input; - this.lastTime = now; - return true; - } - else { - return false; - } -}; + /** + * Compute() + * This, as they say, is where the magic happens. this function should be called + * every time "void loop()" executes. the function will decide for itself whether a new + * pid Output needs to be computed. returns true when the output is computed, + * false when nothing has been done. + */ + compute() { + if (!this.inAuto) { + return false; + } + var now = this.millis(); + var timeChange = (now - this.lastTime); + if (timeChange >= this.SampleTime) { -/** - * SetTunings(...) - * This function allows the controller's dynamic performance to be adjusted. - * it's called automatically from the constructor, but tunings can also - * be adjusted on the fly during normal operation - */ -PID.prototype.setTunings = function(Kp, Ki, Kd) { - if (Kp < 0 || Ki < 0 || Kd < 0) { - return; - } + // Compute all the working error variables + var input = this.input; + var error = this.mySetpoint - input; + this.ITerm += (this.ki * error); + var dInput = input - this.lastInput; + // Compute PID Output + var output = (this.kp * error + this.ITerm - this.kd * dInput) * this.setDirection; - this.dispKp = Kp; - this.dispKi = Ki; - this.dispKd = Kd; + if (output > this.outMax) { + output = this.outMax; + } + else if (output < this.outMin) { + output = this.outMin; + } + this.myOutput = output; - this.SampleTimeInSec = (this.SampleTime) / 1000; - this.kp = Kp; - this.ki = Ki * this.SampleTimeInSec; - this.kd = Kd / this.SampleTimeInSec; -}; + // Remember some variables for next time + this.lastInput = input; + this.lastTime = now; + return true; + } + else { + return false; + } + } -/** - * SetSampleTime(...) - * sets the period, in Milliseconds, at which the calculation is performed - */ -PID.prototype.setSampleTime = function(NewSampleTime) { - if (NewSampleTime > 0) { - var ratio = NewSampleTime / (1.0 * this.SampleTime); - this.ki *= ratio; - this.kd /= ratio; - this.SampleTime = Math.round(NewSampleTime); - } -}; + /** + * SetTunings(...) + * This function allows the controller's dynamic performance to be adjusted. + * it's called automatically from the constructor, but tunings can also + * be adjusted on the fly during normal operation + */ + setTunings(Kp, Ki, Kd) { + if (Kp < 0 || Ki < 0 || Kd < 0) { + return; + } -/** - * SetOutput( ) - * Set output level if in manual mode - */ -PID.prototype.setOutput = function(val) { - if (val > this.outMax) { - this.myOutput = val; - } - else if (val < this.outMin) { - val = this.outMin; - } - this.myOutput = val; -}; + this.dispKp = Kp; + this.dispKi = Ki; + this.dispKd = Kd; -/** - * SetOutputLimits(...) - * This function will be used far more often than SetInputLimits. while - * the input to the controller will generally be in the 0-1023 range (which is - * the default already,) the output will be a little different. maybe they'll - * be doing a time window and will need 0-8000 or something. or maybe they'll - * want to clamp it from 0-125. who knows. at any rate, that can all be done here. - */ -PID.prototype.setOutputLimits = function(Min, Max) { - if (Min >= Max) { - return; - } - this.outMin = Min; - this.outMax = Max; - - if (this.inAuto) { - if (this.myOutput > this.outMax) { - this.myOutput = this.outMax; - } - else if (this.myOutput < this.outMin) { - this.myOutput = this.outMin; - } - - if (this.ITerm > this.outMax) { - this.ITerm = this.outMax; - } - else if (this.ITerm < this.outMin) { - this.ITerm = this.outMin; - } - } -}; + this.SampleTimeInSec = (this.SampleTime) / 1000; + this.kp = Kp; + this.ki = Ki * this.SampleTimeInSec; + this.kd = Kd / this.SampleTimeInSec; + } -/** - * SetMode(...) - * Allows the controller Mode to be set to manual (0) or Automatic (non-zero) - * when the transition from manual to auto occurs, the controller is - * automatically initialized - */ -PID.prototype.setMode = function(Mode) { - var newAuto; - if (Mode == PID.AUTOMATIC || Mode.toString().toLowerCase() == 'automatic' || Mode.toString().toLowerCase() == 'auto') { - newAuto = 1; - } - else if (Mode == PID.MANUAL || Mode.toString().toLowerCase() == 'manual') { - newAuto = 0; - } - else { - throw new Error("Incorrect Mode Chosen"); - } - - if (newAuto == !this.inAuto) { //we just went from manual to auto - this.initialize(); - } - this.inAuto = newAuto; -}; + /** + * SetSampleTime(...) + * sets the period, in Milliseconds, at which the calculation is performed + */ + setSampleTime(NewSampleTime) { + if (NewSampleTime > 0) { + var ratio = NewSampleTime / (1.0 * this.SampleTime); + this.ki *= ratio; + this.kd /= ratio; + this.SampleTime = Math.round(NewSampleTime); + } + } -/** - * Initialize() - * does all the things that need to happen to ensure a bumpless transfer - * from manual to automatic mode. - */ -PID.prototype.initialize = function() { - this.ITerm = this.myOutput; - this.lastInput = this.input; - if (this.ITerm > this.outMax) { - this.ITerm = this.outMax; - } - else if (this.ITerm < this.outMin) { - this.ITerm = this.outMin; - } -}; + /** + * SetOutput( ) + * Set output level if in manual mode + */ + setOutput(val) { + if (val > this.outMax) { + this.myOutput = val; + } + else if (val < this.outMin) { + val = this.outMin; + } + this.myOutput = val; + } -/** - * SetControllerDirection(...) - * The PID will either be connected to a DIRECT acting process (+Output leads - * to +Input) or a REVERSE acting process(+Output leads to -Input.) we need to - * know which one, because otherwise we may increase the output when we should - * be decreasing. This is called from the constructor. - */ -PID.prototype.setControllerDirection = function(ControllerDirection) { - if (ControllerDirection == 0 || ControllerDirection.toString().toLowerCase() == 'direct') { - this.setDirection = 1; - } - else if (ControllerDirection == 1 || ControllerDirection.toString().toLowerCase() == 'reverse') { - this.setDirection = -1; - } - else { - throw new Error("Incorrect Controller Direction Chosen"); - } -}; + /** + * SetOutputLimits(...) + * This function will be used far more often than SetInputLimits. while + * the input to the controller will generally be in the 0-1023 range (which is + * the default already,) the output will be a little different. maybe they'll + * be doing a time window and will need 0-8000 or something. or maybe they'll + * want to clamp it from 0-125. who knows. at any rate, that can all be done here. + */ + setOutputLimits(Min, Max) { + if (Min >= Max) { + return; + } + this.outMin = Min; + this.outMax = Max; -/** - * Status Functions - * Just because you set the Kp=-1 doesn't mean it actually happened. these - * functions query the internal state of the PID. they're here for display - * purposes. this are the functions the PID Front-end uses for example - */ -PID.prototype.getKp = function() { - return this.dispKp; -}; + if (this.inAuto) { + if (this.myOutput > this.outMax) { + this.myOutput = this.outMax; + } + else if (this.myOutput < this.outMin) { + this.myOutput = this.outMin; + } + + if (this.ITerm > this.outMax) { + this.ITerm = this.outMax; + } + else if (this.ITerm < this.outMin) { + this.ITerm = this.outMin; + } + } + } + + /** + * SetMode(...) + * Allows the controller Mode to be set to manual (0) or Automatic (non-zero) + * when the transition from manual to auto occurs, the controller is + * automatically initialized + */ + setMode(Mode) { + var newAuto; + if (Mode == PID.AUTOMATIC || Mode.toString().toLowerCase() == 'automatic' || Mode.toString().toLowerCase() == 'auto') { + newAuto = 1; + } + else if (Mode == PID.MANUAL || Mode.toString().toLowerCase() == 'manual') { + newAuto = 0; + } + else { + throw new Error("Incorrect Mode Chosen"); + } + + if (newAuto == !this.inAuto) { //we just went from manual to auto + this.initialize(); + } + this.inAuto = newAuto; + } + + /** + * Initialize() + * does all the things that need to happen to ensure a bumpless transfer + * from manual to automatic mode. + */ + initialize() { + this.ITerm = this.myOutput; + this.lastInput = this.input; + if (this.ITerm > this.outMax) { + this.ITerm = this.outMax; + } + else if (this.ITerm < this.outMin) { + this.ITerm = this.outMin; + } + } -PID.prototype.getKd = function() { - return this.dispKd; -}; + /** + * SetControllerDirection(...) + * The PID will either be connected to a DIRECT acting process (+Output leads + * to +Input) or a REVERSE acting process(+Output leads to -Input.) we need to + * know which one, because otherwise we may increase the output when we should + * be decreasing. This is called from the constructor. + */ + setControllerDirection(ControllerDirection) { + if (ControllerDirection == 0 || ControllerDirection.toString().toLowerCase() == 'direct') { + this.setDirection = 1; + } + else if (ControllerDirection == 1 || ControllerDirection.toString().toLowerCase() == 'reverse') { + this.setDirection = -1; + } + else { + throw new Error("Incorrect Controller Direction Chosen"); + } + } -PID.prototype.getKi = function() { - return this.dispKi; -}; + /** + * Status Functions + * Just because you set the Kp=-1 doesn't mean it actually happened. these + * functions query the internal state of the PID. they're here for display + * purposes. this are the functions the PID Front-end uses for example + */ + getKp() { + return this.dispKp; + } -PID.prototype.getMode = function() { - return this.inAuto ? "Auto" : "Manual"; -}; + getKd() { + return this.dispKd; + } -PID.prototype.getDirection = function() { - return this.controllerDirection; -}; + getKi() { + return this.dispKi; + } -PID.prototype.getOutput = function() { - return this.myOutput; -}; + getMode() { + return this.inAuto ? "Auto" : "Manual"; + } -PID.prototype.getInput = function() { - return this.input; -}; + getDirection() { + return this.controllerDirection; + } -PID.prototype.getSetPoint = function() { - return this.mySetpoint; -}; + getOutput() { + return this.myOutput; + } + + getInput() { + return this.input; + } + + getSetPoint() { + return this.mySetpoint; + } + +} + +// Constants for backward compatibility +PID.AUTOMATIC = 1; +PID.MANUAL = 0; +PID.DIRECT = 0; +PID.REVERSE = 1; -module.exports = PID; +exports = PID;