From c03821e94acefdd3f081727d28e092969f9938bc Mon Sep 17 00:00:00 2001 From: Andrey Klimov Date: Wed, 29 Dec 2021 02:06:52 +0300 Subject: [PATCH] PID+PWM relay thermostat, init from flash fixed --- lighthub/abstractout.cpp | 2 +- lighthub/item.cpp | 22 ++-- lighthub/item.h | 3 +- lighthub/itemCmd.cpp | 2 +- lighthub/main.cpp | 8 +- lighthub/modules/out_pid.cpp | 157 +++++++++++++++++++++---- lighthub/modules/out_pid.h | 6 +- lighthub/modules/out_relay.cpp | 204 +++++++++++++++++++++++++++++++++ lighthub/modules/out_relay.h | 28 +++++ 9 files changed, 397 insertions(+), 35 deletions(-) create mode 100644 lighthub/modules/out_relay.cpp create mode 100644 lighthub/modules/out_relay.h diff --git a/lighthub/abstractout.cpp b/lighthub/abstractout.cpp index 04307f5..480cca8 100644 --- a/lighthub/abstractout.cpp +++ b/lighthub/abstractout.cpp @@ -19,6 +19,6 @@ int abstractOut::isActive() int abstractOut::Setup() { - if (item) item->setCmd(CMD_OFF); + if (item && (item->getCmd()==-1)) item->setCmd(CMD_OFF); return 1; } \ No newline at end of file diff --git a/lighthub/item.cpp b/lighthub/item.cpp index 00917bc..75ef684 100644 --- a/lighthub/item.cpp +++ b/lighthub/item.cpp @@ -50,6 +50,7 @@ e-mail anklimov@gmail.com #include "modules/out_pid.h" #include "modules/out_multivent.h" #include "modules/out_uartbridge.h" +#include "modules/out_relay.h" #ifdef ELEVATOR_ENABLE #include "modules/out_elevator.h" @@ -147,48 +148,48 @@ void Item::Parse() { case CH_DIMMER: case CH_RGBWW: driver = new out_dmx (this); - // debugSerial<Status()) driver->Stop(); - driver->Setup(); + if (driver->Setup()) + { + if (getCmd()) setFlag(SEND_COMMAND); + if (itemVal) setFlag(SEND_PARAMETERS); + } return true; } else return false; @@ -237,7 +242,6 @@ Item::~Item() if (driver) { delete driver; -// debugSerial<getVal(); //debugSerial<")<type == aJson_Float) outMax=param->valuefloat; else if (param->type == aJson_Int) outMax=param->valueint; - case 4: - param = aJson.getArrayItem(kPIDObj, 3); + + case 6: //kP,kI,kD, alarmTO, alarmVal, outMin + param = aJson.getArrayItem(kPIDObj, 5); if (param->type == aJson_Float) outMin=param->valuefloat; - else if (param->type == aJson_Int) outMin=param->valueint; - case 3: + else if (param->type == aJson_Int) outMin=param->valueint; + + case 5: //kP,kI,kD, alarmTO, alarmVal + case 4: //kP,kI,kD, alarmTO + param = aJson.getArrayItem(kPIDObj, 3); + if (param->type == aJson_Int) alarmTO=param->valueint; + case 3: //kP,kI,kD param = aJson.getArrayItem(kPIDObj, 2); if (param->type == aJson_Float) kD=param->valuefloat; else if (param->type == aJson_Int) kD=param->valueint; - case 2: + + case 2: //kP,kI param = aJson.getArrayItem(kPIDObj, 1); if (param->type == aJson_Float) kI=param->valuefloat; else if (param->type == aJson_Int) kI=param->valueint; - case 1: + + case 1: //kP param = aJson.getArrayItem(kPIDObj, 0); if (param->type == aJson_Float) kP=param->valuefloat; else if (param->type == aJson_Int) kP=param->valueint; @@ -56,7 +66,7 @@ bool out_pid::getConfig() { kP=-kP; direction=REVERSE; - } + } } switch (item->itemVal->type) @@ -79,6 +89,9 @@ bool out_pid::getConfig() store->pid->SetMode(AUTOMATIC); //store->pid->SetOutputLimits(outMin,outMax); store->pid->SetSampleTime(5000); + store->alarmTimer=millis(); + store->alarmArmed=false; + store->alarmTimeout=alarmTO; //in sec return true;} else errorSerial<pid=NULL; if (getConfig()) { infoSerial<itemArr->name<On(); // Turn ON pid by default + //item->On(); // Turn ON pid by default + // if (item->getCmd()) item->setFlag(SEND_COMMAND); + // if (item->itemVal) item->setFlag(SEND_PARAMETERS); + store->prevOut = -2.0; store->driverStatus = CST_INITIALIZED; return 1; } @@ -138,21 +154,92 @@ int out_pid::Poll(short cause) { if (store && store->pid && (Status() == CST_INITIALIZED) && item && (item->getCmd()!=CMD_OFF)) { - double prevOut=store->output; - if(store->pid->Compute()) - //if (abs(store->output-store-prevOut)>OUTPUT_TRESHOLD) + //double prevOut=store->output; + //itemCmd st; + //st.loadItem(item); + //short cmd = st.getCmd(); + if (item->getCmd() != CMD_OFF) + { + if(store->pid->Compute() && !store->alarmArmed) + if (abs(store->output-store->prevOut)>OUTPUT_TRESHOLD) { aJsonObject * oCmd = aJson.getArrayItem(item->itemArg, 1); itemCmd value((float) (store->output));// * (100./255.))); + value.setSuffix(S_SET); executeCommand(oCmd,-1,value); + store->prevOut=store->output; } + if(!store->alarmArmed && isTimeOver(store->alarmTimer,millis(),store->alarmTimeout*1000) ) + { + store->alarmArmed=true; + alarm(true); + } + } } return 1;//store->pollingInterval; }; +void out_pid::alarm(bool state) +{ + +if (!item || item->itemArg) return; + if (state) + { + + aJsonObject * kPIDObj = aJson.getArrayItem(item->itemArg, 0); + if (kPIDObj->type != aJson_Array) + { + errorSerial<type == aJson_Float) outAlarm=param->valuefloat; + else if (param->type == aJson_Int) outAlarm=param->valueint; + else alarmValDefined=false; + + case 4: //kP,kI,kD, alarmTO + case 3: //kP,kI,kD + case 2: //kP,kI + case 1: //kP + 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) + { + if (!alarmValDefined) outAlarm = 0.; + } + else if (!alarmValDefined) outAlarm = .255; + } + } + errorSerial<itemArr->name<itemArg, 1); + itemCmd value (outAlarm);// * (100./255.))); + executeCommand(oCmd,-1,value); + } + else + { + infoSerial<itemArr->name<input=cmd.getFloat(); debugSerial<input<alarmTimer=millis(); +if (store->alarmArmed) + { + alarm(false); + store->alarmArmed=false; + } return 1; //break; @@ -196,17 +290,40 @@ return 1; //break; case S_CMD: - switch (cmd.getCmd()) + { + aJsonObject * oCmd = aJson.getArrayItem(item->itemArg, 1); + short command = cmd.getCmd(); + itemCmd value(ST_VOID,command); + value.setSuffix(S_CMD); + switch (command) { - case CMD_ON: case CMD_OFF: - //item->setCmd(cmd.getCmd()); - //item->SendStatus(SEND_COMMAND); + //value.Percents255(0); + + case CMD_ON: + case CMD_HEAT: + case CMD_COOL: + case CMD_AUTO: + case CMD_FAN: + case CMD_DRY: + + executeCommand(oCmd,-1,value); + return 1; + +/* + case CMD_OFF: + { + aJsonObject * oCmd = aJson.getArrayItem(item->itemArg, 1); + itemCmd value((float) 0.);// * (100./255.))); + value.setSuffix(S_SET); + executeCommand(oCmd,-1,value); return 1; + } */ + default: debugSerial< #include "itemCmd.h" -#define OUTPUT_TRESHOLD 1 +#define OUTPUT_TRESHOLD 1.0 class pidPersistent : public chPersistent { public: @@ -16,6 +16,9 @@ public: double setpoint; float prevOut; int driverStatus; + uint32_t alarmTimer; + bool alarmArmed; + uint16_t alarmTimeout; //in sec }; @@ -32,6 +35,7 @@ public: int getChanType() override; int getDefaultStorageType(){return ST_FLOAT;}; int Ctrl(itemCmd cmd, char* subItem=NULL, bool toExecute=true) override; + void alarm(bool); protected: diff --git a/lighthub/modules/out_relay.cpp b/lighthub/modules/out_relay.cpp new file mode 100644 index 0000000..7001df3 --- /dev/null +++ b/lighthub/modules/out_relay.cpp @@ -0,0 +1,204 @@ +#ifndef RELAY_DISABLE +#include "modules/out_relay.h" +#include "Arduino.h" +#include "options.h" +#include "Streaming.h" + +#include "item.h" +#include "main.h" +#include "dmx.h" + +static int driverStatus = CST_UNKNOWN; + +void out_relay::getConfig() +{ + inverted=false; + pin=item->getArg(0); + if (pin<0) + { + pin=-pin; + inverted=true; + } + if(pin==0 || pin>=PINS_COUNT) pin=32; + period = item->getArg(1); + + } + +#define ACTIVE (inverted)?LOW:HIGH +#define INACTIVE (inverted)?HIGH:LOW + + +int out_relay::Setup() +{ +abstractOut::Setup(); + +debugSerial<setExt(0); +digitalWrite(pin,INACTIVE); +//if (item->getCmd()) item->setFlag(SEND_COMMAND); +//if (item->itemVal) item->setFlag(SEND_PARAMETERS); +driverStatus = CST_INITIALIZED; +return 1; +} + +int out_relay::Stop() +{ +debugSerial<getCmd(); +if (state) + switch(cmd) + { + case CMD_COOL: + strcpy_P(val,cooling_P); + break; + //case CMD_AUTO: + //case CMD_HEAT: + //case CMD_ON: + // + // break; + case CMD_DRY: + strcpy_P(val,drying_P); + break; + case CMD_FAN: + strcpy_P(val,fan_P); + break; + default: + strcpy_P(val,heating_P); + } + else //turned off + if (cmd==CMD_OFF) strcpy_P(val,off_P); + else strcpy_P(val,idle_P); + +publishTopic(item->itemArr->name,val,subtopic); +} + +bool getPinVal(uint8_t pin) +{return (0!=(*portOutputRegister( digitalPinToPort(pin) ) & digitalPinToBitMask(pin)));} + +int out_relay::Poll(short cause) +{ + if (!item) return 0; + + itemCmd st; + st.loadItem(item); + int val = st.getPercents255(); +short cmd = st.getCmd(); +uint32_t timer = item->getExt(); + + if (timer && isTimeOver(timer,millis(),period)) + { + item->setExt(millisNZ()); + if (val && (getPinVal(pin) == INACTIVE)) relay(true); + } + + else if (timer && (getPinVal(pin) == ACTIVE) && isTimeOver(timer,millis(),period*val/255)) + { + relay(false); + if (!item->isActive()) item->setExt(0); + } + + + return 0; +}; + + +int out_relay::Ctrl(itemCmd cmd, char* subItem, bool toExecute) +{ +debugSerial<getExt()) + { + item->setExt(millisNZ()); + relay(true); + } + } + else + { + item->setExt(0); + relay(false); + } + } + return 1; +case S_CMD: + + switch (cmd.getCmd()) + { + case CMD_ON: + case CMD_HEAT: + case CMD_COOL: + case CMD_AUTO: + case CMD_FAN: + case CMD_DRY: + if (!item->getExt()) + { + item->setExt(millisNZ()); + relay(true); + } + return 1; + + case CMD_OFF: + item->setExt(0); + relay(false); + return 1; + + default: + debugSerial< +#include + +class out_relay : public abstractOut { +public: + + out_relay(Item * _item):abstractOut(_item){ getConfig();}; + void getConfig(); + void relay(bool state); + int Setup() override; + int Poll(short cause) override; + int Stop() override; + int Status() override; + + int getChanType() override; + int Ctrl(itemCmd cmd, char* subItem=NULL, bool toExecute=true) override; + +protected: + short pin; + bool inverted; + uint32_t period; +}; +#endif