Skip to content

Commit ac50117

Browse files
committed
Serial line state machine, timers
1 parent 6d01f9c commit ac50117

File tree

6 files changed

+216
-224
lines changed

6 files changed

+216
-224
lines changed

arduino-modbus-rtu-tcp-gateway/01-interfaces.ino

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,22 @@
3636

3737
void startSerial() {
3838
mySerial.begin(localConfig.baud, localConfig.serialConfig);
39-
// Calculate Modbus RTU character timeout and frame delay
40-
byte bits = // number of bits per character (11 in default Modbus RTU settings)
41-
1 + // start bit
42-
(((localConfig.serialConfig & 0x06) >> 1) + 5) + // data bits
43-
(((localConfig.serialConfig & 0x08) >> 3) + 1); // stop bits
44-
if (((localConfig.serialConfig & 0x30) >> 4) > 1) bits += 1; // parity bit (if present)
45-
int T = ((unsigned long)bits * 1000000UL) / localConfig.baud; // time to send 1 character over serial in microseconds
46-
if (localConfig.baud <= 19200) {
47-
charTimeout = 1.5 * T; // inter-character time-out should be 1,5T
48-
frameDelay = 3.5 * T; // inter-frame delay should be 3,5T
49-
} else {
50-
charTimeout = 750;
51-
frameDelay = 1750;
52-
}
5339
#ifdef RS485_CONTROL_PIN
5440
pinMode(RS485_CONTROL_PIN, OUTPUT);
5541
digitalWrite(RS485_CONTROL_PIN, RS485_RECEIVE); // Init Transceiver
5642
#endif /* RS485_CONTROL_PIN */
5743
}
5844

