diff --git a/sonoff/xdrv_92_pid.ino b/sonoff/xdrv_13_controller.ino similarity index 67% rename from sonoff/xdrv_92_pid.ino rename to sonoff/xdrv_13_controller.ino index 163944e40..cb3a17198 100644 --- a/sonoff/xdrv_92_pid.ino +++ b/sonoff/xdrv_13_controller.ino @@ -1,14 +1,18 @@ /* - xdrv_92_pid.ino - PID algorithm plugin for Sonoff-Tasmota - Copyright (C) 2018 Colin Law and Thomas Herrmann + xdrv_13_controller.ino - Controller Support with TimeProp and PID for Sonoff-Tasmota + + Copyright (C) 2018 Colin Law, Thomas Herrmann and Adrian Scillato + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with this program. If not, see . */ @@ -185,7 +189,7 @@ void PID_Show_Sensor() { const char* value = data_json["DS18B20"]["Temperature"]; // check that something was found and it contains a number //if (value != NULL && strlen(value) > 0 && isdigit(value[0]) ) { - if (value != NULL && strlen(value) > 0 && isdigit(value[0]) && strcmp(value,"0.0") ) { + if (value != NULL && strlen(value) > 0 && (isdigit(value[0]) || (value[0] == '-' && isdigit(value[1])) ) ) { snprintf_P(log_data, sizeof(log_data), "PID_Show_Sensor: Temperature: %s", value); AddLog(LOG_LEVEL_INFO); // pass the value to the pid alogorithm to use as current pv @@ -197,13 +201,11 @@ void PID_Show_Sensor() { run_pid_now = true; } } else { - Timeprop_Set_Power( PID_USE_TIMPROP-1, 0 ); snprintf_P(log_data, sizeof(log_data), "PID_Show_Sensor - no temperature found"); AddLog(LOG_LEVEL_INFO); } } else { // parse failed - Timeprop_Set_Power( PID_USE_TIMPROP-1, 0 ); snprintf_P(log_data, sizeof(log_data), "PID_Show_Sensor - json parse failed"); AddLog(LOG_LEVEL_INFO); } @@ -371,4 +373,231 @@ boolean Xdrv92(byte function) return result; } +#endif // USE_PID + + + + + + + +/* + xdrv_91_timeprop.ino - Timeprop support for Sonoff-Tasmota + Copyright (C) 2018 Colin Law and Thomas Herrmann + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * Code to drive one or more relays in a time proportioned manner give a + * required power value. + * + * Given required power values in the range 0.0 to 1.0 the relays will be + * driven on/off in such that the average power suppled will represent + * the required power. + * The cycle time is configurable. If, for example, the + * period is set to 10 minutes and the power input is 0.2 then the output will + * be on for two minutes in every ten minutes. + * + * A value for actuator dead time may be provided. If you have a device that + * takes a significant time to open/close then set this to the average of the + * open and close times. The algorithim will then adjust the output timing + * accordingly to ensure that the output is not switched more rapidly than + * the actuator can cope with. + * + * A facility to invert the output is provided which can be useful when used in + * refrigeration processes and similar. + * + * In the case where only one relay is being driven the power value is set by + * writing the value to the mqtt topic cmnd/timeprop_setpower_0. If more than + * one relay is being driven (as might be the case for a heat/cool application + * where one relay drives the heater and the other the cooler) then the power + * for the second relay is written to topic cmnd/timeprop_setpower_1 and so on. + * + * To cope with the problem of temporary wifi failure etc a + * TIMEPROP_MAX_UPDATE_INTERVALS value is available. This can be set to the max + * expected time between power updates and if this time is exceeded then the + * power will fallback to a given safe value until a new value is provided. Set + * the interval to 0 to disable this feature. + * + * Usage: + * Place this file in the sonoff folder. + * Clone the library https://github.com/colinl/process-control.git from Github + * into a subfolder of lib. + * In user_config.h or user_config_override.h for a single relay, include + * code as follows: + + #define USE_TIMEPROP // include the timeprop feature (+1.2k) + // for single output + #define TIMEPROP_NUM_OUTPUTS 1 // how many outputs to control (with separate alogorithm for each) + #define TIMEPROP_CYCLETIMES 60 // cycle time seconds + #define TIMEPROP_DEADTIMES 0 // actuator action time seconds + #define TIMEPROP_OPINVERTS false // whether to invert the output + #define TIMEPROP_FALLBACK_POWERS 0 // falls back to this if too long betwen power updates + #define TIMEPROP_MAX_UPDATE_INTERVALS 120 // max no secs that are allowed between power updates (0 to disable) + #define TIMEPROP_RELAYS 1 // which relay to control 1:8 + + * or for two relays: + #define USE_TIMEPROP // include the timeprop feature (+1.2k) + // for single output + #define TIMEPROP_NUM_OUTPUTS 2 // how many outputs to control (with separate alogorithm for each) + #define TIMEPROP_CYCLETIMES 60, 10 // cycle time seconds + #define TIMEPROP_DEADTIMES 0, 0 // actuator action time seconds + #define TIMEPROP_OPINVERTS false, false // whether to invert the output + #define TIMEPROP_FALLBACK_POWERS 0, 0 // falls back to this if too long betwen power updates + #define TIMEPROP_MAX_UPDATE_INTERVALS 120, 120 // max no secs that are allowed between power updates (0 to disable) + #define TIMEPROP_RELAYS 1, 2 // which relay to control 1:8 + + * Publish values between 0 and 1 to the topic(s) described above + * +**/ + + +#ifdef USE_TIMEPROP + +# include "Timeprop.h" + +#define D_CMND_TIMEPROP "timeprop_" +#define D_CMND_TIMEPROP_SETPOWER "setpower_" // add index no on end (0:8) and data is power 0:1 + +enum TimepropCommands { CMND_TIMEPROP_SETPOWER }; +const char kTimepropCommands[] PROGMEM = D_CMND_TIMEPROP_SETPOWER; + +static Timeprop timeprops[TIMEPROP_NUM_OUTPUTS]; +static int relayNos[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_RELAYS}; +static long currentRelayStates = 0; // current actual relay states. Bit 0 first relay + +/* call this from elsewhere if required to set the power value for one of the timeprop instances */ +/* index specifies which one, 0 up */ +void Timeprop_Set_Power( int index, float power ) +{ + if (index >= 0 && index < TIMEPROP_NUM_OUTPUTS) + { + timeprops[index].setPower( power, utc_time); + } +} + +void Timeprop_Init() +{ + snprintf_P(log_data, sizeof(log_data), "Timeprop Init"); + AddLog(LOG_LEVEL_INFO); + int cycleTimes[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_CYCLETIMES}; + int deadTimes[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_DEADTIMES}; + int opInverts[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_OPINVERTS}; + int fallbacks[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_FALLBACK_POWERS}; + int maxIntervals[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_MAX_UPDATE_INTERVALS}; + + for (int i=0; i= 0 ? XdrvMailbox.topic : ""), + (XdrvMailbox.data_len >= 0 ? XdrvMailbox.data : "")); + + AddLog(LOG_LEVEL_INFO); + */ + if (0 == strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_TIMEPROP), ua_prefix_len)) { + // command starts with timeprop_ + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + ua_prefix_len, kTimepropCommands); + if (CMND_TIMEPROP_SETPOWER == command_code) { + /* + snprintf_P(log_data, sizeof(log_data), "Timeprop command timeprop_setpower: " + "index: %d data_len: %d payload: %d topic: %s data: %s", + XdrvMailbox.index, + XdrvMailbox.data_len, + XdrvMailbox.payload, + (XdrvMailbox.payload >= 0 ? XdrvMailbox.topic : ""), + (XdrvMailbox.data_len >= 0 ? XdrvMailbox.data : "")); + AddLog(LOG_LEVEL_INFO); + */ + if (XdrvMailbox.index >=0 && XdrvMailbox.index < TIMEPROP_NUM_OUTPUTS) { + timeprops[XdrvMailbox.index].setPower( atof(XdrvMailbox.data), utc_time ); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_TIMEPROP D_CMND_TIMEPROP_SETPOWER "%d\":\"%s\"}"), + XdrvMailbox.index, XdrvMailbox.data); + } + else { + serviced = false; + } + } else { + serviced = false; + } + return serviced; +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +#define XDRV_91 + +boolean Xdrv91(byte function) +{ + boolean result = false; + + switch (function) { + case FUNC_INIT: + Timeprop_Init(); + break; + case FUNC_EVERY_SECOND: + Timeprop_Every_Second(); + break; + case FUNC_COMMAND: + result = Timeprop_Command(); + break; + case FUNC_SET_POWER: + Timeprop_Xdrv_Power(); + break; + } + return result; +} + #endif // USE_TIMEPROP diff --git a/sonoff/xdrv_91_timeprop.ino b/sonoff/xdrv_91_timeprop.ino deleted file mode 100644 index c14aa0b23..000000000 --- a/sonoff/xdrv_91_timeprop.ino +++ /dev/null @@ -1,220 +0,0 @@ -/* - xdrv_91_timeprop.ino - Timeprop support for Sonoff-Tasmota - Copyright (C) 2018 Colin Law and Thomas Herrmann - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/** - * Code to drive one or more relays in a time proportioned manner give a - * required power value. - * - * Given required power values in the range 0.0 to 1.0 the relays will be - * driven on/off in such that the average power suppled will represent - * the required power. - * The cycle time is configurable. If, for example, the - * period is set to 10 minutes and the power input is 0.2 then the output will - * be on for two minutes in every ten minutes. - * - * A value for actuator dead time may be provided. If you have a device that - * takes a significant time to open/close then set this to the average of the - * open and close times. The algorithim will then adjust the output timing - * accordingly to ensure that the output is not switched more rapidly than - * the actuator can cope with. - * - * A facility to invert the output is provided which can be useful when used in - * refrigeration processes and similar. - * - * In the case where only one relay is being driven the power value is set by - * writing the value to the mqtt topic cmnd/timeprop_setpower_0. If more than - * one relay is being driven (as might be the case for a heat/cool application - * where one relay drives the heater and the other the cooler) then the power - * for the second relay is written to topic cmnd/timeprop_setpower_1 and so on. - * - * To cope with the problem of temporary wifi failure etc a - * TIMEPROP_MAX_UPDATE_INTERVALS value is available. This can be set to the max - * expected time between power updates and if this time is exceeded then the - * power will fallback to a given safe value until a new value is provided. Set - * the interval to 0 to disable this feature. - * - * Usage: - * Place this file in the sonoff folder. - * Clone the library https://github.com/colinl/process-control.git from Github - * into a subfolder of lib. - * In user_config.h or user_config_override.h for a single relay, include - * code as follows: - - #define USE_TIMEPROP // include the timeprop feature (+1.2k) - // for single output - #define TIMEPROP_NUM_OUTPUTS 1 // how many outputs to control (with separate alogorithm for each) - #define TIMEPROP_CYCLETIMES 60 // cycle time seconds - #define TIMEPROP_DEADTIMES 0 // actuator action time seconds - #define TIMEPROP_OPINVERTS false // whether to invert the output - #define TIMEPROP_FALLBACK_POWERS 0 // falls back to this if too long betwen power updates - #define TIMEPROP_MAX_UPDATE_INTERVALS 120 // max no secs that are allowed between power updates (0 to disable) - #define TIMEPROP_RELAYS 1 // which relay to control 1:8 - - * or for two relays: - #define USE_TIMEPROP // include the timeprop feature (+1.2k) - // for single output - #define TIMEPROP_NUM_OUTPUTS 2 // how many outputs to control (with separate alogorithm for each) - #define TIMEPROP_CYCLETIMES 60, 10 // cycle time seconds - #define TIMEPROP_DEADTIMES 0, 0 // actuator action time seconds - #define TIMEPROP_OPINVERTS false, false // whether to invert the output - #define TIMEPROP_FALLBACK_POWERS 0, 0 // falls back to this if too long betwen power updates - #define TIMEPROP_MAX_UPDATE_INTERVALS 120, 120 // max no secs that are allowed between power updates (0 to disable) - #define TIMEPROP_RELAYS 1, 2 // which relay to control 1:8 - - * Publish values between 0 and 1 to the topic(s) described above - * -**/ - - -#ifdef USE_TIMEPROP - -# include "Timeprop.h" - -#define D_CMND_TIMEPROP "timeprop_" -#define D_CMND_TIMEPROP_SETPOWER "setpower_" // add index no on end (0:8) and data is power 0:1 - -enum TimepropCommands { CMND_TIMEPROP_SETPOWER }; -const char kTimepropCommands[] PROGMEM = D_CMND_TIMEPROP_SETPOWER; - -static Timeprop timeprops[TIMEPROP_NUM_OUTPUTS]; -static int relayNos[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_RELAYS}; -static long currentRelayStates = 0; // current actual relay states. Bit 0 first relay - -/* call this from elsewhere if required to set the power value for one of the timeprop instances */ -/* index specifies which one, 0 up */ -void Timeprop_Set_Power( int index, float power ) -{ - if (index >= 0 && index < TIMEPROP_NUM_OUTPUTS) - { - timeprops[index].setPower( power, utc_time); - } -} - -void Timeprop_Init() -{ - snprintf_P(log_data, sizeof(log_data), "Timeprop Init"); - AddLog(LOG_LEVEL_INFO); - int cycleTimes[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_CYCLETIMES}; - int deadTimes[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_DEADTIMES}; - int opInverts[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_OPINVERTS}; - int fallbacks[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_FALLBACK_POWERS}; - int maxIntervals[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_MAX_UPDATE_INTERVALS}; - - for (int i=0; i= 0 ? XdrvMailbox.topic : ""), - (XdrvMailbox.data_len >= 0 ? XdrvMailbox.data : "")); - - AddLog(LOG_LEVEL_INFO); - */ - if (0 == strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_TIMEPROP), ua_prefix_len)) { - // command starts with timeprop_ - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + ua_prefix_len, kTimepropCommands); - if (CMND_TIMEPROP_SETPOWER == command_code) { - /* - snprintf_P(log_data, sizeof(log_data), "Timeprop command timeprop_setpower: " - "index: %d data_len: %d payload: %d topic: %s data: %s", - XdrvMailbox.index, - XdrvMailbox.data_len, - XdrvMailbox.payload, - (XdrvMailbox.payload >= 0 ? XdrvMailbox.topic : ""), - (XdrvMailbox.data_len >= 0 ? XdrvMailbox.data : "")); - AddLog(LOG_LEVEL_INFO); - */ - if (XdrvMailbox.index >=0 && XdrvMailbox.index < TIMEPROP_NUM_OUTPUTS) { - timeprops[XdrvMailbox.index].setPower( atof(XdrvMailbox.data), utc_time ); - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_TIMEPROP D_CMND_TIMEPROP_SETPOWER "%d\":\"%s\"}"), - XdrvMailbox.index, XdrvMailbox.data); - } - else { - serviced = false; - } - } else { - serviced = false; - } - return serviced; -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -#define XDRV_91 - -boolean Xdrv91(byte function) -{ - boolean result = false; - - switch (function) { - case FUNC_INIT: - Timeprop_Init(); - break; - case FUNC_EVERY_SECOND: - Timeprop_Every_Second(); - break; - case FUNC_COMMAND: - result = Timeprop_Command(); - break; - case FUNC_SET_POWER: - Timeprop_Xdrv_Power(); - break; - } - return result; -} - -#endif // USE_TIMEPROP