Skip to content
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
6 changes: 6 additions & 0 deletions modbus_serial_cpp_test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# The following five 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.5)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mb_serial_master_cpp)
105 changes: 105 additions & 0 deletions modbus_serial_cpp_test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Simplified Modbus Master example
Initializes Modbus master interface and reads and writes holding registers and coild in the connected Modbus device.
This example uses preliminary CPP ModbusMaster class `main/ModbusMaster.cpp/h` to read/write Modbus registers.
The class is updated from Arduino project and allow to use ESP_Modbus library in CPP projects.

Note: This project is not final and is prepared just for demonstration for CPP users.

# Setup example

All required configuration data is in one source file master.c.

Configure the parameters below or leave them as is:
```
#define MASTER_PORT_NUM 2 // UART port number
#define MASTER_SPEED 115200 // Modbus communication speed
#define MB_UART_RXD_PIN 22 // Modbus RS485 interface pins accordingly
#define MB_UART_TXD_PIN 23
#define MB_UART_RTS_PIN 18
```

Simplified Modbus connection schematic for example test:
```
MB_SLAVE_SHORT_ADDRESS
------------- -------------
| | RS485 network | |
| Slave 1 |---<>--+---<>---| Master |
| | | |
------------- -------------
```

# Setup connections

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 WROVER KIT 1 | | RS-485 side | | External PC (emulator) with USB to serial or
RTS --+--->| DE | / \ | DE|---+ ESP32 WROVER KIT 2 (slave)
| | A|---------------|A | |
+----| /RE | PAIR | /RE|---+-- RTS
+-------x-------+ +-------x-------+
| |
--- ---
Modbus Master device Modbus Slave device

