Skip to content

Commit 0482fb2

Browse files
committed
Prioritize responding slaves
Prioritize requests to responding slaves
1 parent 62bccf9 commit 0482fb2

File tree

3 files changed

+116
-112
lines changed

3 files changed

+116
-112
lines changed

arduino-modbus-rtu-tcp-gateway/02-modbus-tcp.ino

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@
2626
2727
***************************************************************** */
2828

29-
#define ADDRESS_POS (6 * !localConfig.enableRtuOverTcp) // position of slave address in the incoming TCP/UDP message (0 for Modbus RTU over TCP/UDP and 6 for Modbus RTU over TCP/UDP)
29+
// Stored in "header.requestType"
30+
#define PRIORITY_REQUEST B10000000 // Request to slave which is not "nonresponding"
31+
#define SCAN_REQUEST B01000000 // Request triggered by slave scanner
32+
#define UDP_REQUEST B00100000 // UDP request
33+
#define TCP_REQUEST B00001111 // TCP request, also stores TCP client number
3034

3135
enum status : byte {
3236
STAT_OK, // Slave Responded
@@ -40,8 +44,10 @@ enum status : byte {
4044
// bool arrays for storing Modbus RTU status of individual slaves
4145
uint8_t stat[STAT_NUM][(maxSlaves + 1 + 7) / 8];
4246

43-
// Scan request is in queue
44-
bool scanInQueue = false;
47+
// Scan request is in the queue
48+
bool scanReqInQueue = false;
49+
// Counter for priority requests in the queue
50+
byte priorityReqInQueue;
4551

4652
// array for storing error counts
4753
uint16_t errorCount[STAT_NUM];
@@ -57,7 +63,7 @@ typedef struct {
5763
byte msgLen; // lenght of Modbus message stored in queueData
5864
IPAddress remIP; // remote IP for UDP client (UDP response is sent back to remote IP)
5965
unsigned int remPort; // remote port for UDP client (UDP response is sent back to remote port)
60-
byte clientNum; // TCP client who sent the request, UDP_REQUEST (0xFF) designates UDP client
66+
byte requestType; // TCP client who sent the request
6167
byte atts; // attempts counter
6268
} header;
6369

@@ -146,16 +152,16 @@ void recvTcp() {
146152

147153
void processRequests() {
148154
// Insert scan request into queue, allow only one scan request in a queue
149-
if (scanCounter != 0 && queueHeaders.available() > 1 && queueData.available() > sizeof(scanCommand) + 1 && scanInQueue == false) {
150-
scanInQueue = true;
155+
if (scanCounter != 0 && queueHeaders.available() > 1 && queueData.available() > sizeof(scanCommand) + 1 && scanReqInQueue == false) {
156+
scanReqInQueue = true;
151157
// Store scan request in request queue
152158
queueHeaders.push(header{
153159
{ 0x00, 0x00 }, // tid[2]
154160
sizeof(scanCommand) + 1, // msgLen
155161
{}, // remIP
156162
0, // remPort
157-
SCAN_REQUEST, // clientNum
158-
localConfig.serialAttempts - 1, // atts
163+
SCAN_REQUEST, // requestType
164+
0, // atts
159165
});
160166
queueData.push(scanCounter); // address of the scanned slave
161167
for (byte i = 0; i < sizeof(scanCommand); i++) {
@@ -167,21 +173,19 @@ void processRequests() {
167173
// Optimize queue (prioritize requests from responding slaves) and trigger sending via serial
168174
if (serialState == IDLE) { // send new data over serial only if we are not waiting for response
169175
if (!queueHeaders.isEmpty()) {
170-
// boolean queueHasRespondingSlaves; // true if queue holds at least one request to responding slaves
171-
// for (byte i = 0; i < queueHeaders.size(); i++) {
172-
// if (getSlaveStatus(queueHeaders[i].uid, statOk) == true) {
173-
// queueHasRespondingSlaves = true;
174-
// break;
175-
// } else {
176-
// queueHasRespondingSlaves = false;
177-
// }
178-
// }
176+
while (priorityReqInQueue && (queueHeaders.first().requestType & PRIORITY_REQUEST) == false) {
177+
// move requests to non responding slaves to the tail of the queue
178+
for (byte i = 0; i < queueHeaders.first().msgLen; i++) {
179+
queueData.push(queueData.shift());
180+
}
181+
queueHeaders.push(queueHeaders.shift());
182+
}
179183
serialState = SENDING; // trigger sendSerial()
180184
}
181185
}
182186
}
183187

184-
byte checkRequest(const byte inBuffer[], unsigned int msgLength, const IPAddress remoteIP, const unsigned int remotePort, const byte clientNum) {
188+
byte checkRequest(const byte inBuffer[], unsigned int msgLength, const IPAddress remoteIP, const unsigned int remotePort, byte requestType) {
185189
byte addressPos = 6 * !localConfig.enableRtuOverTcp; // position of slave address in the incoming TCP/UDP message (0 for Modbus RTU over TCP/UDP and 6 for Modbus RTU over TCP/UDP)
186190
if (localConfig.enableRtuOverTcp) { // check CRC for Modbus RTU over TCP/UDP
187191
if (checkCRC(inBuffer, msgLength) == false) {
@@ -207,15 +211,20 @@ byte checkRequest(const byte inBuffer[], unsigned int msgLength, const IPAddress
207211
} else if (getSlaveStatus(inBuffer[addressPos], STAT_ERROR_0B)) {
208212
setSlaveStatus(inBuffer[addressPos], STAT_ERROR_0B_QUEUE, true);
209213
}
210-
214+
211215
// all checkes passed OK, we can store the incoming data in request queue
216+
// Add PRIORITY_REQUEST flag to requests for responding slaves
217+
if (getSlaveStatus(inBuffer[addressPos], STAT_ERROR_0B_QUEUE) == false) {
218+
requestType = requestType | PRIORITY_REQUEST;
219+
priorityReqInQueue++;
220+
}
212221
// Store in request queue
213222
queueHeaders.push(header{
214223
{ inBuffer[0], inBuffer[1] }, // tid[2] (ignored in Modbus RTU over TCP/UDP)
215224
(byte)msgLength, // msgLen
216225
(IPAddress)remoteIP, // remIP
217226
(unsigned int)remotePort, // remPort
218-
(byte)clientNum, // clientNum
227+
(byte)(requestType), // requestType
219228
0, // atts
220229
});
221230
for (byte i = 0; i < msgLength; i++) {
@@ -228,8 +237,10 @@ byte checkRequest(const byte inBuffer[], unsigned int msgLength, const IPAddress
228237

229238
void deleteRequest() // delete request from queue
230239
{
231-
if (queueHeaders.first().clientNum == SCAN_REQUEST) scanInQueue = false;
232-
for (byte i = 0; i < queueHeaders.first().msgLen; i++) {
240+
header myHeader = queueHeaders.first();
241+
if (myHeader.requestType & SCAN_REQUEST) scanReqInQueue = false;
242+
if (myHeader.requestType & PRIORITY_REQUEST) priorityReqInQueue--;
243+
for (byte i = 0; i < myHeader.msgLen; i++) {
233244
queueData.shift();
234245
}
235246
queueHeaders.shift();

arduino-modbus-rtu-tcp-gateway/03-modbus-rtu.ino

Lines changed: 65 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ void sendSerial() {
6060
#ifdef RS485_CONTROL_PIN
6161
digitalWrite(RS485_CONTROL_PIN, RS485_RECEIVE); // Disable RS485 Transmit
6262
#endif /* RS485_CONTROL_PIN */
63-
if (queueData[0] == 0x00) { // Modbus broadcast - we do not count attempts and delete immediatelly
63+
if (queueData[0] == 0x00) { // Modbus broadcast - we do not count attempts and delete immediatelly
6464
serialState = IDLE;
6565
deleteRequest();
6666
} else {
@@ -93,41 +93,43 @@ void recvSerial() {
9393
// Process Serial data
9494
// Checks: 1) RTU frame is without errors; 2) CRC; 3) address of incoming packet against first request in queue; 4) only expected responses are forwarded to TCP/UDP
9595
header myHeader = queueHeaders.first();
96-
if (!rxErr && checkCRC(serialIn, rxNdx) == true && serialIn[0] == queueData[0] && serialState == WAITING) {
97-
if (serialIn[1] > 0x80 && myHeader.clientNum != SCAN_REQUEST) {
96+
if (!rxErr && checkCRC(serialIn, rxNdx) == true) {
97+
if (serialIn[1] > 0x80 && (myHeader.requestType & SCAN_REQUEST) == false) {
9898
setSlaveStatus(serialIn[0], STAT_ERROR_0X, true);
9999
} else {
100100
setSlaveStatus(serialIn[0], STAT_OK, true);
101101
}
102-
byte MBAP[] = { myHeader.tid[0], myHeader.tid[1], 0x00, 0x00, highByte(rxNdx - 2), lowByte(rxNdx - 2) };
103-
if (myHeader.clientNum == UDP_REQUEST) {
104-
Udp.beginPacket(myHeader.remIP, myHeader.remPort);
105-
if (localConfig.enableRtuOverTcp) Udp.write(serialIn, rxNdx);
106-
else {
107-
Udp.write(MBAP, 6);
108-
Udp.write(serialIn, rxNdx - 2); //send without CRC
109-
}
110-
Udp.endPacket();
111-
#ifdef ENABLE_EXTRA_DIAG
112-
ethTxCount += rxNdx;
113-
if (!localConfig.enableRtuOverTcp) ethTxCount += 4;
114-
#endif /* ENABLE_EXTRA_DIAG */
115-
} else if (myHeader.clientNum != SCAN_REQUEST) {
116-
EthernetClient client = EthernetClient(myHeader.clientNum);
117-
if (client.connected()) {
118-
if (localConfig.enableRtuOverTcp) client.write(serialIn, rxNdx);
102+
if (serialIn[0] == queueData[0] && serialState == WAITING) {
103+
byte MBAP[] = { myHeader.tid[0], myHeader.tid[1], 0x00, 0x00, highByte(rxNdx - 2), lowByte(rxNdx - 2) };
104+
if (myHeader.requestType & UDP_REQUEST) {
105+
Udp.beginPacket(myHeader.remIP, myHeader.remPort);
106+
if (localConfig.enableRtuOverTcp) Udp.write(serialIn, rxNdx);
119107
else {
120-
client.write(MBAP, 6);
121-
client.write(serialIn, rxNdx - 2); //send without CRC
108+
Udp.write(MBAP, 6);
109+
Udp.write(serialIn, rxNdx - 2); //send without CRC
122110
}
111+
Udp.endPacket();
123112
#ifdef ENABLE_EXTRA_DIAG
124113
ethTxCount += rxNdx;
125114
if (!localConfig.enableRtuOverTcp) ethTxCount += 4;
126115
#endif /* ENABLE_EXTRA_DIAG */
116+
} else if (myHeader.requestType & TCP_REQUEST) {
117+
EthernetClient client = EthernetClient(myHeader.requestType & TCP_REQUEST);
118+
if (client.connected()) {
119+
if (localConfig.enableRtuOverTcp) client.write(serialIn, rxNdx);
120+
else {
121+
client.write(MBAP, 6);
122+
client.write(serialIn, rxNdx - 2); //send without CRC
123+
}
124+
#ifdef ENABLE_EXTRA_DIAG
125+
ethTxCount += rxNdx;
126+
if (!localConfig.enableRtuOverTcp) ethTxCount += 4;
127+
#endif /* ENABLE_EXTRA_DIAG */
128+
}
127129
}
130+
deleteRequest();
131+
serialState = IDLE;
128132
}
129-
deleteRequest();
130-
serialState = IDLE;
131133
}
132134
#ifdef ENABLE_EXTRA_DIAG
133135
serialRxCount += rxNdx;
@@ -138,60 +140,52 @@ void recvSerial() {
138140

139141
// Deal with Serial timeouts (i.e. Modbus RTU timeouts)
140142
if (serialState == WAITING && requestTimeout.isOver()) {
143+
serialState = IDLE;
141144
header myHeader = queueHeaders.first();
142-
if (myHeader.clientNum != SCAN_REQUEST) setSlaveStatus(queueData[0], STAT_ERROR_0B_QUEUE, true);
143-
if (myHeader.atts >= localConfig.serialAttempts) {
144-
if (myHeader.clientNum != SCAN_REQUEST) {
145-
// send modbus error 0x0B (Gateway Target Device Failed to Respond) - usually means that target device (address) is not present
146-
setSlaveStatus(queueData[0], STAT_ERROR_0B, true);
147-
byte MBAP[] = { myHeader.tid[0], myHeader.tid[1], 0x00, 0x00, 0x00, 0x03 };
148-
byte PDU[] = { queueData[0], (byte)(queueData[1] + 0x80), 0x0B };
149-
crc = 0xFFFF;
150-
for (byte i = 0; i < sizeof(PDU); i++) {
151-
calculateCRC(PDU[i]);
152-
}
153-
switch (myHeader.clientNum) {
154-
case UDP_REQUEST:
155-
Udp.beginPacket(myHeader.remIP, myHeader.remPort);
156-
if (!localConfig.enableRtuOverTcp) {
157-
Udp.write(MBAP, 6);
158-
}
159-
Udp.write(PDU, 3);
160-
if (localConfig.enableRtuOverTcp) {
161-
Udp.write(lowByte(crc)); // send CRC, low byte first
162-
Udp.write(highByte(crc));
163-
}
164-
Udp.endPacket();
165-
#ifdef ENABLE_EXTRA_DIAG
166-
ethTxCount += 5;
167-
if (!localConfig.enableRtuOverTcp) ethTxCount += 4;
168-
#endif /* ENABLE_EXTRA_DIAG */
169-
break;
170-
default: // Ethernet client
171-
{
172-
EthernetClient client = EthernetClient(myHeader.clientNum);
173-
if (client.connected()) {
174-
if (!localConfig.enableRtuOverTcp) {
175-
client.write(MBAP, 6);
176-
}
177-
client.write(PDU, 3);
178-
if (localConfig.enableRtuOverTcp) {
179-
client.write(lowByte(crc)); // send CRC, low byte first
180-
client.write(highByte(crc));
181-
}
145+
if (myHeader.requestType & SCAN_REQUEST) { // Only one attempt for scan request (we do not count attempts)
146+
deleteRequest();
147+
} else if (myHeader.atts >= localConfig.serialAttempts) {
148+
// send modbus error 0x0B (Gateway Target Device Failed to Respond) - usually means that target device (address) is not present
149+
setSlaveStatus(queueData[0], STAT_ERROR_0B, true);
150+
byte MBAP[] = { myHeader.tid[0], myHeader.tid[1], 0x00, 0x00, 0x00, 0x03 };
151+
byte PDU[] = { queueData[0], (byte)(queueData[1] + 0x80), 0x0B };
182152
#ifdef ENABLE_EXTRA_DIAG
183-
ethTxCount += 5;
184-
if (!localConfig.enableRtuOverTcp) ethTxCount += 4;
153+
ethTxCount += 5;
154+
if (!localConfig.enableRtuOverTcp) ethTxCount += 4;
185155
#endif /* ENABLE_EXTRA_DIAG */
186-
}
187-
break;
188-
}
156+
crc = 0xFFFF;
157+
for (byte i = 0; i < sizeof(PDU); i++) {
158+
calculateCRC(PDU[i]);
159+
}
160+
if (myHeader.requestType & UDP_REQUEST) {
161+
Udp.beginPacket(myHeader.remIP, myHeader.remPort);
162+
if (!localConfig.enableRtuOverTcp) {
163+
Udp.write(MBAP, 6);
164+
}
165+
Udp.write(PDU, 3);
166+
if (localConfig.enableRtuOverTcp) {
167+
Udp.write(lowByte(crc)); // send CRC, low byte first
168+
Udp.write(highByte(crc));
169+
}
170+
Udp.endPacket();
171+
} else {
172+
EthernetClient client = EthernetClient(myHeader.requestType & TCP_REQUEST);
173+
if (client.connected()) {
174+
if (!localConfig.enableRtuOverTcp) {
175+
client.write(MBAP, 6);
176+
}
177+
client.write(PDU, 3);
178+
if (localConfig.enableRtuOverTcp) {
179+
client.write(lowByte(crc)); // send CRC, low byte first
180+
client.write(highByte(crc));
181+
}
189182
}
190183
}
191184
deleteRequest();
185+
} else {
186+
setSlaveStatus(queueData[0], STAT_ERROR_0B_QUEUE, true);
192187
} // if (myHeader.atts >= MAX_RETRY)
193-
serialState = IDLE;
194-
} // if (serialState == WAITING && requestTimeout.isOver())
188+
} // if (serialState == WAITING && requestTimeout.isOver())
195189
}
196190

197191
bool checkCRC(byte buf[], int len) {

arduino-modbus-rtu-tcp-gateway/05-pages.ino

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ void contentStatus(ChunkedPrint &chunked) {
162162
chunked.print(F("<tr><td>Microcontroller:<td>"));
163163
chunked.print(BOARD);
164164
// chunked.print(freeMemory());
165+
// chunked.print(frameDelay);
165166
chunked.print(F("<tr><td>Ethernet Chip:<td>"));
166167
switch (Ethernet.hardwareStatus()) {
167168
case EthernetW5100:
@@ -201,19 +202,8 @@ void contentStatus(ChunkedPrint &chunked) {
201202

202203
chunked.print(F("<tr><td>IP Address:<td>"));
203204
chunked.print(Ethernet.localIP());
204-
chunked.print(F("<tr><td>Requests Queue:<td>"));
205-
chunked.print(queueDataSize);
206-
chunked.print(F(" / "));
207-
chunked.print(maxQueueData);
208-
chunked.print(F(" bytes, "));
209-
chunked.print(queueHeadersSize);
210-
chunked.print(F(" / "));
211-
chunked.print(maxQueueRequests);
212-
chunked.print(F(" requests"));
213-
queueDataSize = 0;
214-
queueHeadersSize = 0;
215205

216-
#ifdef ENABLE_EXTRA_DIAG
206+
#ifdef ENABLE_EXTRA_DIAG
217207
chunked.print(F("<tr><td>Run Time:<td>"));
218208
byte mod_seconds = byte((seconds) % 60);
219209
byte mod_minutes = byte((seconds / 60) % 60);
@@ -334,16 +324,26 @@ void contentStatus(ChunkedPrint &chunked) {
334324
#endif /* ENABLE_EXTRA_DIAG */
335325

336326
chunked.print(F("<tr><td><br>"
337-
"<tr><td>Modbus Stats:<td>"));
338-
for (byte i = 0; i < STAT_NUM; i++) {
339-
if (i == STAT_ERROR_0B_QUEUE) continue;
327+
"<tr><td>Requests Queue:<td>"));
328+
chunked.print(queueDataSize);
329+
chunked.print(F(" / "));
330+
chunked.print(maxQueueData);
331+
chunked.print(F(" bytes"
332+
"<tr><td><td>"));
333+
chunked.print(queueHeadersSize);
334+
chunked.print(F(" / "));
335+
chunked.print(maxQueueRequests);
336+
chunked.print(F(" requests"));
337+
queueDataSize = 0;
338+
queueHeadersSize = 0;
339+
chunked.print(F("<tr><td>Modbus Stats:<td>"));
340+
for (byte i = 0; i < STAT_ERROR_0B_QUEUE; i++) { // ignore STAT_ERROR_0B_QUEUE
340341
chunked.print(errorCount[i]);
341342
helperStats(chunked, i);
342343
chunked.print(F("<tr><td><td>"));
343344
}
344345
chunked.print(errorInvalid);
345-
chunked.print(F(" Invalid Request (Dropped by Gateway)"
346-
"<tr><td><br>"
346+
chunked.print(F(" Invalid TCP/UDP Request"
347347
"<tr><td>Modbus TCP/UDP Masters:"));
348348
byte countMasters = 0;
349349
for (byte i = 0; i < maxSockNum; i++) {
@@ -362,8 +362,7 @@ void contentStatus(ChunkedPrint &chunked) {
362362
}
363363
}
364364
if (countMasters == 0) chunked.print(F("<td>None"));
365-
chunked.print(F("<tr><td><br>"
366-
"<tr><td>Modbus RTU Slaves:<td><button name="));
365+
chunked.print(F("<tr><td>Modbus RTU Slaves:<td><button name="));
367366
chunked.print(POST_ACTION);
368367
chunked.print(F(" value="));
369368
chunked.print(SCAN);

0 commit comments

Comments
 (0)