From 1bfd2d98ef8222b1d7eaba88f88768f745ff66fc Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 1 Aug 2024 20:19:33 +0300 Subject: [PATCH] MODBUS: prefetch option for write reg (check-before-write), AC: suppressing garbage status after CMD, PID: NOT_FILTER_PID_OUT for repeating values^ by default --- lighthub/modules/out_ac.cpp | 159 +++++++++++++--------- lighthub/modules/out_modbus.cpp | 227 ++++++++++++++++++-------------- lighthub/modules/out_modbus.h | 2 +- lighthub/modules/out_pid.cpp | 2 +- lighthub/options.h | 13 +- 5 files changed, 238 insertions(+), 165 deletions(-) diff --git a/lighthub/modules/out_ac.cpp b/lighthub/modules/out_ac.cpp index 18b7199..54fad84 100644 --- a/lighthub/modules/out_ac.cpp +++ b/lighthub/modules/out_ac.cpp @@ -23,6 +23,8 @@ extern bool disableCMD; #define AC_UNKNOWN CST_UNKNOWN #define AC_IDLE CST_INITIALIZED #define AC_SENDING CST_USER +#define AC_AWAITINGCMD CST_USER+1 +#define AC_POLL CST_USER+2 //byte inCheck = 0; byte qstn[] = {255,255,10,0,0,0,0,0,1,1,77,1,90}; // Команда опроса @@ -237,6 +239,7 @@ SubmitParameters("fan",icmd); if (!(store->power & 0x01)) {strcpy_P(s_buffer,OFF_P);icmd.Cmd(CMD_OFF);} publishTopic(item->itemArr->name, s_buffer,"/cmd"); SubmitParameters("cmd",icmd); + /* if (store->power & 0x01) publishTopic(item->itemArr->name, s_buffer,"/cmd"); @@ -393,16 +396,46 @@ case AC_SENDING: store->timestamp=millisNZ(); } } +break; +case AC_AWAITINGCMD: //Flusing port for 5 sec, poll status + + if (store->timestamp && isTimeOver(store->timestamp,millis(),5000)) + { + setStatus(AC_POLL); + store->timestamp=millisNZ(); + store->inCheck=0; + debugSerial<available()) + { + delay(2); + ACSerial->read(); + } + return true; + + case AC_IDLE: + if (store->timestamp && isTimeOver(store->timestamp,millis(),INTERVAL_AC_POLLING)) setStatus(AC_POLL); + if (cause!=POLLING_SLOW) return false; + break; + + case AC_POLL: + if (cause!=POLLING_SLOW) return false; + + debugSerial.println(F("AC: Polling")); + SendData(qstn, sizeof(qstn)/sizeof(byte)); //Опрос кондиционера + store->timestamp=millisNZ(); + setStatus(AC_IDLE); + } -if (cause!=POLLING_SLOW) return false; +/*if (cause!=POLLING_SLOW) return false; if ((Status() == AC_IDLE) && isTimeOver(store->timestamp,millis(),INTERVAL_AC_POLLING)) { debugSerial.println(F("AC: Polling")); SendData(qstn, sizeof(qstn)/sizeof(byte)); //Опрос кондиционера } - +*/ if(ACSerial->available() >= 37){ //was 0 ACSerial->readBytes(store->data, 37); while(ACSerial->available()){ @@ -468,7 +501,10 @@ int out_AC::Ctrl(itemCmd cmd, char* subItem , bool toExecute, bool authorized) case S_CMD: // s_mode[0]='\0'; - store->inCheck=0; + + store->timestamp=millisNZ(); + setStatus(AC_AWAITINGCMD); + switch (cmd.getCmd()) { case CMD_ON: @@ -476,7 +512,7 @@ int out_AC::Ctrl(itemCmd cmd, char* subItem , bool toExecute, bool authorized) store->data[B_POWER] = store->power; store->data[B_POWER] |= 1; SendData(on, sizeof(on)/sizeof(byte)); - // publishTopic(item->itemArr->name,"ON","/cmd"); + //publishTopic(item->itemArr->name,"ON","/cmd"); // return 1; break; case CMD_OFF: @@ -484,7 +520,7 @@ int out_AC::Ctrl(itemCmd cmd, char* subItem , bool toExecute, bool authorized) store->data[B_POWER] = store->power; store->data[B_POWER] &= ~1; SendData(off, sizeof(off)/sizeof(byte)); - // publishTopic(item->itemArr->name,"OFF","/cmd"); + publishTopic(item->itemArr->name,"OFF","/cmd"); // return 1; break; case CMD_AUTO: @@ -532,62 +568,63 @@ int out_AC::Ctrl(itemCmd cmd, char* subItem , bool toExecute, bool authorized) case S_FAN: s_speed[0]='\0'; - switch (cmd.getCmd()) - { - case CMD_AUTO: - store->data[B_FAN_SPD] = 3; - strcpy_P(s_speed,AUTO_P); - break; - case CMD_HIGH: - store->data[B_FAN_SPD] = 0; - strcpy_P(s_speed,HIGH_P); - break; - case CMD_MED: - store->data[B_FAN_SPD] = 1; - strcpy_P(s_speed,MED_P); - break; - case CMD_LOW: - store->data[B_FAN_SPD] = 2; - strcpy_P(s_speed,LOW_P); - break; - case CMD_OFF: - store->inCheck=0; - store->data[B_POWER] = store->power; - store->data[B_POWER] &= ~1; - SendData(off, sizeof(off)/sizeof(byte)); - return 1; - default: - { - uint8_t speed = 0; - if (cmd.getInt()) speed = map(cmd.getInt(),1,255,1,3); - store->inCheck=0; - switch (speed) { - case 0: - store->data[B_POWER] = store->power; - store->data[B_POWER] &= ~1; - SendData(off, sizeof(off)/sizeof(byte)); - return 1; - case 1: - store->data[B_FAN_SPD] = 2; - strcpy_P(s_speed,LOW_P); - store->data[B_POWER] = store->power; - store->data[B_POWER] |= 1; - break; - case 2: - store->data[B_FAN_SPD] = 1; - strcpy_P(s_speed,MED_P); - store->data[B_POWER] = store->power; - store->data[B_POWER] |= 1; - break; - case 3: - store->data[B_FAN_SPD] = 0; - strcpy_P(s_speed,HIGH_P); - store->data[B_POWER] = store->power; - store->data[B_POWER] |= 1; - } - } - //TODO - mapping digits to speed - } + switch (cmd.getCmd()) + { + case CMD_AUTO: + store->data[B_FAN_SPD] = 3; + strcpy_P(s_speed,AUTO_P); + break; + case CMD_HIGH: + store->data[B_FAN_SPD] = 0; + strcpy_P(s_speed,HIGH_P); + break; + case CMD_MED: + store->data[B_FAN_SPD] = 1; + strcpy_P(s_speed,MED_P); + break; + case CMD_LOW: + store->data[B_FAN_SPD] = 2; + strcpy_P(s_speed,LOW_P); + break; + case CMD_OFF: + store->inCheck=0; + store->timestamp=millisNZ(); + store->data[B_POWER] = store->power; + store->data[B_POWER] &= ~1; + SendData(off, sizeof(off)/sizeof(byte)); + return 1; + default: + { + uint8_t speed = 0; + if (cmd.getInt()) speed = map(cmd.getInt(),1,255,1,3); + store->inCheck=0; + switch (speed) { + case 0: + store->data[B_POWER] = store->power; + store->data[B_POWER] &= ~1; + SendData(off, sizeof(off)/sizeof(byte)); + return 1; + case 1: + store->data[B_FAN_SPD] = 2; + strcpy_P(s_speed,LOW_P); + store->data[B_POWER] = store->power; + store->data[B_POWER] |= 1; + break; + case 2: + store->data[B_FAN_SPD] = 1; + strcpy_P(s_speed,MED_P); + store->data[B_POWER] = store->power; + store->data[B_POWER] |= 1; + break; + case 3: + store->data[B_FAN_SPD] = 0; + strcpy_P(s_speed,HIGH_P); + store->data[B_POWER] = store->power; + store->data[B_POWER] |= 1; + } + } + //TODO - mapping digits to speed + } publishTopic(item->itemArr->name,s_speed,"/fan"); break; diff --git a/lighthub/modules/out_modbus.cpp b/lighthub/modules/out_modbus.cpp index b57ed87..8c64118 100644 --- a/lighthub/modules/out_modbus.cpp +++ b/lighthub/modules/out_modbus.cpp @@ -60,45 +60,6 @@ const reg_t regSize_P[] PROGMEM = } ; #define regSizeNum sizeof(regSize_P)/sizeof(reg_t) -/* -const serial_t serialModes_P[] PROGMEM = -{ - { "8E1", (serialParamType) SERIAL_8E1},//(uint16_t) US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_EVEN }, - { "8N1", (serialParamType) SERIAL_8N1}, - { "8E2", (serialParamType) SERIAL_8E2}, - { "8N2", (serialParamType) SERIAL_8N2}, - { "8O1", (serialParamType) SERIAL_8O1}, - { "8O2", (serialParamType) SERIAL_8O2}, -// { "8M1", SERIAL_8M1}, -// { "8S1", SERIAL_8S1}, - { "7E1", (serialParamType) SERIAL_7E1},//(uint16_t) US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_EVEN }, - { "7N1", (serialParamType) SERIAL_7N1}, - { "7E2", (serialParamType) SERIAL_7E2}, - { "7N2", (serialParamType) SERIAL_7N2}, - { "7O1", (serialParamType) SERIAL_7O1}, - { "7O2", (serialParamType) SERIAL_7O2} -// { "7M1", SERIAL_7M1}, -// { "7S1", SERIAL_7S1} -} ; - -#define serialModesNum sizeof(serialModes_P)/sizeof(serial_t) - -serialParamType str2SerialParam(char * str) -{ debugSerial<"); - for(uint8_t i=0; i (SERIAL_8N1); -} -*/ uint16_t swap (uint16_t x) {return ((x & 0xff) << 8) | ((x & 0xff00) >> 8);} int str2regSize(char * str) @@ -223,6 +184,7 @@ switch (regType) { break; default: debugSerial<valueint,defMappingObj->valueint-registerFrom,regType,registerFrom,registerTo,false,&submitRecurrentOut); executeWithoutCheck=true; } - else errorSerial<valueint<valueint,regType,1)) + { + mappedParam = findRegister(defMappingObj->valueint,0,regType,defMappingObj->valueint,defMappingObj->valueint,false,&submitRecurrentOut); + executeWithoutCheck=true; + } + node.setDefaultResponseBuffer(); + } break; case aJson_String: // parameter name @@ -358,8 +332,6 @@ itemCmd out_Modbus::findRegister(uint16_t registerNum, uint16_t posInBuffer, uin aJsonObject *itemParObj = aJson.getObjectItem(itemParametersObj,defMappingObj->valuestring); if (itemParObj) { - //aJsonObject * markObj = execObj; - //if (execObj->type == aJson_Array) markObj = execObj->child; //Retrive previous data aJsonObject *lastMeasured = aJson.getObjectItem(itemParObj,"@S"); if (lastMeasured && lastMeasured->type ==aJson_Int) @@ -417,37 +389,7 @@ itemCmd out_Modbus::findRegister(uint16_t registerNum, uint16_t posInBuffer, uin break; } } -/* - aJsonObject *nextRegObj = NULL; - int registerType = 0; - - nextRegObj = aJson.getObjectItem(paramObj, "nextreg"); - if (nextRegObj) registerType=MODBUS_HOLDING_REG_TYPE; - else - { - nextRegObj = aJson.getObjectItem(paramObj, "nextir"); - if (nextRegObj) registerType=MODBUS_INPUT_REG_TYPE; - else - { - nextRegObj = aJson.getObjectItem(paramObj, "nextcoil"); - if (nextRegObj) registerType=MODBUS_COIL_REG_TYPE; - else - { - nextRegObj = aJson.getObjectItem(paramObj, "nextdin"); - if (nextRegObj) registerType=MODBUS_DISCRETE_REG_TYPE; - } - } - } - - if (registerType && nextRegObj && (nextRegObj->type) ==aJson_Int && (nextRegObj->valueint>= registerFrom) && (nextRegObj->valueint<=registerTo)) - { - debugSerial<valueint,nextRegObj->valueint-registerFrom,registerType,registerFrom,registerTo,false,&submitRecurrentOut); - executeWithoutCheck=true; - } - else errorSerial<valueint=param; + traceSerial<<"MBUS: Stored "<name<subtype&=~MB_VALUE_OUTDATED; } } @@ -495,7 +438,12 @@ itemCmd out_Modbus::findRegister(uint16_t registerNum, uint16_t posInBuffer, uin if (executeWithoutCheck) { - if (doExecution && (submitRecurrentOut || *submitParam)) executeCommand(execObj, -1, mappedParam); + if (doExecution && (submitRecurrentOut || *submitParam)) + { + executeCommand(execObj, -1, mappedParam); + *submitParam=true; //if requrrent check has submit smth - report it. + } + return mappedParam; } @@ -518,6 +466,7 @@ itemCmd out_Modbus::findRegister(uint16_t registerNum, uint16_t posInBuffer, uin } } } + //if (submitRecurrentOut) *submitParam=true; //if requrrent check has submit smth - report it. return mappedParam; } paramObj=paramObj->next; @@ -582,7 +531,7 @@ void out_Modbus::initLine() node.begin(item->getArg(0), modbusSerial); } -int out_Modbus::sendModbus(char * paramName, int32_t value, uint8_t regType) +int out_Modbus::sendModbus(char * paramName, aJsonObject * outValue) { if (!store) {errorSerial<subtype = PAR_COIL; } + if (regObj->type != aJson_Int) {errorSerial<type == aJson_String) regType=str2regSize(typeObj->valuestring); +if (prefetchObj && (prefetchObj->type == aJson_Boolean) && prefetchObj->valuebool) + { + int modbusRegType = (outValue->subtype == PAR_COIL) ? MODBUS_COIL_REG_TYPE:MODBUS_HOLDING_REG_TYPE; + debugSerial<valueint << " type:" << modbusRegType << " "; - switch(regType) { + /// to prevent CORRUPTIOIN if using same buffer + uint16_t localBuffer; + node.setResponseBuffer(&localBuffer,1); + + bool successRead = readModbus(regObj->valueint,modbusRegType,1); + + + if (successRead) + { + #ifdef PREFETCH_EXEC_IMMEDIALELLY + // option to execute if changed immediatelly + bool submited = false; + findRegister(regObj->valueint,0,modbusRegType,regObj->valueint,regObj->valueint,true,&submited); + node.setDefaultResponseBuffer(); + + if (submited) + { + debugSerial << F("MBUS:")<itemArg, 2); + if (itemParametersObj && itemParametersObj->type ==aJson_Object) + { + aJsonObject *execObj = aJson.getObjectItem(itemParametersObj,paramName); + if (execObj) + { + aJsonObject * markObj = execObj; + if (execObj->type == aJson_Array) markObj = execObj->child; + //Retrive previous data + lastMeasured = aJson.getObjectItem(markObj,"@S"); + if (lastMeasured) + { + if (lastMeasured->type == aJson_Int) + { + traceSerial<valueint<< F(" Now:") << localBuffer<valueint != localBuffer) + { + debugSerial << F("MBUS:")<subtype) { case PAR_U16: case PAR_I16: case PAR_TENS: case PAR_100: - res = node.writeSingleRegister(regObj->valueint,value); + res = node.writeSingleRegister(regObj->valueint,outValue->valueint); break; break; case PAR_I32: case PAR_U32: - res = node.writeSingleRegister(regObj->valueint,swap(value & 0xFFFF)); - res += node.writeSingleRegister(regObj->valueint+1,swap(value >> 16)) ; + res = node.writeSingleRegister(regObj->valueint,swap(outValue->valueint & 0xFFFF)); + res += node.writeSingleRegister(regObj->valueint+1,swap(outValue->valueint >> 16)) ; break; case PAR_U8L: case PAR_I8L: - res = node.writeSingleRegister(regObj->valueint,value & 0xFF); + res = node.writeSingleRegister(regObj->valueint,outValue->valueint & 0xFF); break; case PAR_U8H: case PAR_I8H: - res = node.writeSingleRegister(regObj->valueint,(value & 0xFFFF)>> 8); + res = node.writeSingleRegister(regObj->valueint,(outValue->valueint & 0xFFFF)>> 8); break; case PAR_COIL: - res = node.writeSingleCoil (regObj->valueint,value); + res = node.writeSingleCoil (regObj->valueint,outValue->valueint); break; } mbusSlenceTimer = millisNZ(); - debugSerial<valueint<valueint<valueint<type == aJson_Int) && lastMeasured && (lastMeasured->type == aJson_Int)) lastMeasured->valueint = outValue->valueint; + + return ( res == 0); } @@ -674,7 +696,7 @@ if (itemParametersObj && itemParametersObj->type ==aJson_Object) { savedValue = outValue->valueint; debugSerial<<"MBUS: SEND "<itemArr->name<<" "; - sendRes = sendModbus(execObj->name,outValue->valueint,outValue->subtype); + sendRes = sendModbus(execObj->name,outValue); } while (savedValue != outValue->valueint); //repeat sending if target value changed while we're waited for mbus responce @@ -683,6 +705,7 @@ if (itemParametersObj && itemParametersObj->type ==aJson_Object) case 1: //success execObj->subtype&=~ MB_NEED_SEND; onceSendOk=true; + if (outValue->type == aJson_Int) ///return 1; //relax break; @@ -692,6 +715,11 @@ if (itemParametersObj && itemParametersObj->type ==aJson_Object) if ((execObj->subtype & 3) != MB_SEND_ATTEMPTS) execObj->subtype++; errorSerial<<"Attempt: "<< (execObj->subtype & 3) <name<valueint= + execObj->subtype&=~ MB_NEED_SEND; + break; default: //param not found errorSerial<name<subtype&=~ MB_NEED_SEND; @@ -778,6 +806,7 @@ int8_t regType = PAR_I16; aJsonObject * typeObj = aJson.getObjectItem(templateParamObj, "type"); aJsonObject * mapObj = aJson.getObjectItem(templateParamObj, "map"); + if (typeObj && typeObj->type == aJson_String) regType=str2regSize(typeObj->valuestring); switch(regType) { case PAR_I16: @@ -811,20 +840,14 @@ if (itemParametersObj && itemParametersObj->type ==aJson_Object) aJsonObject *execObj = aJson.getObjectItem(itemParametersObj,suffixStr); if (execObj && ((execObj->type == aJson_Object) || (execObj->type == aJson_Array))) { - /* - aJsonObject *polledValue = aJson.getObjectItem(execObj,"@S"); - if (polledValue && polledValue->type == aJson_Int && (polledValue->valueint == Value)) - { - debugSerial<type == aJson_Array) markObj = execObj->child; + + //Schedule update execObj->subtype |= MB_NEED_SEND; - aJsonObject * markObj = execObj; - if (execObj->type == aJson_Array) markObj = execObj->child; - aJsonObject *outValue = aJson.getObjectItem(markObj,"@V"); if (outValue) // Existant. Preserve original @type { @@ -839,15 +862,17 @@ if (itemParametersObj && itemParametersObj->type ==aJson_Object) outValue = aJson.getObjectItem(markObj,"@V"); if (outValue) outValue->subtype =regType & 0xF; } - +/* Conflict with pre-fetching aJsonObject *polledValue = aJson.getObjectItem(markObj,"@S"); if (polledValue && outValue->type == aJson_Int) { + traceSerial<<"MBUS: not Stored "<itemArr->name<<":"<name<valueint=Value; //to pevent suppressing to change back to previously polled value if this occurs before next polling polledValue->subtype&=~MB_VALUE_OUTDATED; } + */ - } + } } return 1; diff --git a/lighthub/modules/out_modbus.h b/lighthub/modules/out_modbus.h index 34a8148..24272ad 100644 --- a/lighthub/modules/out_modbus.h +++ b/lighthub/modules/out_modbus.h @@ -47,7 +47,7 @@ protected: itemCmd findRegister(uint16_t registerNum, uint16_t posInBuffer, uint8_t regType, uint16_t registerFrom, uint16_t registerTo, bool doExecution = true, bool * submitParam = NULL); void pollModbus(aJsonObject * reg, int regType); void initLine(); - int sendModbus(char * paramName, int32_t value, uint8_t regType); + int sendModbus(char * paramName, aJsonObject * outValue); int sendItemCmd(aJsonObject *templateParamObj, itemCmd cmd); }; #endif diff --git a/lighthub/modules/out_pid.cpp b/lighthub/modules/out_pid.cpp index 58cda5b..a26d576 100644 --- a/lighthub/modules/out_pid.cpp +++ b/lighthub/modules/out_pid.cpp @@ -171,7 +171,7 @@ if (store && store->pid && (Status() == CST_INITIALIZED) && item && (item->getCm if (store->alarmArmed) debugSerial << F(" "); debugSerial<output-store->prevOut)>OUTPUT_TRESHOLD) || (item->getFlag(FLAG_ACTION_NEEDED))) && !store->alarmArmed) + if (( (NOT_FILTER_PID_OUT || (abs(store->output-store->prevOut)>OUTPUT_TRESHOLD)) || (item->getFlag(FLAG_ACTION_NEEDED))) && !store->alarmArmed) { aJsonObject * oCmd = aJson.getArrayItem(item->itemArg, 1); diff --git a/lighthub/options.h b/lighthub/options.h index 5dd4644..364205f 100644 --- a/lighthub/options.h +++ b/lighthub/options.h @@ -1,6 +1,10 @@ #pragma once #include +#ifndef NOT_FILTER_PID_OUT +#define NOT_FILTER_PID_OUT 1 +#endif + #define DHCP_ATTEMPTS_FALLBACK 3 #define TENS_FRACT_LEN 2 #define TENS_BASE 100 @@ -84,10 +88,17 @@ //#define T_ATTEMPTS 200 //#define IET_TEMP 0 //#define IET_ATTEMPTS 1 - +#ifndef THERMO_GIST_CELSIUS #define THERMO_GIST_CELSIUS 1. +#endif + +#ifndef THERMO_OVERHEAT_CELSIUS #define THERMO_OVERHEAT_CELSIUS 38. +#endif + +#ifndef FM_OVERHEAT_CELSIUS #define FM_OVERHEAT_CELSIUS 40. +#endif #ifndef MIN_VOLUME #define MIN_VOLUME 20