Skip to content

Commit

Permalink
Version 2.2.2
Browse files Browse the repository at this point in the history
- Added compatibility with the ESP32 Arduino framework
- Added full featured AnalogWrite methods for ESP32 to control up to 9 PWM and 2 DAC signals.
  • Loading branch information
Dlloydev committed Apr 3, 2021
1 parent fbef1f1 commit e87ddb4
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 34 deletions.
72 changes: 66 additions & 6 deletions QuickPID.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
The parameters specified here are those for for which we can't set up
reliable defaults, so we need to have the user set them.
**********************************************************************************/
QuickPID::QuickPID(int16_t* Input, int16_t* Output, int16_t* Setpoint,
QuickPID::QuickPID(int* Input, int* Output, int* Setpoint,
float Kp, float Ki, float Kd, float POn = 1, uint8_t ControllerDirection = 0)
{
myOutput = Output;
Expand All @@ -40,7 +40,7 @@ QuickPID::QuickPID(int16_t* Input, int16_t* Output, int16_t* Setpoint,
to use Proportional on Error without explicitly saying so.
**********************************************************************************/

QuickPID::QuickPID(int16_t* Input, int16_t* Output, int16_t* Setpoint,
QuickPID::QuickPID(int* Input, int* Output, int* Setpoint,
float Kp, float Ki, float Kd, uint8_t ControllerDirection)
: QuickPID::QuickPID(Input, Output, Setpoint, Kp, Ki, Kd, pOn = 1, ControllerDirection = 0)
{
Expand All @@ -60,8 +60,8 @@ bool QuickPID::Compute()
uint32_t timeChange = (now - lastTime);

if (timeChange >= sampleTimeUs) { // Compute the working error variables
int16_t input = *myInput;
int16_t dInput = input - lastInput;
int input = *myInput;
int dInput = input - lastInput;
error = *mySetpoint - input;

if (kpi < 31 && kpd < 31) outputSum += FX_MUL(FL_FX(kpi) , error) - FX_MUL(FL_FX(kpd), dInput); // fixed-point
Expand Down Expand Up @@ -204,7 +204,7 @@ void QuickPID::SetSampleTimeUs(uint32_t NewSampleTimeUs)
The PID controller is designed to vary its output within a given range.
By default this range is 0-255, the Arduino PWM range.
******************************************************************************/
void QuickPID::SetOutputLimits(int16_t Min, int16_t Max)
void QuickPID::SetOutputLimits(int Min, int Max)
{
if (Min >= Max) return;
outMin = Min;
Expand Down Expand Up @@ -329,7 +329,7 @@ float QuickPID::analogReadAvg(int ADCpin) {
return (float)sum / 16.0;
}

int16_t QuickPID::Saturate(int16_t Out) {
int QuickPID::Saturate(int Out) {
if (Out > outMax) Out = outMax;
else if (Out < outMin) Out = outMin;
return Out;
Expand Down Expand Up @@ -360,3 +360,63 @@ void QuickPID::Stabilize(int inputPin, int outputPin, uint32_t timeout) {
while ((analogReadAvg(inputPin) < atSetpoint) && (millis() <= timeout));
analogWrite(outputPin, atOutput);
}

#if defined(ESP32)

// Adds support for analogWrite() for up to 9 PWM pins plus pins DAC1 and DAC2 which are 8-bit true analog outputs.
// Also adds support for changing the PWM frequency (5000 Hz default) and timer resolution (13-bit default).

analog_write_channel_t _analog_write_channels[9] = { { 2, 5000, 13}, //LED_BUILTIN
{ 4, 5000, 13}, { 13, 5000, 13}, { 14, 5000, 13}, { 16, 5000, 13}, { 17, 5000, 13}, { 27, 5000, 13}, { 32, 5000, 13}, { 33, 5000, 13}
};

void analogWriteFrequency(float frequency) {
for (uint8_t i = 0; i < 9; i++) {
_analog_write_channels[i].frequency = frequency;
if (frequency < 0.0001) {
ledcDetachPin(_analog_write_channels[i].pin);
pinMode(_analog_write_channels[i].pin, INPUT);
}
}
}

void analogWriteFrequency(uint8_t pin, float frequency) {
for (uint8_t i = 0; i < 9; i++) {
if (_analog_write_channels[i].pin == pin) {
_analog_write_channels[i].frequency = frequency;
if (frequency < 0.0001) {
ledcDetachPin(_analog_write_channels[i].pin);
pinMode(_analog_write_channels[i].pin, INPUT);
}
}
}
}

void analogWriteResolution(uint8_t resolution) {
for (uint8_t i = 0; i < 9; i++) {
_analog_write_channels[i].resolution = resolution;
}
}

void analogWriteResolution(uint8_t pin, uint8_t resolution) {
for (uint8_t i = 0; i < 9; i++) {
if (_analog_write_channels[i].pin == pin) {
_analog_write_channels[i].resolution = resolution;
ledcSetup(i, _analog_write_channels[i].frequency, resolution);
ledcAttachPin(_analog_write_channels[i].pin, i);
}
}
}

void analogWrite(uint8_t pin, uint32_t value) {
for (uint8_t i = 0; i < 9; i++) {
if (_analog_write_channels[i].pin == pin) {
uint8_t resolution = _analog_write_channels[i].resolution;
uint32_t valueMax = (pow(2, resolution)) - 1;
if (value > valueMax) value = valueMax;
ledcWrite(i, value);
}
if (pin == DAC1 || pin == DAC2) dacWrite(pin, value & 255);
}
}
#endif
49 changes: 33 additions & 16 deletions QuickPID.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ class QuickPID
// commonly used functions ************************************************************************************

// Constructor. Links the PID to Input, Output, Setpoint and initial Tuning Parameters.
QuickPID(int16_t*, int16_t*, int16_t*, float, float, float, float, uint8_t);
QuickPID(int*, int*, int*, float, float, float, float, uint8_t);

// Overload constructor with proportional mode. Links the PID to Input, Output, Setpoint and Tuning Parameters.
QuickPID(int16_t*, int16_t*, int16_t*, float, float, float, uint8_t);
QuickPID(int*, int*, int*, float, float, float, uint8_t);

// Sets PID to either Manual (0) or Auto (non-0).
void SetMode(uint8_t Mode);
Expand All @@ -34,7 +34,7 @@ class QuickPID
void AutoTune(int, int, int, int, uint32_t);

// Sets and clamps the output to a specific range (0-255 by default).
void SetOutputLimits(int16_t, int16_t);
void SetOutputLimits(int, int);

// available but not commonly used functions ******************************************************************

Expand Down Expand Up @@ -68,35 +68,35 @@ class QuickPID

private:
void Initialize();
int16_t Saturate(int16_t);
int Saturate(int);
void CheckPeak(int);
void StepUp(int, int, uint32_t);
void StepDown(int, int, uint32_t);
void Stabilize(int, int, uint32_t);

float dispKp; // We'll hold on to the tuning parameters for display purposes.
float dispKp; // We'll hold on to the tuning parameters for display purposes.
float dispKi;
float dispKd;
float dispKu;
float dispTu;
float dispTd;

float pOn; // proportional mode (0-1) default = 1, 100% Proportional on Error
float kp; // (P)roportional Tuning Parameter
float ki; // (I)ntegral Tuning Parameter
float kd; // (D)erivative Tuning Parameter
float kpi; // proportional on error amount
float kpd; // proportional on measurement amount
float pOn; // proportional mode (0-1) default = 1, 100% Proportional on Error
float kp; // (P)roportional Tuning Parameter
float ki; // (I)ntegral Tuning Parameter
float kd; // (D)erivative Tuning Parameter
float kpi; // proportional on error amount
float kpd; // proportional on measurement amount

uint8_t controllerDirection;

int16_t *myInput; // Pointers to the Input, Output, and Setpoint variables. This creates a
int16_t *myOutput; // hard link between the variables and the PID, freeing the user from having
int16_t *mySetpoint; // to constantly tell us what these values are. With pointers we'll just know.
int *myInput; // Pointers to the Input, Output, and Setpoint variables. This creates a
int *myOutput; // hard link between the variables and the PID, freeing the user from having
int *mySetpoint; // to constantly tell us what these values are. With pointers we'll just know.

uint32_t sampleTimeUs, lastTime;
int16_t outMin, outMax, error;
int16_t lastInput, outputSum;
int outMin, outMax, error;
int lastInput, outputSum;
bool inAuto;

// AutoTune
Expand All @@ -109,4 +109,21 @@ class QuickPID

};

#if defined(ESP32)
// Adds support for analogWrite() for up to 16 PWM pins plus pins DAC1 and DAC2 which are 8-bit true analog outputs.
// Also adds support for changing the PWM frequency (5000 Hz default) and timer resolution (13-bit default).

typedef struct analog_write_channel {
int8_t pin;
double frequency;
uint8_t resolution;
} analog_write_channel_t;

void analogWriteFrequency(float frequency = 5000);
void analogWriteFrequency(uint8_t pin, float frequency = 5000);
void analogWriteResolution(uint8_t resolution = 13);
void analogWriteResolution(uint8_t pin, uint8_t resolution = 13);
void analogWrite(uint8_t pin, uint32_t value = 0);
#endif

#endif
72 changes: 63 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Development began with a fork of the Arduino PID Library. Modifications and new
- `POn` parameter controls the setpoint weighting and mix of Proportional on Error to Proportional on Measurement
- Reorganized and more efficient PID algorithm, faster analog read function, micros() timing resolution
- Runs a complete PID cycle (*read-compute-write*) faster than just an `analogRead()` command in Arduino
- Includes a complete`analogWrite()`function for ESP32 boards. This controls up to 9 independent PWM pins and 2 DAC pins.

### Performance

Expand Down Expand Up @@ -51,8 +52,8 @@ The new `kpi` and `kpd` parameters are calculated in the `SetTunings()` function
#### QuickPID_Constructor

```c++
QuickPID::QuickPID(int16_t* Input, int16_t* Output, int16_t* Setpoint,
float Kp, float Ki, float Kd, float POn, bool ControllerDirection)
QuickPID::QuickPID(int* Input, int* Output, int* Setpoint,
float Kp, float Ki, float Kd, float POn, uint8_t ControllerDirection)
```
- `Input`, `Output`, and `Setpoint` are pointers to the variables holding these values.
Expand All @@ -64,8 +65,8 @@ QuickPID::QuickPID(int16_t* Input, int16_t* Output, int16_t* Setpoint,
- `ControllerDirection` Either DIRECT or REVERSE determines which direction the output will move for a given error. DIRECT is most common.
```c++
QuickPID::QuickPID(int16_t* Input, int16_t* Output, int16_t* Setpoint,
float Kp, float Ki, float Kd, bool ControllerDirection)
QuickPID::QuickPID(int* Input, int* Output, int* Setpoint,
float Kp, float Ki, float Kd, uint8_t ControllerDirection)
```

This allows you to use Proportional on Error without explicitly saying so.
Expand Down Expand Up @@ -119,15 +120,15 @@ Sets the period, in microseconds, at which the calculation is performed. The def
#### SetOutputLimits

```c++
void QuickPID::SetOutputLimits(int16_t Min, int16_t Max)
void QuickPID::SetOutputLimits(int Min, int Max)
```
The PID controller is designed to vary its output within a given range. By default this range is 0-255, the Arduino PWM range.
#### SetMode
```c++
void QuickPID::SetMode(bool Mode)
void QuickPID::SetMode(uint8_t Mode)
```

Allows the controller Mode to be set to `MANUAL` (0) or `AUTOMATIC` (non-zero). when the transition from manual to automatic occurs, the controller is automatically initialized.
Expand All @@ -143,7 +144,7 @@ Does all the things that need to happen to ensure a bump-less transfer from manu
#### SetControllerDirection

```c++
void QuickPID::SetControllerDirection(bool Direction)
void QuickPID::SetControllerDirection(uint8_t Direction)
```
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.
Expand All @@ -157,8 +158,8 @@ float QuickPID::GetKd()
float QuickPID::GetKu()
float QuickPID::GetTu()
float QuickPID::GetTd()
bool QuickPID::GetMode()
bool QuickPID::GetDirection()
uint8_t QuickPID::GetMode()
uint8_t QuickPID::GetDirection()
```

These functions query the internal state of the PID. They're here for display purposes.
Expand All @@ -171,10 +172,63 @@ int QuickPID::analogReadFast(int ADCpin)
A faster configuration of `analogRead()`where a preset of 32 is used. If the architecture definition isn't found, normal `analogRead()`is used to return a value.
#### AnalogWrite (PWM and DAC) for ESP32
```c++
void analogWrite(uint8_t pin, uint32_t value)
```

Call this function just like in the standard Arduino framework. It controls up to 9 independent PWM outputs and 2 DAC outputs. The controllable GPIO pins are 2, 4, 13, 14, 16, 17, 27, 32 and 33 for PWM and DAC0 (GPIO25) and DAC1 (GPIO26) for true analog outputs. The default PWM frequency is 5000 Hz and the default resolution is 13-bit (0-8191).

#### AnalogWrite Configuration Functions for ESP32

```c++
void analogWriteFrequency(float frequency = 5000);
void analogWriteFrequency(uint8_t pin, float frequency = 5000);
void analogWriteResolution(uint8_t resolution = 13);
void analogWriteResolution(uint8_t pin, uint8_t resolution = 13);
```
Calling `analogWriteFrequency(frequency)`will set the PWM frequency for all 8 assigned pins. Using `analogWriteFrequency(0)`will detach the 9 assigned pins and set them as input.
To independently assign a unique frequency to each PWM pin, use the `analogWriteFrequency(pin, frequency)` function. If the frequency is set to 0, this function will detach the referenced pin and configure it as an input.
Calling `analogWriteResolution(resolution)` will set the resolution for all 9 assigned pins. Read more about the [Supported Range of Frequency and Duty Resolution](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/ledc.html#ledc-api-supported-range-frequency-duty-resolution) here.
To independently assign a unique frequency to each PWM pin, use the `analogWriteResolution(pin, resolution)` function. Note that it is required to call this function once prior to using AnalogWrite as this will automatically setup and attach (initialize) the pin.
#### Fade Example for ESP32
```c++
#include "QuickPID.h"
#define LED_BUILTIN 2
int brightness = 0;
int step = 1;
void setup() {
analogWriteResolution(LED_BUILTIN, 10);
}
void loop() {
analogWrite(LED_BUILTIN, brightness);
brightness += step;
if ( brightness >= 1023) step = -1;
if ( brightness <= 0) step = 1;
delay(1);
}
```

### Change Log

#### [![arduino-library-badge](https://www.ardu-badge.com/badge/QuickPID.svg?)](https://www.ardu-badge.com/QuickPID)

- Added compatibility with the ESP32 Arduino framework
- Added full featured AnalogWrite methods for ESP32 to control up to 9 PWM and 2 DAC signals

#### Version 2.2.1

- Even faster AutoTune function
- AutoTune now determines the controllability of the process
- Added AMIGO_PID tuning rule
Expand Down
4 changes: 4 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
##########################################

QuickPID KEYWORD1
analog_write_channel_t KEYWORD1

##########################################
# Methods and Functions (KEYWORD2)
Expand All @@ -29,6 +30,9 @@ GetMode KEYWORD2
GetDirection KEYWORD2
analogReadFast KEYWORD2
analogReadAvg KEYWORD2
analogWrite KEYWORD2
analogWriteFrequency KEYWORD2
analogWriteResolution KEYWORD2

##########################################
# Constants (LITERAL1)
Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "QuickPID",
"keywords": "PID, controller, signal",
"description": "A fast fixed/floating point PID controller with AutoTune and 9 tuning rules to choose from. This controller can automatically determine and set tuning parameters. An added feature is contolling the mix of Proportional on Error to Proportional on Measurement.",
"description": "A fast fixed/floating point PID controller with AutoTune and 9 tuning rules to choose from. This controller can automatically determine and set tuning parameters. Compatible with most Arduino and ESP32 boards.",
"url": "https://github.com/Dlloydev/QuickPID",
"include": "QuickPID",
"authors":
Expand Down
4 changes: 2 additions & 2 deletions library.properties
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name=QuickPID
version=2.2.1
version=2.2.2
author=David Lloyd
maintainer=David Lloyd <dlloydev@testcor.ca>
sentence=A fast fixed/floating point PID controller with AutoTune and 9 tuning rules to choose from.
paragraph=This controller can automatically determine and set tuning parameters. An added feature is contolling the mix of Proportional on Error to Proportional on Measurement.
paragraph=This controller can automatically determine and set tuning parameters. Compatible with most Arduino and ESP32 boards.
category=Signal Input/Output
url=https://github.com/Dlloydev/QuickPID
architectures=*
Expand Down

0 comments on commit e87ddb4

Please sign in to comment.