├── keywords.txt ├── library.properties ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── LICENSE ├── examples └── SimplePID │ └── SimplePID.ino ├── src ├── PIDController.h └── PIDController.cpp ├── README.md └── CONTRIBUTING.md /keywords.txt: -------------------------------------------------------------------------------- 1 | # Datatypes (such as objects) 2 | PIDController KEYWORD1 3 | 4 | # Methods / functions 5 | begin KEYWORD2 6 | tune KEYWORD2 7 | limit KEYWORD2 8 | setpoint KEYWORD2 9 | minimize KEYWORD2 10 | compute KEYWORD2 11 | getOutput KEYWORD2 12 | 13 | # CONSTANTS 14 | GRAPH LITERAL1 15 | NOGRAPH LITERAL1 16 | VERBOSE LITERAL1 17 | NOVERBOSE LITERAL1 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=PIDController 2 | version=0.0.1 3 | author=Daniel 4 | maintainer=Daniel 5 | sentence=A library that implements PID control to your code. 6 | paragraph=PID controllers are commonly used in control technology. The Wikipedia page explains it well. 7 | category=Device Control 8 | url=https://github.com/DonnyCraft1/PIDArduino 9 | architectures=* 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project. 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Found a bug? If it hasn't been reported yet, please consider doing it! 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Arduino (please complete the following information):** 24 | - You IDE version 25 | - Arduino type [e.g. uno, mega, nano] 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Daniel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/SimplePID/SimplePID.ino: -------------------------------------------------------------------------------- 1 | // Libraries 2 | #include 3 | 4 | // Objects 5 | PIDController pid; // Create an instance of the PID controller class, called "pid" 6 | 7 | // Pins 8 | int outputPin = 3; // The pin the digital output PMW is connected to 9 | int sensorPin = A0; // The pin the analog sensor is connected to 10 | 11 | void setup () { 12 | Serial.begin(9600); // Some methods require the Serial.begin() method to be called first 13 | pinMode(outputPin, OUTPUT); 14 | pinMode(sensorPin, INPUT); 15 | 16 | pid.begin(); // initialize the PID instance 17 | pid.setpoint(600); // The "goal" the PID controller tries to "reach" 18 | pid.tune(1, 1, 1); // Tune the PID, arguments: kP, kI, kD 19 | pid.limit(0, 255); // Limit the PID output between 0 and 255, this is important to get rid of integral windup! 20 | } 21 | 22 | void loop () { 23 | int sensorValue = analogRead(sensorPin); // Read the value from the sensor 24 | int output = pid.compute(sensorValue); // Let the PID compute the value, returns the optimal output 25 | analogWrite(outputPin, output); // Write the output to the output pin 26 | delay(30); // Delay for 30 ms 27 | } 28 | -------------------------------------------------------------------------------- /src/PIDController.h: -------------------------------------------------------------------------------- 1 | #ifndef PIDControllerLib 2 | #define PIDControllerLib 3 | 4 | #if (ARDUINO >= 100) 5 | #include "Arduino.h" 6 | #else 7 | #include "WProgram.h" 8 | #endif 9 | 10 | #define GRAPH "graph" 11 | #define NOGRAPH "nograph" 12 | #define VERBOSE "verbose" 13 | #define NOVERBOSE "noverbose" 14 | 15 | class PIDController { 16 | public: 17 | // Constructor 18 | PIDController(); 19 | 20 | // Methods - double 21 | double compute(double input, String graph = NOGRAPH, String verbose = NOVERBOSE); 22 | 23 | // Methods - void 24 | void begin(); 25 | void tune(double _Kp, double _Ki, double _Kd); 26 | void limit(double min, double max); 27 | void setpoint(double newSetpoint); 28 | void minimize(double newMinimize); 29 | 30 | // Methods - double, getters 31 | double getOutput(); 32 | private: 33 | // Methods 34 | void printGraph(double sensorInput, String verbose); 35 | 36 | // Variables - long 37 | unsigned long lastTime; 38 | 39 | // Variables - double 40 | double output; 41 | double lastErr; 42 | double timeChanged; 43 | 44 | // Variables - double, error variables 45 | double error; 46 | double errSum; 47 | double dErr; 48 | 49 | // Variables - bool 50 | bool doLimit; 51 | bool init; 52 | 53 | // Variables - double - tuining 54 | double Kp; 55 | double Ki; 56 | double Kd; 57 | double divisor; 58 | double minOut; 59 | double maxOut; 60 | double setPoint; 61 | }; 62 | #endif 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PIDArduino 2 | A simple PID controller library for Arduino 3 | 4 | *Warning, this library hasn't been properly tested yet. Bugs may occur* 5 | 6 | # Usage 7 | ```java 8 | #include 9 | PIDController pid; 10 | ``` 11 | 12 | First include the library. 13 | 14 | Then create an instance of the class. 15 | 16 | --- 17 | 18 | ```java 19 | #include 20 | PIDController pid; 21 | 22 | void setup () { 23 | Serial.begin(9600); // Some methods require the Serial.begin() method to be called first 24 | pid.begin(); // initialize the PID instance 25 | pid.setpoint(600); // The "goal" the PID controller tries to "reach" 26 | pid.tune(1, 1, 1); // Tune the PID, arguments: kP, kI, kD 27 | pid.limit(0, 255); // Limit the PID output between 0 and 255, this is important to get rid of integral windup! 28 | } 29 | 30 | void loop () { 31 | int sensorValue = analogRead(A0); // Read the value from the sensor 32 | int output = pid.compute(sensorValue); // Let the PID compute the value, returns the optimal output 33 | delay(30); // Delay for 30 ms 34 | } 35 | ``` 36 | 37 | In the setup function, call: 38 | 39 | * `pid.begin()` to initialize the object. 40 | 41 | * `pid.setpoint(newSetpoint)` to set the wanted value. 42 | 43 | * `pid.tune(kP, kI, kD)` to tune the controller. 44 | 45 | * `pid.limit(min, max)` to get rid of [integral windup](https://en.wikipedia.org/wiki/Integral_windup) (nasty stuff), and to limit the output values from the controller. 46 | 47 | 48 | ***THIS ARTICLE IS NOT FINISHED YET*** 49 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | First off, thank you for considering contributing to PIDArduino 4 | 5 | PIDArduino is an open source project and we love to recieve cotributions from our community - you! There are many ways to contribute, from writing tutorials, improving the documentation, submitting bug reports and feature request or writing code for the actual library. 6 | 7 | # Rules 8 | 9 | * **§1:** Before submitting an issue, research to ensure it hasn't been submitted before. Avoid duplicate issues 10 | 11 | * **§2:** Use common sense 12 | 13 | # Getting started 14 | 15 | *Please do not edit the master branch, always use the [develop](https://github.com/DonnyCraft1/PIDArduino/tree/develop) branch or a new feature branch when contributing! The only exception is critical hotfixes.* 16 | 17 | *Please read up on the [gitflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) workflow before forking the repo if you plan on making a pull request. We try to keep the master branch clean, and merge develop into master only on new releases. The only exception is documentation. It is merged into master and develop regardless the state of releases. You can think of it as a non-critical hotfix.* 18 | 19 | --- 20 | 21 | For something that is bigger than a one or two line fix: 22 | 23 | 1. First go to the [issue tracker](https://github.com/DonnyCraft1/PIDArduino/issues) and open a new issue and share your toughts about what you would like to change, either a bug you found or a feature you would like to implement 24 | 2. Create your own fork of the repo 25 | 3. Create a new branch from the develop branch called `feature/new-feature`, all lower-case and replacing `new-feature` with a descriptive name for the feature. If it's a hotfix, create a new branch from the master branch and call it `hotfix/new-hotfix`, all lower-case and replacing `new-hotfix` with a descriptive name for the hotfix 26 | 4. Do the changes in *your* fork 27 | 5. If you like the fork and think the project could use it, consider a pull request 28 | 6. Create a pull request with the [develop](https://github.com/DonnyCraft1/PIDArduino/tree/develop) branch as the *base* branch 29 | 30 | --- 31 | 32 | Small contributions such as fixing spelling errors, can be submitted by a contributor as a patch directly on the [develop](https://github.com/DonnyCraft1/PIDArduino/tree/develop) branch. 33 | 34 | This includes: 35 | 36 | * Spelling / grammar fixes 37 | * Typo correction, white space and formatting changes 38 | * Comment clean up 39 | 40 | --- 41 | 42 | Critical bugs can be merged direclty into the master branch as a *hotfix*. 43 | But remember to also merge it to the develop branch before deleting the *hotfix* branch. 44 | 45 | # Commit message conventions 46 | 47 | * The summary should be [capitalized](https://en.wikipedia.org/wiki/Capitalization) (first letter uppercase) 48 | * The summary should *not* end with a period 49 | * The summary should be short and descriptive 50 | * The description should explain all the changes you've made 51 | -------------------------------------------------------------------------------- /src/PIDController.cpp: -------------------------------------------------------------------------------- 1 | #include "PIDController.h" 2 | 3 | PIDController::PIDController () { 4 | // Variables - double 5 | double output; 6 | double lastErr; 7 | double errSum; 8 | 9 | // Variables - long 10 | unsigned long lastTime; 11 | 12 | // Variables - bool 13 | bool doConstrain; 14 | bool init; 15 | 16 | // Variables - double - tuining 17 | double Kp; 18 | double Ki; 19 | double Kd; 20 | double divisor; 21 | double minOut; 22 | double maxOut; 23 | double setPoint; 24 | } 25 | 26 | void PIDController::begin () { 27 | Kp = 1; 28 | Ki = 1; 29 | Kd = 1; 30 | divisor = 10; 31 | doLimit = false; 32 | init = true; 33 | } 34 | 35 | void PIDController::setpoint (double newSetpoint) { 36 | setPoint = newSetpoint; 37 | } 38 | 39 | void PIDController::tune (double _Kp, double _Ki, double _Kd) { 40 | if (_Kp < 0 || _Ki < 0 || _Kd < 0) return; 41 | Kp = _Kp; 42 | Ki = _Ki; 43 | Kd = _Kd; 44 | } 45 | 46 | void PIDController::limit(double min, double max) { 47 | minOut = min; 48 | maxOut = max; 49 | doLimit = true; 50 | } 51 | 52 | void PIDController::printGraph (double sensorInput, String verbose) { 53 | Serial.print(sensorInput); 54 | if (verbose == VERBOSE) { 55 | Serial.print(","); 56 | Serial.print(output); 57 | } 58 | Serial.print(","); 59 | Serial.println(setPoint); 60 | } 61 | 62 | 63 | void PIDController::minimize (double newMinimize) { 64 | divisor = newMinimize; 65 | } 66 | 67 | // Getters 68 | double PIDController::getOutput () { 69 | return output; 70 | } 71 | 72 | 73 | double PIDController::compute (double sensor, String graph, String verbose) { 74 | // Return false if it could not execute; 75 | // This is the actual PID algorithm executed every loop(); 76 | 77 | // Failsafe, return if the begin() method hasn't been called 78 | if (!init) return 0; 79 | 80 | // Calculate time difference since last time executed 81 | unsigned long now = millis(); 82 | double timeChange = (double)(now - lastTime); 83 | 84 | // Calculate error (P, I and D) 85 | double error = setPoint - sensor; 86 | errSum += error * timeChange; 87 | if (doLimit) { 88 | errSum = constrain(errSum, minOut * 1.1, maxOut * 1.1); 89 | } 90 | double dErr = (error - lastErr) / timeChange; 91 | 92 | // Calculate the new output by adding all three elements together 93 | double newOutput = (Kp * error + Ki * errSum + Kd * dErr) / divisor; 94 | 95 | // If limit is specifyed, limit the output 96 | if (doLimit) { 97 | output = constrain(newOutput, minOut, maxOut); 98 | } else { 99 | output = newOutput; 100 | } 101 | 102 | // Update lastErr and lastTime to current values for use in next execution 103 | lastErr = error; 104 | lastTime = now; 105 | 106 | // Draw the garph if GRAPH mode 107 | if (graph == GRAPH) { 108 | printGraph(sensor, verbose); 109 | } 110 | 111 | // Return the current output 112 | return output; 113 | } 114 | --------------------------------------------------------------------------------