forked from emelianov/modbus-esp8266
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtrue.ino
121 lines (105 loc) · 5.13 KB
/
true.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
ModbusRTU ESP8266/ESP32
True RTU-TCP bridge example
(c)2021 Alexander Emelianov ([email protected])
https://github.com/emelianov/modbus-esp8266
This code is licensed under the BSD New License. See LICENSE.txt for more info.
*/
#include <vector>
#include <WiFi.h>
#include <ModbusTCP.h>
#include <ModbusRTU.h>
ModbusRTU rtu;
ModbusTCP tcp;
// ModbusRTU(SlaveID) => ModbusTCP(IP) mapping table
struct slave_map_t {
uint8_t slaveId; // Slave id in incoming request
IPAddress ip; // IP address of MosbusTCP Server map request to
uint8_t unitId = MODBUSIP_UNIT; // UnitId on target server
slave_map_t(uint8_t s, IPAddress i, uint8_t u = MODBUSIP_UNIT) {
slaveId = s;
ip = i;
unitId = u;
};
};
std::vector<slave_map_t> mapping; // Slave => IP mappings
uint16_t transRunning = 0; // Currently executed ModbusTCP transaction
uint8_t slaveRunning = 0; // Current request slave
bool cbTcpTrans(Modbus::ResultCode event, uint16_t transactionId, void* data) { // Modbus Transaction callback
if (event != Modbus::EX_SUCCESS) // If transaction got an error
Serial.printf("Modbus result: %02X, Mem: %d\n", event, ESP.getFreeHeap()); // Display Modbus error code (222527)
if (event == Modbus::EX_TIMEOUT) { // If Transaction timeout took place
tcp.disconnect(tcp.eventSource()); // Close connection
}
return true;
}
// Callback receives raw data from ModbusTCP and sends it on behalf of slave (slaveRunning) to master
Modbus::ResultCode cbTcpRaw(uint8_t* data, uint8_t len, void* custom) {
auto src = (Modbus::frame_arg_t*) custom;
Serial.print("TCP IP: ");
Serial.print(IPAddress(src->ipaddr));
Serial.printf(" Fn: %02X, len: %d \n", data[0], len);
if (!src->to_server && transRunning == src->transactionId) { // Check if transaction id is match
rtu.rawResponce(slaveRunning, data, len);
} else
return Modbus::EX_PASSTHROUGH; // Allow frame to be processed by generic ModbusTCP routines
transRunning = 0;
slaveRunning = 0;
return Modbus::EX_SUCCESS; // Stop other processing
}
// Callback receives raw data
Modbus::ResultCode cbRtuRaw(uint8_t* data, uint8_t len, void* custom) {
auto src = (Modbus::frame_arg_t*) custom;
Serial.printf("RTU Slave: %d, Fn: %02X, len: %d, ", src->slaveId, data[0], len);
auto it = std::find_if(mapping.begin(), mapping.end(), [src](slave_map_t& item){return (item.slaveId == src->slaveId);}); // Find mapping
if (it != mapping.end()) {
if (!tcp.isConnected(it->ip)) { // Check if connection established
if (!tcp.connect(it->ip)) { // Try to connect if not
Serial.printf("error: Connection timeout\n");
rtu.errorResponce(it->slaveId, (Modbus::FunctionCode)data[0], Modbus::EX_DEVICE_FAILED_TO_RESPOND); // Send exceprional responce to master if no connection established
// Note:
// Indeed if both sides is build with the Modbus library _default settings_ RTU master side initiating requests to bridge will respond EX_TIMEOUT not EX_DEVICE_FAILED_TO_RESPOND.
// That's because connection timeout and RTU responce timeout are the same (1 second). That case EX_TIMEOUT on reached prior getting EX_DEVICE_FAILED_TO_RESPOND frame.
return Modbus::EX_DEVICE_FAILED_TO_RESPOND; // Stop processing the frame
}
}
// Save transaction ans slave it for responce processing
transRunning = tcp.rawRequest(it->ip, data, len, cbTcpTrans, it->unitId);
if (!transRunning) { // rawRequest returns 0 is unable to send data for some reason
tcp.disconnect(it->ip); // Close TCP connection that case
Serial.printf("send failed\n");
rtu.errorResponce(it->slaveId, (Modbus::FunctionCode)data[0], Modbus::EX_DEVICE_FAILED_TO_RESPOND); // Send exceprional responce to master if request bridging failed
return Modbus::EX_DEVICE_FAILED_TO_RESPOND; // Stop processing the frame
}
Serial.printf("transaction: %d\n", transRunning);
slaveRunning = it->slaveId;
return Modbus::EX_SUCCESS; // Stop procesing the frame
}
Serial.printf("ignored: No mapping\n");
return Modbus::EX_PASSTHROUGH; // Process by generic ModbusRTU routines if no mapping found
}
void setup() {
Serial.begin(115000);
WiFi.begin("SSID", "PASSWORD");
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
tcp.client(); // Initialize ModbusTCP to pracess as client
tcp.onRaw(cbTcpRaw); // Assign raw data processing callback
Serial1.begin(9600, SERIAL_8N1, 18, 19);
rtu.begin(&Serial1);
rtu.slave(3); // Initialize ModbusRTU as slave
rtu.onRaw(cbRtuRaw); // Assign raw data processing callback
// Assign mappings
mapping.push_back({1, IPAddress(192,168,30,18)});
mapping.push_back({2, IPAddress(192,168,30,19)});
}
void loop() {
rtu.task();
tcp.task();
yield();
}