mirror of https://github.com/arendst/Tasmota.git
Improvements to PID controller driver (#19285)
* add: update DEBUG_THERMOSTAT to only control the virtual switch Also, debug output is still generated but end user can control this debug level 3 * add: debug output of main controller parameters when thermostat enabled Also, add units for debug outputs added to make them more intelligible * Update xdrv_39_thermostat.ino * add: debug message when sensor is detected as not alive fix: display thermostat number in debug messages * add: log message prefix string for thermostat * Update xdrv_39_thermostat.ino fix: typos in comments add: debug messages * add: debug prefix for thermostat * add: debug prefix added to output lines * fix: comment typos and small grammatical changes for clarity * add: debug prefix 'THE' added to debug output * add: display PID status and key info on web output * fix: remove extraneous comments that included unused code fragments * add: local sensor handling improvements add: define a local sensor name add: define the local sensor measurement parameter add: limit sensor not seen error message to every 60 seconds to avoid flooding the logs add: include interval since which sensor data was last updated fix: properly update the maximum interval time so missing sensor data is properly alerted * Update xdrv_49_pid.ino
This commit is contained in:
parent
7544d2f23c
commit
9eb184c30e
|
@ -113,6 +113,15 @@
|
|||
// If not using the sensor then you can supply process values via MQTT using
|
||||
// cmnd PidPv
|
||||
|
||||
#define PID_LOCAL_SENSOR_NAME "DS18B20" // local sensor name when PID_USE_LOCAL_SENSOR is defined above
|
||||
// the JSON payload is parsed for this sensor to find the present value
|
||||
// eg "ESP32":{"Temperature":31.4},"DS18B20":{"Temperature":12.6}
|
||||
|
||||
#define PID_LOCAL_SENSOR_TYPE D_JSON_TEMPERATURE // Choose one of D_JSON_TEMPERATURE D_JSON_HUMIDITY D_JSON_PRESSURE
|
||||
// or any string as the sensor type. The JSON payload is parsed for the
|
||||
// value in this field
|
||||
// eg "HDC1080":{"Temperature":24.8,"Humidity":79.2}
|
||||
|
||||
#define PID_SHUTTER 1 // if using the PID to control a 3-way valve, create Tasmota Shutter and define the
|
||||
// number of the shutter here. Otherwise leave this commented out
|
||||
|
||||
|
@ -159,7 +168,15 @@
|
|||
#ifndef PID_USE_TIMPROP
|
||||
#define PID_USE_TIMPROP 1 // To disable this feature define as false in user_config_override
|
||||
#endif
|
||||
//#define PID_USE_LOCAL_SENSOR // [PidPv] If defined then the local sensor will be used for pv.
|
||||
|
||||
// #define PID_USE_LOCAL_SENSOR // If defined then the local sensor will be used for pv.
|
||||
#ifndef PID_LOCAL_SENSOR_NAME
|
||||
#define PID_LOCAL_SENSOR_NAME "DS18B20" // local sensor name when PID_USE_LOCAL_SENSOR is defined
|
||||
#endif
|
||||
#ifndef PID_LOCAL_SENSOR_TYPE
|
||||
#define PID_LOCAL_SENSOR_TYPE D_JSON_TEMPERATURE // local sensor type
|
||||
#endif
|
||||
|
||||
//#define PID_SHUTTER 1 // Number of the shutter here. Otherwise leave this commented out
|
||||
#ifndef PID_REPORT_MORE_SETTINGS
|
||||
#define PID_REPORT_MORE_SETTINGS true // Override to false if less details are required in SENSOR JSON
|
||||
|
@ -242,31 +259,41 @@ void PIDShowSensor() {
|
|||
// Called each time new sensor data available, data in mqtt data in same format
|
||||
// as published in tele/SENSOR
|
||||
// Update period is specified in TELE_PERIOD
|
||||
if (!isnan(TasmotaGlobal.temperature_celsius)) {
|
||||
const float temperature = TasmotaGlobal.temperature_celsius;
|
||||
|
||||
float sensor_reading = NAN;
|
||||
|
||||
#if defined PID_USE_LOCAL_SENSOR
|
||||
// copy the string into a new buffer that will be modified and
|
||||
// parsed to find the local sensor reading if it's there
|
||||
String buf = ResponseData();
|
||||
JsonParser parser((char*)buf.c_str());
|
||||
JsonParserObject root = parser.getRootObject();
|
||||
if (root) {
|
||||
JsonParserToken value_token = root[PID_LOCAL_SENSOR_NAME].getObject()[PSTR(PID_LOCAL_SENSOR_TYPE)];
|
||||
if (value_token.isNum()) {
|
||||
sensor_reading = value_token.getFloat();
|
||||
}
|
||||
}
|
||||
#endif // PID_USE_LOCAL_SENSOR
|
||||
|
||||
if (!isnan(sensor_reading)) {
|
||||
|
||||
// pass the value to the pid alogorithm to use as current pv
|
||||
Pid.last_pv_update_secs = Pid.current_time_secs;
|
||||
Pid.pid.setPv(temperature, Pid.last_pv_update_secs);
|
||||
Pid.pid.setPv(sensor_reading, Pid.last_pv_update_secs);
|
||||
// also trigger running the pid algorithm if we have been told to run it each pv sample
|
||||
if (Pid.update_secs == 0) {
|
||||
// this runs it at the next second
|
||||
Pid.run_pid_now = true;
|
||||
}
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_ERROR, PSTR("PID: No local temperature sensor found"));
|
||||
// limit sensor not seen message to every 60 seconds to avoid flooding the logs
|
||||
if ((Pid.current_time_secs - Pid.last_pv_update_secs) > Pid.max_interval && ((Pid.current_time_secs - Pid.last_pv_update_secs)%60)==0) {
|
||||
AddLog(LOG_LEVEL_ERROR, PSTR("PID: Local temperature sensor missing for longer than PID_MAX_INTERVAL"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* struct XDRVMAILBOX { */
|
||||
/* uint16_t valid; */
|
||||
/* uint16_t index; */
|
||||
/* uint16_t data_len; */
|
||||
/* int16_t payload; */
|
||||
/* char *topic; */
|
||||
/* char *data; */
|
||||
/* } XdrvMailbox; */
|
||||
|
||||
void CmndSetPv(void) {
|
||||
Pid.last_pv_update_secs = Pid.current_time_secs;
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
|
@ -339,14 +366,11 @@ void CmndSetManualPower(void) {
|
|||
void CmndSetMaxInterval(void) {
|
||||
if (XdrvMailbox.payload >= 0) {
|
||||
Pid.pid.setMaxInterval(XdrvMailbox.payload);
|
||||
Pid.max_interval=XdrvMailbox.payload;
|
||||
}
|
||||
ResponseCmndNumber(Pid.pid.getMaxInterval());
|
||||
}
|
||||
|
||||
// case CMND_PID_SETUPDATE_SECS:
|
||||
// Pid.update_secs = atoi(XdrvMailbox.data) ;
|
||||
// if (Pid.update_secs < 0)
|
||||
// Pid.update_secs = 0;
|
||||
void CmndSetUpdateSecs(void) {
|
||||
if (XdrvMailbox.payload >= 0) {
|
||||
Pid.update_secs = (XdrvMailbox.payload);
|
||||
|
@ -364,51 +388,43 @@ void PIDShowValues(void) {
|
|||
double d_buf;
|
||||
ResponseAppend_P(PSTR(",\"PID\":{"));
|
||||
|
||||
// #define D_CMND_PID_SETPV "Pv"
|
||||
d_buf = Pid.pid.getPv();
|
||||
dtostrfd(d_buf, 2, str_buf);
|
||||
ResponseAppend_P(PSTR("\"PidPv\":%s,"), str_buf);
|
||||
// #define D_CMND_PID_SETSETPOINT "Sp"
|
||||
d_buf = Pid.pid.getSp();
|
||||
dtostrfd(d_buf, 2, str_buf);
|
||||
ResponseAppend_P(PSTR("\"PidSp\":%s,"), str_buf);
|
||||
|
||||
#if PID_REPORT_MORE_SETTINGS
|
||||
// #define D_CMND_PID_SETPROPBAND "Pb"
|
||||
d_buf = Pid.pid.getPb();
|
||||
dtostrfd(d_buf, 2, str_buf);
|
||||
ResponseAppend_P(PSTR("\"PidPb\":%s,"), str_buf);
|
||||
// #define D_CMND_PID_SETINTEGRAL_TIME "Ti"
|
||||
d_buf = Pid.pid.getTi();
|
||||
dtostrfd(d_buf, 2, str_buf);
|
||||
ResponseAppend_P(PSTR("\"PidTi\":%s,"), str_buf);
|
||||
// #define D_CMND_PID_SETDERIVATIVE_TIME "Td"
|
||||
d_buf = Pid.pid.getTd();
|
||||
dtostrfd(d_buf, 2, str_buf);
|
||||
ResponseAppend_P(PSTR("\"PidTd\":%s,"), str_buf);
|
||||
// #define D_CMND_PID_SETINITIAL_INT "Initint"
|
||||
d_buf = Pid.pid.getInitialInt();
|
||||
dtostrfd(d_buf, 2, str_buf);
|
||||
ResponseAppend_P(PSTR("\"PidInitialInt\":%s,"), str_buf);
|
||||
// #define D_CMND_PID_SETDERIV_SMOOTH_FACTOR "DSmooth"
|
||||
d_buf = Pid.pid.getDSmooth();
|
||||
dtostrfd(d_buf, 2, str_buf);
|
||||
ResponseAppend_P(PSTR("\"PidDSmooth\":%s,"), str_buf);
|
||||
// #define D_CMND_PID_SETAUTO "Auto"
|
||||
chr_buf = Pid.pid.getAuto();
|
||||
ResponseAppend_P(PSTR("\"PidAuto\":%d,"), chr_buf);
|
||||
// #define D_CMND_PID_SETMANUAL_POWER "ManualPower"
|
||||
d_buf = Pid.pid.getManualPower();
|
||||
dtostrfd(d_buf, 2, str_buf);
|
||||
ResponseAppend_P(PSTR("\"PidManualPower\":%s,"), str_buf);
|
||||
// #define D_CMND_PID_SETMAX_INTERVAL "MaxInterval"
|
||||
i_buf = Pid.pid.getMaxInterval();
|
||||
ResponseAppend_P(PSTR("\"PidMaxInterval\":%d,"), i_buf);
|
||||
|
||||
// #define D_CMND_PID_SETUPDATE_SECS "UpdateSecs"
|
||||
i_buf = Pid.current_time_secs - Pid.last_pv_update_secs;
|
||||
ResponseAppend_P(PSTR("\"PidInterval\":%d,"), i_buf);
|
||||
ResponseAppend_P(PSTR("\"PidUpdateSecs\":%d,"), Pid.update_secs);
|
||||
#endif // PID_REPORT_MORE_SETTINGS
|
||||
|
||||
i_buf = (Pid.current_time_secs - Pid.last_pv_update_secs) > Pid.pid.getMaxInterval();
|
||||
ResponseAppend_P(PSTR("\"PidSensorLost\":%d,"), i_buf);
|
||||
// The actual power value
|
||||
d_buf = Pid.pid.tick(Pid.current_time_secs);
|
||||
dtostrfd(d_buf, 2, str_buf);
|
||||
|
@ -417,6 +433,38 @@ void PIDShowValues(void) {
|
|||
ResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
|
||||
void PIDShowValuesWeb(void) {
|
||||
|
||||
#define D_PID_DISPLAY_NAME "PID Controller"
|
||||
#define D_PID_SET_POINT "Set Point"
|
||||
#define D_PID_PRESENT_VALUE "Current Value"
|
||||
#define D_PID_POWER "Power"
|
||||
#define D_PID_MODE "Controller Mode"
|
||||
#define D_PID_MODE_AUTO "Auto"
|
||||
#define D_PID_MODE_MANUAL "Manual"
|
||||
#define D_PID_MODE_OFF "Off"
|
||||
|
||||
const char HTTP_PID_HL[] PROGMEM = "{s}<hr>{m}<hr>{e}";
|
||||
const char HTTP_PID_INFO[] PROGMEM = "{s}" D_PID_DISPLAY_NAME "{m}%s{e}";
|
||||
const char HTTP_PID_SP_FORMAT[] PROGMEM = "{s}%s " "{m}%*_f ";
|
||||
const char HTTP_PID_PV_FORMAT[] PROGMEM = "{s}%s " "{m}%*_f ";
|
||||
const char HTTP_PID_POWER_FORMAT[] PROGMEM = "{s}%s " "{m}%*_f " D_UNIT_PERCENT;
|
||||
|
||||
float f_buf;
|
||||
|
||||
WSContentSend_P(HTTP_PID_HL);
|
||||
WSContentSend_P(HTTP_PID_INFO, (Pid.pid.getAuto()==1) ? D_PID_MODE_AUTO : Pid.pid.tick(Pid.current_time_secs)>0.0f ? D_PID_MODE_MANUAL : D_PID_MODE_OFF);
|
||||
|
||||
if (Pid.pid.getAuto()==1 || Pid.pid.tick(Pid.current_time_secs)>0.0f) {
|
||||
f_buf = (float)Pid.pid.getSp();
|
||||
WSContentSend_PD(HTTP_PID_SP_FORMAT, D_PID_SET_POINT, 1, &f_buf);
|
||||
f_buf = (float)Pid.pid.getPv();
|
||||
WSContentSend_PD(HTTP_PID_PV_FORMAT, D_PID_PRESENT_VALUE, 1, &f_buf);
|
||||
f_buf = Pid.pid.tick(Pid.current_time_secs)*100.0f;
|
||||
WSContentSend_PD(HTTP_PID_POWER_FORMAT, D_PID_POWER, 0, &f_buf);
|
||||
}
|
||||
}
|
||||
|
||||
void PIDRun(void) {
|
||||
double power = Pid.pid.tick(Pid.current_time_secs);
|
||||
#ifdef PID_DONT_USE_PID_TOPIC
|
||||
|
@ -468,6 +516,9 @@ bool Xdrv49(uint32_t function) {
|
|||
case FUNC_JSON_APPEND:
|
||||
PIDShowValues();
|
||||
break;
|
||||
case FUNC_WEB_SENSOR:
|
||||
PIDShowValuesWeb();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue