Skip to content

Commit 0152d03

Browse files
committed
Modbus requests via WebUI
1 parent 9281c1d commit 0152d03

File tree

6 files changed

+908
-588
lines changed

6 files changed

+908
-588
lines changed

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

Lines changed: 134 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,7 @@
3535

3636

3737
void startSerial() {
38-
mySerial.flush();
39-
mySerial.end();
40-
queueHeaders.clear(); // <- eats memory!
41-
queueData.clear();
42-
scanReqInQueue = false;
43-
priorityReqInQueue= false;
44-
memset(stat[STAT_ERROR_0B_QUEUE], 0, sizeof(stat[STAT_ERROR_0B_QUEUE]));
45-
sendTimer.sleep(0);
46-
mySerial.begin(localConfig.baud, localConfig.serialConfig);
38+
mySerial.begin((localConfig.baud * 100UL), localConfig.serialConfig);
4739
#ifdef RS485_CONTROL_PIN
4840
pinMode(RS485_CONTROL_PIN, OUTPUT);
4941
digitalWrite(RS485_CONTROL_PIN, RS485_RECEIVE); // Init Transceiver
@@ -57,12 +49,12 @@ unsigned long charTime() {
5749
(((localConfig.serialConfig & 0x06) >> 1) + 5) + // data bits
5850
(((localConfig.serialConfig & 0x08) >> 3) + 1); // stop bits
5951
if (((localConfig.serialConfig & 0x30) >> 4) > 1) bits += 1; // parity bit (if present)
60-
return (bits * 1000000UL) / (unsigned long)localConfig.baud;
52+
return (bits * 10000UL) / (unsigned long)localConfig.baud;
6153
}
6254

6355
// character timeout
6456
unsigned long charTimeOut() {
65-
if (localConfig.baud <= 19200UL) {
57+
if (localConfig.baud <= 192) {
6658
return 1.5 * charTime(); // inter-character time-out should be 1,5T
6759
} else {
6860
return 750;
@@ -71,7 +63,7 @@ unsigned long charTimeOut() {
7163

7264
// minimum frame delay
7365
unsigned long frameDelay() {
74-
if (localConfig.baud <= 19200UL) {
66+
if (localConfig.baud <= 192) {
7567
return 3.5 * charTime(); // inter-frame delay should be 3,5T
7668
} else {
7769
return 1750; // 1750 μs
@@ -99,14 +91,16 @@ void startEthernet() {
9991
#else /* ENABLE_DHCP */
10092
Ethernet.begin(mac, localConfig.ip, {}, localConfig.gateway, localConfig.subnet); // No DNS
10193
#endif /* ENABLE_DHCP */
94+
Ethernet.setRetransmissionTimeout(TCP_RETRANSMISSION_TIMEOUT);
95+
Ethernet.setRetransmissionCount(TCP_RETRANSMISSION_COUNT);
10296
modbusServer = EthernetServer(localConfig.tcpPort);
10397
webServer = EthernetServer(localConfig.webPort);
10498
Udp.begin(localConfig.udpPort);
10599
modbusServer.begin();
106100
webServer.begin();
107-
if (Ethernet.hardwareStatus() == EthernetW5100) {
108-
maxSockNum = 4; // W5100 chip never supports more than 4 sockets
109-
}
101+
#if MAX_SOCK_NUM > 4
102+
if (W5100.getChip() == 51) maxSockNum = 4; // W5100 chip never supports more than 4 sockets
103+
#endif
110104
}
111105

112106
void (*resetFunc)(void) = 0; //declare reset function at address 0
@@ -123,6 +117,7 @@ void maintainDhcp() {
123117
}
124118
#endif /* ENABLE_DHCP */
125119

120+
#ifdef ENABLE_EXTRA_DIAG
126121
void maintainUptime() {
127122
unsigned long milliseconds = millis();
128123
if (last_milliseconds > milliseconds) {
@@ -136,19 +131,23 @@ void maintainUptime() {
136131
//We add the "remaining_seconds", so that we can continue measuring the time passed from the last boot of the device.
137132
seconds = (milliseconds / 1000) + remaining_seconds;
138133
}
134+
#endif /* ENABLE_EXTRA_DIAG */
139135

140136
bool rollover() {
141137
// synchronize roll-over of run time, data counters and modbus stats to zero, at 0xFFFFFF00
142138
const unsigned long ROLLOVER = 0xFFFFFF00;
143-
for (byte i = 0; i < STAT_ERROR_0B_QUEUE; i++) { // there is no counter for STAT_ERROR_0B_QUEUE
139+
for (byte i = 0; i < SLAVE_ERROR_0B_QUEUE; i++) { // there is no counter for SLAVE_ERROR_0B_QUEUE
144140
if (errorCount[i] > ROLLOVER) {
145141
return true;
146142
}
147143
}
148-
if (errorTcpCount > ROLLOVER || errorRtuCount > ROLLOVER || errorTimeoutCount > ROLLOVER || seconds > ROLLOVER) {
144+
if (errorTcpCount > ROLLOVER || errorRtuCount > ROLLOVER || errorTimeoutCount > ROLLOVER) {
149145
return true;
150146
}
151147
#ifdef ENABLE_EXTRA_DIAG
148+
if (seconds > ROLLOVER) {
149+
return true;
150+
}
152151
if (serialTxCount > ROLLOVER || serialRxCount > ROLLOVER || ethTxCount > ROLLOVER || ethRxCount > ROLLOVER) {
153152
return true;
154153
}
@@ -161,8 +160,8 @@ void resetStats() {
161160
errorTcpCount = 0;
162161
errorRtuCount = 0;
163162
errorTimeoutCount = 0;
164-
remaining_seconds = -(millis() / 1000);
165163
#ifdef ENABLE_EXTRA_DIAG
164+
remaining_seconds = -(millis() / 1000);
166165
ethRxCount = 0;
167166
ethTxCount = 0;
168167
serialRxCount = 0;
@@ -181,6 +180,123 @@ void generateMac() {
181180
}
182181
}
183182

183+
184+
185+
#if MAX_SOCK_NUM == 8
186+
unsigned long lastSocketUse[MAX_SOCK_NUM] = { 0, 0, 0, 0, 0, 0, 0, 0 }; // +rs 03Feb2019 - records last interaction involving each socket to enable detecting sockets unused for longest time period
187+
byte socketInQueue[MAX_SOCK_NUM] = { 0, 0, 0, 0, 0, 0, 0, 0 };
188+
#elif MAX_SOCK_NUM == 4
189+
unsigned long lastSocketUse[MAX_SOCK_NUM] = { 0, 0, 0, 0 }; // +rs 03Feb2019 - records last interaction involving each socket to enable detecting sockets unused for longest time period
190+
byte socketInQueue[MAX_SOCK_NUM] = { 0, 0, 0, 0 };
191+
#endif
192+
193+
// from https://github.com/SapientHetero/Ethernet/blob/master/src/socket.cpp
194+
void manageSockets() {
195+
uint32_t maxAge = 0; // the 'age' of the socket in a 'disconnectable' state that was last used the longest time ago
196+
byte oldest = MAX_SOCK_NUM; // the socket number of the 'oldest' disconnectable socket
197+
byte modbusListening = MAX_SOCK_NUM;
198+
byte webListening = MAX_SOCK_NUM;
199+
byte dataAvailable = MAX_SOCK_NUM;
200+
byte socketsAvailable = 0;
201+
// SPI.beginTransaction(SPI_ETHERNET_SETTINGS); // begin SPI transaction
202+
// look at all the hardware sockets, record and take action based on current states
203+
for (byte s = 0; s < maxSockNum; s++) { // for each hardware socket ...
204+
byte status = W5100.readSnSR(s); // get socket status...
205+
uint32_t sockAge = millis() - lastSocketUse[s]; // age of the current socket
206+
if (socketInQueue[s] > 0) {
207+
lastSocketUse[s] = millis();
208+
continue; // do not close Modbus TCP sockets currently processed (in queue)
209+
}
210+
211+
switch (status) {
212+
case SnSR::CLOSED:
213+
{
214+
socketsAvailable++;
215+
}
216+
break;
217+
case SnSR::LISTEN:
218+
case SnSR::SYNRECV:
219+
{
220+
lastSocketUse[s] = millis();
221+
if (W5100.readSnPORT(s) == localConfig.webPort) {
222+
webListening = s;
223+
} else {
224+
modbusListening = s;
225+
}
226+
}
227+
break;
228+
case SnSR::FIN_WAIT:
229+
case SnSR::CLOSING:
230+
case SnSR::TIME_WAIT:
231+
case SnSR::LAST_ACK:
232+
{
233+
socketsAvailable++; // socket will be available soon
234+
if (sockAge > TCP_DISCON_TIMEOUT) { // if it's been more than TCP_CLIENT_DISCON_TIMEOUT since disconnect command was sent...
235+
W5100.execCmdSn(s, Sock_CLOSE); // send CLOSE command...
236+
lastSocketUse[s] = millis(); // and record time at which it was sent so we don't do it repeatedly.
237+
}
238+
}
239+
break;
240+
case SnSR::ESTABLISHED:
241+
case SnSR::CLOSE_WAIT:
242+
{
243+
if (EthernetClient(s).available() > 0) {
244+
dataAvailable = s;
245+
lastSocketUse[s] = millis();
246+
} else {
247+
// remote host closed connection, our end still open
248+
if (status == SnSR::CLOSE_WAIT) {
249+
socketsAvailable++; // socket will be available soon
250+
W5100.execCmdSn(s, Sock_DISCON); // send DISCON command...
251+
lastSocketUse[s] = millis(); // record time at which it was sent...
252+
// status becomes LAST_ACK for short time
253+
} else if (((W5100.readSnPORT(s) == localConfig.webPort && sockAge > TCP_WEB_DISCON_AGE)
254+
|| (W5100.readSnPORT(s) == localConfig.tcpPort && sockAge > (localConfig.tcpTimeout * 1000UL)))
255+
&& sockAge > maxAge) {
256+
oldest = s; // record the socket number...
257+
maxAge = sockAge; // and make its age the new max age.
258+
}
259+
}
260+
}
261+
break;
262+
default:
263+
break;
264+
}
265+
}
266+
267+
if (dataAvailable != MAX_SOCK_NUM) {
268+
EthernetClient client = EthernetClient(dataAvailable);
269+
if (W5100.readSnPORT(dataAvailable) == localConfig.webPort) {
270+
recvWeb(client);
271+
} else {
272+
recvTcp(client);
273+
}
274+
}
275+
276+
if (modbusListening == MAX_SOCK_NUM) {
277+
modbusServer.begin();
278+
} else if (webListening == MAX_SOCK_NUM) {
279+
webServer.begin();
280+
}
281+
282+
// If needed, disconnect socket that's been idle (ESTABLISHED without data recieved) the longest
283+
if (oldest != MAX_SOCK_NUM && socketsAvailable == 0 && (webListening == MAX_SOCK_NUM || modbusListening == MAX_SOCK_NUM)) {
284+
disconSocket(oldest);
285+
}
286+
287+
// SPI.endTransaction(); // Serves to o release the bus for other devices to access it. Since the ethernet chip is the only device
288+
// we do not need SPI.beginTransaction(SPI_ETHERNET_SETTINGS) or SPI.endTransaction()
289+
}
290+
291+
void disconSocket(byte s) {
292+
if (W5100.readSnSR(s) == SnSR::ESTABLISHED) {
293+
W5100.execCmdSn(s, Sock_DISCON); // Sock_DISCON does not close LISTEN sockets
294+
lastSocketUse[s] = millis(); // record time at which it was sent...
295+
} else {
296+
W5100.execCmdSn(s, Sock_CLOSE); // send DISCON command...
297+
}
298+
}
299+
184300
// https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library/arduino-random-seed
185301
void CreateTrulyRandomSeed() {
186302
seed1 = 0;

0 commit comments

Comments
 (0)