Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement patched esp-modbus component #7

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions mb_override_component/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)

#set(EXTRA_COMPONENT_DIRS "/home/esp-man/esp32/esp-modbus")
# override_path:

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(modbus_master)
162 changes: 162 additions & 0 deletions mb_override_component/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |

# Modbus Master Example

Note: this is original example of modbus master which uses patched component esp-modbus dowloaded from component registry.
The patched component "patched_esp-modbus" is located in components folder and allows to override the files of original component downloaded by component manager.
These source files may incude additional or changed functionality on appropriate level of code and need to be on the same place and be named the same as original component files.
The folder with the files may include the header files with the same name which will be added into the public include path and then included from user application.
This may allow to share some overriden data with user application. This code cn be used to override any source code of original component and described approach can be used for slave implementation as well.

This example demonstrates using of FreeModbus stack port implementation for ESP32 as a master device.
This implementation is able to read/write values of slave devices connected into Modbus segment. All parameters to be accessed are defined in data dictionary of the modbus master example source file.
The values represented as characteristics with its name and characteristic CID which are linked into registers of slave devices connected into Modbus segment.
The example implements simple control algorithm and checks parameters from slave device and gets alarm (relay in the slave device) when value of holding_data0 parameter exceeded limit.
The instances for the modbus parameters are common for master and slave examples and located in `examples/protocols/modbus/mb_example_common` folder.

Example parameters definition:
--------------------------------------------------------------------------------------------------
| Slave Address | Characteristic ID | Characteristic name | Description |
|---------------------|----------------------|----------------------|----------------------------|
| MB_DEVICE_ADDR1 | CID_INP_DATA_0, | Data_channel_0 | Data channel 1 |
| MB_DEVICE_ADDR1 | CID_HOLD_DATA_0, | Humidity_1 | Humidity 1 |
| MB_DEVICE_ADDR1 | CID_INP_DATA_1 | Temperature_1 | Sensor temperature |
| MB_DEVICE_ADDR1 | CID_HOLD_DATA_1, | Humidity_2 | Humidity 2 |
| MB_DEVICE_ADDR1 | CID_INP_DATA_2 | Temperature_2 | Ambient temperature |
| MB_DEVICE_ADDR1 | CID_HOLD_DATA_2 | Humidity_3 | Humidity 3 |
| MB_DEVICE_ADDR1 | CID_RELAY_P1 | RelayP1 | Alarm Relay outputs on/off |
| MB_DEVICE_ADDR1 | CID_RELAY_P2 | RelayP2 | Alarm Relay outputs on/off |
--------------------------------------------------------------------------------------------------
Note: The Slave Address is the same for all parameters for example test but it can be changed in the ```Example Data (Object) Dictionary``` table of master example to address parameters from other slaves.
The Kconfig ```Modbus slave address``` - CONFIG_MB_SLAVE_ADDR parameter in slave example can be configured to create Modbus multi slave segment.

Simplified Modbus connection schematic for example test:
```
MB_DEVICE_ADDR1
------------- -------------
| | RS485 network | |
| Slave 1 |---<>--+---<>---| Master |
| | | |
------------- -------------
```
Modbus multi slave segment connection schematic:
```
MB_DEVICE_ADDR1
-------------
| |
| Slave 1 |---<>--+
| | |
------------- |
MB_DEVICE_ADDR2 |
------------- | -------------
| | | | |
| Slave 2 |---<>--+---<>---| Master |
| | | | |
------------- | -------------
MB_DEVICE_ADDR3 |
------------- RS485 network
| | |
| Slave 3 |---<>--+
| |
-------------
```

## Hardware required :
Option 1:
PC (Modbus Slave app) + USB Serial adapter connected to USB port + RS485 line drivers + ESP32 based board

Option 2:
Several ESP32 boards flashed with modbus_slave example software to represent slave device with specific slave address (See CONFIG_MB_SLAVE_ADDR). The slave addresses for each board have to be configured as defined in "connection schematic" above.
One ESP32 board flashed with modbus_master example. All the boards require connection of RS485 line drivers (see below).

The MAX485 line driver is used as an example below but other similar chips can be used as well.
RS485 example circuit schematic for connection of master and slave devices into segment:
```
VCC ---------------+ +--------------- VCC
| |
+-------x-------+ +-------x-------+
RXD <------| RO | DIFFERENTIAL | RO|-----> RXD
| B|---------------|B |
TXD ------>| DI MAX485 | \ / | MAX485 DI|<----- TXD
ESP32 BOARD | | RS-485 side | | External PC (emulator) with USB to serial or
RTS --+--->| DE | / \ | DE|---+ ESP32 BOARD (slave)
| | A|---------------|A | |
+----| /RE | PAIR | /RE|---+-- RTS
+-------x-------+ +-------x-------+
| |
--- ---
Modbus Master device Modbus Slave device

```

