diff --git a/platformio.ini b/platformio.ini index ce9d468f..00fd3a7f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,19 +10,13 @@ lib_deps_external = [env] extra_scripts = pre:tools/prebuildscript.py -[common] -build_flags = - -L./src/modules/exec/EctoControlAdapter/lib - -lrsEctoControl - -I./src/modules/exec/EctoControlAdapter/lib - [env:esp8266_1mb_ota] lib_deps = ${common_env_data.lib_deps_external} ${env:esp8266_1mb_ota_fromitems.lib_deps} ESPAsyncUDP lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp8266_1mb_ota="esp8266_1mb_ota" framework = arduino board = nodemcuv2 @@ -45,7 +39,7 @@ lib_deps = ${env:esp8266_1mb_fromitems.lib_deps} ESPAsyncUDP lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp8266_1mb="esp8266_1mb" framework = arduino board = nodemcuv2 @@ -68,7 +62,7 @@ lib_deps = ${env:esp8285_1mb_ota_fromitems.lib_deps} ESPAsyncUDP lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp8266_1mb_ota="esp8266_1mb_ota" framework = arduino board = esp8285 @@ -91,7 +85,7 @@ lib_deps = ${env:esp8266_2mb_fromitems.lib_deps} ESPAsyncUDP lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp8266_2mb="esp8266_2mb" framework = arduino board = d1_wroom_02 @@ -114,7 +108,7 @@ lib_deps = ${env:esp8266_2mb_ota_fromitems.lib_deps} ESPAsyncUDP lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp8266_2mb_ota="esp8266_2mb_ota" framework = arduino board = d1_wroom_02 @@ -137,7 +131,7 @@ lib_deps = ${env:esp8285_1mb_fromitems.lib_deps} ESPAsyncUDP lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp8266_1mb="esp8266_1mb" framework = arduino board = esp8285 @@ -160,7 +154,7 @@ lib_deps = ${env:esp8266_4mb_fromitems.lib_deps} ESPAsyncUDP lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp8266_4mb="esp8266_4mb" framework = arduino board = nodemcuv2 @@ -184,7 +178,7 @@ lib_deps = ${env:esp8266_16mb_fromitems.lib_deps} ESPAsyncUDP lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp8266_16mb="esp8266_16mb" framework = arduino board = nodemcuv2 @@ -207,7 +201,7 @@ lib_deps = ${common_env_data.lib_deps_external} ${env:esp32_4mb_fromitems.lib_deps} lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp32_4mb="esp32_4mb" -Wl,--wrap=esp_panic_handler framework = arduino @@ -231,7 +225,7 @@ lib_deps = ${common_env_data.lib_deps_external} ${env:esp32_4mb3f_fromitems.lib_deps} lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp32_4mb3f="esp32_4mb3f" -Wl,--wrap=esp_panic_handler framework = arduino @@ -256,7 +250,7 @@ lib_deps = ${common_env_data.lib_deps_external} ${env:esp32cam_4mb_fromitems.lib_deps} lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp32cam_4mb="esp32cam_4mb" -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue @@ -282,7 +276,7 @@ lib_deps = ${common_env_data.lib_deps_external} ${env:esp32s2_4mb_fromitems.lib_deps} lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp32s2_4mb="esp32s2_4mb" -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=0 @@ -333,7 +327,7 @@ lib_deps = ${common_env_data.lib_deps_external} ${env:esp32s3_16mb_fromitems.lib_deps} lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp32s3_16mb="esp32s3_16mb" -Wl,--wrap=esp_panic_handler framework = arduino @@ -360,7 +354,7 @@ lib_deps = ${common_env_data.lib_deps_external} ${env:esp32_16mb_fromitems.lib_deps} lib_ignore = LT_WebSockets -build_flags = ${common.build_flags} +build_flags = -Desp32_16mb="esp32_16mb" -Wl,--wrap=esp_panic_handler framework = arduino diff --git a/src/modules/exec/EctoControlAdapter/EctoControlAdapter.cpp b/src/modules/exec/EctoControlAdapter/EctoControlAdapter.cpp index 27e7ca73..2069027f 100644 --- a/src/modules/exec/EctoControlAdapter/EctoControlAdapter.cpp +++ b/src/modules/exec/EctoControlAdapter/EctoControlAdapter.cpp @@ -3,7 +3,7 @@ #include #include -#include "rsEctoControl.h" +#include "lib/rsEctoControl.h" // class ModbusUart; Stream *_mbUART = nullptr; diff --git a/src/modules/exec/EctoControlAdapter/lib/ModbusEC.cpp b/src/modules/exec/EctoControlAdapter/lib/ModbusEC.cpp new file mode 100644 index 00000000..79af9485 --- /dev/null +++ b/src/modules/exec/EctoControlAdapter/lib/ModbusEC.cpp @@ -0,0 +1,517 @@ + +#include "ModbusEC.h" + +ModbusMaster::ModbusMaster(void) +{ + _idle = 0; + _preTransmission = 0; + _postTransmission = 0; +} + +/** +Initialize class object. + +Assigns the Modbus slave ID and serial port. +Call once class has been instantiated, typically within setup(). + +@param slave Modbus slave ID (1..255) +@param &serial reference to serial port object (Serial, Serial1, ... Serial3) +@ingroup setup +*/ +void ModbusMaster::begin(uint8_t slave, Stream &serial) +{ + // txBuffer = (uint16_t*) calloc(ku8MaxBufferSize, sizeof(uint16_t)); + _u8MBSlave = slave; + _serial = &serial; + _u8TransmitBufferIndex = 0; + u16TransmitBufferLength = 0; + +#if __MODBUSMASTER_DEBUG__ + pinMode(__MODBUSMASTER_DEBUG_PIN_A__, OUTPUT); + pinMode(__MODBUSMASTER_DEBUG_PIN_B__, OUTPUT); +#endif +} + +void ModbusMaster::beginTransmission(uint16_t u16Address) +{ + _u16WriteAddress = u16Address; + _u8TransmitBufferIndex = 0; + u16TransmitBufferLength = 0; +} + +// eliminate this function in favor of using existing MB request functions +uint8_t ModbusMaster::requestFrom(uint16_t address, uint16_t quantity) +{ + uint8_t read; + // clamp to buffer length + if (quantity > ku8MaxBufferSize) + { + quantity = ku8MaxBufferSize; + } + // set rx buffer iterator vars + _u8ResponseBufferIndex = 0; + _u8ResponseBufferLength = read; + + return read; +} + +void ModbusMaster::sendBit(bool data) +{ + uint8_t txBitIndex = u16TransmitBufferLength % 16; + if ((u16TransmitBufferLength >> 4) < ku8MaxBufferSize) + { + if (0 == txBitIndex) + { + _u16TransmitBuffer[_u8TransmitBufferIndex] = 0; + } + bitWrite(_u16TransmitBuffer[_u8TransmitBufferIndex], txBitIndex, data); + u16TransmitBufferLength++; + _u8TransmitBufferIndex = u16TransmitBufferLength >> 4; + } +} + +void ModbusMaster::send(uint16_t data) +{ + if (_u8TransmitBufferIndex < ku8MaxBufferSize) + { + _u16TransmitBuffer[_u8TransmitBufferIndex++] = data; + u16TransmitBufferLength = _u8TransmitBufferIndex << 4; + } +} + +void ModbusMaster::send(uint32_t data) +{ + send(lowWord(data)); + send(highWord(data)); +} + +void ModbusMaster::send(uint8_t data) +{ + send(word(data)); +} + +uint8_t ModbusMaster::available(void) +{ + return _u8ResponseBufferLength - _u8ResponseBufferIndex; +} + +uint16_t ModbusMaster::receive(void) +{ + if (_u8ResponseBufferIndex < _u8ResponseBufferLength) + { + return _u16ResponseBuffer[_u8ResponseBufferIndex++]; + } + else + { + return 0xFFFF; + } +} + +/** +Set idle time callback function (cooperative multitasking). + +This function gets called in the idle time between transmission of data +and response from slave. Do not call functions that read from the serial +buffer that is used by ModbusMaster. Use of i2c/TWI, 1-Wire, other +serial ports, etc. is permitted within callback function. + +@see ModbusMaster::ModbusMasterTransaction() +*/ +void ModbusMaster::idle(void (*idle)()) +{ + _idle = idle; +} + +/** +Set pre-transmission callback function. + +This function gets called just before a Modbus message is sent over serial. +Typical usage of this callback is to enable an RS485 transceiver's +Driver Enable pin, and optionally disable its Receiver Enable pin. + +@see ModbusMaster::ModbusMasterTransaction() +@see ModbusMaster::postTransmission() +*/ +void ModbusMaster::preTransmission(void (*preTransmission)()) +{ + _preTransmission = preTransmission; +} + +/** +Set post-transmission callback function. + +This function gets called after a Modbus message has finished sending +(i.e. after all data has been physically transmitted onto the serial +bus). + +Typical usage of this callback is to enable an RS485 transceiver's +Receiver Enable pin, and disable its Driver Enable pin. + +@see ModbusMaster::ModbusMasterTransaction() +@see ModbusMaster::preTransmission() +*/ +void ModbusMaster::postTransmission(void (*postTransmission)()) +{ + _postTransmission = postTransmission; +} + +/** +Retrieve data from response buffer. + +@see ModbusMaster::clearResponseBuffer() +@param u8Index index of response buffer array (0x00..0x3F) +@return value in position u8Index of response buffer (0x0000..0xFFFF) +@ingroup buffer +*/ +uint16_t ModbusMaster::getResponseBuffer(uint8_t u8Index) +{ + if (u8Index < ku8MaxBufferSize) + { + return _u16ResponseBuffer[u8Index]; + } + else + { + return 0xFFFF; + } +} + +/** +Clear Modbus response buffer. + +@see ModbusMaster::getResponseBuffer(uint8_t u8Index) +@ingroup buffer +*/ +void ModbusMaster::clearResponseBuffer() +{ + uint8_t i; + + for (i = 0; i < ku8MaxBufferSize; i++) + { + _u16ResponseBuffer[i] = 0; + } +} + +/** +Place data in transmit buffer. + +@see ModbusMaster::clearTransmitBuffer() +@param u8Index index of transmit buffer array (0x00..0x3F) +@param u16Value value to place in position u8Index of transmit buffer (0x0000..0xFFFF) +@return 0 on success; exception number on failure +@ingroup buffer +*/ +uint8_t ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value) +{ + if (u8Index < ku8MaxBufferSize) + { + _u16TransmitBuffer[u8Index] = u16Value; + return ku8MBSuccess; + } + else + { + return ku8MBIllegalDataAddress; + } +} + +/** +Clear Modbus transmit buffer. + +@see ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value) +@ingroup buffer +*/ +void ModbusMaster::clearTransmitBuffer() +{ + uint8_t i; + + for (i = 0; i < ku8MaxBufferSize; i++) + { + _u16TransmitBuffer[i] = 0; + } +} + +/** +Modbus function 0x03 Read Holding Registers. + +This function code is used to read the contents of a contiguous block of +holding registers in a remote device. The request specifies the starting +register address and the number of registers. Registers are addressed +starting at zero. + +The register data in the response buffer is packed as one word per +register. + +@param u16ReadAddress address of the first holding register (0x0000..0xFFFF) +@param u16ReadQty quantity of holding registers to read (1..125, enforced by remote device) +@return 0 on success; exception number on failure +@ingroup register +*/ +uint8_t ModbusMaster::readHoldingRegisters(uint16_t u16ReadAddress, + uint16_t u16ReadQty) +{ + _u16ReadAddress = u16ReadAddress; + _u16ReadQty = u16ReadQty; + return ModbusMasterTransaction(ku8MBReadHoldingRegisters); +} + +/** +Modbus function 0x10 Write Multiple Registers. + +This function code is used to write a block of contiguous registers (1 +to 123 registers) in a remote device. + +The requested written values are specified in the transmit buffer. Data +is packed as one word per register. + +@param u16WriteAddress address of the holding register (0x0000..0xFFFF) +@param u16WriteQty quantity of holding registers to write (1..123, enforced by remote device) +@return 0 on success; exception number on failure +@ingroup register +*/ +uint8_t ModbusMaster::writeMultipleRegisters(uint16_t u16WriteAddress, + uint16_t u16WriteQty) +{ + _u16WriteAddress = u16WriteAddress; + _u16WriteQty = u16WriteQty; + return ModbusMasterTransaction(ku8MBWriteMultipleRegisters); +} + +// new version based on Wire.h +uint8_t ModbusMaster::writeMultipleRegisters() +{ + _u16WriteQty = _u8TransmitBufferIndex; + return ModbusMasterTransaction(ku8MBWriteMultipleRegisters); +} + +uint8_t ModbusMaster::readAddresEctoControl() +{ + _u16ReadAddress = 0x00; + _u16ReadQty = 1; + return ModbusMasterTransaction(ku8MBProgRead46); +} +uint8_t ModbusMaster::writeAddresEctoControl(uint8_t addr) +{ + _u16WriteAddress = 0x00; + _u16WriteQty = 1; + _u16TransmitBuffer[0] = (uint16_t)addr; + return ModbusMasterTransaction(ku8MBProgWrite47); +} + +/* _____PRIVATE FUNCTIONS____________________________________________________ */ +/** +Modbus transaction engine. +Sequence: + - assemble Modbus Request Application Data Unit (ADU), + based on particular function called + - transmit request over selected serial port + - wait for/retrieve response + - evaluate/disassemble response + - return status (success/exception) + +@param u8MBFunction Modbus function (0x01..0xFF) +@return 0 on success; exception number on failure +*/ +uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) +{ + uint8_t u8ModbusADU[24]; + uint8_t u8ModbusADUSize = 0; + uint8_t i, u8Qty; + uint16_t u16CRC; + uint32_t u32StartTime; + uint8_t u8BytesLeft = 8; + uint8_t u8MBStatus = ku8MBSuccess; + + // assemble Modbus Request Application Data Unit + if (u8MBFunction == ku8MBProgRead46 || u8MBFunction == ku8MBProgWrite47) + { + u8ModbusADU[u8ModbusADUSize++] = 0x00; + u8ModbusADU[u8ModbusADUSize++] = u8MBFunction; + } + else + { + u8ModbusADU[u8ModbusADUSize++] = _u8MBSlave; + u8ModbusADU[u8ModbusADUSize++] = u8MBFunction; + } + + switch (u8MBFunction) + { + case ku8MBReadHoldingRegisters: + u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadAddress); + u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadAddress); + u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadQty); + u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadQty); + break; + } + + switch (u8MBFunction) + { + case ku8MBWriteMultipleRegisters: + u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteAddress); + u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteAddress); + break; + } + + switch (u8MBFunction) + { + case ku8MBWriteMultipleRegisters: + u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty); + u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty); + u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty << 1); + + for (i = 0; i < lowByte(_u16WriteQty); i++) + { + u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i]); + u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i]); + } + break; + } + + // append CRC + u16CRC = 0xFFFF; + for (i = 0; i < u8ModbusADUSize; i++) + { + u16CRC = crc16_update(u16CRC, u8ModbusADU[i]); + } + u8ModbusADU[u8ModbusADUSize++] = lowByte(u16CRC); + u8ModbusADU[u8ModbusADUSize++] = highByte(u16CRC); + u8ModbusADU[u8ModbusADUSize] = 0; + + // flush receive buffer before transmitting request + while (_serial->read() != -1) + ; + + // transmit request + if (_preTransmission) + { + _preTransmission(); + } + for (i = 0; i < u8ModbusADUSize; i++) + { + _serial->write(u8ModbusADU[i]); + } + + u8ModbusADUSize = 0; + _serial->flush(); // flush transmit buffer + if (_postTransmission) + { + _postTransmission(); + } + + // loop until we run out of time or bytes, or an error occurs + u32StartTime = millis(); + while (u8BytesLeft && !u8MBStatus) + { + if (_serial->available()) + { +#if __MODBUSMASTER_DEBUG__ + digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, true); +#endif + u8ModbusADU[u8ModbusADUSize++] = _serial->read(); + u8BytesLeft--; +#if __MODBUSMASTER_DEBUG__ + digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, false); +#endif + } + else + { +#if __MODBUSMASTER_DEBUG__ + digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, true); +#endif + if (_idle) + { + _idle(); + } +#if __MODBUSMASTER_DEBUG__ + digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, false); +#endif + } + + // evaluate slave ID, function code once enough bytes have been read + if (u8ModbusADUSize == 5) + { + // verify response is for correct Modbus slave + if (u8ModbusADU[0] != _u8MBSlave || u8ModbusADU[0] != 0x00) + { + u8MBStatus = ku8MBInvalidSlaveID; + break; + } + + // verify response is for correct Modbus function code (mask exception bit 7) + if ((u8ModbusADU[1] & 0x7F) != u8MBFunction) + { + u8MBStatus = ku8MBInvalidFunction; + break; + } + + // check whether Modbus exception occurred; return Modbus Exception Code + if (bitRead(u8ModbusADU[1], 7)) + { + u8MBStatus = u8ModbusADU[2]; + break; + } + + // evaluate returned Modbus function code + switch (u8ModbusADU[1]) + { + case ku8MBReadHoldingRegisters: + u8BytesLeft = u8ModbusADU[2]; + break; + + case ku8MBWriteMultipleRegisters: + u8BytesLeft = 3; + break; + } + } + if ((millis() - u32StartTime) > ku16MBResponseTimeout) + { + u8MBStatus = ku8MBResponseTimedOut; + } + } + + // verify response is large enough to inspect further + if (!u8MBStatus && u8ModbusADUSize >= 5) + { + // calculate CRC + u16CRC = 0xFFFF; + for (i = 0; i < (u8ModbusADUSize - 2); i++) + { + u16CRC = crc16_update(u16CRC, u8ModbusADU[i]); + } + + // verify CRC + if (!u8MBStatus && (lowByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 2] || + highByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 1])) + { + u8MBStatus = ku8MBInvalidCRC; + } + } + + // disassemble ADU into words + if (!u8MBStatus) + { + // evaluate returned Modbus function code + switch (u8ModbusADU[1]) + { + case ku8MBReadHoldingRegisters: + // load bytes into word; response bytes are ordered H, L, H, L, ... + for (i = 0; i < (u8ModbusADU[2] >> 1); i++) + { + if (i < ku8MaxBufferSize) + { + _u16ResponseBuffer[i] = word(u8ModbusADU[2 * i + 3], u8ModbusADU[2 * i + 4]); + } + + _u8ResponseBufferLength = i; + } + break; + case ku8MBProgRead46: + _u16ResponseBuffer[0] = (uint16_t)u8ModbusADU[2]; + _u8ResponseBufferLength = 1; + break; + } + } + + _u8TransmitBufferIndex = 0; + u16TransmitBufferLength = 0; + _u8ResponseBufferIndex = 0; + return u8MBStatus; +} diff --git a/src/modules/exec/EctoControlAdapter/lib/rsEctoControl.cpp b/src/modules/exec/EctoControlAdapter/lib/rsEctoControl.cpp new file mode 100644 index 00000000..431f6322 --- /dev/null +++ b/src/modules/exec/EctoControlAdapter/lib/rsEctoControl.cpp @@ -0,0 +1,633 @@ +#include +#include +#ifdef ESP32 +#include +#endif +#include "rsEctoControl.h" + +struct IoTValue +{ + float valD = 0; + String valS = ""; + + bool isDecimal = true; +}; +uint8_t _DIR_PIN_ = 0; + +void modbusPreTransmission() +{ + // delay(500); + if (_DIR_PIN_) + digitalWrite(_DIR_PIN_, HIGH); +} + +// Pin 4 made low for Modbus receive mode +// Контакт 4 установлен на низком уровне для режима приема Modbus +void modbusPostTransmission() +{ + if (_DIR_PIN_) + digitalWrite(_DIR_PIN_, LOW); + // delay(500); +} + +//======================================================================================================= +// setup() +RsEctoControl::RsEctoControl(String parameters) +{ + node.preTransmission(modbusPreTransmission); + node.postTransmission(modbusPostTransmission); + + if (parameters != "") + { + DynamicJsonDocument doc(4096); + DeserializationError error = deserializeJson(doc, parameters); + if (!error) + { + if (doc.containsKey("DIR_PIN")) + _DIR_PIN = doc["DIR_PIN"].as(); + if (doc.containsKey("addr")) + _addr = doc["addr"].as(); + if (doc.containsKey("debug")) + _debug = doc["debug"].as(); + } + } + if (_DIR_PIN) + { + _DIR_PIN_ = _DIR_PIN; + pinMode(_DIR_PIN, OUTPUT); + digitalWrite(_DIR_PIN, LOW); + } + + // 0x14 – адаптер OpenTherm (вторая версия) + // 0x15 – адаптер eBus + // 0x16 – адаптер Navien + uint16_t type; + readFunctionModBus(0x0000, type); + if (0x14 != (uint8_t)type || 0x15 != (uint8_t)type || 0x16 != (uint8_t)type) + { + _SerialPrint("E", "EctoControlAdapter", "Не подходящее устройство, type: " + String(type, HEX)); + } + + uint8_t addr = node.readAddresEctoControl(); + _SerialPrint("I", "EctoControlAdapter", "readAddresEctoControl, addr: " + String(addr, HEX) + " - Enter to configuration"); + + getModelVersion(); + getBoilerInfo(); + getBoilerStatus(); +} + +void RsEctoControl::begin(uint8_t slave, Stream &serial) +{ + _modbusUART = &serial; + node.begin(slave, serial); +} + +void RsEctoControl::initFunction(void (*publishData)(String, String), void (*sendTelegramm)(String), void (*SerialPrint)(const String &, const String &, const String &)) +{ //, const String & + _publishData = publishData; + _sendTelegramm = sendTelegramm; + _SerialPrint = SerialPrint; +}; + +void RsEctoControl::doByInterval() +{ + // readBoilerInfo(); + getBoilerStatus(); + + getCodeError(); + getCodeErrorExt(); + if (info.adapterType == 0) + getFlagErrorOT(); + // getFlowRate(); + // getMaxSetCH(); + // getMaxSetDHW(); + // getMinSetCH(); + // getMinSetDHW(); + getModLevel(); + getPressure(); + getTempCH(); + getTempDHW(); + getTempOutside(); +} + +// Основной цикл программы +void RsEctoControl::loop(bool isNetworkActive, bool mqttIsConnect) +{ + _isNetworkActive = isNetworkActive; + _mqttIsConnect = mqttIsConnect; +} + +// Исполнительные комманды +void RsEctoControl::execute(String command, std::vector ¶m) +{ +} + +bool RsEctoControl::writeFunctionModBus(const uint16_t ®, uint16_t &data) +{ + // set word 0 of TX buffer to least-significant word of counter (bits 15..0) + node.setTransmitBuffer(1, lowWord(data)); + // set word 1 of TX buffer to most-significant word of counter (bits 31..16) + node.setTransmitBuffer(0, highWord(data)); + // slave: write TX buffer to (2) 16-bit registers starting at register 0 + uint8_t result = node.writeMultipleRegisters(0, 2); + if (_debug > 2) + { + _SerialPrint("I", "EctoControlAdapter", "writeSingleRegister, addr: " + String((uint8_t)_addr, HEX) + ", reg: " + String(reg, HEX) + ", state: " + String(data) + " = result: " + String(result, HEX)); + } +} + +bool RsEctoControl::readFunctionModBus(const uint16_t ®, uint16_t &reading) +{ + // float retValue = 0; + if (_modbusUART) + { + // if (!addr) + // addr = _addr; + node.begin(_addr, (Stream &)*_modbusUART); + uint8_t result; + // uint32_t reading; + + if (reg == 0x0000) + result = node.readHoldingRegisters(reg, 4); + else + result = node.readHoldingRegisters(reg, 1); + if (_debug > 2) + _SerialPrint("I", "EctoControlAdapter", "readHoldingRegisters, addr: " + String(_addr, HEX) + ", reg: " + String(reg, HEX) + " = result: " + String(result, HEX)); + // break; + if (result == node.ku8MBSuccess) + { + if (reg == 0x0000) + { + reading = node.getResponseBuffer(0x03); + reading = (uint16_t)((uint8_t)(reading) >> 8); + _SerialPrint("I", "EctoControlAdapter", "read info, addr: " + String(_addr, HEX) + ", type: " + String(reading, HEX)); + } + else + { + reading = node.getResponseBuffer(0x00); + if (_debug > 2) + _SerialPrint("I", "EctoControlAdapter", "Success, Received data, register: " + String(reg) + " = " + String(reading, HEX)); + } + node.clearResponseBuffer(); + } + else + { + if (_debug > 2) + _SerialPrint("E", "EctoControlAdapter", "Failed, Response Code: " + String(result, HEX)); + return false; + } + + if (reading != 0x7FFF) + return true; + else + return false; + } + return false; +} + +bool RsEctoControl::getModelVersion() +{ + uint16_t reqData; + info.boilerMemberCode = readFunctionModBus(ReadDataEctoControl::ecR_MemberCode, info.boilerMemberCode); + info.boilerModelCode = readFunctionModBus(ReadDataEctoControl::ecR_ModelCode, info.boilerModelCode); + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_AdaperVersion, reqData); + info.adapterHardVer = highByte(reqData); + info.adapterSoftVer = lowByte(reqData); + return ret; +} + +bool RsEctoControl::getBoilerInfo() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_AdapterInfo, reqData); + info.adapterType = highByte(reqData) & 0xF8; + info.boilerStatus = (highByte(reqData) >> 3) & 1u; + info.rebootStatus = lowByte(reqData); + if (ret) + { + _publishData("adapterType", String(info.adapterType)); + _publishData("boilerStatus", String(info.boilerStatus)); + _publishData("rebootStatus", String(info.rebootStatus)); + } + return ret; +} +bool RsEctoControl::getBoilerStatus() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_BoilerStatus, reqData); + status.burnStatus = (lowByte(reqData) >> 0) & 1u; + status.CHStatus = (lowByte(reqData) >> 1) & 1u; + status.DHWStatus = (lowByte(reqData) >> 2) & 1u; + if (ret) + { + _publishData("burnStatus", String(status.burnStatus)); + _publishData("CHStatus", String(status.CHStatus)); + _publishData("DHWStatus", String(status.DHWStatus)); + } + return ret; +} +bool RsEctoControl::getCodeError() +{ + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_CodeError, code); + if (ret) + { + _publishData("codeError", String(code)); + if (codeExt) + _sendTelegramm("EctoControlAdapter: код ошибки: " + String((int)codeExt)); + } + return ret; +} +bool RsEctoControl::getCodeErrorExt() +{ + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_CodeErrorExt, codeExt); + if (ret) + { + _publishData("codeErrorExt", String(codeExt)); + if (codeExt) + _sendTelegramm("EctoControlAdapter: код ошибки: " + String((int)codeExt)); + } + return ret; +} +bool RsEctoControl::getFlagErrorOT() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_FlagErrorOT, reqData); + flagErr = lowByte(reqData); + if (ret) + { + _publishData("flagErr", String(flagErr)); + switch (flagErr) + { + case 0: + _sendTelegramm("EctoControlAdapter: Необходимо обслуживание!"); + break; + case 1: + _sendTelegramm("EctoControlAdapter: Котёл заблокирован!"); + break; + case 2: + _sendTelegramm("EctoControlAdapter: Низкое давление в отопительном контуре!"); + break; + case 3: + _sendTelegramm("EctoControlAdapter: Ошибка розжига!"); + break; + case 4: + _sendTelegramm("EctoControlAdapter: Низкое давления воздуха!"); + break; + case 5: + _sendTelegramm("EctoControlAdapter: Перегрев теплоносителя в контуре!"); + break; + default: + break; + } + } + return ret; +} + +bool RsEctoControl::getFlowRate() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_FlowRate, reqData); + flow = lowByte(reqData) / 10.f; + if (ret) + _publishData("flowRate", String(flow)); + return ret; +} +bool RsEctoControl::getMaxSetCH() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_MaxSetCH, reqData); + maxSetCH = (float)lowByte(reqData); + if (ret) + _publishData("maxSetCH", String(maxSetCH)); + return ret; +} +bool RsEctoControl::getMaxSetDHW() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_MaxSetDHW, reqData); + maxSetDHW = (float)lowByte(reqData); + if (ret) + _publishData("maxSetDHW", String(maxSetDHW)); + return ret; +} +bool RsEctoControl::getMinSetCH() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_MinSetCH, reqData); + minSetCH = (float)lowByte(reqData); + if (ret) + _publishData("minSetCH", String(minSetCH)); + return ret; +} +bool RsEctoControl::getMinSetDHW() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_MinSetDHW, reqData); + minSetDHW = (float)lowByte(reqData); + if (ret) + _publishData("minSetDHW", String(minSetDHW)); + return ret; +} +bool RsEctoControl::getModLevel() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_ModLevel, reqData); + modLevel = (float)lowByte(reqData); + if (ret) + _publishData("modLevel", String(modLevel)); + return ret; +} +bool RsEctoControl::getPressure() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_Pressure, reqData); + press = lowByte(reqData) / 10.f; + if (ret) + _publishData("press", String(press)); + return ret; +} +bool RsEctoControl::getTempCH() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_TempCH, reqData); + tCH = reqData / 10.f; + if (ret) + _publishData("tCH", String(tCH)); + return ret; +} +bool RsEctoControl::getTempDHW() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_TempDHW, reqData); + tDHW = reqData / 10.f; + if (ret) + _publishData("tDHW", String(tDHW)); + return ret; +} +bool RsEctoControl::getTempOutside() +{ + uint16_t reqData; + bool ret = readFunctionModBus(ReadDataEctoControl::ecR_TempOutside, reqData); + tOut = (float)lowByte(reqData); + if (ret) + _publishData("tOut", String(tOut)); + return ret; +} + +bool RsEctoControl::setTypeConnect(float &data) +{ + bool ret = false; + if (writeFunctionModBus(ecW_SetTypeConnect, (uint16_t &)data)) + { + // TODO запросить результат записи у адаптера + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setTypeConnect"); + } + return ret; +} +bool RsEctoControl::setTCH(float &data) +{ + bool ret = false; + uint16_t d16 = data * 10; + if (writeFunctionModBus(ecW_TSetCH, d16)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setTCH"); + } + + return ret; +} +bool RsEctoControl::setTCHFaultConn(float &data) +{ + bool ret = false; + uint16_t d16 = data * 10; + if (writeFunctionModBus(ecW_TSetCHFaultConn, d16)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setTCHFaultConn"); + } + return ret; +} +bool RsEctoControl::setMinCH(float &data) +{ + bool ret = false; + if (writeFunctionModBus(ecW_TSetMinCH, (uint16_t &)data)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setMinCH"); + } + return ret; +} +bool RsEctoControl::setMaxCH(float &data) +{ + bool ret = false; + if (writeFunctionModBus(ecW_TSetMaxCH, (uint16_t &)data)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setMaxCH"); + } + return ret; +} +bool RsEctoControl::setMinDHW(float &data) +{ + bool ret = false; + if (writeFunctionModBus(ecW_TSetMinDHW, (uint16_t &)data)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setMinDHW"); + } + return ret; +} +bool RsEctoControl::setMaxDHW(float &data) +{ + bool ret = false; + if (writeFunctionModBus(ecW_TSetMaxDHW, (uint16_t &)data)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setMaxDHW"); + } +} +bool RsEctoControl::setTDHW(float &data) +{ + bool ret = false; + if (writeFunctionModBus(ecW_TSetDHW, (uint16_t &)data)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setTDHW"); + } + return ret; +} +bool RsEctoControl::setMaxModLevel(float &data) +{ + bool ret = false; + if (writeFunctionModBus(ecW_SetMaxModLevel, (uint16_t &)data)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setMaxModLevel"); + } + return ret; +} + +bool RsEctoControl::setStatusCH(bool data) +{ + bool ret = false; + enableCH = data; + uint16_t stat = enableCH | (enableDHW << 1) | (enableCH2 << 2); + if (writeFunctionModBus(ecW_SetStatusBoiler, stat)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setStatusCH"); + } + return ret; +} +bool RsEctoControl::setStatusDHW(bool data) +{ + bool ret = false; + enableDHW = data; + uint16_t stat = enableCH | (enableDHW << 1) | (enableCH2 << 2); + if (writeFunctionModBus(ecW_SetStatusBoiler, stat)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setStatusDHW"); + } + return ret; +} +bool RsEctoControl::setStatusCH2(bool data) +{ + bool ret = false; + enableCH2 = data; + uint16_t stat = enableCH | (enableDHW << 1) | (enableCH2 << 2); + if (writeFunctionModBus(ecW_SetStatusBoiler, stat)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, setStatusCH2"); + } + return ret; +} + +bool RsEctoControl::lockOutReset() +{ + bool ret = false; + uint16_t d16 = comm_LockOutReset; + if (writeFunctionModBus(ecW_Command, d16)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, lockOutReset"); + } + return ret; +} +bool RsEctoControl::rebootAdapter() +{ + bool ret = false; + uint16_t d16 = comm_RebootAdapter; + if (writeFunctionModBus(ecW_Command, d16)) + { + ret = true; + } + else + { + if (_debug > 1) + _SerialPrint("E", "EctoControlAdapter", "Failed, rebootAdapter"); + } + return ret; +} + +const String RsEctoControl::getChipId() +{ + return String(ESP_getChipId()) + "-" + String(getFlashChipIdNew()); // + "v" + String(FIRMWARE_VERSION); +} + +uint32_t RsEctoControl::getFlashChipIdNew() +{ + // Cache since size does not change + static uint32_t flashChipId = 0; + + if (flashChipId == 0) + { +#ifdef ESP32 + uint32_t tmp = g_rom_flashchip.device_id; + + for (int i = 0; i < 3; ++i) + { + flashChipId = flashChipId << 8; + flashChipId |= (tmp & 0xFF); + tmp = tmp >> 8; + } + + // esp_flash_read_id(nullptr, &flashChipId); +#elif defined(ESP8266) + flashChipId = ESP.getFlashChipId(); +#endif // ifdef ESP32 + } + return flashChipId; +} + +uint32_t RsEctoControl::ESP_getChipId(void) +{ +#ifdef ESP32 + uint32_t id = 0; + for (uint32_t i = 0; i < 17; i = i + 8) + { + id |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; + } + return id; +#else + return ESP.getChipId(); +#endif +} + +RsEctoControl::~RsEctoControl() +{ +} diff --git a/src/modules/exec/EctoControlAdapter/lib/rsEctoControl.h b/src/modules/exec/EctoControlAdapter/lib/rsEctoControl.h index 91ce2d93..edd88518 100644 --- a/src/modules/exec/EctoControlAdapter/lib/rsEctoControl.h +++ b/src/modules/exec/EctoControlAdapter/lib/rsEctoControl.h @@ -19,7 +19,6 @@ class RsEctoControl //: public ModbusMaster { private: ModbusMaster node; - String _license; uint8_t _addr; uint8_t _debug; Stream *_modbusUART; @@ -45,8 +44,6 @@ private: bool enableCH2; bool _isNetworkActive; bool _mqttIsConnect; - int md = 0; - int md2 = 0; uint32_t getFlashChipIdNew();