Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
38c20bc
P183 initial state
flashmark Aug 24, 2025
46f2a50
P183 2nd attempt
flashmark Aug 24, 2025
7d0e802
P183 Add initial documentation
flashmark Aug 27, 2025
cc31b10
P183 Add documentation (preliminary)
flashmark Aug 28, 2025
53ec930
P183 Minor rework
flashmark Aug 31, 2025
a715544
Baseline before refacoring
flashmark Sep 6, 2025
5f8868a
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Sep 15, 2025
aa34ac3
P183 Uncrustify
flashmark Sep 15, 2025
02f46b3
Merge branch 'P183_Modbus_registers' of https://github.com/flashmark/…
flashmark Sep 15, 2025
11cf754
Starting complete make over
flashmark Sep 30, 2025
e7aabda
P183 Rework phase 1
flashmark Oct 6, 2025
1ee2b35
Merge branch 'mega' into P183_Modbus_registers
flashmark Oct 6, 2025
18e8b86
P183 Small refactoring
flashmark Oct 6, 2025
bd7661e
P183 Snapshot refactoring
flashmark Oct 12, 2025
8c66411
P183 Start cleaning up
flashmark Oct 15, 2025
fc27c88
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Oct 15, 2025
8f5e5f9
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Oct 17, 2025
a4dfca1
P183 Pending rework, not final
flashmark Oct 17, 2025
7795cc1
P183 Several fixes
flashmark Oct 18, 2025
6e201fb
P183 Update logging
flashmark Oct 23, 2025
280e8bf
P183: Init link fix & minor updates
flashmark Oct 23, 2025
4515c8c
P183: initial UML design documentation
flashmark Oct 23, 2025
21b427e
P183: Minor fixes and start of design documentation
flashmark Oct 26, 2025
8d5ce4d
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Oct 27, 2025
5ba2f9f
Merge branch 'mega' into P183_Modbus_registers
flashmark Dec 11, 2025
b432092
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Dec 13, 2025
c182cea
P183 Added documentation
flashmark Dec 13, 2025
67134da
Merge branch 'mega' into P183_Modbus_registers
TD-er Feb 10, 2026
3495afe
Merge branch 'mega' into P183_Modbus_registers
TD-er Feb 10, 2026
abe1a67
Merge branch 'mega' into P183_Modbus_registers
TD-er Feb 12, 2026
e93b4c3
Merge branch 'mega' into P183_Modbus_registers
TD-er Feb 13, 2026
ef31d27
[P183] Make new generic Modbus RTU PR build again
TD-er Feb 13, 2026
c322798
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Feb 16, 2026
da1e31d
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Mar 5, 2026
0098e3c
Secure work in progress, don't merge to master
flashmark Mar 20, 2026
7e01a41
Merge branch 'mega' into P183_Modbus_registers
flashmark Mar 20, 2026
934b595
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Apr 7, 2026
610c3e9
P183 snapshot
flashmark Apr 7, 2026
33e4439
Merge branch 'P183_Modbus_registers' of https://github.com/flashmark/…
flashmark Apr 7, 2026
6b774aa
P183 modbus interfaces support
flashmark Apr 16, 2026
7efc42d
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Apr 16, 2026
68bba7f
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Apr 23, 2026
2e2ab34
Save state some init problems left
flashmark Apr 23, 2026
bb28e71
Merge branch 'P183_Modbus_registers' of https://github.com/flashmark/…
flashmark Apr 23, 2026
470d3c2
securing state, not ready yet
flashmark Apr 27, 2026
884deb3
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark May 1, 2026
c66aee9
Functionality ready, to be debugged and cleaned up
flashmark May 2, 2026
79ffeb4
Merge branch 'P183_Modbus_registers' of https://github.com/flashmark/…
flashmark May 2, 2026
afa1b6a
Small review comment updates
flashmark May 3, 2026
0f9c1dd
Rework & fixed initialization
flashmark May 3, 2026
d254ae4
P183 documentation update
flashmark May 5, 2026
9111e0d
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark May 9, 2026
30d2b12
P183 Modbus cleanup, not ready yet
flashmark May 9, 2026
115cb39
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark May 16, 2026
6f2cb43
P183 Modbus add cache region
flashmark May 17, 2026
e5583da
P183 Unfinished rework
flashmark May 28, 2026
a2dc9e0
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark May 28, 2026
01985f9
Merge branch 'mega' into P183_Modbus_registers
TD-er Jun 5, 2026
c117ebd
[P183] Fix failing build Modbus RTU on ESP8266 builds
TD-er Jun 5, 2026
298517c
[P183] Disable Modbus RTU plugin for now for Neopixel builds due to size
TD-er Jun 5, 2026
c723180
[Modbus RTU] Disable P183 for DisplayB builds due to build size
TD-er Jun 6, 2026
8517192
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Jun 6, 2026
90c3a19
P183: Rework stage 1
flashmark Jun 8, 2026
305d41e
P183: refactored transaction handling
flashmark Jun 14, 2026
c8abfef
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Jun 14, 2026
49e803b
P183: Small fixes
flashmark Jun 14, 2026
67bdaf7
P183: Conditional disabling of blocking wait support
flashmark Jun 24, 2026
281d0e6
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Jun 24, 2026
485c46b
P183: Change queue from pointer type to Transaction type (remove indi…
flashmark Jun 25, 2026
a741bee
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Jun 26, 2026
e7eac10
P183: Rework
flashmark Jun 26, 2026
d68d88b
P183: Fixes
flashmark Jul 3, 2026
1af346c
P183: Refactor link processing
flashmark Jul 5, 2026
41d11b5
P183: Rework after review
flashmark Jul 7, 2026
3a873c5
P183: Update serial settings
flashmark Jul 13, 2026
e90a72f
P183: Revert changes in serial helper. Compiles, but lacks the setup …
flashmark Jul 23, 2026
afa7ca5
Merge branch 'mega' into P183_Modbus_registers
flashmark Jul 23, 2026
7b7649b
P183: Adapted to new serial helper libraray
flashmark Jul 26, 2026
e831f0f
P183: Disable I2C and software serial. Update documentation
flashmark Jul 27, 2026
98effa0
P183: Documentation updates
flashmark Jul 29, 2026
814016c
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Jul 29, 2026
eb3327d
P183: Documentation and plugin sets updates
flashmark Aug 1, 2026
a68f5eb
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Aug 5, 2026
55c6ccd
Merge branch 'letscontrolit:mega' into P183_Modbus_registers
flashmark Aug 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions src/_P183_modbus.ino
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
// These pointers may be used among multiple instances of the same plugin,
// as long as the same serial settings are used.
ModbusDEVICE_struct * P183_ModbusDevice = nullptr;
ModbusQueueState_t P183_ModbusStatus = ModbusQueueState_t::EMPTY;
ModbusQueueState_t P183_ModbusStatus = {};
boolean P183_init = false;

void P183_scan_modbus();
Expand Down Expand Up @@ -211,6 +211,7 @@ boolean Plugin_183(uint8_t function, struct EventStruct *event, String& string)
case PLUGIN_INIT:
{
P183_init = true;
addLogMove(LOG_LEVEL_INFO, "P183 INIT");

// (re)create the serial port object
// If the serial port object already exists, delete it first.
Expand All @@ -219,6 +220,7 @@ boolean Plugin_183(uint8_t function, struct EventStruct *event, String& string)
P183_ModbusDevice = nullptr;
}
P183_ModbusDevice = new ModbusDEVICE_struct();
Comment thread
flashmark marked this conversation as resolved.
Outdated
addLogMove(LOG_LEVEL_INFO, "P183 INIT AFTER NEW");

if (P183_ModbusDevice == nullptr) {
P183_init = false;
Expand All @@ -234,8 +236,9 @@ boolean Plugin_183(uint8_t function, struct EventStruct *event, String& string)
P183_GET_FLAG_COLL_DETECT)) {
break;
}
addLogMove(LOG_LEVEL_INFO, "P183 INIT AFTER INIT");
P183_ModbusDevice->setModbusTimeout(P183_MODBUS_TIMEOUT);

