mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-03-26 22:22:16 +03:00
open src EctoControl
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
#include <map>
|
||||
#include <HardwareSerial.h>
|
||||
|
||||
#include "rsEctoControl.h"
|
||||
#include "lib/rsEctoControl.h"
|
||||
|
||||
// class ModbusUart;
|
||||
Stream *_mbUART = nullptr;
|
||||
|
||||
517
src/modules/exec/EctoControlAdapter/lib/ModbusEC.cpp
Normal file
517
src/modules/exec/EctoControlAdapter/lib/ModbusEC.cpp
Normal file
@@ -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;
|
||||
}
|
||||
633
src/modules/exec/EctoControlAdapter/lib/rsEctoControl.cpp
Normal file
633
src/modules/exec/EctoControlAdapter/lib/rsEctoControl.cpp
Normal file
@@ -0,0 +1,633 @@
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#ifdef ESP32
|
||||
#include <rom/spi_flash.h>
|
||||
#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<int>();
|
||||
if (doc.containsKey("addr"))
|
||||
_addr = doc["addr"].as<int>();
|
||||
if (doc.containsKey("debug"))
|
||||
_debug = doc["debug"].as<int>();
|
||||
}
|
||||
}
|
||||
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<IoTValue> ¶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()
|
||||
{
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user