## How to setup and use an example:

### Configure the application
Start the command below to setup configuration:
```
idf.py menuconfig
```
Configure the UART pins used for modbus communication using and table below.
Define the communication mode parameter for master and slave in Kconfig - CONFIG_MB_COMM_MODE (must be the same for master and slave devices in one segment).
Configure the slave address for each slave in the Modbus segment (the CONFIG_MB_SLAVE_ADDR in Kconfig).
```
------------------------------------------------------------------------------------------------------------------------------
| UART Interface | #define | Default pins for | Default pins for | External RS485 Driver Pin |
| | | ESP32 (C6) | ESP32-S2 (S3, C3, C2, H2) | |
| ----------------------|--------------------|-----------------------|---------------------------|---------------------------|
| Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | GPIO9 | DI |
| Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | GPIO8 | RO |
| Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | GPIO10 | ~RE/DE |
| Ground | n/a | GND | GND | GND |
------------------------------------------------------------------------------------------------------------------------------
```
Note: Each target chip has different GPIO pins available for UART connection. Please refer to UART documentation for selected target for more information.

Connect a USB-to-RS485 adapter to a computer, then connect the adapter's A/B output lines with the corresponding A/B output lines of the RS485 line driver connected to the ESP32 chip (see figure above).

The communication parameters of Modbus stack allow to configure it appropriately but usually it is enough to use default settings.
See the help string of parameters for more information.

### Setup external Modbus slave devices or emulator
Option 1:
Configure the external Modbus master software according to port configuration parameters used in the example. The Modbus Slave application can be used with this example to emulate slave devices with its parameters. Use official documentation for software to setup emulation of slave devices.

Option 2:
Other option is to have the modbus_slave example application flashed into ESP32 based board and connect boards together as showed on the Modbus connection schematic above. See the Modbus slave API documentation to configure communication parameters and slave addresses as defined in "Example parameters definition" table above.

### Build and flash software of master device
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```

(To exit the serial monitor, type ``Ctrl-]``.)

See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.

## Example Output
Example output of the application:
```
I (9035) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful.
I (9045) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 5.539999 (0x40b147ac) read successful.
I (9045) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful.
I (9055) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 2.560000 (0x4023d70a) read successful.
I (9065) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful.
I (9075) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful.
I (9085) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful.
I (9095) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = OFF (0xaa) read successful.
I (9605) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful.
I (9615) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 5.739999 (0x40b7ae12) read successful.
I (9615) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful.
I (9625) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 2.560000 (0x4023d70a) read successful.
I (9635) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful.
I (9645) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful.
I (9655) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful.
I (9665) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = ON (0xff) read successful.
I (10175) MASTER_TEST: Alarm triggered by cid #7.
I (10175) MASTER_TEST: Destroy master...

