diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..7242f03
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+custom: https://paypal.me/modbus?locale.x=en_US
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f0b948a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.vscode/arduino.json
+.vscode/c_cpp_properties.json
+.vscode/*
+src/ModbusSerial.cpp
+src/ModbusSerial.h
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index e69de29..0000000
diff --git a/API.md b/API.md
new file mode 100644
index 0000000..bcfb971
--- /dev/null
+++ b/API.md
@@ -0,0 +1 @@
+Moved to [documentation](documentation)
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100755
index 0000000..01b31a6
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(COMPONENT_ADD_INCLUDEDIRS src)
+set(COMPONENT_SRCS "src/Modbus.cpp" "src/ModbusRTU.cpp")
+set(COMPONENT_PRIV_REQUIRES arduino)
+register_component()
+
+
diff --git a/LICENSE.txt b/LICENSE.txt
index 5810ccb..f7aa0ba 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,8 @@
Copyright (c) 2015, André Sarmento Barbosa
+ 2017, Alexander Emelianov (a.m.emelianov@gmail.com)
All rights reserved.
+
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
diff --git a/README.md b/README.md
index dc26c87..e419e69 100644
--- a/README.md
+++ b/README.md
@@ -1,72 +1,129 @@
-Modbus Library for ESP8266
-==========================
-
-This library allows your ESP8266 to communicate via Modbus protocol. The Modbus is a master-slave protocol
-used in industrial automation and can be used in other areas, such as home automation.
-
-The Modbus generally uses serial RS-232 or RS-485 as physical layer (then called Modbus Serial) and TCP/IP via Ethernet or WiFi (Modbus IP).
-
-In the current version the library allows the ESP8266 operate as a slave, supporting Modbus IP via wireless network. For more information about Modbus see:
-
-http://pt.wikipedia.org/wiki/Modbus http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
-http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf
-
-Features
-========
-
-
-- Operates as a slave
-- Supports Modbus IP (TCP, not keep-alive)
-- Reply exception messages for all supported functions
-- Modbus functions supported:
-
- - 0x01 - Read Coils
- - 0x02 - Read Input Status (Read Discrete Inputs)
- - 0x03 - Read Holding Registers
- - 0x04 - Read Input Registers
- - 0x05 - Write Single Coil
- - 0x06 - Write Single Register
- - 0x0F - Write Multiple Coils
- - 0x10 - Write Multiple Registers
-
-
-
-Notes:
-
-1. When using Modbus IP the transport protocol is TCP (port 502) and the connection is terminated to each transmitted message, that is, is not a keep-alive type connection.
-
-2. The offsets for registers are 0-based. So be careful when setting your supervisory system or your testing software. For example, in ScadaBR (http://www.scadabr.com.br)
-offsets are 0-based, then, a register configured as 100 in the library is set to 100 in ScadaBR. On the other hand, in the CAS Modbus Scanner
-(http://www.chipkin.com/products/software/modbus-software/cas-modbus-scanner/) offsets are 1-based, so a register configured as 100 in library should be 101 in this software.
-
-3. Early in the library Modbus.h file there is an option to limit the operation
-to the functions of Holding Registers, saving space in the program memory.
-Just comment out the following line:
-
-```
-#define USE_HOLDING_REGISTERS_ONLY
-```
-Thus, only the following functions are supported:
-
- - 0x03 - Read Holding Registers
- - 0x06 - Write Single Register
- - 0x10 - Write Multiple Registers
-
-
-
-How to
-======
-
-```
-This README is under development, for now, see the examples of the library.
-```
-
-Contributions
-=============
-http://github.com/andresarmento/modbus-esp8266
-prof (at) andresarmento (dot) com
-
-License
-=======
-The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
-
+# Modbus Library for Arduino
+### ModbusRTU, ModbusTCP and ModbusTCP Security
+
+For detailes on the library usage visit [documentation](documentation) section.
+
+## Features
+
+* Supports all Arduino platforms
+* Operates in any combination of multiple instances of
+ * [Modbus RTU server](examples/RTU)
+ * [Modbus RTU client](examples/RTU)
+ * Modbus TCP server for [ESP8266/ESP32](examples/TCP-ESP) and [Ethernet library](examples/TCP-Ethernet)
+ * Modbus TCP client for [ESP8266/ESP32](examples/TCP-ESP) and [Ethernet library](examples/TCP-Ethernet)
+ * [MODBUS/TCP Security server (ESP8266)](examples/TLS)
+ * [MODBUS/TCP Security client (ESP8266/ESP32)](examples/TLS)
+* Modbus functions supported:
+ * 0x01 - Read Coils
+ * 0x02 - Read Input Status (Read Discrete Inputs)
+ * 0x03 - Read Holding Registers
+ * 0x04 - Read Input Registers
+ * 0x05 - Write Single Coil
+ * 0x06 - Write Single Register
+ * 0x0F - Write Multiple Coils
+ * 0x10 - Write Multiple Registers
+ * 0x14 - Read File Record
+ * 0x15 - Write File Record
+ * 0x16 - Mask Write Register
+ * 0x17 - Read/Write multiple registers
+* [Callbacks](examples/Callback) driven design
+* Real life complex examples:
+ * [ESP8266/ESP32 firmware update over Modbus](examples/Files)
+ * [ModbusRTU to ModbusTCP bridge](examples/Bridge)
+
+## Notes
+
+1. The offsets for registers are 0-based. So be careful when setting your supervisory system or your testing software. For example, in [ScadaBR](http://www.scadabr.com.br) offsets are 0-based, then, a register configured as 100 in the library is set to 100 in ScadaBR. On the other hand, in the [CAS Modbus Scanner](http://www.chipkin.com/products/software/modbus-software/cas-modbus-scanner/) offsets are 1-based, so a register configured as 100 in library should be 101 in this software.
+2. RS-485 transivers based on MAX-485 is working on at least up to 115200. XY-017/XY-485 working only up to 9600 for some reason.
+
+For more information about Modbus see:
+
+* [Modbus (From Wikipedia, the free encyclopedia)](http://pt.wikipedia.org/wiki/Modbus)
+* [MODBUS APPLICATION PROTOCOL SPECIFICATION V1.1b3](https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf)
+* [MODBUS MESSAGING ON TCP/IP IMPLEMENTATION GUIDE V1.0b](http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf)
+* [MODBUS over Serial Line Specification and Implementation Guide V1.02](http://www.modbus.org/docs/Modbus_over_serial_line_V1_02.pdf)
+* [MODBUS/TCP Security Protocol Specification](https://modbus.org/docs/MB-TCP-Security-v21_2018-07-24.pdf)
+
+## Last Changes
+
+```diff
+// 4.1.1
++ Protocol: Fix wrong error code responce on non-existent register
++ ModbusTCP: Fix potential memory leak
++ API: cbEnable/cbDisable functionality extended
++ ESP-IDF: CMakeList.txt added
++ Examples: TCP-to-RTU fixed
+// 4.1.0
++ API: Raw Modbus frame processing functionality
++ ModbusRTU: Precise inter-frame interval control
++ Examples: True ModbusRTU to ModbusTCP Server bridge
++ Examples: ModbusRTU respond to multiple ID from single device
++ ModbusRTU: Add direction control pin for Stream
++ STL: Add Reg count limitation to vector limit of 4000 (for ESP8266 and ESP32)
++ Settings: Added MODBUSIP_CONNECTION_TIMEOUT (ESP32 only)
++ Settings: Set MODBUSIP_MAX_CLIENTS = 8 for ESP32
++ ModbusTCP: Make using DNS names optional feature
++ ModbusRTU: Add separate RE/DE pins control optional feature
++ API: Drop support of Ethernet library v1
++ Examples: Teknic ClearCore ArduinoWrapper examples added
++ Examples: ModbusTCP to ModbusRTU example added
++ ModbusRTU: Flush extra delay optional feature
+// 4.0.0
++ Support of all Arduino boards
++ ModbusTLS: ESP8266 Client/Server and ESP32 Client
++ ModbusTCP: ModbusEthernet - WizNet W5x00, ENC28J60 Ethernet library support
++ 0x14 - Read File Records function
++ 0x15 - Write File Records function
++ Examples: FW update over Modbus fullfunctional example
++ 0x16 - Write Mask Register function+ Test: 0x16
++ 0x17 - Read/Write Registers function
++ ModbusRTU: ESP32 SoftwareSerial support
++ Build with no STL dependency (switchable)
++ API: ModbusIP => ModbusTCP
++ API: Access control callback for individual Modbus function
++ API: Master/Slave => Client/Server according to [PRESS RELEASE](https://modbus.org/docs/Client-ServerPR-07-2020-final.docx.pdf)
++ Lot of code refacting and small fixes
+```
+
+## Roadmap
+
+```diff
+// 4.2.0
+- API: Alternative CRC calulation (reduced memory footprint)
+- ModbusRTU: Static buffer allocation
+- Test: Frame accuracy to specefication
+- Buffer/packet size limitation support
+- Slave/Server: slavePDU use early exit by return where possible
+- Master/Client: Check frame size against header data where possible
+- Master/Client: Additional responce data validation
+- Free global registers and callbacks on remove last Modbus instance
+- Test: push/pull functions
+- ModbusTCP: Refactor connect by dns name (using native implementation for ESP32 etc)
+// 4.3.0
+- ModbusTLS: ESP32 Server
+- Test: TLS ESP32 Server
+- Test: TLS ESP32 Client
+- Examples: TLS Certificate test Role extension and Alt-Name
+- Examples: TLS Add example explanation
+- ModbusTCP: ModbusAsyncTCP
+- API: Extend API to allow custom Modbus commands
+- Examples: Basic file operations
+- Examples: Revising
+```
+## Contributions
+
+https://github.com/emelianov/modbus-esp8266
+
+a.m.emelianov@gmail.com
+
+Original version:
+
+https://github.com/andresarmento/modbus-esp8266
+
+https://github.com/andresarmento/modbus-arduino
+
+prof (at) andresarmento (dot) com
+
+## License
+
+The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
diff --git a/README_pt_BR.md b/README_pt_BR.md
deleted file mode 100644
index 28efc59..0000000
--- a/README_pt_BR.md
+++ /dev/null
@@ -1,83 +0,0 @@
-Biblioteca Modbus para ESP8266
-==============================
-
-Esta biblioteca permite que seu ESP8266 se comunique através do protocolo Modbus.
-O Modbus é um protocolo do tipo mestre-escravo, utilizado em automação industrial,
-podendo ser utilizado em outras áreas, como por exemplo, na automação residencial.
-
-O Modbus geralmente utiliza como meio fÃsico as interfaces seriais RS-232 ou RS-485
-(quando é chamado Modbus Serial) e TCP/IP via Ethernet ou WiFi (Modbus IP).
-
-Na versão atual a biblioteca permite que o arduino opere como escravo, suportando
-o Modbus IP via rede wireless. Para mais informações sobre o Modbus consulte:
-
-http://pt.wikipedia.org/wiki/Modbus
-http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
-http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf
-
-CaracterÃsticas
-===============
-
-
-- Opera como escravo
-- Suporta Modbus IP (TCP não keep-alive)
-- Responde todos os tipos de exceção para as funções suportadas
-- Suporta as seguintes funções Modbus:
-
- - 0x01 - Read Coils
- - 0x02 - Read Input Status (Read Discrete Inputs)
- - 0x03 - Read Holding Registers
- - 0x04 - Read Input Registers
- - 0x05 - Write Single Coil
- - 0x06 - Write Single Register
- - 0x0F - Write Multiple Coils
- - 0x10 - Write Multiple Registers
-
-
-
-Observações:
-
-1. Quando se usa Modbus IP o protocolo de transporte é o TCP (porta 502) e a conexão
-é finalizada a cada mensagem transmitida, ou seja, não é do tipo keep-alive.
-
-2. Os offsets para acesso aos registradores são baseados em 0. Assim, tenha cuidado
-ao configurar seu seu supervisório ou utilitário de teste. Por exempo, no ScadaBR
-(http://www.scadabr.com.br) os offsets são baseados em 0, então, um registrador
-configurado como 100 na biblioteca será configurado como 100 no ScadaBR. Por outro
-lado, no software de teste CAS Modbus Scanner (http://www.chipkin.com/products/software/modbus-software/cas-modbus-scanner/)
-os offsets são baseados em 1, logo, um registrador configurado como 100 na biblioteca
-deverá ser 101 neste software.
-
-3. No inÃcio do arquivo Modbus.h da biblioteca há uma opção para limitar o funcionamento
-da mesma às funções de Holding Registers, salvando espaço na memória de programa.
-Basta retirar o comentário da seguinte linha:
-
-```
-#define USE_HOLDING_REGISTERS_ONLY
-```
-Dessa forma, somente as seguintes funções são suportadas:
-
- - 0x03 - Read Holding Registers
- - 0x06 - Write Single Register
- - 0x10 - Write Multiple Registers
-
-
-Como utilizar
-=============
-
-```
-Este README está em desenvolvimento, por enquanto, consulte os exemplos da biblioteca.
-```
-
-Contribuições
-=============
-http://github.com/andresarmento/modbus-esp8266
-prof (at) andresarmento (dot) com
-
-Licença
-=======
-
-O código neste repositório é licenciado pela BSD New License.
-Veja [LICENSE.txt](LICENSE.txt) para mais informações.
-
-
diff --git a/TODO.txt b/TODO.txt
deleted file mode 100644
index 5cd9a53..0000000
--- a/TODO.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-TODO
-====
-. Modbus for Expressif SDK (in development)
-
-
-
-
-
diff --git a/arduinoIDE/Modbus/Modbus.cpp b/arduinoIDE/Modbus/Modbus.cpp
deleted file mode 100644
index 6e9c7da..0000000
--- a/arduinoIDE/Modbus/Modbus.cpp
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- Modbus.cpp - Source for Modbus Base Library
- Copyright (C) 2014 André Sarmento Barbosa
-*/
-#include "Modbus.h"
-
-Modbus::Modbus() {
- _regs_head = 0;
- _regs_last = 0;
-}
-
-TRegister* Modbus::searchRegister(word address) {
- TRegister *reg = _regs_head;
- //if there is no register configured, bail
- if(reg == 0) return(0);
- //scan through the linked list until the end of the list or the register is found.
- //return the pointer.
- do {
- if (reg->address == address) return(reg);
- reg = reg->next;
- } while(reg);
- return(0);
-}
-
-void Modbus::addReg(word address, word value) {
- TRegister *newreg;
-
- newreg = (TRegister *) malloc(sizeof(TRegister));
- newreg->address = address;
- newreg->value = value;
- newreg->next = 0;
-
- if(_regs_head == 0) {
- _regs_head = newreg;
- _regs_last = _regs_head;
- } else {
- //Assign the last register's next pointer to newreg.
- _regs_last->next = newreg;
- //then make temp the last register in the list.
- _regs_last = newreg;
- }
-}
-
-bool Modbus::Reg(word address, word value) {
- TRegister *reg;
- //search for the register address
- reg = this->searchRegister(address);
- //if found then assign the register value to the new value.
- if (reg) {
- reg->value = value;
- return true;
- } else
- return false;
-}
-
-word Modbus::Reg(word address) {
- TRegister *reg;
- reg = this->searchRegister(address);
- if(reg)
- return(reg->value);
- else
- return(0);
-}
-
-void Modbus::addHreg(word offset, word value) {
- this->addReg(offset + 40001, value);
-}
-
-bool Modbus::Hreg(word offset, word value) {
- return Reg(offset + 40001, value);
-}
-
-word Modbus::Hreg(word offset) {
- return Reg(offset + 40001);
-}
-
-#ifndef USE_HOLDING_REGISTERS_ONLY
- void Modbus::addCoil(word offset, bool value) {
- this->addReg(offset + 1, value?0xFF00:0x0000);
- }
-
- void Modbus::addIsts(word offset, bool value) {
- this->addReg(offset + 10001, value?0xFF00:0x0000);
- }
-
- void Modbus::addIreg(word offset, word value) {
- this->addReg(offset + 30001, value);
- }
-
- bool Modbus::Coil(word offset, bool value) {
- return Reg(offset + 1, value?0xFF00:0x0000);
- }
-
- bool Modbus::Ists(word offset, bool value) {
- return Reg(offset + 10001, value?0xFF00:0x0000);
- }
-
- bool Modbus::Ireg(word offset, word value) {
- return Reg(offset + 30001, value);
- }
-
- bool Modbus::Coil(word offset) {
- if (Reg(offset + 1) == 0xFF00) {
- return true;
- } else return false;
- }
-
- bool Modbus::Ists(word offset) {
- if (Reg(offset + 10001) == 0xFF00) {
- return true;
- } else return false;
- }
-
- word Modbus::Ireg(word offset) {
- return Reg(offset + 30001);
- }
-#endif
-
-
-void Modbus::receivePDU(byte* frame) {
- byte fcode = frame[0];
- word field1 = (word)frame[1] << 8 | (word)frame[2];
- word field2 = (word)frame[3] << 8 | (word)frame[4];
-
- switch (fcode) {
-
- case MB_FC_WRITE_REG:
- //field1 = reg, field2 = value
- this->writeSingleRegister(field1, field2);
- break;
-
- case MB_FC_READ_REGS:
- //field1 = startreg, field2 = numregs
- this->readRegisters(field1, field2);
- break;
-
- case MB_FC_WRITE_REGS:
- //field1 = startreg, field2 = status
- this->writeMultipleRegisters(frame,field1, field2, frame[5]);
- break;
-
- #ifndef USE_HOLDING_REGISTERS_ONLY
- case MB_FC_READ_COILS:
- //field1 = startreg, field2 = numregs
- this->readCoils(field1, field2);
- break;
-
- case MB_FC_READ_INPUT_STAT:
- //field1 = startreg, field2 = numregs
- this->readInputStatus(field1, field2);
- break;
-
- case MB_FC_READ_INPUT_REGS:
- //field1 = startreg, field2 = numregs
- this->readInputRegisters(field1, field2);
- break;
-
- case MB_FC_WRITE_COIL:
- //field1 = reg, field2 = status
- this->writeSingleCoil(field1, field2);
- break;
-
- case MB_FC_WRITE_COILS:
- //field1 = startreg, field2 = numoutputs
- this->writeMultipleCoils(frame,field1, field2, frame[5]);
- break;
-
- #endif
- default:
- this->exceptionResponse(fcode, MB_EX_ILLEGAL_FUNCTION);
- }
-}
-
-void Modbus::exceptionResponse(byte fcode, byte excode) {
- //Clean frame buffer
- free(_frame);
- _len = 2;
- _frame = (byte *) malloc(_len);
- _frame[0] = fcode + 0x80;
- _frame[1] = excode;
-
- _reply = MB_REPLY_NORMAL;
-}
-
-void Modbus::readRegisters(word startreg, word numregs) {
- //Check value (numregs)
- if (numregs < 0x0001 || numregs > 0x007D) {
- this->exceptionResponse(MB_FC_READ_REGS, MB_EX_ILLEGAL_VALUE);
- return;
- }
-
- //Check Address
- //*** See comments on readCoils method.
- if (!this->searchRegister(startreg + 40001)) {
- this->exceptionResponse(MB_FC_READ_REGS, MB_EX_ILLEGAL_ADDRESS);
- return;
- }
-
-
- //Clean frame buffer
- free(_frame);
- _len = 0;
-
- //calculate the query reply message length
- //for each register queried add 2 bytes
- _len = 2 + numregs * 2;
-
- _frame = (byte *) malloc(_len);
- if (!_frame) {
- this->exceptionResponse(MB_FC_READ_REGS, MB_EX_SLAVE_FAILURE);
- return;
- }
-
- _frame[0] = MB_FC_READ_REGS;
- _frame[1] = _len - 2; //byte count
-
- word val;
- word i = 0;
- while(numregs--) {
- //retrieve the value from the register bank for the current register
- val = this->Hreg(startreg + i);
- //write the high byte of the register value
- _frame[2 + i * 2] = val >> 8;
- //write the low byte of the register value
- _frame[3 + i * 2] = val & 0xFF;
- i++;
- }
-
- _reply = MB_REPLY_NORMAL;
-}
-
-void Modbus::writeSingleRegister(word reg, word value) {
- //No necessary verify illegal value (EX_ILLEGAL_VALUE) - because using word (0x0000 - 0x0FFFF)
- //Check Address and execute (reg exists?)
- if (!this->Hreg(reg, value)) {
- this->exceptionResponse(MB_FC_WRITE_REG, MB_EX_ILLEGAL_ADDRESS);
- return;
- }
-
- //Check for failure
- if (this->Hreg(reg) != value) {
- this->exceptionResponse(MB_FC_WRITE_REG, MB_EX_SLAVE_FAILURE);
- return;
- }
-
- _reply = MB_REPLY_ECHO;
-}
-
-void Modbus::writeMultipleRegisters(byte* frame,word startreg, word numoutputs, byte bytecount) {
- //Check value
- if (numoutputs < 0x0001 || numoutputs > 0x007B || bytecount != 2 * numoutputs) {
- this->exceptionResponse(MB_FC_WRITE_REGS, MB_EX_ILLEGAL_VALUE);
- return;
- }
-
- //Check Address (startreg...startreg + numregs)
- for (int k = 0; k < numoutputs; k++) {
- if (!this->searchRegister(startreg + 40001 + k)) {
- this->exceptionResponse(MB_FC_WRITE_REGS, MB_EX_ILLEGAL_ADDRESS);
- return;
- }
- }
-
- //Clean frame buffer
- free(_frame);
- _len = 5;
- _frame = (byte *) malloc(_len);
- if (!_frame) {
- this->exceptionResponse(MB_FC_WRITE_REGS, MB_EX_SLAVE_FAILURE);
- return;
- }
-
- _frame[0] = MB_FC_WRITE_REGS;
- _frame[1] = startreg >> 8;
- _frame[2] = startreg & 0x00FF;
- _frame[3] = numoutputs >> 8;
- _frame[4] = numoutputs & 0x00FF;
-
- word val;
- word i = 0;
- while(numoutputs--) {
- val = (word)frame[6+i*2] << 8 | (word)frame[7+i*2];
- this->Hreg(startreg + i, val);
- i++;
- }
-
- _reply = MB_REPLY_NORMAL;
-}
-
-#ifndef USE_HOLDING_REGISTERS_ONLY
-void Modbus::readCoils(word startreg, word numregs) {
- //Check value (numregs)
- if (numregs < 0x0001 || numregs > 0x07D0) {
- this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_VALUE);
- return;
- }
-
- //Check Address
- //Check only startreg. Is this correct?
- //When I check all registers in range I got errors in ScadaBR
- //I think that ScadaBR request more than one in the single request
- //when you have more then one datapoint configured from same type.
- if (!this->searchRegister(startreg + 1)) {
- this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_ADDRESS);
- return;
- }
-
- //Clean frame buffer
- free(_frame);
- _len = 0;
-
- //Determine the message length = function type, byte count and
- //for each group of 8 registers the message length increases by 1
- _len = 2 + numregs/8;
- if (numregs%8) _len++; //Add 1 to the message length for the partial byte.
-
- _frame = (byte *) malloc(_len);
- if (!_frame) {
- this->exceptionResponse(MB_FC_READ_COILS, MB_EX_SLAVE_FAILURE);
- return;
- }
-
- _frame[0] = MB_FC_READ_COILS;
- _frame[1] = _len - 2; //byte count (_len - function code and byte count)
-
- byte bitn = 0;
- word totregs = numregs;
- word i;
- while (numregs--) {
- i = (totregs - numregs) / 8;
- if (this->Coil(startreg))
- bitSet(_frame[2+i], bitn);
- else
- bitClear(_frame[2+i], bitn);
- //increment the bit index
- bitn++;
- if (bitn == 8) bitn = 0;
- //increment the register
- startreg++;
- }
-
- _reply = MB_REPLY_NORMAL;
-}
-
-void Modbus::readInputStatus(word startreg, word numregs) {
- //Check value (numregs)
- if (numregs < 0x0001 || numregs > 0x07D0) {
- this->exceptionResponse(MB_FC_READ_INPUT_STAT, MB_EX_ILLEGAL_VALUE);
- return;
- }
-
- //Check Address
- //*** See comments on readCoils method.
- if (!this->searchRegister(startreg + 10001)) {
- this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_ADDRESS);
- return;
- }
-
- //Clean frame buffer
- free(_frame);
- _len = 0;
-
- //Determine the message length = function type, byte count and
- //for each group of 8 registers the message length increases by 1
- _len = 2 + numregs/8;
- if (numregs%8) _len++; //Add 1 to the message length for the partial byte.
-
- _frame = (byte *) malloc(_len);
- if (!_frame) {
- this->exceptionResponse(MB_FC_READ_INPUT_STAT, MB_EX_SLAVE_FAILURE);
- return;
- }
-
- _frame[0] = MB_FC_READ_INPUT_STAT;
- _frame[1] = _len - 2;
-
- byte bitn = 0;
- word totregs = numregs;
- word i;
- while (numregs--) {
- i = (totregs - numregs) / 8;
- if (this->Ists(startreg))
- bitSet(_frame[2+i], bitn);
- else
- bitClear(_frame[2+i], bitn);
- //increment the bit index
- bitn++;
- if (bitn == 8) bitn = 0;
- //increment the register
- startreg++;
- }
-
- _reply = MB_REPLY_NORMAL;
-}
-
-void Modbus::readInputRegisters(word startreg, word numregs) {
- //Check value (numregs)
- if (numregs < 0x0001 || numregs > 0x007D) {
- this->exceptionResponse(MB_FC_READ_INPUT_REGS, MB_EX_ILLEGAL_VALUE);
- return;
- }
-
- //Check Address
- //*** See comments on readCoils method.
- if (!this->searchRegister(startreg + 30001)) {
- this->exceptionResponse(MB_FC_READ_COILS, MB_EX_ILLEGAL_ADDRESS);
- return;
- }
-
- //Clean frame buffer
- free(_frame);
- _len = 0;
-
- //calculate the query reply message length
- //for each register queried add 2 bytes
- _len = 2 + numregs * 2;
-
- _frame = (byte *) malloc(_len);
- if (!_frame) {
- this->exceptionResponse(MB_FC_READ_INPUT_REGS, MB_EX_SLAVE_FAILURE);
- return;
- }
-
- _frame[0] = MB_FC_READ_INPUT_REGS;
- _frame[1] = _len - 2;
-
- word val;
- word i = 0;
- while(numregs--) {
- //retrieve the value from the register bank for the current register
- val = this->Ireg(startreg + i);
- //write the high byte of the register value
- _frame[2 + i * 2] = val >> 8;
- //write the low byte of the register value
- _frame[3 + i * 2] = val & 0xFF;
- i++;
- }
-
- _reply = MB_REPLY_NORMAL;
-}
-
-void Modbus::writeSingleCoil(word reg, word status) {
- //Check value (status)
- if (status != 0xFF00 && status != 0x0000) {
- this->exceptionResponse(MB_FC_WRITE_COIL, MB_EX_ILLEGAL_VALUE);
- return;
- }
-
- //Check Address and execute (reg exists?)
- if (!this->Coil(reg, (bool)status)) {
- this->exceptionResponse(MB_FC_WRITE_COIL, MB_EX_ILLEGAL_ADDRESS);
- return;
- }
-
- //Check for failure
- if (this->Coil(reg) != (bool)status) {
- this->exceptionResponse(MB_FC_WRITE_COIL, MB_EX_SLAVE_FAILURE);
- return;
- }
-
- _reply = MB_REPLY_ECHO;
-}
-
-void Modbus::writeMultipleCoils(byte* frame,word startreg, word numoutputs, byte bytecount) {
- //Check value
- word bytecount_calc = numoutputs / 8;
- if (numoutputs%8) bytecount_calc++;
- if (numoutputs < 0x0001 || numoutputs > 0x07B0 || bytecount != bytecount_calc) {
- this->exceptionResponse(MB_FC_WRITE_COILS, MB_EX_ILLEGAL_VALUE);
- return;
- }
-
- //Check Address (startreg...startreg + numregs)
- for (int k = 0; k < numoutputs; k++) {
- if (!this->searchRegister(startreg + 1 + k)) {
- this->exceptionResponse(MB_FC_WRITE_COILS, MB_EX_ILLEGAL_ADDRESS);
- return;
- }
- }
-
- //Clean frame buffer
- free(_frame);
- _len = 5;
- _frame = (byte *) malloc(_len);
- if (!_frame) {
- this->exceptionResponse(MB_FC_WRITE_COILS, MB_EX_SLAVE_FAILURE);
- return;
- }
-
- _frame[0] = MB_FC_WRITE_COILS;
- _frame[1] = startreg >> 8;
- _frame[2] = startreg & 0x00FF;
- _frame[3] = numoutputs >> 8;
- _frame[4] = numoutputs & 0x00FF;
-
- byte bitn = 0;
- word totoutputs = numoutputs;
- word i;
- while (numoutputs--) {
- i = (totoutputs - numoutputs) / 8;
- this->Coil(startreg, bitRead(frame[6+i], bitn));
- //increment the bit index
- bitn++;
- if (bitn == 8) bitn = 0;
- //increment the register
- startreg++;
- }
-
- _reply = MB_REPLY_NORMAL;
-}
-#endif
-
-
-
diff --git a/arduinoIDE/Modbus/Modbus.h b/arduinoIDE/Modbus/Modbus.h
deleted file mode 100644
index a52c875..0000000
--- a/arduinoIDE/Modbus/Modbus.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- Modbus.h - Header for Modbus Base Library
- Copyright (C) 2014 André Sarmento Barbosa
-*/
-#include "Arduino.h"
-
-#ifndef MODBUS_H
-#define MODBUS_H
-
-#define MAX_REGS 32
-#define MAX_FRAME 128
-//#define USE_HOLDING_REGISTERS_ONLY
-
-typedef unsigned int u_int;
-
-//Function Codes
-enum {
- MB_FC_READ_COILS = 0x01, // Read Coils (Output) Status 0xxxx
- MB_FC_READ_INPUT_STAT = 0x02, // Read Input Status (Discrete Inputs) 1xxxx
- MB_FC_READ_REGS = 0x03, // Read Holding Registers 4xxxx
- MB_FC_READ_INPUT_REGS = 0x04, // Read Input Registers 3xxxx
- MB_FC_WRITE_COIL = 0x05, // Write Single Coil (Output) 0xxxx
- MB_FC_WRITE_REG = 0x06, // Preset Single Register 4xxxx
- MB_FC_WRITE_COILS = 0x0F, // Write Multiple Coils (Outputs) 0xxxx
- MB_FC_WRITE_REGS = 0x10, // Write block of contiguous registers 4xxxx
-};
-
-//Exception Codes
-enum {
- MB_EX_ILLEGAL_FUNCTION = 0x01, // Function Code not Supported
- MB_EX_ILLEGAL_ADDRESS = 0x02, // Output Address not exists
- MB_EX_ILLEGAL_VALUE = 0x03, // Output Value not in Range
- MB_EX_SLAVE_FAILURE = 0x04, // Slave Deive Fails to process request
-};
-
-//Reply Types
-enum {
- MB_REPLY_OFF = 0x01,
- MB_REPLY_ECHO = 0x02,
- MB_REPLY_NORMAL = 0x03,
-};
-
-typedef struct TRegister {
- word address;
- word value;
- struct TRegister* next;
-} TRegister;
-
-class Modbus {
- private:
- TRegister *_regs_head;
- TRegister *_regs_last;
-
- void readRegisters(word startreg, word numregs);
- void writeSingleRegister(word reg, word value);
- void writeMultipleRegisters(byte* frame,word startreg, word numoutputs, byte bytecount);
- void exceptionResponse(byte fcode, byte excode);
- #ifndef USE_HOLDING_REGISTERS_ONLY
- void readCoils(word startreg, word numregs);
- void readInputStatus(word startreg, word numregs);
- void readInputRegisters(word startreg, word numregs);
- void writeSingleCoil(word reg, word status);
- void writeMultipleCoils(byte* frame,word startreg, word numoutputs, byte bytecount);
- #endif
-
- TRegister* searchRegister(word addr);
-
- void addReg(word address, word value = 0);
- bool Reg(word address, word value);
- word Reg(word address);
-
- protected:
- byte *_frame;
- byte _len;
- byte _reply;
- void receivePDU(byte* frame);
-
- public:
- Modbus();
-
- void addHreg(word offset, word value = 0);
- bool Hreg(word offset, word value);
- word Hreg(word offset);
-
- #ifndef USE_HOLDING_REGISTERS_ONLY
- void addCoil(word offset, bool value = false);
- void addIsts(word offset, bool value = false);
- void addIreg(word offset, word value = 0);
-
- bool Coil(word offset, bool value);
- bool Ists(word offset, bool value);
- bool Ireg(word offset, word value);
-
- bool Coil(word offset);
- bool Ists(word offset);
- word Ireg(word offset);
- #endif
-};
-
-#endif //MODBUS_H
diff --git a/arduinoIDE/Modbus/keywords.txt b/arduinoIDE/Modbus/keywords.txt
deleted file mode 100644
index 4c183a2..0000000
--- a/arduinoIDE/Modbus/keywords.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-# Syntax Coloring Map For Modbus
-
-# Datatypes (KEYWORD1)
-Modbus KEYWORD1
-u_int KEYWORD1
-TRegister KEYWORD1
-
-# Methods and Functions (KEYWORD2)
-readCoils KEYWORD2
-readInputStatus KEYWORD2
-readRegisters KEYWORD2
-readInputRegisters KEYWORD2
-writeSingleCoil KEYWORD2
-writeSingleRegister KEYWORD2
-writeMultipleCoils KEYWORD2
-writeMultipleRegisters KEYWORD2
-searchRegister KEYWORD2
-receivePDU KEYWORD2
-addReg KEYWORD2
-addCoil KEYWORD2
-addIsts KEYWORD2
-addIreg KEYWORD2
-addHreg KEYWORD2
-Reg KEYWORD2
-Coil KEYWORD2
-Ists KEYWORD2
-Ireg KEYWORD2
-Hreg KEYWORD2
-
-# Constants (LITERAL1)
-MB_FC_READ_COILS LITERAL1
-MB_FC_READ_INPUTS LITERAL1
-MB_FC_READ_REGS LITERAL1
-MB_FC_READ_INPUT_STAT LITERAL1
-MB_FC_WRITE_COIL LITERAL1
-MB_FC_WRITE_REG LITERAL1
-MB_FC_WRITE_COILS LITERAL1
-MB_FC_WRITE_REGS LITERAL1
-MB_REPLY_OFF LITERAL1
-MB_REPLY_ECHO LITERAL1
-MB_REPLY_NORMAL LITERAL1
diff --git a/arduinoIDE/ModbusIP_ESP8266/ModbusIP_ESP8266.cpp b/arduinoIDE/ModbusIP_ESP8266/ModbusIP_ESP8266.cpp
deleted file mode 100644
index 70c103f..0000000
--- a/arduinoIDE/ModbusIP_ESP8266/ModbusIP_ESP8266.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- ModbusIP_ESP8266.cpp - Source for Modbus IP ESP8266 Library
- Copyright (C) 2015 André Sarmento Barbosa
-*/
-#include "ModbusIP_ESP8266.h"
-
-WiFiServer server(MODBUSIP_PORT);
-
-ModbusIP::ModbusIP() {
-
-}
-
-void ModbusIP::config(const char* ssid, const char* password) {
- WiFi.begin(ssid, password);
- server.begin();
-}
-
-void ModbusIP::task() {
- WiFiClient client = server.available();
-
- int raw_len = 0;
-
- if (client) {
- if (client.connected()) {
- for (int x = 0; x < 300; x++) { // Time to have data available
- if (client.available()) {
- while (client.available() > raw_len) { //Computes data length
- raw_len = client.available();
- delay(1);
- }
- break;
- }
- delay(10);
- }
- }
-
- if (raw_len > 7) {
- for (int i=0; i<7; i++) _MBAP[i] = client.read(); //Get MBAP
-
- _len = _MBAP[4] << 8 | _MBAP[5];
- _len--; // Do not count with last byte from MBAP
- if (_MBAP[2] !=0 || _MBAP[3] !=0) return; //Not a MODBUSIP packet
- if (_len > MODBUSIP_MAXFRAME) return; //Length is over MODBUSIP_MAXFRAME
- _frame = (byte*) malloc(_len);
-
- raw_len = raw_len - 7;
- for (int i=0; i< raw_len; i++) _frame[i] = client.read(); //Get Modbus PDU
-
- this->receivePDU(_frame);
- client.flush();
-
- if (_reply != MB_REPLY_OFF) {
- //MBAP
- _MBAP[4] = (_len+1) >> 8; //_len+1 for last byte from MBAP
- _MBAP[5] = (_len+1) & 0x00FF;
-
- size_t send_len = (unsigned int)_len + 7;
- uint8_t sbuf[send_len];
-
- for (int i=0; i<7; i++) sbuf[i] = _MBAP[i];
- for (int i=0; i<_len; i++) sbuf[i+7] = _frame[i];
-
- client.write(sbuf, send_len);
- }
-
- client.stop();
- free(_frame);
- _len = 0;
- }
- }
-}
- /*
- uint8_t buffer[128] = {0};
- uint8_t mux_id;
- uint32_t len = _wifi->recv(&mux_id, buffer, sizeof(buffer), 100);
-
- if (len > 0) {
- int i = 0;
- while (i < 7) {
- _MBAP[i] = buffer[i];
- i++;
- }
-
- _len = _MBAP[4] << 8 | _MBAP[5];
- _len--; // Do not count with last byte from MBAP
- if (_MBAP[2] !=0 || _MBAP[3] !=0) return; //Not a MODBUSIP packet
- if (_len > MODBUSIP_MAXFRAME) return; //Length is over MODBUSIP_MAXFRAME
-
- _frame = (byte*) malloc(_len);
- i = 0;
- while (i < _len){
- _frame[i] = buffer[7+i]; //Forget MBAP and take just modbus pdu
- i++;
- }
-
- this->receivePDU(_frame);
-
- if (_reply != MB_REPLY_OFF) {
- //MBAP
- _MBAP[4] = _len >> 8;
- _MBAP[5] = _len | 0x00FF;
- buffer[4] = _MBAP[4];
- buffer[5] = _MBAP[5];
-
- i = 0;
- while (i < _len){
- buffer[i+7] = _frame[i];
- i++;
- }
- _wifi->send(mux_id, buffer, _len + 7);
- _wifi->releaseTCP(mux_id);
- }
-
- free(_frame);
- _len = 0;
- }
-
-}
-*/
diff --git a/arduinoIDE/ModbusIP_ESP8266/ModbusIP_ESP8266.h b/arduinoIDE/ModbusIP_ESP8266/ModbusIP_ESP8266.h
deleted file mode 100644
index 907b0f2..0000000
--- a/arduinoIDE/ModbusIP_ESP8266/ModbusIP_ESP8266.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- ModbusIP_ESP8266.h - Header for Modbus IP ESP8266 Library
- Copyright (C) 2015 André Sarmento Barbosa
-*/
-#include
-#include
-
-#ifndef MODBUSIP_ESP8266_H
-#define MODBUSIP_ESP8266_H
-
-#define MODBUSIP_PORT 502
-#define MODBUSIP_MAXFRAME 200
-#define MODBUSIP_TIMEOUT 10
-
-class ModbusIP : public Modbus {
- private:
- byte _MBAP[7];
- public:
- ModbusIP();
- void config(const char* ssid, const char* password);
- void task();
-};
-
-#endif //MODBUSIP_ESP8266_H
-
diff --git a/arduinoIDE/ModbusIP_ESP8266/keywords.txt b/arduinoIDE/ModbusIP_ESP8266/keywords.txt
deleted file mode 100644
index f8960e6..0000000
--- a/arduinoIDE/ModbusIP_ESP8266/keywords.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# Syntax Coloring Map For ModbusIP
-
-# Datatypes (KEYWORD1)
-ModbusIP KEYWORD1
-ModbusIP_ESP8266 KEYWORD1
-
-# Methods and Functions (KEYWORD2)
-config KEYWORD2
-task KEYWORD2
-
-# Constants (LITERAL1)
diff --git a/documentation/API.md b/documentation/API.md
new file mode 100644
index 0000000..a69d94b
--- /dev/null
+++ b/documentation/API.md
@@ -0,0 +1,296 @@
+## Common API
+
+```c
+void task();
+```
+
+Processing routine. Should be periodically called form loop().
+
+## Server API
+
+### Add registers
+
+```c
+bool addHreg(uint16_t offset, uint16_t value = 0, uint16_t numregs = 1);
+bool addCoil(uint16_t offset, bool value = false, uint16_t numregs = 1);
+bool addIsts(uint16_t offset, bool value = false, uint16_t numregs = 1);
+bool addIreg(uint16_t offset, uint16_t value = 0, uint16_t numregs = 1);
+```
+
+### Write local reg
+
+```c
+bool Hreg(uint16_t offset, uint16_t value);
+bool Coil(uint16_t offset, bool value);
+bool Ists(uint16_t offset, bool value);
+bool Ireg(uint16_t offset, uint16_t value);
+```
+
+### Read local reg
+
+```c
+uint16_t Hreg(uint16_t offset);
+bool Coil(uint16_t offset);
+bool Ists(uint16_t offset);
+uint16_t Ireg(uint16_t offset);
+```
+
+### Remove reg
+
+```c
+bool removeHreg(uint16_t offset, uint16_t numregs = 1);
+bool removeCoil(uint16_t offset, uint16_t numregs = 1);
+bool removeIsts(uint16_t offset, uint16_t numregs = 1);
+bool removeIreg(uint16_t offset, uint16_t numregs = 1);
+```
+
+### Modbus RTU Specific API
+
+```c
+bool begin(SoftwareSerial* port, int16_t txEnablePin=-1, bool txEnableDirect=true);
+bool begin(HardwareSerial* port, int16_t txEnablePin=-1, bool txEnableDirect=true);
+bool begin(Stream* port);
+```
+
+Assing Serial port. txEnablePin controls transmit enable for MAX-485. Pass txEnableDirect=false if txEnablePin uses inverse logic.
+
+```c
+void setBaudrte(uint32 baud);
+```
+
+Set or override Serial baudrate. Must be called after .begin() for Non-ESP devices.
+
+```c
+void server(uint8_t slaveId);
+void slave(uint8_t slaveId); //Depricated
+```
+
+Select and initialize master or slave mode to work. Switching between modes is not supported. Call is not returning error in this case but behaviour is unpredictible.
+
+```c
+uint8_t server();
+uint8_t slave(); //Depricated
+```
+
+Slave mode: Returns configured slave id. Master mode: Returns slave id for active request or 0 if no request in-progress.
+
+## Modbus TCP Server specific API
+
+```c
+void begin(); // Depricated. Use server() instead.
+void slave(uint16_t port = MODBUSIP_PORT); // For compatibility with ModbusRTU calls. Typically may be replaced with server() call.
+void server(uint16_t port = MODBUSIP_PORT);
+```
+
+## Modbus TCP Client specific
+
+```c
+void master(); // For compatibility with ModbusRTU calls. Typically may be replaced with client() call.
+void client();
+bool connect(IPAddress ip, uint16_t port = MODBUSIP_PORT);
+bool disconnect(IPAddress ip);
+bool isTransaction(uint16_t id);
+bool isConnected(IPAddress ip);
+void dropTransactions();
+```
+
+```c
+void autoConnect(bool enabled);
+```
+
+Select behavior of executing read/write/pull/push. If autoConnect disabled (default) execution returns error if connection to slave is not already established. If autoConnect is enabled trying to establish connection during read/write/pull/push function call. Disabled by default.
+
+## Client API
+
+### Read Coils (0x01) from slave/server
+
+```c
+uint16_t readCoil(uint8_t slaveId, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr);
+uint16_t readCoil(IPAddress ip, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t readCoil(const char* host, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t readCoil(String host, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+
+uint16_t pullCoil(uint8_t slaveId, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr);
+uint16_t pullCoilToIsts(uint8_t slaveId, uint16_t offset, uint16_t startreg, uint16_t numregs = 1, cbTransaction cb = nullptr);
+uint16_t pullCoil(IPAddress ip, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t pullCoilToIsts(IPAddress ip, uint16_t offset, uint16_t startreg, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+```
+
+### Write single Coil to slave/server
+
+```c
+uint16_t writeCoil(IPAddress ip, uint16_t offset, bool value, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t writeCoil(uint8_t slaveId, uint16_t offset, bool value, cbTransaction cb = nullptr);
+uint16_t writeCoil(IPAddress ip, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t writeCoil(uint8_t slaveId, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr);
+
+uint16_t pushCoil(uint8_t slaveId, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr);
+uint16_t pushCoil(IPAddress ip, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+```
+
+### Read Ists (0x02) from slave/server
+
+```c
+uint16_t readIsts(uint8_t slaveId, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr);
+uint16_t readIsts(IPAddress ip, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+
+uint16_t pullIsts(uint8_t slaveId, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr);
+uint16_t pushIstsToCoil(uint8_t slaveId, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr);
+
+uint16_t pullIsts(IPAddress ip, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t pushIstsToCoil(IPAddress ip, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+```
+
+```c
+uint16_t pullHreg(IPAddress ip, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t pullIreg(IPAddress ip, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t pullHregToIreg(IPAddress ip, uint16_t offset, uint16_t startreg, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t pullHreg(uint8_t slaveId, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr);
+uint16_t pullIreg(uint8_t slaveId, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr);
+uint16_t pullHregToIreg(uint8_t slaveId, uint16_t offset, uint16_t startreg, uint16_t numregs = 1, cbTransaction cb = nullptr);
+```
+
+Result is saved to local registers. Method returns corresponding transaction id. [ip/from] or [ip/offset] - slave, [to] or [startreg] - local
+
+### Send [multiple] regs to remote slave/server
+
+```c
+uint16_t pushHreg(IPAddress ip, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t pushIregToHreg(IPAddress ip, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+
+uint16_t pushHreg(uint8_t slaveId, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr);
+uint16_t pushIregToHreg(uint8_t slaveId, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr);
+```
+
+Write Register/Coil or Write Multiple Registers/Coils Modbus function selected automaticly depending on 'numregs' value. [ip/to] - slave, [from] - local
+
+### Write [multiple] values to remote slave/servr reg[s]
+
+```c
+uint16_t writeHreg(IPAddress ip, uint16_t offset, uint16_t value, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t writeHreg(uint8_t slaveId, uint16_t offset, uint16_t value, cbTransaction cb = nullptr);
+```
+
+Writes single value to remote Hreg/Coil.
+
+```c
+uint16_t writeHreg(IPAddress ip, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t writeHreg(uint8_t slaveId, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr);
+```
+
+Writes multiple values from array to remote Coil/Hreg.
+
+### Read values from multiple remote slave/server regs
+
+```c
+uint16_t readHreg(IPAddress ip, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t readIreg(IPAddress ip, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+
+uint16_t readHreg(uint8_t slaveId, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr);
+uint16_t readIreg(uint8_t slaveId, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr);
+```
+
+Reads values from remote Hreg/Coil/Ireg/Ists to array.
+
+## Callbacks API
+
+```c
+void cbEnable(bool state = true);
+void cbDisable();
+```
+
+Callback generation control. Callback generation is enabled by default. *Has no effect on transactions callbacks.*
+
+```c
+void onConnect(cbModbusConnect cb);
+void onDisconnect(cbModbusConnect cb);
+```
+
+*Modbus TCP Server* Assign callback function on new incoming connection event.
+
+```c
+typedef bool (*cbModbusConnect)(IPAddress ip);
+```
+
+*Modbus TCP Sserver* Connect event callback function definition. For onConnect event client's IP address is passed as argument. onDisconnect callback function always gets INADDR_NONE as parameter.
+
+```c
+typedef uint16_t (*cbModbus)(TRegister* reg, uint16_t val);
+```
+
+Get/Set register callback function definition. Pointer to TRegister structure (see source for details) of the register and new value are passed as arguments.
+
+```c
+typedef bool (*cbTransaction)(Modbus::ResultCode event, uint16_t transactionId, void* data);
+```
+
+Transaction end callback function definition. For ModbusIP *data* is currently reserved. For Modbus RTU *transactionId* is also reserved.
+
+```c
+uint32_t eventSource();
+```
+
+Should be called from onGet/onSet or transaction callback function.
+
+*Modbus TCP Client/Server* Returns IP address of remote requesting operation or INADDR_NONE for local. Use IPAddress(eventSource) to operate result as IPAddress type.
+
+*Note:* For transaction callback INADDR_NONE returned in case if transaction is timedout.
+
+*Modbus RTU Master/Slave* Returns slave id.
+
+```c
+bool onSetCoil(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onSetHreg(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onSetIsts(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onSetIreg(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+```
+
+Assign callback function on register modify event. Multiple sequental registers can be affected by specifing `numregs` parameter.
+
+```c
+bool onGetCoil(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onGetHreg(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onGetIsts(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onGetIreg(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+```
+
+Assign callback function on register query event. Multiple sequental registers can be affected by specifing `numregs` parameter.
+
+```c
+bool removeOnGetCoil(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnSetCoil(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnGetHreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnSetHreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnGetIsts(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnSetIsts(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnGetIreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnSetIreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+```
+
+Disconnect specific callback function or all callbacks of the type if cb=NULL.
+
+### Macros
+
+```c
+#define COIL_VAL(v)
+#define COIL_BOOL(v)
+#define ISTS_VAL(v)
+#define ISTS_BOOL(v)
+```
+
+## Contributions
+
+https://github.com/emelianov/modbus-esp8266
+
+a.m.emelianov@gmail.com
+
+Original version:
+
+https://github.com/andresarmento/modbus-esp8266
+https://github.com/andresarmento/modbus-arduino
+
+prof (at) andresarmento (dot) com
+
+## License
+
+The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
diff --git a/documentation/README.md b/documentation/README.md
new file mode 100644
index 0000000..d493b93
--- /dev/null
+++ b/documentation/README.md
@@ -0,0 +1,85 @@
+# FAQ
+
+This library allows your Arduino board to communicate via Modbus protocol. The Modbus is a protocol
+used in industrial automation and also can be used in other areas, such as home automation.
+
+The Modbus generally uses serial RS-485 as physical layer (then called Modbus Serial) and TCP/IP via Ethernet or WiFi (Modbus TCP and Modbus TCP Security).
+
+---
+
+## Where to get documentation for the library?
+
+- [API](API.md)
+- [ModbusTCP](https://github.com/emelianov/modbus-esp8266/tree/master/examples/TCP-ESP#API)
+- [ModbusRTU](https://github.com/emelianov/modbus-esp8266/tree/master/examples/RTU#Modbus-RTU-Specific-API)
+- [Callbacks](https://github.com/emelianov/modbus-esp8266/tree/master/examples/Callback/#Callback-API)
+- [Modbus Security](https://github.com/emelianov/modbus-esp8266/tree/master/examples/TLS)
+- [Modbus File operations](https://github.com/emelianov/modbus-esp8266/tree/master/examples/Files#File-block-API)
+- [Compile time settings](https://github.com/emelianov/modbus-esp8266/tree/master/src/ModbusSettings.h))
+
+---
+
+## Client work cycle diagram
+
+![Client diagram](https://github.com/emelianov/modbus-esp8266/blob/master/resources/client.png)
+
+---
+
+## Server work cycle diagram
+
+![Server diagram](https://github.com/emelianov/modbus-esp8266/blob/master/resources/server.png)
+
+---
+
+## How to send signed value (`int16_t`)?
+
+## How to send `float` or `uint32_t` values?
+
+Modbus standard defines only two types of data: bit value and 16-bit value. All other datatypes should be sent as multiple 16-bit values.
+
+---
+
+## Value not read after `readCoil`/`readHreg`/etc
+
+The library is designed to execute calls async way. That is `readHreg()` function just sends read request to Modbus server device and exits. Responce is processed (as suun as it's arrive) by `task()`. `task()` is also async and exits if data hasn't arrive yet.
+
+---
+
+## When calling `readCoil`/`readHreg`/`writeHreg`/etc multiple times only first of them executed
+
+---
+
+## Transactional callback returns *0xE4* error
+
+It's timeout error. Suggestions below are applicable to persistent errors or frequently errors. Rare timeout errors may be normal in some considerations.
+
+### ModbusRTU
+
+Typically is indicates some kind of wiring or hardware problems.
+
+- Check wiring.
+- Check that baudrate settings are identical for client and server.
+- Try to reduce it to 9600bps.
+- Try to use different power source for Arduino device.
+- Try to replace RS-485 tranceiver.
+- If using Modbus simulator software on PC check the result with alternative software.
+
+### ModbusTCP
+
+It maybe network problems. Use standard procedures as `ping` and firewall settings checks for diagnostics.
+
+---
+
+## If it's possible to create ModbusTCP to ModbusRTU pass through bridge?
+
+Some ideas to implement full functional brodge may be taken from [this code](https://github.com/emelianov/modbus-esp8266/issues/101#issuecomment-755419095).
+Very limited implementation is available in [example](https://github.com/emelianov/modbus-esp8266/examples/bridge).
+
+---
+
+# Modbus Library for Arduino
+### ModbusRTU, ModbusTCP and ModbusTCP Security
+
+(c)2021 [Alexander Emelianov](mailto:a.m.emelianov@gmail.com)
+
+The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
diff --git a/examples/Bridge/MultipleServerID/MultipleServerID.ino b/examples/Bridge/MultipleServerID/MultipleServerID.ino
new file mode 100644
index 0000000..278a99b
--- /dev/null
+++ b/examples/Bridge/MultipleServerID/MultipleServerID.ino
@@ -0,0 +1,51 @@
+/*
+ ModbusRTU ESP32
+ Minimalistic example of server responding for multiple IDs. That is the server looks as multiple devices on bus.
+
+ (c)2022 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+
+ This code is licensed under the BSD New License. See LICENSE.txt for more info.
+*/
+
+#include
+
+#define REGN 10
+#define PRIMARY_ID 1
+#define PRIMERT_VALUE 100
+#define SECONDARY_ID 2
+#define SECONDARY_VALUE 200
+
+ModbusRTU mb;
+
+Modbus::ResultCode cbRtuRaw(uint8_t* data, uint8_t len, void* custom) {
+ auto src = (Modbus::frame_arg_t*) custom; // argument contains some data on incoming packet
+ Serial.printf("RTU Slave: %d, Fn: %02X, len: %d, ", src->slaveId, data[0], len);
+ if (src->slaveId == SECONDARY_ID) // Check if incoming packet is addresses to server with ID
+ return Modbus::EX_FORCE_PROCESS; // Instruct the library to force the packet processing
+ // It's required as otherwise packet will be not processed as not addressed
+ // to the server
+
+ return Modbus::EX_PASSTHROUGH; // or process packet normally
+}
+
+uint16_t cbRead(TRegister* reg, uint16_t val) {
+ if (mb.eventSource() == SECONDARY_ID)
+ return SECONDARY_VALUE;
+ return val;
+}
+
+void setup() {
+ Serial.begin(115200);
+ Serial1.begin(9600);
+ mb.begin(&Serial1);
+ mb.slave(PRIMARY_ID); // Set Modbus to work as a server with ID
+ mb.onRaw(cbRtuRaw); // Assign raw packet callback
+ mb.addHreg(REGN, PRIMARY_VALUE);
+ mb.onGet(cbReadHreg)
+}
+
+void loop() {
+ mb.task();
+ yield();
+}
\ No newline at end of file
diff --git a/examples/Bridge/README.md b/examples/Bridge/README.md
new file mode 100644
index 0000000..598fe48
--- /dev/null
+++ b/examples/Bridge/README.md
@@ -0,0 +1,63 @@
+# Bridge functions
+
+## [Basic](basic/basic.ino)
+
+Basic 'Bridge'. Indeed this sample pulling data from Modbus Server and stores it to local registers. Local registers can be accessed via Modbus Client instance that running aside.
+
+## [ModbusRTU to ModbusTCP bridge](true/true.ino)
+
+Fullfunctional ModbusRTU to ModbusTCP bridge.
+
+## [Multiple Server ID](MultipleServerID/MultipleServerID.ino)
+
+Respond for multiple ModbusRTU IDs from single device
+
+## [ModbusTCP to Modbus RTU Simulator](TCP-to-RTU-Simulator/TCP-to-RTU-Simulator.ino)
+
+Fullfunctional ModbusTCP to ModbusRTU bridge with on-device ModbusRTU simulator
+
+```c
+uint16_t rawRequest(id_ip, uint8_t* data, uint16_t len, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+uint16_t rawResponce(id_ip, uint8_t* data, uint16_t len, uint8_t unit = MODBUSIP_UNIT);
+uint16_t errorResponce(id_ip, Modbus::FunctionCode fn, Modbus::ResultCode excode, uint8_t unit = MODBUSIP_UNIT);
+```
+- `id_ip` SlaveId (`uint8_t`) or server IP address (`IPAddress`)
+- `data` Pointer to data buffer to send
+- `len` Byte count to send
+- `unit` UnitId (ModbusTCP/TLS only)
+- `fn` function code in responce
+- `excode` Exception code in responce
+
+```c
+uint16_t setTransactionId(uint16_t id);
+```
+- `id` Value to replace transaction id sequence (ModbusTCP/TLS only)
+
+```c
+union frame_arg_t {
+struct frame_arg_t {
+ bool to_server; // true if frame is responce for local Modbus server/slave
+ union {
+ // For ModbusRTU
+ uint8_t slaveId;
+ // For ModbusTCP/TLS
+ struct {
+ uint8_t unitId; // UnitId as passed in MBAP header
+ uint32_t ipaddr; // IP address from which frame is received
+ uint16_t transactionId; // TransactionId as passed in MBAP header
+ };
+ };
+};
+typedef std::function cbRaw; // Callback function Type for STL
+typedef ResultCode (*cbRaw)(uint8_t* frame, uint8 len, void* data); // Callback function Type
+bool onRaw(cbRaw cb = nullptr);
+```
+- `frame` Modbus payload frame with stripped MBAP/slaveid and crc
+- `len` frame size in bytes
+- `data` Pointer to frame_arg_t filled with frame header information
+
+*Returns:*
+- If a special error code `Modbus::EX_PASSTHROUGH` returned frame will be processed normally
+- If a special error code `Modbus::EX_FORCE_PROCESS` returned frame will be processed even if addressed to another Modbus unit
+- Any other return code disables normal frame processing. Only transactional callback will be executed (if any and transaction data is correct)
+The callback is executed only on Modbus frame with valid header and CRC.
\ No newline at end of file
diff --git a/examples/Bridge/TCP-to-RTU-Simulator/TCP-to-RTU-Simulator.ino b/examples/Bridge/TCP-to-RTU-Simulator/TCP-to-RTU-Simulator.ino
new file mode 100644
index 0000000..cacf05d
--- /dev/null
+++ b/examples/Bridge/TCP-to-RTU-Simulator/TCP-to-RTU-Simulator.ino
@@ -0,0 +1,138 @@
+/*
+ ModbusRTU ESP8266/ESP32
+ ModbusTCP to ModbusRTU bridge with on-device ModbusRTU simulator
+*/
+#ifdef ESP8266
+ #include
+#else //ESP32
+ #include
+#endif
+#include
+#include
+//#include
+//SoftwareSerial S(13, 15);
+#include
+#define BSIZE 1024
+uint8_t buf1[BSIZE];
+uint8_t buf2[BSIZE];
+StreamBuf S1(buf1, BSIZE);
+StreamBuf S2(buf2, BSIZE);
+DuplexBuf P1(&S1, &S2);
+DuplexBuf P2(&S2, &S1);
+ModbusRTU sym;
+
+int DE_RE = 2;
+
+ModbusRTU rtu;
+ModbusTCP tcp;
+
+IPAddress srcIp;
+
+
+uint16_t transRunning = 0; // Currently executed ModbusTCP transaction
+uint8_t slaveRunning = 0; // Current request slave
+
+bool cbTcpTrans(Modbus::ResultCode event, uint16_t transactionId, void* data) { // Modbus Transaction callback
+ if (event != Modbus::EX_SUCCESS) // If transaction got an error
+ Serial.printf("Modbus result: %02X, Mem: %d\n", event, ESP.getFreeHeap()); // Display Modbus error code (222527)
+ if (event == Modbus::EX_TIMEOUT) { // If Transaction timeout took place
+ tcp.disconnect(tcp.eventSource()); // Close connection
+ transRunning = 0;
+ slaveRunning = 0;
+ }
+ return true;
+}
+
+bool cbRtuTrans(Modbus::ResultCode event, uint16_t transactionId, void* data) {
+ if (event != Modbus::EX_SUCCESS) // If transaction got an error
+ Serial.printf("Modbus result: %02X, Mem: %d\n", event, ESP.getFreeHeap()); // Display Modbus error code (222527)
+ return true;
+}
+
+
+// Callback receives raw data
+Modbus::ResultCode cbTcpRaw(uint8_t* data, uint8_t len, void* custom) {
+ auto src = (Modbus::frame_arg_t*) custom;
+
+ Serial.print("TCP IP in - ");
+ Serial.print(IPAddress(src->ipaddr));
+ Serial.printf(" Fn: %02X, len: %d \n\r", data[0], len);
+
+ if (transRunning) { // Note that we can't process new requests from TCP-side while waiting for responce from RTU-side.
+ tcp.setTransactionId(src->transactionId); // Set transaction id as per incoming request
+ tcp.errorResponce(IPAddress(src->ipaddr), (Modbus::FunctionCode)data[0], Modbus::EX_SLAVE_DEVICE_BUSY);
+ return Modbus::EX_SLAVE_DEVICE_BUSY;
+ }
+
+ rtu.rawRequest(src->unitId, data, len, cbRtuTrans);
+
+ if (!src->unitId) { // If broadcast request (no responce from slave is expected)
+ tcp.setTransactionId(src->transactionId); // Set transaction id as per incoming request
+ tcp.errorResponce(IPAddress(src->ipaddr), (Modbus::FunctionCode)data[0], Modbus::EX_ACKNOWLEDGE);
+
+ transRunning = 0;
+ slaveRunning = 0;
+ return Modbus::EX_ACKNOWLEDGE;
+ }
+
+ srcIp = IPAddress(src->ipaddr);
+
+ slaveRunning = src->unitId;
+
+ transRunning = src->transactionId;
+
+ return Modbus::EX_SUCCESS;
+
+}
+
+
+// Callback receives raw data from ModbusTCP and sends it on behalf of slave (slaveRunning) to master
+Modbus::ResultCode cbRtuRaw(uint8_t* data, uint8_t len, void* custom) {
+ auto src = (Modbus::frame_arg_t*) custom;
+ if (!transRunning) // Unexpected incoming data
+ return Modbus::EX_PASSTHROUGH;
+ tcp.setTransactionId(transRunning); // Set transaction id as per incoming request
+ uint16_t succeed = tcp.rawResponce(srcIp, data, len, slaveRunning);
+ if (!succeed){
+ Serial.print("TCP IP out - failed");
+ }
+ Serial.printf("RTU Slave: %d, Fn: %02X, len: %d, ", src->slaveId, data[0], len);
+ Serial.print("Response TCP IP: ");
+ Serial.println(srcIp);
+
+ transRunning = 0;
+ slaveRunning = 0;
+ return Modbus::EX_PASSTHROUGH;
+}
+
+
+void setup() {
+ Serial.begin(115000);
+ WiFi.begin("E2", "*****");
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(1000);
+ Serial.print(".");
+ }
+ Serial.println("");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ tcp.server(); // Initialize ModbusTCP to pracess as server
+ tcp.onRaw(cbTcpRaw); // Assign raw data processing callback
+
+ //S.begin(19200, SWSERIAL_8E1);
+ //rtu.begin(&S, DE_RE); // Specify RE_DE control pin
+ sym.begin((Stream*)&P2);
+ sym.slave(1);
+ sym.addHreg(1, 100);
+ rtu.begin((Stream*)&P1); // Specify RE_DE control pin
+ rtu.master(); // Initialize ModbusRTU as master
+ rtu.onRaw(cbRtuRaw); // Assign raw data processing callback
+}
+
+void loop() {
+ sym.task();
+ rtu.task();
+ tcp.task();
+ yield();
+}
diff --git a/examples/Bridge/basic/basic.ino b/examples/Bridge/basic/basic.ino
new file mode 100644
index 0000000..acf71c0
--- /dev/null
+++ b/examples/Bridge/basic/basic.ino
@@ -0,0 +1,53 @@
+/*
+ Modbus ESP8266/ESP32
+ Simple ModbesRTU to ModbusIP bridge
+
+ (c)2020 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+#ifdef ESP8266
+ #include
+#else //ESP32
+ #include
+#endif
+#include
+#include
+
+#define TO_REG 10
+#define SLAVE_ID 1
+#define PULL_ID 1
+#define FROM_REG 20
+
+ModbusRTU mb1;
+ModbusIP mb2;
+
+void setup() {
+ Serial.begin(115200);
+ WiFi.begin("SSID", "PASSWORD");
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ Serial1.begin(9600, SERIAL_8N1); // Init Serial on default pins
+ //Serial2.begin(19200, SERIAL_8N1, 19, 18); // Override default pins for ESP32
+ mb1.begin(&Serial1);
+ //mb1.begin(&Serial2, 17); // Specify RE_DE control pin
+ mb1.master();
+ mb2.server();
+ mb2.addHreg(TO_REG);
+}
+
+void loop() {
+ if(!mb1.slave())
+ mb1.pullHreg(PULL_ID, FROM_REG, TO_REG);
+ mb1.task();
+ mb2.task();
+ delay(50);
+}
\ No newline at end of file
diff --git a/examples/Bridge/true/true.ino b/examples/Bridge/true/true.ino
new file mode 100644
index 0000000..b4d3236
--- /dev/null
+++ b/examples/Bridge/true/true.ino
@@ -0,0 +1,121 @@
+/*
+ ModbusRTU ESP8266/ESP32
+ True RTU-TCP bridge example
+
+ (c)2021 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+
+ This code is licensed under the BSD New License. See LICENSE.txt for more info.
+*/
+
+#include
+#include
+#include
+#include
+
+ModbusRTU rtu;
+ModbusTCP tcp;
+
+// ModbusRTU(SlaveID) => ModbusTCP(IP) mapping table
+struct slave_map_t {
+ uint8_t slaveId; // Slave id in incoming request
+ IPAddress ip; // IP address of MosbusTCP Server map request to
+ uint8_t unitId = MODBUSIP_UNIT; // UnitId on target server
+ slave_map_t(uint8_t s, IPAddress i, uint8_t u = MODBUSIP_UNIT) {
+ slaveId = s;
+ ip = i;
+ unitId = u;
+ };
+};
+std::vector mapping; // Slave => IP mappings
+uint16_t transRunning = 0; // Currently executed ModbusTCP transaction
+uint8_t slaveRunning = 0; // Current request slave
+
+bool cbTcpTrans(Modbus::ResultCode event, uint16_t transactionId, void* data) { // Modbus Transaction callback
+ if (event != Modbus::EX_SUCCESS) // If transaction got an error
+ Serial.printf("Modbus result: %02X, Mem: %d\n", event, ESP.getFreeHeap()); // Display Modbus error code (222527)
+ if (event == Modbus::EX_TIMEOUT) { // If Transaction timeout took place
+ tcp.disconnect(tcp.eventSource()); // Close connection
+ }
+ return true;
+}
+
+// Callback receives raw data from ModbusTCP and sends it on behalf of slave (slaveRunning) to master
+Modbus::ResultCode cbTcpRaw(uint8_t* data, uint8_t len, void* custom) {
+ auto src = (Modbus::frame_arg_t*) custom;
+ Serial.print("TCP IP: ");
+ Serial.print(IPAddress(src->ipaddr));
+ Serial.printf(" Fn: %02X, len: %d \n", data[0], len);
+ if (!src->to_server && transRunning == src->transactionId) { // Check if transaction id is match
+ rtu.rawResponce(slaveRunning, data, len);
+ } else
+ return Modbus::EX_PASSTHROUGH; // Allow frame to be processed by generic ModbusTCP routines
+ transRunning = 0;
+ slaveRunning = 0;
+ return Modbus::EX_SUCCESS; // Stop other processing
+}
+
+
+// Callback receives raw data
+Modbus::ResultCode cbRtuRaw(uint8_t* data, uint8_t len, void* custom) {
+ auto src = (Modbus::frame_arg_t*) custom;
+ Serial.printf("RTU Slave: %d, Fn: %02X, len: %d, ", src->slaveId, data[0], len);
+ auto it = std::find_if(mapping.begin(), mapping.end(), [src](slave_map_t& item){return (item.slaveId == src->slaveId);}); // Find mapping
+ if (it != mapping.end()) {
+ if (!tcp.isConnected(it->ip)) { // Check if connection established
+ if (!tcp.connect(it->ip)) { // Try to connect if not
+ Serial.printf("error: Connection timeout\n");
+
+ rtu.errorResponce(it->slaveId, (Modbus::FunctionCode)data[0], Modbus::EX_DEVICE_FAILED_TO_RESPOND); // Send exceprional responce to master if no connection established
+ // Note:
+ // Indeed if both sides is build with the Modbus library _default settings_ RTU master side initiating requests to bridge will respond EX_TIMEOUT not EX_DEVICE_FAILED_TO_RESPOND.
+ // That's because connection timeout and RTU responce timeout are the same (1 second). That case EX_TIMEOUT on reached prior getting EX_DEVICE_FAILED_TO_RESPOND frame.
+ return Modbus::EX_DEVICE_FAILED_TO_RESPOND; // Stop processing the frame
+ }
+ }
+ // Save transaction ans slave it for responce processing
+ transRunning = tcp.rawRequest(it->ip, data, len, cbTcpTrans, it->unitId);
+ if (!transRunning) { // rawRequest returns 0 is unable to send data for some reason
+ tcp.disconnect(it->ip); // Close TCP connection that case
+ Serial.printf("send failed\n");
+ rtu.errorResponce(it->slaveId, (Modbus::FunctionCode)data[0], Modbus::EX_DEVICE_FAILED_TO_RESPOND); // Send exceprional responce to master if request bridging failed
+ return Modbus::EX_DEVICE_FAILED_TO_RESPOND; // Stop processing the frame
+ }
+ Serial.printf("transaction: %d\n", transRunning);
+ slaveRunning = it->slaveId;
+ return Modbus::EX_SUCCESS; // Stop procesing the frame
+ }
+ Serial.printf("ignored: No mapping\n");
+ return Modbus::EX_PASSTHROUGH; // Process by generic ModbusRTU routines if no mapping found
+}
+
+
+void setup() {
+ Serial.begin(115000);
+ WiFi.begin("SSID", "PASSWORD");
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(1000);
+ Serial.print(".");
+ }
+ Serial.println("");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ tcp.client(); // Initialize ModbusTCP to pracess as client
+ tcp.onRaw(cbTcpRaw); // Assign raw data processing callback
+
+ Serial1.begin(9600, SERIAL_8N1, 18, 19);
+ rtu.begin(&Serial1);
+ rtu.slave(3); // Initialize ModbusRTU as slave
+ rtu.onRaw(cbRtuRaw); // Assign raw data processing callback
+
+// Assign mappings
+ mapping.push_back({1, IPAddress(192,168,30,18)});
+ mapping.push_back({2, IPAddress(192,168,30,19)});
+}
+
+void loop() {
+ rtu.task();
+ tcp.task();
+ yield();
+}
\ No newline at end of file
diff --git a/examples/Callback/IP-server-MultipleHRegDebug/IPserver-MultipleHRegDebug.ino b/examples/Callback/IP-server-MultipleHRegDebug/IPserver-MultipleHRegDebug.ino
new file mode 100644
index 0000000..7f717b1
--- /dev/null
+++ b/examples/Callback/IP-server-MultipleHRegDebug/IPserver-MultipleHRegDebug.ino
@@ -0,0 +1,79 @@
+/*
+ Modbus-Arduino Example - Hreg multiple Holding register debug code (Modbus IP ESP8266/ESP32)
+
+ Original library
+ Copyright by André Sarmento Barbosa
+ http://github.com/andresarmento/modbus-arduino
+
+ Current version
+ (c)2018 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+
+#ifdef ESP8266
+ #include
+#else //ESP32
+ #include
+#endif
+#include
+
+#define LEN 10
+
+//ModbusIP object
+ModbusIP mb;
+
+// Callback function to read corresponding DI
+uint16_t cbRead(TRegister* reg, uint16_t val) {
+ Serial.print("Read. Reg RAW#: ");
+ Serial.print(reg->address.address);
+ Serial.print(" Old: ");
+ Serial.print(reg->value);
+ Serial.print(" New: ");
+ Serial.println(val);
+ return val;
+}
+// Callback function to write-protect DI
+uint16_t cbWrite(TRegister* reg, uint16_t val) {
+ Serial.print("Write. Reg RAW#: ");
+ Serial.print(reg->address.address);
+ Serial.print(" Old: ");
+ Serial.print(reg->value);
+ Serial.print(" New: ");
+ Serial.println(val);
+ return val;
+}
+
+// Callback function for client connect. Returns true to allow connection.
+bool cbConn(IPAddress ip) {
+ Serial.println(ip);
+ return true;
+}
+
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("ssid", "pass");
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.print("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ mb.onConnect(cbConn); // Add callback on connection event
+ mb.server();
+
+ if (!mb.addHreg(0, 0xF0F0, LEN)) Serial.println("Error"); // Add Hregs
+ mb.onGetHreg(0, cbRead, LEN); // Add callback on Coils value get
+ mb.onSetHreg(0, cbWrite, LEN);
+}
+
+void loop() {
+ //Call once inside loop() - all magic here
+ mb.task();
+ delay(10);
+}
diff --git a/examples/Callback/README.md b/examples/Callback/README.md
new file mode 100644
index 0000000..6a7e0d9
--- /dev/null
+++ b/examples/Callback/README.md
@@ -0,0 +1,124 @@
+# Callbacks
+
+## [Register read/write callback](onSet/onSet.ino)
+
+## [Use one callback function for multiple registers](onGetShared/onGetShared.ino)
+
+## [Incoming request callback (applicable to server/slave)](Request/Request.ino)
+
+## [Modbus TCP/TLS Incoming connection callback](onSet/onSet.ino)
+
+## [Modbus TCP/TLS Transaction result](Transactional/Transactional.ino)
+
+### Callback API
+
+```c
+bool onSetCoil(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onSetHreg(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onSetIsts(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onSetIreg(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+```
+
+- `address` Address of register assign callback on
+- `cb` Callback function
+- `numregs` Count of sequental segisters assign this callback to
+
+Assign callback function on register modify event. Multiple sequental registers can be affected by specifing `numregs` parameter.
+
+
+```c
+bool onGetCoil(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onGetHreg(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onGetIsts(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool onGetIreg(uint16_t address, cbModbus cb = nullptr, uint16_t numregs = 1);
+```
+
+- `address` Address of register assign callback on
+- `cb` Callback function
+- `numregs` Count of sequental segisters assign this callback to
+
+Assign callback function on register query event. Multiple sequental registers can be affected by specifing `numregs` parameter.
+
+```c
+bool removeOnGetCoil(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnSetCoil(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnGetHreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnSetHreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnGetIsts(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnSetIsts(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnGetIreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+bool removeOnSetIreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+```
+
+- `address` Address of register assign callback on
+- `cb` Callback function or NULL to remove all the callbacks.
+- `numregs` Count of sequental segisters remove this callback to.
+
+Disconnect specific callback function or all callbacks of the type if cb=NULL.
+
+```c
+typedef Modbus::ResultCode (*cbRequest)(Modbus::FunctionCode fc, const Modbus::RequestData data);
+bool onRequest(cbRequest cb = _onRequestDefault);
+bool onRequestSuccess(cbRequest cb = _onRequestDefault);
+
+union Modbus::RequestData {
+ struct {
+ TAddress reg;
+ uint16_t regCount;
+ };
+ struct {
+ TAddress regRead;
+ uint16_t regReadCount;
+ TAddress regWrite;
+ uint16_t regWriteCount;
+ };
+ struct {
+ TAddress regMask;
+ uint16_t andMask;
+ uint16_t orMask;
+ };
+};
+```
+
+Callback function receives Modbus function code, structure `Modbus::RequestData` containing register type and offset (`TAddress` structure) and count of registers requested. The function should return [result code](#Result codes *Modbus::ResultCode*) `Modbus::EX_SUCCESS` to allow request processing or Modbus error code to block processing. This code will be returned to client/master.
+
+```c
+void onConnect(cbModbusConnect cb);
+void onDisonnect(cbModbusConnect cb);
+```
+
+Assign callback function on incoming connection event.
+
+```c
+typedef bool (*cbModbusConnect)(IPAddress ip);
+```
+
+- `ip` Client's address of incomig connection source. `INADDR_NONE` for on disconnect callback.
+
+## Result codes *Modbus::ResultCode*
+
+|Value|Hex|Definition|Decription|
+|---|---|---|---|
+|Modbus::EX_SUCCESS|0x00|Custom|No error|
+|Modbus::EX_ILLEGAL_FUNCTION|0x01|Modbus|Function Code not Supported|
+|Modbus::EX_ILLEGAL_ADDRESS|0x02|Modbus|Output Address not exists|
+|Modbus::EX_ILLEGAL_VALUE|0x03|Modbus|Output Value not in Range|
+|Modbus::EX_SLAVE_FAILURE|0x04|Modbus|Slave or Master Device Fails to process request
+|Modbus::EX_ACKNOWLEDGE|0x05|Modbus|Not used|
+|Modbus::EX_SLAVE_DEVICE_BUSY|0x06|Modbus|Not used|
+|Modbus::EX_MEMORY_PARITY_ERROR|0x08|Modbus|Not used|
+|Modbus::EX_PATH_UNAVAILABLE|0x0A|Modbus|Not used|
+|Modbus::EX_DEVICE_FAILED_TO_RESPOND|0x0B|Modbus|Not used|
+|Modbus::EX_GENERAL_FAILURE|0xE1|Custom|Unexpected master error|
+|Modbus::EX_DATA_MISMACH|0xE2|Custom|Inpud data size mismach|
+|Modbus::EX_UNEXPECTED_RESPONSE|0xE3|Custom|Returned result doesn't mach transaction|
+|Modbus::EX_TIMEOUT|0xE4|Custom|Operation not finished within reasonable time|
+|Modbus::EX_CONNECTION_LOST|0xE5|Custom|Connection with device lost|
+|Modbus::EX_CANCEL|0xE6|Custom|Transaction/request canceled|
+
+# Modbus Library for Arduino
+### ModbusRTU, ModbusTCP and ModbusTCP Security
+
+(c)2020 [Alexander Emelianov](mailto:a.m.emelianov@gmail.com)
+
+The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
\ No newline at end of file
diff --git a/examples/Callback/Request/Request.ino b/examples/Callback/Request/Request.ino
new file mode 100644
index 0000000..fea3dea
--- /dev/null
+++ b/examples/Callback/Request/Request.ino
@@ -0,0 +1,66 @@
+/*
+ Modbus-Arduino Example - Using request processing callback to control access to Hreg write operations
+
+ (c)2020 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+
+#ifdef ESP8266
+ #include
+#else //ESP32
+ #include
+#endif
+#include
+
+const uint16_t RO_FLAG = 100; // Coil register to allow/disallow Hregs write
+const uint16_t RW_HREG = 200; // Sample Hreg
+
+//ModbusIP object
+ModbusTCP mb;
+
+Modbus::ResultCode cbPreRequest(Modbus::FunctionCode fc, const Modbus::RequestData data) {
+ Serial.printf("PRE Function: %02X\n", fc);
+ if ((fc == Modbus::FC_WRITE_REG || fc == Modbus::FC_WRITE_REGS) && mb.Coil(RO_FLAG))
+ return Modbus::EX_ILLEGAL_FUNCTION;
+ return Modbus::EX_SUCCESS;
+}
+
+Modbus::ResultCode cbPostRequest(Modbus::FunctionCode fc, const Modbus::RequestData data) {
+ Serial.printf("POST Function: %02X\n", fc);
+ return Modbus::EX_SUCCESS;
+}
+
+// Callback function for client connect. Returns true to allow connection.
+bool cbConn(IPAddress ip) {
+ Serial.println(ip);
+ return true;
+}
+
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("SID", "PASSWORD");
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.print("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ mb.onConnect(cbConn); // Add callback on connection event
+ mb.onRequest(cbPreRequest);
+ mb.onRequestSuccess(cbPostRequest);
+ mb.server();
+
+ mb.addCoil(RO_FLAG);
+ mb.addHreg(RW_HREG, 100);
+}
+
+void loop() {
+ mb.task();
+ delay(10);
+}
\ No newline at end of file
diff --git a/examples/Callback/Transactional/Transactional.ino b/examples/Callback/Transactional/Transactional.ino
new file mode 100644
index 0000000..68fd532
--- /dev/null
+++ b/examples/Callback/Transactional/Transactional.ino
@@ -0,0 +1,61 @@
+/*
+ Modbus-Arduino Example - Modbus IP Client (ESP8266/ESP32)
+ Write multiple coils to Slave device
+
+ (c)2019 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+
+#ifdef ESP8266
+ #include
+#else
+ #include
+#endif
+#include
+
+const int REG = 100; // Modbus Coils Offset
+const int COUNT = 5; // Count of Coils
+IPAddress remote(192, 168, 20, 102); // Address of Modbus Slave device
+
+ModbusIP mb; // ModbusIP object
+
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin();
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ mb.client();
+}
+
+bool cb(Modbus::ResultCode event, uint16_t transactionId, void* data) { // Modbus Transaction callback
+ if (event != Modbus::EX_SUCCESS) // If transaction got an error
+ Serial.printf("Modbus result: %02X\n", event); // Display Modbus error code
+ if (event == Modbus::EX_TIMEOUT) { // If Transaction timeout took place
+ mb.disconnect(remote); // Close connection to slave and
+ mb.dropTransactions(); // Cancel all waiting transactions
+ }
+ return true;
+}
+
+bool res[COUNT] = {false, true, false, true, true};
+
+void loop() {
+ if (!mb.isConnected(remote)) { // Check if connection to Modbus Slave is established
+ mb.connect(remote); // Try to connect if no connection
+ Serial.print(".");
+ }
+ if (!mb.writeCoil(remote, REG, res, COUNT, cb)) // Try to Write array of COUNT of Coils to Modbus Slave
+ Serial.print("#");
+ mb.task(); // Modbus task
+ delay(50); // Pushing interval
+}
\ No newline at end of file
diff --git a/examples/Callback/onGetShared/onGetShared.ino b/examples/Callback/onGetShared/onGetShared.ino
new file mode 100644
index 0000000..554e114
--- /dev/null
+++ b/examples/Callback/onGetShared/onGetShared.ino
@@ -0,0 +1,81 @@
+/*
+ Modbus-Arduino Example - Publish multiple DI as coils (Modbus IP ESP8266/ESP32)
+
+ Original library
+ Copyright by André Sarmento Barbosa
+ http://github.com/andresarmento/modbus-arduino
+
+ Current version
+ (c)2018 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+
+#ifdef ESP8266
+ #include
+#else //ESP32
+ #include
+#endif
+#include
+
+//Used Pins
+#ifdef ESP8266
+ uint8_t pinList[] = {D0, D1, D2, D3, D4, D5, D6, D7, D8};
+#else //ESP32
+ uint8_t pinList[] = {12, 13, 14, 14, 16, 17, 18, 21, 22, 23};
+#endif
+#define LEN sizeof(pinList)/sizeof(uint8_t)
+#define COIL_BASE 0
+//ModbusIP object
+ModbusIP mb;
+
+// Callback function to read corresponding DI
+uint16_t cbRead(TRegister* reg, uint16_t val) {
+ // Checking value of register address which callback is called on.
+ // See Modbus.h for TRegister and TAddress definition
+ if(reg->address.address < COIL_BASE)
+ return 0;
+ uint8_t offset = reg->address.address - COIL_BASE;
+ if(offset >= LEN)
+ return 0;
+ return COIL_VAL(digitalRead(pinList[offset]));
+}
+// Callback function to write-protect DI
+uint16_t cbWrite(TRegister* reg, uint16_t val) {
+ return reg->value;
+}
+
+// Callback function for client connect. Returns true to allow connection.
+bool cbConn(IPAddress ip) {
+ Serial.println(ip);
+ return true;
+}
+
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("ssid", "password");
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.print("IP address: ");
+ Serial.println(WiFi.localIP());
+ for (uint8_t i = 0; i < LEN; i++)
+ pinMode(pinList[i], INPUT);
+ mb.onConnect(cbConn); // Add callback on connection event
+ mb.server();
+
+ mb.addCoil(COIL_BASE, COIL_VAL(false), LEN); // Add Coils.
+ mb.onGetCoil(COIL_BASE, cbRead, LEN); // Add single callback for multiple Coils. It will be called for each of these coils value get
+ mb.onSetCoil(COIL_BASE, cbWrite, LEN); // The same as above just for set value
+}
+
+void loop() {
+ //Call once inside loop() - all magic here
+ mb.task();
+ delay(10);
+}
\ No newline at end of file
diff --git a/examples/Callback/onSet/onSet.ino b/examples/Callback/onSet/onSet.ino
new file mode 100644
index 0000000..d604f49
--- /dev/null
+++ b/examples/Callback/onSet/onSet.ino
@@ -0,0 +1,71 @@
+/*
+ Modbus-Arduino Example - Test Led using callback (Modbus IP ESP8266/ESP32)
+ Control a Led on D4 pin using Write Single Coil Modbus Function
+ Original library
+ Copyright by André Sarmento Barbosa
+ http://github.com/andresarmento/modbus-arduino
+
+ Current version
+ (c)2017 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+
+#ifdef ESP8266
+ #include
+#else //ESP32
+ #include
+#endif
+#include
+
+//Modbus Registers Offsets
+const int LED_COIL = 100;
+//Used Pins
+#ifdef ESP8266
+ const int ledPin = D4; // Builtin ESP8266 LED
+#else
+ const int ledPin = TX; // ESP32 TX LED
+#endif
+//ModbusIP object
+ModbusIP mb;
+
+// Callback function for write (set) Coil. Returns value to store.
+uint16_t cbLed(TRegister* reg, uint16_t val) {
+ //Attach ledPin to LED_COIL register
+ digitalWrite(ledPin, COIL_BOOL(val));
+ return val;
+}
+
+// Callback function for client connect. Returns true to allow connection.
+bool cbConn(IPAddress ip) {
+ Serial.println(ip);
+ return true;
+}
+
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("SID", "PASSWORD");
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.print("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ mb.onConnect(cbConn); // Add callback on connection event
+ mb.server();
+
+ pinMode(ledPin, OUTPUT);
+ mb.addCoil(LED_COIL); // Add Coil. The same as mb.addCoil(COIL_BASE, false, LEN)
+ mb.onSetCoil(LED_COIL, cbLed); // Add callback on Coil LED_COIL value set
+}
+
+void loop() {
+ //Call once inside loop() - all magic here
+ mb.task();
+ delay(10);
+}
diff --git a/examples/ClearCore/README.md b/examples/ClearCore/README.md
new file mode 100644
index 0000000..f2a5f21
--- /dev/null
+++ b/examples/ClearCore/README.md
@@ -0,0 +1,3 @@
+# Examples for [Teknic ClearCore ArduinoWrapper](https://github.com/Teknic-Inc/ClearCore-Arduino-wrapper)
+
+Not tested on real hardware just based on documentation.
\ No newline at end of file
diff --git a/examples/ClearCore/TCP-Client/TCP-Client.ino b/examples/ClearCore/TCP-Client/TCP-Client.ino
new file mode 100644
index 0000000..a93862f
--- /dev/null
+++ b/examples/ClearCore/TCP-Client/TCP-Client.ino
@@ -0,0 +1,72 @@
+/*
+ ModbusTCP Client for ClearCode Arduino wrapper
+
+ (c)2021 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+
+ This code is licensed under the BSD New License. See LICENSE.txt for more info.
+*/
+
+#include // Ethernet library v2 is required
+
+#include
+#include
+
+class ModbusEthernet : public ModbusAPI> {};
+
+const uint16_t REG = 512; // Modbus Hreg Offset
+IPAddress remote(192, 168, 30, 12); // Address of Modbus Slave device
+const int32_t showDelay = 5000; // Show result every n'th mellisecond
+
+bool usingDhcp = true;
+byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE }; // MAC address for your controller
+IPAddress ip(192, 168, 30, 178); // The IP address will be dependent on your local network
+ModbusEthernet mb; // Declare ModbusTCP instance
+
+void setup() {
+ Serial.begin(9600);
+ uint32_t timeout = 5000;
+ uint32_t startTime = millis();
+ while (!Serial && millis() - startTime < timeout)
+ continue;
+
+ // Get the Ethernet module up and running.
+ if (usingDhcp) {
+ int dhcpSuccess = Ethernet.begin(mac);
+ if (dhcpSuccess)
+ Serial.println("DHCP configuration was successful.");
+ else {
+ Serial.println("DHCP configuration was unsuccessful!");
+ Serial.println("Try again using a manual configuration...");
+ while (true)
+ continue;
+ }
+ }
+ else {
+ Ethernet.begin(mac, ip);
+ }
+
+ // Make sure the physical link is up before continuing.
+ while (Ethernet.linkStatus() == LinkOFF) {
+ Serial.println("The Ethernet cable is unplugged...");
+ delay(1000);
+ }
+ mb.client(); // Act as Modbus TCP server
+}
+
+uint16_t res = 0;
+uint32_t showLast = 0;
+
+void loop() {
+if (mb.isConnected(remote)) { // Check if connection to Modbus Slave is established
+ mb.readHreg(remote, REG, &res); // Initiate Read Hreg from Modbus Slave
+ } else {
+ mb.connect(remote); // Try to connect if not connected
+ }
+ delay(100); // Pulling interval
+ mb.task(); // Common local Modbus task
+ if (millis() - showLast > showDelay) { // Display register value every 5 seconds (with default settings)
+ showLast = millis();
+ Serial.println(res);
+ }
+}
\ No newline at end of file
diff --git a/examples/ClearCore/TCP-Server/TCP-Server.ino b/examples/ClearCore/TCP-Server/TCP-Server.ino
new file mode 100644
index 0000000..abf844c
--- /dev/null
+++ b/examples/ClearCore/TCP-Server/TCP-Server.ino
@@ -0,0 +1,67 @@
+/*
+ ModbusTCP Server for ClearCore Arduino wrapper
+
+ (c)2021 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+
+ This code is licensed under the BSD New License. See LICENSE.txt for more info.
+*/
+
+#include // Ethernet library v2 is required
+
+#include
+#include
+
+class ModbusEthernet : public ModbusAPI> {};
+
+const uint16_t REG = 512; // Modbus Hreg Offset
+const int32_t showDelay = 5000; // Show result every n'th mellisecond
+
+bool usingDhcp = true;
+byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE }; // MAC address for your controller
+IPAddress ip(192, 168, 30, 178); // The IP address will be dependent on your local network
+ModbusEthernet mb; // Declare ModbusTCP instance
+
+// Callback function for client connect. Returns true to allow connection.
+bool cbConn(IPAddress ip) {
+ Serial.println(ip);
+ return true;
+}
+
+void setup() {
+ Serial.begin(9600);
+ uint32_t timeout = 5000;
+ uint32_t startTime = millis();
+ while (!Serial && millis() - startTime < timeout)
+ continue;
+
+ // Get the Ethernet module up and running.
+ if (usingDhcp) {
+ int dhcpSuccess = Ethernet.begin(mac);
+ if (dhcpSuccess)
+ Serial.println("DHCP configuration was successful.");
+ else {
+ Serial.println("DHCP configuration was unsuccessful!");
+ Serial.println("Try again using a manual configuration...");
+ while (true)
+ continue;
+ }
+ }
+ else {
+ Ethernet.begin(mac, ip);
+ }
+
+ // Make sure the physical link is up before continuing.
+ while (Ethernet.linkStatus() == LinkOFF) {
+ Serial.println("The Ethernet cable is unplugged...");
+ delay(1000);
+ }
+ mb.server(); // Act as Modbus TCP server
+ mb.onConnect(cbConn);
+ mb.addHreg(100); // Expose Holding Register #100
+}
+
+void loop() {
+ mb.task(); // Common local Modbus task
+ delay(10);
+}
\ No newline at end of file
diff --git a/examples/Files/FW-Update-Source/FW-Update-Source.ino b/examples/Files/FW-Update-Source/FW-Update-Source.ino
new file mode 100644
index 0000000..5be2231
--- /dev/null
+++ b/examples/Files/FW-Update-Source/FW-Update-Source.ino
@@ -0,0 +1,167 @@
+/*
+ Modbus Library for Arduino Example - Modbus RTU Firmware Update - ESP8266/ESP32
+ Update main node to update from.
+ Hosts simple web-server to upload firmware file and writes it to update target.
+
+ (c)2021 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+#include
+ModbusRTU rtu;
+
+#if defined(ESP8266)
+ #include
+ #include
+#else
+ #include
+#include
+#include
+#endif
+WebServer web(80);
+
+#if defined(ESP8266)
+ #include
+ // SoftwareSerial S(D1, D2, false, 256);
+
+ // receivePin, transmitPin, inverse_logic, bufSize, isrBufSize
+ // connect RX to D2 (GPIO4, Arduino pin 4), TX to D1 (GPIO5, Arduino pin 4)
+ SoftwareSerial S(4, 5);
+#endif
+
+#define UPDATE_ENABLE 301
+#define UPDATE_FILE 301
+#define SLAVE_ID 1
+#define BLOCK_SIZE 64
+
+uint32_t written = 0;
+bool updating = false;
+Modbus::ResultCode result = Modbus::EX_GENERAL_FAILURE;
+bool cb(Modbus::ResultCode event, uint16_t transactionId, void* data) { // Modbus Transaction callback
+ if (event != Modbus::EX_SUCCESS) // If transaction got an error
+ Serial.printf("Modbus result: %02X\n", event); // Display Modbus error code
+ result = event;
+ return true;
+}
+
+void handlePage() {
+ String output = F(R"EOF(
+
+
+
+)EOF");
+ web.sendHeader("Connection", "close");
+ web.sendHeader("Cache-Control", "no-store, must-revalidate");
+ web.sendHeader("Access-Control-Allow-Origin", "*");
+ web.send(200, "text/html; charset=utf-8", output);
+}
+
+void updateHandle() {
+ web.sendHeader("Connection", "close");
+ web.sendHeader("Refresh", "10; url=/");
+ web.send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
+}
+
+void updateUploadHandle() {
+ uint8_t* data = nullptr;
+ uint16_t remaining;
+ HTTPUpload& upload = web.upload();
+ switch (upload.status) {
+ case UPLOAD_FILE_START:
+ result = Modbus::EX_GENERAL_FAILURE;
+ rtu.writeCoil(SLAVE_ID, UPDATE_ENABLE, true, cb);
+ while (rtu.server()) {
+ rtu.task();
+ yield();
+ }
+ updating = (result == Modbus::EX_SUCCESS);
+ written = 0;
+ Serial.print("O");
+ break;
+ case UPLOAD_FILE_WRITE:
+ if (!updating)
+ break;
+ Serial.print("o");
+ data = upload.buf;
+ remaining = upload.currentSize / 2;
+ while (remaining) {
+ uint16_t amount = (remaining > BLOCK_SIZE)?BLOCK_SIZE:remaining;
+ result = Modbus::EX_GENERAL_FAILURE;
+ if (!rtu.writeFileRec(SLAVE_ID, UPDATE_FILE, 0, amount, data, cb)) {
+ updating = false;
+ Serial.println("X:send");
+ break;
+ }
+ while (rtu.server()) {
+ rtu.task();
+ yield();
+ }
+ remaining -= amount;
+ data += amount * 2;
+ written += amount;
+ if (result != Modbus::EX_SUCCESS) {
+ updating = false;
+ Serial.println("X");
+ break;
+ }
+ Serial.print(".");
+ }
+ break;
+ case UPLOAD_FILE_END:
+ if (!updating)
+ break;
+ rtu.writeCoil(SLAVE_ID, UPDATE_ENABLE, false);
+ while (rtu.server()) {
+ rtu.task();
+ yield();
+ }
+ updating = false;
+ Serial.println("!");
+ Serial.print("Written: ");
+ Serial.println(written * 2);
+ break;
+ default:
+ if (updating) {
+ rtu.writeCoil(SLAVE_ID, UPDATE_ENABLE, false);
+ while (rtu.server()) {
+ rtu.task();
+ yield();
+ }
+ updating = false;
+ }
+ Serial.print("X");
+ }
+}
+
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("SSID", "PASSWORD");
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(1000);
+ Serial.print(".");
+ }
+ Serial.println("");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+
+#if defined(ESP8266)
+ S.begin(19200, SWSERIAL_8N1);
+ rtu.begin(&S);
+#else
+ Serial1.begin(19200, SERIAL_8N1, 18, 19);
+ rtu.begin(&Serial1);
+ rtu.client();
+ #endif
+ web.on("/update", HTTP_POST, updateHandle, updateUploadHandle);
+ web.on("/", HTTP_GET, handlePage);
+ web.begin();
+}
+
+void loop() {
+ web.handleClient();
+ rtu.task();
+ yield();
+}
\ No newline at end of file
diff --git a/examples/Files/FW-Update-Target/FW-Update-Target.ino b/examples/Files/FW-Update-Target/FW-Update-Target.ino
new file mode 100644
index 0000000..5864929
--- /dev/null
+++ b/examples/Files/FW-Update-Target/FW-Update-Target.ino
@@ -0,0 +1,100 @@
+/*
+ Modbus Library for Arduino Example - Modbus RTU Firmware Update - ESP8266/ESP32
+ Update target node to update.
+ Receives firmware to upload and flashes it.
+
+ (c)2021 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+#include
+ModbusRTU rtu;
+#if defined (ESP32)
+ #include
+ #define ESP32_SKETCH_SIZE 1310720
+#endif
+#if defined(ESP8266)
+ #include
+ // SoftwareSerial S(D1, D2, false, 256);
+
+ // receivePin, transmitPin, inverse_logic, bufSize, isrBufSize
+ // connect RX to D2 (GPIO4, Arduino pin 4), TX to D1 (GPIO5, Arduino pin 4)
+ SoftwareSerial S(4, 5);
+#endif
+
+#define UPDATE_ENABLE 301
+#define UPDATE_FILE 301
+#define SLAVE_ID 1
+uint32_t written = 0;
+uint16_t update_enable(TRegister* reg, uint16_t val) {
+ uint32_t sketchSpace;
+ if (rtu.Reg(reg->address)) {
+ if (COIL_BOOL(val))
+ return BIT_VAL(true);
+ Serial.println("!");
+ Serial.print("Written: ");
+ Serial.println(written * 2);
+ if(Update.end(true)){ //true to set the size to the current progress
+ Serial.println("Update Success. \nRebooting...");
+ }
+ return BIT_VAL(false);
+ }
+ #ifdef ESP8266
+ sketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
+ #else
+ sketchSpace = ESP32_SKETCH_SIZE;
+ #endif
+ Serial.printf("Starting update. FW max sise: %ld\n", sketchSpace);
+ if(!Update.begin(sketchSpace)) {
+ Update.printError(Serial);
+ return BIT_VAL(false);
+ }
+ written = 0;
+ return BIT_VAL(true);
+}
+
+// Expected arguments are:
+// func = EX_WRITE_FILE_REC
+// fileNum = UPDATE_FILE
+// recNumber ignored
+// recLength = data size (words)
+// frame = data to write ptr
+Modbus::ResultCode handle_file(Modbus::FunctionCode func, uint16_t fileNum, uint16_t recNumber, uint16_t recLength, uint8_t* frame) {
+ if (func != Modbus::FC_WRITE_FILE_REC) {
+ Serial.println("X:func");
+ return Modbus::EX_ILLEGAL_FUNCTION;
+ }
+ if (!rtu.Reg(COIL(UPDATE_ENABLE))) {
+ Serial.println("X:idle");
+ return Modbus::EX_ILLEGAL_VALUE;
+ }
+ if (fileNum != UPDATE_FILE) {
+ Serial.println("X:file");
+ return Modbus::EX_ILLEGAL_VALUE;
+ }
+ Serial.print(".");
+ if(Update.write(frame, recLength * 2) != recLength * 2){
+ Update.printError(Serial);
+ }
+ written += recLength;
+ return Modbus::EX_SUCCESS;
+}
+
+void setup() {
+ Serial.begin(115200);
+#if defined(ESP8266)
+ S.begin(19200, SWSERIAL_8N1);
+ rtu.begin(&S);
+#else
+ Serial1.begin(19200, SERIAL_8N1, 18, 19);
+ rtu.begin(&Serial1);
+ #endif
+ rtu.server(SLAVE_ID);
+ rtu.onFile(handle_file);
+ rtu.addReg(COIL(UPDATE_ENABLE));
+ rtu.onSet(COIL(UPDATE_ENABLE), update_enable);
+}
+
+void loop() {
+ rtu.task();
+ yield();
+}
\ No newline at end of file
diff --git a/examples/Files/README.md b/examples/Files/README.md
new file mode 100644
index 0000000..078d7b3
--- /dev/null
+++ b/examples/Files/README.md
@@ -0,0 +1,62 @@
+# Files operations
+
+## [Firmware update over ModbusRTU - Update source node](FW-Update-Source/FW-Update-Source.ino)
+
+ModbusRTU client that pushes firmware to server node.
+
+How to use:
+* Connect to target node
+* Prapare binary image (Sketch - Export compiled binary)
+* Open http:/// in browser
+* Choose firmware file
+* Press **Update firmware**
+* Debug information on update pregress is available in debug console
+
+## [Firmware update over ModbusRTU - Update target node](FW-Update-Source/FW-Update-Source.ino)
+
+ModbusRTU server that receives and flashes new firmware.
+
+## File block API
+
+### Client side
+
+```c
+uint16_t readFileRec(uint8_t slaveId, uint16_t fileNum, uint16_t startRec, uint16_t len, uint8_t* data, cbTransaction cb);
+uint16_t writeFileRec(uint8_t slaveId, uint16_t fileNum, uint16_t startRec, uint16_t len, uint8_t* data, cbTransaction cb);
+
+uint16_t readFileRec(IPAddress slaveId, uint16_t fileNum, uint16_t startRec, uint16_t len, uint8_t* data, cbTransaction cb, uint8_t unit);
+uint16_t writeFileRec(IPAddress slaveId, uint16_t fileNum, uint16_t startRec, uint16_t len, uint8_t* data, cbTransaction cb, uint8_t unit);
+```
+
+- `slaveId` server id or IP Address
+- `fileNum` File number to access
+- `startRec` Start offset in file (words)
+- `len` Length of data (words)
+- `*data` Pointer to data. In case of `readFileRec` must be at least `len` * 2 bytes.
+- `cb` Transactional callback function
+- `unit` ModbusTCP unit id
+
+### Server side
+
+```c
+typedef std::function cbModbusFileOp; // ST:
+typedef Modbus::ResultCode (*cbModbusFileOp)(Modbus::FunctionCode func, uint16_t fileNum, uint16_t recNumber, uint16_t recLength, uint8_t* frame); // no-STL
+
+bool onFile(std::function); // STL
+bool onFile(Modbus::ResultCode (*cb)(Modbus::FunctionCode, uint16_t, uint16_t, uint16_t, uint8_t*)); // no-STL
+```
+
+- `func` function code to process (FC_READ_FILE_REC or FC_WRITE_FILE_REC)
+- `fileNum` file # to read/write
+- `recNumber` record number in file (record size is word = 2 bytes)
+- `recLength` number of records to read/write
+- `*frame` pointer to data buffer
+
+`onFile` sets file operations handler function.
+
+# Modbus Library for Arduino
+### ModbusRTU, ModbusTCP and ModbusTCP Security
+
+(c)2021 [Alexander Emelianov](mailto:a.m.emelianov@gmail.com)
+
+The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..c82ba51
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,36 @@
+# Examples and API explanation
+
+## [RTU](RTU)
+
+ModbusRTU master and slave examples
+
+## [TCP ESP8266/ESP32](TCP-ESP)
+
+ModbusTCP for ESP8266/ESP32 client and server examples
+
+## [TCP Ethernet W5x00](TCP-Ethernet)
+
+ModbusTCP for W5x00 Ethernet library client and server examples (for all Arduino).
+
+## [TLS ESP8266/ESP32](TLS)
+
+ModbusTCP Security for ESP8266 and ESP32 (client only) examples.
+
+## [Callbacks usage](Callback)
+
+Examples of using callback functions.
+
+## [Files operations](Files)
+
+Modbus file operations examples.
+
+## [ModbusRTU to ModbusTCP bridge and related functions](Bridge)
+
+Very basic example of accessing ModbusRTU slave device connected to ESP8266/ESP32 via ModbusTCP server.
+
+# Modbus Library for Arduino
+### ModbusRTU, ModbusTCP and ModbusTCP Security
+
+(c)2020 [Alexander Emelianov](mailto:a.m.emelianov@gmail.com)
+
+The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
diff --git a/examples/RTU/ESP32-Concurent/ESP32-Concurent.ino b/examples/RTU/ESP32-Concurent/ESP32-Concurent.ino
new file mode 100644
index 0000000..2cee07c
--- /dev/null
+++ b/examples/RTU/ESP32-Concurent/ESP32-Concurent.ino
@@ -0,0 +1,101 @@
+/*
+ ModbusRTU ESP32
+ Concurent thread example
+
+ (c)2020 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+
+ Tool Modbus Slave on PC for test
+ https://www.modbustools.com/download.html
+*/
+
+#include
+
+#define REG 0
+#define REG_NUM 32
+#define SLAVE_ID1 51
+#define SLAVE_ID2 52
+
+#define MBUS_HW_SERIAL Serial1
+#define MBUS_TXD_PIN 16
+#define MBUS_RXD_PIN 35
+
+ModbusRTU mb;
+
+xSemaphoreHandle xMutex;
+Modbus::ResultCode err;
+
+Modbus::ResultCode readSync(uint8_t address, uint16_t start, uint16_t num, uint16_t* buf) {
+ xSemaphoreTake(xMutex, portMAX_DELAY);
+ if (mb.slave()) {
+ xSemaphoreGive(xMutex);
+ return Modbus::EX_GENERAL_FAILURE;
+ }
+ Serial.printf("SlaveID: %d Hreg %d\r\n", address, start);
+ mb.readIreg(address, start, buf, num, [](Modbus::ResultCode event, uint16_t, void*) {
+ err = event;
+ return true;
+ });
+ while (mb.slave()) {
+ vTaskDelay(1);
+ mb.task();
+ }
+ Modbus::ResultCode res = err;
+ xSemaphoreGive(xMutex);
+ return res;
+}
+
+void loop1( void * pvParameters );
+void loop2( void * pvParameters );
+
+void setup() {
+ Serial.begin(115200);
+ MBUS_HW_SERIAL.begin(9600, SERIAL_8N1, MBUS_RXD_PIN, MBUS_TXD_PIN);
+ mb.begin(&MBUS_HW_SERIAL);
+ mb.master();
+ xMutex = xSemaphoreCreateMutex();
+ xTaskCreatePinnedToCore(
+ loop1, /* Task function. */
+ "Task1", /* name of task. */
+ 10000, /* Stack size of task */
+ NULL, /* parameter of the task */
+ 10, /* priority of the task */
+ NULL, /* Task handle to keep track of created task */
+ 0); /* pin task to core 1 */
+
+ xTaskCreatePinnedToCore(
+ loop2, /* Task function. */
+ "Task2", /* name of task. */
+ 10000, /* Stack size of task */
+ NULL, /* parameter of the task */
+ 1, /* priority of the task */
+ NULL, /* Task handle to keep track of created task */
+ 1); /* pin task to core 1 */
+
+}
+
+uint16_t hregs1[REG_NUM];
+void loop1( void * pvParameters ){
+ while(true) {
+ delay(10);
+ if (readSync(SLAVE_ID1, REG, REG_NUM, hregs1) == Modbus::EX_SUCCESS)
+ Serial.println("OK 1");
+ else
+ Serial.println("Error 1");
+ }
+}
+
+uint16_t hregs2[REG_NUM];
+void loop2( void * pvParameters ){
+ while(true) {
+ delay(100);
+ if (readSync(SLAVE_ID2, REG, REG_NUM, hregs2) == Modbus::EX_SUCCESS)
+ Serial.println("OK 2");
+ else
+ Serial.println("Error 2");
+ }
+}
+
+void loop() {
+ delay(100);
+}
diff --git a/examples/RTU/README.MD b/examples/RTU/README.MD
new file mode 100644
index 0000000..1f4e4b9
--- /dev/null
+++ b/examples/RTU/README.MD
@@ -0,0 +1,57 @@
+This example introduces how to use the library for ModbusRTU (typicaly over RS-485) to act as [master](master) or [slave](slave). Additionally there is [example of master](ESP32-Concurent) device for multithread usage with ESP32.
+
+## [Concurrent thread-safe access to Modbus object](ESP32-Concurent/ESP32-Concurent.ino)
+
+## [Simple ModbusRTU master](master/master.ino)
+
+## [Simple ModbusRTU slave](slave/slave.ino)
+
+## [Sync ModbusRTU master](masterSync/masterSync.ino)
+
+## Modbus RTU Specific API
+
+```c
+bool begin(SoftwareSerial* port, int16_t txEnablePin=-1, bool txEnableDirect=true);
+bool begin(HardwareSerial* port, int16_t txEnablePin=-1, bool txEnableDirect=true);
+bool begin(Stream* port);
+```
+
+- `port` Pointer to Serial port
+- `txEnablePin` RX/TX control pin. Not assigned (assume auto RX/TX) by default
+- `txEnableDirect` Direct (true, default) or inverse (false) RX/TX pin control.
+
+Assign Serial port. txEnablePin controls transmit enable for MAX-485.
+
+```c
+void setBaudrate(uint32 baud);
+```
+
+- `baud` New baudrate.
+
+Set or override Serial baudrate. Must be called after .begin() for Non-ESP devices.
+
+```c
+void client();
+void server(uint8_t slaveId);
+void slave(); // Deprecated
+void master(uint8_t slaveId); // Deprecated
+```
+
+- `slaveId` Modbus slave id to associate to.
+
+Select and initialize master or slave mode to work. Switching between modes is not supported. Call is not returning error in this case but behaviour is unpredictable.
+
+```c
+uint8_t client();
+uint8_t slave(); // Deprecated
+```
+
+- Slave mode: Returns configured slave id.
+- Master mode: Returns slave id for active request or 0 if no request in progress.
+
+# Modbus Library for Arduino
+### ModbusRTU, ModbusTCP and ModbusTCP Security
+
+(c)2020 [Alexander Emelianov](mailto:a.m.emelianov@gmail.com)
+
+The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
diff --git a/examples/RTU/master/master.ino b/examples/RTU/master/master.ino
new file mode 100644
index 0000000..d4d8c23
--- /dev/null
+++ b/examples/RTU/master/master.ino
@@ -0,0 +1,62 @@
+/*
+ ModbusRTU ESP8266/ESP32
+ Read multiple coils from slave device example
+
+ (c)2019 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+
+ modified 13 May 2020
+ by brainelectronics
+
+ This code is licensed under the BSD New License. See LICENSE.txt for more info.
+*/
+
+#include
+#if defined(ESP8266)
+ #include
+ // SoftwareSerial S(D1, D2, false, 256);
+
+ // receivePin, transmitPin, inverse_logic, bufSize, isrBufSize
+ // connect RX to D2 (GPIO4, Arduino pin 4), TX to D1 (GPIO5, Arduino pin 4)
+ SoftwareSerial S(4, 5);
+#endif
+
+ModbusRTU mb;
+
+bool cbWrite(Modbus::ResultCode event, uint16_t transactionId, void* data) {
+#ifdef ESP8266
+ Serial.printf_P("Request result: 0x%02X, Mem: %d\n", event, ESP.getFreeHeap());
+#elif ESP32
+ Serial.printf_P("Request result: 0x%02X, Mem: %d\n", event, ESP.getFreeHeap());
+#else
+ Serial.print("Request result: 0x");
+ Serial.print(event, HEX);
+#endif
+ return true;
+}
+
+void setup() {
+ Serial.begin(115200);
+ #if defined(ESP8266)
+ S.begin(9600, SWSERIAL_8N1);
+ mb.begin(&S);
+ #elif defined(ESP32)
+ Serial1.begin(9600, SERIAL_8N1);
+ mb.begin(&Serial1);
+ #else
+ Serial1.begin(9600, SERIAL_8N1);
+ mb.begin(&Serial1);
+ mb.setBaudrate(9600);
+ #endif
+ mb.master();
+}
+
+bool coils[20];
+
+void loop() {
+ if (!mb.slave()) {
+ mb.readCoil(1, 1, coils, 20, cbWrite);
+ }
+ mb.task();
+ yield();
+}
\ No newline at end of file
diff --git a/examples/RTU/masterSync/masterSync.ino b/examples/RTU/masterSync/masterSync.ino
new file mode 100644
index 0000000..75a4614
--- /dev/null
+++ b/examples/RTU/masterSync/masterSync.ino
@@ -0,0 +1,47 @@
+/*
+ Modbus Library for Arduino Example - Modbus RTU Client
+ Read Holding Registers from Modbus RTU Server in blocking way
+ ESP8266 Example
+
+ (c)2020 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+
+#include
+#include
+
+#define SLAVE_ID 1
+#define FIRST_REG 0
+#define REG_COUNT 2
+
+SoftwareSerial S(D2, D1);
+ModbusRTU mb;
+
+bool cb(Modbus::ResultCode event, uint16_t transactionId, void* data) { // Callback to monitor errors
+ if (event != Modbus::EX_SUCCESS) {
+ Serial.print("Request result: 0x");
+ Serial.print(event, HEX);
+ }
+ return true;
+}
+
+void setup() {
+ Serial.begin(115200);
+ S.begin(9600, SWSERIAL_8N1);
+ mb.begin(&S);
+ mb.master();
+}
+
+void loop() {
+ uint16_t res[REG_COUNT];
+ if (!mb.slave()) { // Check if no transaction in progress
+ mb.readHreg(SLAVE_ID, FIRST_REG, res, REG_COUNT, cb); // Send Read Hreg from Modbus Server
+ while(mb.slave()) { // Check if transaction is active
+ mb.task();
+ delay(10);
+ }
+ Serial.println(res[0]);
+ Serial.println(res[1]);
+ }
+ delay(1000);
+}
\ No newline at end of file
diff --git a/examples/RTU/slave/slave.ino b/examples/RTU/slave/slave.ino
new file mode 100644
index 0000000..4a96808
--- /dev/null
+++ b/examples/RTU/slave/slave.ino
@@ -0,0 +1,38 @@
+/*
+ ModbusRTU ESP8266/ESP32
+ Simple slave example
+
+ (c)2019 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+
+ modified 13 May 2020
+ by brainelectronics
+
+ This code is licensed under the BSD New License. See LICENSE.txt for more info.
+*/
+
+#include
+
+#define REGN 10
+#define SLAVE_ID 1
+
+ModbusRTU mb;
+
+void setup() {
+ Serial.begin(9600, SERIAL_8N1);
+#if defined(ESP32) || defined(ESP8266)
+ mb.begin(&Serial);
+#else
+ mb.begin(&Serial);
+ //mb.begin(&Serial, RXTX_PIN); //or use RX/TX direction control pin (if required)
+ mb.setBaudrate(9600);
+#endif
+ mb.slave(SLAVE_ID);
+ mb.addHreg(REGN);
+ mb.Hreg(REGN, 100);
+}
+
+void loop() {
+ mb.task();
+ yield();
+}
\ No newline at end of file
diff --git a/arduinoIDE/ModbusIP_ESP8266/examples/TestAnalogInput/TestAnalogInput.ino b/examples/TCP-ESP/IP-server-AnalogInput/IP-server-AnalogInput.ino
similarity index 68%
rename from arduinoIDE/ModbusIP_ESP8266/examples/TestAnalogInput/TestAnalogInput.ino
rename to examples/TCP-ESP/IP-server-AnalogInput/IP-server-AnalogInput.ino
index 1a29954..0e39a5f 100644
--- a/arduinoIDE/ModbusIP_ESP8266/examples/TestAnalogInput/TestAnalogInput.ino
+++ b/examples/TCP-ESP/IP-server-AnalogInput/IP-server-AnalogInput.ino
@@ -1,15 +1,23 @@
/*
- Modbus-Arduino Example - Test Holding Register (Modbus IP ESP8266)
+ Modbus-Arduino Example - Test Analog Input (Modbus IP ESP8266)
Read Analog sensor on Pin ADC (ADC input between 0 ... 1V)
+ Original library
Copyright by André Sarmento Barbosa
http://github.com/andresarmento/modbus-arduino
+
+ Current version
+ (c)2017 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
*/
-#include
-#include
+#ifdef ESP8266
+ #include
+#else //ESP32
+ #include
+#endif
#include
-//Modbus Registers Offsets (0-9999)
+//Modbus Registers Offsets
const int SENSOR_IREG = 100;
//ModbusIP object
@@ -20,19 +28,18 @@ long ts;
void setup() {
Serial.begin(115200);
- //Config Modbus IP
- mb.config("your_ssid", "your_password");
-
+ WiFi.begin("your_ssid", "your_password");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
-
+
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
+ mb.server(); //Start Modbus IP
// Add SENSOR_IREG register - Use addIreg() for analog Inputs
mb.addIreg(SENSOR_IREG);
@@ -49,4 +56,5 @@ void loop() {
//Setting raw value (0-1024)
mb.Ireg(SENSOR_IREG, analogRead(A0));
}
+ delay(10);
}
diff --git a/arduinoIDE/ModbusIP_ESP8266/examples/TestLed/TestLed.ino b/examples/TCP-ESP/IP-server-Led/IP-server-Led.ino
similarity index 72%
rename from arduinoIDE/ModbusIP_ESP8266/examples/TestLed/TestLed.ino
rename to examples/TCP-ESP/IP-server-Led/IP-server-Led.ino
index c314041..5155711 100644
--- a/arduinoIDE/ModbusIP_ESP8266/examples/TestLed/TestLed.ino
+++ b/examples/TCP-ESP/IP-server-Led/IP-server-Led.ino
@@ -1,15 +1,23 @@
/*
Modbus-Arduino Example - Test Led (Modbus IP ESP8266)
Control a Led on GPIO0 pin using Write Single Coil Modbus Function
+ Original library
Copyright by André Sarmento Barbosa
http://github.com/andresarmento/modbus-arduino
+
+ Current version
+ (c)2017 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
*/
-#include
-#include
+#ifdef ESP8266
+ #include
+#else //ESP32
+ #include
+#endif
#include
-//Modbus Registers Offsets (0-9999)
+//Modbus Registers Offsets
const int LED_COIL = 100;
//Used Pins
const int ledPin = 0; //GPIO0
@@ -20,7 +28,7 @@ ModbusIP mb;
void setup() {
Serial.begin(115200);
- mb.config("your_ssid", "your_password");
+ WiFi.begin("your_ssid", "your_password");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
@@ -32,6 +40,8 @@ void setup() {
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
+ mb.server();
+
pinMode(ledPin, OUTPUT);
mb.addCoil(LED_COIL);
}
@@ -42,4 +52,5 @@ void loop() {
//Attach ledPin to LED_COIL register
digitalWrite(ledPin, mb.Coil(LED_COIL));
+ delay(10);
}
\ No newline at end of file
diff --git a/arduinoIDE/ModbusIP_ESP8266/examples/TestSwitchStatus/TestSwitchStatus.ino b/examples/TCP-ESP/IP-server-SwitchStatus/IP-server-SwitchStatus.ino
similarity index 61%
rename from arduinoIDE/ModbusIP_ESP8266/examples/TestSwitchStatus/TestSwitchStatus.ino
rename to examples/TCP-ESP/IP-server-SwitchStatus/IP-server-SwitchStatus.ino
index b156ece..6a7b339 100644
--- a/arduinoIDE/ModbusIP_ESP8266/examples/TestSwitchStatus/TestSwitchStatus.ino
+++ b/examples/TCP-ESP/IP-server-SwitchStatus/IP-server-SwitchStatus.ino
@@ -1,15 +1,23 @@
/*
Modbus-Arduino Example - Test Holding Register (Modbus IP ESP8266)
Read Switch Status on pin GPIO0
+ Original library
Copyright by André Sarmento Barbosa
http://github.com/andresarmento/modbus-arduino
+
+ Current version
+ (c)2017 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
*/
-#include
-#include
+#ifdef ESP8266
+ #include
+#else //ESP32
+ #include
+#endif
#include
-//Modbus Registers Offsets (0-9999)
+//Modbus Registers Offsets
const int SWITCH_ISTS = 100;
//Used Pins
const int switchPin = 0; //GPIO0
@@ -18,8 +26,15 @@ const int switchPin = 0; //GPIO0
ModbusIP mb;
void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("your_ssid", "your_password");
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
//Config Modbus IP
- mb.config("your_ssid", "your_password");
+ mb.server();
//Set ledPin mode
pinMode(switchPin, INPUT);
// Add SWITCH_ISTS register - Use addIsts() for digital inputs
@@ -32,4 +47,5 @@ void loop() {
//Attach switchPin to SWITCH_ISTS register
mb.Ists(SWITCH_ISTS, digitalRead(switchPin));
+ delay(10);
}
diff --git a/examples/TCP-ESP/README.md b/examples/TCP-ESP/README.md
new file mode 100644
index 0000000..70ae29f
--- /dev/null
+++ b/examples/TCP-ESP/README.md
@@ -0,0 +1,136 @@
+# ESP8266/ESP32 TCP Examples
+
+## [Basic client](client/client.ino)
+
+## [Client with blocking read operation](clientSync/clientSync.ino)
+
+## [Server](server/server.ino)
+
+### API
+
+```c
+void client();
+```
+
+Initialize internal structures to act as a Modbus client.
+
+```c
+bool connect(IPAddress ip, uint16_t port = MODBUSIP_PORT);
+bool disconnect(IPAddress ip);
+```
+
+- `ip` IP address of the remote Modbus server
+- `port` TCP port of remote Modbus server (standard value is 502)
+
+Note: Just one connection to the specific address is supported. That is the library is unable to simultaniousaly connect to Modbus servers that has same IP address but diffrent ports.
+
+```c
+uint16_t readCoil(IPAddress ip, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t readCoil(const char* host, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t readCoil(String host, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t writeCoil(IPAddress ip, uint16_t offset, bool value, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t writeCoil(IPAddress ip, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t readIsts(IPAddress ip, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t writeHreg(IPAddress ip, uint16_t offset, uint16_t value, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t writeHreg(IPAddress ip, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t readHreg(IPAddress ip, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+uint16_t readIreg(IPAddress ip, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t uint = MODBUSIP_UNIT);
+```
+
+- `ip` IP Address of Modbus server to get registers from
+- `host` Hostname fo Modbus server to get registers from
+- `offset` Address of first Modbus register to read/write
+- `numregs` Count of registers to read/write
+- `cb` Transaction callback function (see [Calback examples](../calback) for details). `NULL` if not used
+- `unit` Modbus unit
+
+Sends corresponding Modbus read request to Modbus server at `ip`. Connection with server shoud be already established by connect(ip).
+Returns transaction `id` or `0` on failure. Failure maens that client unable to send the request bacause of no connection to the Modbus server is established or other internal error.
+Note: read/write functions just sending requests to remote Modbus server. The functions returns immediate after request sent and doesn't waiting for result. That is `value` contains no result data on the function exit. `value` will be filled as responce arrive and processed by .task() function.
+
+```c
+bool isTransaction(uint16_t id);
+```
+
+- `id` Transaction id.
+
+Returns `true` if transaction with `id` is active.
+
+```c
+bool isConnected(IPAddress ip);
+```
+
+- `ip` Remote Modbus server IP address
+
+Returns `true` is connection with Modbus server at `ip` is established.
+
+```c
+void dropTransactions();
+```
+
+Cancel all active transactions. Callback with result code `Modbus::EX_CANCEL` will be called for each transaction (if assigned).
+
+### Add local register
+```c
+bool addHreg(uint16_t offset, uint16_t value = 0, uint16_t numregs = 1);
+bool addCoil(uint16_t offset, bool value = false, uint16_t numregs = 1);
+bool addIsts(uint16_t offset, bool value = false, uint16_t numregs = 1);
+bool addIreg(uint16_t offset, uint16_t value = 0, uint16_t nemregs = 1);
+```
+
+- `offset` Address of the first register to add
+- `value` Initial value to be assigned to register(s)
+- `numregs` Count of registers to be created
+
+Adding new register(s) and assigning value(s). If [some] registers already exists value will be updated.
+Returns `true` on success. `false` if operation is failed for some reason.
+
+### Write local reg
+
+```c
+bool Hreg(uint16_t offset, uint16_t value);
+bool Coil(uint16_t offset, bool value);
+bool Ists(uint16_t offset, bool value);
+bool Ireg(uint16_t offset, uint16_t value);
+```
+
+- `offset` Address of the register
+- `value` Value to be assigned to register
+
+Returns `true` on success. `false` if register not previousely added or other error.
+
+### Read local reg
+
+```c
+uint16_t Hreg(uint16_t offset);
+bool Coil(uint16_t offset);
+bool Ists(uint16_t offset);
+uint16_t Ireg(uint16_t offset);
+```
+
+- `offset` Address of the register to read
+
+
+Returns current value of the register.
+
+### Remove reg(s)
+
+```c
+bool removeHreg(uint16_t offset, uint16_t numregs = 1);
+bool removeCoil(uint16_t offset, uint16_t numregs = 1);
+bool removeIsts(uint16_t offset, uint16_t numregs = 1);
+bool removeIreg(uint16_t offset, uint16_t numregs = 1);
+```
+
+- `offset` Address of the first register to remove
+- `numregs` Count of registers to be created
+
+Function trying to remove `numregs` registers starting from `offset`. If some of registers within the range are not exists removal continues execution.
+Returns `true` if atleast one register in the range was removed.
+
+# Modbus Library for Arduino
+### ModbusRTU, ModbusTCP and ModbusTCP Security
+
+(c)2020 [Alexander Emelianov](mailto:a.m.emelianov@gmail.com)
+
+The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
\ No newline at end of file
diff --git a/examples/TCP-ESP/client/client.ino b/examples/TCP-ESP/client/client.ino
new file mode 100644
index 0000000..22d3de7
--- /dev/null
+++ b/examples/TCP-ESP/client/client.ino
@@ -0,0 +1,55 @@
+/*
+ Modbus-Arduino Example - Master Modbus IP Client (ESP8266/ESP32)
+ Read Holding Register from Server device
+
+ (c)2018 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+
+#ifdef ESP8266
+ #include
+#else
+ #include
+#endif
+#include
+
+const int REG = 528; // Modbus Hreg Offset
+IPAddress remote(192, 168, 30, 13); // Address of Modbus Slave device
+const int LOOP_COUNT = 10;
+
+ModbusIP mb; //ModbusIP object
+
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("SSID", "PASSWORD");
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ mb.client();
+}
+
+uint16_t res = 0;
+uint8_t show = LOOP_COUNT;
+
+void loop() {
+ if (mb.isConnected(remote)) { // Check if connection to Modbus Slave is established
+ mb.readHreg(remote, REG, &res); // Initiate Read Coil from Modbus Slave
+ } else {
+ mb.connect(remote); // Try to connect if no connection
+ }
+ mb.task(); // Common local Modbus task
+ delay(100); // Pulling interval
+ if (!show--) { // Display Slave register value one time per second (with default settings)
+ Serial.println(res);
+ show = LOOP_COUNT;
+ }
+}
\ No newline at end of file
diff --git a/examples/TCP-ESP/clientPull/clientPull.ino b/examples/TCP-ESP/clientPull/clientPull.ino
new file mode 100644
index 0000000..2098004
--- /dev/null
+++ b/examples/TCP-ESP/clientPull/clientPull.ino
@@ -0,0 +1,71 @@
+/*
+ Modbus-Arduino Example - Modbus IP Client (ESP8266/ESP32)
+ Control Led on D4/TX pin by remote Modbus device using Read Single Coil Modbus Function
+
+ (c)2018 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+
+#ifdef ESP8266
+ #include
+#else
+ #include
+#endif
+#include
+
+
+const int LED_COIL = 1; // Modbus Coil Offset
+IPAddress remote(192, 168, 30, 116); // Address of Modbus Slave device
+
+//Used Pins
+#ifdef ESP8266
+ #define USE_LED D4
+ #else
+ #define UES_LED TX
+ #endif
+
+ModbusIP mb; //ModbusIP object
+
+uint16_t gc(TRegister* r, uint16_t v) { // Callback function
+ if (r->value != v) { // Check if Coil state is going to be changed
+ Serial.print("Set reg: ");
+ Serial.println(v);
+ if (COIL_BOOL(v)) {
+ digitalWrite(USE_LED, LOW);
+ } else {
+ digitalWrite(USE_LED, HIGH);
+ }
+ }
+ return v;
+}
+
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("SSID", "password");
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ mb.client(); // Initialize local Modbus Client
+ pinMode(USE_LED, OUTPUT);
+ mb.addCoil(LED_COIL); // Add Coil
+ mb.onSetCoil(LED_COIL, gc); // Assign Callback on set the Coil
+}
+
+void loop() {
+ if (mb.isConnected(remote)) { // Check if connection to Modbus Slave is established
+ mb.pullCoil(remote, LED_COIL, LED_COIL); // Initiate Read Coil from Modbus Slave
+ } else {
+ mb.connect(remote); // Try to connect if no connection
+ }
+ mb.task(); // Common local Modbus task
+ delay(10); // Polling interval
+}
diff --git a/examples/TCP-ESP/clientSync/clientSync.ino b/examples/TCP-ESP/clientSync/clientSync.ino
new file mode 100644
index 0000000..03c2213
--- /dev/null
+++ b/examples/TCP-ESP/clientSync/clientSync.ino
@@ -0,0 +1,55 @@
+/*
+ Modbus Library for Arduino Example - Modbus IP Client (ESP8266/ESP32)
+ Read Holding Register from Modbus Server in blocking way
+
+ (c)2020 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+*/
+
+#ifdef ESP8266
+ #include
+#else
+ #include
+#else
+#error "Unsupported platform"
+#endif
+#include
+
+const int REG = 528; // Modbus Hreg Offset
+IPAddress remote(192, 168, 30, 13); // Address of Modbus Slave device
+
+ModbusIP mb; //ModbusTCP object
+
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("SSID", "PASSWORD");
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+
+ mb.client();
+}
+
+uint16_t res = 0;
+
+void loop() {
+ if (mb.isConnected(remote)) { // Check if connection to Modbus Slave is established
+ uint16_t trans = mb.readHreg(remote, REG, &res); // Initiate Read Hreg from Modbus Server
+ while(mb.isTransaction(trans)) { // Check if transaction is active
+ mb.task();
+ delay(10);
+ }
+ Serial.println(res); // At this point res is filled with responce value
+ } else {
+ mb.connect(remote); // Try to connect if no connection
+ }
+ delay(100);
+}
\ No newline at end of file
diff --git a/arduinoIDE/ModbusIP_ESP8266/examples/TestHoldingReg/TestHoldingReg.ino b/examples/TCP-ESP/server/server.ino
similarity index 69%
rename from arduinoIDE/ModbusIP_ESP8266/examples/TestHoldingReg/TestHoldingReg.ino
rename to examples/TCP-ESP/server/server.ino
index 982fadc..1cf0977 100644
--- a/arduinoIDE/ModbusIP_ESP8266/examples/TestHoldingReg/TestHoldingReg.ino
+++ b/examples/TCP-ESP/server/server.ino
@@ -2,15 +2,23 @@
Modbus-Arduino Example - Test Holding Register (Modbus IP ESP8266)
Configure Holding Register (offset 100) with initial value 0xABCD
You can get or set this holding register
+ Original library
Copyright by André Sarmento Barbosa
http://github.com/andresarmento/modbus-arduino
+
+ Current version
+ (c)2017 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
*/
-#include
-#include
+#ifdef ESP8266
+ #include
+#else //ESP32
+ #include
+#endif
#include
-// Modbus Registers Offsets (0-9999)
+// Modbus Registers Offsets
const int TEST_HREG = 100;
@@ -20,7 +28,7 @@ ModbusIP mb;
void setup() {
Serial.begin(115200);
- mb.config("your_ssid", "your_password");
+ WiFi.begin("your_ssid", "your_password");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
@@ -32,11 +40,12 @@ void setup() {
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
+ mb.server();
mb.addHreg(TEST_HREG, 0xABCD);
}
void loop() {
//Call once inside loop() - all magic here
mb.task();
-
+ delay(10);
}
diff --git a/examples/TCP-Ethernet/README.md b/examples/TCP-Ethernet/README.md
new file mode 100644
index 0000000..8857af7
--- /dev/null
+++ b/examples/TCP-Ethernet/README.md
@@ -0,0 +1,15 @@
+# W5x00 Example
+
+## Physical connection between ESP32 and W5500
+
+* GPIO23 <--> MOSI
+* GPIO19 <--> MISO
+* GPIO18 <--> SCLK
+* GPIO5 <--> SCS
+
+# Modbus Library for Arduino
+### ModbusRTU, ModbusTCP and ModbusTCP Security
+
+(c)2020 [Alexander Emelianov](mailto:a.m.emelianov@gmail.com)
+
+The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
diff --git a/examples/TCP-Ethernet/client/client.ino b/examples/TCP-Ethernet/client/client.ino
new file mode 100644
index 0000000..c477adc
--- /dev/null
+++ b/examples/TCP-Ethernet/client/client.ino
@@ -0,0 +1,52 @@
+/*
+ ModbusTCP for W5x00 Ethernet library
+ Basic Client code example
+
+ (c)2020 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+
+ This code is licensed under the BSD New License. See LICENSE.txt for more info.
+*/
+
+#include
+#include // Ethernet library v2 is required
+#include
+
+const uint16_t REG = 512; // Modbus Hreg Offset
+IPAddress remote(192, 168, 30, 12); // Address of Modbus Slave device
+const int32_t showDelay = 5000; // Show result every n'th mellisecond
+
+// Enter a MAC address and IP address for your controller below.
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE
+};
+IPAddress ip(192, 168, 30, 178); // The IP address will be dependent on your local network:
+ModbusEthernet mb; // Declare ModbusTCP instance
+
+void setup() {
+ Serial.begin(115200); // Open serial communications and wait for port to open
+ #if defined(AVR_LEONARDO)
+ while (!Serial) {} // wait for serial port to connect. Needed for Leonardo only
+ #endif
+ Ethernet.init(5); // SS pin
+ Ethernet.begin(mac, ip); // start the Ethernet connection
+ delay(1000); // give the Ethernet shield a second to initialize
+ mb.client(); // Act as Modbus TCP client
+}
+
+uint16_t res = 0;
+uint32_t showLast = 0;
+
+void loop() {
+if (mb.isConnected(remote)) { // Check if connection to Modbus Slave is established
+ mb.readHreg(remote, REG, &res); // Initiate Read Hreg from Modbus Slave
+ } else {
+ mb.connect(remote); // Try to connect if not connected
+ }
+ delay(100); // Pulling interval
+ mb.task(); // Common local Modbus task
+ if (millis() - showLast > showDelay) { // Display register value every 5 seconds (with default settings)
+ showLast = millis();
+ Serial.println(res);
+ }
+}
diff --git a/examples/TCP-Ethernet/server/server.ino b/examples/TCP-Ethernet/server/server.ino
new file mode 100644
index 0000000..769e7cd
--- /dev/null
+++ b/examples/TCP-Ethernet/server/server.ino
@@ -0,0 +1,37 @@
+/*
+ ModbusTCP for W5x00 Ethernet library
+ Basic Server code example
+
+ (c)2020 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+
+ This code is licensed under the BSD New License. See LICENSE.txt for more info.
+*/
+
+#include
+#include // Ethernet library v2 is required
+#include
+
+// Enter a MAC address and IP address for your controller below.
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+IPAddress ip(192, 168, 30, 177); // The IP address will be dependent on your local network:
+ModbusEthernet mb; // Declare ModbusTCP instance
+
+void setup() {
+ Serial.begin(115200); // Open serial communications and wait for port to open
+ #if defined(AVR_LEONARDO)
+ while (!Serial) {} // wait for serial port to connect. Needed for Leonardo only
+ #endif
+ Ethernet.init(5); // SS pin
+ Ethernet.begin(mac, ip); // start the Ethernet connection
+ delay(1000); // give the Ethernet shield a second to initialize
+ mb.server(); // Act as Modbus TCP server
+ mb.addReg(HREG(100)); // Add Holding register #100
+}
+
+void loop() {
+ mb.task(); // Server Modbus TCP queries
+ delay(50);
+}
\ No newline at end of file
diff --git a/examples/TLS/README.md b/examples/TLS/README.md
new file mode 100644
index 0000000..ede543f
--- /dev/null
+++ b/examples/TLS/README.md
@@ -0,0 +1,47 @@
+# Modbus\TCP Security Example
+
+### *Target Platforms:*
+- *ESP8266 (CLient/Server)*
+- *ESP32 (Client only)*
+
+## [Sample certificates](certs)
+
+[cert.cmd](certs/cert.cmd) Script to recreate all the certificates in the catalog. Requires OpenSSL installed.
+
+[Good issue explanation to read](https://github.com/esp8266/Arduino/issues/6128)
+
+## [Client](client/client.ino)
+
+```c
+bool connect(const char* host, uint16_t port, const char* client_cert = nullptr, const char* client_private_key = nullptr, const char* ca_cert = nullptr);
+bool connectWithKnownKey(IPAddress ip, uint16_t port, const char* client_cert = nullptr, const char* client_private_key = nullptr, const char* key = nullptr);
+```
+
+- `const char* host` Host name to connect to
+- `uint16_t port` Host port
+- `const char* client_cert` Client's certificate
+- `const char* client_private_key` Client's private key
+- `const char* ca_cert` Certificate of CA. Can be omitted (or set NULL) to escape certificate chain verifying.
+- `IPAddress ip` Host IP address to connect to
+- `const char* key` Server's public key
+
+All certificates must be in PEM format and can be stored in PROGMEM.
+
+## [Server](server/server.ino)
+
+```c
+void server(uint16_t port, const char* server_cert = nullptr, const char* server_private_key = nullptr, const char* ca_cert = nullptr);
+```
+- `uint16_t port` Port to bind to
+- `const char* server_cert` Server certificate in PEM format.
+- `const char* server_private_key` Server private key in PEM format.
+- `const char* ca_cert` Certificate of CA.
+
+All certificates must be in PEM format and can be stored in PROGMEM.
+
+# Modbus Library for Arduino
+### ModbusRTU, ModbusTCP and ModbusTCP Security
+
+(c)2020 [Alexander Emelianov](mailto:a.m.emelianov@gmail.com)
+
+The code in this repo is licensed under the BSD New License. See LICENSE.txt for more info.
diff --git a/examples/TLS/certs/ca.conf b/examples/TLS/certs/ca.conf
new file mode 100644
index 0000000..c48c6ab
--- /dev/null
+++ b/examples/TLS/certs/ca.conf
@@ -0,0 +1,11 @@
+[ req ]
+prompt = no
+default_bits = 2048
+distinguished_name = req_dn
+x509_extensions = v3_req
+
+[ req_dn ]
+CN = root_ca
+
+[v3_req]
+basicConstraints=CA:TRUE
diff --git a/examples/TLS/certs/ca_cer.pem b/examples/TLS/certs/ca_cer.pem
new file mode 100644
index 0000000..14a4793
--- /dev/null
+++ b/examples/TLS/certs/ca_cer.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwjCCAaqgAwIBAgIUTz9NFtf8JkdIkrDroXVB/ANtqlYwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHcm9vdF9jYTAeFw0yMDA4MjcxMDI4MzFaFw0zMTExMTQx
+MDI4MzFaMBIxEDAOBgNVBAMMB3Jvb3RfY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQCpYdVI2PjW+Pdw5bMqxFz0s3jgYgTHyt51NJGlImgJpmjmj16T
+rwcqAe70BtsSjOQeWRoF/rk46ZO/ntDbVkP8ZA40Vf8F8Yft64f1OOBf93rTR0sH
+oUk+HmE3Iu+bWYSewNMw/LJyF2r95V2xNeX50Y+BhQskBoWYR7C671ifFlsQHI+a
+/BpALEi7qt6kGenlhrmRAjweNxVNILHTPH7Fr/TYXWfAb69TzXWTUFy0bdwZfPIP
+b2HXyGINGiD6EtZDkybPk17zZgJKMdxpEG5XA/O+daVh3Prlar+amqb30zntOVga
+AcyREcmzYFFBWQmuKNw9mz9x09GWLWjBaYP9AgMBAAGjEDAOMAwGA1UdEwQFMAMB
+Af8wDQYJKoZIhvcNAQELBQADggEBAIFtNowXu8wfahpKF5MoNKrqA9AG/aGzlmbD
+pBKr5Cvvo7NdC6oZMdXlS0VkRmAyw9mEJgwspUvqsLx0/lgN+sE381MUovWL9BIs
+o4IOax5473q6ZwV87jwpsrlNHBiolw+WCDKVYuDktaThCaxqxmPKCPMbgYPdqWB0
+l1gYDJJ+MwNH/CRsynpM8Hppf88BwwbM6JYegg5/DLxRl5z3HjCAVD8vBoqkWLRD
+b9tIER4WDJhZG4tzgMW+lbMJyDoQA1cw4BGag4Ir1er32+w1519UR/VK0ltk9BK9
+yHObfUNN6saco1/f4OM4tzaQOKa+6U1iXVBTBjE2IHPchGqctBk=
+-----END CERTIFICATE-----
diff --git a/examples/TLS/certs/ca_cer.srl b/examples/TLS/certs/ca_cer.srl
new file mode 100644
index 0000000..bc42790
--- /dev/null
+++ b/examples/TLS/certs/ca_cer.srl
@@ -0,0 +1 @@
+4221D52EC27B0A950D9F41EFC7D20A43100E437D
diff --git a/examples/TLS/certs/ca_key.pem b/examples/TLS/certs/ca_key.pem
new file mode 100644
index 0000000..d9ad72b
--- /dev/null
+++ b/examples/TLS/certs/ca_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAqWHVSNj41vj3cOWzKsRc9LN44GIEx8redTSRpSJoCaZo5o9e
+k68HKgHu9AbbEozkHlkaBf65OOmTv57Q21ZD/GQONFX/BfGH7euH9TjgX/d600dL
+B6FJPh5hNyLvm1mEnsDTMPyychdq/eVdsTXl+dGPgYULJAaFmEewuu9YnxZbEByP
+mvwaQCxIu6repBnp5Ya5kQI8HjcVTSCx0zx+xa/02F1nwG+vU811k1BctG3cGXzy
+D29h18hiDRog+hLWQ5Mmz5Ne82YCSjHcaRBuVwPzvnWlYdz65Wq/mpqm99M57TlY
+GgHMkRHJs2BRQVkJrijcPZs/cdPRli1owWmD/QIDAQABAoIBAFn2/argm2LK/9o2
+FrC7dUf/X0+GoFVh+kA0eLtGCA5AFe2H7srwJxT3y+xPC+LRdIRt/PV8MvL4lSIs
+/2/QZPHUTvsbRgXpILKM7DyiRgKS1ukLL93Qm69jwWzgoHVZ2afccQ/O2BTjPU+3
+mMj8ALdsyBUaDi3HTQPx5/uSDvcHsoIneIrecX0/I5Yi5+BoVQIkwOkZZsFtHvg6
+44Hf4sqhbB0f7PSSEFjdWceANjMoMZ/upBa6KgvYgYc2gBaU+aCsBC2g2zUY9waG
+pbVGMl61gSaD4IqcVCYSFWZkzpeIw2YHwmyFO1H5PCRzgVRYaE72alHmtDrP2cx+
+Ftc+naECgYEA0zQo8VVDWEvcUTIb5igIOGjTJZMBCpR2Y0M7Kh/qx9XfBguEKGiS
+KBYWDolweCeZ0tdFW8GTb3RwLLcLBnl/sge7ouvKUlk2iFNbAFipjwYUkAtcfX9a
+sPwR+JXbZF2LhBMZsw7dSwhWWjStGaAiXEFFeWqTdV/vwwVdtaiPrgkCgYEAzU7d
+VirIVsm7ps5L71zkZtf211o4LRysQS64oh6JUQPzcvC85yU3HtS2VNSCz2GiA1R/
+jqzGAL5q6k0UPBT4xzAwDmBJvtRjFr38FCCcl3Txeqjy7zkG/H0UpmQNMA1jjf/9
+iBRrR1vwjj79xyyl4PojpZmi6GVN2IIWrpf9o1UCgYBEbhf96XRCfYHKxQOJFNtk
++4G+IN0rgmLBUp0uztyRFtiF6uFM/mSsnEtVNm68X4hVae5NBnEwoXde5Yeq917K
+XfsLlH4fJEyo6ukHObLmZj/vU98JwmOuCF4CPvuwjyaPCmk/PMeycecYnwyeyuWX
+IobSChfw5b6XX3u3SgATkQKBgQCXq35J7LspmkhdlyNzxh0ZeMvrFcRQV1FNqhVN
+9t8ckZ2kuQHkhJKu3ReBnaixSYAlk6PUJAD2hbV4N88N/7Q1enzV8f4o0sANCfcS
+a3EjVooaQnuNjISDvGen8FvptspoGcgTYnpKMjqI6zIRlQNKK6Bv8wrtQgF7Q8c7
+3h7LLQKBgHQjFqVmM0a3WhgXGTWIhDALWXDgLcsVTQwGnUCafLgdP8wrjF1+tZf1
+UIw04p35FE2xlpMVkYRItYLIuQ4S8Q323rk2yIpTYYZApKYjT5BuNFFUinE5QsqV
+iXeCigYuKwOJ6Gi8c+lgFkxZnA3+rZJg9vdzp/yz4Xgy7gJ6QVBc
+-----END RSA PRIVATE KEY-----
diff --git a/examples/TLS/certs/cert.cmd b/examples/TLS/certs/cert.cmd
new file mode 100644
index 0000000..65e3567
--- /dev/null
+++ b/examples/TLS/certs/cert.cmd
@@ -0,0 +1,17 @@
+set OPATH=C:\Program Files\OpenSSL-Win64\bin
+
+rem CA
+"%OPATH%\openssl" genrsa -out ca_key.pem 2048
+"%OPATH%\openssl" req -x509 -new -nodes -key ca_key.pem -days 4096 -config ca.conf -out ca_cer.pem
+
+rem SERVER
+"%OPATH%\openssl" genrsa -out server_key.pem 2048
+"%OPATH%\openssl" req -out server_req.csr -key server_key.pem -new -config server.conf
+"%OPATH%\openssl" x509 -req -in server_req.csr -out server_cer.pem -sha256 -CAcreateserial -days 4000 -CA ca_cer.pem -CAkey ca_key.pem
+"%OPATH%\openssl" rsa -in server_key.pem -pubout -out server_pubkey.pem
+
+rem CLIENT
+"%OPATH%\openssl" genrsa -out client1_key.pem 2048
+"%OPATH%\openssl" req -out client1_req.csr -key client1_key.pem -new -config client.conf
+"%OPATH%\openssl" x509 -req -in client1_req.csr -out client1_cer.pem -sha256 -CAcreateserial -days 4000 -CA ca_cer.pem -CAkey ca_key.pem
+
diff --git a/examples/TLS/certs/client.cmd b/examples/TLS/certs/client.cmd
new file mode 100644
index 0000000..719b5f4
--- /dev/null
+++ b/examples/TLS/certs/client.cmd
@@ -0,0 +1 @@
+openssl s_client -showcerts -connect 192.168.30.123:802 -cert client1_cer.pem -key client1_key.pem -verifyCAfile ca_cer.pem
\ No newline at end of file
diff --git a/examples/TLS/certs/client.conf b/examples/TLS/certs/client.conf
new file mode 100644
index 0000000..522e43c
--- /dev/null
+++ b/examples/TLS/certs/client.conf
@@ -0,0 +1,7 @@
+[ req ]
+prompt = no
+default_bits = 2048
+distinguished_name = req_dn
+
+[ req_dn ]
+CN = client
diff --git a/examples/TLS/certs/client1_cer.pem b/examples/TLS/certs/client1_cer.pem
new file mode 100644
index 0000000..39a0460
--- /dev/null
+++ b/examples/TLS/certs/client1_cer.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICqjCCAZICFEIh1S7CewqVDZ9B78fSCkMQDkN9MA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB3Jvb3RfY2EwHhcNMjAwODI3MTAyODMxWhcNMzEwODEwMTAyODMx
+WjARMQ8wDQYDVQQDDAZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDIU4Czr45Jgj7TiUbF0MSs4ydC41R/rB2dSlwcbrmjmZHlJn2fifcVCBuQ
+H4a/SCbUNDOXz23p2NZHeLpcHV8TeuvDocmplJIuKNoN8BPbeZ+IS5yrvHBzC0S2
+bXs1gOkcWwmdD87NqQD8v7m+hZjBIBDvPAHPBXsEzkNNlqnye2mRYI8G0sGTqMWV
+zCt+m2mwJLAIwVCWYLVEn3sY/ksU6SrwyNKfnoCCw+0hfaMVdUq2u9wpDD2i9fKT
+yehSW727r5dOtGUbp3hoxiFWzAlaodvIk0eZ/12EMboc3y4WLax7W2vefNc9sKeM
+6jgYRoqz4YRgJLbZzk1tJk292521AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAeY
+SL7wIYQONK2uqhqb9MmbfOZznlaGz6kybB0GtVmZpvBaqZtCmTSOSbs/0YVF3OSv
++L9+kWTGsaWx/6t1fdiDG8DlZCqF3dwbmd0YmV2GYbpRF53rYSUETSsdO2g1Fs0a
+lvSVrQvhUj/cXvlTqtvjSVBELwFmlu0qhUHqN8Ap3dgy1YUZvRQcJS1GZ46iZLae
+SQYAANvfYXC4gBy1vfgKeDkZD4Qs+NnV6J+aFpXTYsmMMOS/lfLTpWP2tEfuaexW
+dGPlQ5dw7JZHcPrD9EdVIvDozACS0Y8B7oP4xvKFJnsqE7RmOsnukO0D7CQkxkBy
+hJmblVkcv6VRNS9JHDQ=
+-----END CERTIFICATE-----
diff --git a/examples/TLS/certs/client1_key.pem b/examples/TLS/certs/client1_key.pem
new file mode 100644
index 0000000..2427ca8
--- /dev/null
+++ b/examples/TLS/certs/client1_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAyFOAs6+OSYI+04lGxdDErOMnQuNUf6wdnUpcHG65o5mR5SZ9
+n4n3FQgbkB+Gv0gm1DQzl89t6djWR3i6XB1fE3rrw6HJqZSSLijaDfAT23mfiEuc
+q7xwcwtEtm17NYDpHFsJnQ/OzakA/L+5voWYwSAQ7zwBzwV7BM5DTZap8ntpkWCP
+BtLBk6jFlcwrfptpsCSwCMFQlmC1RJ97GP5LFOkq8MjSn56AgsPtIX2jFXVKtrvc
+KQw9ovXyk8noUlu9u6+XTrRlG6d4aMYhVswJWqHbyJNHmf9dhDG6HN8uFi2se1tr
+3nzXPbCnjOo4GEaKs+GEYCS22c5NbSZNvdudtQIDAQABAoIBAQCQQeGag795G/vW
+JTL73JzkyydIuZ/t2KnyzMuMBghU0ZAIbjFko9t0H8SJgspsEK81fOnyVoOWNHoK
+Odwp3VTMGGaTGHy6S60A5JYyF0KVd/30Dk8iNK7dia3PmQNywgQcUUqY+fs4io2V
+dRNzKY2Y9Vh8jr/WruGp0kcRJn/3hrR7S10UyWDbQYE7R3Hir7V0YMFWEbzgwhRE
+6MO6H5obFdZFxy7V+RJLeeq+dKHrvOmtd6F6hWSQUVX9YOVjh820IhhhC3F20EQw
+FTiVO9UfpmOzhtBp0vOBCWIHa5Yu+AXufrytfT//DClyiex+kfXrmS+OhZS/zqPf
+YjadqQF5AoGBAPXWUUD/jzkE82TPpwtRuIhZtF0kLedpBkzSg0e+Fgbdw4osRbMs
+13cXMucWW9wK0TikHeoCcq1N2xDRWGreNqolbj9KEqWG0D2LcTBm0pKXuhAT+bWQ
+hJmsiNEQYsM9hJLByLWNp3mwgzDLVjXDAxJgirP1L6Qw65SbQoYMt06bAoGBANCb
+i3T0A/YP6ounu2iGiEqrJTU/11zh+ykVSvHd4MpV+szex7pBRlXpkFE2iqElAoja
+xVrGsQCTebtJIz58Fy7tJQlTRqilRHCTRR60x+0ab7768OHZNKcSRXFDLVTdEyzv
+dKTIZh0IJfbz/DpwyNqTM0GYLhDXJfyJxu7YmeHvAoGAC8N1n+aas9/Ixcop9CC0
+89FXEB3rFGeyJXrtTUGLTEjQUoxLyYcbyFcT2Hr5ak4aNNulks0LL7/J+8QItxRr
+CTlBTUX+Hm2VCVziza4d5WXdQWezSzzfG3tmEJr4Ht+SuHMNZ6KfoPMRVARm26u5
+OefkuzfAT9sHatUDGecB3oECgYEAgWaLOlAHiQJUfq7cTLlvH8pMOVzRrfcsAk8H
+/0KgJ0LwYVcsU7gb9jz83bPUiKNZkCUM2QN5Vp8kmu2CZEc7ZkuKdt9mbESgUKi5
+7pM7lTOZ78Df3WkMBTsLQnfmTccZFv2uwGzjEs00J50vb9z4asV2vRC2OpILKT0Z
+3p0Tz5cCgYEAnFJ5HAVKlzi4k8l7v1SuDb9vCLIG+XQYBNrhW/3BLQftOyEKgK5O
+6fkNQT4u7Bgmzu3kpljq7jKBlPdW+l009L0gEEO5QBQLy64IquyydB1kiBIY5jGZ
+x7M6SvzBWzC7gzH/P94LxtqM22zzU0LszocV2j1UkxqBVXv0EYVjPB0=
+-----END RSA PRIVATE KEY-----
diff --git a/examples/TLS/certs/client1_req.csr b/examples/TLS/certs/client1_req.csr
new file mode 100644
index 0000000..cc79f2d
--- /dev/null
+++ b/examples/TLS/certs/client1_req.csr
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICVjCCAT4CAQAwETEPMA0GA1UEAwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAyFOAs6+OSYI+04lGxdDErOMnQuNUf6wdnUpcHG65o5mR
+5SZ9n4n3FQgbkB+Gv0gm1DQzl89t6djWR3i6XB1fE3rrw6HJqZSSLijaDfAT23mf
+iEucq7xwcwtEtm17NYDpHFsJnQ/OzakA/L+5voWYwSAQ7zwBzwV7BM5DTZap8ntp
+kWCPBtLBk6jFlcwrfptpsCSwCMFQlmC1RJ97GP5LFOkq8MjSn56AgsPtIX2jFXVK
+trvcKQw9ovXyk8noUlu9u6+XTrRlG6d4aMYhVswJWqHbyJNHmf9dhDG6HN8uFi2s
+e1tr3nzXPbCnjOo4GEaKs+GEYCS22c5NbSZNvdudtQIDAQABoAAwDQYJKoZIhvcN
+AQELBQADggEBAIvHh0usZM0QBJvUO1e/LRFrKrS/6dNQ7lkbqEIQxl9NRddak5ad
+QYy4aBSA1yR/T2TSqK8Tq3W1eRdvH62KZn0VqumzgtGfRoI6Xtp5pUrCWw1Bv3eX
+L1lGicBQezVmXn1vUyNn/+U8FUbPoSWIqPAjetoBEnxPDLEy72OYs5dnC5AC7bXN
+jT+pdYqQncy+ghtPgpc1WrHWVIcamhhyUuUcy69sa2eigBwfxh8lfOknUjUE69mI
+BafDkwu4DzpayDe/C/TatTan30jur2Dpr+fKiEtfag+15E+BXphdAOodhWLMzJKz
+xEvxo4UH/Uo/5dEmwSe7wdYZYIa5OFvTLhY=
+-----END CERTIFICATE REQUEST-----
diff --git a/examples/TLS/certs/server.cmd b/examples/TLS/certs/server.cmd
new file mode 100644
index 0000000..fe80478
--- /dev/null
+++ b/examples/TLS/certs/server.cmd
@@ -0,0 +1 @@
+openssl s_server -showcerts -accept 802 -cert server_cer.pem -key server_key.pem -verifyCAfile ca_cer.pem
\ No newline at end of file
diff --git a/examples/TLS/certs/server.conf b/examples/TLS/certs/server.conf
new file mode 100644
index 0000000..d8a56b8
--- /dev/null
+++ b/examples/TLS/certs/server.conf
@@ -0,0 +1,16 @@
+[ req ]
+prompt = no
+default_bits = 2048
+distinguished_name = req_dn
+req_extensions = v3_req
+x509_extensions = v3_req
+
+[ req_dn ]
+CN = modbustls
+
+[v3_req]
+# The extentions to add to a self-signed cert
+subjectKeyIdentifier = hash
+basicConstraints = critical,CA:false
+subjectAltName = IP:192.168.30.127
+keyUsage = critical,digitalSignature,keyEncipherment
\ No newline at end of file
diff --git a/examples/TLS/certs/server_cer.pem b/examples/TLS/certs/server_cer.pem
new file mode 100644
index 0000000..c6efd5e
--- /dev/null
+++ b/examples/TLS/certs/server_cer.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICrTCCAZUCFEIh1S7CewqVDZ9B78fSCkMQDkN8MA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB3Jvb3RfY2EwHhcNMjAwODI3MTAyODMxWhcNMzEwODEwMTAyODMx
+WjAUMRIwEAYDVQQDDAltb2RidXN0bHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDdKlwKryxSEBsHO1z5fr0f6aJX8PHD1Iftfka3PT285wRoNCcPa5eT
+o5dBSyJM9JgKpqGsdm2M7UBAJZAFBSgQi++pRuNsssza1uUre28T3PHV463Oma57
+mFfpIlKGfL/rVuUlqu4igNIgQT/wQJmxJO8tDrWaTjMz4VgCNkG4y1veeIpz7/Cy
+9S5CxEKBbibQncpUXyV4tTT9O37qze0Gr+d7frnyyTOtr80AwMMg1Pn61hZbku9E
+L/VE13oWVBBSXz0exHVG8X9Ne4uyuyG3HAWheglhQ7m2RkBxTkDVolSf6ec1Xgn3
+15BMCG1eBTAZKNdRRSr4+x60p47ReaWJAgMBAAEwDQYJKoZIhvcNAQELBQADggEB
+AGqz1benN8ygveD3F/XxCMgEPfI8WhYS3PQ6sPBE850TuQ+9OrHvue8q87/RfJBW
+Yllkyi2JHGuY2muMBJWGWTDHK72JwI69hIIwE9bGrvFUwAZjCbK9+gF6UIUDznNN
+bSHHSWTfkCMLFz0Q6XbhJvF2AX5dtmYL+AWqD8+G5ZIrbHgd/o4neA21DTYRfUt2
+0UQ0RAxKxf/BenJWr8IzvOxo0MHxi7JivgreCfxna1REZYxIwVFlh7O9KhB1QQBJ
+oezb4CS0Um9sQowFTZ3AtRxpv1u60ZjTeJwSIL00YEyZ0sEO3K4h3PTbh2VxSoQD
+D2fcSb1t+gWsvLVZCmNMbVo=
+-----END CERTIFICATE-----
diff --git a/examples/TLS/certs/server_key.pem b/examples/TLS/certs/server_key.pem
new file mode 100644
index 0000000..73eccb2
--- /dev/null
+++ b/examples/TLS/certs/server_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA3SpcCq8sUhAbBztc+X69H+miV/Dxw9SH7X5Gtz09vOcEaDQn
+D2uXk6OXQUsiTPSYCqahrHZtjO1AQCWQBQUoEIvvqUbjbLLM2tblK3tvE9zx1eOt
+zpmue5hX6SJShny/61blJaruIoDSIEE/8ECZsSTvLQ61mk4zM+FYAjZBuMtb3niK
+c+/wsvUuQsRCgW4m0J3KVF8leLU0/Tt+6s3tBq/ne3658skzra/NAMDDINT5+tYW
+W5LvRC/1RNd6FlQQUl89HsR1RvF/TXuLsrshtxwFoXoJYUO5tkZAcU5A1aJUn+nn
+NV4J99eQTAhtXgUwGSjXUUUq+PsetKeO0XmliQIDAQABAoIBAAEFxB0siCjs+CMF
+bD2fD2LJYr3DWGrOXb6EWfFY8CMickvFCfUxSyccl4NuxH7Ulqtd79trRMBlDGn/
+gnXzeybwbrA6qqyC+x175t1XmcDewaN6hQAyh7L8llN2nCkRBJYi9bZB3w37yHzr
+sE79DXjbMdvkeIR5HhV8UjrYY19mVxbJsTokbrDXJEuDvR1kIkYM10UnEqMdDRiV
+pSVFS3XkwNfCZHn/f2slcv1Piah8qfTseb5QPNouCZBW+PU5E1BuoFTH2ZtkzlZk
+L6lcg42Ameyn5G9w8Walz89zwUIe3sycdJJ8+tRCWA2a2Aj8MeFiLfLxXnTjPNLU
+nsVkuH0CgYEA/vbYyef3nzlmV4H+Rk9Rn3nGOeSTQj00avCUQS298bztEvFwcibM
+84GOg0j68n/IW6gd4qBJyGpjBYl9ggcSTYBXKEK5FgtO6fM+Rnvnds3/WO7cMz3z
+SzGIYranzcpaphj8H2xihW1OLsOUlb0+M71+6Y8P8Yui/mQIRRxHzMsCgYEA3hBc
+/lzxs0A/TiwLlCpMIBuadkAxc4zgFY1LYHMveCwCa1cp9lujL5NRVOd8VhmYfpDg
+MWAN3q7N/3dOu+hdMDoArXCXS5g5My3c6Ki87aSL1rmHyt9nYIBWZzjnwAii/zqs
+kcKaFoEYv1vCuIMDFg+S1f+dpwuYlNn2pcnmwHsCgYEAlm/1+CQbsmI+5ZE5BClX
+At7qPEyHKwVMAXFUOKURtyn/RDcbXu9P7Lnb6dDM6PrGsHYgtBBZmJxVMvYuDOO5
+Q+tfAc1kwgIIHPg+HX6MU0g2yzWczctW214tl/koR7+G/wws7ymXdBzLjcIu0K9p
+nUPJN2wHP0Fh+fHyAz0tjEMCgYB4R9C3Dkz01LX1d7IF3StCsPDnYDno5sNxqQjN
+A1cQ9nWRArN994DagicpoAEe+do5o+trkyWwGmsGFu+UpHXla2V2jGfG0HsbF5py
+gwNijSAZfIDrCDsMcDdczdvpjkQLjxJuGUQxMFfhPqioHH6Ncn4MX9pa4tMQvUb1
+4fiVBQKBgAOtTdRqX66vvcOsjJmr2f+fw5SRXc09hxLaCDNjC6jVbCUw0aj6WKlx
+sBfP8fvlHqJ6wA9/W2l+YiIf3G2jY2Z8OlOINs3hdXpH0JBzoeEiFwfcfZPAMFb1
+M4JURmEGAriH2lw/5iMQ/YqB9+NoE8t8lBLrhjwXWxN3qxoSruwe
+-----END RSA PRIVATE KEY-----
diff --git a/examples/TLS/certs/server_pubkey.pem b/examples/TLS/certs/server_pubkey.pem
new file mode 100644
index 0000000..06548c2
--- /dev/null
+++ b/examples/TLS/certs/server_pubkey.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3SpcCq8sUhAbBztc+X69
+H+miV/Dxw9SH7X5Gtz09vOcEaDQnD2uXk6OXQUsiTPSYCqahrHZtjO1AQCWQBQUo
+EIvvqUbjbLLM2tblK3tvE9zx1eOtzpmue5hX6SJShny/61blJaruIoDSIEE/8ECZ
+sSTvLQ61mk4zM+FYAjZBuMtb3niKc+/wsvUuQsRCgW4m0J3KVF8leLU0/Tt+6s3t
+Bq/ne3658skzra/NAMDDINT5+tYWW5LvRC/1RNd6FlQQUl89HsR1RvF/TXuLsrsh
+txwFoXoJYUO5tkZAcU5A1aJUn+nnNV4J99eQTAhtXgUwGSjXUUUq+PsetKeO0Xml
+iQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/examples/TLS/certs/server_req.csr b/examples/TLS/certs/server_req.csr
new file mode 100644
index 0000000..42b13d8
--- /dev/null
+++ b/examples/TLS/certs/server_req.csr
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbW9kYnVzdGxzMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEA3SpcCq8sUhAbBztc+X69H+miV/Dxw9SH7X5Gtz09
+vOcEaDQnD2uXk6OXQUsiTPSYCqahrHZtjO1AQCWQBQUoEIvvqUbjbLLM2tblK3tv
+E9zx1eOtzpmue5hX6SJShny/61blJaruIoDSIEE/8ECZsSTvLQ61mk4zM+FYAjZB
+uMtb3niKc+/wsvUuQsRCgW4m0J3KVF8leLU0/Tt+6s3tBq/ne3658skzra/NAMDD
+INT5+tYWW5LvRC/1RNd6FlQQUl89HsR1RvF/TXuLsrshtxwFoXoJYUO5tkZAcU5A
+1aJUn+nnNV4J99eQTAhtXgUwGSjXUUUq+PsetKeO0XmliQIDAQABoAAwDQYJKoZI
+hvcNAQELBQADggEBAHNXxhHJZt64+Ot1ekZ/VaQcitt/MwOW3kpN+yPIN6iTFSb2
+fXZkrlRG+TIU4hTnJ85HgsoK1hB/9GqEvJ2zerxeMeXH3QFm9jy+bzVJ6vR/hWPH
+e4UI2u78w1kY1Z51xNBhIQQ4FJKb+iV1IsijE2sp+mpGbSQKQihG/FBOrxKUAV1q
+GomcPTE40XXm6O9TsnnK9AnQCb3AsiZPC/Dm4+xoLwebf92wUPAzPbP74e/AL2ti
+PDt3NTb6JCNMoXlhRnroGzOtigvRHF54GAEyLdwpSi1gLfQV1z6uBlU6vZIDOJFm
+l0LJMHWDCxewfU+IjDYa77W+/8ApwGcEOF01e3g=
+-----END CERTIFICATE REQUEST-----
diff --git a/examples/TLS/client/client.ino b/examples/TLS/client/client.ino
new file mode 100644
index 0000000..1186a66
--- /dev/null
+++ b/examples/TLS/client/client.ino
@@ -0,0 +1,161 @@
+/*
+ Modbus-Uni - Most complete Modbus Library for Arduino
+
+ Modbus/TCP Security Client for ESP8266 Example
+
+ (c)2020 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+ This code is licensed under the BSD New License. See LICENSE.txt for more info.
+*/
+
+#if defined(ESP8266)
+#include
+#elif defined(ESP32)
+#include
+#else
+#error Platform is not supported
+#endif
+#include
+#include
+
+// The hardcoded certificate authority for this example.
+// Don't use it on your own apps!!!!!
+const char ca_cert[] PROGMEM = R"EOF(
+-----BEGIN CERTIFICATE-----
+MIICwjCCAaqgAwIBAgIUTz9NFtf8JkdIkrDroXVB/ANtqlYwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHcm9vdF9jYTAeFw0yMDA4MjcxMDI4MzFaFw0zMTExMTQx
+MDI4MzFaMBIxEDAOBgNVBAMMB3Jvb3RfY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQCpYdVI2PjW+Pdw5bMqxFz0s3jgYgTHyt51NJGlImgJpmjmj16T
+rwcqAe70BtsSjOQeWRoF/rk46ZO/ntDbVkP8ZA40Vf8F8Yft64f1OOBf93rTR0sH
+oUk+HmE3Iu+bWYSewNMw/LJyF2r95V2xNeX50Y+BhQskBoWYR7C671ifFlsQHI+a
+/BpALEi7qt6kGenlhrmRAjweNxVNILHTPH7Fr/TYXWfAb69TzXWTUFy0bdwZfPIP
+b2HXyGINGiD6EtZDkybPk17zZgJKMdxpEG5XA/O+daVh3Prlar+amqb30zntOVga
+AcyREcmzYFFBWQmuKNw9mz9x09GWLWjBaYP9AgMBAAGjEDAOMAwGA1UdEwQFMAMB
+Af8wDQYJKoZIhvcNAQELBQADggEBAIFtNowXu8wfahpKF5MoNKrqA9AG/aGzlmbD
+pBKr5Cvvo7NdC6oZMdXlS0VkRmAyw9mEJgwspUvqsLx0/lgN+sE381MUovWL9BIs
+o4IOax5473q6ZwV87jwpsrlNHBiolw+WCDKVYuDktaThCaxqxmPKCPMbgYPdqWB0
+l1gYDJJ+MwNH/CRsynpM8Hppf88BwwbM6JYegg5/DLxRl5z3HjCAVD8vBoqkWLRD
+b9tIER4WDJhZG4tzgMW+lbMJyDoQA1cw4BGag4Ir1er32+w1519UR/VK0ltk9BK9
+yHObfUNN6saco1/f4OM4tzaQOKa+6U1iXVBTBjE2IHPchGqctBk=
+-----END CERTIFICATE-----
+)EOF";
+
+// The client's private key which must be kept secret
+const char client_private_key[] PROGMEM = R"EOF(
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAyFOAs6+OSYI+04lGxdDErOMnQuNUf6wdnUpcHG65o5mR5SZ9
+n4n3FQgbkB+Gv0gm1DQzl89t6djWR3i6XB1fE3rrw6HJqZSSLijaDfAT23mfiEuc
+q7xwcwtEtm17NYDpHFsJnQ/OzakA/L+5voWYwSAQ7zwBzwV7BM5DTZap8ntpkWCP
+BtLBk6jFlcwrfptpsCSwCMFQlmC1RJ97GP5LFOkq8MjSn56AgsPtIX2jFXVKtrvc
+KQw9ovXyk8noUlu9u6+XTrRlG6d4aMYhVswJWqHbyJNHmf9dhDG6HN8uFi2se1tr
+3nzXPbCnjOo4GEaKs+GEYCS22c5NbSZNvdudtQIDAQABAoIBAQCQQeGag795G/vW
+JTL73JzkyydIuZ/t2KnyzMuMBghU0ZAIbjFko9t0H8SJgspsEK81fOnyVoOWNHoK
+Odwp3VTMGGaTGHy6S60A5JYyF0KVd/30Dk8iNK7dia3PmQNywgQcUUqY+fs4io2V
+dRNzKY2Y9Vh8jr/WruGp0kcRJn/3hrR7S10UyWDbQYE7R3Hir7V0YMFWEbzgwhRE
+6MO6H5obFdZFxy7V+RJLeeq+dKHrvOmtd6F6hWSQUVX9YOVjh820IhhhC3F20EQw
+FTiVO9UfpmOzhtBp0vOBCWIHa5Yu+AXufrytfT//DClyiex+kfXrmS+OhZS/zqPf
+YjadqQF5AoGBAPXWUUD/jzkE82TPpwtRuIhZtF0kLedpBkzSg0e+Fgbdw4osRbMs
+13cXMucWW9wK0TikHeoCcq1N2xDRWGreNqolbj9KEqWG0D2LcTBm0pKXuhAT+bWQ
+hJmsiNEQYsM9hJLByLWNp3mwgzDLVjXDAxJgirP1L6Qw65SbQoYMt06bAoGBANCb
+i3T0A/YP6ounu2iGiEqrJTU/11zh+ykVSvHd4MpV+szex7pBRlXpkFE2iqElAoja
+xVrGsQCTebtJIz58Fy7tJQlTRqilRHCTRR60x+0ab7768OHZNKcSRXFDLVTdEyzv
+dKTIZh0IJfbz/DpwyNqTM0GYLhDXJfyJxu7YmeHvAoGAC8N1n+aas9/Ixcop9CC0
+89FXEB3rFGeyJXrtTUGLTEjQUoxLyYcbyFcT2Hr5ak4aNNulks0LL7/J+8QItxRr
+CTlBTUX+Hm2VCVziza4d5WXdQWezSzzfG3tmEJr4Ht+SuHMNZ6KfoPMRVARm26u5
+OefkuzfAT9sHatUDGecB3oECgYEAgWaLOlAHiQJUfq7cTLlvH8pMOVzRrfcsAk8H
+/0KgJ0LwYVcsU7gb9jz83bPUiKNZkCUM2QN5Vp8kmu2CZEc7ZkuKdt9mbESgUKi5
+7pM7lTOZ78Df3WkMBTsLQnfmTccZFv2uwGzjEs00J50vb9z4asV2vRC2OpILKT0Z
+3p0Tz5cCgYEAnFJ5HAVKlzi4k8l7v1SuDb9vCLIG+XQYBNrhW/3BLQftOyEKgK5O
+6fkNQT4u7Bgmzu3kpljq7jKBlPdW+l009L0gEEO5QBQLy64IquyydB1kiBIY5jGZ
+x7M6SvzBWzC7gzH/P94LxtqM22zzU0LszocV2j1UkxqBVXv0EYVjPB0=
+-----END RSA PRIVATE KEY-----
+)EOF";
+
+// The server's public certificate which must be shared
+const char client_cert[] PROGMEM = R"EOF(
+-----BEGIN CERTIFICATE-----
+MIICqjCCAZICFEIh1S7CewqVDZ9B78fSCkMQDkN9MA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB3Jvb3RfY2EwHhcNMjAwODI3MTAyODMxWhcNMzEwODEwMTAyODMx
+WjARMQ8wDQYDVQQDDAZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDIU4Czr45Jgj7TiUbF0MSs4ydC41R/rB2dSlwcbrmjmZHlJn2fifcVCBuQ
+H4a/SCbUNDOXz23p2NZHeLpcHV8TeuvDocmplJIuKNoN8BPbeZ+IS5yrvHBzC0S2
+bXs1gOkcWwmdD87NqQD8v7m+hZjBIBDvPAHPBXsEzkNNlqnye2mRYI8G0sGTqMWV
+zCt+m2mwJLAIwVCWYLVEn3sY/ksU6SrwyNKfnoCCw+0hfaMVdUq2u9wpDD2i9fKT
+yehSW727r5dOtGUbp3hoxiFWzAlaodvIk0eZ/12EMboc3y4WLax7W2vefNc9sKeM
+6jgYRoqz4YRgJLbZzk1tJk292521AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAeY
+SL7wIYQONK2uqhqb9MmbfOZznlaGz6kybB0GtVmZpvBaqZtCmTSOSbs/0YVF3OSv
++L9+kWTGsaWx/6t1fdiDG8DlZCqF3dwbmd0YmV2GYbpRF53rYSUETSsdO2g1Fs0a
+lvSVrQvhUj/cXvlTqtvjSVBELwFmlu0qhUHqN8Ap3dgy1YUZvRQcJS1GZ46iZLae
+SQYAANvfYXC4gBy1vfgKeDkZD4Qs+NnV6J+aFpXTYsmMMOS/lfLTpWP2tEfuaexW
+dGPlQ5dw7JZHcPrD9EdVIvDozACS0Y8B7oP4xvKFJnsqE7RmOsnukO0D7CQkxkBy
+hJmblVkcv6VRNS9JHDQ=
+-----END CERTIFICATE-----
+)EOF";
+
+// The server's public certificate which must be shared
+const char server_pk[] PROGMEM = R"EOF(
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3SpcCq8sUhAbBztc+X69
+H+miV/Dxw9SH7X5Gtz09vOcEaDQnD2uXk6OXQUsiTPSYCqahrHZtjO1AQCWQBQUo
+EIvvqUbjbLLM2tblK3tvE9zx1eOtzpmue5hX6SJShny/61blJaruIoDSIEE/8ECZ
+sSTvLQ61mk4zM+FYAjZBuMtb3niKc+/wsvUuQsRCgW4m0J3KVF8leLU0/Tt+6s3t
+Bq/ne3658skzra/NAMDDINT5+tYWW5LvRC/1RNd6FlQQUl89HsR1RvF/TXuLsrsh
+txwFoXoJYUO5tkZAcU5A1aJUn+nnNV4J99eQTAhtXgUwGSjXUUUq+PsetKeO0Xml
+iQIDAQAB
+-----END PUBLIC KEY-----
+)EOF";
+
+//Modbus Registers Offsets
+const int LED_COIL = 100;
+
+//ModbusIP object
+ModbusTLS mb;
+// Set time via NTP, as required for x.509 validation
+void setClock()
+{
+ configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
+
+ Serial.print("Waiting for NTP time sync: ");
+ time_t now = time(nullptr);
+ while (now < 8 * 3600 * 2) {
+ delay(500);
+ Serial.print(".");
+ now = time(nullptr);
+ }
+ Serial.println("");
+ struct tm timeinfo;
+ gmtime_r(&now, &timeinfo);
+ Serial.print("Current time: ");
+ Serial.print(asctime(&timeinfo));
+}
+//IPAddress remote(192,168,30,127);
+char* remote = "modbustls";
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("E2", "fOlissio92");
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+ setClock();
+ mb.client();
+}
+uint16_t v;
+void loop() {
+ if (!mb.isConnected(remote)) {
+ delay(1000);
+ //mb.connectWithKnownKey(remote, MODBUSTLS_PORT, client_cert, client_private_key, server_pk);
+ mb.connect(remote, MODBUSTLS_PORT, client_cert, client_private_key, ca_cert);
+ Serial.print(".");
+ } else {
+ mb.readHreg(remote, 0, &v);
+ mb.task();
+ delay(100);
+ }
+}
\ No newline at end of file
diff --git a/examples/TLS/server/server.ino b/examples/TLS/server/server.ino
new file mode 100644
index 0000000..226d3cb
--- /dev/null
+++ b/examples/TLS/server/server.ino
@@ -0,0 +1,142 @@
+/*
+ Modbus-Uni - Most complete Modbus Library for Arduino
+
+ Modbus/TCP Security Server for ESP8266 Example
+
+ (c)2020 Alexander Emelianov (a.m.emelianov@gmail.com)
+ https://github.com/emelianov/modbus-esp8266
+ This code is licensed under the BSD New License. See LICENSE.txt for more info.
+*/
+
+#include
+#include
+#include
+
+// The hardcoded certificate authority for this example.
+// Don't use it on your own apps!!!!!
+
+const char ca_cert[] PROGMEM = R"EOF(
+-----BEGIN CERTIFICATE-----
+MIICwjCCAaqgAwIBAgIUTz9NFtf8JkdIkrDroXVB/ANtqlYwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHcm9vdF9jYTAeFw0yMDA4MjcxMDI4MzFaFw0zMTExMTQx
+MDI4MzFaMBIxEDAOBgNVBAMMB3Jvb3RfY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQCpYdVI2PjW+Pdw5bMqxFz0s3jgYgTHyt51NJGlImgJpmjmj16T
+rwcqAe70BtsSjOQeWRoF/rk46ZO/ntDbVkP8ZA40Vf8F8Yft64f1OOBf93rTR0sH
+oUk+HmE3Iu+bWYSewNMw/LJyF2r95V2xNeX50Y+BhQskBoWYR7C671ifFlsQHI+a
+/BpALEi7qt6kGenlhrmRAjweNxVNILHTPH7Fr/TYXWfAb69TzXWTUFy0bdwZfPIP
+b2HXyGINGiD6EtZDkybPk17zZgJKMdxpEG5XA/O+daVh3Prlar+amqb30zntOVga
+AcyREcmzYFFBWQmuKNw9mz9x09GWLWjBaYP9AgMBAAGjEDAOMAwGA1UdEwQFMAMB
+Af8wDQYJKoZIhvcNAQELBQADggEBAIFtNowXu8wfahpKF5MoNKrqA9AG/aGzlmbD
+pBKr5Cvvo7NdC6oZMdXlS0VkRmAyw9mEJgwspUvqsLx0/lgN+sE381MUovWL9BIs
+o4IOax5473q6ZwV87jwpsrlNHBiolw+WCDKVYuDktaThCaxqxmPKCPMbgYPdqWB0
+l1gYDJJ+MwNH/CRsynpM8Hppf88BwwbM6JYegg5/DLxRl5z3HjCAVD8vBoqkWLRD
+b9tIER4WDJhZG4tzgMW+lbMJyDoQA1cw4BGag4Ir1er32+w1519UR/VK0ltk9BK9
+yHObfUNN6saco1/f4OM4tzaQOKa+6U1iXVBTBjE2IHPchGqctBk=
+-----END CERTIFICATE-----
+)EOF";
+
+// The server's private key which must be kept secret
+const char server_private_key[] PROGMEM = R"EOF(
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA3SpcCq8sUhAbBztc+X69H+miV/Dxw9SH7X5Gtz09vOcEaDQn
+D2uXk6OXQUsiTPSYCqahrHZtjO1AQCWQBQUoEIvvqUbjbLLM2tblK3tvE9zx1eOt
+zpmue5hX6SJShny/61blJaruIoDSIEE/8ECZsSTvLQ61mk4zM+FYAjZBuMtb3niK
+c+/wsvUuQsRCgW4m0J3KVF8leLU0/Tt+6s3tBq/ne3658skzra/NAMDDINT5+tYW
+W5LvRC/1RNd6FlQQUl89HsR1RvF/TXuLsrshtxwFoXoJYUO5tkZAcU5A1aJUn+nn
+NV4J99eQTAhtXgUwGSjXUUUq+PsetKeO0XmliQIDAQABAoIBAAEFxB0siCjs+CMF
+bD2fD2LJYr3DWGrOXb6EWfFY8CMickvFCfUxSyccl4NuxH7Ulqtd79trRMBlDGn/
+gnXzeybwbrA6qqyC+x175t1XmcDewaN6hQAyh7L8llN2nCkRBJYi9bZB3w37yHzr
+sE79DXjbMdvkeIR5HhV8UjrYY19mVxbJsTokbrDXJEuDvR1kIkYM10UnEqMdDRiV
+pSVFS3XkwNfCZHn/f2slcv1Piah8qfTseb5QPNouCZBW+PU5E1BuoFTH2ZtkzlZk
+L6lcg42Ameyn5G9w8Walz89zwUIe3sycdJJ8+tRCWA2a2Aj8MeFiLfLxXnTjPNLU
+nsVkuH0CgYEA/vbYyef3nzlmV4H+Rk9Rn3nGOeSTQj00avCUQS298bztEvFwcibM
+84GOg0j68n/IW6gd4qBJyGpjBYl9ggcSTYBXKEK5FgtO6fM+Rnvnds3/WO7cMz3z
+SzGIYranzcpaphj8H2xihW1OLsOUlb0+M71+6Y8P8Yui/mQIRRxHzMsCgYEA3hBc
+/lzxs0A/TiwLlCpMIBuadkAxc4zgFY1LYHMveCwCa1cp9lujL5NRVOd8VhmYfpDg
+MWAN3q7N/3dOu+hdMDoArXCXS5g5My3c6Ki87aSL1rmHyt9nYIBWZzjnwAii/zqs
+kcKaFoEYv1vCuIMDFg+S1f+dpwuYlNn2pcnmwHsCgYEAlm/1+CQbsmI+5ZE5BClX
+At7qPEyHKwVMAXFUOKURtyn/RDcbXu9P7Lnb6dDM6PrGsHYgtBBZmJxVMvYuDOO5
+Q+tfAc1kwgIIHPg+HX6MU0g2yzWczctW214tl/koR7+G/wws7ymXdBzLjcIu0K9p
+nUPJN2wHP0Fh+fHyAz0tjEMCgYB4R9C3Dkz01LX1d7IF3StCsPDnYDno5sNxqQjN
+A1cQ9nWRArN994DagicpoAEe+do5o+trkyWwGmsGFu+UpHXla2V2jGfG0HsbF5py
+gwNijSAZfIDrCDsMcDdczdvpjkQLjxJuGUQxMFfhPqioHH6Ncn4MX9pa4tMQvUb1
+4fiVBQKBgAOtTdRqX66vvcOsjJmr2f+fw5SRXc09hxLaCDNjC6jVbCUw0aj6WKlx
+sBfP8fvlHqJ6wA9/W2l+YiIf3G2jY2Z8OlOINs3hdXpH0JBzoeEiFwfcfZPAMFb1
+M4JURmEGAriH2lw/5iMQ/YqB9+NoE8t8lBLrhjwXWxN3qxoSruwe
+-----END RSA PRIVATE KEY-----
+)EOF";
+
+// The server's public certificate which must be shared
+const char server_cert[] PROGMEM = R"EOF(
+-----BEGIN CERTIFICATE-----
+MIICrTCCAZUCFEIh1S7CewqVDZ9B78fSCkMQDkN8MA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB3Jvb3RfY2EwHhcNMjAwODI3MTAyODMxWhcNMzEwODEwMTAyODMx
+WjAUMRIwEAYDVQQDDAltb2RidXN0bHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDdKlwKryxSEBsHO1z5fr0f6aJX8PHD1Iftfka3PT285wRoNCcPa5eT
+o5dBSyJM9JgKpqGsdm2M7UBAJZAFBSgQi++pRuNsssza1uUre28T3PHV463Oma57
+mFfpIlKGfL/rVuUlqu4igNIgQT/wQJmxJO8tDrWaTjMz4VgCNkG4y1veeIpz7/Cy
+9S5CxEKBbibQncpUXyV4tTT9O37qze0Gr+d7frnyyTOtr80AwMMg1Pn61hZbku9E
+L/VE13oWVBBSXz0exHVG8X9Ne4uyuyG3HAWheglhQ7m2RkBxTkDVolSf6ec1Xgn3
+15BMCG1eBTAZKNdRRSr4+x60p47ReaWJAgMBAAEwDQYJKoZIhvcNAQELBQADggEB
+AGqz1benN8ygveD3F/XxCMgEPfI8WhYS3PQ6sPBE850TuQ+9OrHvue8q87/RfJBW
+Yllkyi2JHGuY2muMBJWGWTDHK72JwI69hIIwE9bGrvFUwAZjCbK9+gF6UIUDznNN
+bSHHSWTfkCMLFz0Q6XbhJvF2AX5dtmYL+AWqD8+G5ZIrbHgd/o4neA21DTYRfUt2
+0UQ0RAxKxf/BenJWr8IzvOxo0MHxi7JivgreCfxna1REZYxIwVFlh7O9KhB1QQBJ
+oezb4CS0Um9sQowFTZ3AtRxpv1u60ZjTeJwSIL00YEyZ0sEO3K4h3PTbh2VxSoQD
+D2fcSb1t+gWsvLVZCmNMbVo=
+-----END CERTIFICATE-----
+)EOF";
+
+
+//Modbus Registers Offsets
+const int LED_COIL = 100;
+
+//ModbusIP object
+ModbusTLS mb;
+// Set time via NTP, as required for x.509 validation
+void setClock()
+{
+
+ configTime(5 * 3600, 0, "pool.ntp.org", "time.nist.gov");
+
+ Serial.print("Waiting for NTP time sync: ");
+ time_t now = time(nullptr);
+ while (now < 8 * 3600 * 2) {
+ delay(500);
+ Serial.print(".");
+ now = time(nullptr);
+ }
+
+ Serial.println("");
+ struct tm timeinfo;
+ gmtime_r(&now, &timeinfo);
+ Serial.print("Current time: ");
+ Serial.print(asctime(&timeinfo));
+}
+ bool c(IPAddress ip) {
+ Serial.println(ip);
+ return true;
+ }
+void setup() {
+ Serial.begin(115200);
+
+ WiFi.begin("E2", "fOlissio92");
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(".");
+ }
+ Serial.println("");
+ Serial.println("WiFi connected");
+ Serial.println("IP address: ");
+ Serial.println(WiFi.localIP());
+ setClock();
+ mb.server(MODBUSTLS_PORT, server_cert, server_private_key, ca_cert);
+
+ mb.addHreg(0);
+}
+
+void loop() {
+ mb.task();
+ delay(100);
+}
\ No newline at end of file
diff --git a/expressifSDK/In development.txt b/expressifSDK/In development.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/keywords.txt b/keywords.txt
new file mode 100644
index 0000000..38bc2f4
--- /dev/null
+++ b/keywords.txt
@@ -0,0 +1,135 @@
+# Syntax Coloring Map For ModbusIP-ESP8266
+
+# Datatypes (KEYWORD1)
+ModbusRTU KEYWORD1
+ModbusIP KEYWORD1
+ModbusTCP KEYWORD1
+ModbusIP_ESP8266 KEYWORD1
+Modbus KEYWORD1
+TRegister KEYWORD1
+TTransaction KEYWORD1
+TAddress KEYWORD1
+ResultCode KEYWORD1
+
+# Methods and Functions (KEYWORD2)
+client KEYWORD2
+server KEYWORD2
+task KEYWORD2
+onConnect KEYWORD2
+onDisconnect KEYWORD2
+cbEnable KEYWORD2
+cbDisable KEYWORD2
+eventSource KEYWORD2
+onGetCoil KEYWORD2
+onSetCoil KEYWORD2
+onGetHreg KEYWORD2
+onSetHreg KEYWORD2
+onGetIreg KEYWORD2
+onSetIreg KEYWORD2
+onGetIsts KEYWORD2
+onSetIsts KEYWORD2
+removeOnGetCoil KEYWORD2
+removeOnSetCoil KEYWORD2
+removeOnGetHreg KEYWORD2
+removeOnSetHreg KEYWORD2
+removeOnGetIsts KEYWORD2
+removeOnSetIsts KEYWORD2
+removeOnGetIreg KEYWORD2
+removeOnSetIreg KEYWORD2
+addCoil KEYWORD2
+addIsts KEYWORD2
+addIreg KEYWORD2
+addHreg KEYWORD2
+Coil KEYWORD2
+Ists KEYWORD2
+Ireg KEYWORD2
+Hreg KEYWORD2
+isTransaction KEYWORD2
+isConnected KEYWORD2
+writeCoil KEYWORD2
+readCoil KEYWORD2
+writeHreg KEYWORD2
+readHreg KEYWORD2
+readIsts KEYWORD2
+readIreg KEYWORD2
+pushCoil KEYWORD2
+pullCoil KEYWORD2
+pullIsts KEYWORD2
+pushHreg KEYWORD2
+pullHreg KEYWORD2
+pullIreg KEYWORD2
+pullHregToIreg KEYWORD2
+pullCoilToIsts KEYWORD2
+pushIstsToCoil KEYWORD2
+pushIregToHreg KEYWORD2
+removeHreg KEYWORD2
+removeIreg KEYWORD2
+removeCoil KEYWORD2
+removeIsts KEYWORD2
+autoConnect KEYWORD2
+disconnect KEYWORD2
+dropTransactions KEYWORD2
+isCoil KEYWORD2
+isHreg KEYWORD2
+isIsts KEYWORD2
+isIreg KEYWORD2
+begin KEYWORD2
+setBaudrate KEYWORD2
+readFileRec KEYWORD2
+writeFileRec KEYWORD2
+maskHreg KEYWORD2
+readWriteHreg KEYWORD2
+onFile KEYWORD2
+onRequest KEYWORD2
+onRequestSuccess KEYWORD2
+onSet KEYWORD2
+onGet KEYWORD2
+removeOnSet KEYWORD2
+removeOnGet KEYWORD2
+Reg KEYWORD2
+addReg KEYWORD2
+removeReg KEYWORD2
+
+# Constants and Macros (LITERAL1)
+BIT_VAL LITERAL1
+BIT_BOOL LITERAL1
+COIL_VAL LITERAL1
+COIL_BOOL LITERAL1
+ISTS_VAL LITERAL1
+ISTS_BOOL LITERAL1
+ResultCode LITERAL1
+FunctionCode LITERAL1
+EX_SUCCESS LITERAL1
+EX_ILLEGAL_FUNCTION LITERAL1
+EX_ILLEGAL_ADDRESS LITERAL1
+EX_ILLEGAL_VALUE LITERAL1
+EX_SLAVE_FAILURE LITERAL1
+EX_ACKNOWLEDGE LITERAL1
+EX_SLAVE_DEVICE_BUSY LITERAL1
+EX_MEMORY_PARITY_ERROR LITERAL1
+EX_PATH_UNAVAILABLE LITERAL1
+EX_DEVICE_FAILED_TO_RESPOND LITERAL1
+EX_GENERAL_FAILURE LITERAL1
+EX_DATA_MISMACH LITERAL1
+EX_UNEXPECTED_RESPONSE LITERAL1
+EX_TIMEOUT LITERAL1
+EX_CONNECTION_LOST LITERAL1
+EX_CANCEL LITERAL1
+COIL LITERAL1
+HREG LITERAL1
+ISTS LITERAL1
+IREG LITERAL1
+FC_READ_COILS LITERAL1
+FC_READ_INPUT_STAT LITERAL1
+FC_READ_REGS LITERAL1
+FC_READ_INPUT_REGS LITERAL1
+FC_WRITE_COIL LITERAL1
+FC_WRITE_REG LITERAL1
+FC_DIAGNOSTICS LITERAL1
+FC_WRITE_COILS LITERAL1
+FC_WRITE_REGS LITERAL1
+FC_READ_FILE_REC LITERAL1
+FC_WRITE_FILE_REC LITERAL1
+FC_MASKWRITE_REG LITERAL1
+FC_READWRITE_REGS LITERAL1
+
diff --git a/library.properties b/library.properties
new file mode 100644
index 0000000..765f44a
--- /dev/null
+++ b/library.properties
@@ -0,0 +1,9 @@
+name=modbus-esp8266
+version=4.1.0
+author=Andre Sarmento Barbosa, Alexander Emelianov
+maintainer=Alexander Emelianov
+sentence=Modbus Library for Arduino. ModbusRTU, ModbusTCP and ModbusTCP Security
+paragraph=Most complete Modbus protocol implementation for Arduino. The Modbus is a master-slave protocol used in industrial automation and also can be used in other areas, such as home automation.
+category=Communication
+url=https://github.com/emelianov/modbus-esp8266
+architectures=*
diff --git a/resources/client.png b/resources/client.png
new file mode 100644
index 0000000..5d6b825
Binary files /dev/null and b/resources/client.png differ
diff --git a/resources/client.uml b/resources/client.uml
new file mode 100644
index 0000000..3f0b538
--- /dev/null
+++ b/resources/client.uml
@@ -0,0 +1,21 @@
+@startuml
+!pragma useVerticalIf on
+start
+partition readHreg() {
+if (no request is alreaty running) then (yes)
+ :readHreg;
+endif
+}
+while (request is active)
+partition task() {
+if (responce arrived) then (yes)
+ #palegreen:Execute Transactional Callback;
+ :fill result data;
+endif
+if (responce timeout) then (yes)
+ #palegreen:Execute Transactional Callback;
+endif
+}
+endwhile (no request)
+stop
+@enduml
\ No newline at end of file
diff --git a/resources/server.png b/resources/server.png
new file mode 100644
index 0000000..f8082e9
Binary files /dev/null and b/resources/server.png differ
diff --git a/resources/server.uml b/resources/server.uml
new file mode 100644
index 0000000..d4c03a7
--- /dev/null
+++ b/resources/server.uml
@@ -0,0 +1,26 @@
+@startuml
+!pragma useVerticalIf on
+start
+partition task() {
+if (valid request arrived) then (yes)
+ #palegreen:Execute onRequest Callback;
+ if (EX_SUCCESS) then (yes)
+ if (correct request parameters) then (yes)
+ :Preapre responce;
+ #palegreen:Execute onGet/onSet Callback for each register in request;
+ #palegreen:Execute onRequestSuccess Callback;
+ else (no)
+ #pink:Return error code;
+ stop
+ endif
+ else (no)
+ #pink:Return error code;
+ stop
+ endif
+else
+ #pink:Return error code;
+ stop
+endif
+}
+stop
+@enduml
\ No newline at end of file
diff --git a/src/Modbus.cpp b/src/Modbus.cpp
new file mode 100644
index 0000000..66f9f98
--- /dev/null
+++ b/src/Modbus.cpp
@@ -0,0 +1,931 @@
+/*
+ Modbus Library for Arduino
+ Core functions
+ Copyright (C) 2014 Andr� Sarmento Barbosa
+ 2017-2023 Alexander Emelianov (a.m.emelianov@gmail.com)
+*/
+#include "Modbus.h"
+
+#if defined(MODBUS_GLOBAL_REGS)
+#if defined(MODBUS_USE_STL)
+ std::vector Modbus::_regs;
+ std::vector Modbus::_callbacks;
+ #if defined(MODBUS_FILES)
+ std::function Modbus::_onFile;
+ #endif
+#else
+ DArray Modbus::_regs;
+ DArray Modbus::_callbacks;
+ #if defined(MODBUS_FILES)
+ cbModbusFileOp Modbus::_onFile = nullptr;
+ #endif
+#endif
+#endif
+
+uint16_t Modbus::callback(TRegister* reg, uint16_t val, TCallback::CallbackType t) {
+#define MODBUS_COMPARE_CB [reg, t](TCallback& cb){return cb.address == reg->address && cb.type == t;}
+ uint16_t newVal = val;
+#if defined(MODBUS_USE_STL)
+ std::vector::iterator it = _callbacks.begin();
+ do {
+ it = std::find_if(it, _callbacks.end(), MODBUS_COMPARE_CB);
+ if (it != _callbacks.end()) {
+ newVal = it->cb(reg, newVal);
+ it++;
+ }
+ } while (it != _callbacks.end());
+#else
+ size_t r = 0;
+ do {
+ r = _callbacks.find(MODBUS_COMPARE_CB, r);
+ if (r < _callbacks.size())
+ newVal = _callbacks[r].cb(reg, newVal);
+ r++;
+ } while (r < _callbacks.size());
+#endif
+ return newVal;
+}
+
+TRegister* Modbus::searchRegister(TAddress address) {
+#define MODBUS_COMPARE_REG [address](TRegister& addr){return (addr.address == address);}
+#if defined(MODBUS_USE_STL)
+ std::vector::iterator it = std::find_if(_regs.begin(), _regs.end(), MODBUS_COMPARE_REG);
+ if (it != _regs.end()) return &*it;
+#else
+ size_t r = _regs.find(MODBUS_COMPARE_REG);
+ if (r < _regs.size()) return _regs.entry(r);
+#endif
+ return nullptr;
+}
+
+bool Modbus::addReg(TAddress address, uint16_t value, uint16_t numregs) {
+ #if defined(MODBUS_MAX_REGS)
+ if (_regs.size() + numregs > MODBUS_MAX_REGS) return false;
+ #endif
+ if (0xFFFF - address.address < numregs)
+ numregs = 0xFFFF - address.address;
+ for (uint16_t i = 0; i < numregs; i++) {
+ if (!searchRegister(address + i))
+ _regs.push_back({address + i, value});
+ }
+ //std::sort(_regs.begin(), _regs.end());
+ return true;
+}
+
+bool Modbus::Reg(TAddress address, uint16_t value) {
+ TRegister* reg;
+ reg = searchRegister(address); //search for the register address
+ if (reg) { //if found then assign the register value to the new value.
+ if (cbEnabled) {
+ reg->value = callback(reg, value, TCallback::ON_SET);
+ } else {
+ reg->value = value;
+ }
+ return true;
+ } else
+ return false;
+}
+
+uint16_t Modbus::Reg(TAddress address) {
+ TRegister* reg;
+ reg = searchRegister(address);
+ if(reg)
+ if (cbEnabled) {
+ return callback(reg, reg->value, TCallback::ON_GET);
+ } else {
+ return reg->value;
+ }
+ else
+ return 0;
+}
+
+bool Modbus::removeReg(TAddress address, uint16_t numregs) {
+ TRegister* reg;
+ bool atLeastOne = false;
+ if (0xFFFF - address.address < numregs)
+ numregs = 0xFFFF - address.address;
+ for (uint16_t i = 0; i < numregs; i++) {
+ reg = searchRegister(address + i);
+ if (reg) {
+ atLeastOne = true;
+ removeOnSet(address + i);
+ removeOnGet(address + i);
+ #if defined(MODBUS_USE_STL)
+ _regs.erase(std::remove( _regs.begin(), _regs.end(), *reg), _regs.end() );
+ #else
+ _regs.remove(_regs.find(MODBUS_COMPARE_REG));
+ #endif
+ }
+ }
+ return atLeastOne;
+}
+
+bool Modbus::addReg(TAddress address, uint16_t* value, uint16_t numregs) {
+ if (0xFFFF - address.address < numregs)
+ numregs = 0xFFFF - address.address;
+ for (uint16_t k = 0; k < numregs; k++)
+ addReg(address + k, value[k]);
+ return true;
+}
+
+void Modbus::slavePDU(uint8_t* frame) {
+ FunctionCode fcode = (FunctionCode)frame[0];
+ uint16_t field1 = (uint16_t)frame[1] << 8 | (uint16_t)frame[2];
+ uint16_t field2 = (uint16_t)frame[3] << 8 | (uint16_t)frame[4];
+ uint16_t field3 = 0;
+ uint16_t field4 = 0;
+ uint16_t bytecount_calc;
+ uint16_t k;
+ ResultCode ex;
+ switch (fcode) {
+ case FC_WRITE_REG:
+ //field1 = reg, field2 = value
+ ex = _onRequest(fcode, {HREG(field1), field2});
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ if (!Reg(HREG(field1), field2)) { //Check Address and execute (reg exists?)
+ exceptionResponse(fcode, EX_ILLEGAL_ADDRESS);
+ return;
+ }
+ if (Reg(HREG(field1)) != field2) { //Check for failure
+ exceptionResponse(fcode, EX_SLAVE_FAILURE);
+ return;
+ }
+ _reply = REPLY_ECHO;
+ _onRequestSuccess(fcode, {HREG(field1), field2});
+ break;
+
+ case FC_READ_REGS:
+ //field1 = startreg, field2 = numregs, header len = 3
+ ex = _onRequest(fcode, {HREG(field1), field2});
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ ex = readWords(HREG(field1), field2, fcode);
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ _onRequestSuccess(fcode, {HREG(field1), field2});
+ break;
+
+ case FC_WRITE_REGS:
+ //field1 = startreg, field2 = numregs, frame[5] = data lenght, header len = 6
+ ex = _onRequest(fcode, {HREG(field1), field2});
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ if (field2 < 0x0001 || field2 > MODBUS_MAX_WORDS || 0xFFFF - field1 < field2 || frame[5] != 2 * field2) { //Check constrains
+ exceptionResponse(fcode, EX_ILLEGAL_VALUE);
+ return;
+ }
+ for (k = 0; k < field2; k++) { //Check Address (startreg...startreg + numregs)
+ if (!searchRegister(HREG(field1) + k)) {
+ exceptionResponse(fcode, EX_ILLEGAL_ADDRESS);
+ return;
+ }
+ }
+ if (!setMultipleWords((uint16_t*)(frame + 6), HREG(field1), field2)) {
+ exceptionResponse(fcode, EX_SLAVE_FAILURE);
+ return;
+ }
+ successResponce(HREG(field1), field2, fcode);
+ _reply = REPLY_NORMAL;
+ _onRequestSuccess(fcode, {HREG(field1), field2});
+ break;
+
+ case FC_READ_COILS:
+ //field1 = startreg, field2 = numregs
+ ex = _onRequest(fcode, {COIL(field1), field2});
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ ex = readBits(COIL(field1), field2, fcode);
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ _onRequestSuccess(fcode, {COIL(field1), field2});
+ break;
+
+ case FC_READ_INPUT_STAT:
+ //field1 = startreg, field2 = numregs
+ ex = _onRequest(fcode, {ISTS(field1), field2});
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ ex = readBits(ISTS(field1), field2, fcode);
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ _onRequestSuccess(fcode, {ISTS(field1), field2});
+ break;
+
+ case FC_READ_INPUT_REGS:
+ //field1 = startreg, field2 = numregs
+ ex = _onRequest(fcode, {IREG(field1), field2});
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ ex = readWords(IREG(field1), field2, fcode);
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ _onRequestSuccess(fcode, {IREG(field1), field2});
+ break;
+
+ case FC_WRITE_COIL:
+ //field1 = reg, field2 = status, header len = 3
+ ex = _onRequest(fcode, {COIL(field1), field2});
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ if (field2 != 0xFF00 && field2 != 0x0000) { //Check value (status)
+ exceptionResponse(fcode, EX_ILLEGAL_VALUE);
+ return;
+ }
+ if (!Reg(COIL(field1), field2)) { //Check Address and execute (reg exists?)
+ exceptionResponse(fcode, EX_ILLEGAL_ADDRESS);
+ return;
+ }
+ if (Reg(COIL(field1)) != field2) { //Check for failure
+ exceptionResponse(fcode, EX_SLAVE_FAILURE);
+ return;
+ }
+ _reply = REPLY_ECHO;
+ _onRequestSuccess(fcode, {COIL(field1), field2});
+ break;
+
+ case FC_WRITE_COILS:
+ //field1 = startreg, field2 = numregs, frame[5] = bytecount, header len = 6
+ ex = _onRequest(fcode, {COIL(field1), field2});
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ bytecount_calc = field2 / 8;
+ if (field2%8) bytecount_calc++;
+ if (field2 < 0x0001 || field2 > MODBUS_MAX_BITS || 0xFFFF - field1 < field2 || frame[5] != bytecount_calc) { //Check registers range and data size maches
+ exceptionResponse(fcode, EX_ILLEGAL_VALUE);
+ return;
+ }
+ for (k = 0; k < field2; k++) { //Check Address (startreg...startreg + numregs)
+ if (!searchRegister(COIL(field1) + k)) {
+ exceptionResponse(fcode, EX_ILLEGAL_ADDRESS);
+ return;
+ }
+ }
+ if (!setMultipleBits(frame + 6, COIL(field1), field2)) {
+ exceptionResponse(fcode, EX_SLAVE_FAILURE);
+ return;
+ }
+ successResponce(COIL(field1), field2, fcode);
+ _reply = REPLY_NORMAL;
+ _onRequestSuccess(fcode, {COIL(field1), field2});
+ break;
+ #if defined(MODBUS_FILES)
+ case FC_READ_FILE_REC:
+ if (frame[1] < 0x07 || frame[1] > 0xF5) { // Wrong request data size
+ exceptionResponse(fcode, EX_ILLEGAL_VALUE);
+ return;
+ }
+ {
+ uint8_t bufSize = 2; // 2 bytes for frame header
+ uint8_t* recs = frame + 2; // Begin of sub-recs blocks
+ uint8_t recsCount = frame[1] / 7; // Count of sub-rec blocks
+ for (uint8_t p = 0; p < recsCount; p++) { // Calc output buffer size required
+ //uint16_t fileNum = (uint16_t)recs[1] << 8 | (uint16_t)recs[2];
+ uint16_t recNum = (uint16_t)recs[3] << 8 | (uint16_t)recs[4];
+ uint16_t recLen = (uint16_t)recs[5] << 8 | (uint16_t)recs[6];
+ //Serial.printf("%d, %d, %d\n", fileNum, recNum, recLen);
+ if (recs[0] != 0x06 || recNum > 0x270F) { // Wrong ref type or count of records
+ exceptionResponse(fcode, EX_ILLEGAL_ADDRESS);
+ return;
+ }
+ bufSize += recLen * 2 + 2; // 4 bytes for header + data
+ recs += 7;
+ }
+// if (bufSize > MODBUS_MAX_FRAME) { // Frame to return too large
+// exceptionResponse(fcode, EX_ILLEGAL_ADDRESS);
+// return;
+// }
+ uint8_t* srcFrame = _frame;
+ _frame = (uint8_t*)malloc(bufSize);
+ if (!_frame) {
+ free(srcFrame);
+ exceptionResponse(fcode, EX_SLAVE_FAILURE);
+ return;
+ }
+ _len = bufSize;
+ recs = frame + 2; // Begin of sub-recs blocks
+ uint8_t* data = _frame + 2;
+ for (uint8_t p = 0; p < recsCount; p++) {
+ uint16_t fileNum = (uint16_t)recs[1] << 8 | (uint16_t)recs[2];
+ uint16_t recNum = (uint16_t)recs[3] << 8 | (uint16_t)recs[4];
+ uint16_t recLen = (uint16_t)recs[5] << 8 | (uint16_t)recs[6];
+ ResultCode res = fileOp(fcode, fileNum, recNum, recLen, data + 2);
+ if (res != EX_SUCCESS) { // File read failed
+ free(srcFrame);
+ exceptionResponse(fcode, res);
+ return;
+ }
+ data[0] = recLen * 2 + 1;
+ data[1] = 0x06;
+ data += recLen * 2 + 2;
+ recs += 7;
+ }
+ _frame[0] = fcode;
+ _frame[1] = bufSize;
+ _reply = REPLY_NORMAL;
+ free(srcFrame);
+ }
+ break;
+ case FC_WRITE_FILE_REC: {
+ if (frame[1] < 0x09 || frame[1] > 0xFB) { // Wrong request data size
+ exceptionResponse(fcode, EX_ILLEGAL_VALUE);
+ return;
+ }
+ uint8_t* recs = frame + 2; // Begin of sub-recs blocks
+ while (recs < frame + frame[1]) {
+ if (recs[0] != 0x06) {
+ exceptionResponse(fcode, EX_ILLEGAL_ADDRESS);
+ return;
+ }
+ uint16_t fileNum = (uint16_t)recs[1] << 8 | (uint16_t)recs[2];
+ uint16_t recNum = (uint16_t)recs[3] << 8 | (uint16_t)recs[4];
+ uint16_t recLen = (uint16_t)recs[5] << 8 | (uint16_t)recs[6];
+ if (recs + recLen * 2 > frame + frame[1]) {
+ exceptionResponse(fcode, EX_ILLEGAL_ADDRESS);
+ return;
+ }
+ ResultCode res = fileOp(fcode, fileNum, recNum, recLen, recs + 7);
+ if (res != EX_SUCCESS) { // File write failed
+ exceptionResponse(fcode, res);
+ return;
+ }
+ recs += 7 + recLen * 2;
+ }
+ }
+ _reply = REPLY_ECHO;
+ break;
+ #endif
+ case FC_MASKWRITE_REG:
+ //field1 = reg, field2 = AND mask, field3 = OR mask
+ // Result = (Current Contents AND And_Mask) OR (Or_Mask AND (NOT And_Mask))
+ field3 = (uint16_t)frame[5] << 8 | (uint16_t)frame[6];
+ ex = _onRequest(fcode, {HREG(field1), field2, field3});
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ field4 = Reg(HREG(field1));
+ field4 = (field4 & field2) | (field3 & ~field2);
+ if (!Reg(HREG(field1), field4)) { //Check Address and execute (reg exists?)
+ exceptionResponse(fcode, EX_ILLEGAL_ADDRESS);
+ return;
+ }
+ if (Reg(HREG(field1)) != field4) { //Check for failure
+ exceptionResponse(fcode, EX_SLAVE_FAILURE);
+ return;
+ }
+ _reply = REPLY_ECHO;
+ _onRequestSuccess(fcode, {HREG(field1), field2, field3});
+ break;
+ case FC_READWRITE_REGS:
+ //field1 = readreg, field2 = read count, frame[9] = data lenght, header len = 10
+ //field3 = wtitereg, field4 = write count
+ field3 = (uint16_t)frame[5] << 8 | (uint16_t)frame[6];
+ field4 = (uint16_t)frame[7] << 8 | (uint16_t)frame[8];
+ ex = _onRequest(fcode, {HREG(field1), field2, HREG(field3), field4});
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ if (field2 < 0x0001 || field2 > MODBUS_MAX_WORDS ||
+ field4 < 0x0001 || field4 > MODBUS_MAX_WORDS ||
+ 0xFFFF - field1 < field2 || 0xFFFF - field1 < field2 ||
+ frame[9] != 2 * field4) { //Check value
+ exceptionResponse(fcode, EX_ILLEGAL_VALUE);
+ return;
+ }
+ if (!setMultipleWords((uint16_t*)(frame + 10), HREG(field3), field4)) {
+ exceptionResponse(fcode, EX_SLAVE_FAILURE);
+ return;
+ }
+ ex = readWords(HREG(field1), field2, fcode);
+ if (ex != EX_SUCCESS) {
+ exceptionResponse(fcode, ex);
+ return;
+ }
+ _onRequestSuccess(fcode, {HREG(field1), field2, HREG(field3), field4});
+ break;
+
+ default:
+ ex = _onRequest(fcode, {frame + 1});
+ if (ex != EX_PASSTHROUGH) {
+ exceptionResponse(fcode, EX_ILLEGAL_FUNCTION);
+ }
+ return;
+ }
+}
+
+void Modbus::successResponce(TAddress startreg, uint16_t numoutputs, FunctionCode fn) {
+ free(_frame);
+ _len = 5;
+ _frame = (uint8_t*) malloc(_len);
+ if (!_frame) {
+ _reply = REPLY_OFF;
+ return;
+ }
+ _frame[0] = fn;
+ _frame[1] = startreg.address >> 8;
+ _frame[2] = startreg.address & 0x00FF;
+ _frame[3] = numoutputs >> 8;
+ _frame[4] = numoutputs & 0x00FF;
+}
+
+void Modbus::exceptionResponse(FunctionCode fn, ResultCode excode) {
+ free(_frame);
+ _len = 2;
+ _frame = (uint8_t*) malloc(_len);
+ if (!_frame) {
+ _reply = REPLY_OFF;
+ return;
+ }
+ _frame[0] = fn + 0x80;
+ _frame[1] = excode;
+ _reply = REPLY_NORMAL;
+}
+
+void Modbus::getMultipleBits(uint8_t* frame, TAddress startreg, uint16_t numregs) {
+ uint8_t bitn = 0;
+ uint16_t i = 0;
+ while (numregs--) {
+ if (BIT_BOOL(Reg(startreg)))
+ bitSet(frame[i], bitn);
+ else
+ bitClear(frame[i], bitn);
+ bitn++; //increment the bit index
+ if (bitn == 8) {
+ i++;
+ bitn = 0;
+ }
+ startreg++; //increment the register
+ }
+}
+
+void Modbus::getMultipleWords(uint16_t* frame, TAddress startreg, uint16_t numregs) {
+ for (uint8_t i = 0; i < numregs; i++) {
+ frame[i] = __swap_16(Reg(startreg + i));
+ }
+}
+
+Modbus::ResultCode Modbus::readBits(TAddress startreg, uint16_t numregs, FunctionCode fn) {
+ if (numregs < 0x0001 || numregs > MODBUS_MAX_BITS || (0xFFFF - startreg.address) < numregs)
+ return EX_ILLEGAL_ADDRESS;
+ //Check Address
+ //Check only startreg. Is this correct?
+ //When I check all registers in range I got errors in ScadaBR
+ //I think that ScadaBR request more than one in the single request
+ //when you have more then one datapoint configured from same type.
+#if defined(MODBUS_STRICT_REG)
+ for (k = 0; k < numregs; k++) { //Check Address (startreg...startreg + numregs)
+ if (!searchRegister(startreg + k))
+ return EX_ILLEGAL_ADDRESS;
+ }
+#else
+ if (!searchRegister(startreg))
+ return EX_ILLEGAL_ADDRESS;
+#endif
+ free(_frame);
+ //Determine the message length = function type, byte count and
+ //for each group of 8 registers the message length increases by 1
+ _len = 2 + numregs/8;
+ if (numregs % 8) _len++; //Add 1 to the message length for the partial byte.
+ _frame = (uint8_t*) malloc(_len);
+ if (!_frame)
+ return EX_SLAVE_FAILURE;
+ _frame[0] = fn;
+ _frame[1] = _len - 2; //byte count (_len - function code and byte count)
+ _frame[_len - 1] = 0; //Clean last probably partial byte
+ getMultipleBits(_frame+2, startreg, numregs);
+ _reply = REPLY_NORMAL;
+ return EX_SUCCESS;
+}
+
+Modbus::ResultCode Modbus::readWords(TAddress startreg, uint16_t numregs, FunctionCode fn) {
+ //Check value (numregs)
+ if (numregs < 0x0001 || numregs > MODBUS_MAX_WORDS || 0xFFFF - startreg.address < numregs)
+ return EX_ILLEGAL_ADDRESS;
+#if defined(MODBUS_STRICT_REG)
+ for (k = 0; k < numregs; k++) { //Check Address (startreg...startreg + numregs)
+ if (!searchRegister(startreg + k))
+ return EX_ILLEGAL_ADDRESS;
+ }
+#else
+ if (!searchRegister(startreg))
+ return EX_ILLEGAL_ADDRESS;
+#endif
+ free(_frame);
+ _len = 2 + numregs * 2; //calculate the query reply message length. 2 bytes per register + 2 bytes for header
+ _frame = (uint8_t*) malloc(_len);
+ if (!_frame)
+ return EX_SLAVE_FAILURE;
+ _frame[0] = fn;
+ _frame[1] = _len - 2; //byte count
+ getMultipleWords((uint16_t*)(_frame + 2), startreg, numregs);
+ _reply = REPLY_NORMAL;
+ return EX_SUCCESS;
+}
+
+bool Modbus::setMultipleBits(uint8_t* frame, TAddress startreg, uint16_t numoutputs) {
+ uint8_t bitn = 0;
+ uint16_t i = 0;
+ bool result = true;
+ while (numoutputs--) {
+ Reg(startreg, BIT_VAL(bitRead(frame[i], bitn)));
+ if (Reg(startreg) != BIT_VAL(bitRead(frame[i], bitn)))
+ result = false;
+ bitn++; //increment the bit index
+ if (bitn == 8) {
+ i++;
+ bitn = 0;
+ }
+ startreg++; //increment the register
+ }
+ return result;
+}
+
+bool Modbus::setMultipleWords(uint16_t* frame, TAddress startreg, uint16_t numregs) {
+ bool result = true;
+ for (uint8_t i = 0; i < numregs; i++) {
+ Reg(startreg + i, __swap_16(frame[i]));
+ if (Reg(startreg + i) != __swap_16(frame[i]))
+ result = false;
+ }
+ return result;
+}
+
+bool Modbus::onGet(TAddress address, cbModbus cb, uint16_t numregs) {
+ TRegister* reg;
+ bool atLeastOne = false;
+ if (!cb) {
+ return removeOnGet(address, nullptr, numregs);
+ }
+ while (numregs > 0) {
+ reg = searchRegister(address);
+ if (reg) {
+ _callbacks.push_back({TCallback::ON_GET, address, cb});
+ atLeastOne = true;
+ }
+ address++;
+ numregs--;
+ }
+ return atLeastOne;
+}
+bool Modbus::onSet(TAddress address, cbModbus cb, uint16_t numregs) {
+ TRegister* reg;
+ bool atLeastOne = false;
+ if (!cb) {
+ return removeOnSet(address, nullptr, numregs);
+ }
+ while (numregs > 0) {
+ reg = searchRegister(address);
+ if (reg) {
+ _callbacks.push_back({TCallback::ON_SET, address, cb});
+ atLeastOne = true;
+ }
+ address++;
+ numregs--;
+ }
+ return atLeastOne;
+}
+
+bool Modbus::removeOn(TCallback::CallbackType t, TAddress address, cbModbus cb, uint16_t numregs) {
+ size_t s = _callbacks.size();
+ #if defined(MODBUS_USE_STL)
+ #define MODBUS_COMPARE_ON [t, address, cb](const TCallback entry){\
+ return entry.type == t && entry.address == address \
+ && (!cb || std::addressof(cb) == std::addressof(entry.cb));}
+ while(numregs--) {
+ _callbacks.erase(remove_if(_callbacks.begin(), _callbacks.end(), MODBUS_COMPARE_ON), _callbacks.end());
+ address++;
+ }
+ #else
+ #define MODBUS_COMPARE_ON [t, address, cb](const TCallback entry){ \
+ return entry.type == t && entry.address == address \
+ && (!cb || entry.cb == cb);}
+ while(numregs--) {
+ size_t r = 0;
+ do {
+ r = _callbacks.find(MODBUS_COMPARE_ON);
+ _callbacks.remove(r);
+ } while (r < _callbacks.size());
+ address++;
+ }
+ #endif
+ return s == _callbacks.size();
+}
+bool Modbus::removeOnSet(TAddress address, cbModbus cb, uint16_t numregs) {
+ return removeOn(TCallback::ON_SET, address, cb, numregs);
+}
+
+bool Modbus::removeOnGet(TAddress address, cbModbus cb, uint16_t numregs) {
+ return removeOn(TCallback::ON_GET, address, cb, numregs);
+}
+
+bool Modbus::readSlave(uint16_t address, uint16_t numregs, FunctionCode fn) {
+ free(_frame);
+ _len = 5;
+ _frame = (uint8_t*) malloc(_len);
+ if (!_frame) {
+ _reply = REPLY_OFF;
+ return false;
+ }
+ _frame[0] = fn;
+ _frame[1] = address >> 8;
+ _frame[2] = address & 0x00FF;
+ _frame[3] = numregs >> 8;
+ _frame[4] = numregs & 0x00FF;
+ return true;
+}
+
+bool Modbus::writeSlaveBits(TAddress startreg, uint16_t to, uint16_t numregs, FunctionCode fn, bool* data) {
+ free(_frame);
+ _len = 6 + numregs/8;
+ if (numregs % 8) _len++; //Add 1 to the message length for the partial byte.
+ _frame = (uint8_t*) malloc(_len);
+ if (!_frame) {
+ _reply = REPLY_OFF;
+ return false;
+ }
+ _frame[0] = fn;
+ _frame[1] = to >> 8;
+ _frame[2] = to & 0x00FF;
+ _frame[3] = numregs >> 8;
+ _frame[4] = numregs & 0x00FF;
+ _frame[5] = _len - 6;
+ _frame[_len - 1] = 0; //Clean last probably partial byte
+ if (data) {
+ boolToBits(_frame + 6, data, numregs);
+ } else {
+ getMultipleBits(_frame + 6, startreg, numregs);
+ }
+ _reply = REPLY_NORMAL;
+ return true;
+}
+
+bool Modbus::writeSlaveWords(TAddress startreg, uint16_t to, uint16_t numregs, FunctionCode fn, uint16_t* data) {
+ free(_frame);
+ _len = 6 + 2 * numregs;
+ _frame = (uint8_t*) malloc(_len);
+ if (!_frame) {
+ _reply = REPLY_OFF;
+ return false;
+ }
+ _frame[0] = fn;
+ _frame[1] = to >> 8;
+ _frame[2] = to & 0x00FF;
+ _frame[3] = numregs >> 8;
+ _frame[4] = numregs & 0x00FF;
+ _frame[5] = _len - 6;
+ if (data) {
+ uint16_t* frame = (uint16_t*)(_frame + 6);
+ for (uint8_t i = 0; i < numregs; i++) {
+ frame[i] = __swap_16(data[i]);
+ }
+ } else {
+ getMultipleWords((uint16_t*)(_frame + 6), startreg, numregs);
+ }
+ return true;
+}
+
+void Modbus::boolToBits(uint8_t* dst, bool* src, uint16_t numregs) {
+ uint8_t bitn = 0;
+ uint16_t i = 0;
+ uint16_t j = 0;
+ while (numregs--) {
+ if (src[j])
+ bitSet(dst[i], bitn);
+ else
+ bitClear(dst[i], bitn);
+ bitn++; //increment the bit index
+ if (bitn == 8) {
+ i++;
+ bitn = 0;
+ }
+ j++; //increment the register
+ }
+}
+
+void Modbus::bitsToBool(bool* dst, uint8_t* src, uint16_t numregs) {
+ uint8_t bitn = 0;
+ uint16_t i = 0;
+ uint16_t j = 0;
+ while (numregs--) {
+ dst[j] = bitRead(src[i], bitn);
+ bitn++; //increment the bit index
+ if (bitn == 8) {
+ i++;
+ bitn = 0;
+ }
+ j++; //increment the register
+ }
+}
+
+void Modbus::masterPDU(uint8_t* frame, uint8_t* sourceFrame, TAddress startreg, uint8_t* output) {
+ uint8_t fcode = frame[0];
+ if ((fcode & 0x80) != 0) { // Check if error responce
+ _reply = frame[1];
+ return;
+ }
+ if (fcode != sourceFrame[0]) { // Check if responce matches the request
+ _reply = EX_DATA_MISMACH;
+ return;
+ }
+ _reply = EX_SUCCESS;
+ uint16_t field2 = (uint16_t)sourceFrame[3] << 8 | (uint16_t)sourceFrame[4];
+ uint8_t bytecount_calc;
+ switch (fcode) {
+ case FC_READ_REGS:
+ case FC_READ_INPUT_REGS:
+ case FC_READWRITE_REGS:
+ //field2 = numregs, frame[1] = data lenght, header len = 2
+ if (frame[1] != 2 * field2) { //Check if data size matches
+ _reply = EX_DATA_MISMACH;
+ break;
+ }
+ if (output) {
+ uint16_t* from = (uint16_t*)(frame + 2);
+ uint16_t* to = (uint16_t*)output;
+ while(field2--) {
+ *(to++) = __swap_16(*(from++));
+ }
+ } else {
+ setMultipleWords((uint16_t*)(frame + 2), startreg, field2);
+ }
+ break;
+ case FC_READ_COILS:
+ case FC_READ_INPUT_STAT:
+ //field2 = numregs, frame[1] = data length, header len = 2
+ bytecount_calc = field2 / 8;
+ if (field2 % 8) bytecount_calc++;
+ if (frame[1] != bytecount_calc) { // check if data size matches
+ _reply = EX_DATA_MISMACH;
+ break;
+ }
+ if (output) {
+ bitsToBool((bool*)output, frame + 2, field2);
+ } else {
+ setMultipleBits(frame + 2, startreg, field2);
+ }
+ break;
+ #if defined(MODBUS_FILES)
+ case FC_READ_FILE_REC:
+ // Should check if byte order swap needed
+ if (frame[1] < 0x07 || frame[1] > 0xF5) { // Wrong request data size
+ _reply = EX_ILLEGAL_VALUE;
+ return;
+ }
+ {
+ uint8_t* data = frame + 2;
+ uint8_t* eoFrame = frame + frame[1];
+ while (data < eoFrame) {
+ //data[0] - sub-req length
+ //data[1] = 0x06
+ if (data[1] != 0x06 || data[0] < 0x07 || data[0] > 0xF5 || data + data[0] > eoFrame) { // Wrong request data size
+ _reply = EX_ILLEGAL_VALUE;
+ return;
+ }
+ memcpy(output, data + 2, data[0]);
+ data += data[0] + 1;
+ output += data[0] - 1;
+ }
+ }
+ break;
+ case FC_WRITE_FILE_REC:
+ #endif
+ case FC_WRITE_REG:
+ case FC_WRITE_REGS:
+ case FC_WRITE_COIL:
+ case FC_WRITE_COILS:
+ case FC_MASKWRITE_REG:
+ break;
+
+ default:
+ _reply = EX_GENERAL_FAILURE;
+ }
+}
+
+bool Modbus::cbEnable(const bool state) {
+ const bool old_state = state;
+ cbEnabled = state;
+ return old_state;
+}
+bool Modbus::cbDisable() {
+ return cbEnable(false);
+}
+Modbus::~Modbus() {
+ free(_frame);
+}
+
+#if defined(MODBUS_FILES)
+#if defined(MODBUS_USE_STL)
+bool Modbus::onFile(std::function cb) {
+#else
+bool Modbus::onFile(Modbus::ResultCode (*cb)(Modbus::FunctionCode, uint16_t, uint16_t, uint16_t, uint8_t*)) {
+#endif
+ _onFile = cb;
+ return true;
+}
+Modbus::ResultCode Modbus::fileOp(Modbus::FunctionCode fc, uint16_t fileNum, uint16_t recNum, uint16_t recLen, uint8_t* frame) {
+ if (!_onFile) return EX_ILLEGAL_ADDRESS;
+ return _onFile(fc, fileNum, recNum, recLen, frame);
+}
+
+ bool Modbus::readSlaveFile(uint16_t* fileNum, uint16_t* startRec, uint16_t* len, uint8_t count, FunctionCode fn) {
+ _len = count * 7 + 2;
+ if (_len > MODBUS_MAX_FRAME) return false;
+ free(_frame);
+ _frame = (uint8_t*) malloc(_len);
+ if (!_frame) return false;
+ _frame[0] = fn;
+ _frame[1] = _len - 2;
+ uint8_t* subReq = _frame + 2;
+ for (uint8_t i = 0; i < count; i++) {
+ subReq[0] = 0x06;
+ subReq[1] = fileNum[i] >> 8;
+ subReq[2] = fileNum[i] & 0x00FF;
+ subReq[3] = startRec[i] >> 8;
+ subReq[4] = startRec[i] & 0x00FF;
+ subReq[5] = len[i] >> 8;
+ subReq[6] = len[i] & 0x00FF;
+ subReq += 7;
+ }
+ return true;
+ }
+ bool Modbus::writeSlaveFile(uint16_t* fileNum, uint16_t* startRec, uint16_t* len, uint8_t count, FunctionCode fn, uint8_t* data) {
+ _len = 2;
+ for (uint8_t i = 0; i < count; i++) {
+ _len += len[i] * 2 + 7;
+ }
+ if (_len > MODBUS_MAX_FRAME) return false;
+ free(_frame);
+ _frame = (uint8_t*) malloc(_len);
+ if (!_frame) return false;
+ _frame[0] = fn;
+ _frame[1] = _len - 2;
+ uint8_t* subReq = _frame + 2;
+ for (uint8_t i = 0; i < count; i++) {
+ subReq[0] = 0x06;
+ subReq[1] = fileNum[i] >> 8;
+ subReq[2] = fileNum[i] & 0x00FF;
+ subReq[3] = startRec[i] >> 8;
+ subReq[4] = startRec[i] & 0x00FF;
+ subReq[5] = len[i] >> 8;
+ subReq[6] = len[i] & 0x00FF;
+ uint8_t clen = len[i] * 2;
+ memcpy(subReq + 7, data, clen);
+ subReq += 7 + clen;
+ data += clen;
+ }
+ return true;
+ }
+ #endif
+
+bool Modbus::onRaw(cbRaw cb) {
+ _cbRaw = cb;
+ return true;
+}
+Modbus::ResultCode Modbus::_onRequestDefault(Modbus::FunctionCode fc, const RequestData data) {
+ return EX_SUCCESS;
+}
+bool Modbus::onRequest(cbRequest cb) {
+ _onRequest = cb;
+ return true;
+}
+#if defined (MODBUSAPI_OPTIONAL)
+bool Modbus::onRequestSuccess(cbRequest cb) {
+ _onRequestSuccess = cb;
+ return true;
+}
+#endif
+
+#if defined(ARDUINO_SAM_DUE_STL)
+namespace std {
+ void __throw_bad_function_call() {
+ Serial.println(F("STL ERROR - __throw_bad_function_call"));
+ __builtin_unreachable();
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Modbus.h b/src/Modbus.h
new file mode 100644
index 0000000..97fd340
--- /dev/null
+++ b/src/Modbus.h
@@ -0,0 +1,363 @@
+/*
+ Modbus Library for Arduino
+ Core functions
+ Copyright (C) 2014 Andr� Sarmento Barbosa
+ 2017-2022 Alexander Emelianov (a.m.emelianov@gmail.com)
+*/
+#pragma once
+#include "ModbusSettings.h"
+#include "Arduino.h"
+#if defined(MODBUS_USE_STL)
+ #include
+ #include
+ #include
+ #include
+#else
+ #include "darray.h"
+#endif
+
+static inline uint16_t __swap_16(uint16_t num) { return (num >> 8) | (num << 8); }
+
+#define COIL(n) (TAddress){TAddress::COIL, n}
+#define ISTS(n) (TAddress){TAddress::ISTS, n}
+#define IREG(n) (TAddress){TAddress::IREG, n}
+#define HREG(n) (TAddress){TAddress::HREG, n}
+#define NULLREG (TAddress){TAddress::NONE, 0xFFFF}
+#define BIT_VAL(v) (v?0xFF00:0x0000)
+#define BIT_BOOL(v) (v==0xFF00)
+#define COIL_VAL(v) (v?0xFF00:0x0000)
+#define COIL_BOOL(v) (v==0xFF00)
+#define ISTS_VAL(v) (v?0xFF00:0x0000)
+#define ISTS_BOOL(v) (v==0xFF00)
+
+// For depricated (v1.xx) onSet/onGet format compatibility
+#define cbDefault nullptr
+
+struct TRegister;
+#if defined(MODBUS_USE_STL)
+typedef std::function cbModbus; // Callback function Type
+#else
+typedef uint16_t (*cbModbus)(TRegister* reg, uint16_t val); // Callback function Type
+#endif
+
+struct TAddress {
+ enum RegType {COIL, ISTS, IREG, HREG, NONE = 0xFF};
+ RegType type;
+ uint16_t address;
+ bool operator==(const TAddress &obj) const { // TAddress == TAddress
+ return type == obj.type && address == obj.address;
+ }
+ bool operator!=(const TAddress &obj) const { // TAddress != TAddress
+ return type != obj.type || address != obj.address;
+ }
+ TAddress& operator++() { // ++TAddress
+ address++;
+ return *this;
+ }
+ TAddress operator++(int) { // TAddress++
+ TAddress result(*this);
+ ++(*this);
+ return result;
+ }
+ TAddress& operator+=(const int& inc) { // TAddress += integer
+ address += inc;
+ return *this;
+ }
+ const TAddress operator+(const int& inc) const { // TAddress + integer
+ TAddress result(*this);
+ result.address += inc;
+ return result;
+ }
+ bool isCoil() {
+ return type == COIL;
+ }
+ bool isIsts() {
+ return type == ISTS;
+ }
+ bool isIreg() {
+ return type == IREG;
+ }
+ bool isHreg() {
+ return type == HREG;
+ }
+};
+
+struct TCallback {
+ enum CallbackType {ON_SET, ON_GET};
+ CallbackType type;
+ TAddress address;
+ cbModbus cb;
+};
+
+struct TRegister {
+ TAddress address;
+ uint16_t value;
+ bool operator ==(const TRegister &obj) const {
+ return address == obj.address;
+ }
+};
+
+class Modbus {
+ public:
+ //Function Codes
+ enum FunctionCode {
+ FC_READ_COILS = 0x01, // Read Coils (Output) Status
+ FC_READ_INPUT_STAT = 0x02, // Read Input Status (Discrete Inputs)
+ FC_READ_REGS = 0x03, // Read Holding Registers
+ FC_READ_INPUT_REGS = 0x04, // Read Input Registers
+ FC_WRITE_COIL = 0x05, // Write Single Coil (Output)
+ FC_WRITE_REG = 0x06, // Preset Single Register
+ FC_DIAGNOSTICS = 0x08, // Not implemented. Diagnostics (Serial Line only)
+ FC_WRITE_COILS = 0x0F, // Write Multiple Coils (Outputs)
+ FC_WRITE_REGS = 0x10, // Write block of contiguous registers
+ FC_READ_FILE_REC = 0x14, // Read File Record
+ FC_WRITE_FILE_REC = 0x15, // Write File Record
+ FC_MASKWRITE_REG = 0x16, // Mask Write Register
+ FC_READWRITE_REGS = 0x17 // Read/Write Multiple registers
+ };
+ //Exception Codes
+ //Custom result codes used internally and for callbacks but never used for Modbus responce
+ enum ResultCode {
+ EX_SUCCESS = 0x00, // Custom. No error
+ EX_ILLEGAL_FUNCTION = 0x01, // Function Code not Supported
+ EX_ILLEGAL_ADDRESS = 0x02, // Output Address not exists
+ EX_ILLEGAL_VALUE = 0x03, // Output Value not in Range
+ EX_SLAVE_FAILURE = 0x04, // Slave or Master Device Fails to process request
+ EX_ACKNOWLEDGE = 0x05, // Not used
+ EX_SLAVE_DEVICE_BUSY = 0x06, // Not used
+ EX_MEMORY_PARITY_ERROR = 0x08, // Not used
+ EX_PATH_UNAVAILABLE = 0x0A, // Not used
+ EX_DEVICE_FAILED_TO_RESPOND = 0x0B, // Not used
+ EX_GENERAL_FAILURE = 0xE1, // Custom. Unexpected master error
+ EX_DATA_MISMACH = 0xE2, // Custom. Inpud data size mismach
+ EX_UNEXPECTED_RESPONSE = 0xE3, // Custom. Returned result doesn't mach transaction
+ EX_TIMEOUT = 0xE4, // Custom. Operation not finished within reasonable time
+ EX_CONNECTION_LOST = 0xE5, // Custom. Connection with device lost
+ EX_CANCEL = 0xE6, // Custom. Transaction/request canceled
+ EX_PASSTHROUGH = 0xE7, // Custom. Raw callback. Indicate to normal processing on callback exit
+ EX_FORCE_PROCESS = 0xE8 // Custom. Raw callback. Indicate to force processing on callback exit
+ };
+ union RequestData {
+ struct {
+ TAddress reg;
+ uint16_t regCount;
+ };
+ struct {
+ TAddress regRead;
+ uint16_t regReadCount;
+ TAddress regWrite;
+ uint16_t regWriteCount;
+ };
+ struct {
+ TAddress regMask;
+ uint16_t andMask;
+ uint16_t orMask;
+ };
+ uint8_t* data;
+ RequestData(TAddress r1, uint16_t c1) {
+ reg = r1;
+ regCount = c1;
+ };
+ RequestData(TAddress r1, uint16_t c1, TAddress r2, uint16_t c2) {
+ regRead = r1;
+ regReadCount = c1;
+ regWrite = r2;
+ regWriteCount = c2;
+ };
+ RequestData(TAddress r1, uint16_t m1, uint16_t m2) {
+ regMask = r1;
+ andMask = m1;
+ orMask = m2;
+ };
+ RequestData(uint8_t* d) {
+ data = d;
+ };
+ };
+
+ struct frame_arg_t {
+ bool to_server;
+ union {
+ uint8_t slaveId;
+ struct {
+ uint8_t unitId;
+ uint32_t ipaddr;
+ uint16_t transactionId;
+ };
+ };
+ frame_arg_t(uint8_t s, bool m = false) {
+ slaveId = s;
+ to_server = m;
+ };
+ frame_arg_t(uint8_t u, uint32_t a, uint16_t t, bool m = false) {
+ unitId = u;
+ ipaddr = a;
+ transactionId = t;
+ to_server = m;
+ };
+ };
+
+ ~Modbus();
+
+ bool cbEnable(const bool state = true);
+ bool cbDisable();
+
+ private:
+ ResultCode readBits(TAddress startreg, uint16_t numregs, FunctionCode fn);
+ ResultCode readWords(TAddress startreg, uint16_t numregs, FunctionCode fn);
+
+ bool setMultipleBits(uint8_t* frame, TAddress startreg, uint16_t numoutputs);
+ bool setMultipleWords(uint16_t* frame, TAddress startreg, uint16_t numoutputs);
+
+ void getMultipleBits(uint8_t* frame, TAddress startreg, uint16_t numregs);
+ void getMultipleWords(uint16_t* frame, TAddress startreg, uint16_t numregs);
+
+ void bitsToBool(bool* dst, uint8_t* src, uint16_t numregs);
+ void boolToBits(uint8_t* dst, bool* src, uint16_t numregs);
+
+ protected:
+ //Reply Types
+ enum ReplyCode {
+ REPLY_OFF = 0x01,
+ REPLY_ECHO = 0x02,
+ REPLY_NORMAL = 0x03,
+ REPLY_ERROR = 0x04,
+ REPLY_UNEXPECTED = 0x05
+ };
+ #if defined(MODBUS_USE_STL)
+ #if defined(MODBUS_GLOBAL_REGS)
+ static std::vector _regs;
+ static std::vector _callbacks;
+ #if defined(MODBUS_FILES)
+ static std::function _onFile;
+ #endif
+ #else
+ std::vector _regs;
+ std::vector _callbacks;
+ #if defined(MODBUS_FILES)
+ std::function _onFile;
+ #endif
+ #endif
+ #else
+ #if defined(MODBUS_GLOBAL_REGS)
+ static DArray _regs;
+ static DArray _callbacks;
+ #if defined(MODBUS_FILES)
+ static ResultCode (*_onFile)(FunctionCode, uint16_t, uint16_t, uint16_t, uint8_t*);
+ #endif
+ #else
+ DArray _regs;
+ DArray _callbacks;
+ #if defined(MODBUS_FILES)
+ ResultCode (*_onFile)(FunctionCode, uint16_t, uint16_t, uint16_t, uint8_t*)= nullptr;
+ #endif
+ #endif
+ #endif
+
+ uint8_t* _frame = nullptr;
+ uint16_t _len = 0;
+ uint8_t _reply = 0;
+ bool cbEnabled = true;
+ uint16_t callback(TRegister* reg, uint16_t val, TCallback::CallbackType t);
+ virtual TRegister* searchRegister(TAddress addr);
+ void exceptionResponse(FunctionCode fn, ResultCode excode); // Fills _frame with response
+ void successResponce(TAddress startreg, uint16_t numoutputs, FunctionCode fn); // Fills frame with response
+ void slavePDU(uint8_t* frame); //For Slave
+ void masterPDU(uint8_t* frame, uint8_t* sourceFrame, TAddress startreg, uint8_t* output = nullptr); //For Master
+ // frame - data received form slave
+ // sourceFrame - data have sent fo slave
+ // startreg - local register to start put data to
+ // output - if not null put data to the buffer insted local registers. output assumed to by array of uint16_t or boolean
+
+ bool readSlave(uint16_t address, uint16_t numregs, FunctionCode fn);
+ bool writeSlaveBits(TAddress startreg, uint16_t to, uint16_t numregs, FunctionCode fn, bool* data = nullptr);
+ bool writeSlaveWords(TAddress startreg, uint16_t to, uint16_t numregs, FunctionCode fn, uint16_t* data = nullptr);
+ // startreg - local register to get data from
+ // to - slave register to write data to
+ // numregs - number of registers
+ // fn - Modbus function
+ // data - if null use local registers. Otherwise use data from array to erite to slave
+ bool removeOn(TCallback::CallbackType t, TAddress address, cbModbus cb = nullptr, uint16_t numregs = 1);
+ public:
+ bool addReg(TAddress address, uint16_t value = 0, uint16_t numregs = 1);
+ bool Reg(TAddress address, uint16_t value);
+ uint16_t Reg(TAddress address);
+ bool removeReg(TAddress address, uint16_t numregs = 1);
+ bool addReg(TAddress address, uint16_t* value, uint16_t numregs = 1);
+ bool Reg(TAddress address, uint16_t* value, uint16_t numregs = 1);
+
+ bool onGet(TAddress address, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool onSet(TAddress address, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool removeOnSet(TAddress address, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool removeOnGet(TAddress address, cbModbus cb = nullptr, uint16_t numregs = 1);
+
+ virtual uint32_t eventSource() {return 0;}
+ #if defined(MODBUS_USE_STL)
+ typedef std::function cbRequest; // Callback function Type
+ typedef std::function cbRaw; // Callback function Type
+ #else
+ typedef ResultCode (*cbRequest)(FunctionCode fc, const RequestData data); // Callback function Type
+ typedef ResultCode (*cbRaw)(uint8_t*, uint8_t, void*); // Callback function Type
+ #endif
+
+ protected:
+ cbRaw _cbRaw = nullptr;
+ static ResultCode _onRequestDefault(FunctionCode fc, const RequestData data);
+ cbRequest _onRequest = _onRequestDefault;
+ public:
+ bool onRaw(cbRaw cb = nullptr);
+ bool onRequest(cbRequest cb = _onRequestDefault);
+ #if defined (MODBUSAPI_OPTIONAL)
+ protected:
+ cbRequest _onRequestSuccess = _onRequestDefault;
+ public:
+ bool onRequestSuccess(cbRequest cb = _onRequestDefault);
+ #endif
+
+ #if defined(MODBUS_FILES)
+ public:
+ #if defined(MODBUS_USE_STL)
+ bool onFile(std::function);
+ #else
+ bool onFile(ResultCode (*cb)(FunctionCode, uint16_t, uint16_t, uint16_t, uint8_t*));
+ #endif
+ private:
+ ResultCode fileOp(FunctionCode fc, uint16_t fileNum, uint16_t recNum, uint16_t recLen, uint8_t* frame);
+ protected:
+ bool readSlaveFile(uint16_t* fileNum, uint16_t* startRec, uint16_t* len, uint8_t count, FunctionCode fn);
+ // fileNum - sequental array of files numbers to read
+ // startRec - array of strart records for each file
+ // len - array of counts of records to read in terms of register size (2 bytes) for each file
+ // count - count of records to be compose in the single request
+ // fn - Modbus function. Assumed to be 0x14
+ bool writeSlaveFile(uint16_t* fileNum, uint16_t* startRec, uint16_t* len, uint8_t count, FunctionCode fn, uint8_t* data);
+ // fileNum - sequental array of files numbers to read
+ // startRec - array of strart records for each file
+ // len - array of counts of records to read in terms of register size (2 bytes) for each file
+ // count - count of records to be compose in the single request
+ // fn - Modbus function. Assumed to be 0x15
+ // data - sequental set of data records
+ #endif
+
+};
+
+#if defined(MODBUS_USE_STL)
+typedef std::function cbTransaction; // Callback skeleton for requests
+#else
+typedef bool (*cbTransaction)(Modbus::ResultCode event, uint16_t transactionId, void* data); // Callback skeleton for requests
+#endif
+//typedef Modbus::ResultCode (*cbRequest)(Modbus::FunctionCode func, TRegister* reg, uint16_t regCount); // Callback function Type
+#if defined(MODBUS_FILES)
+// Callback skeleton for file read/write
+#if defined(MODBUS_USE_STL)
+typedef std::function cbModbusFileOp;
+#else
+typedef Modbus::ResultCode (*cbModbusFileOp)(Modbus::FunctionCode func, uint16_t fileNum, uint16_t recNumber, uint16_t recLength, uint8_t* frame);
+#endif
+#endif
+
+#if defined(ARDUINO_SAM_DUE_STL)
+// Arduino Due STL workaround
+namespace std {
+ void __throw_bad_function_call();
+}
+#endif
\ No newline at end of file
diff --git a/src/ModbusAPI.h b/src/ModbusAPI.h
new file mode 100644
index 0000000..8c31a93
--- /dev/null
+++ b/src/ModbusAPI.h
@@ -0,0 +1,507 @@
+/*
+ Modbus Library for Arduino
+ Modbus public API implementation
+ Copyright (C) 2014 Andr� Sarmento Barbosa
+ 2017-2021 Alexander Emelianov (a.m.emelianov@gmail.com)
+*/
+#pragma once
+#include "Modbus.h"
+
+template
+class ModbusAPI : public T {
+ public:
+ // Alternative API
+ template
+ uint16_t read(TYPEID id, TAddress reg, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t read(TYPEID id, TAddress reg, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t write(TYPEID id, TAddress reg, uint16_t value, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t write(TYPEID id, TAddress reg, bool value, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t write(TYPEID id, TAddress reg, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t write(TYPEID id, TAddress reg, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+/*
+ template
+ uint16_t push(TYPEID id, TAddress to, TAddress from, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t pull(TYPEID id, TAddress from, TAddress to, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+*/
+ // Classic API
+ bool Hregs(uint16_t offset, uint16_t* value, uint16_t numregs = 1) {return this->Reg(HREG(offset), value);}
+ bool Coils(uint16_t offset, bool* value, uint16_t numregs = 1) {return this->Reg(COIL(offset), value);}
+ bool Istss(uint16_t offset, bool* value, uint16_t numregs = 1) {return this->Reg(ISTS(offset), value);}
+ bool Iregs(uint16_t offset, uint16_t* value, uint16_t numregs = 1) {return this->Reg(IREG(offset), value);}
+
+ //bool addHreg(uint16_t offset, uint16_t* value, uint16_t numregs = 1) {return this->addReg(HREG(offset), value);}
+ //bool addCoil(uint16_t offset, bool* value, uint16_t numregs = 1) {return this->addReg(COIL(offset), value);}
+ //bool addIsts(uint16_t offset, bool* value, uint16_t numregs = 1) {return this->addReg(ISTS(offset), value);}
+ //bool addIreg(uint16_t offset, uint16_t* value, uint16_t numregs = 1) {return this->addReg(IREG(offset), value);}
+
+ bool addHreg(uint16_t offset, uint16_t value = 0, uint16_t numregs = 1);
+ bool addCoil(uint16_t offset, bool value = false, uint16_t numregs = 1);
+ bool addIsts(uint16_t offset, bool value = false, uint16_t numregs = 1);
+ bool addIreg(uint16_t offset, uint16_t value = 0, uint16_t numregs = 1);
+
+ bool Hreg(uint16_t offset, uint16_t value);
+ bool Coil(uint16_t offset, bool value);
+ bool Ists(uint16_t offset, bool value);
+ bool Ireg(uint16_t offset, uint16_t value);
+
+ bool Coil(uint16_t offset);
+ bool Ists(uint16_t offset);
+ uint16_t Ireg(uint16_t offset);
+ uint16_t Hreg(uint16_t offset);
+
+ bool removeCoil(uint16_t offset, uint16_t numregs = 1);
+ bool removeIsts(uint16_t offset, uint16_t numregs = 1);
+ bool removeIreg(uint16_t offset, uint16_t numregs = 1);
+ bool removeHreg(uint16_t offset, uint16_t numregs = 1);
+
+ bool onGetCoil(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool onSetCoil(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool onGetHreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool onSetHreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool onGetIsts(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool onSetIsts(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool onGetIreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool onSetIreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+
+ bool removeOnGetCoil(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool removeOnSetCoil(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool removeOnGetHreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool removeOnSetHreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool removeOnGetIsts(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool removeOnSetIsts(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool removeOnGetIreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+ bool removeOnSetIreg(uint16_t offset, cbModbus cb = nullptr, uint16_t numregs = 1);
+
+ template
+ uint16_t writeCoil(TYPEID id, uint16_t offset, bool value, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t writeCoil(TYPEID id, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t readCoil(TYPEID id, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t writeHreg(TYPEID id, uint16_t offset, uint16_t value, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t writeHreg(TYPEID id, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t readIsts(TYPEID id, uint16_t offset, bool* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t readHreg(TYPEID id, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t readIreg(TYPEID id, uint16_t offset, uint16_t* value, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+
+ template
+ uint16_t pushCoil(TYPEID id, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t pullCoil(TYPEID id, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t pullIsts(TYPEID id, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t pushHreg(TYPEID id, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t pullHreg(TYPEID id, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t pullIreg(TYPEID id, uint16_t from, uint16_t to, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+
+ template
+ uint16_t pullHregToIreg(TYPEID id, uint16_t offset, uint16_t startreg, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t pullCoilToIsts(TYPEID id, uint16_t offset, uint16_t startreg, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t pushIstsToCoil(TYPEID id, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t pushIregToHreg(TYPEID id, uint16_t to, uint16_t from, uint16_t numregs = 1, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+
+ template
+ uint16_t readFileRec(TYPEID slaveId, uint16_t fileNum, uint16_t startRec, uint16_t len, uint8_t* data, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t writeFileRec(TYPEID slaveId, uint16_t fileNum, uint16_t startRec, uint16_t len, uint8_t* data, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+
+ template
+ uint16_t maskHreg(TYPEID slaveId, uint16_t offset, uint16_t andMask, uint16_t orMask, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t readWriteHreg(TYPEID slaveId, uint16_t readOffset, uint16_t* readValue, uint16_t readNumregs, uint16_t writeOffset, uint16_t* writeValue, uint16_t writeNumregs, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+
+ template
+ uint16_t rawRequest(TYPEID ip, const uint8_t* data, uint16_t len, cbTransaction cb = nullptr, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t rawResponce(TYPEID ip, const uint8_t* data, uint16_t len, uint8_t unit = MODBUSIP_UNIT);
+ template
+ uint16_t errorResponce(TYPEID ip, Modbus::FunctionCode fn, Modbus::ResultCode excode, uint8_t unit = MODBUSIP_UNIT);
+};
+
+// FNAME writeCoil, writeIsts, writeHreg, writeIreg
+// REG COIL, ISTS, HREG, IREG
+// FUNC Modbus function
+// MAXNUM Register count limit
+// VALTYPE bool, uint16_t
+// VALUE
+#define IMPLEMENT_WRITEREG(FNAME, REG, FUNC, VALUE, VALTYPE) \
+template \
+template \
+uint16_t ModbusAPI::FNAME(TYPEID ip, uint16_t offset, VALTYPE value, cbTransaction cb, uint8_t unit) { \
+ this->readSlave(offset, VALUE(value), Modbus::FUNC); \
+ return this->send(ip, REG(offset), cb, unit); \
+}
+IMPLEMENT_WRITEREG(writeCoil, COIL, FC_WRITE_COIL, COIL_VAL, bool)
+IMPLEMENT_WRITEREG(writeHreg, HREG, FC_WRITE_REG, , uint16_t)
+
+#define IMPLEMENT_WRITEREGS(FNAME, REG, FUNC, VALUE, MAXNUM, VALTYPE) \
+template \
+template \
+uint16_t ModbusAPI::FNAME(TYPEID ip, uint16_t offset, VALTYPE* value, uint16_t numregs, cbTransaction cb, uint8_t unit) { \
+ if (numregs < 0x0001 || numregs > MAXNUM) return false; \
+ this->VALUE(REG(offset), offset, numregs, Modbus::FUNC, value); \
+ return this->send(ip, REG(offset), cb, unit); \
+}
+IMPLEMENT_WRITEREGS(writeCoil, COIL, FC_WRITE_COILS, writeSlaveBits, MODBUS_MAX_BITS, bool)
+IMPLEMENT_WRITEREGS(writeHreg, HREG, FC_WRITE_REGS, writeSlaveWords, MODBUS_MAX_WORDS, uint16_t)
+
+#define IMPLEMENT_READREGS(FNAME, REG, FUNC, MAXNUM, VALTYPE) \
+template \
+template \
+uint16_t ModbusAPI::FNAME(TYPEID ip, uint16_t offset, VALTYPE* value, uint16_t numregs, cbTransaction cb, uint8_t unit) { \
+ if (numregs < 0x0001 || numregs > MAXNUM) return false; \
+ this->readSlave(offset, numregs, Modbus::FUNC); \
+ return this->send(ip, REG(offset), cb, unit, (uint8_t*)value); \
+}
+IMPLEMENT_READREGS(readCoil, COIL, FC_READ_COILS, MODBUS_MAX_BITS, bool)
+IMPLEMENT_READREGS(readHreg, HREG, FC_READ_REGS, MODBUS_MAX_WORDS, uint16_t)
+IMPLEMENT_READREGS(readIsts, ISTS, FC_READ_INPUT_STAT, MODBUS_MAX_BITS, bool)
+IMPLEMENT_READREGS(readIreg, IREG, FC_READ_INPUT_REGS, MODBUS_MAX_WORDS, uint16_t)
+
+#if defined(MODBUS_ADD_REG)
+#define ADDREG(R) this->addReg(R(to), (uint16_t)0, numregs);
+#else
+#define ADDREG(R) ;
+#endif
+#define IMPLEMENT_PULL(FNAME, REG, FUNC, MAXNUM) \
+template \
+template \
+uint16_t ModbusAPI::FNAME(TYPEID ip, uint16_t from, uint16_t to, uint16_t numregs, cbTransaction cb, uint8_t unit) { \
+ if (numregs < 0x0001 || numregs > MAXNUM) return false; \
+ ADDREG(REG) \
+ this->readSlave(from, numregs, Modbus::FUNC); \
+ return this->send(ip, REG(to), cb, unit); \
+}
+IMPLEMENT_PULL(pullCoil, COIL, FC_READ_COILS, MODBUS_MAX_BITS)
+IMPLEMENT_PULL(pullIsts, ISTS, FC_READ_INPUT_STAT, MODBUS_MAX_BITS)
+IMPLEMENT_PULL(pullHreg, HREG, FC_READ_REGS, MODBUS_MAX_WORDS)
+IMPLEMENT_PULL(pullIreg, IREG, FC_READ_INPUT_REGS, MODBUS_MAX_WORDS)
+IMPLEMENT_PULL(pullHregToIreg, IREG, FC_READ_REGS, MODBUS_MAX_WORDS)
+IMPLEMENT_PULL(pullCoilToIsts, ISTS, FC_READ_COILS, MODBUS_MAX_BITS)
+
+#define IMPLEMENT_PUSH(FNAME, REG, FUNC, MAXNUM, FINT) \
+template \
+template \
+uint16_t ModbusAPI::FNAME(TYPEID ip, uint16_t to, uint16_t from, uint16_t numregs, cbTransaction cb, uint8_t unit) { \
+ if (numregs < 0x0001 || numregs > MAXNUM) return false; \
+ if (!this->searchRegister(REG(from))) return false; \
+ this->FINT(REG(from), to, numregs, Modbus::FUNC); \
+ return this->send(ip, REG(from), cb, unit); \
+}
+IMPLEMENT_PUSH(pushCoil, COIL, FC_WRITE_COILS, MODBUS_MAX_BITS, writeSlaveBits)
+IMPLEMENT_PUSH(pushHreg, HREG, FC_WRITE_REGS, MODBUS_MAX_WORDS, writeSlaveWords)
+IMPLEMENT_PUSH(pushIregToHreg, IREG, FC_WRITE_REGS, MODBUS_MAX_WORDS, writeSlaveWords)
+IMPLEMENT_PUSH(pushIstsToCoil, ISTS, FC_WRITE_COILS, MODBUS_MAX_BITS, writeSlaveBits)
+
+template
+template
+uint16_t ModbusAPI::read(TYPEID id, TAddress reg, uint16_t* value, uint16_t numregs, cbTransaction cb, uint8_t unit) {
+ switch (reg.type) {
+ case TAddress::HREG:
+ return readHreg(id, reg.address, value, numregs, cb, unit);
+ case TAddress::IREG:
+ return readIreg(id, reg.address, value, numregs, cb, unit);
+ default:
+ return 0;
+ }
+}
+template
+template
+uint16_t ModbusAPI::read(TYPEID id, TAddress reg, bool* value, uint16_t numregs, cbTransaction cb, uint8_t unit) {
+ switch (reg.type) {
+ case TAddress::COIL:
+ return readCoil(id, reg.address, value, numregs, cb, unit);
+ case TAddress::ISTS:
+ return readIsts(id, reg.address, value, numregs, cb, unit);
+ default:
+ return 0;
+ }
+}
+template
+template
+uint16_t ModbusAPI::write(TYPEID id, TAddress reg, uint16_t value, cbTransaction cb, uint8_t unit) {
+ switch (reg.type) {
+ case TAddress::COIL:
+ return writeCoil(id, reg.address, value, cb, unit);
+ case TAddress::HREG:
+ return writeHreg(id, reg.address, value, cb, unit);
+ default:
+ return 0;
+ }
+}
+template
+template
+uint16_t ModbusAPI::write(TYPEID id, TAddress reg, bool value, cbTransaction cb, uint8_t unit) {
+ switch (reg.type) {
+ case TAddress::COIL:
+ return writeCoil(id, reg.address, value, cb, unit);
+ default:
+ return 0;
+ }
+}
+template
+template
+uint16_t ModbusAPI::write(TYPEID id, TAddress reg, uint16_t* value, uint16_t numregs, cbTransaction cb, uint8_t unit) {
+ switch (reg.type) {
+ case TAddress::COIL:
+ return writeCoil(id, reg.address, value, numregs, cb, unit);
+ case TAddress::HREG:
+ return writeHreg(id, reg.address, value, numregs, cb, unit);
+ default:
+ return 0;
+ }
+}
+template
+template
+uint16_t ModbusAPI::write(TYPEID id, TAddress reg, bool* value, uint16_t numregs, cbTransaction cb, uint8_t unit) {
+ switch (reg.type) {
+ case TAddress::COIL:
+ return writeCoil(id, reg.address, value, cb, numregs, unit);
+ default:
+ return 0;
+ }
+}
+
+template \
+bool ModbusAPI::addHreg(uint16_t offset, uint16_t value, uint16_t numregs) {
+ return this->addReg(HREG(offset), value, numregs);
+}
+template \
+bool ModbusAPI::Hreg(uint16_t offset, uint16_t value) {
+ return this->Reg(HREG(offset), value);
+}
+template \
+uint16_t ModbusAPI::Hreg(uint16_t offset) {
+ return this->Reg(HREG(offset));
+}
+template \
+bool ModbusAPI::removeHreg(uint16_t offset, uint16_t numregs) {
+ return this->removeReg(HREG(offset), numregs);
+}
+template \
+bool ModbusAPI::addCoil(uint16_t offset, bool value, uint16_t numregs) {
+ return this->addReg(COIL(offset), COIL_VAL(value), numregs);
+}
+template \
+bool ModbusAPI::addIsts(uint16_t offset, bool value, uint16_t numregs) {
+ return this->addReg(ISTS(offset), ISTS_VAL(value), numregs);
+}
+template \
+bool ModbusAPI::addIreg(uint16_t offset, uint16_t value, uint16_t numregs) {
+ return this->addReg(IREG(offset), value, numregs);
+}
+template \
+bool ModbusAPI::Coil(uint16_t offset, bool value) {
+ return this->Reg(COIL(offset), COIL_VAL(value));
+}
+template \
+bool ModbusAPI::Ists(uint16_t offset, bool value) {
+ return this->Reg(ISTS(offset), ISTS_VAL(value));
+}
+template \
+bool ModbusAPI::Ireg(uint16_t offset, uint16_t value) {
+ return this->Reg(IREG(offset), value);
+}
+template \
+bool ModbusAPI::Coil(uint16_t offset) {
+ return COIL_BOOL(this->Reg(COIL(offset)));
+}
+template \
+bool ModbusAPI::Ists(uint16_t offset) {
+ return ISTS_BOOL(this->Reg(ISTS(offset)));
+}
+template \
+uint16_t ModbusAPI::Ireg(uint16_t offset) {
+ return this->Reg(IREG(offset));
+}
+template \
+bool ModbusAPI::removeCoil(uint16_t offset, uint16_t numregs) {
+ return this->removeReg(COIL(offset), numregs);
+}
+template \
+bool ModbusAPI::removeIsts(uint16_t offset, uint16_t numregs) {
+ return this->removeReg(ISTS(offset), numregs);
+}
+template \
+bool ModbusAPI::removeIreg(uint16_t offset, uint16_t numregs) {
+ return this->removeReg(IREG(offset), numregs);
+}
+template \
+bool ModbusAPI::onGetCoil(uint16_t offset, cbModbus cb, uint16_t numregs) {
+ return this->onGet(COIL(offset), cb, numregs);
+}
+template \
+bool ModbusAPI::onSetCoil(uint16_t offset, cbModbus cb, uint16_t numregs) {
+ return this->onSet(COIL(offset), cb, numregs);
+}
+template \
+bool ModbusAPI::onGetHreg(uint16_t offset, cbModbus cb, uint16_t numregs) {
+ return this->onGet(HREG(offset), cb, numregs);
+}
+template \
+bool ModbusAPI::onSetHreg(uint16_t offset, cbModbus cb, uint16_t numregs) {
+ return this->onSet(HREG(offset), cb, numregs);
+}
+template \
+bool ModbusAPI::onGetIsts(uint16_t offset, cbModbus cb, uint16_t numregs) {
+ return this->onGet(ISTS(offset), cb, numregs);
+}
+template \
+bool ModbusAPI::onSetIsts(uint16_t offset, cbModbus cb, uint16_t numregs) {
+ return this->onSet(ISTS(offset), cb, numregs);
+}
+template \
+bool ModbusAPI::onGetIreg(uint16_t offset, cbModbus cb, uint16_t numregs) {
+ return this->onGet(IREG(offset), cb, numregs);
+}
+template \
+bool ModbusAPI::onSetIreg(uint16_t offset, cbModbus cb, uint16_t numregs) {
+ return this->onSet(IREG(offset), cb, numregs);
+}
+template \
+bool ModbusAPI::removeOnGetCoil(uint16_t offset, cbModbus cb, uint16_t numregs) {
+ return this->removeOnGet(COIL(offset), cb, numregs);
+}
+template