PID item and float-point fixes

This commit is contained in:
2021-03-09 05:28:13 +03:00
parent e7f606d16c
commit 058cde331d
11 changed files with 171 additions and 110 deletions

View File

@@ -8,4 +8,4 @@
#-DWiz5500
-DSTATUSLED
-DMCP23017
-DPID_DISABLE
#-DPID_DISABLE

View File

@@ -98,6 +98,7 @@ int txt2subItem(char *payload) {
else if (strcmp_P(payload, HUE_P) == 0) cmd = S_HUE;
else if (strcmp_P(payload, SAT_P) == 0) cmd = S_SAT;
else if (strcmp_P(payload, TEMP_P) == 0) cmd = S_TEMP;
else if (strcmp_P(payload, VAL_P) == 0) cmd = S_VAL;
return cmd;
}
@@ -328,7 +329,7 @@ long int Item::getVal() //Return Val if val is int or first elem of Value array
uint8_t Item::getSubtype()
{
if (!itemVal) return 0;//-1;
if (itemVal->type == aJson_Int) return itemVal->subtype;
if (itemVal->type == aJson_Int || itemVal->type == aJson_Float) return itemVal->subtype;
else if (itemVal->type == aJson_Array) {
aJsonObject *t = aJson.getArrayItem(itemVal, 0);
if (t) return t->subtype;
@@ -349,14 +350,15 @@ void Item::setVal(short n, int par) // Only store if VAL is array defined in c
void Item::setVal(long int par) // Only store if VAL is int (autogenerated or config-defined)
{
if (!itemVal || itemVal->type != aJson_Int) return;
if (!itemVal || (itemVal->type != aJson_Int && itemVal->type != aJson_Float)) return;
//debugSerial<<F(" Store ")<<F(" Val=")<<par<<endl;
itemVal->valueint = par;
itemVal->type = aJson_Int;
}
void Item::setSubtype(uint8_t par) // Only store if VAL is int (autogenerated or config-defined)
{
if (!itemVal || itemVal->type != aJson_Int) return;
if (!itemVal || (itemVal->type != aJson_Int && itemVal->type != aJson_Float)) return;
//debugSerial<<F(" Store ")<<F(" Val=")<<par<<endl;
itemVal->subtype = par & 0xF;
}
@@ -587,12 +589,15 @@ int Item::Ctrl(itemCmd cmd, char* subItem)
suffixCode = retrieveCode(&subItem);
if (!suffixCode && defaultSuffixCode)
{
suffixCode = defaultSuffixCode;
if (!cmd.getSuffix()) cmd.setSuffix(suffixCode);
}
int fr = freeRam();
debugSerial<<F("RAM=")<<fr<<F(" Item=")<<itemArr->name<<F(" Sub=")<<subItem<<F(" Cmd=");
debugSerial<<F("RAM=")<<fr<<F(" Item=")<<itemArr->name<<F(" Sub=")<<subItem<<F(" Cmd=")<<F("; ");
if (fr < 180) {
errorSerial<<F("OutOfMemory!\n")<<endl;
return -1;
@@ -741,6 +746,10 @@ int Item::Ctrl(itemCmd cmd, char* subItem)
SendStatus(SEND_PARAMETERS | SEND_DEFFERED);
break;
case S_VAL:
st.assignFrom(cmd);
break;
case S_SAT:
if (itemType == CH_GROUP && cmd.isColor()) st.setArgType(ST_HSV);//Extend storage for group channel
if (st.setS(cmd.getS()))
@@ -768,7 +777,7 @@ int Item::Ctrl(itemCmd cmd, char* subItem)
default:
st.Cmd(cmd.getCmd());
st.setSuffix(cmd.getSuffix());
st.setSuffix(suffixCode);
toExecute=true;
} //Switch commands
@@ -823,6 +832,7 @@ if (driver) //New style modular code
case CMD_VOID:
res = driver->Ctrl(st, subItem, toExecute);
debugSerial<<F("Driver Res:")<<res<<endl;
break;
default: //another command
@@ -929,7 +939,11 @@ return 1;
void printActiveStatus(bool active)
{
if (active) debugSerial<<F(" active\n");
else debugSerial<<F(" inactive\n");
}
int Item::isActive() {
itemArgStore st;
@@ -953,17 +967,22 @@ int Item::isActive() {
case CMD_AUTO:
case CMD_HEAT:
case CMD_COOL:
debugSerial<<F(" active\n");
printActiveStatus(true);
return 1;
case CMD_OFF:
case CMD_HALT:
case -1: ///// No last command
debugSerial<<F(" inactive\n");
printActiveStatus(false);
return 0;
}
// Last time was not a command but parameters set. Looking inside
if (driver) return driver->isActive();
if (driver) {
bool active = driver->isActive();
printActiveStatus(active);
return active;
}
// No driver - check value
st.aslong = getVal();
switch (itemType) {
@@ -996,9 +1015,8 @@ int Item::isActive() {
val = st.v; //Light volume
break;
case CH_DIMMER: //Everywhere, in flat VAL
case CH_DIMMER: //Legacy channels
case CH_MODBUS:
// case CH_THERMO:
case CH_VC:
case CH_VCTEMP:
case CH_PWM:
@@ -1008,6 +1026,7 @@ int Item::isActive() {
debugSerial<<F(" unknown\n");
return 0;
} //switch
debugSerial<<F(" value is ");
debugSerial<<val<<endl;
if (val) return 1; else return 0;

View File

@@ -34,7 +34,8 @@ e-mail anklimov@gmail.com
#define S_HUE 9
#define S_SAT 10
#define S_TEMP 11
#define S_ADDITIONAL 11
#define S_VAL 12
#define S_ADDITIONAL 12
#define CH_DIMMER 0 //DMX 1 ch
#define CH_RGBW 1 //DMX 4 ch

View File

@@ -388,6 +388,7 @@ itemCmd itemCmd::assignFrom(itemCmd from)
case ST_INT32:
case ST_UINT32:
param.asInt32=from.param.asInt32;
cmd.itemArgType=from.cmd.itemArgType;
break;
case ST_FLOAT_FARENHEIT:
toFarenheit = true;
@@ -418,6 +419,7 @@ itemCmd itemCmd::assignFrom(itemCmd from)
case ST_FLOAT_CELSIUS:
cmd.itemArgType=from.cmd.itemArgType;
param.asfloat=from.param.asfloat;
break;
default:
debugSerial<<F("Wrong Assignment ")<<from.cmd.itemArgType<<F("->")<<cmd.itemArgType<<endl;
}
@@ -615,7 +617,7 @@ long int itemCmd::getInt()
case ST_FLOAT:
case ST_FLOAT_CELSIUS:
case ST_FLOAT_FARENHEIT:
return int (param.asfloat);
return param.asfloat;
default:
@@ -673,6 +675,10 @@ short itemCmd::getPercents(bool inverse)
if (inverse) return map(param.v,0,255,100,0);
else return map(param.v,0,255,0,100);
case ST_FLOAT:
if (inverse) return param.asfloat;
else return 100-param.asfloat;
default:
return 0;
}
@@ -691,6 +697,10 @@ short itemCmd::getPercents255(bool inverse)
case ST_HSV255:
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);
default:
return 0;
}
@@ -778,6 +788,12 @@ itemCmd itemCmd::Int(uint32_t i)
return *this;
}
itemCmd itemCmd::Float(float f)
{
cmd.itemArgType=ST_FLOAT;
param.asfloat=f;
return *this;
}
itemCmd itemCmd::Tens(int32_t i)
{
@@ -874,6 +890,17 @@ bool itemCmd::loadItem(Item * item, bool includeCommand)
//debugOut();
return 1;
}
switch (item->itemVal->type)
{
case aJson_Int:
Int(item->itemVal->valueint);
return true;
case aJson_Float:
Float(item->itemVal->valueint);
return true;
}
}
return false;
}

