├── 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 | ![Portada](https://github.com/JhonControl/ESP8266_ModbusRTU_Master_V2/blob/master/extras/NodeMCU%20Modbus%20MASTER.jpg) 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 | ![Portada](https://github.com/JhonControl/ESP8266_ModbusRTU_Master_V2/blob/master/extras/NodeMCU_PDAControl.jpg) 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 | --------------------------------------------------------------------------------