mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-12 01:24:04 +00:00
In a lot of places an object was allocated on the heap, but not always it was deleted thus leading to memory leaks.
208 lines
5.3 KiB
C++
208 lines
5.3 KiB
C++
#include "../DataStructs/Modbus.h"
|
|
|
|
#include "../DataStructs/ControllerSettingsStruct.h"
|
|
#include "../ESPEasyCore/ESPEasy_Log.h"
|
|
|
|
#ifdef USES_MODBUS
|
|
|
|
Modbus::Modbus() : ModbusClient(nullptr), errcnt(0), timeout(0),
|
|
TXRXstate(MODBUS_IDLE), RXavailable(0), payLoad(0) {}
|
|
|
|
Modbus::~Modbus() {
|
|
if (ModbusClient) {
|
|
ModbusClient->flush();
|
|
ModbusClient->stop();
|
|
delete (ModbusClient);
|
|
delay(1);
|
|
ModbusClient = nullptr;
|
|
}
|
|
}
|
|
|
|
bool Modbus::begin(uint8_t function, uint8_t ModbusID, uint16_t ModbusRegister, MODBUS_registerTypes_t type, char *IPaddress)
|
|
{
|
|
currentRegister = ModbusRegister;
|
|
currentFunction = function;
|
|
incomingValue = type;
|
|
resultReceived = false;
|
|
ModbusClient = new (std::nothrow) WiFiClient();
|
|
if (ModbusClient == nullptr) {
|
|
return false;
|
|
}
|
|
ModbusClient->setNoDelay(true);
|
|
ModbusClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT);
|
|
timeout = millis();
|
|
ModbusClient->flush();
|
|
|
|
if (ModbusClient->connected()) {
|
|
LogString += F(" already connected. ");
|
|
} else {
|
|
LogString += F("connect: "); LogString += IPaddress;
|
|
|
|
if (ModbusClient->connect(IPaddress, 502) != 1) {
|
|
LogString += F(" fail. ");
|
|
TXRXstate = MODBUS_IDLE;
|
|
errcnt++;
|
|
|
|
if (LogString.length() > 1) { addLog(LOG_LEVEL_DEBUG, LogString); }
|
|
return false;
|
|
}
|
|
}
|
|
LogString += F(" OK, sending read request: ");
|
|
|
|
sendBuffer[6] = ModbusID;
|
|
sendBuffer[7] = function;
|
|
sendBuffer[8] = (ModbusRegister >> 8);
|
|
sendBuffer[9] = (ModbusRegister & 0x00ff);
|
|
|
|
if ((incomingValue == signed64) || (incomingValue == unsigned64)) {
|
|
sendBuffer[11] = 4;
|
|
}
|
|
|
|
if ((incomingValue == signed32) || (incomingValue == unsigned32)) {
|
|
sendBuffer[11] = 2;
|
|
}
|
|
|
|
if ((incomingValue == signed16) || (incomingValue == unsigned16)) {
|
|
sendBuffer[11] = 1;
|
|
}
|
|
ModbusClient->flush();
|
|
ModbusClient->write(&sendBuffer[0], sizeof(sendBuffer));
|
|
|
|
for (unsigned int i = 0; i < sizeof(sendBuffer); i++) {
|
|
LogString += ((unsigned int)(sendBuffer[i]));
|
|
LogString += ' ';
|
|
}
|
|
TXRXstate = MODBUS_RECEIVE;
|
|
|
|
if (LogString.length() > 1) { addLog(LOG_LEVEL_DEBUG, LogString); }
|
|
return true;
|
|
}
|
|
|
|
bool Modbus::handle() {
|
|
unsigned int RXavailable = 0;
|
|
|
|
LogString = "";
|
|
int64_t rxValue = 0;
|
|
|
|
switch (TXRXstate) {
|
|
case MODBUS_IDLE:
|
|
|
|
// clean up;
|
|
if (ModbusClient) {
|
|
ModbusClient->flush();
|
|
ModbusClient->stop();
|
|
delete (ModbusClient);
|
|
delay(1);
|
|
ModbusClient = nullptr;
|
|
}
|
|
break;
|
|
|
|
case MODBUS_RECEIVE:
|
|
|
|
if (hasTimeout()) { break; }
|
|
|
|
if (ModbusClient->available() < 9) { break; }
|
|
|
|
LogString += F("reading bytes: ");
|
|
|
|
for (int a = 0; a < 9; a++) {
|
|
payLoad = ModbusClient->read();
|
|
LogString += (payLoad); LogString += ' ';
|
|
}
|
|
LogString += F("> ");
|
|
|
|
if (payLoad > 8) {
|
|
LogString += F("Payload too large !? ");
|
|
errcnt++;
|
|
TXRXstate = MODBUS_IDLE;
|
|
}
|
|
|
|
case MODBUS_RECEIVE_PAYLOAD:
|
|
|
|
if (hasTimeout()) { break; }
|
|
RXavailable = ModbusClient->available();
|
|
|
|
if (payLoad != RXavailable) {
|
|
TXRXstate = MODBUS_RECEIVE_PAYLOAD;
|
|
break;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < RXavailable; i++) {
|
|
rxValue = rxValue << 8;
|
|
char a = ModbusClient->read();
|
|
rxValue = rxValue | a;
|
|
LogString += static_cast<int>(a);
|
|
LogString += ' ';
|
|
}
|
|
|
|
switch (incomingValue) {
|
|
case signed16:
|
|
result = (int16_t)rxValue;
|
|
break;
|
|
case unsigned16:
|
|
result = (uint16_t)rxValue;
|
|
break;
|
|
case signed32:
|
|
result = (int32_t)rxValue;
|
|
break;
|
|
case unsigned32:
|
|
result = (uint32_t)rxValue;
|
|
break;
|
|
case signed64:
|
|
result = (int64_t)rxValue;
|
|
break;
|
|
case unsigned64:
|
|
result = (uint64_t)rxValue;
|
|
break;
|
|
}
|
|
|
|
LogString += "value: "; LogString += result;
|
|
|
|
// if ((systemTimePresent()) && (hour() == 0)) errcnt = 0;
|
|
|
|
TXRXstate = MODBUS_IDLE;
|
|
|
|
resultReceived = true;
|
|
break;
|
|
|
|
default:
|
|
LogString += F("default. ");
|
|
TXRXstate = MODBUS_IDLE;
|
|
break;
|
|
}
|
|
|
|
if (LogString.length() > 1) { addLog(LOG_LEVEL_DEBUG, LogString); }
|
|
return true;
|
|
}
|
|
|
|
bool Modbus::hasTimeout()
|
|
{
|
|
if ((millis() - timeout) > 10000) { // too many bytes or timeout
|
|
LogString += F("Modbus RX timeout. "); LogString += String(ModbusClient->available());
|
|
errcnt++;
|
|
TXRXstate = MODBUS_IDLE;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// tryread can be called in a round robin fashion. It will initiate a read if Modbus is idle and update the result once it is available.
|
|
// subsequent calls (if Modbus is busy etc. ) will return false and not update the result.
|
|
// Use to read multiple values non blocking in an re-entrant function. Not tested yet.
|
|
bool Modbus::tryRead(uint8_t ModbusID, uint16_t M_register, MODBUS_registerTypes_t type, char *IPaddress, double& result) {
|
|
if (isBusy()) { return false; // not done yet
|
|
}
|
|
|
|
if (available()) {
|
|
if ((currentFunction == MODBUS_FUNCTION_READ) && (currentRegister == M_register)) {
|
|
result = read(); // result belongs to this request.
|
|
return true;
|
|
}
|
|
} else {
|
|
begin(MODBUS_FUNCTION_READ, ModbusID, M_register, type, IPaddress); // idle and no result -> begin read request
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endif // USES_MODBUS
|