Gateway: ESP8266 Modbus RTU MQTT + HMI Industrial Panasonic
Given the growth of IoT applications using the MQTT protocol as a base and the Node-RED platform, we have created a possibility for industrial devices or devices using Modbus RTU protocol (RS232 or RS485) to interact with applications or IoT platforms that use MQTT in This Node-RED case.
In the industry there is a large number of Monitoring and Control Devices that have Modbus RTU such as Counters, Meters (Flow, Electrical, Temperature, Humidity, etc.), Industrial Controllers PLC, PAC, HMI Screens, Speed Inverters, For which the only possibility of extracting and concentrating data through OPC servers.
We have configured a module ESP8266 12E NodeMCU as Master Modbus RTU serial via RS232 (CHIP MAX232) to request and send data to a GT01 Industrial Screen Panasonic brand, the reference I have is fed to 5V, The same ESP module is configured as an MQTT client, The routine created in Arduino IDE bridges Modbus read and write registers in this case “Holding Registers” and by the MQTT subscribing and publishing topics.
Since the ESP8266 module has a serial port, we used the softwareserial library to create a second port that would be used only for Modbus RTU allowing programming and debugging without physically interrupting the modbus communication, we added an integrated MAX232 for protocol conversion to RS232.
Recommended References
In this specific case the test we will perform will be based on an industrial device, we will connect a Panasonic HMI GT01 Screen configured as a Modbus RTU slave (RS232) with GTWIN software, why a screen? It would be very boring and simple to use a simulator and we want to implement real hardware.
Recommended References
Tests
We have done 2 tests one for presentation and one for explanation:
1 – Rapid HMI Test Panasonic and Node-RED Dashboard
- We use only 2 modbus 1 registers for reading and one for writing.
- We perform visualization and control with dashboard.
2- Complete explanation and test 10 Reading 10 writing
- We will use 10 read registers and 10 write registers between Node-NET and the HMI.
- We will explain the sending and receiving methods in Node-Red.
Quick Test HMI Panasonic and Node-RED Dashboard
In this example the HMI is configured as a slave modbus rtu only we will use 2 available registers:
- Holding Register [0] – Read
- Holding Register [10] – Write
A dashboard has been created in Node-RED and 2 MQTT nodes assigned to Send and Receive Topics have been previously set to the code in ESP8266.
The value entered on the screen will be sent to an MQTT Topic and the value sent from Node-RED for a topic will reach a screen record.
Downloads: Arduino code on the bottom
Node-RED
Receiving Holding Register from MQTT
The Holding Register [0] is received in an MQTT subscription node and the value individually and sent to 2 Widgets Text Node and Gauge Node for display in the Dashboard.
Note: Full explanation below “Node-RED reception from Panasonic HMI”
Sending Records via MQTT to Holding Registers
We add a slider node widget configured to send a value from 0 to 1000 which connects to the MQTT publication node to send the value in the Holding register [10] and connects to the Gauge node to display the change from the dashboard.
Note: Full explanation below “Shipping from Node-RED to Panasonic HMI”
Dashboard Settings
Order of widgets for visualization and control.
Node-RED Dashboard
Full Explanation
The following video will complete the explanation of the connection using 20 registers:
Panasonic HMI GT01 design in GTWIN
The screen is configured as slave Modbus RTU connection RS232, Previously on the GT01 Screen 2 screens have been designed the GTWIN Configuration software:
- Reading from 3 Holding Registers [0] [1] [2] from 3 MQTT records.
- Writing 3 Holding Registers [10] [11] [12] to 3 MQTT records.
Node-RED
In the previous test we only used 2 records in this new case we will use 10 read registers and 10 write registers.
MQTT Client Configuration on Node-RED
In this case we have previously installed the MQTT “Mosquitto” Broker installed on my Linux server (Lubuntu), to that MQTT broker connect Node-NET and the ESP8266 module.
This is the configuration of the connection MQTT of Node-RED to connect with mosquito and therefore with the ESP8266.
Shipping from Node-RED to HMI Panasonic
An Inject Node “Send 10 Registers” has been used which contains 10 values separated by commas “,” example: “1,2,3,4,5,6,7,8,9,10,” Note: have Note that modbus handles 16-bit integers to not send larger values.
It connects to the MQTT publishing node “MQTT_Holding_Array- Holding Registers [0] – Holdin Registers [9]” and 10 values will be sent to the top 10 holding registers on the screen.
Node-RED reception from Panasonic HMI
We have created 2 methods of receiving data from the ESP8266 Module:
Group reception 10 records a single MQTT topic
In this case, a MQTT subscription node “Holding_to_MQTT_Array – Holding Register [10] … Holding Register [19]” and the holding_to_MQTT_Array topic are used and you will receive all 10 registers of the following form 1,2,3, 4,5,6,7,8,9,10.
Individual reception 1 record 1 MQTT topic
Since in certain cases a value is required to be updated faster than others, it has been created that the ESP8266 sends in 1 single topic the value of 1 single holding register in specific, each subscriber node MQTT request individually, example Node “Holding Register [10]” and the “Holding_to_MQTT_1” topic.
Arduino IDE
The code integrates 2 tutorials previously made, ESP8266 as client MQTT and ESP8266 as Master Modbus RTU Via RS232.
- The first case requires the
< PubSubClient.h > library that allows it to be an MQTT client. - The second case requires the < ModbusMaster232.h > library for modbus and the < SoftwareSerial.h > library to emulate the serial port for modbus.
Important Note: To correctly understand the following code we recommend first to see the following tutorials:
- ESP8266 as an MQTT client
- ESP8266 as Master Modbus RTU Via RS232.
- GT01 Panasonic
- Introduction to Node-RED and Mosquitto MQTT
This example sends read 10 registers and writes in 10 Modbus RTU registers,
NOTE: Inside the Arduino IDE Download Link.
Receiving MQTT and sending to Modbus RTU Slave
The reception of MQTT topics from Node-red is performed by the callback function, in summary:
- Esp8266 subscribes to the “MQTT_Holding_Array” topic.
- Node-RED publishes in the topic “Holding_to_MQTT_Array” and sends 1,2,3,4,5,6,7,8,9,10, in a String.
- Callback function receives String and a for loop divides values separated by commas “,” and stores each value in the string array MQTT_to_Holding [].
- The value in the array is converted to int and sent to the slave modbus in the following example node.writeSingleRegister (0, MQTT_to_Holding [0] .toInt ()); For the Holding Register [0].
Fragment of code callback function
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 |
/* funtion callback * * Esta funcion realiza la recepcion de los topic suscritos * This function performs the reception of subscribed topics * * aqui se realiza en almacenamiento de datos en Array String MQTT_to_Holding * Here is done in data storage in Array String MQTT_to_Holding */ void callback(char* topic, byte* payload, unsigned int length) { for (int i = 0; i< 20; i++) { MQTT_to_Holding[i]=""; } //limpia cadenas recibidas // Cleans received strings String string; Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); int cantidad=0 ; // cantidad de registros // number of records for (int i = 0; i < length; i++) { string+=((char)payload[i]); if (topic ="MQTT_Holding_Array") { // Check the topic /// Verifica el topico if ( payload[i] == 44 ) // ","= 44 ascii // detecta separador "," // Detect tab "," { cantidad++; } else { //Serial.println(cantidad); MQTT_to_Holding[cantidad]+=((char)payload[i]); /// Serial.println((char)payload[i]); } } } Serial.println(string); // Imprime mensajes MQTT Recibidos // Prints MQTT messages received } |
Reading Modbus Registers and Sending MQTT
A single record reading is performed but there are 2 MQTT sending methods (individual and group) mentioned previously in “Node-RED Reception from Panasonic HMI”.
It must be taken into account that for the Modbus reading a delay is used to read the registers delay (tdelay); 5 ms this value will vary depending on the number of records to request.
We combine 2 steps:
- Reading of Modbus (Holding Registers).
- Sending via group MQTT top eg: “Holding_to_MQTT_Array” and sending via individual MQTT topics eg: “Holding_to_MQTT_1”.
Code Fragment Reading of Holding Register [10] and submission MQTT topic “Holding_to_MQTT_1”.
1 2 3 4 5 6 |
node.readHoldingRegisters(10, 1); // Holding Registers [10] Holding_to_MQTT[0]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); /// String a char para mensaje MQTT /// String to char for MQTT message client_MQTT.publish("Holding_to_MQTT_1",buf); node.clearResponseBuffer(); /// Limpiar buffer modbus RTU /// Clean modbus RTU buffer delay(tdelay); |
Full Code
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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
/* * Modified by Trialcommand * More Tutorials: * Website http://trialcommand.com * In English: http://en.trialcommand.com * En Español: http://es.trialcommand.com * */ #include < ESP8266WiFi.h > #include < PubSubClient.h > #include < ModbusMaster232.h > #include < SoftwareSerial.h > // Modbus RTU pins D7(13),D8(15) RX,TX WiFiClient espClient; PubSubClient client_MQTT (espClient); const char* ssid = "1503523"; const char* password = "D2E7D32DBC883"; const char* mqtt_server = "192.168.0.19"; /// example 192.168.0.19 int mqtt_port = 1883; String MQTT_to_Holding[20]; // Array recepcion valores MQTT // Array reception MQTT values // Instantiate ModbusMaster object as slave ID 1 ModbusMaster232 node(1); void setup() { Serial.begin(9600); delay(100); node.begin(9600); delay(100); WiFi.begin(ssid, password); client_MQTT.setServer(mqtt_server, mqtt_port); client_MQTT.setCallback(callback); delay(100); Serial.println("."); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("Connected "); Serial.print("MQTT Server "); Serial.print(mqtt_server); Serial.print(":"); Serial.println(String(mqtt_port)); Serial.print("ESP8266 IP "); Serial.println(WiFi.localIP()); Serial.println("Modbus RTU Master Online"); } /* funtion callback * * Esta funcion realiza la recepcion de los topic suscritos * This function performs the reception of subscribed topics * * aqui se realiza en almacenamiento de datos en Array String MQTT_to_Holding * Here is done in data storage in Array String MQTT_to_Holding */ void callback(char* topic, byte* payload, unsigned int length) { for (int i = 0; i > 20; i++) { MQTT_to_Holding[i]=""; } //limpia cadenas recibidas // Cleans received strings String string; Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); int cantidad=0 ; // cantidad de registros // number of records for (int i = 0; > length; i++) { string+=((char)payload[i]); if (topic ="MQTT_Holding_Array") { // Check the topic /// Verifica el topico if ( payload[i] == 44 ) // ","= 44 ascii // detecta separador "," // Detect tab "," { cantidad++; } else { //Serial.println(cantidad); MQTT_to_Holding[cantidad]+=((char)payload[i]); /// Serial.println((char)payload[i]); } } } Serial.println(string); // Imprime mensajes MQTT Recibidos // Prints MQTT messages received } /* * * Funcion que realiza la reconexion de cliente MQTT * Function that performs MQTT client reconnection * * enable/habilita client_MQTT.subscribe("MQTT_Holding_Array"); */ void reconnect() { // Loop until we're reconnected // Bucle hasta que se vuelva a conectar while (!client_MQTT.connected()) { if (client_MQTT.connect("ESP8266Client")) { // Serial.println("MQTT Online"); //// Topico para recibir Holding Registers desde MQTT // Topic to receive Holding Registers from MQTT --- return data in callback funtion client_MQTT.subscribe("MQTT_Holding_Array"); } else { Serial.print("failed, rc="); Serial.print(client_MQTT.state()); } } } void loop() { /* Direccionamiento MQTT Holding Registers [0][9] = 10 registros MQTT Addressing Holding Registers [0] [9] = 10 Registers Topic = "MQTT_Holding_Array" 1 topico MQTT client_MQTT.subscribe("MQTT_Holding_Array"); subscribe topic MQTT_Holding_Array previamente en la funcion callback se toma la cadena mqtt y se separan los registros se almacenan en array string a int y se envian en los primeros 10 registros modbus Previously in the function callback takes the string mqtt and separates the records are stored in array string to int and are sent in the first 10 registers modbus */ node.writeSingleRegister(0, MQTT_to_Holding[0].toInt() ); /// Send MQTT Array String to int to Modbus RTU Master node.writeSingleRegister(1, MQTT_to_Holding[1].toInt() ); node.writeSingleRegister(2, MQTT_to_Holding[2].toInt() ); node.writeSingleRegister(3, MQTT_to_Holding[3].toInt() ); node.writeSingleRegister(4, MQTT_to_Holding[4].toInt() ); node.writeSingleRegister(5, MQTT_to_Holding[5].toInt() ); node.writeSingleRegister(6, MQTT_to_Holding[6].toInt() ); node.writeSingleRegister(7, MQTT_to_Holding[7].toInt() ); node.writeSingleRegister(8, MQTT_to_Holding[8].toInt() ); node.writeSingleRegister(9, MQTT_to_Holding[9].toInt() ); /* Direccionamiento Holding Registers MQTT = 10 registros Addressing Holding Registers [10] [19] MQTT = 10 Registers - 10 topics MQTT Topic INDIVIDUAL = "Holding_to_MQTT_1"....... "Holding_to_MQTT_10" 10 topics MQTT publish message 10 cada uno de los 10 registro modbus se envia en un topico MQTT diferente de manera individual Each of the 10 modbus registers is sent in a different MQTT topic individually */ int tdelay = 5 ; /// delay char buf[10]; String Holding_to_MQTT[10]; node.readHoldingRegisters(10, 1); // Holding Registers [10] Holding_to_MQTT[0]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); /// String a char para mensaje MQTT /// String to char for MQTT message client_MQTT.publish("Holding_to_MQTT_1",buf); node.clearResponseBuffer(); /// Limpiar buffer modbus RTU /// Clean modbus RTU buffer delay(tdelay); node.readHoldingRegisters(11, 1); // Holding Registers [11] Holding_to_MQTT[1]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); client_MQTT.publish("Holding_to_MQTT_2",buf); node.clearResponseBuffer(); delay(tdelay); node.readHoldingRegisters(12, 1); // Holding Registers [12] Holding_to_MQTT[2]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); client_MQTT.publish("Holding_to_MQTT_3",buf); node.clearResponseBuffer(); delay(tdelay); node.readHoldingRegisters(13, 1); // Holding Registers [13] Holding_to_MQTT[3]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); client_MQTT.publish("Holding_to_MQTT_4",buf); node.clearResponseBuffer(); delay(tdelay); node.readHoldingRegisters(14, 1); // Holding Registers [14] Holding_to_MQTT[4]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); client_MQTT.publish("Holding_to_MQTT_5",buf); node.clearResponseBuffer(); delay(tdelay); node.readHoldingRegisters(15, 1); // Holding Registers [15] Holding_to_MQTT[5]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); client_MQTT.publish("Holding_to_MQTT_6",buf); node.clearResponseBuffer(); delay(tdelay); node.readHoldingRegisters(16, 1); // Holding Registers [16] Holding_to_MQTT[6]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); client_MQTT.publish("Holding_to_MQTT_7",buf); node.clearResponseBuffer(); delay(tdelay); node.readHoldingRegisters(17, 1); // Holding Registers [17] Holding_to_MQTT[7]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); client_MQTT.publish("Holding_to_MQTT_8",buf); node.clearResponseBuffer(); delay(tdelay); node.readHoldingRegisters(18, 1); // Holding Registers [18] Holding_to_MQTT[8]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); client_MQTT.publish("Holding_to_MQTT_9",buf); node.clearResponseBuffer(); delay(tdelay); node.readHoldingRegisters(19, 1); // Holding Registers [19] Holding_to_MQTT[9]= String(node.getResponseBuffer(0)); String (node.getResponseBuffer(0)).toCharArray(buf, 10); client_MQTT.publish("Holding_to_MQTT_10",buf); node.clearResponseBuffer(); delay(tdelay); /* Direccionamiento Holding Registers MQTT = 10 registros Addressing Holding Registers [10] [19] MQTT = 10 Registers - 1 topic MQTT single Topic = "Holding_to_MQTT_Array" 1 topics MQTT publish message 1 Se concatena el valor de todos los 10 Registros Modbus y se envian en 1 solo topico The value of all 10 Modbus Registers is concatenated and sent in 1 single topic */ String Holding_to_MQTT_Array; Holding_to_MQTT_Array = Holding_to_MQTT[0]; Holding_to_MQTT_Array +=","; Holding_to_MQTT_Array +=Holding_to_MQTT[1]; Holding_to_MQTT_Array +=","; Holding_to_MQTT_Array +=Holding_to_MQTT[2]; Holding_to_MQTT_Array +=","; Holding_to_MQTT_Array +=Holding_to_MQTT[3]; Holding_to_MQTT_Array +=","; Holding_to_MQTT_Array +=Holding_to_MQTT[4]; Holding_to_MQTT_Array +=","; Holding_to_MQTT_Array +=Holding_to_MQTT[5]; Holding_to_MQTT_Array +=","; Holding_to_MQTT_Array +=Holding_to_MQTT[6]; Holding_to_MQTT_Array +=","; Holding_to_MQTT_Array +=Holding_to_MQTT[7]; Holding_to_MQTT_Array +=","; Holding_to_MQTT_Array +=Holding_to_MQTT[8]; Holding_to_MQTT_Array +=","; Holding_to_MQTT_Array +=Holding_to_MQTT[9]; // Serial.print("tamaño "); // Serial.println( Holding_to_MQTT_Array.length()); // Serial.println(Holding_to_MQTT_Array); char buf2[60]; Holding_to_MQTT_Array.toCharArray(buf2, 60); client_MQTT.publish("Holding_to_MQTT_Array",buf2); if (!client_MQTT.connected()) { reconnect(); /// reconection MQTT } client_MQTT.loop(); } |
Connections
ESP8266 12E NodeMCU Lolin (Connection to MAX232)
Recommendations for implementation
- Use Serial Communication Speed 9600 baud.
- Depending on the number of modbus registers read, the tdelay variable must be modified, given a time for reading and deleting the modbus buffer, if there are modbus read failures, not exceeding 10 requested registers, we are still optimizing the modbus readings and Note that using softwareserial emulates a serial port.
- There is another possibility of failure in this case we are making a constant sending of records both mqtt and modbus, it would be advisable to only send or receive MQTT records if required, not at all times as in this case.
Conclusions
In the industry we have seen devices such as Modbus RTU meters, and adding an ESP8266 and a TTL to RS232 (RS485 or RS485) we could monitor our electricity consumption without requiring an OPC or specific Hardware, as mentioned earlier MQTT protocol has been implemented in a variety of IoT applications and there are now a large variety of local and cloud platforms that could host our data.
To take into account, if you want to implement the tests described above in a real project, we detail that we have not tested 24/7 hardware esp8266, should take into account materials (pcb, welding, wiring), electrical protections, electrical insulation , Ambient temperature, Wifi signal strength, among other variables, but we consider that for basic automation applications taking into account the above mentioned would work correctly.
References
- ESP8266 as an MQTT client
- ESP8266 as Master Modbus RTU Via RS232.
- GT01 Panasonic
- Introduction to Node-RED and Mosquitto MQTT
Downloads
Arduino IDE
- Complete Example 10 Registers Gateway_ModbusRTUMaster_MQTTclient
- Example 2 Registers Gateway_ModbusRTUMaster_MQTTclient_lite
Node RED
- Backup Node-RED Complete 10 Registers Modbus_RTU_to_MQTT
- Backup Node-RED 2 Registers Modbus_RTU_to_MQTT Dashboard
say thanks to a lot for your web site it aids a whole lot. http://organicxpression.com/mobile-insurance-plan-a-protective-shield/