Skip to content

Commit a6da405

Browse files
committed
Fetch API
1 parent 39956f8 commit a6da405

File tree

6 files changed

+285
-242
lines changed

6 files changed

+285
-242
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@
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);
3846
mySerial.begin(localConfig.baud, localConfig.serialConfig);
3947
#ifdef RS485_CONTROL_PIN
4048
pinMode(RS485_CONTROL_PIN, OUTPUT);

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

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,8 @@
3131
#define UDP_REQUEST B00100000 // UDP request
3232
#define TCP_REQUEST B00001111 // TCP request, also stores TCP client number
3333

34-
// bool arrays for storing Modbus RTU status of individual slaves
35-
uint8_t stat[STAT_NUM][(MAX_SLAVES + 1 + 7) / 8];
36-
37-
// Scan request is in the queue
38-
bool scanReqInQueue = false;
39-
// Counter for priority requests in the queue
40-
byte priorityReqInQueue;
41-
4234
uint8_t masks[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
4335

44-
typedef struct {
45-
byte tid[2]; // MBAP Transaction ID
46-
byte msgLen; // lenght of Modbus message stored in queueData
47-
IPAddress remIP; // remote IP for UDP client (UDP response is sent back to remote IP)
48-
unsigned int remPort; // remote port for UDP client (UDP response is sent back to remote port)
49-
byte requestType; // TCP client who sent the request
50-
byte atts; // attempts counter
51-
} header;
52-
53-
// each request is stored in 3 queues (all queues are written to, read and deleted in sync)
54-
CircularBuffer<header, MAX_QUEUE_REQUESTS> queueHeaders; // queue of requests' headers and metadata
55-
CircularBuffer<byte, MAX_QUEUE_DATA> queueData; // queue of PDU data
56-
5736
byte addressPos;
5837

5938
void recvUdp() {
@@ -173,15 +152,15 @@ byte checkRequest(const byte inBuffer[], unsigned int msgLength, const IPAddress
173152
msgLength = msgLength - addressPos - (2 * localConfig.enableRtuOverTcp); // in Modbus RTU over TCP/UDP do not store CRC
174153
// check if we have space in request queue
175154
if (queueHeaders.available() < 1 || queueData.available() < msgLength) {
176-
setSlaveStatus(inBuffer[addressPos], STAT_ERROR_0A, true);
155+
setSlaveStatus(inBuffer[addressPos], STAT_ERROR_0A, true, false);
177156
return 0x0A; // return modbus error 0x0A (Gateway Overloaded)
178157
}
179158
// allow only one request to non responding slaves
180159
if (getSlaveStatus(inBuffer[addressPos], STAT_ERROR_0B_QUEUE)) {
181160
errorCount[STAT_ERROR_0B]++;
182161
return 0x0B; // return modbus error 11 (Gateway Target Device Failed to Respond) - usually means that target device (address) is not present
183162
} else if (getSlaveStatus(inBuffer[addressPos], STAT_ERROR_0B)) {
184-
setSlaveStatus(inBuffer[addressPos], STAT_ERROR_0B_QUEUE, true);
163+
setSlaveStatus(inBuffer[addressPos], STAT_ERROR_0B_QUEUE, true, false);
185164
} else {
186165
// Add PRIORITY_REQUEST flag to requests for responding slaves
187166
requestType = requestType | PRIORITY_REQUEST;
@@ -224,7 +203,7 @@ bool getSlaveStatus(const uint8_t slave, const byte status) {
224203
return (stat[status][slave / 8] & masks[slave & 7]) > 0;
225204
}
226205

227-
void setSlaveStatus(const uint8_t slave, byte status, const bool value) {
206+
void setSlaveStatus(const uint8_t slave, byte status, const bool value, const bool isScan) {
228207
if (slave >= MAX_SLAVES) return; // error
229208
if (value == 0) {
230209
stat[status][slave / 8] &= ~masks[slave & 7];
@@ -233,6 +212,6 @@ void setSlaveStatus(const uint8_t slave, byte status, const bool value) {
233212
stat[i][slave / 8] &= ~masks[slave & 7]; // set all other flags to false
234213
}
235214
stat[status][slave / 8] |= masks[slave & 7];
236-
if (status != STAT_ERROR_0B_QUEUE) errorCount[status]++; // there is no counter for STAT_ERROR_0B_QUEUE
215+
if (status != STAT_ERROR_0B_QUEUE && isScan == false) errorCount[status]++; // there is no counter for STAT_ERROR_0B_QUEUE, ignor scans in statistics
237216
}
238217
}

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@
1919
int rxNdx = 0;
2020
int txNdx = 0;
2121

22-
MicroTimer recvTimer;
23-
MicroTimer sendTimer;
24-
2522
void sendSerial() {
2623
if (!sendTimer.isOver()) {
2724
return;
@@ -64,9 +61,10 @@ void sendSerial() {
6461
// this if statement is not very reliable (too fast)
6562
// Serial.isFlushed() method is needed....see https://github.com/arduino/Arduino/pull/3737
6663
txNdx = 0;
64+
mySerial.flush();
6765
#ifdef RS485_CONTROL_PIN
68-
sendTimer.sleep(charTimeOut()); // very short delay before we toggle the RS485_CONTROL_PIN and disable RS485 transmit
69-
#endif /* RS485_CONTROL_PIN */
66+
// sendTimer.sleep(frameDelay()); // Short delay before we toggle the RS485_CONTROL_PIN and disable RS485 transmit. Not needed if we use flush()
67+
#endif /* RS485_CONTROL_PIN */
7068
serialState++;
7169
}
7270
}
@@ -95,7 +93,7 @@ void sendSerial() {
9593
deleteRequest();
9694
} else if (myHeader.atts >= localConfig.serialAttempts) {
9795
// send modbus error 0x0B (Gateway Target Device Failed to Respond) - usually means that target device (address) is not present
98-
setSlaveStatus(queueData[0], STAT_ERROR_0B, true);
96+
setSlaveStatus(queueData[0], STAT_ERROR_0B, true, false);
9997
byte MBAP[] = { myHeader.tid[0], myHeader.tid[1], 0x00, 0x00, 0x00, 0x03 };
10098
byte PDU[5] = { queueData[0], (byte)(queueData[1] + 0x80), 0x0B };
10199
crc = 0xFFFF;
@@ -107,7 +105,7 @@ void sendSerial() {
107105
sendResponse(MBAP, PDU, 5);
108106
errorTimeoutCount++;
109107
} else {
110-
setSlaveStatus(queueData[0], STAT_ERROR_0B_QUEUE, true);
108+
setSlaveStatus(queueData[0], STAT_ERROR_0B_QUEUE, true, false);
111109
errorTimeoutCount++;
112110
} // if (myHeader.atts >= MAX_RETRY)
113111
serialState = 0; // IDLE
@@ -136,9 +134,9 @@ void recvSerial() {
136134
header myHeader = queueHeaders.first();
137135
if (checkCRC(serialIn, rxNdx) == true && serialIn[0] == queueData[0] && serialState == WAITING) {
138136
if (serialIn[1] > 0x80 && (myHeader.requestType & SCAN_REQUEST) == false) {
139-
setSlaveStatus(serialIn[0], STAT_ERROR_0X, true);
137+
setSlaveStatus(serialIn[0], STAT_ERROR_0X, true, false);
140138
} else {
141-
setSlaveStatus(serialIn[0], STAT_OK, true);
139+
setSlaveStatus(serialIn[0], STAT_OK, true, myHeader.requestType & SCAN_REQUEST);
142140
}
143141
byte MBAP[] = { myHeader.tid[0], myHeader.tid[1], 0x00, 0x00, highByte(rxNdx - 2), lowByte(rxNdx - 2) };
144142
sendResponse(MBAP, serialIn, rxNdx);

arduino-modbus-rtu-tcp-gateway/04-webserver.ino

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ enum action_type : byte {
2828
ETH_SOFT, // Ethernet software reset
2929
SERIAL_SOFT, // Serial software reset
3030
SCAN, // Initialize RS485 scan
31-
RST_STATS, // Reset Modbus Statistics
31+
RST_STATS, // Reset Modbus Statistics
3232
WEB // Restart webserver
3333
};
3434
enum action_type action;
@@ -43,7 +43,7 @@ enum page : byte {
4343
PAGE_IP,
4444
PAGE_TCP,
4545
PAGE_RTU,
46-
PAGE_WAIT // page with "Reloading. Please wait..." message. Must be the last element within this enum!!
46+
PAGE_WAIT, // page with "Reloading. Please wait..." message.
4747
};
4848

4949
// Keys for POST parameters, used in web forms and processed by processPost() function.
@@ -79,9 +79,36 @@ enum post_key : byte {
7979
POST_FRAMEDELAY, //frame delay
8080
POST_TIMEOUT, // response timeout
8181
POST_ATTEMPTS, // number of request attempts
82-
POST_ACTION // actions on Tools page
82+
POST_ACTION, // actions on Tools page
8383
};
8484

85+
// Keys for JSON elements, used in: 1) JSON documents, 2) ID of span tags, 3) Javascript.
86+
enum JSON_type : byte {
87+
JSON_SECS, // Runtime seconds
88+
JSON_MINS, // Runtime minutes
89+
JSON_HOURS, // Runtime hours
90+
JSON_DAYS, // Runtime days
91+
JSON_ERROR, // Modbus status from array errorCount[STAT_ERROR_0B_QUEUE]
92+
JSON_ERROR_1,
93+
JSON_ERROR_2,
94+
JSON_ERROR_3,
95+
JSON_ERROR_TCP,
96+
JSON_ERROR_RTU,
97+
JSON_ERROR_TIMEOUT,
98+
JSON_QUEUE_DATA,
99+
JSON_QUEUE_REQUESTS,
100+
JSON_MASTERS, // list of Modbus TCP/UDP masters separated by <br>
101+
JSON_SLAVES, // list of Modbus RTU slaves separated by <br>
102+
#ifdef ENABLE_EXTRA_DIAG
103+
JSON_RS485_TX,
104+
JSON_RS485_RX,
105+
JSON_ETH_TX,
106+
JSON_ETH_RX,
107+
#endif /* ENABLE_EXTRA_DIAG */
108+
JSON_LAST, // Must be the very last element in this array
109+
};
110+
111+
85112
void recvWeb() {
86113
EthernetClient client = webServer.available();
87114
if (client) {
@@ -139,6 +166,7 @@ void recvWeb() {
139166
else if ((uri[0] == '/') && !strcmp(uri + 2, ".htm")) {
140167
reqPage = (byte)(uri[1] - 48); // Convert single ASCII char to byte
141168
}
169+
142170
// Actions that require "please wait" page
143171
if (action == WEB || action == REBOOT || action == ETH_SOFT || action == FACTORY || action == MAC) {
144172
reqPage = PAGE_WAIT;
@@ -147,6 +175,8 @@ void recvWeb() {
147175
// Send page
148176
if ((reqPage > 0) && (reqPage <= PAGE_WAIT))
149177
sendPage(client, reqPage);
178+
else if (!strcmp(uri, "/data.json"))
179+
sendJson(client);
150180
else if (!strcmp(uri, "/favicon.ico")) // a favicon
151181
send204(client); // if you don't have a favicon, send 204
152182
else // if the page is unknown, HTTP response code 404
@@ -156,22 +186,21 @@ void recvWeb() {
156186
if (reqPage == PAGE_WAIT) {
157187
for (byte n = 0; n < maxSockNum; n++) {
158188
// in case of webserver restart, stop only clients from old webserver (clients with port different from current settings)
159-
// EthernetClient clientTemp = EthernetClient(n);
160-
unsigned int port = W5100.readSnPORT(n);
189+
EthernetClient client = EthernetClient(n);
190+
unsigned int port = client.localPort();
161191
// for WEB, stop only clients from old webserver (those that do not match modbus ports or current web port); for other actions stop all clients
162192
if (action != WEB || (port && port != localConfig.webPort && port != localConfig.udpPort && port != localConfig.tcpPort)) {
163-
W5100.execCmdSn(n, Sock_CLOSE); // close it forcefully
164-
// clientTemp.flush();
165-
// clientTemp.stop();
193+
client.flush();
194+
client.stop();
166195
}
167196
}
168197
switch (action) {
169198
case WEB:
170199
webServer = EthernetServer(localConfig.webPort);
171200
break;
172201
case REBOOT:
173-
Serial.flush();
174-
Serial.end();
202+
mySerial.flush();
203+
mySerial.end();
175204
resetFunc();
176205
break;
177206
default: // ETH_SOFT, FACTORY, MAC
@@ -218,7 +247,7 @@ void processPost(char postParameter[]) {
218247
#endif /* ENABLE_DHCP */
219248
case POST_IP ... POST_IP_3:
220249
{
221-
action = ETH_SOFT; // this ETH_SOFT is triggered when the user changes anything on the "IP Settings" page.
250+
action = ETH_SOFT; // this ETH_SOFT is triggered when the user changes anything on the "IP Settings" page.
222251
// No need to trigger ETH_SOFT for other cases (POST_SUBNET, POST_GATEWAY etc.)
223252
localConfig.ip[paramKeyByte - POST_IP] = (byte)paramValueUlong;
224253
}
@@ -236,10 +265,10 @@ void processPost(char postParameter[]) {
236265
case POST_TCP:
237266
{
238267
for (byte i = 0; i < maxSockNum; i++) {
239-
EthernetClient clientTemp = EthernetClient(i);
240-
if (clientTemp.status() != SnSR::UDP && clientTemp.localPort() == localConfig.tcpPort) {
241-
clientTemp.flush();
242-
clientTemp.stop();
268+
EthernetClient client = EthernetClient(i);
269+
if (client.localPort() == localConfig.tcpPort) {
270+
client.flush();
271+
client.stop();
243272
}
244273
}
245274
localConfig.tcpPort = (unsigned int)paramValueUlong;
@@ -266,7 +295,7 @@ void processPost(char postParameter[]) {
266295
break;
267296
case POST_BAUD:
268297
{
269-
action = SERIAL_SOFT; // this SERIAL_SOFT is triggered when the user changes anything on the "RS485 Settings" page.
298+
action = SERIAL_SOFT; // this SERIAL_SOFT is triggered when the user changes anything on the "RS485 Settings" page.
270299
// No need to trigger ETH_SOFT for other cases (POST_DATA, POST_PARITY etc.)
271300
localConfig.baud = paramValueUlong;
272301
byte minFrameDelay = (byte)(frameDelay() / 1000UL) + 1;
@@ -313,6 +342,7 @@ void processPost(char postParameter[]) {
313342
memcpy(tempMac, localConfig.macEnd, 3); // keep current MAC
314343
localConfig = DEFAULT_CONFIG;
315344
memcpy(localConfig.macEnd, tempMac, 3);
345+
startSerial();
316346
break;
317347
}
318348
case MAC:
@@ -330,10 +360,11 @@ void processPost(char postParameter[]) {
330360
}
331361
// new parameter values received, save them to EEPROM
332362
EEPROM.put(CONFIG_START + 1, localConfig); // it is safe to call, only changed values are updated
333-
if (action == SERIAL_SOFT) { // can do it without "please wait" page
334-
Serial.flush();
335-
Serial.end();
336-
startSerial(); // TODO clear queue?
363+
#ifdef ENABLE_DHCP
364+
EEPROM.put(CONFIG_START + 1 + sizeof(localConfig), extraConfig);
365+
#endif /* ENABLE_DHCP */
366+
if (action == SERIAL_SOFT) { // can do it without "please wait" page
367+
startSerial();
337368
action = NONE;
338369
}
339370
}

0 commit comments

Comments
 (0)