diff --git a/gw_slave_tcp_master_serial/CMakeLists.txt b/gw_slave_tcp_master_serial/CMakeLists.txt new file mode 100644 index 0000000..b93af75 --- /dev/null +++ b/gw_slave_tcp_master_serial/CMakeLists.txt @@ -0,0 +1,15 @@ +# 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.5) + +set(EXCLUDE_COMPONENTS examples test_app test freemodbus) + +# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. +list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) + +# Include parameters from common modbus folder +set(MB_PARAMS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../mb_example_common") +list(APPEND EXTRA_COMPONENT_DIRS "${MB_PARAMS_DIR}") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(modbus_tcp_master) diff --git a/gw_slave_tcp_master_serial/Makefile b/gw_slave_tcp_master_serial/Makefile new file mode 100644 index 0000000..4b97da6 --- /dev/null +++ b/gw_slave_tcp_master_serial/Makefile @@ -0,0 +1,13 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := modbus_tcp_master + +EXTRA_COMPONENT_DIRS := ../../../ +EXTRA_COMPONENT_DIRS += ../../mb_example_common +EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/common_components/protocol_examples_common +EXCLUDE_COMPONENTS := test freemodbus + +include $(IDF_PATH)/make/project.mk diff --git a/gw_slave_tcp_master_serial/README.md b/gw_slave_tcp_master_serial/README.md new file mode 100644 index 0000000..888b12f --- /dev/null +++ b/gw_slave_tcp_master_serial/README.md @@ -0,0 +1,166 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | + +# Modbus TCP Slave to Modbus Serial gateway example + +This example demonstrates using of FreeModbus stack port implementation for ESP32 targets as a TCP gateway device. +This implementation is able to read/write requests from external TCP Master and translate them to the slave devices connected into Modbus segment. The modbus data dictionary is not used but the mapping areas are defined in the Modbus TCP to reflect the values in its memory (this can be disabled). + +The Gateway gets requests from Slave TCP and translates the request to installed Modbus Master instance previously configured. +The translation and mapping teqnique uses the approach with wrapped callback read/write functions to make the adapter to translate the data between instances. This example is prepared from scratch to just demonstrate possible approach for the gateway. Other approaches can be used to override the component sources and realize the gateway object for data translation. The adapter functionality is located in mb_lib library which exposes some internals of installed modbus library. + +STATUS: WIP, only holding registers are supported (functionality will be extended later). + +The instances for the modbus parameters are common for master and slave examples and located in `examples/protocols/modbus/mb_example_common` folder. + +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 + ------------- ------------- + | | Network | | + | Slave 1 |---<>--+---<>---| Master | + | | | | + ------------- ------------- +``` +Modbus multi slave segment connection schematic: +``` + MB_DEVICE_ADDR1 + ------------- + | | + | Slave 1 |---<>--+ + | | | + ------------- | + MB_DEVICE_ADDR2 | + ------------- | ------------- + | | | | | + | Slave 2 |---<>--+---<>---| Master | + | | | | | + ------------- | ------------- + MB_DEVICE_ADDR3 | + ------------- Network (Ethernet or WiFi connection) + | | | + | Slave 3 |---<>--+ + | | + ------------- +``` + +## Hardware required : +Option 1: +PC (Modbus TCP Slave application) + ESP32 based development board with modbus_tcp_slave example. + +Option 2: +Several ESP32 based boards flashed with modbus_tcp_slave example software to represent slave devices. The IP slave addresses for each board have to be configured in `Modbus Example Configuration` menu according to the communication table of example. +One ESP32 based development board should be flashed with modbus_master example and connected to the same network. All the boards require configuration of network settings as described in `examples/common_components/protocol_examples_common`. + +## How to setup and use an example: + +### Configure the application +Start the command below to setup configuration: +``` +idf.py menuconfig +``` + +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. +There are three ways to configure how the master example will obtain slave IP addresses in the network: +* Enable CONFIG_MB_MDNS_IP_RESOLVER option allows to query for modbus services provided by Modbus slaves in the network and automatically configure IP table. This requires to activate the same option for each slave with unique modbus slave address configured in `Modbus Example Configuration` menu. + +### 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. + +### Connect to the device IP from external TCP Master + +Connect the external Modbus TCP Master software to the slave IP address showed in the log. The mDNS service can be used to connect to the device. + +## Example Output +Example output of the application: +``` +I (4463) esp_netif_handlers: example_netif_sta ip: 192.168.88.247, mask: 255.255.255.0, gw: 192.168.88.1 +I (4463) example_connect: Got IPv4 event: Interface "example_netif_sta" address: 192.168.88.247 +I (4473) example_common: Connected to example_netif_sta +I (4473) example_common: - IPv4 address: 192.168.88.247, +I (4483) wifi:Set ps type: 0, coexist: 0 + +I (4483) MB_TCP_SLAVE_PORT: Socket (#54), listener on port: 1502, errno=0 +I (4493) MB_TCP_SLAVE_PORT: Protocol stack initialized. +I (4553) uart: queue free spaces: 20 +I (4553) SLAVE_TEST: Modbus master stack initialized... +I (4553) SLAVE_TEST: Modbus slave stack initialized. +I (4553) SLAVE_TEST: Start modbus test... +I (11883) wifi:idx:0 (ifx:0, 64:d1:54:1a:23:5b), tid:0, ssn:5, winSize:64 +I (12923) MB_TCP_SLAVE_PORT: Socket (#55), accept client connection from address: 192.168.88.249 +W (13233) port_stub: callback __wrap_eMBRegHoldingCB, 0x3ffcd975, 1, 5 +W (13233) GW_TCP_BUF: 54 f6 00 00 00 06 01 03 0a 00 00 05 +I (13283) GW: Received response from serial slave. +I (13283) SLAVE_TEST: HOLDING READ (12756870 us), ADDR:0, TYPE:2, INST_ADDR:0x3ffb4ec4, SIZE:5 +W (14503) port_stub: callback __wrap_eMBRegHoldingCB, 0x3ffcd975, 1, 5 +W (14503) GW_TCP_BUF: 54 f7 00 00 00 06 01 03 0a 00 00 05 +I (14533) GW: Received response from serial slave. +I (14543) SLAVE_TEST: HOLDING READ (14010727 us), ADDR:0, TYPE:2, INST_ADDR:0x3ffb4ec4, SIZE:5 +W (15753) port_stub: callback __wrap_eMBRegHoldingCB, 0x3ffcd975, 1, 5 +W (15753) GW_TCP_BUF: 54 f8 00 00 00 06 01 03 0a 00 00 05 +I (15813) GW: Received response from serial slave. +I (15813) SLAVE_TEST: HOLDING READ (15284289 us), ADDR:0, TYPE:2, INST_ADDR:0x3ffb4ec4, SIZE:5 +W (17023) port_stub: callback __wrap_eMBRegHoldingCB, 0x3ffcd975, 1, 5 +W (17023) GW_TCP_BUF: 54 f9 00 00 00 06 01 03 0a 00 00 05 +I (17073) GW: Received response from serial slave. +I (17073) SLAVE_TEST: HOLDING READ (16544129 us), ADDR:0, TYPE:2, INST_ADDR:0x3ffb4ec4, SIZE:5 +W (18293) port_stub: callback __wrap_eMBRegHoldingCB, 0x3ffcd975, 1, 5 +W (18293) GW_TCP_BUF: 54 fa 00 00 00 06 01 03 0a 00 00 05 +I (18343) GW: Received response from serial slave. +I (18353) SLAVE_TEST: HOLDING READ (17818814 us), ADDR:0, TYPE:2, INST_ADDR:0x3ffb4ec4, SIZE:5 +W (18663) port_stub: callback __wrap_eMBRegHoldingCB, 0x3ffcd979, 1, 2 +W (18663) GW_TCP_BUF: 54 fb 00 00 00 0b 01 10 00 00 00 02 04 00 00 42 +W (18673) GW_TCP_BUF: 5c +I (18713) GW: Received response from serial slave. +I (18723) SLAVE_TEST: HOLDING WRITE (18192158 us), ADDR:0, TYPE:1, INST_ADDR:0x3ffb4ec4, SIZE:2 +I (18723) SLAVE_TEST: Modbus controller destroyed. +``` +The example reads the characteristics from serial slave device(s) over TCP. The output line describes the characteristics read from serial slave device and reflected in this gateway. + +Modbus Serial Slave Log: +Rx:003002-01 03 00 01 00 05 D4 09 +Tx:003003-01 03 0A 5C 42 55 AA 55 AA 55 AA 55 AA 9A BC +Rx:003004-01 03 00 01 00 05 D4 09 +Tx:003005-01 03 0A 5C 42 55 AA 55 AA 55 AA 55 AA 9A BC +Rx:003006-01 03 00 01 00 05 D4 09 +Tx:003007-01 03 0A 5C 42 55 AA 55 AA 55 AA 55 AA 9A BC +Rx:003008-01 03 00 01 00 05 D4 09 +Tx:003009-01 03 0A 5C 42 55 AA 55 AA 55 AA 55 AA 9A BC +Rx:003010-01 03 00 01 00 05 D4 09 +Tx:003011-01 03 0A 5C 42 55 AA 55 AA 55 AA 55 AA 9A BC + +Modbus TCP Master log: +Tx:007118-56 CF 00 00 00 06 01 03 00 00 00 05 +Rx:007119-56 CF 00 00 00 0D 01 03 0A 14 7A 40 FE D7 0A 40 23 EB 85 +Tx:007120-56 D0 00 00 00 06 01 03 00 00 00 05 +Rx:007121-56 D0 00 00 00 0D 01 03 0A A3 D7 41 10 D7 0A 40 23 EB 85 +Tx:007122-56 D1 00 00 00 0B 01 10 00 00 00 02 04 00 00 42 30 +Rx:007123-56 D1 00 00 00 06 01 10 00 00 00 02 +Tx:007124-56 D2 00 00 00 06 01 03 00 00 00 05 +Rx:007125-56 D2 00 00 00 0D 01 03 0A CC CD 3F 8C D7 0A 40 23 EB 85 +Tx:007126-56 D3 00 00 00 06 01 03 00 00 00 05 +Rx:007127-56 D3 00 00 00 0D 01 03 0A CC CD 40 0C D7 0A 40 23 EB 85 +Tx:007128-56 D4 00 00 00 06 01 03 00 00 00 05 +Rx:007129-56 D4 00 00 00 0D 01 03 0A 33 34 40 53 D7 0A 40 23 EB 85 + + + + + diff --git a/gw_slave_tcp_master_serial/main/CMakeLists.txt b/gw_slave_tcp_master_serial/main/CMakeLists.txt new file mode 100644 index 0000000..f9b55f5 --- /dev/null +++ b/gw_slave_tcp_master_serial/main/CMakeLists.txt @@ -0,0 +1,9 @@ +set(PROJECT_NAME "gw_slave_tcp_master_serial") + +idf_component_register(SRCS "gw_slave_tcp_master_serial.c" + INCLUDE_DIRS ".") + +add_subdirectory(mb_lib) +target_link_libraries(${COMPONENT_LIB} PUBLIC mb_lib) + +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/gw_slave_tcp_master_serial/main/Kconfig.projbuild b/gw_slave_tcp_master_serial/main/Kconfig.projbuild new file mode 100644 index 0000000..6ad76b9 --- /dev/null +++ b/gw_slave_tcp_master_serial/main/Kconfig.projbuild @@ -0,0 +1,115 @@ +menu "Modbus TCP over RTU gateway example Configuration" + + config MB_SLAVE_ADDR + int "Modbus slave address" + range 1 247 if !FMB_TCP_UID_ENABLED + range 0 247 if FMB_TCP_UID_ENABLED + default 1 + help + This is the Modbus slave address in the network. + The address is used as an index to resolve slave ip address. + + config MB_MDNS_IP_RESOLVER + bool "Resolve slave addresses using mDNS service" + default y + help + This option allows to use mDNS service to resolve IP addresses of the Modbus slaves. + If the option is disabled the ip addresses of slaves are defined in static table. + + 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 23 if IDF_TARGET_ESP32C6 + range 0 56 if IDF_TARGET_ESP32P4 + default 20 if IDF_TARGET_ESP32P4 + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 + 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 diff --git a/gw_slave_tcp_master_serial/main/component.mk b/gw_slave_tcp_master_serial/main/component.mk new file mode 100644 index 0000000..d1eb1e5 --- /dev/null +++ b/gw_slave_tcp_master_serial/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/gw_slave_tcp_master_serial/main/gw_slave_tcp_master_serial.c b/gw_slave_tcp_master_serial/main/gw_slave_tcp_master_serial.c new file mode 100644 index 0000000..1236ab0 --- /dev/null +++ b/gw_slave_tcp_master_serial/main/gw_slave_tcp_master_serial.c @@ -0,0 +1,572 @@ +/* + * SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// This example demonstrates using of FreeModbus stack port implementation for ESP32 targets as a TCP gateway device. +// This implementation is able to read/write requests from external TCP Master and translate them to the slave devices connected into Modbus segment. +// The modbus data dictionary is not used but the mapping areas are defined in the Modbus TCP to reflect the values in its memory (this can be disabled). +// The Gateway gets requests from Slave TCP instance and translates the request to installed Modbus Master instance previously configured. +// The translation and mapping teqnique uses the approach with wrapped callback read/write functions to make the adapter to translate the data between instances. +// This example is prepared from scratch to just demonstrate possible approach for the gateway. +// Other approaches can be used to override the component sources and realize the gateway object for data translation. +// The adapter functionality is located in mb_lib library which exposes some internals of installed modbus library. + +#include +#include "esp_err.h" +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "mdns.h" +#include "esp_netif.h" + +#if __has_include("esp_mac.h") +#include "esp_mac.h" +#endif + +#include "protocol_examples_common.h" + +#include "mbcontroller.h" // for mbcontroller defines and api +#include "modbus_params.h" // for modbus parameters structures + +#define MB_TCP_PORT_NUMBER (CONFIG_FMB_TCP_PORT_DEFAULT) +#define MB_MDNS_PORT (502) +#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val } + +// Defines below are used to define register start address for each type of Modbus registers +#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) >> 1)) +#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) >> 1)) +#define MB_REG_DISCRETE_INPUT_START (0x0000) +#define MB_REG_COILS_START (0x0000) +#define MB_REG_INPUT_START_AREA0 (INPUT_OFFSET(input_data0)) // register offset input area 0 +#define MB_REG_INPUT_START_AREA1 (INPUT_OFFSET(input_data4)) // register offset input area 1 +#define MB_REG_HOLDING_START_AREA0 (HOLD_OFFSET(holding_data0)) +#define MB_REG_HOLDING_START_AREA0_SIZE ((size_t)((HOLD_OFFSET(holding_data4) - HOLD_OFFSET(holding_data0)) << 1)) +#define MB_REG_HOLDING_START_AREA1 (HOLD_OFFSET(holding_data4)) +#define MB_REG_HOLDING_START_AREA1_SIZE ((size_t)((HOLD_OFFSET(holding_area1_end) - HOLD_OFFSET(holding_data4)) << 1)) +#define MB_REG_HOLDING_START_AREA2 (HOLD_OFFSET(holding_u8_a)) +#define MB_REG_HOLDING_START_AREA2_SIZE ((size_t)((HOLD_OFFSET(holding_area2_end) - HOLD_OFFSET(holding_u8_a)) << 1)) + +#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info +#define MB_CHAN_DATA_MAX_VAL (100) +#define MB_CHAN_DATA_OFFSET (1.1f) + +#define MB_READ_MASK (MB_EVENT_INPUT_REG_RD \ + | MB_EVENT_HOLDING_REG_RD \ + | MB_EVENT_DISCRETE_RD \ + | MB_EVENT_COILS_RD) +#define MB_WRITE_MASK (MB_EVENT_HOLDING_REG_WR \ + | MB_EVENT_COILS_WR) +#define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK) +#define MB_TEST_VALUE (12345.0) +#define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR) + +static const char *TAG = "SLAVE_TEST"; + +static portMUX_TYPE param_lock = portMUX_INITIALIZER_UNLOCKED; + +#if CONFIG_MB_MDNS_IP_RESOLVER + +#define MB_ID_BYTE0(id) ((uint8_t)(id)) +#define MB_ID_BYTE1(id) ((uint8_t)(((uint16_t)(id) >> 8) & 0xFF)) +#define MB_ID_BYTE2(id) ((uint8_t)(((uint32_t)(id) >> 16) & 0xFF)) +#define MB_ID_BYTE3(id) ((uint8_t)(((uint32_t)(id) >> 24) & 0xFF)) + +#define MB_ID2STR(id) MB_ID_BYTE0(id), MB_ID_BYTE1(id), MB_ID_BYTE2(id), MB_ID_BYTE3(id) + +#if CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT +#define MB_DEVICE_ID (uint32_t)CONFIG_FMB_CONTROLLER_SLAVE_ID +#endif + +#define MB_MDNS_INSTANCE(pref) pref"mb_slave_tcp" + +// convert mac from binary format to string +static inline char* gen_mac_str(const uint8_t* mac, char* pref, char* mac_str) +{ + sprintf(mac_str, "%s%02X%02X%02X%02X%02X%02X", pref, MAC2STR(mac)); + return mac_str; +} + +static inline char* gen_id_str(char* service_name, char* slave_id_str) +{ + sprintf(slave_id_str, "%s%02X%02X%02X%02X", service_name, MB_ID2STR(MB_DEVICE_ID)); + return slave_id_str; +} + +static inline char* gen_host_name_str(char* service_name, char* name) +{ + sprintf(name, "%s_%02X", service_name, MB_SLAVE_ADDR); + return name; +} + +static void start_mdns_service(void) +{ + char temp_str[32] = {0}; + uint8_t sta_mac[6] = {0}; + ESP_ERROR_CHECK(esp_read_mac(sta_mac, ESP_MAC_WIFI_STA)); + char* hostname = gen_host_name_str(MB_MDNS_INSTANCE(""), temp_str); + //initialize mDNS + ESP_ERROR_CHECK(mdns_init()); + //set mDNS hostname (required if you want to advertise services) + ESP_ERROR_CHECK(mdns_hostname_set(hostname)); + ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname); + + //set default mDNS instance name + ESP_ERROR_CHECK(mdns_instance_name_set(MB_MDNS_INSTANCE("esp32_"))); + + //structure with TXT records + mdns_txt_item_t serviceTxtData[] = { + {"board","esp32"} + }; + + //initialize service + ESP_ERROR_CHECK(mdns_service_add(hostname, "_modbus", "_tcp", MB_MDNS_PORT, serviceTxtData, 1)); + //add mac key string text item + ESP_ERROR_CHECK(mdns_service_txt_item_set("_modbus", "_tcp", "mac", gen_mac_str(sta_mac, "\0", temp_str))); + //add slave id key txt item + ESP_ERROR_CHECK( mdns_service_txt_item_set("_modbus", "_tcp", "mb_id", gen_id_str("\0", temp_str))); +} + +static void stop_mdns_service(void) +{ + mdns_free(); +} + +#endif + +// Set register values into known state +static void setup_reg_data(void) +{ + // Define initial state of parameters + discrete_reg_params.discrete_input0 = 1; + discrete_reg_params.discrete_input1 = 0; + discrete_reg_params.discrete_input2 = 1; + discrete_reg_params.discrete_input3 = 0; + discrete_reg_params.discrete_input4 = 1; + discrete_reg_params.discrete_input5 = 0; + discrete_reg_params.discrete_input6 = 1; + discrete_reg_params.discrete_input7 = 0; + + holding_reg_params.holding_data0 = 1.34; + holding_reg_params.holding_data1 = 2.56; + holding_reg_params.holding_data2 = 3.78; + holding_reg_params.holding_data3 = 4.90; + + holding_reg_params.holding_data4 = 5.67; + holding_reg_params.holding_data5 = 6.78; + holding_reg_params.holding_data6 = 7.79; + holding_reg_params.holding_data7 = 8.80; + +#if CONFIG_FMB_EXT_TYPE_SUPPORT + mb_set_uint8_a((val_16_arr *)&holding_reg_params.holding_u8_a[0], (uint8_t)0x55); + mb_set_uint8_a((val_16_arr *)&holding_reg_params.holding_u8_a[1], (uint8_t)0x55); + mb_set_uint8_b((val_16_arr *)&holding_reg_params.holding_u8_b[0], (uint8_t)0x55); + mb_set_uint8_b((val_16_arr *)&holding_reg_params.holding_u8_b[1], (uint8_t)0x55); + mb_set_uint16_ab((val_16_arr *)&holding_reg_params.holding_u16_ab[1], (uint16_t)MB_TEST_VALUE); + mb_set_uint16_ab((val_16_arr *)&holding_reg_params.holding_u16_ab[0], (uint16_t)MB_TEST_VALUE); + mb_set_uint16_ba((val_16_arr *)&holding_reg_params.holding_u16_ba[0], (uint16_t)MB_TEST_VALUE); + mb_set_uint16_ba((val_16_arr *)&holding_reg_params.holding_u16_ba[1], (uint16_t)MB_TEST_VALUE); + + mb_set_float_abcd((val_32_arr *)&holding_reg_params.holding_float_abcd[0], (float)MB_TEST_VALUE); + mb_set_float_abcd((val_32_arr *)&holding_reg_params.holding_float_abcd[1], (float)MB_TEST_VALUE); + mb_set_float_cdab((val_32_arr *)&holding_reg_params.holding_float_cdab[0], (float)MB_TEST_VALUE); + mb_set_float_cdab((val_32_arr *)&holding_reg_params.holding_float_cdab[1], (float)MB_TEST_VALUE); + mb_set_float_badc((val_32_arr *)&holding_reg_params.holding_float_badc[0], (float)MB_TEST_VALUE); + mb_set_float_badc((val_32_arr *)&holding_reg_params.holding_float_badc[1], (float)MB_TEST_VALUE); + mb_set_float_dcba((val_32_arr *)&holding_reg_params.holding_float_dcba[0], (float)MB_TEST_VALUE); + mb_set_float_dcba((val_32_arr *)&holding_reg_params.holding_float_dcba[1], (float)MB_TEST_VALUE); + + mb_set_uint32_abcd((val_32_arr *)&holding_reg_params.holding_uint32_abcd[0], (uint32_t)MB_TEST_VALUE); + mb_set_uint32_abcd((val_32_arr *)&holding_reg_params.holding_uint32_abcd[1], (uint32_t)MB_TEST_VALUE); + mb_set_uint32_cdab((val_32_arr *)&holding_reg_params.holding_uint32_cdab[0], (uint32_t)MB_TEST_VALUE); + mb_set_uint32_cdab((val_32_arr *)&holding_reg_params.holding_uint32_cdab[1], (uint32_t)MB_TEST_VALUE); + mb_set_uint32_badc((val_32_arr *)&holding_reg_params.holding_uint32_badc[0], (uint32_t)MB_TEST_VALUE); + mb_set_uint32_badc((val_32_arr *)&holding_reg_params.holding_uint32_badc[1], (uint32_t)MB_TEST_VALUE); + mb_set_uint32_dcba((val_32_arr *)&holding_reg_params.holding_uint32_dcba[0], (uint32_t)MB_TEST_VALUE); + mb_set_uint32_dcba((val_32_arr *)&holding_reg_params.holding_uint32_dcba[1], (uint32_t)MB_TEST_VALUE); + + mb_set_double_abcdefgh((val_64_arr *)&holding_reg_params.holding_double_abcdefgh[0], (double)MB_TEST_VALUE); + mb_set_double_abcdefgh((val_64_arr *)&holding_reg_params.holding_double_abcdefgh[1], (double)MB_TEST_VALUE); + mb_set_double_hgfedcba((val_64_arr *)&holding_reg_params.holding_double_hgfedcba[0], (double)MB_TEST_VALUE); + mb_set_double_hgfedcba((val_64_arr *)&holding_reg_params.holding_double_hgfedcba[1], (double)MB_TEST_VALUE); + mb_set_double_ghefcdab((val_64_arr *)&holding_reg_params.holding_double_ghefcdab[0], (double)MB_TEST_VALUE); + mb_set_double_ghefcdab((val_64_arr *)&holding_reg_params.holding_double_ghefcdab[1], (double)MB_TEST_VALUE); + mb_set_double_badcfehg((val_64_arr *)&holding_reg_params.holding_double_badcfehg[0], (double)MB_TEST_VALUE); + mb_set_double_badcfehg((val_64_arr *)&holding_reg_params.holding_double_badcfehg[1], (double)MB_TEST_VALUE); +#endif + + coil_reg_params.coils_port0 = 0x55; + coil_reg_params.coils_port1 = 0xAA; + + input_reg_params.input_data0 = 1.12; + input_reg_params.input_data1 = 2.34; + input_reg_params.input_data2 = 3.56; + input_reg_params.input_data3 = 4.78; + input_reg_params.input_data4 = 1.12; + input_reg_params.input_data5 = 2.34; + input_reg_params.input_data6 = 3.56; + input_reg_params.input_data7 = 4.78; +} + +static void slave_operation_func(void *arg) +{ + mb_param_info_t reg_info; // keeps the Modbus registers access information + + ESP_LOGI(TAG, "Modbus slave stack initialized."); + ESP_LOGI(TAG, "Start modbus test..."); + // The cycle below will be terminated when parameter holding_data0 + // incremented each access cycle reaches the CHAN_DATA_MAX_VAL value. + for(;holding_reg_params.holding_data0 < MB_CHAN_DATA_MAX_VAL;) { + // Check for read/write events of Modbus master for certain events + (void)mbc_slave_check_event(MB_READ_WRITE_MASK); + ESP_ERROR_CHECK_WITHOUT_ABORT(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT)); + const char* rw_str = (reg_info.type & MB_READ_MASK) ? "READ" : "WRITE"; + + // Filter events and process them accordingly + if(reg_info.type & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) { + // Get parameter information from parameter queue + ESP_LOGI(TAG, "HOLDING %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u", + rw_str, + (unsigned)reg_info.time_stamp, + (unsigned)reg_info.mb_offset, + (unsigned)reg_info.type, + (int)reg_info.address, + (unsigned)reg_info.size); + if (reg_info.address == (uint8_t*)&holding_reg_params.holding_data0) + { + portENTER_CRITICAL(¶m_lock); + holding_reg_params.holding_data0 += MB_CHAN_DATA_OFFSET; + if (holding_reg_params.holding_data0 >= (MB_CHAN_DATA_MAX_VAL - MB_CHAN_DATA_OFFSET)) { + coil_reg_params.coils_port1 = 0xFF; + } + portEXIT_CRITICAL(¶m_lock); + } + } else if (reg_info.type & MB_EVENT_INPUT_REG_RD) { + ESP_LOGI(TAG, "INPUT READ (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u", + (unsigned)reg_info.time_stamp, + (unsigned)reg_info.mb_offset, + (unsigned)reg_info.type, + (int)reg_info.address, + (unsigned)reg_info.size); + } else if (reg_info.type & MB_EVENT_DISCRETE_RD) { + ESP_LOGI(TAG, "DISCRETE READ (%u us): ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u", + (unsigned)reg_info.time_stamp, + (unsigned)reg_info.mb_offset, + (unsigned)reg_info.type, + (int)reg_info.address, + (unsigned)reg_info.size); + } else if (reg_info.type & (MB_EVENT_COILS_RD | MB_EVENT_COILS_WR)) { + ESP_LOGI(TAG, "COILS %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u", + rw_str, + (unsigned)reg_info.time_stamp, + (unsigned)reg_info.mb_offset, + (unsigned)reg_info.type, + (int)reg_info.address, + (unsigned)reg_info.size); + if (coil_reg_params.coils_port1 == 0xFF) break; + } + } + // Destroy of Modbus controller on alarm + ESP_LOGI(TAG,"Modbus controller destroyed."); + vTaskDelay(100); +} + +static esp_err_t init_services(void) +{ + esp_err_t result = nvs_flash_init(); + if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + result = nvs_flash_init(); + } + MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "nvs_flash_init fail, returns(0x%x).", + (int)result); + result = esp_netif_init(); + MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "esp_netif_init fail, returns(0x%x).", + (int)result); + result = esp_event_loop_create_default(); + MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "esp_event_loop_create_default fail, returns(0x%x).", + (int)result); +#if CONFIG_MB_MDNS_IP_RESOLVER + // Start mdns service and register device + start_mdns_service(); +#endif + // This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + // Read "Establishing Wi-Fi or Ethernet Connection" section in + // examples/protocols/README.md for more information about this function. + result = example_connect(); + MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "example_connect fail, returns(0x%x).", + (int)result); +#if CONFIG_EXAMPLE_CONNECT_WIFI + result = esp_wifi_set_ps(WIFI_PS_NONE); + MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "esp_wifi_set_ps fail, returns(0x%x).", + (int)result); +#endif + return ESP_OK; +} + +static esp_err_t destroy_services(void) +{ + esp_err_t err = ESP_OK; + + err = example_disconnect(); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "example_disconnect fail, returns(0x%x).", + (int)err); + err = esp_event_loop_delete_default(); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "esp_event_loop_delete_default fail, returns(0x%x).", + (int)err); + err = esp_netif_deinit(); + MB_RETURN_ON_FALSE((err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_STATE, + TAG, + "esp_netif_deinit fail, returns(0x%x).", + (int)err); + err = nvs_flash_deinit(); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "nvs_flash_deinit fail, returns(0x%x).", + (int)err); +#if CONFIG_MB_MDNS_IP_RESOLVER + stop_mdns_service(); +#endif + return err; +} + +// Mimic the data dictionary (it is needed to initialize the master but will not be used) +const mb_parameter_descriptor_t device_par_dummy[] = { +{ + 0, (char*)"Dummy_key", (char*)"Dummy_unit", 0, + MB_PARAM_INPUT, 0, 1, 0, + PARAM_TYPE_FLOAT, 4, OPTS( 0, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER } +}; + +// Modbus master initialization +static esp_err_t master_serial_init(void) +{ + // Initialize and start Modbus controller + mb_communication_info_t comm = { + .port = CONFIG_MB_UART_PORT_NUM, +#if CONFIG_MB_COMM_MODE_ASCII + .mode = MB_MODE_ASCII, +#elif CONFIG_MB_COMM_MODE_RTU + .mode = MB_MODE_RTU, +#endif + .baudrate = CONFIG_MB_UART_BAUD_RATE, + .parity = UART_PARITY_DISABLE + }; + void* master_handler = NULL; + + esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler); + MB_RETURN_ON_FALSE((master_handler != 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); + err = mbc_master_setup((void *)&comm); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller setup fail, returns(0x%x).", (int)err); + + // Set UART pin numbers + err = uart_set_pin(CONFIG_MB_UART_PORT_NUM, CONFIG_MB_UART_TXD, CONFIG_MB_UART_RXD, + CONFIG_MB_UART_RTS, 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(); + 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(CONFIG_MB_UART_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(&device_par_dummy[0], 1); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller set descriptor fail, returns(0x%x).", (int16_t)err); + ESP_LOGI(TAG, "Modbus master stack initialized..."); + return err; +} + +// Modbus slave initialization +static esp_err_t slave_tcp_init(mb_communication_info_t* comm_info) +{ + mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure + + void* slave_handler = NULL; + + // Initialization of Modbus controller + esp_err_t err = mbc_slave_init_tcp(&slave_handler); + MB_RETURN_ON_FALSE((err == ESP_OK && slave_handler != NULL), ESP_ERR_INVALID_STATE, + TAG, + "mb controller initialization fail."); + + comm_info->ip_addr = NULL; // Bind to any address + comm_info->ip_netif_ptr = (void*)get_example_netif(); + comm_info->slave_uid = MB_SLAVE_ADDR; + + // Setup communication parameters and start stack + err = mbc_slave_setup((void*)comm_info); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "mbc_slave_setup fail, returns(0x%x).", + (int)err); + + // The code below initializes Modbus register area descriptors + // for Modbus Holding Registers, Input Registers, Coils and Discrete Inputs + // Initialization should be done for each supported Modbus register area according to register map. + // When external master trying to access the register in the area that is not initialized + // by mbc_slave_set_descriptor() API call then Modbus stack + // will send exception response for this register area. + reg_area.type = MB_PARAM_HOLDING; // Set type of register area + reg_area.start_offset = MB_REG_HOLDING_START_AREA0; // Offset of register area in Modbus protocol + reg_area.address = (void*)&holding_reg_params.holding_data0; // Set pointer to storage instance + reg_area.size = (MB_REG_HOLDING_START_AREA1 - MB_REG_HOLDING_START_AREA0) << 1; // Set the size of register storage instance + err = mbc_slave_set_descriptor(reg_area); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "mbc_slave_set_descriptor fail, returns(0x%x).", + (int)err); + + reg_area.type = MB_PARAM_HOLDING; // Set type of register area + reg_area.start_offset = MB_REG_HOLDING_START_AREA1; // Offset of register area in Modbus protocol + reg_area.address = (void*)&holding_reg_params.holding_data4; + reg_area.size = MB_REG_HOLDING_START_AREA1_SIZE; + err = mbc_slave_set_descriptor(reg_area); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "mbc_slave_set_descriptor fail, returns(0x%x).", + (int)err); + +#if CONFIG_FMB_EXT_TYPE_SUPPORT + // The extended parameters register area + reg_area.type = MB_PARAM_HOLDING; + reg_area.start_offset = MB_REG_HOLDING_START_AREA2; + reg_area.address = (void*)&holding_reg_params.holding_u8_a; + reg_area.size = MB_REG_HOLDING_START_AREA2_SIZE; + err = mbc_slave_set_descriptor(reg_area); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "mbc_slave_set_descriptor fail, returns(0x%x).", + (int)err); +#endif + + // Initialization of Input Registers area + reg_area.type = MB_PARAM_INPUT; + reg_area.start_offset = MB_REG_INPUT_START_AREA0; + reg_area.address = (void*)&input_reg_params.input_data0; + reg_area.size = sizeof(float) << 2; + err = mbc_slave_set_descriptor(reg_area); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "mbc_slave_set_descriptor fail, returns(0x%x).", + (int)err); + reg_area.type = MB_PARAM_INPUT; + reg_area.start_offset = MB_REG_INPUT_START_AREA1; + reg_area.address = (void*)&input_reg_params.input_data4; + reg_area.size = sizeof(float) << 2; + err = mbc_slave_set_descriptor(reg_area); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "mbc_slave_set_descriptor fail, returns(0x%x).", + (int)err); + + // Initialization of Coils register area + reg_area.type = MB_PARAM_COIL; + reg_area.start_offset = MB_REG_COILS_START; + reg_area.address = (void*)&coil_reg_params; + reg_area.size = sizeof(coil_reg_params); + err = mbc_slave_set_descriptor(reg_area); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "mbc_slave_set_descriptor fail, returns(0x%x).", + (int)err); + + // Initialization of Discrete Inputs register area + reg_area.type = MB_PARAM_DISCRETE; + reg_area.start_offset = MB_REG_DISCRETE_INPUT_START; + reg_area.address = (void*)&discrete_reg_params; + reg_area.size = sizeof(discrete_reg_params); + err = mbc_slave_set_descriptor(reg_area); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "mbc_slave_set_descriptor fail, returns(0x%x).", + (int)err); + + // Set values into known state + setup_reg_data(); + + // Starts of modbus controller and stack + err = mbc_slave_start(); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "mbc_slave_start fail, returns(0x%x).", + (int)err); + vTaskDelay(5); + return err; +} + +static esp_err_t slave_destroy(void) +{ + esp_err_t err = mbc_slave_destroy(); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, + TAG, + "mbc_slave_destroy fail, returns(0x%x).", + (int)err); + return err; +} + +// An example application of Modbus slave. It is based on freemodbus stack. +// See deviceparams.h file for more information about assigned Modbus parameters. +// These parameters can be accessed from main application and also can be changed +// by external Modbus master host. +void app_main(void) +{ + ESP_ERROR_CHECK(init_services()); + + // Set UART log level + + esp_log_level_set(TAG, ESP_LOG_INFO); + + mb_communication_info_t comm_info = { 0 }; + +#if !CONFIG_EXAMPLE_CONNECT_IPV6 + comm_info.ip_addr_type = MB_IPV4; +#else + comm_info.ip_addr_type = MB_IPV6; +#endif + comm_info.ip_mode = MB_MODE_TCP; + + comm_info.ip_port = MB_TCP_PORT_NUMBER; + // Setup Modbus TCP Slave + ESP_ERROR_CHECK(slave_tcp_init(&comm_info)); + // Setup Modbus Serial Master + ESP_ERROR_CHECK(master_serial_init()); + + // The function is required if the gateway is still needs mapping of the read values, + // otherwise it is not required. + + // The Modbus slave logic is located in this function (user handling of Modbus) + slave_operation_func(NULL); + + ESP_ERROR_CHECK(mbc_master_destroy()); + + ESP_ERROR_CHECK(slave_destroy()); + ESP_ERROR_CHECK(destroy_services()); +} diff --git a/gw_slave_tcp_master_serial/main/idf_component.yml b/gw_slave_tcp_master_serial/main/idf_component.yml new file mode 100644 index 0000000..0c12d30 --- /dev/null +++ b/gw_slave_tcp_master_serial/main/idf_component.yml @@ -0,0 +1,8 @@ +dependencies: + idf: ">=4.1" + espressif/esp-modbus: + version: "^1.0" + espressif/mdns: + version: "^1.0.0" + rules: + - if: "idf_version >=5.0" diff --git a/gw_slave_tcp_master_serial/main/mb_lib/CMakeLists.txt b/gw_slave_tcp_master_serial/main/mb_lib/CMakeLists.txt new file mode 100644 index 0000000..35ce485 --- /dev/null +++ b/gw_slave_tcp_master_serial/main/mb_lib/CMakeLists.txt @@ -0,0 +1,56 @@ +add_library(mb_lib "${CMAKE_CURRENT_LIST_DIR}/mb_adapter.c") + +idf_component_get_property(dir espressif__esp-modbus COMPONENT_DIR) +target_include_directories(mb_lib PUBLIC + "${CMAKE_CURRENT_LIST_DIR}" + "${CMAKE_CURRENT_LIST_DIR}/include" + "${dir}/freemodbus/common" + "${dir}/freemodbus/common/include" + "${dir}/freemodbus/modbus/include" + "${dir}/freemodbus/modbus/rtu" + "${dir}/freemodbus/modbus/ascii" + "${dir}/freemodbus/modbus/tcp" + "${dir}/freemodbus/port/" + "${dir}/freemodbus/modbus/functions" + "${dir}/freemodbus/serial_slave/modbus_controller" + "${dir}/freemodbus/serial_master/modbus_controller" + "${dir}/freemodbus/tcp_slave/port" + "${dir}/freemodbus/tcp_slave/modbus_controller" + "${dir}/freemodbus/tcp_master/port" + "${dir}/freemodbus/tcp_master/modbus_controller" + ) + +idf_component_get_property(driver_lib driver COMPONENT_LIB) +target_link_libraries(mb_lib PUBLIC ${driver_lib}) +idf_component_get_property(timer_lib esp_timer COMPONENT_LIB) +target_link_libraries(mb_lib PUBLIC ${timer_lib}) +idf_component_get_property(netif_lib esp_netif COMPONENT_LIB) +target_link_libraries(mb_lib PUBLIC ${netif_lib}) + +# Wrap port functions to substitute port with port_adapter object +set(WRAP_FUNCTIONS +# eMBPoll + eMBRegInputCBSerialMaster + eMBRegHoldingCBSerialMaster + eMBRegCoilsCBSerialMaster + eMBRegDiscreteCBSerialMaster +# mbc_reg_input_slave_cb +# mbc_reg_holding_slave_cb +# mbc_reg_coils_slave_cb +# mbc_reg_discrete_slave_cb + eMBRegDiscreteCB + eMBRegCoilsCB + eMBRegHoldingCB + eMBRegInputCB +) + +message(STATUS "DEBUG: Use mb_lib library folder: ${CMAKE_CURRENT_LIST_DIR}.") + +foreach(wrap ${WRAP_FUNCTIONS}) + message(STATUS "DEBUG: wrap function ${wrap}") + target_link_libraries(mb_lib PUBLIC "-Wl,--undefined=${wrap}") + target_link_libraries(mb_lib PUBLIC "-Wl,--wrap=${wrap}") +endforeach() + +# allow multiple symbol definitions +target_link_libraries(mb_lib PUBLIC "-Wl,--allow-multiple-definition") #--allow-multiple-definition -z,multidefs \ No newline at end of file diff --git a/gw_slave_tcp_master_serial/main/mb_lib/mb_adapter.c b/gw_slave_tcp_master_serial/main/mb_lib/mb_adapter.c new file mode 100644 index 0000000..03a8540 --- /dev/null +++ b/gw_slave_tcp_master_serial/main/mb_lib/mb_adapter.c @@ -0,0 +1,248 @@ +/* + * SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" + +#include "esp_timer.h" +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_err.h" + +// #include "mb_common.h" +// #include "esp_modbus_common.h" +// #include "mbc_slave.h" + +// #include "mb_common.h" +// #include "port_common.h" +// #include "mb_config.h" +// #include "port_serial_common.h" +// #include "port_tcp_common.h" +// #include "port_adapter.h" + +#include "mb_adapter.h" +#include "mb.h" +#include "mb_m.h" +#include "esp_modbus_master.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static const char *TAG = "mb_adapter"; + +// Below are function wrappers to substitute actual port object with the adapter object for test purpose + +#define MB_MBAP_LEN (7) +#define MB_PDU_SZ (5) + +#define MB_TCP_MBAP_GET_FIELD(buffer, field) ((uint16_t)((buffer[field] << 8U) | buffer[field + 1])) +#define MB_TCP_MBAP_SET_FIELD(buffer, field, val) { \ + buffer[(field)] = (uint8_t)((val) >> 8U); \ + buffer[(field) + 1] = (uint8_t)((val) & 0xFF); \ +} + +#define MB_GET_USH_FIELD(pbuf, offset) (__extension__( \ +{ \ + ((uint16_t)((MB_GET_ELEM(pbuf, offset) << 8U) | MB_GET_ELEM(pbuf, offset + 1))); \ +} \ +)) + +#define MB_SET_USH_FIELD(pbuf, offset, val) (__extension__( \ +{ \ + MB_GET_ELEM(pbuf, offset) = (uint8_t)((val) >> 8U); \ + MB_GET_ELEM(pbuf, offset + 1) = (uint8_t)((val) & 0xFF); \ + (*(uint16_t *)(&MB_GET_ELEM(pbuf, offset))); \ +} \ +)) + +#define EACH_ITEM(array, length) \ +(typeof(*(array)) *pitem = (array); (pitem < &((array)[length])); pitem++) + +// uint8_t *pb = (uint8_t*)pitem; +#define MB_SWAP_BUFFER(pinst, size) (__extension__( \ +{ \ + for EACH_ITEM(pinst, size) { \ + (*pitem) = MB_GET_USH_FIELD(((uint8_t*)pitem), 0); \ + } \ + (pinst); \ +} \ +)) + +#define MB_GET_ELEM(buf, offset) (__extension__( \ +{ \ + (*((uint8_t *)(buf + offset))); \ +} \ +)) + +#define MB_DEVICE_MAPPING 0 + +uint8_t *mb_restore_mbap_ptr(uint8_t *pdata, uint16_t reg_offs, uint16_t reg_cnt, eMBRegisterMode mode, mb_param_request_t *preq, uint16_t *plen) +{ + uint8_t *pbuf = (uint8_t *)pdata; + mb_param_request_t request = { 0 }; + uint8_t *ret_ptr = NULL; + uint16_t length = 0; + + // Todo: other commands handling can be added later + switch (mode) { + case MB_REG_READ: + // Read input registers + // The command handling to be added here + + // Read holding registers + // TID PID LEN UID FC OFF RLEN B1 B2 + // 00 01 00 00 00 05 01 03 00 01 00 01 00 00 + // Note: callback func updates buffer for answer + // TID PID LEN UID FC CNT B1 B2 + // 00 01 00 00 00 05 01 03 02 01 00 + if ((MB_GET_ELEM(pbuf,-2) == 0x03) && (MB_GET_USH_FIELD(pbuf, -7) == 0x0000)) { + ret_ptr = &MB_GET_ELEM(pbuf, -9); +#if MB_DEVICE_MAPPING + // Note: Need to restore the offset set by function handler for correct mapping + MB_SET_USH_FIELD(pbuf, -1, reg_offs); +#endif + request.slave_addr = MB_GET_ELEM(pbuf, -3); + request.command = MB_GET_ELEM(pbuf, -2); + request.reg_start = reg_offs; + request.reg_size = reg_cnt; + length = MB_MBAP_LEN + MB_PDU_SZ; + } + break; + case MB_REG_WRITE: + // Write Holding register + // TID PID LEN UID FC OFFS RLEN B1 B2 + // 00 01 00 00 00 01 01 06 00 01 00 01 00 01 + if ((MB_GET_ELEM(pbuf, -5) == 0x06) && (MB_GET_USH_FIELD(pbuf, -10) == 0x0000)) { + ret_ptr = &MB_GET_ELEM(pbuf, -12); + request.slave_addr = MB_GET_ELEM(pbuf, -6); + request.command = MB_GET_ELEM(pbuf, -5); + request.reg_start = MB_GET_USH_FIELD(pbuf, -4); + request.reg_size = MB_GET_USH_FIELD(pbuf, -2); + length = MB_MBAP_LEN + MB_PDU_SZ + (reg_cnt << 1); + } + // Write Multiple Holding registers + // TID PID LEN UID FC OFFS RLEN BCNT B1 B2 + // 00 01 00 00 00 01 01 10 00 01 00 01 02 00 00 + // 41 29 00 00 00 0b 01 10 00 01 00 02 04 00 00 00 00 + else if((MB_GET_ELEM(pbuf, -6) == 0x10) + && (MB_GET_USH_FIELD(pbuf, -11) == 0x0000) + && ((MB_GET_USH_FIELD(pbuf, -3) << 1) == MB_GET_ELEM(pbuf, -1)) + ) { + ret_ptr = &MB_GET_ELEM(pbuf, -13); + request.slave_addr = MB_GET_ELEM(pbuf, -7); + request.command = MB_GET_ELEM(pbuf, -6); + request.reg_start = MB_GET_USH_FIELD(pbuf, -5); + request.reg_size = MB_GET_USH_FIELD(pbuf, -3); + length = MB_MBAP_LEN + MB_PDU_SZ + 1 + MB_GET_ELEM(pbuf, -1); + } + break; + default: + break; + } + if (ret_ptr) { + if (preq) *preq = request; + if (plen) *plen = length; + } + return ret_ptr; +} + +eMBErrorCode mb_master_transfer_request(uint8_t *preg_data, uint16_t reg_offs, uint16_t reg_cnt, eMBRegisterMode mode) +{ + uint16_t len_bytes = 0; + mb_param_request_t request = { 0 }; + eMBErrorCode ret = MB_EILLSTATE; + + // Note: Other safer approaches are possible for this funtion + uint8_t *mbap_ptr = mb_restore_mbap_ptr(preg_data, reg_offs, reg_cnt, mode, &request, &len_bytes); + if (mbap_ptr) { + ESP_LOG_BUFFER_HEX_LEVEL("GW_TCP_MBAP", (void*)mbap_ptr, len_bytes, ESP_LOG_WARN); + // Change the own address of master to let it handle response from slave for the UID + vMBMasterSetDestAddress(request.slave_addr); + // Todo: Need to control timeout correctly to prevent desynchronization issues + // between TCP slave and SERIAL master + esp_err_t err = mbc_master_send_request(&request, (void *)preg_data); + if (err == ESP_OK) { + ESP_LOGW(TAG, "Received response from serial slave UID: %d.", request.slave_addr); + // need to swap the buffer bytes (MB network format) + //ESP_LOG_BUFFER_HEX_LEVEL("GW_SER_BUF1", (void*)preg_data, (reg_cnt << 1), ESP_LOG_WARN); + MB_SWAP_BUFFER((uint16_t*)preg_data, reg_cnt); + ESP_LOG_BUFFER_HEX_LEVEL("GW_SER_DATA", (void*)preg_data, (reg_cnt << 1), ESP_LOG_WARN); + ret = MB_ENOERR; + } + } + return ret; +} + +eMBErrorCode __wrap_eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode) +{ + ESP_LOGW(TAG, "callback %s, %p, %u, %u", __func__, pucRegBuffer, usAddress, usNRegs); + + eMBErrorCode xRet = mb_master_transfer_request(pucRegBuffer, usAddress, usNRegs, eMode); + // In any case try to map the data to the device registers (can be disabled) +#if MB_DEVICE_MAPPING + xRet = __real_eMBRegHoldingCB(pucRegBuffer, usAddress, usNRegs, eMode); +#endif + return xRet; +} + +eMBErrorCode __wrap_eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs) +{ + // + ESP_LOGW(TAG, "callback %s, %p, %u, %u", __func__, pucRegBuffer, usAddress, usNRegs); + eMBErrorCode xRet = mb_master_transfer_request(pucRegBuffer, usAddress, usNRegs, MB_REG_READ); + +#if MB_DEVICE_MAPPING + xRet = __real_eMBRegInputCB(pucRegBuffer, usAddress, usNRegs); +#endif + + return xRet; +} + +eMBErrorCode __wrap_eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete) +{ + ESP_LOGW(TAG, "callback %s, %p, %u, %u", __func__, pucRegBuffer, usAddress, usNDiscrete); + return __real_eMBRegDiscreteCB(pucRegBuffer, usAddress, usNDiscrete); +} + +eMBErrorCode __wrap_eMBRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress, + USHORT usNCoils, eMBRegisterMode eMode) +{ + ESP_LOGW(TAG, "callback %s, %p, %u, %u", __func__, pucRegBuffer, usAddress, usNCoils); + return __real_eMBRegCoilsCB(pucRegBuffer, usAddress, usNCoils, eMode); +} + +eMBErrorCode __wrap_eMBRegInputCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress, + USHORT usNRegs) +{ + ESP_LOGW(TAG, "callback %s, %p, %u, %u", __func__, pucRegBuffer, usAddress, usNRegs); + return __real_eMBRegInputCBSerialMaster(pucRegBuffer, usAddress, usNRegs);; +} + +eMBErrorCode __wrap_eMBRegHoldingCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress, + USHORT usNRegs, eMBRegisterMode eMode) +{ + ESP_LOGW(TAG, "callback %s, %p, %u, %u", __func__, pucRegBuffer, usAddress, usNRegs); + return __real_eMBRegHoldingCBSerialMaster(pucRegBuffer, usAddress, usNRegs, eMode); +} + +eMBErrorCode __wrap_eMBRegCoilsCBSerialMaster(UCHAR* pucRegBuffer, USHORT usAddress, + USHORT usNCoils, eMBRegisterMode eMode) +{ + ESP_LOGW(TAG, "callback %s, %p, %u, %u", __func__, pucRegBuffer, usAddress, usNCoils); + return __real_eMBRegCoilsCBSerialMaster(pucRegBuffer, usAddress, usNCoils, eMode); +} + +eMBErrorCode __wrap_eMBRegDiscreteCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress, + USHORT usNDiscrete) +{ + ESP_LOGW(TAG, "callback %s, %p, %u, %u", __func__, pucRegBuffer, usAddress, usNDiscrete); + return __real_eMBRegDiscreteCBSerialMaster(pucRegBuffer, usAddress, usNDiscrete); +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/gw_slave_tcp_master_serial/main/mb_lib/mb_adapter.h b/gw_slave_tcp_master_serial/main/mb_lib/mb_adapter.h new file mode 100644 index 0000000..8183388 --- /dev/null +++ b/gw_slave_tcp_master_serial/main/mb_lib/mb_adapter.h @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_log.h" + +#include "mb.h" + +extern eMBErrorCode __real_eMBRegInputCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs); + +extern eMBErrorCode __real_eMBRegHoldingCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode); + +extern eMBErrorCode __real_eMBRegCoilsCBSerialMaster(UCHAR* pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode); + +extern eMBErrorCode __real_eMBRegDiscreteCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete); + +extern eMBErrorCode __real_mbc_reg_input_slave_cb(UCHAR * reg_buffer, USHORT address, USHORT n_regs); + +extern eMBErrorCode __real_mbc_reg_holding_slave_cb(UCHAR * reg_buffer, USHORT address, USHORT n_regs, eMBRegisterMode mode); + +extern eMBErrorCode __real_mbc_reg_coils_slave_cb(UCHAR* reg_buffer, USHORT address, USHORT n_coils, eMBRegisterMode mode); + +extern eMBErrorCode __real_mbc_reg_discrete_slave_cb(UCHAR* reg_buffer, USHORT address, USHORT n_discrete); + +extern eMBErrorCode __real_eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete); + +extern eMBErrorCode __real_eMBRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode); + +extern eMBErrorCode __real_eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode); + +extern eMBErrorCode __real_eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs); + +eMBErrorCode __wrap_eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete); + +eMBErrorCode __wrap_eMBRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress, + USHORT usNCoils, eMBRegisterMode eMode); + +eMBErrorCode __wrap_eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress, + USHORT usNRegs, eMBRegisterMode eMode); + +eMBErrorCode __wrap_eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs); \ No newline at end of file diff --git a/gw_slave_tcp_master_serial/sdkconfig.ci.ethernet b/gw_slave_tcp_master_serial/sdkconfig.ci.ethernet new file mode 100644 index 0000000..4dcb354 --- /dev/null +++ b/gw_slave_tcp_master_serial/sdkconfig.ci.ethernet @@ -0,0 +1,33 @@ +# +# Modbus configuration +# +CONFIG_FMB_COMM_MODE_TCP_EN=y +CONFIG_FMB_TCP_PORT_DEFAULT=502 +CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20 +CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 +CONFIG_FMB_PORT_TASK_PRIO=10 +CONFIG_FMB_COMM_MODE_RTU_EN=n +CONFIG_FMB_COMM_MODE_ASCII_EN=n +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000 +CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300 +CONFIG_FMB_TCP_UID_ENABLED=n +CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y +CONFIG_MB_MDNS_IP_RESOLVER=n +CONFIG_MB_SLAVE_IP_FROM_STDIN=y +CONFIG_MB_SLAVE_ADDR=1 +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +CONFIG_EXAMPLE_CONNECT_IPV6=n +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_EXAMPLE_ETHERNET_EMAC_TASK_STACK_SIZE=4096 + +CONFIG_ETH_ENABLED=y +CONFIG_ETH_USE_ESP32_EMAC=y +CONFIG_ETH_PHY_INTERFACE_RMII=y +CONFIG_ETH_USE_SPI_ETHERNET=n diff --git a/gw_slave_tcp_master_serial/sdkconfig.ci.wifi b/gw_slave_tcp_master_serial/sdkconfig.ci.wifi new file mode 100644 index 0000000..284d59f --- /dev/null +++ b/gw_slave_tcp_master_serial/sdkconfig.ci.wifi @@ -0,0 +1,19 @@ +CONFIG_FMB_COMM_MODE_TCP_EN=y +CONFIG_FMB_TCP_PORT_DEFAULT=502 +CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20 +CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 +CONFIG_FMB_PORT_TASK_PRIO=10 +CONFIG_FMB_COMM_MODE_RTU_EN=n +CONFIG_FMB_COMM_MODE_ASCII_EN=n +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000 +CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300 +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +CONFIG_FMB_TCP_UID_ENABLED=n +CONFIG_MB_MDNS_IP_RESOLVER=n +CONFIG_MB_SLAVE_IP_FROM_STDIN=y +CONFIG_EXAMPLE_CONNECT_IPV6=n +CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y +CONFIG_EXAMPLE_CONNECT_ETHERNET=n +CONFIG_EXAMPLE_CONNECT_WIFI=y +CONFIG_EXAMPLE_WIFI_SSID="${CI_WIFI_SSID}" +CONFIG_EXAMPLE_WIFI_PASSWORD="${CI_WIFI_PASSW}" diff --git a/gw_slave_tcp_master_serial/sdkconfig.defaults b/gw_slave_tcp_master_serial/sdkconfig.defaults new file mode 100644 index 0000000..ded9b40 --- /dev/null +++ b/gw_slave_tcp_master_serial/sdkconfig.defaults @@ -0,0 +1,21 @@ +# +# Modbus configuration +# +CONFIG_FMB_COMM_MODE_TCP_EN=y +CONFIG_FMB_TCP_PORT_DEFAULT=1502 +CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20 +CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 +CONFIG_FMB_PORT_TASK_PRIO=10 +CONFIG_FMB_COMM_MODE_RTU_EN=y +CONFIG_FMB_COMM_MODE_ASCII_EN=y +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=2000 +CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300 +CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y +CONFIG_MB_MDNS_IP_RESOLVER=n +CONFIG_FMB_TCP_UID_ENABLED=n +CONFIG_MB_SLAVE_IP_FROM_STDIN=y +CONFIG_EXAMPLE_CONNECT_IPV6=n +CONFIG_EXAMPLE_CONNECT_ETHERNET=n +CONFIG_EXAMPLE_CONNECT_WIFI=y +CONFIG_EXAMPLE_WIFI_SSID="${CI_WIFI_SSID}" +CONFIG_EXAMPLE_WIFI_PASSWORD="${CI_WIFI_PASSW}" \ No newline at end of file diff --git a/mb_example_common/CMakeLists.txt b/mb_example_common/CMakeLists.txt new file mode 100644 index 0000000..828fc58 --- /dev/null +++ b/mb_example_common/CMakeLists.txt @@ -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) +idf_component_register(SRCS "modbus_params.c" + INCLUDE_DIRS "include") +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/mb_example_common/README.md b/mb_example_common/README.md new file mode 100644 index 0000000..4a08b7e --- /dev/null +++ b/mb_example_common/README.md @@ -0,0 +1,10 @@ +# Modbus Example Common + +This directory contains component that is common for Modbus master and slave examples. The component defines Modbus parameters that are shared between examples and provide code that you can copy and adapt into your own projects. +For more information please refer to Modbus example README.md files located in the folders: + +* `examples/protocols/modbus/serial/mb_master` Modbus serial master implementation (RTU and ASCII) +* `examples/protocols/modbus/serial/mb_slave` Modbus serial slave implementation (RTU and ASCII) +* `examples/protocols/modbus/serial/mb_master` Modbus serial master implementation (RTU and ASCII) +* `examples/protocols/modbus/tcp/mb_tcp_slave` Modbus serial slave implementation (TCP) +* `examples/protocols/modbus/tcp/mb_tcp_master` Modbus serial master implementation (TCP) \ No newline at end of file diff --git a/mb_example_common/component.mk b/mb_example_common/component.mk new file mode 100644 index 0000000..f0dc18c --- /dev/null +++ b/mb_example_common/component.mk @@ -0,0 +1,5 @@ +# +# Component Makefile +# +COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_SRCDIRS := . diff --git a/mb_example_common/include/modbus_params.h b/mb_example_common/include/modbus_params.h new file mode 100644 index 0000000..9b765bb --- /dev/null +++ b/mb_example_common/include/modbus_params.h @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/*===================================================================================== + * Description: + * The Modbus parameter structures used to define Modbus instances that + * can be addressed by Modbus protocol. Define these structures per your needs in + * your application. Below is just an example of possible parameters. + *====================================================================================*/ +#ifndef _DEVICE_PARAMS +#define _DEVICE_PARAMS + +#include "sdkconfig.h" + +// This file defines structure of modbus parameters which reflect correspond modbus address space +// for each modbus register type (coils, discreet inputs, holding registers, input registers) +#pragma pack(push, 1) +typedef struct +{ + uint8_t discrete_input0:1; + uint8_t discrete_input1:1; + uint8_t discrete_input2:1; + uint8_t discrete_input3:1; + uint8_t discrete_input4:1; + uint8_t discrete_input5:1; + uint8_t discrete_input6:1; + uint8_t discrete_input7:1; + uint8_t discrete_input_port1; + uint8_t discrete_input_port2; +} discrete_reg_params_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct +{ + uint8_t coils_port0; + uint8_t coils_port1; + uint8_t coils_port2; +} coil_reg_params_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct +{ + float input_data0; // 0 + float input_data1; // 2 + float input_data2; // 4 + float input_data3; // 6 + uint16_t data[150]; // 8 + 150 = 158 + float input_data4; // 158 + float input_data5; + float input_data6; + float input_data7; + uint16_t data_block1[150]; +} input_reg_params_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct +{ +#if CONFIG_FMB_EXT_TYPE_SUPPORT + uint16_t holding_u8_a[2]; + uint16_t holding_u8_b[2]; + uint16_t holding_u16_ab[2]; + uint16_t holding_u16_ba[2]; + uint32_t holding_uint32_abcd[2]; + uint32_t holding_uint32_cdab[2]; + uint32_t holding_uint32_badc[2]; + uint32_t holding_uint32_dcba[2]; + float holding_float_abcd[2]; + float holding_float_cdab[2]; + float holding_float_badc[2]; + float holding_float_dcba[2]; + double holding_double_abcdefgh[2]; + double holding_double_hgfedcba[2]; + double holding_double_ghefcdab[2]; + double holding_double_badcfehg[2]; + uint32_t holding_area2_end; +#endif + float holding_data0; + float holding_data1; + float holding_data2; + float holding_data3; + uint16_t test_regs[150]; + float holding_data4; + float holding_data5; + float holding_data6; + float holding_data7; + uint32_t holding_area1_end; +} holding_reg_params_t; +#pragma pack(pop) + +extern holding_reg_params_t holding_reg_params; +extern input_reg_params_t input_reg_params; +extern coil_reg_params_t coil_reg_params; +extern discrete_reg_params_t discrete_reg_params; + +#endif // !defined(_DEVICE_PARAMS) diff --git a/mb_example_common/modbus_params.c b/mb_example_common/modbus_params.c new file mode 100644 index 0000000..8a5bbd4 --- /dev/null +++ b/mb_example_common/modbus_params.c @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/*===================================================================================== + * Description: + * C file to define parameter storage instances + *====================================================================================*/ +#include +#include "modbus_params.h" + +// Here are the user defined instances for device parameters packed by 1 byte +// These are keep the values that can be accessed from Modbus master +holding_reg_params_t holding_reg_params = { 0 }; + +input_reg_params_t input_reg_params = { 0 }; + +coil_reg_params_t coil_reg_params = { 0 }; + +discrete_reg_params_t discrete_reg_params = { 0 };