Skip to content

Commit b16885c

Browse files
committed
Bugfix Modbus RTU Request form, code comments
1 parent 35b8b19 commit b16885c

File tree

6 files changed

+280
-157
lines changed

6 files changed

+280
-157
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ Allows you to connect your Modbus devices (such as sensors, energy meters, HVAC
6666
- all web interface inputs have proper validation
6767
- factory defaults for user settings can be specified in advanced_settings.h
6868
- settings marked \* are only available if ENABLE_DHCP is defined in the sketch
69-
- settings marked \*\* are only available if ENABLE_EXTRA_DIAG is defined in the sketch
69+
- settings marked \*\* are only available if ENABLE_EXTENDED_WEBUI is defined in the sketch
7070
* advanced settings:
7171
- can be changed in sketch (advanced_settings.h)
7272
- stored in flash memory
@@ -98,7 +98,7 @@ Connect your Arduino to ethernet and use your web browser to access the web inte
9898
Enjoy :-)
9999
# Settings
100100
- settings marked \* are only available if ENABLE_DHCP is defined in the sketch
101-
- settings marked \*\* are only available if ENABLE_EXTRA_DIAG is defined in the sketch
101+
- settings marked \*\* are only available if ENABLE_EXTENDED_WEBUI is defined in the sketch
102102

103103
## System Info
104104
<img src="pics/modbus1.png" alt="modbus1" style="zoom:100%;" />
@@ -260,7 +260,7 @@ The number of used sockets is determined (by the Ethernet.h library) based on mi
260260

261261
## Memory
262262

263-
Not everything could fit into the limited flash memory of Arduino Nano / Uno. If you have a microcontroller with more memory (such as Mega), you can enable extra settings in the main sketch by defining ENABLE_DHCP and/or ENABLE_EXTRA_DIAG in advanced settings.
263+
Not everything could fit into the limited flash memory of Arduino Nano / Uno. If you have a microcontroller with more memory (such as Mega), you can enable extra settings in the main sketch by defining ENABLE_DHCP and/or ENABLE_EXTENDED_WEBUI in advanced settings.
264264

265265
# Links and Credits
266266

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