addLogMove(LOG_LEVEL_DEBUG, "AFTER TIMEOUT");
Comment thread
flashmark marked this conversation as resolved.
Outdated
# ifdef P183_DEBUG

if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
Comment thread
flashmark marked this conversation as resolved.
Outdated
Expand All @@ -260,20 +263,23 @@ boolean Plugin_183(uint8_t function, struct EventStruct *event, String& string)
case PLUGIN_EXIT:
{
P183_init = false;
delete P183_ModbusDevice;
P183_ModbusDevice = nullptr;
success = true;

if (P183_ModbusDevice != nullptr) {
delete P183_ModbusDevice;
P183_ModbusDevice = nullptr;
}
success = true;
break;
}

case PLUGIN_READ:
{
uint16_t value = 0;

static uint16_t registerValues[4] = {0, 0, 0, 0};
for (int outputIndex = 0; outputIndex < P183_NR_OUTPUTS; ++outputIndex)
{
P183_modbus_readRegister(P183_ADDRESS(outputIndex), &value);
UserVar.setFloat(event->TaskIndex, outputIndex, value);
// TODO: Abuse PCONFIG_LONG static storage for now.
P183_modbus_readRegister(P183_ADDRESS(outputIndex), &(registerValues[outputIndex]));
UserVar.setFloat(event->TaskIndex, outputIndex, registerValues[outputIndex]);
}
success = true;
break;
Expand Down Expand Up @@ -356,6 +362,12 @@ boolean Plugin_183(uint8_t function, struct EventStruct *event, String& string)
}
break;
}
case PLUGIN_TEN_PER_SECOND: {
if (P183_init && (P183_ModbusDevice != nullptr)) {
P183_ModbusDevice->processCommand();
}
break;
}
}
return success;
}
Expand Down Expand Up @@ -459,7 +471,7 @@ void P183_scan_modbus()
uint16_t value = 0;

