Termostat refactoring

All Sensors (1-w, DHT, HDC, CCS):unified transfer measurements
to mqtt/local items
This commit is contained in:
2021-03-11 15:08:43 +03:00
parent 8b0ba44445
commit a444ef9b31
22 changed files with 29835 additions and 29580 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -473,12 +473,25 @@ void Input::dht22Poll() {
float temp = dht.readTemperature(); float temp = dht.readTemperature();
float humidity = dht.readHumidity(); float humidity = dht.readHumidity();
#endif #endif
debugSerial << F("IN:") << pin << F(" DHT22 type. T=") << temp << F("°C H=") << humidity << F("%")<<endl;
// New tyle unified activities
aJsonObject *actT = aJson.getObjectItem(inputObj, "temp");
aJsonObject *actH = aJson.getObjectItem(inputObj, "hum");
executeCommand(actT,-1,itemCmd(temp));
executeCommand(actH,-1,itemCmd(humidity));
//Legacy action conf - TODO - remove in further releases
aJsonObject *emit = aJson.getObjectItem(inputObj, "emit"); aJsonObject *emit = aJson.getObjectItem(inputObj, "emit");
aJsonObject *item = aJson.getObjectItem(inputObj, "item");
if (item && item->type == aJson_String) thermoSetCurTemp(item->valuestring, temp); //aJsonObject *item = aJson.getObjectItem(inputObj, "item");
debugSerial << F("IN:") << pin << F(" DHT22 type. T=") << temp << F("°C H=") << humidity << F("%")<<endl; //if (item && item->type == aJson_String) thermoSetCurTemp(item->valuestring, temp);
if (emit && emit->type == aJson_String && temp && humidity && temp == temp && humidity == humidity) { if (emit && emit->type == aJson_String && temp && humidity && temp == temp && humidity == humidity) {
char addrstr[MQTT_TOPIC_LENGTH] = ""; char addrstr[MQTT_TOPIC_LENGTH] = "";
#ifdef WITH_DOMOTICZ #ifdef WITH_DOMOTICZ
if(getIdxField()){ if(getIdxField()){
publishDataToDomoticz(DHT_POLL_DELAY_DEFAULT, emit, "{\"idx\":%s,\"svalue\":\"%.1f;%.0f;0\"}", getIdxField(), temp, humidity); publishDataToDomoticz(DHT_POLL_DELAY_DEFAULT, emit, "{\"idx\":%s,\"svalue\":\"%.1f;%.0f;0\"}", getIdxField(), temp, humidity);
@@ -498,99 +511,10 @@ void Input::dht22Poll() {
if (mqttClient.connected() && !ethernetIdleCount) if (mqttClient.connected() && !ethernetIdleCount)
mqttClient.publish(addrstr, valstr); mqttClient.publish(addrstr, valstr);
//setNextPollTime(millis() + DHT_POLL_DELAY_DEFAULT);
// debugSerial << F(" NextPollMillis=") << nextPollTime() << endl;
} }
//else
// setNextPollTime(millis() + DHT_POLL_DELAY_DEFAULT / 3);
setNextPollTime(millis()); setNextPollTime(millis());
} }
#endif #endif
/*
bool Input::executeCommand(aJsonObject* cmd, int8_t toggle, char* defCmd)
{
if (!cmd) return false;
switch (cmd->type)
{
case aJson_String: //legacy - no action
break;
case aJson_Array: //array - recursive iterate
{
configLocked++;
aJsonObject * command = cmd->child;
while (command)
{
executeCommand(command,toggle,defCmd);
command = command->next;
}
configLocked--;
}
break;
case aJson_Object:
{
aJsonObject *item = aJson.getObjectItem(cmd, "item");
aJsonObject *icmd = aJson.getObjectItem(cmd, "icmd");
aJsonObject *irev = aJson.getObjectItem(cmd, "irev");
aJsonObject *ecmd = aJson.getObjectItem(cmd, "ecmd");
aJsonObject *erev = aJson.getObjectItem(cmd, "erev");
aJsonObject *emit = aJson.getObjectItem(cmd, "emit");
char * itemCommand;
if (irev && toggle && irev->type == aJson_String) itemCommand = irev->valuestring;
else if(icmd && icmd->type == aJson_String) itemCommand = icmd->valuestring;
else itemCommand = defCmd;
char * emitCommand;
if (erev && toggle && erev->type == aJson_String) emitCommand = erev->valuestring;
else if(ecmd && ecmd->type == aJson_String) emitCommand = ecmd->valuestring;
else emitCommand = defCmd;
debugSerial << F("IN:") << (pin) << F(" : ") <<endl;
if (item) debugSerial << item->valuestring<< F(" -> ")<<itemCommand<<endl;
if (emit) debugSerial << emit->valuestring<< F(" -> ")<<emitCommand<<endl;
if (emit && emitCommand && emit->type == aJson_String) {
// *
TODO implement
#ifdef WITH_DOMOTICZ
if (getIdxField())
{ (newValue) ? publishDataToDomoticz(0, emit, "{\"command\":\"switchlight\",\"idx\":%s,\"switchcmd\":\"On\"}",
: publishDataToDomoticz(0,emit,"{\"command\":\"switchlight\",\"idx\":%s,\"switchcmd\":\"Off\"}",getIdxField()); getIdxField())
: publishDataToDomoticz(0, emit,
"{\"command\":\"switchlight\",\"idx\":%s,\"switchcmd\":\"Off\"}",
getIdxField());
} else
#endif
* //
{
char addrstr[MQTT_TOPIC_LENGTH];
strncpy(addrstr,emit->valuestring,sizeof(addrstr));
if (mqttClient.connected() && !ethernetIdleCount)
{
if (!strchr(addrstr,'/')) setTopic(addrstr,sizeof(addrstr),T_OUT,emit->valuestring);
mqttClient.publish(addrstr, emitCommand , true);
}
}
} // emit
if (item && itemCommand && item->type == aJson_String) {
//debugSerial <<F("Controlled item:")<< item->valuestring <<endl;
Item it(item->valuestring);
if (it.isValid()) it.Ctrl(itemCommand, true);
}
return true;
}
default:
return false;
} //switch type
return false;
}
*/
// TODO Polling via timed interrupt with CHECK_INTERRUPT cause // TODO Polling via timed interrupt with CHECK_INTERRUPT cause
bool Input::changeState(uint8_t newState, short cause) bool Input::changeState(uint8_t newState, short cause)
@@ -1032,6 +956,12 @@ if (!strchr(addrstr,'/')) setTopic(addrstr,sizeof(addrstr),T_OUT,emit->valuestri
void Input::onAnalogChanged(float newValue) { void Input::onAnalogChanged(float newValue) {
debugSerial << F("IN:") << (pin) << F("=") << newValue << endl; debugSerial << F("IN:") << (pin) << F("=") << newValue << endl;
// New tyle unified activities
aJsonObject *act = aJson.getObjectItem(inputObj, "act");
executeCommand(act,-1,itemCmd(newValue));
// Legacy
aJsonObject *item = aJson.getObjectItem(inputObj, "item"); aJsonObject *item = aJson.getObjectItem(inputObj, "item");
aJsonObject *emit = aJson.getObjectItem(inputObj, "emit"); aJsonObject *emit = aJson.getObjectItem(inputObj, "emit");

View File

@@ -747,7 +747,7 @@ int Item::Ctrl(itemCmd cmd, char* subItem)
break; break;
case S_VAL: case S_VAL:
st.assignFrom(cmd); st=cmd;
break; break;
case S_SAT: case S_SAT:
@@ -899,7 +899,24 @@ switch (itemType) {
} }
case CH_THERMO: case CH_THERMO:
///thermoSet(name,cmd,Par1); all activities done - update temp & cmd switch (suffixCode)
{
case S_VAL:
//S_NOTFOUND:
{
thermostatStore tStore;
tStore.asint=getExt();
if (!tStore.timestamp16) mqttClient.publish("/alarmoff/snsr", itemArr->name);
tStore.tempX100=st.getFloat()*100.; //Save measurement
tStore.timestamp16=millisNZ(8) & 0xFFFF; //And timestamp
debugSerial<<F(" T:")<<F(":")<<tStore.tempX100<<F(" TS:")<<tStore.timestamp16<<endl;
setExt(tStore.asint);
}
break;
case S_SET:
st.saveItem(this);
break;
}
break; break;
@@ -941,8 +958,8 @@ return 1;
void printActiveStatus(bool active) void printActiveStatus(bool active)
{ {
if (active) debugSerial<<F(" active\n"); if (active) debugSerial<<F(" active ");
else debugSerial<<F(" inactive\n"); else debugSerial<<F(" inactive ");
} }
int Item::isActive() { int Item::isActive() {
@@ -996,13 +1013,13 @@ int Item::isActive() {
Item it(i->valuestring); Item it(i->valuestring);
if (it.isValid() && it.isActive()>0) { if (it.isValid() && it.isActive()>0) {
debugSerial<<F(" active\n"); printActiveStatus(true);
return 1; return 1;
} }
} }
i = i->next; i = i->next;
} //while } //while
debugSerial<<F(" inactive\n"); printActiveStatus(false);
return 0; return 0;
} //if } //if
break; break;
@@ -1033,40 +1050,6 @@ int Item::isActive() {
} }
/* /*
short thermoSet(char * name, short cmd, short t)
{
if (items)
{
aJsonObject *item= aJson.getObjectItem(items, name);
if (item && (item->type==aJson_Array) && (aJson.getArrayItem(item, I_TYPE)->valueint==CH_THERMO))
{
for (int i=aJson.getArraySize(item);i<4;i++) aJson.addItemToArray(item,aJson.createItem(int(0))); //Enlarge item to 4 elements
if (!cmd) aJson.getArrayItem(item, I_VAL)->valueint=t;
aJson.getArrayItem(item, I_CMD)->valueint=cmd;
}
}
}
void PooledItem::Idle()
{
if (PoolingInterval)
{
Pool();
next=millis()+PoolingInterval;
}
};
addr 10d addr 10d
Снять аварию 42001 (2001=7d1) =>4 Снять аварию 42001 (2001=7d1) =>4

View File

@@ -156,3 +156,13 @@ class Item
int defaultSuffixCode; int defaultSuffixCode;
}; };
typedef union
{
struct
{
int16_t tempX100;
uint16_t timestamp16;
};
int32_t asint;
} thermostatStore;

View File

@@ -342,8 +342,14 @@ itemCmd itemCmd::assignFrom(itemCmd from)
param.v=map(from.param.v,0,255,0,100); param.v=map(from.param.v,0,255,0,100);
break; break;
case ST_FLOAT: case ST_FLOAT:
param.v = (int) from.param.asfloat; if (cmd.itemArgType == ST_HSV)
break; param.v = constrain(from.param.asfloat,0,100);
else
{
param=from.param;
cmd.itemArgType=from.cmd.itemArgType;
}
break;
default: default:
debugSerial<<F("Wrong Assignment ")<<from.cmd.itemArgType<<F("->")<<cmd.itemArgType<<endl; debugSerial<<F("Wrong Assignment ")<<from.cmd.itemArgType<<F("->")<<cmd.itemArgType<<endl;
} }
@@ -378,6 +384,15 @@ itemCmd itemCmd::assignFrom(itemCmd from)
case ST_PERCENTS255: case ST_PERCENTS255:
param.v=from.param.v; param.v=from.param.v;
break; break;
case ST_FLOAT:
if (cmd.itemArgType == ST_HSV255)
param.v = constrain(map(from.param.asfloat,0,100,0,255),0,255);
else
{
param=from.param;
cmd.itemArgType=from.cmd.itemArgType;
}
break;
default: default:
debugSerial<<F("Wrong Assignment ")<<from.cmd.itemArgType<<F("->")<<cmd.itemArgType<<endl; debugSerial<<F("Wrong Assignment ")<<from.cmd.itemArgType<<F("->")<<cmd.itemArgType<<endl;
} }
@@ -420,6 +435,13 @@ itemCmd itemCmd::assignFrom(itemCmd from)
cmd.itemArgType=from.cmd.itemArgType; cmd.itemArgType=from.cmd.itemArgType;
param.asfloat=from.param.asfloat; param.asfloat=from.param.asfloat;
break; break;
case ST_HSV255:
case ST_HSV:
case ST_RGB:
case ST_RGBW:
cmd.itemArgType=from.cmd.itemArgType;
param=from.param;
break;
default: default:
debugSerial<<F("Wrong Assignment ")<<from.cmd.itemArgType<<F("->")<<cmd.itemArgType<<endl; debugSerial<<F("Wrong Assignment ")<<from.cmd.itemArgType<<F("->")<<cmd.itemArgType<<endl;
} }
@@ -605,7 +627,7 @@ long int itemCmd::getInt()
case ST_UINT32: case ST_UINT32:
case ST_RGB: case ST_RGB:
case ST_RGBW: case ST_RGBW:
case ST_TENS:
return param.aslong; return param.aslong;
case ST_PERCENTS: case ST_PERCENTS:
@@ -618,6 +640,9 @@ long int itemCmd::getInt()
case ST_FLOAT_CELSIUS: case ST_FLOAT_CELSIUS:
case ST_FLOAT_FARENHEIT: case ST_FLOAT_FARENHEIT:
return param.asfloat; return param.asfloat;
case ST_TENS:
return param.aslong/10;
default: default:
@@ -679,6 +704,11 @@ short itemCmd::getPercents(bool inverse)
if (inverse) return param.asfloat; if (inverse) return param.asfloat;
else return 100-param.asfloat; else return 100-param.asfloat;
case ST_TENS:
if (inverse) return param.asInt32/10;
else return 100-param.asInt32/10;
default: default:
return 0; return 0;
} }
@@ -690,17 +720,22 @@ short itemCmd::getPercents255(bool inverse)
case ST_PERCENTS: case ST_PERCENTS:
case ST_HSV: case ST_HSV:
if (inverse) return map(param.v,0,100,255,0); if (inverse) return map(param.v,0,100,255,0);
else return map(param.v,0,100,0,255); else return map(param.v,0,100,0,255);
case ST_PERCENTS255: case ST_PERCENTS255:
case ST_HSV255: case ST_HSV255:
if (inverse) return 255-param.v; else return param.v; if (inverse) return 255-param.v; else return param.v;
case ST_FLOAT: case ST_FLOAT:
if (inverse) return map(param.asfloat,0,100,255,0); if (inverse) return map(param.asfloat,0,100,255,0);
else return map(param.asfloat,0,100,0,255); else return map(param.asfloat,0,100,0,255);
case ST_TENS:
if (inverse) return map(param.asInt32,0,1000,255,0);
else return map(param.asInt32,0,1000,0,255);
default: default:
return 0; return 0;
} }

View File

@@ -2097,6 +2097,90 @@ configLocked--;
}//if }//if
} }
void thermoRelay(int pin, bool on)
{
int thermoPin = abs(pin);
pinMode(thermoPin, OUTPUT);
if (on)
{
digitalWrite(thermoPin, (pin<0)?LOW:HIGH);
debugSerial<<F(" ON")<<endl;
}
else
{
digitalWrite(thermoPin, (pin<0)?HIGH:LOW);
debugSerial<<F(" OFF")<<endl;
}
}
void thermoLoop(void) {
if (!isTimeOver(timerThermostatCheck,millis(),THERMOSTAT_CHECK_PERIOD))
return;
if (!items) return;
bool thermostatCheckPrinted = false;
configLocked++;
for (aJsonObject *thermoItem = items->child; thermoItem; thermoItem = thermoItem->next) {
if ((thermoItem->type == aJson_Array) && (aJson.getArrayItem(thermoItem, I_TYPE)->valueint == CH_THERMO))
{
Item thermostat(thermoItem); //Init Item
if (!thermostat.isValid()) continue;
itemCmd thermostatCmd(&thermostat); // Got itemCmd
thermostatStore tStore;
tStore.asint=thermostat.getExt();
int thermoPin = thermostat.getArg(0);
float thermoSetting = thermostatCmd.getFloat();
int thermoStateCommand = thermostat.getCmd();
float curTemp = (float) tStore.tempX100/100.;
bool active = thermostat.isActive();
debugSerial << F(" Set:") << thermoSetting << F(" Cur:") << curTemp
<< F(" cmd:") << thermoStateCommand;
if (tStore.timestamp16) //Valid temperature
{
if (isTimeOver(tStore.timestamp16,millisNZ(8) & 0xFFFF,PERIOD_THERMOSTAT_FAILED,0xFFFF))
{
errorSerial<<thermoItem->name<<F(" Alarm Expired\n");
mqttClient.publish("/alarm/snsr", thermoItem->name);
tStore.timestamp16=0; //Stop termostat
thermostat.setExt(tStore.asint);
thermoRelay(thermoPin,false);
}
else
{ // Not expired yet
if (curTemp > THERMO_OVERHEAT_CELSIUS) mqttClient.publish("/alarm/ovrht", thermoItem->name);
if (!active) thermoRelay(thermoPin,false);//OFF
else if (curTemp < thermoSetting - THERMO_GIST_CELSIUS) thermoRelay(thermoPin,true);//ON
else if (curTemp >= thermoSetting) thermoRelay(thermoPin,false);//OFF
else debugSerial<<F(" -target zone-")<<endl; // Nothing to do
}
}
else debugSerial<<F(" Expired\n");
thermostatCheckPrinted = true;
} //item is termostat
} //for
configLocked--;
timerThermostatCheck = millis();// + THERMOSTAT_CHECK_PERIOD;
publishStat();
#ifndef DISABLE_FREERAM_PRINT
(thermostatCheckPrinted) ? debugSerial<<F("\nRAM=")<<freeRam()<<" " : debugSerial<<F(" ")<<freeRam()<<F(" ");
#endif
}
/*
bool isThermostatWithMinArraySize(aJsonObject *item, int minimalArraySize) { bool isThermostatWithMinArraySize(aJsonObject *item, int minimalArraySize) {
return (item->type == aJson_Array) && (aJson.getArrayItem(item, I_TYPE)->valueint == CH_THERMO) && return (item->type == aJson_Array) && (aJson.getArrayItem(item, I_TYPE)->valueint == CH_THERMO) &&
(aJson.getArraySize(item) >= minimalArraySize); (aJson.getArraySize(item) >= minimalArraySize);
@@ -2115,6 +2199,9 @@ bool thermoDisabledOrDisconnected(aJsonObject *thermoExtensionArray, int thermoS
} }
//TODO: refactoring //TODO: refactoring
void thermoLoop(void) { void thermoLoop(void) {
// if (millis() < timerThermostatCheck) // if (millis() < timerThermostatCheck)
@@ -2215,3 +2302,4 @@ short thermoSetCurTemp(char *name, float t) {
} }
return 1;} return 1;}
return 0;} return 0;}
*/

View File

@@ -113,6 +113,12 @@ if (reg!=0xff)
M5.Lcd.print("%\n"); M5.Lcd.print("%\n");
#endif #endif
// New tyle unified activities
aJsonObject *actT = aJson.getObjectItem(in->inputObj, "temp");
aJsonObject *actH = aJson.getObjectItem(in->inputObj, "hum");
executeCommand(actT,-1,itemCmd(t));
executeCommand(actH,-1,itemCmd(h));
publish(t,"/T"); publish(t,"/T");
publish(h,"/H"); publish(h,"/H");
if (CCS811ready) ccs811.setEnvironmentalData(h,t); if (CCS811ready) ccs811.setEnvironmentalData(h,t);
@@ -165,6 +171,13 @@ int in_ccs811::Poll(short cause)
if (co2<10000.) //Spontaneous calculation error suppress if (co2<10000.) //Spontaneous calculation error suppress
{ {
// New tyle unified activities
aJsonObject *actCO2 = aJson.getObjectItem(in->inputObj, "co2");
aJsonObject *actTVOC = aJson.getObjectItem(in->inputObj, "tvoc");
executeCommand(actCO2,-1,itemCmd(co2));
executeCommand(actTVOC,-1,itemCmd(tvoc));
publish(co2,"/CO2"); publish(co2,"/CO2");
publish(tvoc,"/TVOC"); publish(tvoc,"/TVOC");
publish(ccs811Baseline,"/base");} publish(ccs811Baseline,"/base");}

View File

@@ -37,10 +37,11 @@
#define TIMEOUT_REINIT 5000UL #define TIMEOUT_REINIT 5000UL
#define TIMEOUT_RETAIN 5000UL #define TIMEOUT_RETAIN 5000UL
#define INTERVAL_1W 5000UL #define INTERVAL_1W 5000UL
#define PERIOD_THERMOSTAT_FAILED (300 * 1000UL)>>8
#define T_ATTEMPTS 200 //#define T_ATTEMPTS 200
#define IET_TEMP 0 //#define IET_TEMP 0
#define IET_ATTEMPTS 1 //#define IET_ATTEMPTS 1
#define THERMO_GIST_CELSIUS 1. #define THERMO_GIST_CELSIUS 1.
#define THERMO_OVERHEAT_CELSIUS 38. #define THERMO_OVERHEAT_CELSIUS 38.

View File

@@ -642,9 +642,9 @@ itemCmd mapInt(int32_t arg, aJsonObject* map)
return _itemCmd.Int(arg); return _itemCmd.Int(arg);
} }
unsigned long millisNZ() unsigned long millisNZ(uint8_t shift)
{ {
unsigned long now = millis(); unsigned long now = millis()>>shift;
if (!now) now=1; if (!now) now=1;
return now; return now;
} }

View File

@@ -65,4 +65,4 @@ bool isTimeOver(uint32_t timestamp, uint32_t currTime, uint32_t time, uint32_t m
bool executeCommand(aJsonObject* cmd, int8_t toggle = -1); bool executeCommand(aJsonObject* cmd, int8_t toggle = -1);
bool executeCommand(aJsonObject* cmd, int8_t toggle, itemCmd _itemCmd); bool executeCommand(aJsonObject* cmd, int8_t toggle, itemCmd _itemCmd);
itemCmd mapInt(int32_t arg, aJsonObject* map); itemCmd mapInt(int32_t arg, aJsonObject* map);
unsigned long millisNZ(); unsigned long millisNZ(uint8_t shift=0);