Skip to content

Commit

Permalink
Merge pull request #10 from arendst/development
Browse files Browse the repository at this point in the history
update dev branch
  • Loading branch information
vic42 authored Jan 7, 2021
2 parents 02468cf + 98b529c commit 57507e4
Show file tree
Hide file tree
Showing 17 changed files with 1,277 additions and 62 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
## [Unreleased] - Development

## [9.2.0.3]
### Added
- Support for time proportioned (``#define USE_TIMEPROP``) and optional PID (``#define USE_PID``) relay control (#10412)

### Breaking Changed
- ESP32 switch from default SPIFFS to default LittleFS file system loosing current (zigbee) files

Expand Down
1 change: 1 addition & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Support for IR inverted leds using ``#define IR_SEND_INVERTED true`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
- Support for disabling 38kHz IR modulation using ``#define IR_SEND_USE_MODULATION false`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
- Support for SPI display driver for ST7789 TFT by Gerhard Mutz [#9037](https://github.com/arendst/Tasmota/issues/9037)
- Support for time proportioned (``#define USE_TIMEPROP``) and optional PID (``#define USE_PID``) relay control [#10412](https://github.com/arendst/Tasmota/issues/10412)
- Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin [#8630](https://github.com/arendst/Tasmota/issues/8630)
- SPI display driver SSD1331 Color oled by Jeroen Vermeulen [#10376](https://github.com/arendst/Tasmota/issues/10376)

Expand Down
234 changes: 234 additions & 0 deletions lib/lib_div/ProcessControl/PID.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
/**
* Copyright 2018 Colin Law
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* See Timeprop.h for Usage
*
**/


#include "PID.h"

PID::PID() {
m_initialised = 0;
m_last_sample_time = 0;
m_last_pv_update_time = 0;
m_last_power = 0.0;
}

void PID::initialise( double setpoint, double prop_band, double t_integral, double t_derivative,
double integral_default, int max_interval, double smooth_factor, unsigned char mode_auto, double manual_op ) {

m_setpoint = setpoint;
m_prop_band = prop_band;
m_t_integral = t_integral;
m_t_derivative = t_derivative;
m_integral_default = integral_default;
m_max_interval = max_interval;
m_smooth_factor= smooth_factor;
m_mode_auto= mode_auto;
m_manual_op = manual_op;

m_initialised = 1;

}


/* called regularly to calculate and return new power value */
double PID::tick( unsigned long nowSecs ) {
double power;
double factor;
if (m_initialised && m_last_pv_update_time) {
// we have been initialised and have been given a pv value
// check whether too long has elapsed since pv was last updated
if (m_max_interval > 0 && nowSecs - m_last_pv_update_time > m_max_interval) {
// yes, too long has elapsed since last PV update so go to fallback power
power = m_manual_op;
} else {
// is this the first time through here?
if (m_last_sample_time) {
// not first time
unsigned long delta_t = nowSecs - m_last_sample_time; // seconds
if (delta_t <= 0 || delta_t > m_max_interval) {
// too long since last sample so leave integral as is and set deriv to zero
m_derivative = 0;
} else {
if (m_smooth_factor > 0) {
// A derivative smoothing factor has been supplied
// smoothing time constant is td/factor but with a min of delta_t to stop overflows
int ts = m_t_derivative/m_smooth_factor > delta_t ? m_t_derivative/m_smooth_factor : delta_t;
factor = 1.0/(ts/delta_t);
} else {
// no integral smoothing so factor is 1, this makes smoothed_value the previous pv
factor = 1.0;
}
double delta_v = (m_pv - m_smoothed_value) * factor;
m_smoothed_value = m_smoothed_value + delta_v;
m_derivative = m_t_derivative * delta_v/delta_t;
// lock the integral if abs(previous integral + error) > prop_band/2
// as this means that P + I is outside the linear region so power will be 0 or full
// also lock if control is disabled
double error = m_pv - m_setpoint;
double pbo2 = m_prop_band/2.0;
double epi = error + m_integral;
if (epi < 0.0) epi = -epi; // abs value of error + m_integral
if (epi < pbo2 && m_mode_auto) {
if (m_t_integral <= 0) {
// t_integral is zero (or silly), set integral to one end or the other
// or half way if exactly on sp
if (error > 0.0) {
m_integral = pbo2;
} else if (error < 0) {
m_integral = -pbo2;
} else {
m_integral = 0.0;
}
} else {
m_integral = m_integral + error * delta_t/m_t_integral;
}
}
// clamp to +- 0.5 prop band widths so that it cannot push the zero power point outside the pb
// do this here rather than when integral is updated to allow for the fact that the pb may change dynamically
if ( m_integral < -pbo2 ) {
m_integral = -pbo2;
} else if (m_integral > pbo2) {
m_integral = pbo2;
}
}

} else {
// first time through, initialise context data
m_smoothed_value = m_pv;
// setup the integral term so that the power out would be integral_default if pv=setpoint
m_integral = (0.5 - m_integral_default)*m_prop_band;
m_derivative = 0.0;
}

double proportional = m_pv - m_setpoint;
if (m_prop_band == 0) {
// prop band is zero so drop back to on/off control with zero hysteresis
if (proportional > 0.0) {
power = 0.0;
} else if (proportional < 0.0) {
power = 1.0;
} else {
// exactly on sp so leave power as it was last time round
power = m_last_power;
}
}
else {
power = -1.0/m_prop_band * (proportional + m_integral + m_derivative) + 0.5;
}
// set power to disabled value if the loop is not enabled
if (!m_mode_auto) {
power = m_manual_op;
}
m_last_sample_time = nowSecs;
}
} else {
// not yet initialised or no pv value yet so set power to disabled value
power = m_manual_op;
}
if (power < 0.0) {
power = 0.0;
} else if (power > 1.0) {
power = 1.0;
}
m_last_power = power;
return power;
}

// call to pass in new process value
void PID::setPv( double pv, unsigned long nowSecs ){
m_pv = pv;
m_last_pv_update_time = nowSecs;
}

// methods to modify configuration data
void PID::setSp( double setpoint ) {
m_setpoint = setpoint;
}

void PID::setPb( double prop_band ) {
m_prop_band = prop_band;
}

void PID::setTi( double t_integral ) {
m_t_integral = t_integral;
}

void PID::setTd( double t_derivative ) {
m_t_derivative = t_derivative;
}

void PID::setInitialInt( double integral_default ) {
m_integral_default = integral_default;
}

void PID::setDSmooth( double smooth_factor ) {
m_smooth_factor = smooth_factor;
}

void PID::setAuto( unsigned char mode_auto ) {
m_mode_auto = mode_auto;
}

void PID::setManualPower( double manual_op ) {
m_manual_op = manual_op;
}

void PID::setMaxInterval( int max_interval ) {
m_max_interval = max_interval;
}


double PID::getPv() {
return(m_pv);
}

double PID::getSp() {
return(m_setpoint);
}

double PID::getPb() {
return(m_prop_band);
}

double PID::getTi() {
return(m_t_integral);
}

double PID::getTd() {
return(m_t_derivative);
}

double PID::getInitialInt() {
return(m_integral_default);
}

double PID::getDSmooth() {
return(m_smooth_factor);
}

unsigned char PID::getAuto() {
return(m_mode_auto);
}

double PID::getManualPower() {
return(m_manual_op);
}

int PID::getMaxInterval() {
return(m_max_interval);
}
101 changes: 101 additions & 0 deletions lib/lib_div/ProcessControl/PID.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Copyright 2018 Colin Law
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

/**
* A PID control class
*
* Github repository https://github.com/colinl/process-control.git
*
* Given ...
*
* Usage:
* First call initialise(), see below for parameters then
* ...
* The functions require a parameter nowSecs which is a representation of the
* current time in seconds. The absolute value of this is immaterial, it is
* used for relative timing only.
*
**/


#ifndef PID_h
#define PID_h

class PID {
public:

PID();

/*
Initialiser given
current time in seconds
*/
void initialise( double setpoint, double prop_band, double t_integral, double t_derivative,
double integral_default, int max_interval, double smooth_factor, unsigned char mode_auto, double manual_op );


/* called regularly to calculate and return new power value */
double tick(unsigned long nowSecs);

// call to pass in new process value
void setPv( double pv, unsigned long nowSecs );

// methods to modify configuration data
void setSp( double setpoint );
void setPb( double prop_band );
void setTi( double t_integral );
void setTd( double t_derivative );
void setInitialInt( double integral_default );
void setDSmooth( double smooth_factor );
void setAuto( unsigned char mode_auto );
void setManualPower( double manual_op );
void setMaxInterval( int max_interval );

double getPv();
double getSp();
double getPb();
double getTi();
double getTd();
double getInitialInt();
double getDSmooth();
unsigned char getAuto();
double getManualPower();
int getMaxInterval();

private:
double m_pv;
double m_setpoint;
double m_prop_band;
double m_t_integral;
double m_t_derivative;
double m_integral_default;
double m_smooth_factor;
unsigned char m_mode_auto;
double m_manual_op;
int m_max_interval;
double m_last_power;


unsigned char m_initialised;
unsigned long m_last_pv_update_time; // the time of last pv update secs
unsigned long m_last_sample_time; // the time of the last tick() run
double m_smoothed_value;
double m_integral;
double m_derivative ;
};

#endif // Timeprop_h
Loading

0 comments on commit 57507e4

Please sign in to comment.