45+
// time to send 1 character over serial in microseconds
46+
unsigned long charTime() {
47+
byte bits = // number of bits per character (11 in default Modbus RTU settings)
48+
1 + // start bit
49+
(((localConfig.serialConfig & 0x06) >> 1) + 5) + // data bits
50+
(((localConfig.serialConfig & 0x08) >> 3) + 1); // stop bits
51+
if (((localConfig.serialConfig & 0x30) >> 4) > 1) bits += 1; // parity bit (if present)
52+
return ((unsigned long)bits * 1000000UL) / localConfig.baud;
53+
}
54+
5955
void startEthernet() {
6056
if (ETH_RESET_PIN != 0) {
6157
pinMode(ETH_RESET_PIN, OUTPUT);
@@ -253,22 +249,3 @@ ISR(WDT_vect) {
253249

254250

255251

256-
257-
258-
#ifdef __arm__
259-
// should use uinstd.h to define sbrk but Due causes a conflict
260-
extern "C" char* sbrk(int incr);
261-
#else // __ARM__
262-
extern char *__brkval;
263-
#endif // __arm__
264-
265-
int freeMemory() {
266-
char top;
267-
#ifdef __arm__
268-
return &top - reinterpret_cast<char*>(sbrk(0));
269-
#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
270-
return &top - __brkval;
271-
#else // __arm__
272-
return __brkval ? &top - __brkval : &top - __malloc_heap_start;
273-
#endif // __arm__
274-
}

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

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
- receives Modbus TCP (or Modbus RTU over TCP) messages
1010
- calls checkRequest
1111
12-
processRequests
12+
scanRequest
1313
- inserts scan request into queue
14-
- optimizes queue
1514
1615
checkRequest
1716
- checks Modbus TCP/UDP requests (correct MBAP header, CRC in case of Modbus RTU over TCP/UDP)
@@ -50,8 +49,10 @@ bool scanReqInQueue = false;
5049
byte priorityReqInQueue;
5150

5251
// array for storing error counts
53-
uint16_t errorCount[STAT_NUM];
54-
uint16_t errorInvalid;
52+
uint16_t errorCount[STAT_ERROR_0B_QUEUE]; // there is no counter for STAT_ERROR_0B_QUEUE
53+
uint16_t errorTcpCount;
54+
uint16_t errorRtuCount;
55+
uint16_t errorTimeoutCount;
5556

5657
uint16_t queueDataSize;
5758
uint8_t queueHeadersSize;
@@ -68,15 +69,15 @@ typedef struct {
6869
} header;
6970

7071
// each request is stored in 3 queues (all queues are written to, read and deleted in sync)
71-
CircularBuffer<header, MAX_QUEUE_REQUESTS> queueHeaders; // queue of requests' headers and metadata
72+
CircularBuffer<header, MAX_QUEUE_REQUESTS> queueHeaders; // queue of requests' headers and metadata
7273
CircularBuffer<byte, MAX_QUEUE_DATA> queueData; // queue of PDU data
7374

7475
void recvUdp() {
7576
unsigned int msgLength = Udp.parsePacket();
7677
if (msgLength) {
7778
#ifdef ENABLE_EXTRA_DIAG
7879
ethRxCount += msgLength;
79-
#endif /* ENABLE_EXTRA_DIAG */
80+
#endif /* ENABLE_EXTRA_DIAG */
8081
byte inBuffer[MODBUS_SIZE + 4]; // Modbus TCP frame is 4 bytes longer than Modbus RTU frame
8182
// Modbus TCP/UDP frame: [0][1] transaction ID, [2][3] protocol ID, [4][5] length and [6] unit ID (address)..... no CRC
8283
// Modbus RTU frame: [0] address.....[n-1][n] CRC
@@ -117,7 +118,7 @@ void recvTcp() {
117118
unsigned int msgLength = client.available();
118119
#ifdef ENABLE_EXTRA_DIAG
119120
ethRxCount += msgLength;
120-
#endif /* ENABLE_EXTRA_DIAG */
121+
#endif /* ENABLE_EXTRA_DIAG */
121122
byte inBuffer[MODBUS_SIZE + 4]; // Modbus TCP frame is 4 bytes longer than Modbus RTU frame
122123
// Modbus TCP/UDP frame: [0][1] transaction ID, [2][3] protocol ID, [4][5] length and [6] unit ID (address).....
123124
// Modbus RTU frame: [0] address.....
@@ -150,18 +151,18 @@ void recvTcp() {
150151
}
151152
}
152153

153-
void processRequests() {
154+
void scanRequest() {
154155
// Insert scan request into queue, allow only one scan request in a queue
155156
if (scanCounter != 0 && queueHeaders.available() > 1 && queueData.available() > sizeof(SCAN_COMMAND) + 1 && scanReqInQueue == false) {
156157
scanReqInQueue = true;
157158
// Store scan request in request queue
158159
queueHeaders.push(header{
159-
{ 0x00, 0x00 }, // tid[2]
160+
{ 0x00, 0x00 }, // tid[2]
160161
sizeof(SCAN_COMMAND) + 1, // msgLen
161-
{}, // remIP
162-
0, // remPort
163-
SCAN_REQUEST, // requestType
164-
0, // atts
162+
{}, // remIP
163+
0, // remPort
164+
SCAN_REQUEST, // requestType
165+
0, // atts
165166
});
166167
queueData.push(scanCounter); // address of the scanned slave
167168
for (byte i = 0; i < sizeof(SCAN_COMMAND); i++) {
@@ -170,32 +171,19 @@ void processRequests() {
170171
scanCounter++;
171172
if (scanCounter == MAX_SLAVES + 1) scanCounter = 0;
172173
}
173-
// Optimize queue (prioritize requests from responding slaves) and trigger sending via serial
174-
if (serialState == IDLE) { // send new data over serial only if we are not waiting for response
175-
if (!queueHeaders.isEmpty()) {
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-
}
183-
serialState = SENDING; // trigger sendSerial()
184-
}
185-
}
186174
}
187175

188176
byte checkRequest(const byte inBuffer[], unsigned int msgLength, const IPAddress remoteIP, const unsigned int remotePort, byte requestType) {
189177
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)
190178
if (localConfig.enableRtuOverTcp) { // check CRC for Modbus RTU over TCP/UDP
191179
if (checkCRC(inBuffer, msgLength) == false) {
192-
errorInvalid++;
193-
return 0; // drop request and do not return an code
180+
errorTcpCount++;
181+
return 0; // drop request and do not return any error code
194182
}
195183
} else { // check MBAP header structure for Modbus TCP/UDP
196184
if (inBuffer[2] != 0x00 || inBuffer[3] != 0x00 || inBuffer[4] != 0x00 || inBuffer[5] != msgLength - 6) {
197-
errorInvalid++;
198-
return 0; // drop request and do not return an code
185+
errorTcpCount++;
186+
return 0; // drop request and do not return any error code
199187
}
200188
}
201189
msgLength = msgLength - addressPos - (2 * localConfig.enableRtuOverTcp); // in Modbus RTU over TCP/UDP do not store CRC
@@ -215,7 +203,9 @@ byte checkRequest(const byte inBuffer[], unsigned int msgLength, const IPAddress
215203
requestType = requestType | PRIORITY_REQUEST;
216204
priorityReqInQueue++;
217205
}
218-
206+
if (inBuffer[addressPos] == 0x00) { // Modbus Broadcast
207+
requestType = requestType | SCAN_REQUEST; // Treat broadcast as scan (only one attempt, short timeout, do not expect response)
208+
}
219209
// all checkes passed OK, we can store the incoming data in request queue
220210
// Store in request queue
221211
queueHeaders.push(header{
@@ -259,6 +249,6 @@ void setSlaveStatus(const uint8_t slave, byte status, const bool value) {
259249
stat[i][slave / 8] &= ~masks[slave & 7]; // set all other flags to false
260250
}
261251
stat[status][slave / 8] |= masks[slave & 7];
262-
errorCount[status]++;
252+
if (status != STAT_ERROR_0B_QUEUE) errorCount[status]++; // there is no counter for STAT_ERROR_0B_QUEUE
263253
}
264254
}

0 commit comments

Comments
 (0)