mirror of https://github.com/LaskaKit/ESPlan.git
159 lines
4.1 KiB
C++
Executable File
159 lines
4.1 KiB
C++
Executable File
/*
|
||
* mb_client.cpp – Implementace Modbus RTU klienta
|
||
*/
|
||
|
||
#include "mb_client.h"
|
||
|
||
uint16_t ModbusClient::crc16(const uint8_t* data, uint8_t len) {
|
||
uint16_t crc = 0xFFFF;
|
||
for (uint8_t i = 0; i < len; i++) {
|
||
crc ^= (uint16_t)data[i];
|
||
for (uint8_t b = 0; b < 8; b++) {
|
||
if (crc & 0x0001) crc = (crc >> 1) ^ 0xA001;
|
||
else crc >>= 1;
|
||
}
|
||
}
|
||
return crc;
|
||
}
|
||
|
||
void ModbusClient::flushRx() {
|
||
while (_serial->available()) _serial->read();
|
||
}
|
||
|
||
void ModbusClient::begin(uint32_t baud, uint8_t /*addr*/) {
|
||
_baud = baud;
|
||
_serial = &Serial2;
|
||
_serial->begin(_baud, SERIAL_8N1, PIN_RS485_RX, PIN_RS485_TX);
|
||
Serial.printf("[MOD] RS485 init: %d baud, 8N1, RX=%d TX=%d\n",
|
||
_baud, PIN_RS485_RX, PIN_RS485_TX);
|
||
}
|
||
|
||
void ModbusClient::setBaudRate(uint32_t baud) {
|
||
_baud = baud;
|
||
_serial->end();
|
||
_serial->begin(_baud, SERIAL_8N1, PIN_RS485_RX, PIN_RS485_TX);
|
||
Serial.printf("[MOD] Baudrate zmenen na: %d\n", _baud);
|
||
}
|
||
|
||
ModbusStatus ModbusClient::readRegisters(uint8_t slaveAddr, uint16_t startReg,
|
||
uint8_t count, uint16_t* out) {
|
||
if (!_serial || count == 0 || count > 64) return ModbusStatus::FRAME_ERROR;
|
||
|
||
uint8_t req[8];
|
||
req[0] = slaveAddr;
|
||
req[1] = 0x03;
|
||
req[2] = (startReg >> 8) & 0xFF;
|
||
req[3] = startReg & 0xFF;
|
||
req[4] = 0x00;
|
||
req[5] = count;
|
||
uint16_t c = crc16(req, 6);
|
||
req[6] = c & 0xFF;
|
||
req[7] = (c >> 8) & 0xFF;
|
||
|
||
memcpy(lastTx, req, 8);
|
||
lastTxLen = 8;
|
||
|
||
flushRx();
|
||
_serial->write(req, 8);
|
||
_serial->flush();
|
||
|
||
uint8_t expectedLen = 3 + count * 2 + 2;
|
||
uint8_t buf[160];
|
||
uint8_t rxLen = 0;
|
||
uint32_t t0 = millis();
|
||
|
||
while (rxLen < expectedLen && (millis() - t0 < TIMEOUT_MS)) {
|
||
if (_serial->available()) buf[rxLen++] = _serial->read();
|
||
yield();
|
||
}
|
||
|
||
uint8_t copyLen = (rxLen < sizeof(lastRx)) ? rxLen : sizeof(lastRx);
|
||
memcpy(lastRx, buf, copyLen);
|
||
lastRxLen = copyLen;
|
||
|
||
if (rxLen < expectedLen) {
|
||
lastResult = ModbusStatus::TIMEOUT;
|
||
return ModbusStatus::TIMEOUT;
|
||
}
|
||
|
||
uint16_t rxCrc = (uint16_t)buf[rxLen-1] << 8 | buf[rxLen-2];
|
||
uint16_t calcCrc = crc16(buf, rxLen - 2);
|
||
if (rxCrc != calcCrc) {
|
||
lastResult = ModbusStatus::CRC_ERROR;
|
||
return ModbusStatus::CRC_ERROR;
|
||
}
|
||
|
||
if (buf[1] & 0x80) {
|
||
lastResult = ModbusStatus::EXCEPTION;
|
||
return ModbusStatus::EXCEPTION;
|
||
}
|
||
|
||
lastResult = ModbusStatus::OK;
|
||
for (uint8_t i = 0; i < count; i++) {
|
||
out[i] = (uint16_t)buf[3 + i*2] << 8 | buf[4 + i*2];
|
||
}
|
||
return ModbusStatus::OK;
|
||
}
|
||
|
||
ModbusStatus ModbusClient::writeRegister(uint8_t slaveAddr, uint16_t reg, uint16_t value) {
|
||
if (!_serial) return ModbusStatus::FRAME_ERROR;
|
||
|
||
uint8_t req[8];
|
||
req[0] = slaveAddr;
|
||
req[1] = 0x06;
|
||
req[2] = (reg >> 8) & 0xFF;
|
||
req[3] = reg & 0xFF;
|
||
req[4] = (value >> 8) & 0xFF;
|
||
req[5] = value & 0xFF;
|
||
uint16_t c = crc16(req, 6);
|
||
req[6] = c & 0xFF;
|
||
req[7] = (c >> 8) & 0xFF;
|
||
|
||
memcpy(lastTx, req, 8);
|
||
lastTxLen = 8;
|
||
|
||
flushRx();
|
||
_serial->write(req, 8);
|
||
_serial->flush();
|
||
|
||
// Pri broadcast adrese 0x00 nepriche odpoved
|
||
if (slaveAddr == 0x00) {
|
||
lastRxLen = 0;
|
||
lastResult = ModbusStatus::OK;
|
||
delay(50);
|
||
return ModbusStatus::OK;
|
||
}
|
||
|
||
uint8_t buf[8];
|
||
uint8_t rxLen = 0;
|
||
uint32_t t0 = millis();
|
||
|
||
while (rxLen < 8 && (millis() - t0 < TIMEOUT_MS)) {
|
||
if (_serial->available()) buf[rxLen++] = _serial->read();
|
||
yield();
|
||
}
|
||
|
||
memcpy(lastRx, buf, rxLen);
|
||
lastRxLen = rxLen;
|
||
|
||
if (rxLen < 8) { lastResult = ModbusStatus::TIMEOUT; return ModbusStatus::TIMEOUT; }
|
||
|
||
uint16_t rxCrc = (uint16_t)buf[7] << 8 | buf[6];
|
||
uint16_t calcCrc = crc16(buf, 6);
|
||
if (rxCrc != calcCrc) { lastResult = ModbusStatus::CRC_ERROR; return ModbusStatus::CRC_ERROR; }
|
||
|
||
lastResult = ModbusStatus::OK;
|
||
return ModbusStatus::OK;
|
||
}
|
||
|
||
const char* ModbusClient::statusStr(ModbusStatus s) {
|
||
switch (s) {
|
||
case ModbusStatus::OK: return "OK";
|
||
case ModbusStatus::TIMEOUT: return "TIMEOUT";
|
||
case ModbusStatus::CRC_ERROR: return "CRC_ERROR";
|
||
case ModbusStatus::EXCEPTION: return "EXCEPTION";
|
||
case ModbusStatus::FRAME_ERROR: return "FRAME_ERROR";
|
||
default: return "UNKNOWN";
|
||
}
|
||
}
|