Added DemandManager, removing TimerManager from direct temperature demand control aspects

Likewise moved demand adjustments into DemandManager.
This commit is contained in:
Ray Jones 2020-04-11 18:49:52 +10:00
parent 9839571893
commit 67998747d7
23 changed files with 553 additions and 511 deletions

View File

@ -119,6 +119,7 @@
#include <FreeRTOS.h>
#include "RTC/TimerManager.h"
#include "Utility/GetLine.h"
#include "Utility/DemandManager.h"
// SSID & password now stored in NV storage - these are still the default values.
//#define AP_SSID "Afterburner"
@ -155,7 +156,6 @@ void checkDebugCommands();
void manageCyclicMode();
void manageFrostMode();
void manageHumidity();
int checkStartTemp();
void doStreaming();
void heaterOn();
void heaterOff();
@ -519,11 +519,9 @@ void setup() {
pHourMeter->init(bESP32PowerUpInit || RTC_Store.getBootInit()); // ensure persistent memory variable are reset after powerup, or OTA update
RTC_Store.setBootInit(false);
// bug fix: was not applying saved set points!
// reqDemand(RTC_Store.getDesiredTemp());
CTimerManager::setWorkingTemperature(RTC_Store.getDesiredTemp());
CTimerManager::setWorkingPumpHz(RTC_Store.getDesiredPump());
// apply saved set points!
CDemandManager::reload();
// Check for solo DS18B20
// store it's serial number as the primary sensor
// This allows seamless standard operation, and marks the iniital sensor
@ -952,7 +950,7 @@ void manageCyclicMode()
const sCyclicThermostat& cyclic = NVstore.getUserSettings().cyclic;
if(cyclic.Stop && RTC_Store.getCyclicEngaged()) { // cyclic mode enabled, and user has started heater
int stopDeltaT = cyclic.Stop + 1; // bump up by 1 degree - no point invoking at 1 deg over!
float deltaT = getTemperatureSensor() - getDemandDegC();
float deltaT = getTemperatureSensor() - CDemandManager::getDegC();
// DebugPort.printf("Cyclic=%d bUserOn=%d deltaT=%d\r\n", cyclic, bUserON, deltaT);
// ensure we cancel user ON mode if heater throws an error
@ -1024,31 +1022,6 @@ void manageHumidity()
}
}
int checkStartTemp()
{
int stopDeltaT = 0;
int cyclicstop = NVstore.getUserSettings().cyclic.Stop;
if(cyclicstop) { // cyclic mode enabled
stopDeltaT = cyclicstop + 1; // bump up by 1 degree - no point invoking at 1 deg over!
}
float deltaT = getTemperatureSensor() - getDemandDegC();
if(deltaT > stopDeltaT) {
if(cyclicstop) {
DebugPort.printf("CYCLIC MODE: Skipping directly to suspend, deltaT > +%d\r\n", stopDeltaT);
heaterOff(); // over temp - request heater stop
return -2;
}
else {
// too warm - deny start
return -1;
}
}
return 0;
}
void initBlueWireSerial()
{
@ -1080,35 +1053,35 @@ bool validateFrame(const CProtocol& frame, const char* name)
}
// return values:
// 0: OK
// -1: too warm
// -2: suspended
// -3: Low Voltage Cutout
// -4: Insufficent fuel
int requestOn(bool checkTemp)
CDemandManager::eStartCode
requestOn()
{
DebugPort.println("Start Request!");
bool fuelOK = 2 != SmartError.checkfuelUsage();
if(!fuelOK) {
return -4;
return CDemandManager::eStartLowFuel;
}
bool LVCOK = 2 != SmartError.checkVolts(FilteredSamples.FastipVolts.getValue(), FilteredSamples.FastGlowAmps.getValue());
if(bHasHtrData && LVCOK) {
RTC_Store.setCyclicEngaged(true); // for cyclic mode
RTC_Store.setFrostOn(false); // cancel frost mode
// only start if below appropriate temperature threshold, raised for cyclic mode
int denied = checkStartTemp();
if(!checkTemp || !denied) {
// int denied = checkStartTemp();
CDemandManager::eStartCode startCode = CDemandManager::checkStart();
if(startCode == CDemandManager::eStartOK) {
heaterOn();
return 0;
}
else {
return denied;
if(startCode == CDemandManager::eStartSuspend) {
SmartError.inhibit(true); // ensure our suspend does not get immediately cancelled by prior error sitting in system!
DebugPort.printf("CYCLIC MODE: Skipping directly to suspend, deltaT > +%d\r\n", NVstore.getUserSettings().cyclic.Stop+1);
heaterOff(); // over temp - request heater stop
}
}
return startCode;
}
else {
return -3; // LVC
return CDemandManager::eStartLVC; // LVC
}
}
@ -1133,110 +1106,6 @@ void heaterOff()
}
bool reqDemand(uint8_t newDemand, bool save)
{
if(bHasOEMController)
return false;
uint8_t max = DefaultBTCParams.getTemperature_Max();
uint8_t min = DefaultBTCParams.getTemperature_Min();
if(newDemand >= max)
newDemand = max;
if(newDemand <= min)
newDemand = min;
// set and save the demand to NV storage
// note that we now maintain fixed Hz and Thermostat set points seperately
if(getThermostatModeActive()) {
CTimerManager::setWorkingTemperature(newDemand);
}
else {
CTimerManager::setWorkingPumpHz(newDemand);
}
ScreenManager.reqUpdate();
return true;
}
bool reqDemandDelta(int delta)
{
uint8_t newDemand;
if(getThermostatModeActive()) {
newDemand = CTimerManager::getWorkingTemperature() + delta;
}
else {
newDemand = CTimerManager::getWorkingPumpHz() + delta;
}
return reqDemand(newDemand);
}
bool reqThermoToggle()
{
return setThermostatMode(getThermostatModeActive() ? 0 : 1);
}
bool setThermostatMode(uint8_t val)
{
if(bHasOEMController)
return false;
sUserSettings settings = NVstore.getUserSettings();
if(INBOUNDS(val, 0, 1))
settings.useThermostat = val;
NVstore.setUserSettings(settings);
return true;
}
void setDegFMode(bool state)
{
sUserSettings settings = NVstore.getUserSettings();
settings.degF = state ? 0x01 : 0x00;
NVstore.setUserSettings(settings);
}
bool getThermostatModeActive()
{
if(bHasOEMController) {
return getHeaterInfo().isThermostat();
}
else {
return NVstore.getUserSettings().useThermostat != 0;
}
}
bool getExternalThermostatModeActive()
{
#if USE_JTAG == 0
return GPIOin.usesExternalThermostat() && (NVstore.getUserSettings().ThermostatMethod == 3);
#else
//CANNOT USE GPIO WITH JTAG DEBUG
return false;
#endif
}
bool getExternalThermostatOn()
{
#if USE_JTAG == 0
return GPIOin.getState(1);
#else
//CANNOT USE GPIO WITH JTAG DEBUG
return false;
#endif
}
const char* getExternalThermostatHoldTime()
{
#if USE_JTAG == 0
return GPIOin.getExtThermHoldTime();
#else
//CANNOT USE GPIO WITH JTAG DEBUG
return "00:00";
#endif
}
void checkDisplayUpdate()
{
// only update OLED when not processing blue wire
@ -1265,39 +1134,6 @@ void forceBootInit()
RTC_Store.setBootInit();
}
uint8_t getDemandDegC()
{
return CTimerManager::getWorkingTemperature();
}
void setDemandDegC(uint8_t val)
{
uint8_t max = DefaultBTCParams.getTemperature_Max();
uint8_t min = DefaultBTCParams.getTemperature_Min();
BOUNDSLIMIT(val, min, max);
CTimerManager::setWorkingTemperature(val);
}
uint8_t getDemandPump()
{
return CTimerManager::getWorkingPumpHz();
}
float getTemperatureDesired()
{
if(bHasOEMController) {
return getHeaterInfo().getHeaterDemand();
}
else {
if(getThermostatModeActive()) {
return CTimerManager::getWorkingTemperature();
}
else {
return CTimerManager::getWorkingPumpHz(); // timer manager will return pump Hz, as demand value, not real Hz
}
}
}
float getTemperatureSensor(int source)
{
@ -1307,62 +1143,6 @@ float getTemperatureSensor(int source)
}
void setPumpMin(float val)
{
sHeaterTuning tuning = NVstore.getHeaterTuning();
tuning.setPmin(val);
NVstore.setHeaterTuning(tuning);
}
void setPumpMax(float val)
{
sHeaterTuning tuning = NVstore.getHeaterTuning();
tuning.setPmax(val);
NVstore.setHeaterTuning(tuning);
}
void setFanMin(uint16_t cVal)
{
sHeaterTuning tuning = NVstore.getHeaterTuning();
if(INBOUNDS(cVal, 500, 5000))
tuning.Fmin = cVal;
NVstore.setHeaterTuning(tuning);
}
void setFanMax(uint16_t cVal)
{
sHeaterTuning tuning = NVstore.getHeaterTuning();
if(INBOUNDS(cVal, 500, 5000))
tuning.Fmax = cVal;
NVstore.setHeaterTuning(tuning);
}
void setFanSensor(uint8_t cVal)
{
sHeaterTuning tuning = NVstore.getHeaterTuning();
if(INBOUNDS(cVal, 1, 2))
tuning.fanSensor = cVal;
NVstore.setHeaterTuning(tuning);
}
void setSystemVoltage(float val) {
sHeaterTuning tuning = NVstore.getHeaterTuning();
tuning.setSysVoltage(val);
NVstore.setHeaterTuning(tuning);
}
void setGlowDrive(uint8_t val) {
sHeaterTuning tuning = NVstore.getHeaterTuning();
if(INBOUNDS(val, 1, 6))
tuning.glowDrive = val;
NVstore.setHeaterTuning(tuning);
}
void saveNV()
{
NVstore.save();
}
const CProtocolPackage& getHeaterInfo()
{
@ -1874,8 +1654,6 @@ void doStreaming()
KeyPad.update(); // scan keypad - key presses handler via callback functions!
Bluetooth.check(); // check for Bluetooth activity
#if USE_JTAG == 0
//CANNOT USE GPIO WITH JTAG DEBUG
GPIOin.manage();
@ -1883,6 +1661,8 @@ void doStreaming()
GPIOalg.manage();
#endif
Bluetooth.check(); // check for Bluetooth activity
// manage changes in Bluetooth connection status
if(Bluetooth.isConnected()) {
if(!bBTconnected) {

View File

@ -253,7 +253,7 @@ CBluetoothHC05::foldbackDesiredTemp()
StaticJsonBuffer<32> jsonBuffer; // create a JSON buffer on the stack
JsonObject& root = jsonBuffer.createObject(); // create object to add JSON commands to
if(foldbackModerator.addJson("TempDesired", getTemperatureDesired(), root)) {
if(foldbackModerator.addJson("TempDesired", CDemandManager::getDemand(), root)) {
char opStr[32];
root.printTo(opStr);
send(opStr);

View File

@ -70,10 +70,11 @@ CBasicScreen::show()
long tDelta = millis() - _showAbortTime;
if(tDelta < 0) {
switch(_abortreason) {
case -1: strcpy(msg, "Ignored - too warm!"); break;
case -2: strcpy(msg, "Suspended - too warm!"); break;
case -3: strcpy(msg, "Ignored - low voltage!"); break;
case -4: strcpy(msg, "Ignored - fuel empty!"); break;
case CDemandManager::eStartOK: strcpy(msg, "Start OK!"); break;
case CDemandManager::eStartTooWarm: strcpy(msg, "Ignored - too warm!"); break;
case CDemandManager::eStartSuspend: strcpy(msg, "Suspended - too warm!"); break;
case CDemandManager::eStartLVC: strcpy(msg, "Ignored - low voltage!"); break;
case CDemandManager::eStartLowFuel: strcpy(msg, "Ignored - fuel empty!"); break;
}
// centre message at bottom of screen
_printMenuText(_display.xCentre(), _display.height() - _display.textHeight(), msg, false, eCentreJustify);
@ -120,12 +121,12 @@ CBasicScreen::show()
case 0:
// Show current heat demand setting
if(getThermostatModeActive()) {
if(getExternalThermostatModeActive()) {
if(CDemandManager::isThermostat()) {
if(CDemandManager::isExtThermostatMode()) {
sprintf(msg, "External @ %.1fHz", getHeaterInfo().getPump_Fixed());
}
else {
float fTemp = getTemperatureDesired();
float fTemp = CDemandManager::getDegC();
if(NVstore.getUserSettings().degF) {
fTemp = fTemp * 9 / 5 + 32;
sprintf(msg, "Setpoint = %.0f`F", fTemp);
@ -267,7 +268,7 @@ CBasicScreen::keyHandler(uint8_t event)
repeatCount = -1; // prevent double handling
if(NVstore.getUserSettings().menuMode < 2) {
_showModeTime = millis() + 5000;
_nModeSel = getThermostatModeActive() ? 0 : 1;
_nModeSel = CDemandManager::isThermostat() ? 0 : 1;
}
}
}
@ -299,7 +300,7 @@ CBasicScreen::keyHandler(uint8_t event)
if(repeatCount > 3) {
repeatCount = -1;
_abortreason = requestOn();
if(_abortreason) {
if(_abortreason != CDemandManager::eStartOK) {
_showAbortTime = millis() + 5000;
}
}
@ -317,7 +318,7 @@ CBasicScreen::keyHandler(uint8_t event)
// release DOWN key to reduce set demand, provided we are not in mode select
if(NVstore.getUserSettings().menuMode < 2) {
if(event & key_Down) {
if(reqDemandDelta(-1)) {
if(CDemandManager::deltaDemand(-1)) {
_showSetModeTime = millis() + 5000;
_bShowOtherSensors = 0;
_feedbackType = 0;
@ -328,7 +329,7 @@ CBasicScreen::keyHandler(uint8_t event)
}
// release UP key to increase set demand, provided we are not in mode select
if(event & key_Up) {
if(reqDemandDelta(+1)) {
if(CDemandManager::deltaDemand(+1)) {
_showSetModeTime = millis() + 5000;
_bShowOtherSensors = 0;
_feedbackType = 0;
@ -350,8 +351,7 @@ CBasicScreen::keyHandler(uint8_t event)
else {
_showModeTime = millis() + 5000;
_nModeSel = 0;
setThermostatMode(1); // set the new mode
NVstore.save();
CDemandManager::setThermostatMode(1); // set the new mode
}
_ScreenManager.reqUpdate();
}
@ -367,8 +367,7 @@ CBasicScreen::keyHandler(uint8_t event)
else {
_showModeTime = millis() + 5000;
_nModeSel = 1;
setThermostatMode(0); // set the new mode
NVstore.save();
CDemandManager::setThermostatMode(0); // set the new mode
}
_ScreenManager.reqUpdate();
}

View File

@ -21,6 +21,7 @@
#include <stdint.h>
#include "ScreenHeader.h"
#include "../Utility/DemandManager.h"
class C128x64_OLED;
class CScreenManager;
@ -31,7 +32,7 @@ class CBasicScreen : public CScreenHeader
unsigned long _showSetModeTime;
unsigned long _showModeTime;
unsigned long _showAbortTime;
int _abortreason;
CDemandManager::eStartCode _abortreason;
uint8_t _bShowOtherSensors;
uint8_t _feedbackType;
uint8_t _nModeSel;

View File

@ -29,6 +29,7 @@
#include "../Utility/NVStorage.h"
#include "../Utility/FuelGauge.h"
#include "../RTC/RTCStore.h"
#include "../Utility/DemandManager.h"
#define MINIFONT miniFontInfo
@ -98,13 +99,13 @@ CDetailedScreen::show()
float desiredT = 0;
float fPump = 0;
if((runstate && (runstate <= 5)) || (runstate == 9) || _showTarget) { // state 9 = manufactured "heating glow plug"
if(getThermostatModeActive() && !getExternalThermostatModeActive()) {
desiredT = getTemperatureDesired();
if(CDemandManager::isThermostat() && !CDemandManager::isExtThermostatMode()) {
desiredT = CDemandManager::getDemand();
}
else {
fPump = getHeaterInfo().getPump_Fixed();
if(NVstore.getUserSettings().cyclic.isEnabled())
desiredT = getDemandDegC();
desiredT = CDemandManager::getDegC();
}
}
@ -234,9 +235,8 @@ CDetailedScreen::keyHandler(uint8_t event)
if(event & key_Down) {
if(_keyRepeatCount > 1) { // held Down - toggle thermo/fixed mode
_keyRepeatCount = -1; // prevent double handling
if(reqThermoToggle()) {
if(CDemandManager::toggleThermostat()) {
_showTarget = millis() + 3500;
NVstore.save();
}
else _reqOEMWarning();
}
@ -256,11 +256,17 @@ CDetailedScreen::keyHandler(uint8_t event)
if(event & keyReleased) {
if(_keyRepeatCount == 0) { // short Up press - lower target
if(event & key_Up) {
if(reqDemandDelta(+1)) _showTarget = millis() + 3500;
if(CDemandManager::deltaDemand(+1)) {
_showTarget = millis() + 3500;
_ScreenManager.reqUpdate();
}
else _reqOEMWarning();
}
if(event & key_Down) { // short Down press - lower target
if(reqDemandDelta(-1)) _showTarget = millis() + 3500;
if(CDemandManager::deltaDemand(-1)) {
_showTarget = millis() + 3500;
_ScreenManager.reqUpdate();
}
else _reqOEMWarning();
}
if(event & key_Centre) { // short Centre press - show target
@ -333,8 +339,8 @@ CDetailedScreen::showThermometer(float fDesired, float fActual, float fPump)
// draw target setting
// may be suppressed if not in normal start or run state
if((fDesired != 0) || (fPump != 0)) {
if(getThermostatModeActive() && getExternalThermostatModeActive()) {
const char* pTimeStr = getExternalThermostatHoldTime();
if(CDemandManager::isThermostat() && CDemandManager::isExtThermostatMode()) {
const char* pTimeStr = CDemandManager::getExtThermostatHoldTime();
if(pTimeStr) {
CTransientFont AF(_display, &MINIFONT); // temporarily use a mini font
_drawBitmap(X_TARGET_ICON-1, Y_TARGET_ICON+2, ExtThermo2IconInfo); // draw external input #2 icon
@ -343,7 +349,7 @@ CDetailedScreen::showThermometer(float fDesired, float fActual, float fPump)
}
else
_drawBitmap(X_TARGET_ICON-1, Y_TARGET_ICON+2, ExtThermo2IconInfo); // draw external input #2 icon
if(getExternalThermostatOn())
if(CDemandManager::isExtThermostatOn())
_drawBitmap(X_TARGET_ICON-2, Y_TARGET_ICON+10, CloseIconInfo); // draw external input #2 icon
else
_drawBitmap(X_TARGET_ICON-2, Y_TARGET_ICON+10, OpenIconInfo); // draw external input #2 icon

View File

@ -260,9 +260,11 @@ CFuelMixtureScreen::_adjustSetting(int dir)
void
CFuelMixtureScreen::_saveNV()
{
setPumpMin(adjPump[0]);
setPumpMax(adjPump[1]);
setFanMin(adjFan[0]);
setFanMax(adjFan[1]);
saveNV();
sHeaterTuning tuning = NVstore.getHeaterTuning();
tuning.setPmin(adjPump[0]);
tuning.setPmax(adjPump[1]);
tuning.setFmin(adjFan[0]);
tuning.setFmax(adjFan[1]);
NVstore.setHeaterTuning(tuning);
NVstore.save();
}

View File

@ -231,8 +231,10 @@ CHeaterSettingsScreen::_adjust(int dir)
void
CHeaterSettingsScreen::_saveNV()
{
setSystemVoltage(float(_sysVoltage));
setFanSensor(_fanSensor);
setGlowDrive(_glowDrive);
saveNV();
sHeaterTuning tuning = NVstore.getHeaterTuning();
tuning.setSysVoltage(float(_sysVoltage));
tuning.setFanSensor(_fanSensor);
tuning.setGlowDrive(_glowDrive);
NVstore.setHeaterTuning(tuning);
NVstore.save();
}

View File

@ -32,6 +32,7 @@
#include "KeyPad.h"
#include "../Utility/helpers.h"
#include "../Protocol/Protocol.h"
#include "../Utility/NVStorage.h"
CInheritSettingsScreen::CInheritSettingsScreen(C128x64_OLED& display, CScreenManager& mgr) : CPasswordScreen(display, mgr)
@ -122,13 +123,15 @@ CInheritSettingsScreen::keyHandler(uint8_t event)
void
CInheritSettingsScreen::_copySettings()
{
setPumpMin(getHeaterInfo().getPump_Min());
setPumpMax(getHeaterInfo().getPump_Max());
setFanMin(getHeaterInfo().getFan_Min());
setFanMax(getHeaterInfo().getFan_Max());
setFanSensor(getHeaterInfo().getFan_Sensor());
setSystemVoltage(getHeaterInfo().getSystemVoltage());
saveNV();
sHeaterTuning tuning = NVstore.getHeaterTuning();
tuning.setPmin(getHeaterInfo().getPump_Min());
tuning.setPmax(getHeaterInfo().getPump_Max());
tuning.setFmin(getHeaterInfo().getFan_Min());
tuning.setFmax(getHeaterInfo().getFan_Max());
tuning.fanSensor = getHeaterInfo().getFan_Sensor();
tuning.setSysVoltage(getHeaterInfo().getSystemVoltage());
NVstore.setHeaterTuning(tuning);
NVstore.save();
_enableStoringMessage();
_nAdoptSettings = 0; // will cause return to main menu after storing message expires
}

View File

@ -136,7 +136,7 @@ CPrimingScreen::show()
}
else {
// follow actual heater settings
if(getThermostatModeActive()) {
if(CDemandManager::isThermostat()) {
_drawBitmap(loc.xPos, midline, NVstore.getUserSettings().degF ? ThermostatDegFIconInfo : ThermostatDegCIconInfo);
}
else {
@ -243,7 +243,7 @@ CPrimingScreen::keyHandler(uint8_t event)
_colSel = 0;
switch(_paramSel) {
case 1:
_colSel = getThermostatModeActive() ? 0 : 1;
_colSel = CDemandManager::isThermostat() ? 0 : 1;
break;
case 2:
_colSel = NVstore.getUserSettings().degF ? 1 : 0;
@ -270,7 +270,7 @@ CPrimingScreen::keyHandler(uint8_t event)
_colSel = NVstore.getUserSettings().degF ? 1 : 0;
break;
case 1:
_colSel = getThermostatModeActive() ? 0 : 1;
_colSel = CDemandManager::isThermostat() ? 0 : 1;
break;
}
break;
@ -285,19 +285,17 @@ CPrimingScreen::keyHandler(uint8_t event)
switch(_paramSel) {
case 0:
_paramSel = 1;
_colSel = getThermostatModeActive() ? 0 : 1;
_colSel = CDemandManager::isThermostat() ? 0 : 1;
break;
case 1:
_colSel++;
WRAPLIMITS(_colSel, 0, 1);
setThermostatMode(_colSel == 0);
saveNV();
CDemandManager::setThermostatMode(_colSel == 0);
break;
case 2:
_colSel++;
WRAPLIMITS(_colSel, 0, 1);
setDegFMode(_colSel != 0);
saveNV();
CDemandManager::setDegFMode(_colSel != 0);
break;
case 3:
if(_resetConfirm) {
@ -330,14 +328,12 @@ CPrimingScreen::keyHandler(uint8_t event)
case 1:
_colSel--;
WRAPLIMITS(_colSel, 0, 1);
setThermostatMode(_colSel == 0);
saveNV();
CDemandManager::setThermostatMode(_colSel == 0);
break;
case 2:
_colSel--;
WRAPLIMITS(_colSel, 0, 1);
setDegFMode(_colSel != 0);
saveNV();
CDemandManager::setDegFMode(_colSel != 0);
break;
case 3:
_colSel--;

View File

@ -43,16 +43,17 @@ CSmartError::reset()
// we use inhibit when we manually command the heater off during preheat
// otherwise we'll register an ignition fail event
void
CSmartError::inhibit()
CSmartError::inhibit(bool reseterror)
{
_bInhibit = true;
// m_Error = 0;
if(reseterror)
_Error = 0;
}
// accept a fresh heater frame
// inpsect the advertised run state, tracking when and how it transitions out
// of preheat especially.
// abnormal transitions are registered and becoem our smart m_Error
// abnormal transitions are registered and becoem our smart '_Error'
// In addition, the hetaer frame has the ErrState updated to track the
// smart error, providing no heater error exists!
void

View File

@ -28,7 +28,7 @@ class CSmartError {
public:
CSmartError();
void reset();
void inhibit();
void inhibit(bool reseterror=false);
void monitor(const CProtocol& heaterFrame);
void monitor(uint8_t runstate);
int checkVolts(float volts, float plugI, bool throwfault=true); // 0 = OK, 1 = within 0.5V of LVC, 2 = under LVC

View File

@ -22,7 +22,8 @@
#include "TxManage.h"
#include "../Utility/NVStorage.h"
#include "../Utility/helpers.h"
#include "freertos/queue.h"
#include "../Utility/DemandManager.h"
#include "freertos/freertos.h"
//#define DEBUG_THERMOSTAT
@ -181,19 +182,19 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
float tActual = getTemperatureSensor();
int8_t s8Temp = (int8_t)(tActual + 0.5);
m_TxFrame.setTemperature_Actual(s8Temp); // use current temp, for now
m_TxFrame.setHeaterDemand(getDemandDegC());
m_TxFrame.setHeaterDemand(CDemandManager::getDegC());
m_TxFrame.setThermostatModeProtocol(1); // assume using thermostat control for now
if(!getThermostatModeActive()) {
if(!CDemandManager::isThermostat()) {
m_TxFrame.setThermostatModeProtocol(0); // not using any form of thermostat control
m_TxFrame.setHeaterDemand(getDemandPump()); // set fixed Hz demand instead
m_TxFrame.setHeaterDemand(CDemandManager::getPumpHz()); // set fixed Hz demand instead
m_TxFrame.setTemperature_Actual(0); // must force actual to 0 for Hz mode
}
else if(NVstore.getUserSettings().ThermostatMethod) {
uint8_t ThermoMode = NVstore.getUserSettings().ThermostatMethod; // get the METHOD of thermostat control
float Window = NVstore.getUserSettings().ThermostatWindow;
float tCurrent = getTemperatureSensor();
float tDesired = float(getDemandDegC());
float tDesired = float(CDemandManager::getDegC());
float tDelta = tCurrent - tDesired;
float fTemp;
#ifdef DEBUG_THERMOSTAT
@ -203,8 +204,8 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
switch(ThermoMode) {
case 3: // GPIO controlled thermostat mode
if(getExternalThermostatModeActive()) {
if(getExternalThermostatOn()) {
if(CDemandManager::isExtThermostatMode()) {
if(CDemandManager::isExtThermostatOn()) {
s8Temp = m_TxFrame.getTemperature_Max(); // input active (contact closure) - max burn
}
else {
@ -232,11 +233,11 @@ CTxManage::PrepareFrame(const CProtocol& basisFrame, bool isBTCmaster)
s8Temp = (int8_t)(tActual + 0.5); // use rounded actual unless within window
if(fabs(tDelta) < Window) {
// hold at desired if inside window
s8Temp = getDemandDegC();
s8Temp = CDemandManager::getDegC();
}
else if(fabs(tDelta) <= 1.0) {
// force outside if delta is <= 1 but greater than window
s8Temp = getDemandDegC() + ((tDelta > 0) ? 1 : -1);
s8Temp = CDemandManager::getDegC() + ((tDelta > 0) ? 1 : -1);
}
m_TxFrame.setTemperature_Actual(s8Temp);
#ifdef DEBUG_THERMOSTAT

View File

@ -34,6 +34,7 @@
#include "../Utility/NVStorage.h"
#include "../Utility/helpers.h"
#include "../RTC/RTCStore.h"
#include "../Utility/DemandManager.h"
// main array to hold information of which timer is active at any particular minute of the week
// LSBs are used for the timerID + 1
@ -44,8 +45,6 @@ int CTimerManager::_activeTimer = 0;
int CTimerManager::_activeDow = 0;
int CTimerManager::_nextTimer = 0;
int CTimerManager::_nextStart = 0;
uint8_t CTimerManager::_workingTemperature = 22;
uint8_t CTimerManager::_workingPumpHz = 22;
bool CTimerManager::_timerChanged = false;
#define SET_MAPS() { \
@ -271,11 +270,8 @@ CTimerManager::manageTime(int _hour, int _minute, int _dow)
// get timer settings
int ID = (newID & 0xf) - 1;
NVstore.getTimerInfo(ID, timer);
if (timer.temperature) {
_workingTemperature = timer.temperature;
_workingPumpHz = timer.temperature;
}
DebugPort.printf("Start of timer interval, starting heater @ %dC\r\n", _workingTemperature);
CDemandManager::setFromTimer(timer.temperature);
DebugPort.printf("Start of timer interval, starting heater @ %dC\r\n", timer.temperature);
requestOn();
_activeDow = dow; // dow when timer interval start was detected
retval = 1;
@ -285,9 +281,8 @@ CTimerManager::manageTime(int _hour, int _minute, int _dow)
if(!RTC_Store.getFrostOn())
requestOff();
retval = 2;
_workingTemperature = RTC_Store.getDesiredTemp();
_workingPumpHz = RTC_Store.getDesiredPump();
DebugPort.printf("End of timer interval, stopping heater & %dC\r\n", _workingTemperature);
CDemandManager::reload();
DebugPort.printf("End of timer interval, stopping heater @ %dC\r\n", CDemandManager::getDegC());
}
_activeTimer = newID;
}
@ -426,49 +421,3 @@ CTimerManager::createOneShotMap(sTimer& timer, uint16_t* pTimerMap, uint16_t* pT
return false;
}
// Concept of timer working temperature is that when a timer runs, it installs
// the programmed temperature for that timer as the new set point.
// When the timer stops, it reverts to the usual user set temperature.
//
// BUT, if a timer is running, the working temperature is updated with the new demand
// and the user temperature is also changed accordingly.
// The programmed timer temeprature is not altered and wil lrecur in the future when that time runs.
uint8_t
CTimerManager::getWorkingTemperature()
{
return _workingTemperature;
}
void
CTimerManager::setWorkingTemperature(uint8_t newDegC)
{
if(getThermostatModeActive()) {
_workingTemperature = newDegC;
RTC_Store.setDesiredTemp(newDegC);
}
else {
_workingPumpHz = newDegC;
RTC_Store.setDesiredPump(newDegC);
}
}
// Concept of timer working pump Hz is that when a timer runs, it installs
// the programmed temperature for that timer as the new set point.
// That is then converted by the heater into Hz
// When the timer stops, it reverts to the usual user set temperature.
//
// BUT, if a timer is running, the working temperature is updated with the new demand
// and the user temperature is also changed accordingly.
// The programmed timer temeprature is not altered and wil lrecur in the future when that time runs.
uint8_t
CTimerManager::getWorkingPumpHz()
{
return _workingPumpHz;
}
void
CTimerManager::setWorkingPumpHz(uint8_t newDemand)
{
_workingPumpHz = newDemand;
RTC_Store.setDesiredPump(newDemand);
}

View File

@ -52,13 +52,7 @@ public:
static void getTimer(int idx, sTimer& timerInfo);
static int setTimer(sTimer& timerInfo);
static bool hasTimerChanged() { return _timerChanged; };
static uint8_t getWorkingTemperature();
static uint8_t getWorkingPumpHz();
static void setWorkingTemperature(uint8_t newDegC);
static void setWorkingPumpHz(uint8_t newDemand);
private:
static uint8_t _workingTemperature;
static uint8_t _workingPumpHz;
static int _activeTimer;
static int _activeDow;
static int _prevState;

View File

@ -38,6 +38,7 @@
#include "HourMeter.h"
#include "TempSense.h"
#include "BoardDetect.h"
#include "DemandManager.h"
extern CModerator MQTTmoderator;
@ -166,7 +167,7 @@ bool makeJSONString(CModerator& moderator, char* opStr, int len)
}
}
}
bSend |= moderator.addJson("TempDesired", getTemperatureDesired(), root);
bSend |= moderator.addJson("TempDesired", CDemandManager::getDemand(), root);
bSend |= moderator.addJson("TempMode", NVstore.getUserSettings().degF, root);
if(NVstore.getUserSettings().menuMode < 2) {
bSend |= moderator.addJson("TempMin", getHeaterInfo().getTemperature_Min(), root);
@ -176,7 +177,7 @@ bool makeJSONString(CModerator& moderator, char* opStr, int len)
bSend |= moderator.addJson("RunString", getHeaterInfo().getRunStateStr(), root); // verbose it up!
bSend |= moderator.addJson("ErrorState", getHeaterInfo().getErrState(), root );
bSend |= moderator.addJson("ErrorString", getHeaterInfo().getErrStateStrEx(), root); // verbose it up!
bSend |= moderator.addJson("Thermostat", getThermostatModeActive(), root );
bSend |= moderator.addJson("Thermostat", CDemandManager::isThermostat(), root );
bSend |= moderator.addJson("PumpFixed", getHeaterInfo().getPump_Fixed(), root );
bSend |= moderator.addJson("PumpMin", getHeaterInfo().getPump_Min(), root );
bSend |= moderator.addJson("PumpMax", getHeaterInfo().getPump_Max(), root );
@ -214,7 +215,7 @@ bool makeJSONStringEx(CModerator& moderator, char* opStr, int len)
if(stop) stop++; // deliver effective threshold, not internal working value
bSend |= moderator.addJson("ThermostatOvertemp", stop, root);
bSend |= moderator.addJson("ThermostatUndertemp", NVstore.getUserSettings().cyclic.Start, root);
bSend |= moderator.addJson("CyclicTemp", getDemandDegC(), root); // actual pivot point for cyclic mode
bSend |= moderator.addJson("CyclicTemp", CDemandManager::getDegC(), root); // actual pivot point for cyclic mode, follows desired temp in thermostat mode
bSend |= moderator.addJson("CyclicOff", stop, root); // threshold of over temp for cyclic mode
bSend |= moderator.addJson("CyclicOn", NVstore.getUserSettings().cyclic.Start, root); // threshold of under temp for cyclic mode
bSend |= moderator.addJson("FrostOn", NVstore.getUserSettings().FrostOn, root); // temp drops below this, auto start - 0 = disable
@ -306,7 +307,7 @@ bool makeJSONStringGPIO(CModerator& moderator, char* opStr, int len)
bSend |= moderator.addJson("GPmodeAnlg", GPIOalgNames[info.algMode], root);
}
bSend |= moderator.addJson("ExtThermoTmout", (uint32_t)NVstore.getUserSettings().ExtThermoTimeout, root);
const char* stop = getExternalThermostatHoldTime();
const char* stop = CDemandManager::getExtThermostatHoldTime();
if(stop)
bSend |= moderator.addJson("ExtThermoStop", stop, root);
else

View File

@ -0,0 +1,287 @@
/*
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2020 Ray Jones <ray@mrjones.id.au>
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
///////////////////////////////////////////////////////////////////////////
//
// CDemandManager
//
// This provides management of the heater temperature or pump demands
//
///////////////////////////////////////////////////////////////////////////
#include <Arduino.h>
#include "DemandManager.h"
#include "NVStorage.h"
#include "helpers.h"
#include "../RTC/RTCStore.h"
#include "../Protocol/Protocol.h"
// Concept of timer operation:
//
// The volatile variables _setDegC & _setPumpHz are altered when a timer runs.
// The timer installs the programmed timer value as the new set point.
// But if the timer value is zero, no change takes place.
//
// _setPumpHz will match _setDegC.
// _setPumpHz is converted into Hz based using the interploation that is
// performed inside the heater ECU according to Max/min temp & pump settings.
//
// When the timer interval expires, the volatile values revert back to the stored
// RTC memory values.
//
// Exception.
// If a timer is running and the user alters the setting, according to the current
// thermostat state _setDegC or _setPumpHz are updated with the new demand and the
// associated RTC memory value is also updated accordingly.
// When the timer completes, it continues with the same value.
// The programmed timer temperature is not altered by the user adjusting the running
// setting and the same setpoint will recur in the future when that timer runs again.
uint8_t CDemandManager::_setDegC = 22;
uint8_t CDemandManager::_setPumpHz = 22;
uint8_t
CDemandManager::getDegC()
{
return _setDegC;
}
uint8_t
CDemandManager::getPumpHz()
{
return _setPumpHz; // value lies in the typical range of 8-35 - interpolated by heater to real Hz
}
// set a new temperature setpoint, also saving it to non volatile RTC memory (battery backed RAM)
void
CDemandManager::setDegC(uint8_t newDegC)
{
BOUNDSLIMIT(newDegC, NVstore.getHeaterTuning().Tmin, NVstore.getHeaterTuning().Tmax);
_setDegC = newDegC;
RTC_Store.setDesiredTemp(newDegC);
}
// set a new pump setpoint, also saving it to non volatile RTC memory (battery backed RAM)
void
CDemandManager::setPumpHz(uint8_t newDemand)
{
// Pump demands use the same range as temperature demands :-)
BOUNDSLIMIT(newDemand, NVstore.getHeaterTuning().Tmin, NVstore.getHeaterTuning().Tmax);
_setPumpHz = newDemand;
RTC_Store.setDesiredPump(newDemand);
}
// set a transient setpoint for use by prgrammed timer starts
// setpoints only change if timer temperature is actually defined
void
CDemandManager::setFromTimer(uint8_t timerDemand)
{
if(timerDemand) {
_setPumpHz = timerDemand;
_setDegC = timerDemand;
}
}
// revert setpoints to stored RTC memory values
void
CDemandManager::reload()
{
_setDegC = RTC_Store.getDesiredTemp();
_setPumpHz = RTC_Store.getDesiredPump();
}
// test the ambient temperature and check if it satisfies a start condition when running
// in thermostat mode
// If running in Fixed Hz, the start is not denied, but cyclic suspend may be engaged
CDemandManager::eStartCode
CDemandManager::checkStart()
{
// create a deny start temperature margin
int stopDeltaT = 0;
int cyclicstop = NVstore.getUserSettings().cyclic.Stop;
if(cyclicstop) { // cyclic mode enabled
// if cylic mode, raise the margin by the cyclic stop range
stopDeltaT = cyclicstop + 1; // bump up by 1 degree - no point invoking cyclic at 1 deg over!
}
// determine temperature error vs desired thermostat value
float deltaT = getTemperatureSensor() - getDegC();
if(deltaT > stopDeltaT) {
// temperature exceeded the allowed margin
if(cyclicstop) {
// using cyclic mode - suggest immediate cyclic suspend
return eStartSuspend;
}
else {
// only deny start if actually using thermostat mode
if(isThermostat()) {
return eStartTooWarm; // too warm - deny start
}
}
}
return eStartOK; // allow start
}
// generic method adjust the active heter demand.
// thi may be Pump Hz or desired temeperature, dependent upon if thermostat mode is active
bool
CDemandManager::setDemand(uint8_t newDemand)
{
if(hasOEMcontroller())
return false;
// bounds operate over the same range for either mode
BOUNDSLIMIT(newDemand, NVstore.getHeaterTuning().Tmin, NVstore.getHeaterTuning().Tmax);
// set and save the demand to NV storage
// note that we now maintain fixed Hz and Thermostat set points seperately
if(isThermostat()) {
setDegC(newDemand);
}
else {
setPumpHz(newDemand);
}
return true;
}
bool
CDemandManager::deltaDemand(int delta)
{
if(hasOEMcontroller())
return false;
uint8_t newDemand;
if(isThermostat()) {
newDemand = getDegC() + delta;
setDegC(newDemand);
}
else {
newDemand = getPumpHz() + delta;
setPumpHz(newDemand);
}
return true;
}
bool
CDemandManager::toggleThermostat()
{
return setThermostatMode(isThermostat() ? 0 : 1);
}
bool
CDemandManager::setThermostatMode(uint8_t val, bool save)
{
if(hasOEMcontroller())
return false;
sUserSettings settings = NVstore.getUserSettings();
settings.useThermostat = val ? 0x01 : 0x00;
NVstore.setUserSettings(settings);
if(save)
NVstore.save();
return true;
}
// set system to show degF or degC
void
CDemandManager::setDegFMode(bool state)
{
sUserSettings settings = NVstore.getUserSettings();
settings.degF = state ? 0x01 : 0x00;
NVstore.setUserSettings(settings);
NVstore.save();
}
// return tru is using a thermostat mode
bool
CDemandManager::isThermostat()
{
if(hasOEMcontroller()) {
return getHeaterInfo().isThermostat();
}
else {
return NVstore.getUserSettings().useThermostat != 0;
}
}
// generic get demand for Pump Hz or degC, as would be used in the value sent to the heater
uint8_t
CDemandManager::getDemand()
{
if(hasOEMcontroller()) {
return getHeaterInfo().getHeaterDemand();
}
else {
if(isThermostat()) {
return getDegC();
}
else {
return getPumpHz(); // timer manager will return pump Hz, as demand value, not real Hz
}
}
}
// return true if external thermostat mode is active
bool
CDemandManager::isExtThermostatMode()
{
#if USE_JTAG == 0
return GPIOin.usesExternalThermostat() && (NVstore.getUserSettings().ThermostatMethod == 3);
#else
//CANNOT USE GPIO WITH JTAG DEBUG
return false;
#endif
}
// return true if external thermosat is closed
bool
CDemandManager::isExtThermostatOn()
{
#if USE_JTAG == 0
return GPIOin.getState(1);
#else
//CANNOT USE GPIO WITH JTAG DEBUG
return false;
#endif
}
const char*
CDemandManager::getExtThermostatHoldTime()
{
#if USE_JTAG == 0
return GPIOin.getExtThermHoldTime();
#else
//CANNOT USE GPIO WITH JTAG DEBUG
return "00:00";
#endif
}

View File

@ -0,0 +1,70 @@
/*
* This file is part of the "bluetoothheater" distribution
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
*
* Copyright (C) 2020 Ray Jones <ray@mrjones.id.au>
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
///////////////////////////////////////////////////////////////////////////
//
// CDemandManager
//
// This provides management of the heater temperature or pump demands
//
///////////////////////////////////////////////////////////////////////////
#ifndef __DEMANDMANAGER_H__
#define __DEMANDMANAGER_H__
#include <stdint.h>
struct sTimer;
class CDemandManager {
private:
static uint8_t _setDegC;
static uint8_t _setPumpHz;
public:
enum eStartCode { eStartOK=0,
eStartTooWarm=-1,
eStartSuspend=-2,
eStartLVC=-3,
eStartLowFuel=-4
};
static uint8_t getDegC(); // absolute degC value to use
static uint8_t getPumpHz(); // absolute Pump Hz value to use - scaled to fit temp range
static uint8_t getDemand(); // can be pump Hz or degC, depending upon thermostat mode
static void setDegC(uint8_t newDegC); // set and save absolute degC value to use
static void setPumpHz(uint8_t newDemand); // set and save absolute scaled pump Hz value to use
static bool setDemand(uint8_t newDemand); // set and save demand, according to thermostat mode
static bool deltaDemand(int delta); // nudge demand
static void setFromTimer(uint8_t newDemand); // set voltile degC and PumpHz, for timer starts
static void reload(); // reload stored RTC values
static eStartCode checkStart(); // test if start allowed
static bool toggleThermostat();
static bool setThermostatMode(uint8_t val, bool save = true);
static void setDegFMode(bool state);
static bool isThermostat();
static bool isExtThermostatMode();
static bool isExtThermostatOn();
static const char* getExtThermostatHoldTime();
};
#endif //__DEMANDMANAGER_H__

View File

@ -88,6 +88,12 @@ sHeaterTuning::setPmax(float val)
Pmax = cVal;
}
void
sHeaterTuning::setFanSensor(int val)
{
if(INBOUNDS(val, 1, 2))
fanSensor = val;
}
void
sHeaterTuning::setSysVoltage(float fVal)
@ -98,6 +104,13 @@ sHeaterTuning::setSysVoltage(float fVal)
}
}
void
sHeaterTuning::setGlowDrive(uint8_t val) {
if(INBOUNDS(val, 1, 6))
glowDrive = val;
}
float
sHeaterTuning::getLVC() const
{

View File

@ -128,7 +128,11 @@ struct sHeaterTuning : public CESP32_NVStorage {
float getPmax() const;
void setPmin(float val);
void setPmax(float val);
void setFmin(int val) { Fmin = val; };
void setFmax(int val) { Fmax = val; };
void setFanSensor(int val);
void setSysVoltage(float val);
void setGlowDrive(uint8_t val);
float getLVC() const;
};

View File

@ -28,7 +28,7 @@
#include "BTC_JSON.h"
#include "../WiFi/BTCWebServer.h"
#include "FuelGauge.h"
#include "DemandManager.h"
// a class to track the blue wire receive / transmit states
// class CommStates
@ -107,10 +107,11 @@ void DecodeCmd(const char* cmd, String& payload)
{
int val;
if(strcmp("TempDesired", cmd) == 0) {
if( !reqDemand(payload.toInt(), false) ) { // this request is blocked if OEM controller active
if( !CDemandManager::setDemand(payload.toInt()) ) { // this request is blocked if OEM controller active
resetJSONmoderator("TempDesired");
}
}
/*
else if(strcmp("Run", cmd) == 0) {
refreshMQTT();
if(payload == "1") {
@ -128,20 +129,47 @@ void DecodeCmd(const char* cmd, String& payload)
requestOff();
}
}
*/
else if((strcmp("RunState", cmd) == 0) || (strcmp("Run", cmd) == 0)) {
refreshMQTT();
if(payload.toInt()) {
CDemandManager::eStartCode result = requestOn();
switch(result) {
case CDemandManager::eStartOK: sendJSONtext("{\"StartString\":\"\"}"); break;
case CDemandManager::eStartTooWarm: sendJSONtext("{\"StartString\":\"Ambient too warm!\"}"); break;
case CDemandManager::eStartSuspend: sendJSONtext("{\"StartString\":\"Immediate Cyclic suspension!\"}"); break;
case CDemandManager::eStartLVC: sendJSONtext("{\"StartString\":\"Battery below LVC!\"}"); break;
case CDemandManager::eStartLowFuel: sendJSONtext("{\"StartString\":\"Fuel Empty!\"}"); break;
}
}
else {
requestOff();
}
}
else if(strcmp("PumpMin", cmd) == 0) {
setPumpMin(payload.toFloat());
sHeaterTuning tuning = NVstore.getHeaterTuning();
tuning.setPmin(payload.toFloat());
NVstore.setHeaterTuning(tuning);
}
else if(strcmp("PumpMax", cmd) == 0) {
setPumpMax(payload.toFloat());
sHeaterTuning tuning = NVstore.getHeaterTuning();
tuning.setPmax(payload.toFloat());
NVstore.setHeaterTuning(tuning);
}
else if(strcmp("FanMin", cmd) == 0) {
setFanMin(payload.toInt());
sHeaterTuning tuning = NVstore.getHeaterTuning();
if(INBOUNDS(payload.toInt(), 500, 5000))
tuning.setFmin(payload.toInt());
NVstore.setHeaterTuning(tuning);
}
else if(strcmp("FanMax", cmd) == 0) {
setFanMax(payload.toInt());
sHeaterTuning tuning = NVstore.getHeaterTuning();
if(INBOUNDS(payload.toInt(), 500, 5000))
tuning.setFmax(payload.toInt());
NVstore.setHeaterTuning(tuning);
}
else if(strcmp("CyclicTemp", cmd) == 0) {
setDemandDegC(payload.toInt()); // directly set demandDegC
CDemandManager::setDegC(payload.toInt()); // directly set demandDegC
}
else if((strcmp("CyclicOff", cmd) == 0) || (strcmp("ThermostatOvertemp", cmd) == 0)) {
sUserSettings us = NVstore.getUserSettings();
@ -171,7 +199,7 @@ void DecodeCmd(const char* cmd, String& payload)
NVstore.setUserSettings(settings);
}
else if(strcmp("Thermostat", cmd) == 0) {
if(!setThermostatMode(payload.toInt())) { // this request is blocked if OEM controller active
if(!CDemandManager::setThermostatMode(payload.toInt(), false)) { // this request is blocked if OEM controller active
resetJSONmoderator("ThermoStat");
}
}
@ -183,7 +211,7 @@ void DecodeCmd(const char* cmd, String& payload)
}
else if(strcmp("NVsave", cmd) == 0) {
if(payload.toInt() == 8861)
saveNV();
NVstore.save();
}
else if(strcmp("Watchdog", cmd) == 0) {
doJSONwatchdog(payload.toInt());
@ -214,7 +242,9 @@ void DecodeCmd(const char* cmd, String& payload)
refreshMQTT();
}
else if(strcmp("SystemVoltage", cmd) == 0) {
setSystemVoltage(payload.toFloat());
sHeaterTuning tuning = NVstore.getHeaterTuning();
tuning.setSysVoltage(payload.toFloat());
NVstore.setHeaterTuning(tuning);
}
else if(strcmp("TimerDays", cmd) == 0) {
// value encoded as "ID Days,Days"
@ -243,7 +273,9 @@ void DecodeCmd(const char* cmd, String& payload)
resetJSONTimerModerator(payload.toInt());
}
else if(strcmp("FanSensor", cmd) == 0) {
setFanSensor(payload.toInt());
sHeaterTuning tuning = NVstore.getHeaterTuning();
tuning.setFanSensor(payload.toInt());
NVstore.setHeaterTuning(tuning);
}
else if(strcmp("IQuery", cmd) == 0) {
resetJSONIPmoderator(); // force IP params to be sent

View File

@ -24,40 +24,21 @@
#define __BTC_HELPERS_H__
#include "UtilClasses.h"
#include "DemandManager.h"
class CTempSense;
struct sGPIO;
extern void forceBootInit();
extern int requestOn(bool checkTemp=true);
extern CDemandManager::eStartCode requestOn();
extern void requestOff();
extern bool reqDemandDelta(int delta);
extern bool reqDemand(uint8_t newTemp, bool save=true);
extern bool reqThermoToggle();
extern bool setThermostatMode(uint8_t);
extern bool getThermostatModeActive(); // OEM: actual mode from blue wire, BTC: or our NV
extern bool getExternalThermostatModeActive();
extern bool getExternalThermostatOn();
extern const char* getExternalThermostatHoldTime();
extern void reqPumpPrime(bool on);
extern float getTemperatureDesired(); // OEM: the advertised value, BTC our setpoint
extern uint8_t getDemandDegC();
extern void setDemandDegC(uint8_t val);
extern uint8_t getDemandPump();
extern float getTemperatureSensor(int source = 0);
extern void setPumpMin(float);
extern void setPumpMax(float);
extern void setFanMin(uint16_t);
extern void setFanMax(uint16_t);
extern void setFanSensor(uint8_t cVal);
extern void setDateTime(const char* newTime);
extern void setDate(const char* newTime);
extern void setTime(const char* newTime);
extern void setGlowDrive(uint8_t val);
extern void saveNV();
extern void setSystemVoltage(float val);
extern void interpretJsonCommand(char* pLine);
extern void resetWebModerator();
extern void resetFuelGauge();
@ -81,7 +62,6 @@ extern void checkFOTA();
extern void setUploadSize(long val);
extern void getGPIOinfo(sGPIO& info);
extern void simulateGPIOin(uint8_t newKey);
extern void setDegFMode(bool state);
extern float getBatteryVoltage(bool fast);
extern float getGlowVolts();
extern float getGlowCurrent();

View File

@ -38,46 +38,29 @@
#include "../Protocol/Protocol.h"
#include "../Utility/BTC_JSON.h"
#include "../Utility/TempSense.h"
#include "freertos/queue.h"
#include "../Utility/DemandManager.h"
#include "freertos/freertos.h"
extern void DecodeCmd(const char* cmd, String& payload);
#define USE_RTOS_MQTTTIMER
//#define USE_LOCAL_MQTTSTRINGS
//#define MQTT_DBG_RAWBYTES
//IPAddress testMQTTserver(5, 196, 95, 208); // test.mosquito.org
//IPAddress testMQTTserver(18, 194, 98, 249); // broker.hivemq.com
AsyncMqttClient MQTTclient;
char topicnameJSONin[128];
char topicnameCmd[128];
CModerator MQTTmoderator; // for basic MQTT interface
CModerator BasicMQTTmoderator; // for basic MQTT interface
unsigned long MQTTrestart = 0;
char statusTopic[128];
TimerHandle_t mqttReconnectTimer = NULL;
SemaphoreHandle_t mqttSemaphore = NULL;
void subscribe(const char* topic);
#ifdef USE_LOCAL_MQTTSTRINGS
char mqttHost[128];
char mqttUser[32];
char mqttPass[32];
#endif
char statusTopic[128];
#ifdef USE_RTOS_MQTTTIMER
TimerHandle_t mqttReconnectTimer = NULL;
QueueHandle_t mqttQueue = NULL;
#else
unsigned long mqttReconnect = 0;
#endif
void connectToMqtt() {
#ifdef USE_RTOS_MQTTTIMER
xTimerStop(mqttReconnectTimer, 0);
#else
mqttReconnect = 0;
#endif
if(!MQTTclient.connected()) {
DebugPort.println("MQTT: Connecting...");
if(NVstore.getMQTTinfo().enabled) {
@ -86,24 +69,16 @@ void connectToMqtt() {
}
}
#ifdef USE_RTOS_MQTTTIMER
// timer callbacks are called from ISRL
// this code MUST run from IRAM, and then safest to pass the event down thru a queue to user code
// ALL callback code MUST run from IRAM, safest to pass the event down thru a queue to user code
void IRAM_ATTR callbackMQTTreconnect() {
BaseType_t awoken;
int flag = 1;
xQueueSendFromISR(mqttQueue, &flag, &awoken);
xSemaphoreGiveFromISR(mqttSemaphore, &awoken);
}
#endif
void onMqttConnect(bool sessionPresent)
{
#ifdef USE_RTOS_MQTTTIMER
xTimerStop(mqttReconnectTimer, 0);
#else
mqttReconnect = 0;
#endif
DebugPort.println("MQTT: Connected to broker.");
// DebugPort.printf("Session present: %d\r\n", sessionPresent);
@ -137,12 +112,6 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
{
// handle message arrived
DebugPort.printf("MQTT: onMessage %s ", topic);
#ifdef MQTT_DBG_RAWBYTES
for(int i=0; i<len; i++) {
DebugPort.printf("0x%02X ", payload[i]);
}
DebugPort.println();
#endif
// string may not neccesarily be null terminated, make sure it is
char szPayload[1024];
int maxlen = sizeof(szPayload)-1;
@ -162,7 +131,7 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
}
else if(strcmp(topic, statusTopic) == 0) { // check if incoming topic is our general status
if(strcmp(szPayload, "1") == 0) {
// MQTTmoderator.reset();
// BasicMQTTmoderator.reset();
MQTTclient.publish(statusTopic, NVstore.getMQTTinfo().qos, true, "online");
}
}
@ -184,11 +153,7 @@ void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
if (WiFi.isConnected()) {
if(NVstore.getMQTTinfo().enabled) {
#ifdef USE_RTOS_MQTTTIMER
xTimerStart(mqttReconnectTimer, 0);
#else
mqttReconnect = millis() + 5000;
#endif
}
}
}
@ -201,16 +166,12 @@ void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
bool mqttInit()
{
#ifdef USE_RTOS_MQTTTIMER
#ifndef BLOCK_MQTT_RECON
if(mqttReconnectTimer==NULL)
// mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(20000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(20000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(callbackMQTTreconnect));
if(mqttQueue == NULL)
mqttQueue = xQueueCreate(10, 4);
#endif
#else
mqttReconnect = 0;
if(mqttSemaphore == NULL) {
mqttSemaphore = xSemaphoreCreateBinary();
}
#endif
MQTTrestart = 0;
@ -229,25 +190,13 @@ bool mqttInit()
const sMQTTparams params = NVstore.getMQTTinfo();
if(params.enabled) {
#ifdef USE_LOCAL_MQTTSTRINGS
strncpy(mqttHost, params.host, 127);
strncpy(mqttUser, params.username, 31);
strncpy(mqttPass, params.password, 31);
mqttHost[127] = 0;
mqttUser[31] = 0;
mqttPass[31] = 0;
DebugPort.printf("MQTT: setting broker to %s:%d\r\n", mqttHost, params.port);
DebugPort.printf("MQTT: %s/%s\r\n", mqttUser, mqttPass);
MQTTclient.setServer(mqttHost, params.port);
MQTTclient.setCredentials(mqttUser, mqttPass);
#else
// the client only stores a pointer - this must not be a volatile memory location!
// - NO STACK vars!!!
DebugPort.printf("MQTT: setting broker to %s:%d\r\n", NVstore.getMQTTinfo().host, NVstore.getMQTTinfo().port);
MQTTclient.setServer(NVstore.getMQTTinfo().host, NVstore.getMQTTinfo().port);
DebugPort.printf("MQTT: %s/%s\r\n", NVstore.getMQTTinfo().username, NVstore.getMQTTinfo().password);
MQTTclient.setCredentials(NVstore.getMQTTinfo().username, NVstore.getMQTTinfo().password);
#endif
static bool setCallbacks = false;
// callbacks should only be added once (vector of callbacks in client!)
if(!setCallbacks) {
@ -275,18 +224,6 @@ bool mqttPublishJSON(const char* str)
return false;
}
/*void kickMQTT() {
if (WiFi.isConnected()) {
if(NVstore.getMQTTinfo().enabled) {
#ifdef USE_RTOS_MQTTTIMER
xTimerStart(mqttReconnectTimer, 0);
#else
mqttReconnect = millis() + 5000;
#endif
}
}
}*/
void doMQTT()
{
// manage restart of MQTT
@ -300,32 +237,17 @@ void doMQTT()
// most MQTT is managed via callbacks!!!
if(NVstore.getMQTTinfo().enabled) {
#ifdef USE_RTOS_MQTTTIMER
int flag;
if(xQueueReceive(mqttQueue, &flag, 0)) {
DebugPort.println("MQTT connect request via queue");
// test if MQTT start timeout has expired
if(xSemaphoreTake(mqttSemaphore, 0)) {
DebugPort.println("MQTT connect request via semaphore");
connectToMqtt();
}
#else
if(mqttReconnect) {
long tDelta = millis() - mqttReconnect;
if(tDelta > 0) {
mqttReconnect = 0;
connectToMqtt();
}
}
#endif
#ifdef USE_RTOS_MQTTTIMER
#ifndef BLOCK_MQTT_RECON
if (!MQTTclient.connected() && WiFi.isConnected() && !xTimerIsTimerActive(mqttReconnectTimer)) {
DebugPort.println("Starting MQTT timer");
xTimerStart(mqttReconnectTimer, 0);
}
#endif
#else
if (!MQTTclient.connected() && WiFi.isConnected() && mqttReconnect==0) {
mqttReconnect = millis() + 5000;
DebugPort.println("Starting MQTT timer");
xTimerStart(mqttReconnectTimer, 0);
}
#endif
@ -341,7 +263,7 @@ bool isMQTTconnected() {
void pubTopic(const char* name, int value)
{
if(MQTTclient.connected()) {
if(MQTTmoderator.shouldSend(name, value)) {
if(BasicMQTTmoderator.shouldSend(name, value)) {
const sMQTTparams params = NVstore.getMQTTinfo();
char topic[128];
sprintf(topic, "%s/sts/%s", getTopicPrefix(), name);
@ -355,7 +277,7 @@ void pubTopic(const char* name, int value)
void pubTopic(const char* name, float value)
{
if(MQTTclient.connected()) {
if(MQTTmoderator.shouldSend(name, value)) {
if(BasicMQTTmoderator.shouldSend(name, value)) {
const sMQTTparams params = NVstore.getMQTTinfo();
char topic[128];
sprintf(topic, "%s/sts/%s", getTopicPrefix(), name);
@ -369,7 +291,7 @@ void pubTopic(const char* name, float value)
void pubTopic(const char* name, const char* payload)
{
if(MQTTclient.connected()) {
if(MQTTmoderator.shouldSend(name, payload)) {
if(BasicMQTTmoderator.shouldSend(name, payload)) {
const sMQTTparams params = NVstore.getMQTTinfo();
char topic[128];
sprintf(topic, "%s/sts/%s", getTopicPrefix(), name);
@ -415,11 +337,11 @@ void updateMQTT()
pubTopic("Temp4Current", "n/a");
}
}
pubTopic("TempDesired", getTemperatureDesired());
pubTopic("TempDesired", CDemandManager::getDemand());
pubTopic("TempBody", getHeaterInfo().getTemperature_HeatExchg());
pubTopic("ErrorState", getHeaterInfo().getErrState());
pubTopic("ErrorString", getHeaterInfo().getErrStateStrEx()); // verbose it up!
pubTopic("Thermostat", getThermostatModeActive());
pubTopic("Thermostat", CDemandManager::isThermostat());
pubTopic("PumpFixed", getHeaterInfo().getPump_Fixed() );
pubTopic("PumpActual", getHeaterInfo().getPump_Actual());
pubTopic("FanRPM", getFanSpeed());
@ -433,7 +355,7 @@ void updateMQTT()
void refreshMQTT()
{
MQTTmoderator.reset();
BasicMQTTmoderator.reset();
}
void subscribe(const char* topic)

View File

@ -29,7 +29,6 @@ bool mqttInit();
void doMQTT();
bool mqttPublishJSON(const char* str);
void connectToMqtt();
//void kickMQTT();
bool isMQTTconnected();
const char* getTopicPrefix();