```


```
idf.py build
idf.py -p PORT -b 921600 flash monitor
```

In order to read or write other registers of device, edit the device_parameters table.

By default it is supposed that your device contains five holding registers at Modbus address = 0.
Every retry cycle Master reads the holding registers of slave defined as `MB_SLAVE_SHORT_ADDRESS`. Also reads the coil registers and invert some bits defined in masked value.

Note: Enable `CONFIG_FMB_TIMER_ISR_IN_IRAM` option to decrease handling delays if you use NVS flash functions in your application.
`CONFIG_FMB_TIMER_PORT_ENABLED` should be disabled to avoid failures when you have other tasks started on the same CPU.
This increases delays of MB processing but allows to decrease possible issues.
`CONFIG_FMB_SERIAL_TASK_PRIO` - should be set to your (highest task priority + 1) executed on the same CPU.
As an alternative replace the sources in ESP_IDF folders with the `*.c/h files` from components folder of this project.

## Example Output:
```
I (360) MB_MASTER: Modbus master stack initialized...
I (550) MB: COILS: 0x5555
I (550) MB: 11 11 22 22 33 33 44 44 55 55
I (790) MB: COILS: 0xFFFF
I (790) MB: 11 11 22 22 33 33 44 44 55 55
I (1010) MB: COILS: 0x5555
I (1010) MB: 11 11 22 22 33 33 44 44 55 55
I (1190) MB: COILS: 0xFFFF
I (1190) MB: 11 11 22 22 33 33 44 44 55 55
I (1380) MB: COILS: 0x5555
I (1380) MB: 11 11 22 22 33 33 44 44 55 55
I (1590) MB: COILS: 0xFFFF
I (1590) MB: 11 11 22 22 33 33 44 44 55 55
I (1840) MB: COILS: 0x5555
I (1840) MB: 11 11 22 22 33 33 44 44 55 55
I (2040) MB: COILS: 0xFFFF
I (2040) MB: 11 11 22 22 33 33 44 44 55 55
I (2230) MB: COILS: 0x5555
I (2230) MB: 11 11 22 22 33 33 44 44 55 55
I (2450) MB: COILS: 0xFFFF
I (2450) MB: 11 11 22 22 33 33 44 44 55 55
I (2450) MODBUS: Modbus Test completed.
```

Possible issues:

E (9714) MODBUS_MASTER: Characteristic #0 MB_hold_reg-0 (Data), parameter read fail.
E (9874) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x107) (ESP_ERR_TIMEOUT).
These errors mean that slave device is not able to communicate. Check board connections.





2 changes: 2 additions & 0 deletions modbus_serial_cpp_test/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "master.cpp"
INCLUDE_DIRS ".")
99 changes: 99 additions & 0 deletions modbus_serial_cpp_test/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
menu "Modbus Example Configuration"

config MB_UART_PORT_ONE
bool
default y
depends on (ESP_CONSOLE_UART_NUM !=1) && (SOC_UART_NUM > 1)

config MB_UART_PORT_TWO
bool
default y
depends on (ESP_CONSOLE_UART_NUM !=2) && (SOC_UART_NUM > 2)

config MB_UART_PORT_NUM
int "UART port number"
range 0 2 if MB_UART_PORT_TWO
default 2 if MB_UART_PORT_TWO
range 0 1 if MB_UART_PORT_ONE
default 1 if MB_UART_PORT_ONE
help
UART communication port number for Modbus example.

config MB_UART_BAUD_RATE
int "UART communication speed"
range 1200 115200
default 115200
help
UART communication speed for Modbus example.

config MB_UART_RXD
int "UART RXD pin number"
range 0 34 if IDF_TARGET_ESP32
range 0 23 if IDF_TARGET_ESP32C6
range 0 56 if IDF_TARGET_ESP32P4
default 22 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32P4
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
range 0 20 if IDF_TARGET_ESP32C2
range 0 27 if IDF_TARGET_ESP32H2
default 8 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 8 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2
help
GPIO number for UART RX pin. See UART documentation for more information
about available pin numbers for UART.

config MB_UART_TXD
int "UART TXD pin number"
range 0 34 if IDF_TARGET_ESP32
range 0 23 if IDF_TARGET_ESP32C6
range 0 56 if IDF_TARGET_ESP32P4
default 23 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32P4
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
range 0 20 if IDF_TARGET_ESP32C2
range 0 27 if IDF_TARGET_ESP32H2
default 9 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 9 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2
help
GPIO number for UART TX pin. See UART documentation for more information
about available pin numbers for UART.

config MB_UART_RTS
int "UART RTS pin number"
range 0 34 if IDF_TARGET_ESP32
range 0 56 if IDF_TARGET_ESP32P4
range 0 23 if IDF_TARGET_ESP32C6
default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6
default 20 if IDF_TARGET_ESP32P4
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
range 0 20 if IDF_TARGET_ESP32C2
range 0 27 if IDF_TARGET_ESP32H2
default 10 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 10 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2
help
GPIO number for UART RTS pin. This pin is connected to
~RE/DE pin of RS485 transceiver to switch direction.
See UART documentation for more information about available pin
numbers for UART.

choice MB_COMM_MODE
prompt "Modbus communication mode"
default MB_COMM_MODE_RTU if CONFIG_FMB_COMM_MODE_RTU_EN
help
Selection of Modbus communication mode option for Modbus.

config MB_COMM_MODE_RTU
bool "RTU mode"
depends on FMB_COMM_MODE_RTU_EN

config MB_COMM_MODE_ASCII
bool "ASCII mode"
depends on FMB_COMM_MODE_ASCII_EN

endchoice

endmenu
4 changes: 4 additions & 0 deletions modbus_serial_cpp_test/main/component.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
6 changes: 6 additions & 0 deletions modbus_serial_cpp_test/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
dependencies:
idf: ">=5.0"
espressif/esp-modbus:
version: "^2.0.0"
override_path: "/home/esp-man/esp32/5_modbus/esp-modbus"

105 changes: 105 additions & 0 deletions modbus_serial_cpp_test/main/master.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "string.h"
#include "esp_log.h"

#define MASTER_MAX_CIDS 2
#define MASTER_MAX_RETRY 100
#define MASTER_PORT_NUM (uart_port_t)2
#define MASTER_SPEED 115200
#define MASTER_TAG "MODBUS_MASTER"
#define MB_UART_RXD_PIN 22
#define MB_UART_TXD_PIN 23
#define MB_UART_RTS_PIN 18

#define TAG "MB_MASTER_MAIN"
#define MB_SLAVE_SHORT_ADDRESS 1
#define MB_RETRIES 50

#include "sdkconfig.h"

#include "mbcontroller.h"

#ifdef __cplusplus
extern "C" {
#endif

enum {
MB_DEVICE_ADDR1 = 1
};

// Enumeration of all supported CIDs for device (used in parameter definition table)
enum {
CID_DEV_REG0 = 0,
CID_DEV_REG1
};


#define STR(fieldname) ((const char*)( fieldname ))
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }

static void *master_handle = NULL;

// Example Data (Object) Dictionary for Modbus parameters
const mb_parameter_descriptor_t device_parameters[2] = {
// CID, Name, Units, Modbus addr, register type, Modbus Reg Start Addr, Modbus Reg read length,
// Instance offset (NA), Instance type, Instance length (bytes), Options (NA), Permissions
{ CID_DEV_REG0, STR("MB_hold_reg-0"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 1,
0, PARAM_TYPE_U16, PARAM_SIZE_U16, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_DEV_REG1, STR("MB_hold_reg-1"), STR("Data"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 3, 1,
0, PARAM_TYPE_U16, PARAM_SIZE_U16, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }
};

// Calculate number of parameters in the table
const uint16_t num_device_parameters = (sizeof(device_parameters)/sizeof(device_parameters[0]));

// Modbus master initialization
static esp_err_t master_init(void)
{
// Initialize Modbus controller
mb_communication_info_t comm;
comm.ser_opts.port = (uart_port_t)MASTER_PORT_NUM;
comm.ser_opts.mode = (mb_comm_mode_t)MB_RTU;
comm.ser_opts.baudrate = MASTER_SPEED;
comm.ser_opts.parity = MB_PARITY_NONE;
comm.ser_opts.uid = 0;
comm.ser_opts.response_tout_ms = 1000;
comm.ser_opts.data_bits = UART_DATA_8_BITS;
comm.ser_opts.stop_bits = UART_STOP_BITS_1;

esp_err_t err = mbc_master_create_serial(&comm, &master_handle);
MB_RETURN_ON_FALSE((master_handle != NULL), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail.");
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail, returns(0x%x).", (int)err);

// Set UART pin numbers
err = uart_set_pin(MASTER_PORT_NUM, MB_UART_TXD_PIN, MB_UART_RXD_PIN,
MB_UART_RTS_PIN, UART_PIN_NO_CHANGE);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set pin failure, uart_set_pin() returned (0x%x).", (int)err);

err = mbc_master_start(master_handle);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller start fail, returned (0x%x).", (int)err);

// Set driver mode to Half Duplex
err = uart_set_mode(MASTER_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set mode failure, uart_set_mode() returned (0x%x).", (int)err);

err = mbc_master_set_descriptor(master_handle, &device_parameters[0], num_device_parameters);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller set descriptor fail, returns(0x%x).", (int)err);

ESP_LOGI(TAG, "Modbus master stack initialized...");
return err;
}

void app_main(void)
{
// Initialization of device peripheral and objects
ESP_ERROR_CHECK(master_init());
}

#ifdef __cplusplus
}
#endif
6 changes: 6 additions & 0 deletions modbus_simple_master/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# The following five 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.5)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(simple_mb_master)
9 changes: 9 additions & 0 deletions modbus_simple_master/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#

PROJECT_NAME := blink

include $(IDF_PATH)/make/project.mk

Loading