├── extras
├── NodeMCU_PDAControl.jpg
└── NodeMCU Modbus MASTER.jpg
├── .gitattributes
├── ModBusMaster232
├── keywords.txt
├── keywords.txt~
├── examples
│ ├── _RS232_01_send_data
│ │ └── _RS232_01_send_data.ino
│ ├── _RS232_02_receive_data
│ │ └── _RS232_02_receive_data.ino
│ ├── _RS232_03_modbus_slave_mode
│ │ └── _RS232_03_modbus_slave_mode.ino
│ ├── _RS232_04_modbus_read_coils
│ │ └── _RS232_04_modbus_read_coils.ino
│ └── _RS232_05_modbus_write_single_register
│ │ └── _RS232_05_modbus_write_single_register.ino
├── ModbusMaster232.h
└── ModbusMaster232.cpp
├── .gitignore
├── README.md
└── ModbusRTUMaster
└── ModbusRTUMaster.ino
/extras/NodeMCU_PDAControl.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JhonControl/ESP8266_ModbusRTU_Master_V2/HEAD/extras/NodeMCU_PDAControl.jpg
--------------------------------------------------------------------------------
/extras/NodeMCU Modbus MASTER.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JhonControl/ESP8266_ModbusRTU_Master_V2/HEAD/extras/NodeMCU Modbus MASTER.jpg
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/ModBusMaster232/keywords.txt:
--------------------------------------------------------------------------------
1 | ModbusMaster232 KEYWORD1
2 | node KEYWORD3
3 |
4 |
5 | getResponseBuffer KEYWORD2
6 | clearResponseBuffer KEYWORD2
7 | setTransmitBuffer KEYWORD2
8 | clearTransmitBuffer KEYWORD2
9 | setSlaveAddress KEYWORD2
10 | send KEYWORD2
11 | available KEYWORD2
12 | receive KEYWORD2
13 | readCoils KEYWORD2
14 | readDiscreteInputs KEYWORD2
15 | readHoldingRegisters KEYWORD2
16 | readInputRegisters KEYWORD2
17 | writeSingleCoil KEYWORD2
18 | writeSingleRegister KEYWORD2
19 | writeMultipleCoils KEYWORD2
20 | writeMultipleCoils KEYWORD2
21 | writeMultipleRegisters KEYWORD2
22 | writeMultipleRegisters KEYWORD2
23 | maskWriteRegister KEYWORD2
24 | readWriteMultipleRegisters KEYWORD2
25 | readWriteMultipleRegisters KEYWORD2
26 |
27 |
--------------------------------------------------------------------------------
/ModBusMaster232/keywords.txt~:
--------------------------------------------------------------------------------
1 | ModbusMaster232 KEYWORD1
2 | node KEYWORD1
3 |
4 |
5 | getResponseBuffer KEYWORD2
6 | clearResponseBuffer KEYWORD2
7 | setTransmitBuffer KEYWORD2
8 | clearTransmitBuffer KEYWORD2
9 | setSlaveAddress KEYWORD2
10 | send KEYWORD2
11 | available KEYWORD2
12 | receive KEYWORD2
13 | readCoils KEYWORD2
14 | readDiscreteInputs KEYWORD2
15 | readHoldingRegisters KEYWORD2
16 | readInputRegisters KEYWORD2
17 | writeSingleCoil KEYWORD2
18 | writeSingleRegister KEYWORD2
19 | writeMultipleCoils KEYWORD2
20 | writeMultipleCoils KEYWORD2
21 | writeMultipleRegisters KEYWORD2
22 | writeMultipleRegisters KEYWORD2
23 | maskWriteRegister KEYWORD2
24 | readWriteMultipleRegisters KEYWORD2
25 | readWriteMultipleRegisters KEYWORD2
26 |
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear in the root of a volume
35 | .DocumentRevisions-V100
36 | .fseventsd
37 | .Spotlight-V100
38 | .TemporaryItems
39 | .Trashes
40 | .VolumeIcon.icns
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ESP8266 ModbusRTU Master
2 | Test ESP8266 Modbus RTU Master for Industrial applications
3 |
4 | 
5 |
6 |
7 | # Tutorial NodeMCU ESP8266 12E Modbus RTU Master v2 (Improved - Mejorado)
8 |
9 | https://youtu.be/qOjwa6sFg1Y
10 |
11 | # Tutorial ESP8266 12E Modbus RTU Master (Improved)
12 | On this occasion we will make the configuration of Modulo ESP8266 12E as Master Modbus RTU Serial with Arduino IDE for Industrial applications.
13 |
14 | Post: http://pdacontrolen.com/tutorial-esp8266-12e-modbus-rtu-master/
15 |
16 |
17 | #Tutorial ESP8266 12E Modbus RTU Maestro (Mejorado)
18 | En esta ocasión realizaremos la configuración de Modulo ESP8266 -12E como Maestro Modbus RTU Serial con Arduino IDE para aplicaciones Industriales.
19 |
20 | Post: http://pdacontroles.com/pruebas-esp8266-12e-modbus-rtu-maestro/
21 |
22 | 
23 |
24 |
25 | In this case we have used an ESP8266 12E NodeMCU since it has more GPIOs and 1 ADC
26 |
27 | En este caso hemos utilizado un ESP8266 12E NodeMCU dado que cuenta con mas GPIO'S y 1 ADC
28 |
29 | #More info - Mas Informacion:
30 | Ingles
31 | http://pdacontrolen.com
32 | Español
33 | http://pdacontroles.com
34 |
--------------------------------------------------------------------------------
/ModBusMaster232/examples/_RS232_01_send_data/_RS232_01_send_data.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * This sketch shows how to send data through RS-232 standard.
3 | * This standard defines the electrical characteristics of drivers
4 | * and receivers for use in digital systems. It does not specify
5 | * or recommend any communications protocol. For a complete
6 | * communication protocol, please see the Modbus examples.
7 | *
8 | * Copyright (C) 2014 Libelium Comunicaciones Distribuidas S.L.
9 | * http://www.libelium.com
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, either version 2 of the License, or
14 | * (at your option) any later version.
15 | *
16 | * This program is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | * GNU General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU General Public License
22 | * along with this program. If not, see .
23 | *
24 | * Version: 0.1
25 | * Design: David Gascon
26 | * Implementation: Ahmad Saad
27 | */
28 |
29 |
30 | void setup() {
31 |
32 | // Configure the multiplexer select pin
33 | pinMode(5, OUTPUT);
34 |
35 | Serial.begin(9600);
36 | delay(100);
37 |
38 | // Hello message
39 | Serial.println("Hello this is RS-232 communication send example!");
40 | delay(100);
41 |
42 | }
43 |
44 | void loop() {
45 | // Using the SOCKET1
46 | digitalWrite(5, HIGH);
47 |
48 | // Reading the analog input 1
49 | int analog0 = analogRead(A0);
50 | // Temperature formula TMP36
51 | float temperature = 100.0 * (analog0 * 5.0) / 1023 - 50;
52 |
53 |
54 | // Send data through RS-232 line
55 | Serial.print("Temperature from TMP36 : ");
56 | Serial.print(temperature);
57 | Serial.println(" C");
58 |
59 | delay(1000);
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/ModBusMaster232/examples/_RS232_02_receive_data/_RS232_02_receive_data.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * This sketch shows how to receive data through RS-232 standard.
3 | * This standard defines the electrical characteristics of drivers
4 | * and receivers for use in digital systems. It does not specify
5 | * or recommend any communications protocol. For a complete
6 | * communication protocol, please see the Modbus examples.
7 | *
8 | * Copyright (C) 2014 Libelium Comunicaciones Distribuidas S.L.
9 | * http://www.libelium.com
10 | *
11 | * This program is free software: you can redistribute it and/or modify
12 | * it under the terms of the GNU General Public License as published by
13 | * the Free Software Foundation, either version 2 of the License, or
14 | * (at your option) any later version.
15 | *
16 | * This program is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | * GNU General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU General Public License
22 | * along with this program. If not, see .
23 | *
24 | * Version: 0.1
25 | * Design: David Gascon
26 | * Implementation: Ahmad Saad
27 | */
28 |
29 |
30 | void setup() {
31 |
32 | // Note: For viewing data in the serial monitor, you should open
33 | // the Serial at the same speed
34 | Serial.begin(9600);
35 | delay(100);
36 |
37 | //Print hello message.
38 | Serial.println("Hello this is RS-232 communication receive data example.");
39 | }
40 |
41 |
42 |
43 | void loop() {
44 |
45 | // If data in response buffer
46 | if (Serial.available())
47 | {
48 | while (Serial.available()) {
49 | // Read one byte from the buffer
50 | // Print data received in the serial monitor
51 | Serial.print(char(Serial.read()));
52 | }
53 |
54 | Serial.print("\n");
55 | }
56 |
57 | delay(100);
58 |
59 | }
60 |
61 |
62 |
--------------------------------------------------------------------------------
/ModBusMaster232/examples/_RS232_03_modbus_slave_mode/_RS232_03_modbus_slave_mode.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * This sketch shows the use of the Modbus communication protocol over
3 | * RS-232 standard, and the use of the main functions of the library.
4 | *. Modbus allows for communication between many devices connected
5 | * to the same network. There are many variants of Modbus protocols,
6 | * but Arduino implements the RTU format. Modbus RTU is the most
7 | * common implementation available for Modbus.
8 | *
9 | * This example shows how to configure the Arduino as a Modbus
10 | * slave device.
11 | *
12 | * Copyright (C) 2014 Libelium Comunicaciones Distribuidas S.L.
13 | * http://www.libelium.com
14 | *
15 | * This program is free software: you can redistribute it and/or modify
16 | * it under the terms of the GNU General Public License as published by
17 | * the Free Software Foundation, either version 2 of the License, or
18 | * (at your option) any later version.
19 | *
20 | * This program is distributed in the hope that it will be useful,
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | * GNU General Public License for more details.
24 | *
25 | * You should have received a copy of the GNU General Public License
26 | * along with this program. If not, see .
27 | *
28 | * Version: 0.1
29 | * Design: David Gascon
30 | * Implementation: Ahmad Saad
31 | */
32 |
33 | // Include always this library when you are using Modbus functions
34 | #include
35 |
36 | // Create new mbs instance
37 | ModbusSlave232 mbs;
38 |
39 | // Slave registers
40 | enum {
41 | MB_0, // Register 0
42 | MB_1, // Register 1
43 | MB_2, // Register 2
44 | MB_3, // Register 3
45 | MB_4, // Register 4
46 | MB_REGS // Dummy register. using 0 offset to keep size of array
47 | };
48 |
49 | int regs[MB_REGS];
50 |
51 | void setup(){
52 |
53 | // Modbus slave configuration parameters
54 | // SlaveId
55 | const unsigned char SLAVE = 1;
56 | // Baud rate
57 | const long BAUD = 9600;
58 | const unsigned PARITY = 'n';
59 |
60 | // Configure msb with config settings
61 | mbs.configure(SLAVE, BAUD, PARITY );
62 |
63 | }
64 |
65 | void loop()
66 | {
67 | // Pass current register values to mbs
68 | mbs.update(regs, MB_REGS);
69 |
70 | // Read all the analog Inputs, and store the values in
71 | // the Modbus registers.
72 | regs[MB_0] = analogRead(A0); // Analog 0 value
73 | regs[MB_1] = analogRead(A1); // Analog 1 value
74 | regs[MB_2] = analogRead(A3); // Analog 2 value
75 | regs[MB_3] = analogRead(A3); // Analog 3 value
76 |
77 | delay(1000);
78 |
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/ModBusMaster232/examples/_RS232_04_modbus_read_coils/_RS232_04_modbus_read_coils.ino:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * This sketch shows the use of the Modbus communication protocol over
4 | * RS-232 standard, and the use of the main functions of the library.
5 | *. Modbus allows for communication between many devices connected
6 | * to the same network. There are many variants of Modbus protocols,
7 | * but Arduino implements the RTU format. Modbus RTU is the most
8 | * common implementation available for Modbus.
9 | *
10 | * This example shows the use of the function readCoils. This function
11 | * requests the ON/OFF status of discrete coils from the slave device.
12 | *
13 | * Copyright (C) 2014 Libelium Comunicaciones Distribuidas S.L.
14 | * http://www.libelium.com
15 | *
16 | * This program is free software: you can redistribute it and/or modify
17 | * it under the terms of the GNU General Public License as published by
18 | * the Free Software Foundation, either version 2 of the License, or
19 | * (at your option) any later version.
20 | *
21 | * This program is distributed in the hope that it will be useful,
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 | * GNU General Public License for more details.
25 | *
26 | * You should have received a copy of the GNU General Public License
27 | * along with this program. If not, see .
28 | *
29 | * Version: 0.1
30 | * Implementation: Ahmad Saad
31 | */
32 |
33 |
34 | #include
35 |
36 | // Instantiate ModbusMaster object as slave ID 1
37 | ModbusMaster232 node(1);
38 |
39 | // Define one address for reading
40 | #define address 1
41 | // Define the number of bits to read
42 | #define bitQty 1
43 |
44 | void setup()
45 | {
46 | // Initialize Modbus communication baud rate
47 | node.begin(19200);
48 |
49 | // Print hello message
50 | /// Serial.println("Modbus communication over RS-232");
51 | delay(100);
52 | }
53 |
54 |
55 | void loop()
56 | {
57 | // This variable will store the result of the communication
58 | // result = 0 : no errors
59 | // result = 1 : error occurred
60 | int result = node.readHoldingRegisters(address, bitQty);
61 |
62 | int dato = node.getResponseBuffer(0);
63 | ///Serial.print(dato);
64 | /*
65 | if (result != 0) {
66 | // If no response from the slave, print an error message
67 | Serial.println("Communication error");
68 | delay(1000);
69 | }
70 | else {
71 |
72 | // If all ok
73 | Serial.print("Read value : ");
74 |
75 | // Print the read data from the slave
76 |
77 |
78 |
79 | delay(1000);
80 | }
81 | */
82 | // Serial.print("\n");
83 | delay(1000);
84 |
85 | // Clear the response buffer
86 | node.clearResponseBuffer();
87 |
88 | }
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/ModBusMaster232/examples/_RS232_05_modbus_write_single_register/_RS232_05_modbus_write_single_register.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * This sketch shows the use of the Modbus communication protocol over
3 | * RS-232 standard, and the use of the main functions of the library.
4 | *. Modbus allows for communication between many devices connected
5 | * to the same network. There are many variants of Modbus protocols,
6 | * but Arduino implements the RTU format. Modbus RTU is the most
7 | * common implementation available for Modbus.
8 | *
9 | * This example shows the use of the function writeSingleRegiter().
10 | * This function code is used to write a single holding register
11 | * in a remote device.
12 | *
13 | * Copyright (C) 2014 Libelium Comunicaciones Distribuidas S.L.
14 | * http://www.libelium.com
15 | *
16 | * This program is free software: you can redistribute it and/or modify
17 | * it under the terms of the GNU General Public License as published by
18 | * the Free Software Foundation, either version 2 of the License, or
19 | * (at your option) any later version.
20 | *
21 | * This program is distributed in the hope that it will be useful,
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 | * GNU General Public License for more details.
25 | *
26 | * You should have received a copy of the GNU General Public License
27 | * along with this program. If not, see .
28 | *
29 | * Version: 0.1
30 | * Design: David Gascon
31 | * Implementation: Ahmad Saad
32 | */
33 |
34 |
35 | //Include these libraries for using the RS-232 and Modbus functions
36 | #include
37 |
38 | // Instantiate ModbusMaster object as slave ID 1
39 | ModbusMaster232 node(1);
40 |
41 | //define one address for reading
42 | #define address 1
43 |
44 | //define the number of bytes to read
45 | #define byteData 0
46 |
47 | void setup()
48 | {
49 | // Power on the USB for viewing data in the serial monitor.
50 | Serial.begin(9600);
51 |
52 | // Initialize Modbus communication baud rate
53 | node.begin(9600);
54 |
55 | //Print hello message
56 | Serial.println("Modbus communication over RS-232");
57 | delay(100);
58 | }
59 |
60 |
61 | void loop()
62 | {
63 | // This variable will store the result of the communication
64 | // result = 0 : no errors
65 | // result = 1 : error occurred
66 | int result = node.writeSingleRegister(address, byteData);
67 |
68 | if (result != 0) {
69 | // If no response from the slave, print an error message
70 | Serial.println("Communication error");
71 | delay(100);
72 | }
73 | else {
74 |
75 | // If all ok
76 | Serial.print("Data writted successfully.");
77 | delay(100);
78 | }
79 |
80 | Serial.print("\n");
81 | delay(1000);
82 |
83 | // Clear the response buffer
84 | node.clearResponseBuffer();
85 |
86 | }
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/ModbusRTUMaster/ModbusRTUMaster.ino:
--------------------------------------------------------------------------------
1 | /* https://www.cooking-hacks.com/documentation/tutorials/modbus-module-shield-tutorial-for-arduino-raspberry-pi-intel-galileo/
2 | *
3 | * This sketch shows the use of the Modbus communication protocol over
4 | * RS-232 standard, and the use of the main functions of the library.
5 | *. Modbus allows for communication between many devices connected
6 | * to the same network. There are many variants of Modbus protocols,
7 | * but Arduino implements the RTU format. Modbus RTU is the most
8 | * common implementation available for Modbus.
9 | *
10 | * This example shows the use of the function readCoils. This function
11 | * requests the ON/OFF status of discrete coils from the slave device.
12 | *
13 | * Copyright (C) 2014 Libelium Comunicaciones Distribuidas S.L.
14 | * http://www.libelium.com
15 | *
16 | * This program is free software: you can redistribute it and/or modify
17 | * it under the terms of the GNU General Public License as published by
18 | * the Free Software Foundation, either version 2 of the License, or
19 | * (at your option) any later version.
20 | *
21 | * This program is distributed in the hope that it will be useful,
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 | * GNU General Public License for more details.
25 | *
26 | * You should have received a copy of the GNU General Public License
27 | * along with this program. If not, see .
28 | *
29 | * Version: 0.1
30 | * Implementation: Ahmad Saad
31 | *
32 | *
33 | * Modified by PDAControl
34 | * Settings for libraries for ESP8266
35 | * - Function crc16
36 | * - Function makeWord
37 | * More info : http://pdacontrolen.com
38 | * Mas informacion: http://pdacontroles.com
39 | * Channel Youtube https://www.youtube.com/c/JhonValenciaPDAcontrol/videos
40 | *
41 | *
42 | */
43 |
44 |
45 | #include
46 |
47 |
48 | // Instantiate ModbusMaster object as slave ID 1
49 | ModbusMaster232 node(1);
50 |
51 | //functions modbus
52 | //node.readHoldingRegisters(,)
53 | //node.writeSingleRegister(,)
54 | // node.readInputRegisters(,);
55 | // node.writeSingleCoil(,)
56 | // node.readWriteMultipleRegisters(,,,)
57 |
58 | const int analogInPin = A0;
59 | int led = 0 ;
60 | int response = 0;
61 |
62 | // Define one address for reading
63 | #define address 1
64 | // Define the number of bits to read
65 | #define bitQty 1
66 |
67 | void setup()
68 | {
69 | pinMode(4, OUTPUT);
70 | // Initialize Modbus communication baud rate
71 | node.begin(19200);
72 | delay(100);
73 | }
74 |
75 |
76 | void loop()
77 | {
78 |
79 |
80 | /// Write Digital Outputs - Holding Register[4] /-> [5] ////////////
81 | response = node.readHoldingRegisters(4, 1);
82 | /// get value - captura valor
83 | int led = node.getResponseBuffer(0);
84 | // GPIO 04
85 | digitalWrite(4,led);
86 | // Clear the response buffer
87 | node.clearResponseBuffer();
88 |
89 | delay(100);
90 |
91 | // Send Random Value - Holding Register[5] /-> [6] ////////////
92 | node.writeSingleRegister(5, random(1, 999 ));
93 |
94 | delay(100);
95 |
96 | //Read Analog Input - Holding Register[6] /-> [7] ////////////
97 | node.writeSingleRegister(6, analogRead(analogInPin) );
98 |
99 |
100 | delay(100);
101 |
102 | // Clear the response buffer
103 | node.clearResponseBuffer();
104 |
105 |
106 | }
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/ModBusMaster232/ModbusMaster232.h:
--------------------------------------------------------------------------------
1 | /**
2 | @file
3 | Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol).
4 |
5 | @defgroup setup ModbusMaster Object Instantiation/Initialization
6 | @defgroup buffer ModbusMaster Buffer Management
7 | @defgroup discrete Modbus Function Codes for Discrete Coils/Inputs
8 | @defgroup register Modbus Function Codes for Holding/Input Registers
9 | @defgroup constant Modbus Function Codes, Exception Codes
10 | */
11 | /*
12 |
13 | ModbusMaster.h - Arduino library for communicating with Modbus slaves
14 | over RS232/485 (via RTU protocol).
15 |
16 | This file is part of ModbusMaster.
17 |
18 | ModbusMaster is free software: you can redistribute it and/or modify
19 | it under the terms of the GNU General Public License as published by
20 | the Free Software Foundation, either version 3 of the License, or
21 | (at your option) any later version.
22 |
23 | ModbusMaster is distributed in the hope that it will be useful,
24 | but WITHOUT ANY WARRANTY; without even the implied warranty of
25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 | GNU General Public License for more details.
27 |
28 | You should have received a copy of the GNU General Public License
29 | along with ModbusMaster. If not, see .
30 |
31 | Written by Doc Walker (Rx)
32 | Copyright © 2009-2013 Doc Walker <4-20ma at wvfans dot net>
33 |
34 |
35 | Modified by PDAControl 2016
36 | - Function crc16
37 | - Function makeWord
38 |
39 | */
40 |
41 |
42 |
43 | #ifndef _MODBUSMASTER232_H_INCLUDED
44 | #define _MODBUSMASTER232_H_INCLUDED
45 |
46 |
47 | /**
48 | @def __MODBUSMASTER_DEBUG__ (1).
49 | Set to 1 to enable debugging features within class:
50 | - pin 4 cycles for each byte read in the Modbus response
51 | - pin 5 cycles for each millisecond timeout during the Modbus response
52 | */
53 | #define __MODBUSMASTER_DEBUG__ (1)
54 |
55 |
56 | /* _____STANDARD INCLUDES____________________________________________________ */
57 |
58 | // include types & constants of Wiring core API
59 |
60 | #ifndef inttypes_h
61 | #include
62 | #endif
63 |
64 |
65 |
66 | /* _____UTILITY MACROS_______________________________________________________ */
67 | /**
68 | @def lowWord(ww) ((uint16_t) ((ww) & 0xFFFF))
69 | Macro to return low word of a 32-bit integer.
70 | */
71 | #define lowWord(ww) ((uint16_t) ((ww) & 0xFFFF))
72 |
73 |
74 | /**
75 | @def highWord(ww) ((uint16_t) ((ww) >> 16))
76 | Macro to return high word of a 32-bit integer.
77 | */
78 | #define highWord(ww) ((uint16_t) ((ww) >> 16))
79 |
80 |
81 |
82 | /**
83 | @def LONG(hi, lo) ((uint32_t) ((hi) << 16 | (lo)))
84 | Macro to generate 32-bit integer from (2) 16-bit words.
85 | */
86 | #define LONG(hi, lo) ((uint32_t) ((hi) << 16 | (lo)))
87 |
88 | #define lowByte(w) ((uint8_t) ((w) & 0xff))
89 | #define highByte(w) ((uint8_t) ((w) >> 8))
90 |
91 | #define bitRead(value, bit) (((value) >> (bit)) & 0x01)
92 | #define bitSet(value, bit) ((value) |= (1UL << (bit)))
93 | #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
94 | #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
95 |
96 |
97 | /*
98 |
99 | Commented function crc16 not supported by ESP8266 - PDAControl
100 | //http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__util__crc_1ga95371c87f25b0a2497d9cba13190847f.html
101 |
102 | _____PROJECT INCLUDES_____________________________________________________ */
103 | // functions to calculate Modbus Application Data Unit CRC
104 | //#include
105 |
106 | //#include
107 |
108 |
109 | /* _____CLASS DEFINITIONS____________________________________________________ */
110 | /**
111 |
112 |
113 |
114 | Arduino class library for communicating with Modbus slaves over
115 | RS232/485 (via RTU protocol).
116 | */
117 | class ModbusMaster232
118 | {
119 | public:
120 |
121 | ModbusMaster232();
122 | ModbusMaster232(uint8_t);
123 | ModbusMaster232(uint8_t, uint8_t);
124 |
125 | void begin();
126 | void begin(unsigned long);
127 | void idle(void (*)());
128 |
129 | // Modbus exception codes
130 | /**
131 | Modbus protocol illegal function exception.
132 |
133 | The function code received in the query is not an allowable action for
134 | the server (or slave). This may be because the function code is only
135 | applicable to newer devices, and was not implemented in the unit
136 | selected. It could also indicate that the server (or slave) is in the
137 | wrong state to process a request of this type, for example because it is
138 | unconfigured and is being asked to return register values.
139 |
140 | @ingroup constant
141 | */
142 | static const uint8_t ku8MBIllegalFunction = 0x01;
143 |
144 | /**
145 | Modbus protocol illegal data address exception.
146 |
147 | The data address received in the query is not an allowable address for
148 | the server (or slave). More specifically, the combination of reference
149 | number and transfer length is invalid. For a controller with 100
150 | registers, the ADU addresses the first register as 0, and the last one
151 | as 99. If a request is submitted with a starting register address of 96
152 | and a quantity of registers of 4, then this request will successfully
153 | operate (address-wise at least) on registers 96, 97, 98, 99. If a
154 | request is submitted with a starting register address of 96 and a
155 | quantity of registers of 5, then this request will fail with Exception
156 | Code 0x02 "Illegal Data Address" since it attempts to operate on
157 | registers 96, 97, 98, 99 and 100, and there is no register with address
158 | 100.
159 |
160 | @ingroup constant
161 | */
162 | static const uint8_t ku8MBIllegalDataAddress = 0x02;
163 |
164 | /**
165 | Modbus protocol illegal data value exception.
166 |
167 | A value contained in the query data field is not an allowable value for
168 | server (or slave). This indicates a fault in the structure of the
169 | remainder of a complex request, such as that the implied length is
170 | incorrect. It specifically does NOT mean that a data item submitted for
171 | storage in a register has a value outside the expectation of the
172 | application program, since the MODBUS protocol is unaware of the
173 | significance of any particular value of any particular register.
174 |
175 | @ingroup constant
176 | */
177 | static const uint8_t ku8MBIllegalDataValue = 0x03;
178 |
179 | /**
180 | Modbus protocol slave device failure exception.
181 |
182 | An unrecoverable error occurred while the server (or slave) was
183 | attempting to perform the requested action.
184 |
185 | @ingroup constant
186 | */
187 | static const uint8_t ku8MBSlaveDeviceFailure = 0x04;
188 |
189 | // Class-defined success/exception codes
190 | /**
191 | ModbusMaster success.
192 |
193 | Modbus transaction was successful; the following checks were valid:
194 | - slave ID
195 | - function code
196 | - response code
197 | - data
198 | - CRC
199 |
200 | @ingroup constant
201 | */
202 | static const uint8_t ku8MBSuccess = 0x00;
203 |
204 | /**
205 | ModbusMaster invalid response slave ID exception.
206 |
207 | The slave ID in the response does not match that of the request.
208 |
209 | @ingroup constant
210 | */
211 | static const uint8_t ku8MBInvalidSlaveID = 0xE0;
212 |
213 | /**
214 | ModbusMaster invalid response function exception.
215 |
216 | The function code in the response does not match that of the request.
217 |
218 | @ingroup constant
219 | */
220 | static const uint8_t ku8MBInvalidFunction = 0xE1;
221 |
222 | /**
223 | ModbusMaster response timed out exception.
224 |
225 | The entire response was not received within the timeout period,
226 | ModbusMaster::ku8MBResponseTimeout.
227 |
228 | @ingroup constant
229 | */
230 | static const uint8_t ku8MBResponseTimedOut = 0xE2;
231 |
232 | /**
233 | ModbusMaster invalid response CRC exception.
234 |
235 | The CRC in the response does not match the one calculated.
236 |
237 | @ingroup constant
238 | */
239 | static const uint8_t ku8MBInvalidCRC = 0xE3;
240 |
241 | uint16_t getResponseBuffer(uint8_t);
242 | void clearResponseBuffer();
243 | uint8_t setTransmitBuffer(uint8_t, uint16_t);
244 | void clearTransmitBuffer();
245 |
246 | void beginTransmission(uint16_t);
247 | uint8_t requestFrom(uint16_t, uint16_t);
248 | void sendBit(bool);
249 | void send(uint8_t);
250 | void send(uint16_t);
251 | void send(uint32_t);
252 | void setSlaveAddress(uint8_t);
253 |
254 | uint8_t available(void);
255 | uint16_t receive(void);
256 |
257 |
258 | uint8_t readCoils(uint16_t, uint16_t);
259 | uint8_t readDiscreteInputs(uint16_t, uint16_t);
260 | uint8_t readHoldingRegisters(uint16_t, uint16_t);
261 | uint8_t readInputRegisters(uint16_t, uint8_t);
262 | uint8_t writeSingleCoil(uint16_t, uint8_t);
263 | uint8_t writeSingleRegister(uint16_t, uint16_t);
264 | uint8_t writeMultipleCoils(uint16_t, uint16_t);
265 | uint8_t writeMultipleCoils();
266 | uint8_t writeMultipleRegisters(uint16_t, uint16_t);
267 | uint8_t writeMultipleRegisters();
268 | uint8_t maskWriteRegister(uint16_t, uint16_t, uint16_t);
269 | uint8_t readWriteMultipleRegisters(uint16_t, uint16_t, uint16_t, uint16_t);
270 | uint8_t readWriteMultipleRegisters(uint16_t, uint16_t);
271 |
272 |
273 | private:
274 | uint8_t _u8SerialPort; ///< serial port (0..3) initialized in constructor
275 | uint8_t _u8MBSlave; ///< Modbus slave (1..255) initialized in constructor
276 | uint16_t _u16BaudRate; ///< baud rate (300..115200) initialized in begin()
277 | static const uint8_t ku8MaxBufferSize = 64; ///< size of response/transmit buffers
278 | uint16_t _u16ReadAddress; ///< slave register from which to read
279 | uint16_t _u16ReadQty; ///< quantity of words to read
280 | uint16_t _u16ResponseBuffer[ku8MaxBufferSize]; ///< buffer to store Modbus slave response; read via GetResponseBuffer()
281 | uint16_t _u16WriteAddress; ///< slave register to which to write
282 | uint16_t _u16WriteQty; ///< quantity of words to write
283 | uint16_t _u16TransmitBuffer[ku8MaxBufferSize]; ///< buffer containing data to transmit to Modbus slave; set via SetTransmitBuffer()
284 | uint16_t* txBuffer; // from Wire.h -- need to clean this up Rx
285 | uint8_t _u8TransmitBufferIndex;
286 | uint16_t u16TransmitBufferLength;
287 | uint16_t* rxBuffer; // from Wire.h -- need to clean this up Rx
288 | uint8_t _u8ResponseBufferIndex;
289 | uint8_t _u8ResponseBufferLength;
290 |
291 | // Modbus function codes for bit access
292 | static const uint8_t ku8MBReadCoils = 0x01; ///< Modbus function 0x01 Read Coils
293 | static const uint8_t ku8MBReadDiscreteInputs = 0x02; ///< Modbus function 0x02 Read Discrete Inputs
294 | static const uint8_t ku8MBWriteSingleCoil = 0x05; ///< Modbus function 0x05 Write Single Coil
295 | static const uint8_t ku8MBWriteMultipleCoils = 0x0F; ///< Modbus function 0x0F Write Multiple Coils
296 |
297 | // Modbus function codes for 16 bit access
298 | static const uint8_t ku8MBReadHoldingRegisters = 0x03; ///< Modbus function 0x03 Read Holding Registers
299 | static const uint8_t ku8MBReadInputRegisters = 0x04; ///< Modbus function 0x04 Read Input Registers
300 | static const uint8_t ku8MBWriteSingleRegister = 0x06; ///< Modbus function 0x06 Write Single Register
301 | static const uint8_t ku8MBWriteMultipleRegisters = 0x10; ///< Modbus function 0x10 Write Multiple Registers
302 | static const uint8_t ku8MBMaskWriteRegister = 0x16; ///< Modbus function 0x16 Mask Write Register
303 | static const uint8_t ku8MBReadWriteMultipleRegisters = 0x17; ///< Modbus function 0x17 Read Write Multiple Registers
304 |
305 | // Modbus timeout [milliseconds]
306 | static const uint8_t ku8MBResponseTimeout = 200; ///< Modbus timeout [milliseconds]
307 |
308 | // master function that conducts Modbus transactions
309 | uint8_t ModbusMasterTransaction(uint8_t u8MBFunction);
310 |
311 | // idle callback function; gets called during idle time between TX and RX
312 | void (*_idle)();
313 |
314 |
315 | //Commented function makeWord not supported by ESP8266 - PDAControl
316 | //uint16_t makeWord(uint16_t w);
317 | // uint16_t makeWord(uint8_t h, uint8_t l);
318 |
319 | };
320 | #endif
321 |
322 | /**
323 | @example examples/Basic/Basic.pde
324 | @example examples/PhoenixContact_nanoLC/PhoenixContact_nanoLC.pde
325 | */
326 |
--------------------------------------------------------------------------------
/ModBusMaster232/ModbusMaster232.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | @file
3 | Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol).
4 | */
5 | /*
6 |
7 | ModbusMaster.cpp - Arduino library for communicating with Modbus slaves
8 | over RS232/485 (via RTU protocol).
9 |
10 | This file is part of ModbusMaster.
11 |
12 | ModbusMaster is free software: you can redistribute it and/or modify
13 | it under the terms of the GNU General Public License as published by
14 | the Free Software Foundation, either version 3 of the License, or
15 | (at your option) any later version.
16 |
17 | ModbusMaster is distributed in the hope that it will be useful,
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | GNU General Public License for more details.
21 |
22 | You should have received a copy of the GNU General Public License
23 | along with ModbusMaster. If not, see .
24 |
25 | Written by Doc Walker (Rx)
26 | Copyright © 2009-2013 Doc Walker <4-20ma at wvfans dot net>
27 |
28 |
29 | Modified by PDAControl 2016
30 | - Function crc16
31 | - Function makeWord
32 |
33 |
34 | */
35 | #include "ModbusMaster232.h"
36 |
37 |
38 | #include "Arduino.h"
39 |
40 |
41 | /* _____PUBLIC FUNCTIONS_____________________________________________________ */
42 |
43 | /**
44 | Constructor.
45 | Creates class object using default serial port 0, Modbus slave ID 1.
46 | @ingroup setup
47 | */
48 | ModbusMaster232::ModbusMaster232(void)
49 | {
50 | _u8SerialPort = 0;
51 | _u8MBSlave = 1;
52 | }
53 |
54 |
55 | /**
56 | Constructor.
57 |
58 | Creates class object using default serial port 0, specified Modbus slave ID.
59 |
60 | @overload void ModbusMaster::ModbusMaster(uint8_t u8MBSlave)
61 | @param u8MBSlave Modbus slave ID (1..255)
62 | @ingroup setup
63 | */
64 | ModbusMaster232::ModbusMaster232(uint8_t u8MBSlave)
65 | {
66 | _u8SerialPort = 0;
67 | _u8MBSlave = u8MBSlave;
68 | }
69 |
70 |
71 | /**
72 | CRC ESP8266
73 |
74 | Creates class object using default serial port 0, specified Modbus slave ID.
75 |
76 | @overload void ModbusMaster::ModbusMaster(uint8_t u8MBSlave)
77 | @param u8MBSlave Modbus slave ID (1..255)
78 | @ingroup setup
79 | */
80 |
81 | //Function crc16 created for ESP8266 - PDAControl
82 | //http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__util__crc_1ga95371c87f25b0a2497d9cba13190847f.html
83 | // append CRC
84 |
85 | uint16_t _crc16_update2 (uint16_t crc, uint8_t a)
86 | {
87 | int i;
88 |
89 | crc ^= a;
90 | for (i = 0; i < 8; ++i)
91 | {
92 | if (crc & 1)
93 | crc = (crc >> 1) ^ 0xA001;
94 | else
95 | crc = (crc >> 1);
96 | }
97 |
98 | return crc;
99 | }
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | /**
111 | Initialize class object.
112 |
113 | Sets up the serial port using default 19200 baud rate.
114 | Call once class has been instantiated, typically within setup().
115 |
116 | @ingroup setup
117 | */
118 | void ModbusMaster232::begin(void)
119 | {
120 | Serial.begin(9600);
121 | }
122 |
123 |
124 | /**
125 | Initialize class object.
126 |
127 | Sets up the serial port using specified baud rate.
128 | Call once class has been instantiated, typically within setup().
129 |
130 | @overload ModbusMaster::begin(uint16_t u16BaudRate)
131 | @param u16BaudRate baud rate, in standard increments (300..115200)
132 | @ingroup setup
133 | */
134 | void ModbusMaster232::begin(unsigned long BaudRate )
135 | {
136 | // txBuffer = (uint16_t*) calloc(ku8MaxBufferSize, sizeof(uint16_t));
137 | _u8TransmitBufferIndex = 0;
138 | u16TransmitBufferLength = 0;
139 |
140 | delay(100);
141 | Serial.begin(BaudRate);
142 | }
143 |
144 |
145 | void ModbusMaster232::beginTransmission(uint16_t u16Address)
146 | {
147 | _u16WriteAddress = u16Address;
148 | _u8TransmitBufferIndex = 0;
149 | u16TransmitBufferLength = 0;
150 | }
151 |
152 | // eliminate this function in favor of using existing MB request functions
153 | uint8_t ModbusMaster232::requestFrom(uint16_t address, uint16_t quantity)
154 | {
155 | uint8_t read;
156 | // clamp to buffer length
157 | if(quantity > ku8MaxBufferSize)
158 | {
159 | quantity = ku8MaxBufferSize;
160 | }
161 | // set rx buffer iterator vars
162 | _u8ResponseBufferIndex = 0;
163 | _u8ResponseBufferLength = read;
164 |
165 | return read;
166 | }
167 |
168 |
169 | void ModbusMaster232::sendBit(bool data)
170 | {
171 | uint8_t txBitIndex = u16TransmitBufferLength % 16;
172 | if ((u16TransmitBufferLength >> 4) < ku8MaxBufferSize)
173 | {
174 | if (0 == txBitIndex)
175 | {
176 | _u16TransmitBuffer[_u8TransmitBufferIndex] = 0;
177 | }
178 | bitWrite(_u16TransmitBuffer[_u8TransmitBufferIndex], txBitIndex, data);
179 | u16TransmitBufferLength++;
180 | _u8TransmitBufferIndex = u16TransmitBufferLength >> 4;
181 | }
182 | }
183 |
184 |
185 | void ModbusMaster232::send(uint16_t data)
186 | {
187 | if (_u8TransmitBufferIndex < ku8MaxBufferSize)
188 | {
189 | _u16TransmitBuffer[_u8TransmitBufferIndex++] = data;
190 | u16TransmitBufferLength = _u8TransmitBufferIndex << 4;
191 | }
192 | }
193 |
194 |
195 | void ModbusMaster232::send(uint32_t data)
196 | {
197 | send(lowWord(data));
198 | send(highWord(data));
199 | }
200 |
201 |
202 | void ModbusMaster232::send(uint8_t data)
203 | {
204 | send(uint16_t(data));
205 | }
206 |
207 |
208 |
209 | uint8_t ModbusMaster232::available(void)
210 | {
211 | return _u8ResponseBufferLength - _u8ResponseBufferIndex;
212 | }
213 |
214 |
215 | uint16_t ModbusMaster232::receive(void)
216 | {
217 | if (_u8ResponseBufferIndex < _u8ResponseBufferLength)
218 | {
219 | return _u16ResponseBuffer[_u8ResponseBufferIndex++];
220 | }
221 | else
222 | {
223 | return 0xFFFF;
224 | }
225 | }
226 |
227 |
228 |
229 |
230 | /**
231 | Set idle time callback function (cooperative multitasking).
232 |
233 | This function gets called in the idle time between transmission of data
234 | and response from slave. Do not call functions that read from the serial
235 | buffer that is used by ModbusMaster. Use of i2c/TWI, 1-Wire, other
236 | serial ports, etc. is permitted within callback function.
237 |
238 | @see ModbusMaster::ModbusMasterTransaction()
239 | */
240 | void ModbusMaster232::idle(void (*idle)())
241 | {
242 | _idle = idle;
243 | }
244 |
245 |
246 | /**
247 | Retrieve data from response buffer.
248 |
249 | @see ModbusMaster::clearResponseBuffer()
250 | @param u8Index index of response buffer array (0x00..0x3F)
251 | @return value in position u8Index of response buffer (0x0000..0xFFFF)
252 | @ingroup buffer
253 | */
254 | uint16_t ModbusMaster232::getResponseBuffer(uint8_t u8Index)
255 | {
256 | if (u8Index < ku8MaxBufferSize)
257 | {
258 | return _u16ResponseBuffer[u8Index];
259 | }
260 | else
261 | {
262 | return 0xFFFF;
263 | }
264 | }
265 |
266 |
267 | /**
268 | Clear Modbus response buffer.
269 |
270 | @see ModbusMaster::getResponseBuffer(uint8_t u8Index)
271 | @ingroup buffer
272 | */
273 | void ModbusMaster232::clearResponseBuffer()
274 | {
275 | uint8_t i;
276 |
277 | for (i = 0; i < ku8MaxBufferSize; i++)
278 | {
279 | _u16ResponseBuffer[i] = 0;
280 | }
281 | }
282 |
283 |
284 | /**
285 | Place data in transmit buffer.
286 |
287 | @see ModbusMaster::clearTransmitBuffer()
288 | @param u8Index index of transmit buffer array (0x00..0x3F)
289 | @param u16Value value to place in position u8Index of transmit buffer (0x0000..0xFFFF)
290 | @return 0 on success; exception number on failure
291 | @ingroup buffer
292 | */
293 | uint8_t ModbusMaster232::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value)
294 | {
295 | if (u8Index < ku8MaxBufferSize)
296 | {
297 | _u16TransmitBuffer[u8Index] = u16Value;
298 | return ku8MBSuccess;
299 | }
300 | else
301 | {
302 | return ku8MBIllegalDataAddress;
303 | }
304 | }
305 |
306 |
307 | /**
308 | Clear Modbus transmit buffer.
309 |
310 | @see ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value)
311 | @ingroup buffer
312 | */
313 | void ModbusMaster232::clearTransmitBuffer()
314 | {
315 | uint8_t i;
316 |
317 | for (i = 0; i < ku8MaxBufferSize; i++)
318 | {
319 | _u16TransmitBuffer[i] = 0;
320 | }
321 | }
322 |
323 |
324 | /**
325 | Modbus function 0x01 Read Coils.
326 |
327 | This function code is used to read from 1 to 2000 contiguous status of
328 | coils in a remote device. The request specifies the starting address,
329 | i.e. the address of the first coil specified, and the number of coils.
330 | Coils are addressed starting at zero.
331 |
332 | The coils in the response buffer are packed as one coil per bit of the
333 | data field. Status is indicated as 1=ON and 0=OFF. The LSB of the first
334 | data word contains the output addressed in the query. The other coils
335 | follow toward the high order end of this word and from low order to high
336 | order in subsequent words.
337 |
338 | If the returned quantity is not a multiple of sixteen, the remaining
339 | bits in the final data word will be padded with zeros (toward the high
340 | order end of the word).
341 |
342 | @param u16ReadAddress address of first coil (0x0000..0xFFFF)
343 | @param u16BitQty quantity of coils to read (1..2000, enforced by remote device)
344 | @return 0 on success; exception number on failure
345 | @ingroup discrete
346 | */
347 | uint8_t ModbusMaster232::readCoils(uint16_t u16ReadAddress, uint16_t u16BitQty)
348 | {
349 | _u16ReadAddress = u16ReadAddress;
350 | _u16ReadQty = u16BitQty;
351 | return ModbusMasterTransaction(ku8MBReadCoils);
352 | }
353 |
354 |
355 | /**
356 | Modbus function 0x02 Read Discrete Inputs.
357 |
358 | This function code is used to read from 1 to 2000 contiguous status of
359 | discrete inputs in a remote device. The request specifies the starting
360 | address, i.e. the address of the first input specified, and the number
361 | of inputs. Discrete inputs are addressed starting at zero.
362 |
363 | The discrete inputs in the response buffer are packed as one input per
364 | bit of the data field. Status is indicated as 1=ON; 0=OFF. The LSB of
365 | the first data word contains the input addressed in the query. The other
366 | inputs follow toward the high order end of this word, and from low order
367 | to high order in subsequent words.
368 |
369 | If the returned quantity is not a multiple of sixteen, the remaining
370 | bits in the final data word will be padded with zeros (toward the high
371 | order end of the word).
372 |
373 | @param u16ReadAddress address of first discrete input (0x0000..0xFFFF)
374 | @param u16BitQty quantity of discrete inputs to read (1..2000, enforced by remote device)
375 | @return 0 on success; exception number on failure
376 | @ingroup discrete
377 | */
378 | uint8_t ModbusMaster232::readDiscreteInputs(uint16_t u16ReadAddress,
379 | uint16_t u16BitQty)
380 | {
381 | _u16ReadAddress = u16ReadAddress;
382 | _u16ReadQty = u16BitQty;
383 | return ModbusMasterTransaction(ku8MBReadDiscreteInputs);
384 | }
385 |
386 |
387 | /**
388 | Modbus function 0x03 Read Holding Registers.
389 |
390 | This function code is used to read the contents of a contiguous block of
391 | holding registers in a remote device. The request specifies the starting
392 | register address and the number of registers. Registers are addressed
393 | starting at zero.
394 |
395 | The register data in the response buffer is packed as one word per
396 | register.
397 |
398 | @param u16ReadAddress address of the first holding register (0x0000..0xFFFF)
399 | @param u16ReadQty quantity of holding registers to read (1..125, enforced by remote device)
400 | @return 0 on success; exception number on failure
401 | @ingroup register
402 | */
403 | uint8_t ModbusMaster232::readHoldingRegisters(uint16_t u16ReadAddress,
404 | uint16_t u16ReadQty)
405 | {
406 | _u16ReadAddress = u16ReadAddress;
407 | _u16ReadQty = u16ReadQty;
408 | return ModbusMasterTransaction(ku8MBReadHoldingRegisters);
409 | }
410 |
411 |
412 | /**
413 | Modbus function 0x04 Read Input Registers.
414 |
415 | This function code is used to read from 1 to 125 contiguous input
416 | registers in a remote device. The request specifies the starting
417 | register address and the number of registers. Registers are addressed
418 | starting at zero.
419 |
420 | The register data in the response buffer is packed as one word per
421 | register.
422 |
423 | @param u16ReadAddress address of the first input register (0x0000..0xFFFF)
424 | @param u16ReadQty quantity of input registers to read (1..125, enforced by remote device)
425 | @return 0 on success; exception number on failure
426 | @ingroup register
427 | */
428 | uint8_t ModbusMaster232::readInputRegisters(uint16_t u16ReadAddress,
429 | uint8_t u16ReadQty)
430 | {
431 | _u16ReadAddress = u16ReadAddress;
432 | _u16ReadQty = u16ReadQty;
433 | return ModbusMasterTransaction(ku8MBReadInputRegisters);
434 | }
435 |
436 |
437 | /**
438 | Modbus function 0x05 Write Single Coil.
439 |
440 | This function code is used to write a single output to either ON or OFF
441 | in a remote device. The requested ON/OFF state is specified by a
442 | constant in the state field. A non-zero value requests the output to be
443 | ON and a value of 0 requests it to be OFF. The request specifies the
444 | address of the coil to be forced. Coils are addressed starting at zero.
445 |
446 | @param u16WriteAddress address of the coil (0x0000..0xFFFF)
447 | @param u8State 0=OFF, non-zero=ON (0x00..0xFF)
448 | @return 0 on success; exception number on failure
449 | @ingroup discrete
450 | */
451 | uint8_t ModbusMaster232::writeSingleCoil(uint16_t u16WriteAddress, uint8_t u8State)
452 | {
453 | _u16WriteAddress = u16WriteAddress;
454 | _u16WriteQty = (u8State ? 0xFF00 : 0x0000);
455 | return ModbusMasterTransaction(ku8MBWriteSingleCoil);
456 | }
457 |
458 |
459 | /**
460 | Modbus function 0x06 Write Single Register.
461 |
462 | This function code is used to write a single holding register in a
463 | remote device. The request specifies the address of the register to be
464 | written. Registers are addressed starting at zero.
465 |
466 | @param u16WriteAddress address of the holding register (0x0000..0xFFFF)
467 | @param u16WriteValue value to be written to holding register (0x0000..0xFFFF)
468 | @return 0 on success; exception number on failure
469 | @ingroup register
470 | */
471 | uint8_t ModbusMaster232::writeSingleRegister(uint16_t u16WriteAddress,
472 | uint16_t u16WriteValue)
473 | {
474 | _u16WriteAddress = u16WriteAddress;
475 | _u16WriteQty = 0;
476 | _u16TransmitBuffer[0] = u16WriteValue;
477 | return ModbusMasterTransaction(ku8MBWriteSingleRegister);
478 | }
479 |
480 |
481 | /**
482 | Modbus function 0x0F Write Multiple Coils.
483 |
484 | This function code is used to force each coil in a sequence of coils to
485 | either ON or OFF in a remote device. The request specifies the coil
486 | references to be forced. Coils are addressed starting at zero.
487 |
488 | The requested ON/OFF states are specified by contents of the transmit
489 | buffer. A logical '1' in a bit position of the buffer requests the
490 | corresponding output to be ON. A logical '0' requests it to be OFF.
491 |
492 | @param u16WriteAddress address of the first coil (0x0000..0xFFFF)
493 | @param u16BitQty quantity of coils to write (1..2000, enforced by remote device)
494 | @return 0 on success; exception number on failure
495 | @ingroup discrete
496 | */
497 | uint8_t ModbusMaster232::writeMultipleCoils(uint16_t u16WriteAddress,
498 | uint16_t u16BitQty)
499 | {
500 | _u16WriteAddress = u16WriteAddress;
501 | _u16WriteQty = u16BitQty;
502 | return ModbusMasterTransaction(ku8MBWriteMultipleCoils);
503 | }
504 | uint8_t ModbusMaster232::writeMultipleCoils()
505 | {
506 | _u16WriteQty = u16TransmitBufferLength;
507 | return ModbusMasterTransaction(ku8MBWriteMultipleCoils);
508 | }
509 |
510 |
511 | /**
512 | Modbus function 0x10 Write Multiple Registers.
513 |
514 | This function code is used to write a block of contiguous registers (1
515 | to 123 registers) in a remote device.
516 |
517 | The requested written values are specified in the transmit buffer. Data
518 | is packed as one word per register.
519 |
520 | @param u16WriteAddress address of the holding register (0x0000..0xFFFF)
521 | @param u16WriteQty quantity of holding registers to write (1..123, enforced by remote device)
522 | @return 0 on success; exception number on failure
523 | @ingroup register
524 | */
525 | uint8_t ModbusMaster232::writeMultipleRegisters(uint16_t u16WriteAddress,
526 | uint16_t u16WriteQty)
527 | {
528 | _u16WriteAddress = u16WriteAddress;
529 | _u16WriteQty = u16WriteQty;
530 | return ModbusMasterTransaction(ku8MBWriteMultipleRegisters);
531 | }
532 |
533 | // new version based on Wire.h
534 | uint8_t ModbusMaster232::writeMultipleRegisters()
535 | {
536 | _u16WriteQty = _u8TransmitBufferIndex;
537 | return ModbusMasterTransaction(ku8MBWriteMultipleRegisters);
538 | }
539 |
540 |
541 | /**
542 | Modbus function 0x16 Mask Write Register.
543 |
544 | This function code is used to modify the contents of a specified holding
545 | register using a combination of an AND mask, an OR mask, and the
546 | register's current contents. The function can be used to set or clear
547 | individual bits in the register.
548 |
549 | The request specifies the holding register to be written, the data to be
550 | used as the AND mask, and the data to be used as the OR mask. Registers
551 | are addressed starting at zero.
552 |
553 | The function's algorithm is:
554 |
555 | Result = (Current Contents && And_Mask) || (Or_Mask && (~And_Mask))
556 |
557 | @param u16WriteAddress address of the holding register (0x0000..0xFFFF)
558 | @param u16AndMask AND mask (0x0000..0xFFFF)
559 | @param u16OrMask OR mask (0x0000..0xFFFF)
560 | @return 0 on success; exception number on failure
561 | @ingroup register
562 | */
563 | uint8_t ModbusMaster232::maskWriteRegister(uint16_t u16WriteAddress,
564 | uint16_t u16AndMask, uint16_t u16OrMask)
565 | {
566 | _u16WriteAddress = u16WriteAddress;
567 | _u16TransmitBuffer[0] = u16AndMask;
568 | _u16TransmitBuffer[1] = u16OrMask;
569 | return ModbusMasterTransaction(ku8MBMaskWriteRegister);
570 | }
571 |
572 |
573 | /**
574 | Modbus function 0x17 Read Write Multiple Registers.
575 |
576 | This function code performs a combination of one read operation and one
577 | write operation in a single MODBUS transaction. The write operation is
578 | performed before the read. Holding registers are addressed starting at
579 | zero.
580 |
581 | The request specifies the starting address and number of holding
582 | registers to be read as well as the starting address, and the number of
583 | holding registers. The data to be written is specified in the transmit
584 | buffer.
585 |
586 | @param u16ReadAddress address of the first holding register (0x0000..0xFFFF)
587 | @param u16ReadQty quantity of holding registers to read (1..125, enforced by remote device)
588 | @param u16WriteAddress address of the first holding register (0x0000..0xFFFF)
589 | @param u16WriteQty quantity of holding registers to write (1..121, enforced by remote device)
590 | @return 0 on success; exception number on failure
591 | @ingroup register
592 | */
593 | uint8_t ModbusMaster232::readWriteMultipleRegisters(uint16_t u16ReadAddress,
594 | uint16_t u16ReadQty, uint16_t u16WriteAddress, uint16_t u16WriteQty)
595 | {
596 | _u16ReadAddress = u16ReadAddress;
597 | _u16ReadQty = u16ReadQty;
598 | _u16WriteAddress = u16WriteAddress;
599 | _u16WriteQty = u16WriteQty;
600 | return ModbusMasterTransaction(ku8MBReadWriteMultipleRegisters);
601 | }
602 | uint8_t ModbusMaster232::readWriteMultipleRegisters(uint16_t u16ReadAddress,
603 | uint16_t u16ReadQty)
604 | {
605 | _u16ReadAddress = u16ReadAddress;
606 | _u16ReadQty = u16ReadQty;
607 | _u16WriteQty = _u8TransmitBufferIndex;
608 | return ModbusMasterTransaction(ku8MBReadWriteMultipleRegisters);
609 | }
610 |
611 |
612 | /* _____PRIVATE FUNCTIONS____________________________________________________ */
613 | /**
614 | Modbus transaction engine.
615 | Sequence:
616 | - assemble Modbus Request Application Data Unit (ADU),
617 | based on particular function called
618 | - transmit request over selected serial port
619 | - wait for/retrieve response
620 | - evaluate/disassemble response
621 | - return status (success/exception)
622 |
623 | @param u8MBFunction Modbus function (0x01..0xFF)
624 | @return 0 on success; exception number on failure
625 | */
626 | uint8_t ModbusMaster232::ModbusMasterTransaction(uint8_t u8MBFunction)
627 | {
628 | uint8_t u8ModbusADU[256];
629 | uint8_t u8ModbusADUSize = 0;
630 | uint8_t i, u8Qty;
631 | uint16_t u16CRC;
632 | uint32_t u32StartTime;
633 | uint8_t u8BytesLeft = 8;
634 | uint8_t u8MBStatus = ku8MBSuccess;
635 |
636 | // assemble Modbus Request Application Data Unit
637 | u8ModbusADU[u8ModbusADUSize++] = _u8MBSlave;
638 | u8ModbusADU[u8ModbusADUSize++] = u8MBFunction;
639 |
640 | switch(u8MBFunction)
641 | {
642 | case ku8MBReadCoils:
643 | case ku8MBReadDiscreteInputs:
644 | case ku8MBReadInputRegisters:
645 | case ku8MBReadHoldingRegisters:
646 | case ku8MBReadWriteMultipleRegisters:
647 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadAddress);
648 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadAddress);
649 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadQty);
650 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadQty);
651 | break;
652 | }
653 |
654 | switch(u8MBFunction)
655 | {
656 | case ku8MBWriteSingleCoil:
657 | case ku8MBMaskWriteRegister:
658 | case ku8MBWriteMultipleCoils:
659 | case ku8MBWriteSingleRegister:
660 | case ku8MBWriteMultipleRegisters:
661 | case ku8MBReadWriteMultipleRegisters:
662 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteAddress);
663 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteAddress);
664 | break;
665 | }
666 |
667 | switch(u8MBFunction)
668 | {
669 | case ku8MBWriteSingleCoil:
670 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty);
671 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty);
672 | break;
673 |
674 | case ku8MBWriteSingleRegister:
675 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[0]);
676 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]);
677 | break;
678 |
679 | case ku8MBWriteMultipleCoils:
680 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty);
681 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty);
682 | u8Qty = (_u16WriteQty % 8) ? ((_u16WriteQty >> 3) + 1) : (_u16WriteQty >> 3);
683 | u8ModbusADU[u8ModbusADUSize++] = u8Qty;
684 | for (i = 0; i < u8Qty; i++)
685 | {
686 | switch(i % 2)
687 | {
688 | case 0: // i is even
689 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i >> 1]);
690 | break;
691 |
692 | case 1: // i is odd
693 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i >> 1]);
694 | break;
695 | }
696 | }
697 | break;
698 |
699 | case ku8MBWriteMultipleRegisters:
700 | case ku8MBReadWriteMultipleRegisters:
701 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty);
702 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty);
703 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty << 1);
704 |
705 | for (i = 0; i < lowByte(_u16WriteQty); i++)
706 | {
707 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i]);
708 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i]);
709 | }
710 | break;
711 |
712 | case ku8MBMaskWriteRegister:
713 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[0]);
714 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]);
715 | u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[1]);
716 | u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[1]);
717 | break;
718 | }
719 |
720 |
721 | u16CRC = 0xFFFF;
722 | for (i = 0; i < u8ModbusADUSize; i++)
723 | {
724 | //Function crc16 for ESP8266 - PDAControl
725 | u16CRC = _crc16_update2(u16CRC, u8ModbusADU[i]);
726 | }
727 | u8ModbusADU[u8ModbusADUSize++] = lowByte(u16CRC);
728 | u8ModbusADU[u8ModbusADUSize++] = highByte(u16CRC);
729 | u8ModbusADU[u8ModbusADUSize] = 0;
730 |
731 | // transmit request
732 | for (i = 0; i < u8ModbusADUSize; i++)
733 | {
734 | Serial.print(char(u8ModbusADU[i]));
735 | }
736 |
737 | delay(2);
738 |
739 | u8ModbusADUSize = 1;
740 |
741 | // loop until we run out of time or bytes, or an error occurs
742 | u32StartTime = millis();
743 |
744 | int val = 0xFF;
745 | long cont = 0;
746 |
747 | while((val != _u8MBSlave) && (cont < 100)) {
748 | val = Serial.read();
749 | delay(5);
750 | cont ++;
751 | }
752 |
753 | u8ModbusADU[u8ModbusADUSize] = val;
754 |
755 |
756 | while (u8BytesLeft && !u8MBStatus)
757 | {
758 | if (Serial.available())
759 | {
760 | u8ModbusADU[u8ModbusADUSize] = Serial.read();
761 | u8BytesLeft--;
762 | u8ModbusADUSize ++;
763 |
764 | }
765 | else
766 | {
767 |
768 | if (_idle)
769 | {
770 | _idle();
771 | }
772 |
773 | }
774 |
775 | // evaluate slave ID, function code once enough bytes have been read
776 | if (u8ModbusADUSize == 5)
777 | {
778 |
779 | // verify response is for correct Modbus slave
780 | if (u8ModbusADU[0] != _u8MBSlave)
781 | {
782 | u8MBStatus = ku8MBInvalidSlaveID;
783 | break;
784 | }
785 |
786 | // verify response is for correct Modbus function code (mask exception bit 7)
787 | if ((u8ModbusADU[1] & 0x7F) != u8MBFunction)
788 | {
789 | u8MBStatus = ku8MBInvalidFunction;
790 | break;
791 | }
792 |
793 | // check whether Modbus exception occurred; return Modbus Exception Code
794 | if (bitRead(u8ModbusADU[1], 7))
795 | {
796 | u8MBStatus = u8ModbusADU[2];
797 | break;
798 | }
799 |
800 | // evaluate returned Modbus function code
801 | switch(u8ModbusADU[1])
802 | {
803 | case ku8MBReadCoils:
804 | case ku8MBReadDiscreteInputs:
805 | case ku8MBReadInputRegisters:
806 | case ku8MBReadHoldingRegisters:
807 | case ku8MBReadWriteMultipleRegisters:
808 | u8BytesLeft = u8ModbusADU[2];
809 | break;
810 |
811 | case ku8MBWriteSingleCoil:
812 | case ku8MBWriteMultipleCoils:
813 | case ku8MBWriteSingleRegister:
814 | case ku8MBWriteMultipleRegisters:
815 | u8BytesLeft = 3;
816 | break;
817 |
818 | case ku8MBMaskWriteRegister:
819 | u8BytesLeft = 5;
820 | break;
821 | }
822 | }
823 | if (millis() > (u32StartTime + ku8MBResponseTimeout))
824 | {
825 | u8MBStatus = ku8MBResponseTimedOut;
826 | }
827 | }
828 |
829 | // verify response is large enough to inspect further
830 | if (!u8MBStatus && u8ModbusADUSize >= 5)
831 | {
832 | // calculate CRC
833 | u16CRC = 0xFFFF;
834 | for (i = 0; i < (u8ModbusADUSize - 2); i++)
835 | {
836 | //Function crc16 for ESP8266 - PDAControl
837 | u16CRC = _crc16_update2(u16CRC, u8ModbusADU[i]);
838 | }
839 |
840 |
841 |
842 |
843 | // verify CRC
844 | if (!u8MBStatus && (lowByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 2] ||
845 | highByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 1]))
846 | {
847 | u8MBStatus = ku8MBInvalidCRC;
848 | }
849 | }
850 |
851 | // disassemble ADU into words
852 | if (!u8MBStatus)
853 | {
854 | // evaluate returned Modbus function code
855 | switch(u8ModbusADU[1])
856 | {
857 | case ku8MBReadCoils:
858 | case ku8MBReadDiscreteInputs:
859 | // load bytes into word; response bytes are ordered L, H, L, H, ...
860 | for (i = 0; i < (u8ModbusADU[2] >> 1); i++)
861 | {
862 | if (i < ku8MaxBufferSize)
863 | {
864 |
865 | //Function makeWord Modified to ESP8266 - PDAControl
866 | //modificado for ESP8266
867 | // _u16ResponseBuffer[i] = makeWord(u8ModbusADU[2 * i + 4], u8ModbusADU[2 * i + 3]);
868 |
869 | _u16ResponseBuffer[i] = (u8ModbusADU[2 * i + 4] << 8) | u8ModbusADU[2 * i + 3];
870 |
871 | }
872 |
873 | _u8ResponseBufferLength = i;
874 | }
875 |
876 | // in the event of an odd number of bytes, load last byte into zero-padded word
877 | if (u8ModbusADU[2] % 2)
878 | {
879 | if (i < ku8MaxBufferSize)
880 | {
881 | //Function makeWord Modified to ESP8266 - PDAControl
882 |
883 | // _u16ResponseBuffer[i] = makeWord(0, u8ModbusADU[2 * i + 3]);
884 |
885 | _u16ResponseBuffer[i] = (0 << 8) | u8ModbusADU[2 * i + 3];
886 | //return (h << 8) | l;
887 | }
888 |
889 | _u8ResponseBufferLength = i + 1;
890 | }
891 | break;
892 |
893 | case ku8MBReadInputRegisters:
894 | case ku8MBReadHoldingRegisters:
895 | case ku8MBReadWriteMultipleRegisters:
896 | // load bytes into word; response bytes are ordered H, L, H, L, ...
897 | for (i = 0; i < (u8ModbusADU[2] >> 1); i++)
898 | {
899 | if (i < ku8MaxBufferSize)
900 | {
901 | //Function makeWord Modified to ESP8266 - PDAControl
902 |
903 | // _u16ResponseBuffer[i] = makeWord(u8ModbusADU[2 * i + 3], u8ModbusADU[2 * i + 4]);
904 |
905 | //return (h << 8) | l;
906 | _u16ResponseBuffer[i] = (u8ModbusADU[2 * i + 3] << 8) | u8ModbusADU[2 * i + 4];
907 | }
908 |
909 | _u8ResponseBufferLength = i;
910 | }
911 | break;
912 | }
913 | }
914 |
915 |
916 | _u8TransmitBufferIndex = 0;
917 | u16TransmitBufferLength = 0;
918 | _u8ResponseBufferIndex = 0;
919 | return u8MBStatus;
920 | }
921 |
922 | /*
923 |
924 | MakeWord function deleted -for ESP8266 PDAControl
925 |
926 |
927 | unsigned int ModbusMaster232::makeWord(unsigned int w)
928 | {
929 | return w;
930 | }
931 |
932 |
933 | unsigned int ModbusMaster232::makeWord(uint8_t h, uint8_t l)
934 | {
935 | return (h << 8) | l;
936 | }
937 | */
938 | void ModbusMaster232::setSlaveAddress(uint8_t u8MBSlave){
939 |
940 | _u8MBSlave = u8MBSlave;
941 | }
942 |
943 |
--------------------------------------------------------------------------------