for (uint8_t id = 0; id <= 247; id++) {
//TODO: how to scan the Modbus devices in teh new structure
// TODO: how to scan the Modbus devices in teh new structure
int result = P183_modbus_readRegister(1, &value);
log += F("** Address ");
log += String(id);
Expand Down
226 changes: 113 additions & 113 deletions src/src/Helpers/Modbus_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@ ModbusDEVICE_struct::~ModbusDEVICE_struct() {
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ModbusDEVICE_struct::reset() {
if (_modbus_link != nullptr) {
_modbus_link->freeTransactions(this);
ModbusMGR_singleton.disconnect(_deviceID);
_modbus_link = nullptr;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use some (weak) std::shared_ptr like structure for this?

}
_deviceID = 0;
_queueID = 0;
_sendframe_size = 0;
_recv_buf_used = 0;
_deviceID = 0;

_modbus_address = MODBUS_BROADCAST_ADDRESS;
}

Expand All @@ -48,6 +47,16 @@ bool ModbusDEVICE_struct::init(uint8_t slaveAddress,

_modbus_address = slaveAddress;

if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("---> ModbusDevice Init: Slave address = ");
log += slaveAddress;
log += F(", This = ");
log += (ulong)this;
Comment thread
flashmark marked this conversation as resolved.
Outdated
log += F(", deviceID = ");
log += _deviceID;
addLogMove(LOG_LEVEL_INFO, log);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More strformat() logging, so no new String has to be created and concatenated multiple times, causing re-allocations

}

// TODO: further implementation needed
return success;
}
Expand All @@ -74,17 +83,29 @@ uint16_t ModbusDEVICE_struct::getModbusTimeout() const
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ModbusDEVICE_struct::readHoldingRegister(uint16_t address,
uint16_t *valuePtr,
ModbusQueueState_t *statePtr) {
buildFrame(_modbus_address, MODBUS_READ_HOLDING_REGISTERS, address, 1);
uint16_t crc = CalculateCRC(_sendframe, _sendframe_size);
_sendframe[_sendframe_size++] = lowByte(crc); // CRC low byte
_sendframe[_sendframe_size++] = highByte(crc); // CRC high byte
_queueID = queueFrame();
*statePtr = ModbusQueueState_t::QUEUED;
_statePtr = statePtr;
_resultPtr = valuePtr;
_state = ModbusQueueState_t::QUEUED;
_messageType = ModbusMessageType::READ_HOLDING_REGISTERS;
ModbusQueueState_t *statePtr)
{
if (_modbus_link == nullptr) {
return false;
}
Modbus_RequestQueueElement *request = _modbus_link->newTransaction(this);

request->_messageType = ModbusMessageType::READ_HOLDING_REGISTERS;
request->_userData = valuePtr;
request->_sendframe[0] = _modbus_address;
request->_sendframe[1] = MODBUS_READ_HOLDING_REGISTERS;
request->_sendframe[2] = highByte(address);
request->_sendframe[3] = lowByte(address);
request->_sendframe[4] = 0;
request->_sendframe[5] = 1; // Read 1 register
uint16_t crc = CalculateCRC(request->_sendframe, 6);
request->_sendframe[6] = lowByte(crc); // CRC low byte
request->_sendframe[7] = highByte(crc); // CRC high byte
request->_sendframe_length = 8; // Size with CRC
request->_rcvframe_length = 7; // Expect 8 bytes in response
dump_buffer(request->_sendframe, request->_sendframe_length);
uint16_t queueID = _modbus_link->queueRequest(request);
*statePtr = ModbusQueueState_t::QUEUED;

// Don't touch *valueptr here, it might contain a previous valid result.
return false; // TODO: implement
Expand All @@ -95,17 +116,22 @@ bool ModbusDEVICE_struct::writeSingleRegister(uint16_t address,
uint16_t value,
ModbusQueueState_t *statePtr)
{
buildFrame(_modbus_address, MODBUS_WRITE_SINGLE_REGISTER, address, 1);
_sendframe[4] = highByte(value);
_sendframe[5] = lowByte(value);
uint16_t crc = CalculateCRC(_sendframe, _sendframe_size);
_sendframe[_sendframe_size++] = lowByte(crc); // CRC low byte
_sendframe[_sendframe_size++] = highByte(crc); // CRC high byte
_queueID = queueFrame();
*statePtr = ModbusQueueState_t::QUEUED;
_statePtr = statePtr;
_state = ModbusQueueState_t::QUEUED;
_messageType = ModbusMessageType::WRITE_SINGLE_REGISTER;
Modbus_RequestQueueElement *request = _modbus_link->newTransaction(this);

request->_sendframe[0] = _modbus_address;
request->_sendframe[1] = MODBUS_WRITE_SINGLE_REGISTER;
request->_sendframe[2] = highByte(address);
request->_sendframe[3] = lowByte(address);
request->_sendframe[4] = highByte(value);
request->_sendframe[5] = lowByte(value);
uint16_t crc = CalculateCRC(request->_sendframe, 6);
request->_sendframe[6] = lowByte(crc); // CRC low byte
request->_sendframe[7] = highByte(crc); // CRC high byte
request->_sendframe_length = 8; // Size with CRC
request->_rcvframe_length = 8; // Expect 8 bytes in response
uint16_t queueID = _modbus_link->queueRequest(request);
*statePtr = ModbusQueueState_t::QUEUED;
request->_messageType = ModbusMessageType::WRITE_SINGLE_REGISTER;
return false;
}

Expand All @@ -114,104 +140,74 @@ void ModbusDEVICE_struct::processCommand() {
if (_modbus_link != nullptr) {
_modbus_link->processCommand(); // Trigger processing of the command queue on the link
}
else {
_state = ModbusQueueState_t::ERROR;
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Callback function called by the Modbus link when a response is received for a queued request.
// Note that the response might be an invalid response or a timeout
// The queueID identifies the request.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ModbusDEVICE_struct::linkCallback(uint16_t queueID)
void ModbusDEVICE_struct::linkCallback(Modbus_RequestQueueElement *req)
{
if (queueID != _queueID) {
return; // Not for us
}
bool response = _modbus_link->getResponse(_queueID, _recv_buf, _recv_buf_used);

if (response) {
switch (_messageType) {
case ModbusMessageType::READ_HOLDING_REGISTERS:
{
if ((_recv_buf[0] == _modbus_address) && (_recv_buf[1] == MODBUS_READ_HOLDING_REGISTERS) && (_recv_buf[2] == 2)) {
uint16_t crc = CalculateCRC(_recv_buf, 5);

if ((_recv_buf[5] == lowByte(crc)) && (_recv_buf[6] == highByte(crc))) {
// Valid response
if (_resultPtr != nullptr) {
*_resultPtr = (_recv_buf[3] << 8) | _recv_buf[4]; // Combine high and low byte
}
_state = ModbusQueueState_t::AVAILABLE;
} else {
// Invalid CRC
_state = ModbusQueueState_t::ERROR;
String log = F("---> Device callback: ");
Comment thread
flashmark marked this conversation as resolved.
Outdated

log += req->_id;
log += F(", Message = ");
log += (uint8_t)req->_messageType;
Comment thread
flashmark marked this conversation as resolved.
Outdated


switch (req->_messageType) {
case ModbusMessageType::READ_HOLDING_REGISTERS:
{
if ((req->_rcvframe[0] == _modbus_address) && (req->_rcvframe[1] == MODBUS_READ_HOLDING_REGISTERS) && (req->_rcvframe[2] == 2)) {
uint16_t crc = CalculateCRC(req->_rcvframe, 5);

if ((req->_rcvframe[5] == lowByte(crc)) && (req->_rcvframe[6] == highByte(crc))) {
// Valid response
if (req->_userData != nullptr) {
*((uint16_t *)req->_userData) = (req->_rcvframe[3] << 8) | req->_rcvframe[4]; // Combine high and low byte
}
} else {
// Invalid response
_state = ModbusQueueState_t::ERROR;
// Invalid CRC
}
return;
break;
} else {
// Invalid response
}
case ModbusMessageType::WRITE_SINGLE_REGISTER:
{
if ((_recv_buf[0] == _modbus_address) && (_recv_buf[1] == MODBUS_READ_HOLDING_REGISTERS) && (_recv_buf[2] == 2)) {
uint16_t crc = CalculateCRC(_recv_buf, 5);

if ((_recv_buf[5] == lowByte(crc)) && (_recv_buf[6] == highByte(crc))) {
// Valid response
_state = ModbusQueueState_t::AVAILABLE;
}
else {
// Invalid CRC
_state = ModbusQueueState_t::ERROR;
}
} else {
// Invalid response
_state = ModbusQueueState_t::ERROR;
break;
}

case ModbusMessageType::WRITE_SINGLE_REGISTER:
{
if ((req->_rcvframe[0] == _modbus_address) && (req->_rcvframe[1] == MODBUS_READ_HOLDING_REGISTERS) && (req->_rcvframe[2] == 2)) {
uint16_t crc = CalculateCRC(req->_rcvframe, 5);

if ((req->_rcvframe[5] == lowByte(crc)) && (req->_rcvframe[6] == highByte(crc))) {
// Valid response
}
break;
}
case ModbusMessageType::NONE:
{
// Should not happen
_state = ModbusQueueState_t::ERROR;
break;
else {
// Invalid CRC
}
} else {
// Invalid response
}
break;
}

default:
{
// Unknown message type
_state = ModbusQueueState_t::ERROR;
break;
}
case ModbusMessageType::NONE:
{
// Should not happen
break;
}
}

// Update the state as seen by the client
if (_statePtr != nullptr) {
*_statePtr = _state;
default:
{
// Unknown message type
break;
}
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Build a Modbus RTU frame filling in the standard fields.
// Note that thsi does not include the CRC.
// The frame is stored in the _sendframe buffer and the size is stored in _sendframe_size.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ModbusDEVICE_struct::buildFrame(uint8_t slaveAddress,
uint8_t functionCode,
uint16_t startAddress,
uint8_t byteCount) {
_sendframe[0] = slaveAddress;
_sendframe[1] = functionCode;
_sendframe[2] = highByte(startAddress);
_sendframe[3] = lowByte(startAddress);
_sendframe[4] = highByte(byteCount);
_sendframe[5] = lowByte(byteCount);
_sendframe_size = 6; // Size without the CRC
_modbus_link->freeTransaction(req);
addLogMove(LOG_LEVEL_INFO, log);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -236,15 +232,19 @@ uint16_t ModbusDEVICE_struct::CalculateCRC(uint8_t *buf, int len) {
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Queue the assembled Modbus frame for transmission over the link using the ModbusLink object.
// The function returns the queue ID assigned to the request, or 0 if queuing failed
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
uint16_t ModbusDEVICE_struct::queueFrame() {
if (_modbus_link == nullptr) {
return false;
void ModbusDEVICE_struct::dump_buffer(const uint8_t *buffer, size_t length) {
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("---> Modbus: Dumping buffer: ");

for (size_t i = 0; i < length; ++i) {
log += String(buffer[i], HEX);

if (i < length - 1) {
log += F(", ");
}
}
addLogMove(LOG_LEVEL_INFO, log);
}
_queueID = _modbus_link->queueRequest(this, _sendframe, _sendframe_size, MODBUS_RECEIVE_BUFFER, _timeout);
return _queueID;
}

#endif // if FEATURE_MODBUS
Loading