```
The example reads the characteristics from slave device(s), while alarm is not triggered in the slave device (See the "Example parameters definition"). The output line describes Timestamp, Cid of characteristic, Characteristic name (Units), Characteristic value (Hex).

26 changes: 26 additions & 0 deletions mb_override_component/component.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
INCLUDEDIRS := common/include
PRIV_INCLUDEDIRS := common port modbus modbus/ascii modbus/functions
PRIV_INCLUDEDIRS += modbus/rtu modbus/tcp modbus/include
PRIV_INCLUDEDIRS += serial_slave/port serial_slave/modbus_controller
PRIV_INCLUDEDIRS += serial_master/port serial_master/modbus_controller
PRIV_INCLUDEDIRS += tcp_slave/port tcp_slave/modbus_controller
PRIV_INCLUDEDIRS += tcp_master/port tcp_master/modbus_controller
SRCDIRS := common
SRCDIRS += modbus modbus/ascii modbus/functions modbus/rtu modbus/tcp
SRCDIRS += serial_slave/port serial_slave/modbus_controller
SRCDIRS += serial_master/port serial_master/modbus_controller
SRCDIRS += tcp_slave/port tcp_slave/modbus_controller
SRCDIRS += tcp_master/port tcp_master/modbus_controller
SRCDIRS += port

COMPONENT_PRIV_INCLUDEDIRS = $(addprefix freemodbus/, \
$(PRIV_INCLUDEDIRS) \
)

COMPONENT_SRCDIRS = $(addprefix freemodbus/, \
$(SRCDIRS) \
)

COMPONENT_ADD_INCLUDEDIRS = $(addprefix freemodbus/, \
$(INCLUDEDIRS) \
)
84 changes: 84 additions & 0 deletions mb_override_component/components/patched_esp-modbus/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# The following cmake file allows to patch the esp-modbus component files of original component.
# If the project needs to override any of the standard sources, the source
# file needs to be added into freemodbus folder to its place where it is located
# in original component. The patched file will be compiled instead of original one
# located in the official component freemodbus folder.

set(MB_PATCH_DIR "${CMAKE_CURRENT_SOURCE_DIR}")

set(override_srcs "${MB_PATCH_DIR}/freemodbus/patch_lib.c")
set(comp_dir)
set(comp_name)
set(comp_kconfig)
set(optional_reqs freemodbus espressif__esp-modbus)
idf_build_get_property(build_components BUILD_COMPONENTS)

foreach(req ${optional_reqs})
if(req IN_LIST build_components)
idf_component_get_property(comp_lib ${req} COMPONENT_LIB)
idf_component_get_property(comp_srcs ${req} SRCS)
idf_component_get_property(comp_dir ${req} COMPONENT_DIR)
idf_component_get_property(comp_name ${req} COMPONENT_NAME)
idf_component_get_property(comp_kconfig ${req} KCONFIG)
message(STATUS "The library ${comp_lib}, name: [${comp_name}], path: ${comp_dir} is found and will be overriden by files from [${COMPONENT_NAME}] component.")
endif()
endforeach()

set(include_dirs common/include)

set(priv_include_dirs common port modbus modbus/ascii modbus/functions
modbus/rtu modbus/tcp modbus/include)

if(EXISTS "${comp_dir}")

list(APPEND priv_include_dirs serial_slave/port serial_slave/modbus_controller
serial_master/port serial_master/modbus_controller
tcp_slave/port tcp_slave/modbus_controller
tcp_master/port tcp_master/modbus_controller)

add_prefix(include_dirs "${comp_dir}/freemodbus/" ${include_dirs})
add_prefix(priv_include_dirs "${comp_dir}/freemodbus/" ${priv_include_dirs})

if(IS_DIRECTORY ${MB_PATCH_DIR})
foreach(src ${comp_srcs})
get_filename_component(src_wo_ext ${src} NAME_WE)
file(RELATIVE_PATH src_rel "${comp_dir}" "${src}")
get_filename_component(src_dir ${src_rel} DIRECTORY)
# message(STATUS "Source relative path:${src_rel} ")
if(EXISTS "${MB_PATCH_DIR}/${src_rel}")
message(STATUS "Override source: ${MB_PATCH_DIR}/${src_dir}/${src_wo_ext}.c")
list(APPEND override_srcs ${src_dir}/${src_wo_ext}.c)
# message(STATUS "header check: ${src_dir}/${src_wo_ext}.h")
if((EXISTS "${MB_PATCH_DIR}/${src_dir}/${src_wo_ext}.h") AND (NOT ${MB_PATCH_DIR}/src_dir IN_LIST priv_include_dirs))
list(APPEND include_dirs "${MB_PATCH_DIR}/${src_dir}")
message(STATUS "Add override include: ${MB_PATCH_DIR}/${src_dir}/${src_wo_ext}.h ")
endif()
else()
if(NOT src_dir IN_LIST priv_include_dirs)
list(APPEND priv_include_dirs "${comp_dir}/${src_dir}")
endif()
list(APPEND override_srcs "${comp_dir}/${src_rel}")
endif()
endforeach()
endif()

message(STATUS "DEBUG: Use esp-modbus component folder: ${CMAKE_CURRENT_LIST_DIR}.")
endif()

set(requires driver lwip)

# esp_timer component was introduced in v4.2
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.1")
list(APPEND requires esp_timer)
endif()

idf_component_register(SRCS "${override_srcs}"
INCLUDE_DIRS "${include_dirs}"
PRIV_INCLUDE_DIRS "${priv_include_dirs}"
KCONFIG "${comp_kconfig}"
REQUIRES "${requires}"
PRIV_REQUIRES esp_netif "${comp_name}")

# This workaround allows to link this patched library into project in spite of availability of espressif_esp-modbus library
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u mb_patch_lib_include")

10 changes: 10 additions & 0 deletions mb_override_component/components/patched_esp-modbus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
This component represents the patched component of esp-modbus located in "patched_esp-modbus" folder.

How it works:

The original component version selected by project idf_component.yml file will be dowloaded
by component manager but its sources will be overriden by sources from "patched_esp-modbus/freemodbus/" folder.
Only the files that exist in this folder will be overriden during compilation. All other sources of the original component
will be used unchanged. This technique allows to override the code of original component without changing
the official esp-modbus component files. The source files to be patched needs to be added into
the same place where they located in the original component.
Loading