mirror of
https://github.com/anklimov/lighthub
synced 2025-12-11 22:29:51 +03:00
Termostat refactoring
All Sensors (1-w, DHT, HDC, CCS):unified transfer measurements to mqtt/local items
This commit is contained in:
@@ -473,12 +473,25 @@ void Input::dht22Poll() {
|
||||
float temp = dht.readTemperature();
|
||||
float humidity = dht.readHumidity();
|
||||
#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 *item = aJson.getObjectItem(inputObj, "item");
|
||||
if (item && item->type == aJson_String) thermoSetCurTemp(item->valuestring, temp);
|
||||
debugSerial << F("IN:") << pin << F(" DHT22 type. T=") << temp << F("°C H=") << humidity << F("%")<<endl;
|
||||
|
||||
//aJsonObject *item = aJson.getObjectItem(inputObj, "item");
|
||||
//if (item && item->type == aJson_String) thermoSetCurTemp(item->valuestring, temp);
|
||||
|
||||
|
||||
if (emit && emit->type == aJson_String && temp && humidity && temp == temp && humidity == humidity) {
|
||||
char addrstr[MQTT_TOPIC_LENGTH] = "";
|
||||
|
||||
#ifdef WITH_DOMOTICZ
|
||||
if(getIdxField()){
|
||||
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)
|
||||
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());
|
||||
}
|
||||
#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
|
||||
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) {
|
||||
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 *emit = aJson.getObjectItem(inputObj, "emit");
|
||||
|
||||
|
||||
@@ -747,7 +747,7 @@ int Item::Ctrl(itemCmd cmd, char* subItem)
|
||||
|
||||
break;
|
||||
case S_VAL:
|
||||
st.assignFrom(cmd);
|
||||
st=cmd;
|
||||
break;
|
||||
|
||||
case S_SAT:
|
||||
@@ -899,7 +899,24 @@ switch (itemType) {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -941,8 +958,8 @@ return 1;
|
||||
|
||||
void printActiveStatus(bool active)
|
||||
{
|
||||
if (active) debugSerial<<F(" active\n");
|
||||
else debugSerial<<F(" inactive\n");
|
||||
if (active) debugSerial<<F(" active ");
|
||||
else debugSerial<<F(" inactive ");
|
||||
}
|
||||
|
||||
int Item::isActive() {
|
||||
@@ -996,13 +1013,13 @@ int Item::isActive() {
|
||||
Item it(i->valuestring);
|
||||
|
||||
if (it.isValid() && it.isActive()>0) {
|
||||
debugSerial<<F(" active\n");
|
||||
printActiveStatus(true);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
i = i->next;
|
||||
} //while
|
||||
debugSerial<<F(" inactive\n");
|
||||
printActiveStatus(false);
|
||||
return 0;
|
||||
} //if
|
||||
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
|
||||
Снять аварию 42001 (2001=7d1) =>4
|
||||
|
||||
|
||||
@@ -156,3 +156,13 @@ class Item
|
||||
int defaultSuffixCode;
|
||||
|
||||
};
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
int16_t tempX100;
|
||||
uint16_t timestamp16;
|
||||
};
|
||||
int32_t asint;
|
||||
} thermostatStore;
|
||||
@@ -342,8 +342,14 @@ itemCmd itemCmd::assignFrom(itemCmd from)
|
||||
param.v=map(from.param.v,0,255,0,100);
|
||||
break;
|
||||
case ST_FLOAT:
|
||||
param.v = (int) from.param.asfloat;
|
||||
break;
|
||||
if (cmd.itemArgType == ST_HSV)
|
||||
param.v = constrain(from.param.asfloat,0,100);
|
||||
else
|
||||
{
|
||||
param=from.param;
|
||||
cmd.itemArgType=from.cmd.itemArgType;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debugSerial<<F("Wrong Assignment ")<<from.cmd.itemArgType<<F("->")<<cmd.itemArgType<<endl;
|
||||
}
|
||||
@@ -378,6 +384,15 @@ itemCmd itemCmd::assignFrom(itemCmd from)
|
||||
case ST_PERCENTS255:
|
||||
param.v=from.param.v;
|
||||
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:
|
||||
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;
|
||||
param.asfloat=from.param.asfloat;
|
||||
break;
|
||||
case ST_HSV255:
|
||||
case ST_HSV:
|
||||
case ST_RGB:
|
||||
case ST_RGBW:
|
||||
cmd.itemArgType=from.cmd.itemArgType;
|
||||
param=from.param;
|
||||
break;
|
||||
default:
|
||||
debugSerial<<F("Wrong Assignment ")<<from.cmd.itemArgType<<F("->")<<cmd.itemArgType<<endl;
|
||||
}
|
||||
@@ -605,7 +627,7 @@ long int itemCmd::getInt()
|
||||
case ST_UINT32:
|
||||
case ST_RGB:
|
||||
case ST_RGBW:
|
||||
case ST_TENS:
|
||||
|
||||
return param.aslong;
|
||||
|
||||
case ST_PERCENTS:
|
||||
@@ -618,6 +640,9 @@ long int itemCmd::getInt()
|
||||
case ST_FLOAT_CELSIUS:
|
||||
case ST_FLOAT_FARENHEIT:
|
||||
return param.asfloat;
|
||||
case ST_TENS:
|
||||
return param.aslong/10;
|
||||
|
||||
|
||||
|
||||
default:
|
||||
@@ -677,7 +702,12 @@ short itemCmd::getPercents(bool inverse)
|
||||
|
||||
case ST_FLOAT:
|
||||
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:
|
||||
return 0;
|
||||
@@ -690,17 +720,22 @@ short itemCmd::getPercents255(bool inverse)
|
||||
|
||||
case ST_PERCENTS:
|
||||
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);
|
||||
|
||||
case ST_PERCENTS255:
|
||||
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:
|
||||
if (inverse) return map(param.asfloat,0,100,255,0);
|
||||
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:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2097,6 +2097,90 @@ configLocked--;
|
||||
}//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) {
|
||||
return (item->type == aJson_Array) && (aJson.getArrayItem(item, I_TYPE)->valueint == CH_THERMO) &&
|
||||
(aJson.getArraySize(item) >= minimalArraySize);
|
||||
@@ -2115,6 +2199,9 @@ bool thermoDisabledOrDisconnected(aJsonObject *thermoExtensionArray, int thermoS
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//TODO: refactoring
|
||||
void thermoLoop(void) {
|
||||
// if (millis() < timerThermostatCheck)
|
||||
@@ -2215,3 +2302,4 @@ short thermoSetCurTemp(char *name, float t) {
|
||||
}
|
||||
return 1;}
|
||||
return 0;}
|
||||
*/
|
||||
@@ -113,6 +113,12 @@ if (reg!=0xff)
|
||||
M5.Lcd.print("%\n");
|
||||
#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(h,"/H");
|
||||
if (CCS811ready) ccs811.setEnvironmentalData(h,t);
|
||||
@@ -165,6 +171,13 @@ int in_ccs811::Poll(short cause)
|
||||
|
||||
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(tvoc,"/TVOC");
|
||||
publish(ccs811Baseline,"/base");}
|
||||
|
||||
@@ -37,10 +37,11 @@
|
||||
#define TIMEOUT_REINIT 5000UL
|
||||
#define TIMEOUT_RETAIN 5000UL
|
||||
#define INTERVAL_1W 5000UL
|
||||
#define PERIOD_THERMOSTAT_FAILED (300 * 1000UL)>>8
|
||||
|
||||
#define T_ATTEMPTS 200
|
||||
#define IET_TEMP 0
|
||||
#define IET_ATTEMPTS 1
|
||||
//#define T_ATTEMPTS 200
|
||||
//#define IET_TEMP 0
|
||||
//#define IET_ATTEMPTS 1
|
||||
|
||||
#define THERMO_GIST_CELSIUS 1.
|
||||
#define THERMO_OVERHEAT_CELSIUS 38.
|
||||
|
||||
@@ -642,9 +642,9 @@ itemCmd mapInt(int32_t arg, aJsonObject* map)
|
||||
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;
|
||||
return now;
|
||||
}
|
||||
|
||||
@@ -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, itemCmd _itemCmd);
|
||||
itemCmd mapInt(int32_t arg, aJsonObject* map);
|
||||
unsigned long millisNZ();
|
||||
unsigned long millisNZ(uint8_t shift=0);
|
||||
|
||||
Reference in New Issue
Block a user