Lines changed: 80 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,8 @@
1-
/* *******************************************************************
2-
Ethernet and serial interface functions
3-
4-
startSerial()
5-
- starts HW serial interface which we use for RS485 line
6-
7-
charTime(), charTimeOut(), frameDelay()
8-
- calculate Modbus RTU character timeout and inter-frame delay
9-
10-
startEthernet()
11-
- initiates ethernet interface
12-
- if enabled, gets IP from DHCP
13-
- starts all servers (Modbus TCP, UDP, web server)
14-
15-
resetFunc()
16-
- well... resets Arduino
17-
18-
maintainDhcp()
19-
- maintain DHCP lease
20-
21-
maintainUptime()
22-
- maintains up time in case of millis() overflow
23-
24-
maintainCounters(), rollover()
25-
- synchronizes roll-over of data counters to zero
26-
27-
resetStats()
28-
- resets Modbus stats
29-
30-
generateMac()
31-
- generate random MAC using pseudo random generator (faster and than build-in random())
32-
33-
manageSockets()
34-
- closes sockets which are waiting to be closed or which refuse to close
35-
- forwards sockets with data available (webserver or Modbus TCP) for further processing
36-
- disconnects (closes) sockets which are too old / idle for too long
37-
- opens new sockets if needed (and if available)
38-
39-
CreateTrulyRandomSeed()
40-
- seed pseudorandom generator using watch dog timer interrupt (works only on AVR)
41-
- see https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library/arduino-random-seed
42-
43-
44-
+ preprocessor code for identifying microcontroller board
45-
46-
***************************************************************** */
47-
48-
1+
/**************************************************************************/
2+
/*!
3+
@brief Initiates HW serial interface which we use for the RS485 line.
4+
*/
5+
/**************************************************************************/
496
void startSerial() {
507
mySerial.begin((data.config.baud * 100UL), data.config.serialConfig);
518
#ifdef RS485_CONTROL_PIN
@@ -54,7 +11,7 @@ void startSerial() {
5411
#endif /* RS485_CONTROL_PIN */
5512
}
5613

57-
// number of bits per character (11 in default Modbus RTU settings)
14+
// Number of bits per character (11 in default Modbus RTU settings)
5815
byte bitsPerChar() {
5916
byte bits =
6017
1 + // start bit
@@ -64,7 +21,7 @@ byte bitsPerChar() {
6421
return bits;
6522
}
6623

67-
// character timeout in micros
24+
// Character timeout in micros
6825
uint32_t charTimeOut() {
6926
if (data.config.baud <= 192) {
7027
return (15000UL * bitsPerChar()) / data.config.baud; // inter-character time-out should be 1,5T
@@ -73,7 +30,7 @@ uint32_t charTimeOut() {
7330
}
7431
}
7532

76-
// minimum frame delay in micros
33+
// Minimum frame delay in micros
7734
uint32_t frameDelay() {
7835
if (data.config.baud <= 192) {
7936
return (35000UL * bitsPerChar()) / data.config.baud; // inter-frame delay should be 3,5T
@@ -82,6 +39,12 @@ uint32_t frameDelay() {
8239
}
8340
}
8441

42+
/**************************************************************************/
43+
/*!
44+
@brief Initiates ethernet interface, if DHCP enabled, gets IP from DHCP,
45+
starts all servers (UDP, web server).
46+
*/
47+
/**************************************************************************/
8548
void startEthernet() {
8649
if (ETH_RESET_PIN != 0) {
8750
pinMode(ETH_RESET_PIN, OUTPUT);
@@ -112,8 +75,18 @@ void startEthernet() {
11275
#endif
11376
}
11477

78+
/**************************************************************************/
79+
/*!
80+
@brief Resets Arduino (works only on AVR chips).
81+
*/
82+
/**************************************************************************/
11583
void (*resetFunc)(void) = 0; //declare reset function at address 0
11684

85+
/**************************************************************************/
86+
/*!
87+
@brief Checks SPI connection to the W5X00 chip.
88+
*/
89+
/**************************************************************************/
11790
void checkEthernet() {
11891
static byte attempts = 0;
11992
IPAddress tempIP = Ethernet.localIP();
@@ -128,6 +101,11 @@ void checkEthernet() {
128101
checkEthTimer.sleep(CHECK_ETH_INTERVAL);
129102
}
130103

104+
/**************************************************************************/
105+
/*!
106+
@brief Maintains DHCP lease.
107+
*/
108+
/**************************************************************************/
131109
#ifdef ENABLE_DHCP
132110
void maintainDhcp() {
133111
if (data.config.enableDhcp && dhcpSuccess == true) { // only call maintain if initial DHCP request by startEthernet was successfull
@@ -136,6 +114,11 @@ void maintainDhcp() {
136114
}
137115
#endif /* ENABLE_DHCP */
138116

117+
/**************************************************************************/
118+
/*!
119+
@brief Maintains uptime in case of millis() overflow.
120+
*/
121+
/**************************************************************************/
139122
#ifdef ENABLE_EXTENDED_WEBUI
140123
void maintainUptime() {
141124
uint32_t milliseconds = millis();
@@ -152,8 +135,12 @@ void maintainUptime() {
152135
}
153136
#endif /* ENABLE_EXTENDED_WEBUI */
154137

138+
/**************************************************************************/
139+
/*!
140+
@brief Synchronizes roll-over of data counters to zero.
141+
*/
142+
/**************************************************************************/
155143
bool rollover() {
156-
// synchronize roll-over of run time, data counters and modbus stats to zero, at 0xFFFFFF00
157144
const uint32_t ROLLOVER = 0xFFFFFF00;
158145
for (byte i = 0; i < ERROR_LAST; i++) {
159146
if (data.errorCnt[i] > ROLLOVER) {
@@ -173,7 +160,11 @@ bool rollover() {
173160
return false;
174161
}
175162

176-
// resets counters to 0: data.errorCnt, data.rtuCnt, data.ethCnt
163+
/**************************************************************************/
164+
/*!
165+
@brief Resets error stats, RTU counter and ethernet data counter.
166+
*/
167+
/**************************************************************************/
177168
void resetStats() {
178169
memset(data.errorCnt, 0, sizeof(data.errorCnt));
179170
#ifdef ENABLE_EXTENDED_WEBUI
@@ -183,7 +174,12 @@ void resetStats() {
183174
#endif /* ENABLE_EXTENDED_WEBUI */
184175
}
185176

186-
// generate new MAC (bytes 0, 1 and 2 are static, bytes 3, 4 and 5 are generated randomly)
177+
/**************************************************************************/
178+
/*!
179+
@brief Generate random MAC using pseudo random generator,
180+
bytes 0, 1 and 2 are static (MAC_START), bytes 3, 4 and 5 are generated randomly
181+
*/
182+
/**************************************************************************/
187183
void generateMac() {
188184
// Marsaglia algorithm from https://github.com/RobTillaart/randomHelpers
189185
seed1 = 36969L * (seed1 & 65535L) + (seed1 >> 16);
@@ -196,21 +192,29 @@ void generateMac() {
196192
}
197193
}
198194

195+
/**************************************************************************/
196+
/*!
197+
@brief Write (update) data to Arduino EEPROM.
198+
*/
199+
/**************************************************************************/
199200
void updateEeprom() {
200201
eepromTimer.sleep(EEPROM_INTERVAL * 60UL * 60UL * 1000UL); // EEPROM_INTERVAL is in hours, sleep is in milliseconds!
201202
data.eepromWrites++; // we assume that at least some bytes are written to EEPROM during EEPROM.update or EEPROM.put
202203
EEPROM.put(DATA_START, data);
203204
}
204205

205-
#if MAX_SOCK_NUM == 8
206-
uint32_t lastSocketUse[MAX_SOCK_NUM] = { 0, 0, 0, 0, 0, 0, 0, 0 };
207-
byte socketInQueue[MAX_SOCK_NUM] = { 0, 0, 0, 0, 0, 0, 0, 0 };
208-
#elif MAX_SOCK_NUM == 4
209-
uint32_t lastSocketUse[MAX_SOCK_NUM] = { 0, 0, 0, 0 };
210-
byte socketInQueue[MAX_SOCK_NUM] = { 0, 0, 0, 0 };
211-
#endif
212206

213-
// from https://github.com/SapientHetero/Ethernet/blob/master/src/socket.cpp
207+
uint32_t lastSocketUse[MAX_SOCK_NUM];
208+
byte socketInQueue[MAX_SOCK_NUM];
209+
/**************************************************************************/
210+
/*!
211+
@brief Closes sockets which are waiting to be closed or which refuse to close,
212+
forwards sockets with data available for further processing by the webserver,
213+
disconnects (closes) sockets which are too old (idle for too long), opens
214+
new sockets if needed (and if available).
215+
From https://github.com/SapientHetero/Ethernet/blob/master/src/socket.cpp
216+
*/
217+
/**************************************************************************/
214218
void manageSockets() {
215219
uint32_t maxAge = 0; // the 'age' of the socket in a 'disconnectable' state that was last used the longest time ago
216220
byte oldest = MAX_SOCK_NUM; // the socket number of the 'oldest' disconnectable socket
@@ -308,6 +312,12 @@ void manageSockets() {
308312
// we do not need SPI.beginTransaction(SPI_ETHERNET_SETTINGS) or SPI.endTransaction() ??
309313
}
310314

315+
/**************************************************************************/
316+
/*!
317+
@brief Disconnect or close a socket.
318+
@param s Socket number.
319+
*/
320+
/**************************************************************************/
311321
void disconSocket(byte s) {
312322
if (W5100.readSnSR(s) == SnSR::ESTABLISHED) {
313323
W5100.execCmdSn(s, Sock_DISCON); // Sock_DISCON does not close LISTEN sockets
@@ -317,7 +327,13 @@ void disconSocket(byte s) {
317327
}
318328
}
319329

320-
// https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library/arduino-random-seed
330+
331+
/**************************************************************************/
332+
/*!
333+
@brief Seed pseudorandom generator using watch dog timer interrupt (works only on AVR).
334+
See https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library/arduino-random-seed
335+
*/
336+
/**************************************************************************/
321337
void CreateTrulyRandomSeed() {
322338
seed1 = 0;
323339
nrot = 32; // Must be at least 4, but more increased the uniformity of the produced seeds entropy.
@@ -344,11 +360,9 @@ ISR(WDT_vect) {
344360
seed1 = seed1 ^ TCNT1L;
345361
}
346362

347-
// Board definitions
363+
// Preprocessor code for identifying microcontroller board
348364
#if defined(TEENSYDUINO)
349-
350365
// --------------- Teensy -----------------
351-
352366
#if defined(__AVR_ATmega32U4__)
353367
#define BOARD F("Teensy 2.0")
354368
#elif defined(__AVR_AT90USB1286__)
@@ -366,9 +380,7 @@ ISR(WDT_vect) {
366380
#else
367381
#define BOARD F("Unknown Board")
368382
#endif
369-
370383
#else // --------------- Arduino ------------------
371-
372384
#if defined(ARDUINO_AVR_ADK)
373385
#define BOARD F("Arduino Mega Adk")
374386
#elif defined(ARDUINO_AVR_BT) // Bluetooth
@@ -422,5 +434,4 @@ ISR(WDT_vect) {
422434
#else
423435
#define BOARD F("Unknown Board")
424436
#endif
425-
426437
#endif

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ byte masks[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
3939

4040
uint16_t crc;
4141

42+
/**************************************************************************/
43+
/*!
44+
@brief Receives Modbus UDP (or Modbus RTU over UDP) messages, calls @ref checkRequest()
45+
*/
46+
/**************************************************************************/
4247
void recvUdp() {
4348
uint16_t msgLength = Udp.parsePacket();
4449
if (msgLength) {
@@ -80,6 +85,11 @@ void recvUdp() {
8085
}
8186
}
8287

88+
/**************************************************************************/
89+
/*!
90+
@brief Receives Modbus TCP (or Modbus RTU over TCP) messages, calls @ref checkRequest()
91+
*/
92+
/**************************************************************************/
8393
void recvTcp(EthernetClient &client) {
8494
uint16_t msgLength = client.available();
8595
#ifdef ENABLE_EXTENDED_WEBUI
@@ -150,6 +160,19 @@ void scanRequest() {
150160
}
151161
}
152162

163+
/**************************************************************************/
164+
/*!
165+
@brief Checks Modbus TCP/UDP requests (correct MBAP header,
166+
CRC in case of Modbus RTU over TCP/UDP), checks availability of queue,
167+
stores requests into queue or returns an error.
168+
@param inBuffer Modbus TCP/UDP requests
169+
@param msgLength Length of the Modbus TCP/UDP requests
170+
@param remoteIP Remote IP
171+
@param remotePort Remote port
172+
@param requestType UDP or TCP, priority or scan request
173+
@return Modbus error code to be sent back to the recipient.
174+
*/
175+
/**************************************************************************/
153176
byte checkRequest(byte inBuffer[], uint16_t msgLength, const uint32_t remoteIP, const uint16_t remotePort, byte requestType) {
154177
byte addressPos = 6 * !data.config.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)
155178
if (data.config.enableRtuOverTcp) { // check CRC for Modbus RTU over TCP/UDP
@@ -167,12 +190,12 @@ byte checkRequest(byte inBuffer[], uint16_t msgLength, const uint32_t remoteIP,
167190
// check if we have space in request queue
168191
if (queueHeaders.available() < 1 || queueData.available() < msgLength) {
169192
setSlaveStatus(inBuffer[addressPos], SLAVE_ERROR_0A, true, false);
170-
return 0x0A; // return modbus error 0x0A (Gateway Overloaded)
193+
return 0x0A; // return Modbus error code 10 (Gateway Overloaded)
171194
}
172195
// allow only one request to non responding slaves
173196
if (getSlaveStatus(inBuffer[addressPos], SLAVE_ERROR_0B_QUEUE)) {
174197
data.errorCnt[SLAVE_ERROR_0B]++;
175-
return 0x0B; // return modbus error 11 (Gateway Target Device Failed to Respond)
198+
return 0x0B; // return Modbus error code 11 (Gateway Target Device Failed to Respond)
176199
} else if (getSlaveStatus(inBuffer[addressPos], SLAVE_ERROR_0B)) {
177200
setSlaveStatus(inBuffer[addressPos], SLAVE_ERROR_0B_QUEUE, true, false);
178201
} else {

0 commit comments

Comments
 (0)