View File

@@ -189,6 +189,7 @@ public:
itemCmd Int(int32_t i);
itemCmd Int(uint32_t i);
itemCmd Float(float f);
itemCmd Tens(int32_t i);
itemCmd Cmd(uint8_t i);
itemCmd HSV(uint16_t h, uint8_t s, uint8_t v);

View File

@@ -926,16 +926,20 @@ void resetHard() {
void Changed(int i, DeviceAddress addr, float currentTemp) {
char addrstr[32] = "NIL";
//char addrbuf[17];
char valstr[16] = "NIL";
char *owEmitString = NULL;
//char valstr[16] = "NIL";
//char *owEmitString = NULL;
char *owItem = NULL;
SetBytes(addr, 8, addrstr);
addrstr[17] = 0;
if (!root) return;
printFloatValueToStr(currentTemp,valstr);
debugSerial<<endl<<F("T:")<<valstr<<F("<");
//printFloatValueToStr(currentTemp,valstr);
debugSerial<<endl<<F("T:")<<currentTemp<<F("<")<<addrstr<<F(">")<<endl;
aJsonObject *owObj = aJson.getObjectItem(owArr, addrstr);
if ((currentTemp != -127.0) && (currentTemp != 85.0) && (currentTemp != 0.0))
executeCommand(owObj,-1,itemCmd(currentTemp));
/*
if (owObj) {
owEmitString = getStringFromConfig(owObj, "emit");
debugSerial<<owEmitString<<F(">")<<endl;
@@ -970,7 +974,7 @@ void Changed(int i, DeviceAddress addr, float currentTemp) {
} // if valid temperature
} // if Address in config
else debugSerial<<addrstr<<F(">")<<endl; // No item found
*/
}
#endif //_owire

View File

@@ -16,7 +16,7 @@ bool out_pid::getConfig()
int direction = DIRECT;
// Retrieve and store
if (!store || !item || !item->itemArg || (item->itemArg->type != aJson_Array) || aJson.getArraySize(item->itemArg)<2)
if (!store || !item || !item->itemArg || (item->itemArg->type != aJson_Array) || aJson.getArraySize(item->itemArg)<1)
{
errorSerial<<F("PID: config failed:")<<(bool)store<<F(",")<<(bool)item<<F(",")<<(bool)item->itemArg<<F(",")<<(item->itemArg->type != aJson_Array)<<F(",")<< (aJson.getArraySize(item->itemArg)<2)<<endl;
return false;
@@ -28,19 +28,30 @@ bool out_pid::getConfig()
errorSerial<<F("Invalid PID param array.")<<endl;
return false;
}
double outMin=0.;
double outMax=100.;
aJsonObject * param;
switch (aJson.getArraySize(kPIDObj))
{
{ case 5:
param = aJson.getArrayItem(kPIDObj, 4);
if (param->type == aJson_Float) outMax=param->valuefloat;
else if (param->type == aJson_Int) outMax=param->valueint;
case 4:
param = aJson.getArrayItem(kPIDObj, 3);
if (param->type == aJson_Float) outMin=param->valuefloat;
else if (param->type == aJson_Int) outMin=param->valueint;
case 3:
param = aJson.getArrayItem(kPIDObj, 2);
if (param->type == aJson_Float) kD=param->valuefloat;
else if (param->type == aJson_Int) kD=param->valueint;
case 2:
param = aJson.getArrayItem(kPIDObj, 1);
if (param->type == aJson_Float) kI=param->valuefloat;
else if (param->type == aJson_Int) kI=param->valueint;
case 1:
param = aJson.getArrayItem(kPIDObj, 0);
if (param->type == aJson_Float) kP=param->valuefloat;
else if (param->type == aJson_Int) kP=param->valueint;
if (kP<0)
{
kP=-kP;
@@ -50,20 +61,28 @@ bool out_pid::getConfig()
switch (item->itemVal->type)
{
case aJson_Int: item->itemVal->valuefloat = item->itemVal->valueint;
case aJson_Int:
store->setpoint = item->itemVal->valueint;
break;
case aJson_Float:
store->setpoint = item->itemVal->valuefloat;
break;
default:
store->setpoint = 20.;
}
item->itemVal->type = aJson_Float;
if (!store->pid) store->pid= new PID (&store->input, &store->output, &item->itemVal->valuefloat, kP, kI, kD, direction);
if (!store->pid)
return true;
//store->addr=item->getArg(0);
{store->pid= new PID (&store->input, &store->output, &store->setpoint, kP, kI, kD, direction);
if (!store->pid) return false;
store->pid->SetMode(AUTOMATIC);
store->pid->SetOutputLimits(outMin,outMax);
return true;}
else errorSerial<<F("PID already initialized")<<endl;
return false;
}
@@ -73,12 +92,10 @@ if (!store) store= (pidPersistent *)item->setPersistent(new pidPersistent);
if (!store)
{ errorSerial<<F("PID: Out of memory")<<endl;
return 0;}
store->pid=NULL;
//store->timestamp=millis();
if (getConfig())
{
//item->clearFlag(ACTION_NEEDED);
//item->clearFlag(ACTION_IN_PROCESS);
infoSerial<<F("PID config loaded ")<< item->itemArr->name<<endl;
store->driverStatus = CST_INITIALIZED;
return 1;
@@ -93,8 +110,8 @@ else
int out_pid::Stop()
{
Serial.println("Modbus De-Init");
if (store) delete (store->pid());
Serial.println("PID De-Init");
if (store) delete (store->pid);
delete store;
item->setPersistent(NULL);
store = NULL;
@@ -110,99 +127,78 @@ return CST_UNKNOWN;
int out_pid::isActive()
{
return item->getVal();
return (item->getCmd()!=CMD_OFF);
}
int out_pid::Poll(short cause)
{
if ((Status() == CST_INITIALIZED) && isTimeOver(store->timestamp,millis(),store->pollingInterval))
{
if (store && store->pid && (Status() == CST_INITIALIZED) && item && (item->getCmd()!=CMD_OFF))
{
double prevOut=store->output;
store->pid->Compute();
if (abs(store->output-prevOut)>OUTPUT_TRESHOLD)
{
aJsonObject * oCmd = aJson.getArrayItem(item->itemArg, 1);
itemCmd value((float) store->output);
executeCommand(oCmd,-1,value);
}
store->timestamp=millisNZ();
debugSerial<<F("endPoll ")<< item->itemArr->name << endl;
}
}
return store->pollingInterval;
return 1;//store->pollingInterval;
};
int out_pid::getChanType()
{
return CH_MODBUS;
return CH_THERMO;
}
//!Control unified Modbus item
// Priority of selection sub-items control to:
// 1. if defined standard suffix Code inside cmd
// 2. custom textual subItem
// 3. non-standard numeric suffix Code equal param id
//!Control unified PID controller item
// /set suffix - to setup setpoint
// /val suffix - to put value into controller
// accept ON and OFF commands
int out_pid::Ctrl(itemCmd cmd, char* subItem, bool toExecute)
{
//int chActive = item->isActive();
//bool toExecute = (chActive>0);
//itemCmd st(ST_UINT32,CMD_VOID);
if (!store || !store->pid || (Status() != CST_INITIALIZED)) return 0;
int suffixCode = cmd.getSuffix();
aJsonObject *templateParamObj = NULL;
short mappedCmdVal = 0;
// trying to find parameter in template with name == subItem (NB!! standard suffixes dint working here)
if (subItem && strlen (subItem)) templateParamObj = aJson.getObjectItem(store->parameters, subItem);
if (!templateParamObj)
{
// Trying to find template parameter where id == suffixCode
templateParamObj = store->parameters->child;
while (templateParamObj)
{
//aJsonObject *regObj = aJson.getObjectItem(paramObj, "reg");
aJsonObject *idObj = aJson.getObjectItem(templateParamObj, "id");
if (idObj->type==aJson_Int && idObj->valueint == suffixCode) break;
aJsonObject *mapObj = aJson.getObjectItem(templateParamObj, "mapcmd");
if (mapObj && (mappedCmdVal = cmd.doReverseMappingCmd(mapObj))) break;
templateParamObj=templateParamObj->next;
}
}
// aJsonObject *typeObj = aJson.getObjectItem(paramObj, "type");
// aJsonObject *mapObj = aJson.getObjectItem(paramObj, "map");
// aJsonObject * itemParametersObj = aJson.getArrayItem(item->itemArg, 2);
// uint16_t data = node.getResponseBuffer(posInBuffer);
if (cmd.isCommand() && !suffixCode) suffixCode=S_CMD; //if some known command find, but w/o correct suffix - got it
switch(suffixCode)
{
case S_NOTFOUND:
// turn on and set
toExecute = true;
debugSerial<<F("Forced execution");
case S_SET:
if (!cmd.isValue()) return 0;
case S_VAL:
// Input value for PID
if (!cmd.isValue()) return 0;
store->input=cmd.getFloat();
debugSerial<<F("Input value:")<<store->input<<endl;
return 1;
//break;
//TODO
return 1;
//break;
case S_NOTFOUND:
case S_SET:
// Setpoint for PID
if (!cmd.isValue()) return 0;
store->setpoint=cmd.getFloat();
debugSerial<<F("Setpoint:")<<store->setpoint<<endl;
//cmd.saveItem(item);
//item->SendStatus(SEND_PARAMETERS);
return 1;
//break;
case S_CMD:
switch (cmd.getCmd())
{
case CMD_ON:
case CMD_OFF:
item->setCmd(cmd.getCmd());
item->SendStatus(SEND_COMMAND);
return 1;
case CMD_OFF:
return 1;
default:
debugSerial<<F("Unknown cmd ")<<cmd.getCmd()<<endl;
} //switch cmd

View File

@@ -5,12 +5,14 @@
#include <item.h>
#include <PID_v1.h>
#define OUTPUT_TRESHOLD 1
class pidPersistent : public chPersistent {
public:
PID pid;
PID * pid;
double output;
double input;
double setpoint;
int driverStatus;
};

View File

@@ -114,6 +114,7 @@ const char SAT_P[] PROGMEM = "sat";
const char TEMP_P[] PROGMEM = "temp";
const char HSV_P[] PROGMEM = "HSV";
const char RGB_P[] PROGMEM = "RGB";
const char VAL_P[] PROGMEM = "val";
/*
const char RPM_P[] PROGMEM = "rpm";

View File

@@ -532,6 +532,7 @@ bool executeCommand(aJsonObject* cmd, int8_t toggle)
bool executeCommand(aJsonObject* cmd, int8_t toggle, itemCmd _itemCmd)
//bool executeCommand(aJsonObject* cmd, int8_t toggle, char* defCmd)
{
if (!cmd) return 0;
switch (cmd->type)
{
case aJson_String: //legacy - no action
@@ -572,17 +573,21 @@ switch (toggle)
if (!ecmd) ecmd = aJson.getObjectItem(cmd, "ecmd");
}
char * itemCommand;
char * itemCommand = NULL;
char Buffer[16];
if(icmd && icmd->type == aJson_String) itemCommand = icmd->valuestring;
else itemCommand = _itemCmd.toString(Buffer,sizeof(Buffer));
//else itemCommand = _itemCmd.toString(Buffer,sizeof(Buffer));
char * emitCommand;
if(ecmd && ecmd->type == aJson_String) emitCommand = ecmd->valuestring;
else emitCommand = _itemCmd.toString(Buffer,sizeof(Buffer));
//debugSerial << F("IN:") << (pin) << F(" : ") <<endl;
if (item) debugSerial << item->valuestring<< F(" -> ")<<itemCommand<<endl;
if (item) {
if (itemCommand)
debugSerial << item->valuestring<< F(" -> ")<<itemCommand<<endl;
else debugSerial << item->valuestring<< F(" -> ");_itemCmd.debugOut();
}
if (emit) debugSerial << emit->valuestring<< F(" -> ")<<emitCommand<<endl;
@@ -602,7 +607,7 @@ TODO implement
#endif
*/
{
char addrstr[MQTT_TOPIC_LENGTH];
strncpy(addrstr,emit->valuestring,sizeof(addrstr));
if (mqttClient.connected() && !ethernetIdleCount)
@@ -610,12 +615,17 @@ 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) {
if (item && item->type == aJson_String) {
//debugSerial <<F("Controlled item:")<< item->valuestring <<endl;
Item it(item->valuestring);
if (it.isValid()) it.Ctrl(itemCommand);
if (it.isValid())
{
if (itemCommand) it.Ctrl(itemCommand);
else it.Ctrl(_itemCmd);
}
}
return true;
}

View File

@@ -13,7 +13,7 @@ src_dir = lighthub
default_envs =
; ****** UNCOMMENT single environment name for target platform below *******
; Arduino Mega (without onewire) + Ethernet shield Wiznet 5100
mega2560slim-5100
; mega2560slim-5100
; Arduino Mega + Ethernet shield Wiznet 5100
; mega2560-5100
@@ -22,7 +22,7 @@ default_envs =
; mega2560-5500
; LightHub controller HW revision 2.1 and above (Wiznet 5500 CS on pin 53)
; lighthub21
lighthub21
; Arduino DUE + Ethernet shield Wiznet 5100
; due-5100
@@ -583,8 +583,8 @@ framework = arduino
board = due
build_flags = !python get_build_flags.py lighthub21
;upload_command = arduinoOTA -address 192.168.11.172 -port 65280 -username arduino -password password -b -upload /sketch -sketch $SOURCE
;upload_command = arduinoOTA -address 192.168.88.34 -port 65280 -username arduino -password password -b -upload /sketch -sketch $SOURCE
;upload_protocol = custom
upload_command = arduinoOTA -address 192.168.88.64 -port 65280 -username arduino -password password -b -upload /sketch -sketch $SOURCE
upload_protocol = custom
lib_ignore =
;DS2482_OneWire //UNCOMMENT for software 1-wire driver
DHT sensor library for ESPx