diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46ceede --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +.config +*.o +*.pyc + +# gtags +GTAGS +GRTAGS +GPATH + +# emacs +.dir-locals.el + +# emacs temp file suffixes +*~ +.#* +\#*# + +# eclipse setting +.settings + +# MacOS directory files +.DS_Store + +# Test files +test/build +test/sdkconfig +test/sdkconfig.old + +# Doc build artifacts +docs/_build/ +docs/doxygen-warning-log.txt +docs/sphinx-warning-log.txt +docs/sphinx-warning-log-sanitized.txt +docs/xml/ +docs/xml_in/ +docs/man/ +docs/doxygen_sqlite3.db + +TEST_LOGS + + +# gcov coverage reports +*.gcda +*.gcno +coverage.info +coverage_report/ + +# VS Code Settings +.vscode/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..ad0e049 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,177 @@ +stages: + - build + - deploy + +variables: + # System environment + ESP_DOCS_ENV_IMAGE: "$CI_DOCKER_REGISTRY/esp-idf-doc-env-v5.0:2-2" + ESP_DOCS_PATH: "$CI_PROJECT_DIR" + + # GitLab-CI environment + GET_SOURCES_ATTEMPTS: "10" + ARTIFACT_DOWNLOAD_ATTEMPTS: "10" + GIT_SUBMODULE_STRATEGY: none + + ESP_IDF_GIT: "https://gitlab-ci-token:${CI_JOB_TOKEN}@${GITLAB_HTTPS_SERVER}/espressif/esp-idf.git" + +.setup_idf_tools: &setup_idf_tools | + tools/idf_tools.py --non-interactive install && eval "$(tools/idf_tools.py --non-interactive export)" || exit 1 + +.add_gh_key_remote: &add_gh_key_remote | + command -v ssh-agent >/dev/null || exit 1 + eval $(ssh-agent -s) + printf '%s\n' "${GH_PUSH_KEY}" | tr -d '\r' | ssh-add - > /dev/null + mkdir -p ~/.ssh && chmod 700 ~/.ssh + [[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config || ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts + git remote remove github || true + git remote add github ${GH_PUSH_REPO} + +after_script: + # Just for cleaning space, no other causes + - git clean -ffdx + + +# This template gets expanded multiple times, once for every IDF version. +# IDF version is specified by setting the espressif/idf image tag. +# +# EXAMPLE_TARGETS sets the list of IDF_TARGET values to build examples for. +# It should be equal to the list of targets supported by the specific IDF version. +# +# TEST_TARGETS sets the list of IDF_TARGET values to build the test_app for. +# It should contain only the targets with optimized assembly implementations. +# +.build_template: + stage: build + tags: + - build + - internet + script: + - ./build_all.sh + variables: + EXAMPLE_TARGETS: "esp32" + TEST_TARGETS: "esp32" + +build_idf_v4.1: + extends: .build_template + image: espressif/idf:release-v4.1 + +build_idf_v4.2: + extends: .build_template + image: espressif/idf:release-v4.2 + variables: + EXAMPLE_TARGETS: "esp32 esp32s2" + +build_idf_v4.3: + extends: .build_template + image: espressif/idf:release-v4.3 + variables: + EXAMPLE_TARGETS: "esp32 esp32s2 esp32c3" + +build_idf_v4.4: + extends: .build_template + image: espressif/idf:release-v4.4 + variables: + EXAMPLE_TARGETS: "esp32 esp32s2 esp32s3 esp32c3" + TEST_TARGETS: "esp32 esp32s3" + +build_idf_latest: + extends: .build_template + image: espressif/idf:latest + variables: + EXAMPLE_TARGETS: "esp32 esp32s2 esp32s3 esp32c3" + TEST_TARGETS: "esp32 esp32s3" + # GNU Make based build system is not supported starting from IDF v5.0 + SKIP_GNU_MAKE_BUILD: 1 + +build_docs: + stage: build + image: $ESP_DOCS_ENV_IMAGE + tags: + - build_docs + artifacts: + when: always + paths: + - docs/_build/*/*/*.txt + - docs/_build/*/*/html/* + expire_in: 4 days + # No cleaning when the artifacts + after_script: [] + script: + - cd docs + - pip install -r requirements.txt + - build-docs -l en -t esp32 + +.deploy_docs_template: + stage: deploy + image: $ESP_DOCS_ENV_IMAGE + tags: + - deploy_docs + needs: + - build_docs + only: + changes: + - "docs/**/*" + script: + - source ${CI_PROJECT_DIR}/docs/utils.sh + - add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER + - export GIT_VER=$(git describe --always) + - pip install -r ${CI_PROJECT_DIR}/docs/requirements.txt + - deploy-docs + +deploy_docs_preview: + extends: + - .deploy_docs_template + except: + refs: + - master + variables: + TYPE: "preview" + DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/" + DOCS_DEPLOY_PRIVATEKEY: "$DOCS_DEPLOY_KEY" + DOCS_DEPLOY_SERVER: "$DOCS_SERVER" + DOCS_DEPLOY_SERVER_USER: "$DOCS_SERVER_USER" + DOCS_DEPLOY_PATH: "$DOCS_PATH" + DOCS_DEPLOY_URL_BASE: "https://$DOCS_PREVIEW_SERVER_URL/docs/esp-modbus" + +deploy_docs_production: + extends: + - .deploy_docs_template + only: + refs: + - master + variables: + TYPE: "production" + DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/" + DOCS_DEPLOY_PRIVATEKEY: "$DOCS_PROD_DEPLOY_KEY" + DOCS_DEPLOY_SERVER: "$DOCS_PROD_SERVER" + DOCS_DEPLOY_SERVER_USER: "$DOCS_PROD_SERVER_USER" + DOCS_DEPLOY_PATH: "$DOCS_PROD_PATH" + DOCS_DEPLOY_URL_BASE: "https://docs.espressif.com/projects/esp-modbus" + +push_master_to_github: + stage: deploy + tags: + - deploy + only: + - master + - /^release\/v/ + - /^v\d+\.\d+(\.\d+)?($|-)/ + when: on_success + script: + - git clone --depth 1 ${ESP_IDF_GIT} esp-idf + - *add_gh_key_remote + - esp-idf/tools/ci/push_to_github.sh + +upload_to_component_manager: + stage: deploy + image: python:3.10-alpine + tags: + - deploy + rules: + - if: '$CI_COMMIT_BRANCH == "master"' + - if: '$FORCE_PUSH_COMPONENT == "1"' + script: + - pip install idf-component-manager + - export IDF_COMPONENT_API_TOKEN=${ESP_MODBUS_API_KEY} + - python -m idf_component_manager upload-component --allow-existing --name=esp-modbus --namespace=espressif + diff --git a/components/freemodbus/CMakeLists.txt b/CMakeLists.txt similarity index 70% rename from components/freemodbus/CMakeLists.txt rename to CMakeLists.txt index aea9e8d..0f773ae 100644 --- a/components/freemodbus/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,5 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly set(srcs "common/esp_modbus_master.c" "common/esp_modbus_slave.c" @@ -44,14 +46,20 @@ set(srcs set(include_dirs common/include) set(priv_include_dirs common port modbus modbus/ascii modbus/functions - modbus/rtu modbus/tcp modbus/include) + modbus/rtu modbus/tcp modbus/include) 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) + serial_master/port serial_master/modbus_controller + tcp_slave/port tcp_slave/modbus_controller + tcp_master/port tcp_master/modbus_controller) + +add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/freemodbus/" ${srcs}) +add_prefix(include_dirs "${CMAKE_CURRENT_LIST_DIR}/freemodbus/" ${include_dirs}) +add_prefix(priv_include_dirs "${CMAKE_CURRENT_LIST_DIR}/freemodbus/" ${priv_include_dirs}) + +message(STATUS "DEBUG: Use esp-modbus component folder: ${CMAKE_CURRENT_LIST_DIR}.") idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "${include_dirs}" PRIV_INCLUDE_DIRS "${priv_include_dirs}" - REQUIRES driver) + REQUIRES driver lwip) diff --git a/components/freemodbus/Kconfig b/Kconfig similarity index 100% rename from components/freemodbus/Kconfig rename to Kconfig diff --git a/build_all.sh b/build_all.sh new file mode 100755 index 0000000..6fe4402 --- /dev/null +++ b/build_all.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# +# Build the test app and all examples from the examples directory. +# Expects TEST_TARGETS environment variables to be set. +# Each variable is the list of IDF_TARGET values to build the examples and +# the test app for, respectively. +# +# ----------------------------------------------------------------------------- +# Safety settings (see https://gist.github.com/ilg-ul/383869cbb01f61a51c4d). + +if [[ -n "${DEBUG_SHELL}" ]] +then + set -x # Activate the expand mode if DEBUG is anything but empty. +fi + +if [[ -z "${EXAMPLE_TARGETS}" || -z "${TEST_TARGETS}" ]] +then + echo "EXAMPLE_TARGETS and TEST_TARGETS environment variables must be set before calling this script" + exit 1 +fi + +if [[ -z "${SKIP_GNU_MAKE_BUILD}" ]] +then + echo "SKIP_GNU_MAKE_BUILD not set, will build with GNU Make based build system as well." + export SKIP_GNU_MAKE_BUILD=0 +fi + +set -o errexit # Exit if command failed. +set -o pipefail # Exit if pipe failed. +set -o nounset # Exit if variable not set. + + +STARS='***************************************************' + +# ----------------------------------------------------------------------------- + +die() { + echo "${1:-"Unknown Error"}" 1>&2 + exit 1 +} + +# build_for_targets +# call this in the project directory +function build_for_targets +{ + target_list="$1" + for IDF_TARGET in ${target_list} + do + export IDF_TARGET + if [[ "${IDF_TARGET}" = "esp32" ]] && [[ "${SKIP_GNU_MAKE_BUILD}" = "0" ]] + then + echo "${STARS}" + echo "Building in $PWD with Make" + # -j option will be set via MAKEFLAGS in .gitlab-ci.yml + # shellcheck disable=SC2015 + make defconfig && make || die "Make build in ${PWD} has failed" + rm -rf build + fi + + echo "${STARS}" + echo "Building in $PWD with CMake for ${IDF_TARGET}" + if [[ ${IDF_TARGET} != "esp32" ]] + then + # IDF 4.0 doesn't support idf.py set-target, and only supports esp32. + idf.py set-target "${IDF_TARGET}" + fi + idf.py build || die "CMake build in ${PWD} has failed for ${IDF_TARGET}" + idf.py fullclean + done +} + +function build_folders +{ + pushd "$1" + EXAMPLES=$(find . -maxdepth 1 -mindepth 1 -type d | cut -d '/' -f 2) + for NAME in ${EXAMPLES} + do + cd "${NAME}" + build_for_targets "$2" + cd .. + done + popd +} + +echo "${STARS}" +# Build the tests +build_folders test/serial "${TEST_TARGETS}" +echo "${STARS}" +# Build the tests +build_folders test/tcp "${TEST_TARGETS}" +echo "${STARS}" + diff --git a/component.mk b/component.mk new file mode 100644 index 0000000..a596857 --- /dev/null +++ b/component.mk @@ -0,0 +1,29 @@ +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) \ + ) + + + diff --git a/docs/Doxyfile b/docs/Doxyfile new file mode 100644 index 0000000..c3c933c --- /dev/null +++ b/docs/Doxyfile @@ -0,0 +1,57 @@ +# This is Doxygen configuration file +# +# Doxygen provides over 260 configuration statements +# To make this file easier to follow, +# it contains only statements that are non-default +# +# NOTE: +# It is recommended not to change defaults unless specifically required +# Test any changes how they affect generated documentation +# Make sure that correct warnings are generated to flag issues with documented code +# +# For the complete list of configuration statements see: +# http://doxygen.nl/manual/config.html + + +PROJECT_NAME = "IDF Programming Guide" + +## The 'INPUT' statement below is used as input by script 'gen-df-input.py' +## to automatically generate API reference list files heder_file.inc +## These files are placed in '_inc' directory +## and used to include in API reference documentation + +INPUT = \ + $(PROJECT_PATH)/freemodbus/common/include/esp_modbus_common.h \ + $(PROJECT_PATH)/freemodbus/common/include/esp_modbus_slave.h \ + $(PROJECT_PATH)/freemodbus/common/include/esp_modbus_master.h \ + +## Get warnings for functions that have no documentation for their parameters or return value +## +WARN_NO_PARAMDOC = YES + +## Enable preprocessing and remove __attribute__(...) expressions from the INPUT files +## +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +PREDEFINED = \ + $(ENV_DOXYGEN_DEFINES) \ + +## Do not complain about not having dot +## +HAVE_DOT = NO + +## Generate XML that is required for Breathe +## +GENERATE_XML = YES +XML_OUTPUT = xml + +GENERATE_HTML = NO +HAVE_DOT = NO +GENERATE_LATEX = NO +GENERATE_MAN = YES +GENERATE_RTF = NO + +## Skip distracting progress messages +## +QUIET = YES diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..daec062 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,11 @@ +# ESP-Modbus Library + +This folder represents the official documentation for the ESP-Modbus library (**esp-modbus component documentation**). The Modbus is a data communications protocol originally published by Modicon (now Schneider Electric) in 1979 for use with its programmable logic controllers (PLCs). The Modbus has become a de facto standard communication protocol and is now a commonly available means of connecting industrial electronic devices. This library supports Modbus communication in the networks that are based on RS485 or Ethernet interfaces. + +# Hosted Documentation + +* English: https://docs.espressif.com/projects/esp-modbus/ + +# Building Documentation + +The documentation is built using the python package `esp-docs`, which can be installed by running `pip install esp-docs`. Running `build-docs --help` will give a summary of available options. For more information see the `esp-docs` documentation at https://github.com/espressif/esp-docs/blob/master/README.md \ No newline at end of file diff --git a/docs/_static/404-page__en.svg b/docs/_static/404-page__en.svg new file mode 100644 index 0000000..928ec63 --- /dev/null +++ b/docs/_static/404-page__en.svg @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/modbus-data-mapping.png b/docs/_static/modbus-data-mapping.png new file mode 100644 index 0000000..18fd2cf Binary files /dev/null and b/docs/_static/modbus-data-mapping.png differ diff --git a/docs/_static/modbus-segment.png b/docs/_static/modbus-segment.png new file mode 100644 index 0000000..80fb6bf Binary files /dev/null and b/docs/_static/modbus-segment.png differ diff --git a/docs/conf_common.py b/docs/conf_common.py new file mode 100644 index 0000000..bc44ed1 --- /dev/null +++ b/docs/conf_common.py @@ -0,0 +1,20 @@ +from esp_docs.conf_docs import * # noqa: F403,F401 + +extensions += ['sphinx_copybutton', + # Needed as a trigger for running doxygen + 'esp_docs.esp_extensions.dummy_build_system', + 'esp_docs.esp_extensions.run_doxygen' + ] + +# link roles config +github_repo = 'espressif/esp-modbus' + +# context used by sphinx_idf_theme +html_context['github_user'] = 'espressif' +html_context['github_repo'] = 'esp-modbus' + +# Extra options required by sphinx_idf_theme +project_slug = 'esp-modbus' + +idf_targets = ['esp32', 'esp32s2', 'esp32c3'] +languages = ['en'] diff --git a/docs/en/api-reference/protocols/modbus.rst b/docs/en/api-reference/protocols/modbus.rst deleted file mode 100644 index 6e17cf9..0000000 --- a/docs/en/api-reference/protocols/modbus.rst +++ /dev/null @@ -1,662 +0,0 @@ -ESP-Modbus -========== - -Overview --------- - -The Modbus serial communication protocol is de facto standard protocol widely used to connect industrial electronic devices. Modbus allows communication among many devices connected to the same network, for example, a system that measures temperature and humidity and communicates the results to a computer. The Modbus protocol uses several types of data: Holding Registers, Input Registers, Coils (single bit output), Discrete Inputs. Versions of the Modbus protocol exist for serial port and for Ethernet and other protocols that support the Internet protocol suite. -There are many variants of Modbus protocols, some of them are: - - - * ``Modbus RTU`` — This is used in serial communication and makes use of a compact, binary representation of the data for protocol communication. The RTU format follows the commands/data with a cyclic redundancy check checksum as an error check mechanism to ensure the reliability of data. Modbus RTU is the most common implementation available for Modbus. A Modbus RTU message must be transmitted continuously without inter-character hesitations. Modbus messages are framed (separated) by idle (silent) periods. The RS-485 interface communication is usually used for this type. - * ``Modbus ASCII`` — This is used in serial communication and makes use of ASCII characters for protocol communication. The ASCII format uses a longitudinal redundancy check checksum. Modbus ASCII messages are framed by leading colon (":") and trailing newline (CR/LF). - * ``Modbus TCP/IP or Modbus TCP`` — This is a Modbus variant used for communications over TCP/IP networks, connecting over port 502. It does not require a checksum calculation, as lower layers already provide checksum protection. - -The following document (and included code snippets) requires some familiarity with the Modbus protocol. Refer to the Modbus Organization's with protocol specifications for specifics. - -Messaging Model And Data Mapping --------------------------------- - -Modbus is an application protocol that defines rules for messaging structure and data organization that are independent of the data transmission medium. Traditional serial Modbus is a register-based protocol that defines message transactions that occur between master(s) and slave devices (multiple masters are allowed on using Modbus TCP/IP). The slave devices listen for communication from the master and simply respond as instructed. The master(s) always controls communication and may communicate directly to one slave, or all connected slaves, but the slaves cannot communicate directly with each other. - -.. figure:: ../../../_static/modbus-segment.png - :align: center - :scale: 80% - :alt: Modbus segment diagram - :figclass: align-center - - Modbus segment diagram - -.. note:: It is assumed that the number of slaves and their register maps are known by the Modbus master before the start of stack. - -The register map of each slave device is usually part of its device manual. A Slave device usually permits configuration of its short slave address and communication options that are used within the device's network segment. - -The Modbus protocol allows devices to map data to four types of registers (Holding, Input, Discrete, Coil). The figure below illustrates an example mapping of a device's data to the four types of registers. - -.. figure:: ../../../_static/modbus-data-mapping.png - :align: center - :scale: 80% - :alt: Modbus data mapping - :figclass: align-center - - Modbus data mapping - -The following sections give an overview of how to use the ESP_Modbus component found under `components/freemodbus`. The sections cover initialization of a Modbus port, and the setup a master or slave device accordingly: - -- :ref:`modbus_api_port_initialization` -- :ref:`modbus_api_slave_overview` -- :ref:`modbus_api_master_overview` - -.. _modbus_api_port_initialization: - -Modbus Port Initialization -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ESP_Modbus supports Modbus SERIAL and TCP ports and a port must be initialized before calling any other Modbus API. The functions below are used to create and then initialize Modbus controller interface (either master or slave) over a particular transmission medium (either Serial or TCP/IP): - -- :cpp:func:`mbc_slave_init` -- :cpp:func:`mbc_master_init` -- :cpp:func:`mbc_slave_init_tcp` -- :cpp:func:`mbc_master_init_tcp` - -The API call uses the first parameter to recognize the type of port being initialized. Supported enumeration for different ports: :cpp:enumerator:`MB_PORT_SERIAL_MASTER`, :cpp:enumerator:`MB_PORT_SERIAL_SLAVE` accordingly. -The parameters :cpp:enumerator:`MB_PORT_TCP_MASTER`, :cpp:enumerator:`MB_PORT_TCP_SLAVE` are reserved for internal usage. - -.. code:: c - - void* master_handler = NULL; // Pointer to allocate interface structure - // Initialization of Modbus master for serial port - esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler); - if (master_handler == NULL || err != ESP_OK) { - ESP_LOGE(TAG, "mb controller initialization fail."); - } - -This example code to initialize slave port: - -.. code:: c - - void* slave_handler = NULL; // Pointer to allocate interface structure - // Initialization of Modbus slave for TCP - esp_err_t err = mbc_slave_init_tcp(&slave_handler); - if (slave_handler == NULL || err != ESP_OK) { - // Error handling is performed here - ESP_LOGE(TAG, "mb controller initialization fail."); - } - -.. _modbus_api_master_overview: - -Modbus Master API Overview --------------------------- - -The following overview describes how to setup Modbus master communication. The overview reflects a typical programming workflow and is broken down into the sections provided below: - -1. :ref:`modbus_api_port_initialization` - Initialization of Modbus controller interface for the selected port. -2. :ref:`modbus_api_master_configure_descriptor` - Configure data descriptors to access slave parameters. -3. :ref:`modbus_api_master_setup_communication_options` - Allows to setup communication options for selected port. -4. :ref:`modbus_api_master_start_communication` - Start stack and sending / receiving data. -5. :ref:`modbus_api_master_destroy` - Destroy Modbus controller and its resources. - -.. _modbus_api_master_configure_descriptor: - -Configuring Master Data Access -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The architectural approach of ESP_Modbus includes one level above standard Modbus IO driver. -The additional layer is called Modbus controller and its goal is to add an abstraction such as CID - characteristic identifier. -The CID is linked to a corresponding Modbus registers through the table called Data Dictionary and represents device physical parameter (such as temperature, humidity, etc.) in specific Modbus slave device. -This approach allows the upper layer (e.g., MESH or MQTT) to be isolated from Modbus specifics thus simplify Modbus integration with other protocols/networks. - -The Data Dictionary is the list in the Modbus master which shall be defined by user to link each CID to its corresponding Modbus registers representation using Register Mapping table of the Modbus slave being used. -Each element in this data dictionary is of type :cpp:type:`mb_parameter_descriptor_t` and represents the description of one physical characteristic: - -.. list-table:: Table 1 Modbus master Data Dictionary description - :widths: 8 10 82 - :header-rows: 1 - - * - Field - - Description - - Detailed information - * - ``cid`` - - Characteristic ID - - The identifier of characteristic (must be unique). - * - ``param_key`` - - Characteristic Name - - String description of the characteristic. - * - ``param_units`` - - Characteristic Units - - Physical Units of the characteristic. - * - ``mb_slave_addr`` - - Modbus Slave Address - - The short address of the device with correspond parameter UID. - * - ``mb_param_type`` - - Modbus Register Type - - Type of Modbus register area. - :cpp:enumerator:`MB_PARAM_INPUT`, :cpp:enumerator:`MB_PARAM_HOLDING`, :cpp:enumerator:`MB_PARAM_COIL`, :cpp:enumerator:`MB_PARAM_DISCRETE` - represents Input , Holding, Coil and Discrete input register area accordingly; - * - ``mb_reg_start`` - - Modbus Register Start - - Relative register address of the characteristic in the register area. - * - ``mb_size`` - - Modbus Register Size - - Length of characteristic in registers. - * - ``param_offset`` - - Instance Offset - - Offset to instance of the characteristic in bytes. It is used to calculate the absolute address to the characteristic in the storage structure. - It is optional field and can be set to zero if the parameter is not used in the application. - * - ``param_type`` - - Data Type - - Specifies type of the characteristic. - :cpp:enumerator:`PARAM_TYPE_U8`, :cpp:enumerator:`PARAM_TYPE_U16`, :cpp:enumerator:`PARAM_TYPE_U32` - Unsigned integer 8/16/32 bit type; - :cpp:enumerator:`PARAM_TYPE_FLOAT` - IEEE754 floating point format; - :cpp:enumerator:`PARAM_TYPE_ASCII` - ASCII string or binary data; - * - ``param_size`` - - Data Size - - The storage size of the characteristic (bytes). - * - ``param_opts`` - - Parameter Options - - Limits, options of characteristic used during processing of alarm in user application (optional) - * - ``access`` - - Parameter access type - - Can be used in user application to define the behavior of the characteristic during processing of data in user application; - :cpp:enumerator:`PAR_PERMS_READ_WRITE_TRIGGER`, :cpp:enumerator:`PAR_PERMS_READ`, :cpp:enumerator:`PAR_PERMS_READ_WRITE_TRIGGER`; - -.. note:: The ``cid`` and ``param_key`` have to be unique. Please use the prefix to the parameter key if you have several similar parameters in your register map table. - -.. list-table:: Table 2 Example Register mapping table of Modbus slave - :widths: 5 5 2 10 5 5 68 - :header-rows: 1 - - * - CID - - Register - - Length - - Range - - Type - - Units - - Description - * - 0 - - 30000 - - 4 - - MAX_UINT - - U32 - - Not defined - - Serial number of device (4 bytes) read-only - * - 1 - - 30002 - - 2 - - MAX_UINT - - U16 - - Not defined - - Software version (4 bytes) read-only - * - 2 - - 40000 - - 4 - - -20..40 - - FLOAT - - DegC - - Room temperature in DegC. Writing a temperature value to this register for single point calibration. - -.. code:: c - - // Enumeration of modbus slave addresses accessed by master device - enum { - MB_DEVICE_ADDR1 = 1, - MB_DEVICE_ADDR2, - MB_SLAVE_COUNT - }; - - // Enumeration of all supported CIDs for device - enum { - CID_SER_NUM1 = 0, - CID_SW_VER1, - CID_TEMP_DATA_1, - CID_SER_NUM2, - CID_SW_VER2, - CID_TEMP_DATA_2 - }; - - // Example Data Dictionary for Modbus parameters in 2 slaves in the segment - mb_parameter_descriptor_t device_parameters[] = { - // 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_SER_NUM1, STR("Serial_number_1"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2, - 0, PARAM_TYPE_U32, 4, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, - { CID_SW_VER1, STR("Software_version_1"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 1, - 0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, - { CID_TEMP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2, - 0, PARAM_TYPE_FLOAT, 4, OPTS( 16, 30, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }, - { CID_SER_NUM2, STR("Serial_number_2"), STR("--"), MB_DEVICE_ADDR2, MB_PARAM_INPUT, 0, 2, - 0, PARAM_TYPE_U32, 4, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, - { CID_SW_VER2, STR("Software_version_2"), STR("--"), MB_DEVICE_ADDR2, MB_PARAM_INPUT, 2, 1, - 0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, - { CID_TEMP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 0, 2, - 0, PARAM_TYPE_FLOAT, 4, OPTS( 20, 30, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }, - }; - // Calculate number of parameters in the table - uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0])); - -During initialization of the Modbus stack, a pointer to the Data Dictionary (called descriptor) must be provided as the parameter of the function below. - -:cpp:func:`mbc_master_set_descriptor`: Initialization of master descriptor. - -.. code:: c - - ESP_ERROR_CHECK(mbc_master_set_descriptor(&device_parameters[0], num_device_parameters)); - -The Data Dictionary can be initialized from SD card, MQTT or other source before start of stack. Once the initialization and setup is done, the Modbus controller allows the reading of complex parameters from any slave included in descriptor table using its CID. - -.. _modbus_api_master_setup_communication_options: - -Master Communication Options -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Calling the setup function allows for specific communication options to be defined for port. - -:cpp:func:`mbc_master_setup` - -The communication structure provided as a parameter is different for serial and TCP communication mode. - -Example setup for serial port: - -.. code:: c - - mb_communication_info_t comm_info = { - .port = MB_PORT_NUM, // Serial port number - .mode = MB_MODE_RTU, // Modbus mode of communication (MB_MODE_RTU or MB_MODE_ASCII) - .baudrate = 9600, // Modbus communication baud rate - .parity = MB_PARITY_NONE // parity option for serial port - }; - - ESP_ERROR_CHECK(mbc_master_setup((void*)&comm_info)); - -Modbus master TCP port requires additional definition of IP address table where number of addresses should be equal to number of unique slave addresses in master Modbus Data Dictionary: - -The order of IP address string corresponds to short slave address in the Data Dictionary. - -.. code:: c - - #define MB_SLAVE_COUNT 2 // Number of slaves in the segment being accessed (as defined in Data Dictionary) - - char* slave_ip_address_table[MB_SLAVE_COUNT] = { - "192.168.1.2", // Address corresponds to UID1 and set to predefined value by user - "192.168.1.3", // corresponds to UID2 in the segment - NULL // end of table - }; - - mb_communication_info_t comm_info = { - .ip_port = MB_TCP_PORT, // Modbus TCP port number (default = 502) - .ip_addr_type = MB_IPV4, // version of IP protocol - .ip_mode = MB_MODE_TCP, // Port communication mode - .ip_addr = (void*)slave_ip_address_table, // assign table of IP addresses - .ip_netif_ptr = esp_netif_ptr // esp_netif_ptr pointer to the corresponding network interface - }; - - ESP_ERROR_CHECK(mbc_master_setup((void*)&comm_info)); - -.. note:: Refer to :doc:`esp_netif component <../network/esp_netif>` for more information about network interface initialization. - -The slave IP addresses in the table can be assigned automatically using mDNS service as described in the example. -Refer to :example:`protocols/modbus/tcp/mb_tcp_master` for more information. - -.. note:: RS485 communication requires call to UART specific APIs to setup communication mode and pins. Refer to :ref:`uart-api-running-uart-communication` section of UART documentation. - - -.. _modbus_api_master_start_communication: - -Master Communication -^^^^^^^^^^^^^^^^^^^^ - -The starting of the Modbus controller is the final step in enabling communication. This is performed using function below: - -:cpp:func:`mbc_master_start` - -.. code:: c - - esp_err_t err = mbc_master_start(); - if (err != ESP_OK) { - ESP_LOGE(TAG, "mb controller start fail, err=%x.", err); - } - -The list of functions below are used by the Modbus master stack from a user's application: - -:cpp:func:`mbc_master_send_request`: This function executes a blocking Modbus request. The master sends a data request (as defined in parameter request structure :cpp:type:`mb_param_request_t`) and then blocks until a response from corresponding slave and returns the status of command execution. This function provides a standard way for read/write access to Modbus devices in the network. - -:cpp:func:`mbc_master_get_cid_info`: The function gets information about each characteristic supported in the data dictionary and returns the characteristic's description in the form of the :cpp:type:`mb_parameter_descriptor_t` structure. Each characteristic is accessed using its CID. - -:cpp:func:`mbc_master_get_parameter`: The function reads the data of a characteristic defined in the parameters of a Modbus slave device. The additional data for request is taken from parameter description table. - -Example: - -.. code:: c - - const mb_parameter_descriptor_t* param_descriptor = NULL; - uint8_t temp_data[4] = {0}; // temporary buffer to hold maximum CID size - uint8_t type = 0; - .... - - // Get the information for characteristic cid from data dictionary - esp_err_t err = mbc_master_get_cid_info(cid, ¶m_descriptor); - if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) { - err = mbc_master_get_parameter(param_descriptor->cid, (char*)param_descriptor->param_key, (uint8_t*)temp_data, &type); - if (err == ESP_OK) { - ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%08x) read successful.", - param_descriptor->cid, - (char*)param_descriptor->param_key, - (char*)param_descriptor->param_units, - *(uint32_t*)temp_data); - } else { - ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).", - param_descriptor->cid, - (char*)param_descriptor->param_key, - (int)err, - (char*)esp_err_to_name(err)); - } - } else { - ESP_LOGE(TAG, "Could not get information for characteristic %d.", cid); - } - - -:cpp:func:`mbc_master_set_parameter` - -The function writes characteristic's value defined as a name and cid parameter in corresponded slave device. The additional data for parameter request is taken from master parameter description table. - -.. code:: c - - uint8_t type = 0; // Type of parameter - uint8_t temp_data[4] = {0}; // temporary buffer - - esp_err_t err = mbc_master_set_parameter(CID_TEMP_DATA_2, "Temperature_2", (uint8_t*)temp_data, &type); - if (err == ESP_OK) { - ESP_LOGI(TAG, "Set parameter data successfully."); - } else { - ESP_LOGE(TAG, "Set data fail, err = 0x%x (%s).", (int)err, (char*)esp_err_to_name(err)); - } - - -.. _modbus_api_master_destroy: - -Modbus Master Teardown -^^^^^^^^^^^^^^^^^^^^^^ - -This function stops Modbus communication stack and destroys controller interface and free all used active objects. - -:cpp:func:`mbc_master_destroy` - -.. code:: c - - ESP_ERROR_CHECK(mbc_master_destroy()); - - -.. _modbus_api_slave_overview: - -Modbus Slave API Overview -------------------------- - -The sections below represent typical programming workflow for the slave API which should be called in following order: - -1. :ref:`modbus_api_port_initialization` - Initialization of Modbus controller interface for the selected port. -2. :ref:`modbus_api_slave_configure_descriptor` - Configure data descriptors to access slave parameters. -3. :ref:`modbus_api_slave_setup_communication_options` - Allows to setup communication options for selected port. -4. :ref:`modbus_api_slave_communication` - Start stack and sending / receiving data. Filter events when master accesses the register areas. -5. :ref:`modbus_api_slave_destroy` - Destroy Modbus controller and its resources. - -.. _modbus_api_slave_configure_descriptor: - -Configuring Slave Data Access -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following functions must be called when the Modbus controller slave port is already initialized. Refer to :ref:`modbus_api_port_initialization`. - -The slave stack requires the user to define structures (memory storage areas) that store the Modbus parameters accessed by stack. These structures should be prepared by the user and be assigned to the Modbus controller interface using :cpp:func:`mbc_slave_set_descriptor` API call before the start of communication. The slave task can call the :cpp:func:`mbc_slave_check_event` function which will block until the Modbus master access the slave. The slave task can then get information about the data being accessed. - -.. note:: One slave can define several area descriptors per each type of Modbus register area with different start_offset. - -Register area is defined by using the :cpp:type:`mb_register_area_descriptor_t` structure. - -.. list-table:: Table 3 Modbus register area descriptor - :widths: 8 92 - :header-rows: 1 - - * - Field - - Description - * - ``start_offset`` - - Zero based register relative offset for defined register area. Example: register address = 40002 ( 4x register area - Function 3 - holding register ), start_offset = 2 - * - ``type`` - - Type of the Modbus register area. Refer to :cpp:type:`mb_param_type_t` for more information. - * - ``address`` - - A pointer to the memory area which is used to store the register data for this area descriptor. - * - ``size`` - - The size of the memory area in bytes which is used to store register data. - -:cpp:func:`mbc_slave_set_descriptor` - -The function initializes Modbus communication descriptors for each type of Modbus register area (Holding Registers, Input Registers, Coils (single bit output), Discrete Inputs). Once areas are initialized and the :cpp:func:`mbc_slave_start()` API is called the Modbus stack can access the data in user data structures by request from master. - -.. code:: c - - #define MB_REG_INPUT_START_AREA0 (0) - #define MB_REG_HOLDING_START_AREA0 (0) - #define MB_REG_HOLD_CNT (100) - #define MB_REG_INPUT_CNT (100) - - mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure - unit16_t holding_reg_area[MB_REG_HOLD_CNT] = {0}; // storage area for holding registers - unit16_t input_reg_area[MB_REG_INPUT_CNT] = {0}; // storage area for input registers - - 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_area[0]; // Set pointer to storage instance - reg_area.size = sizeof(holding_reg_area) << 1; // Set the size of register storage area in bytes - ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area)); - - reg_area.type = MB_PARAM_INPUT; - reg_area.start_offset = MB_REG_INPUT_START_AREA0; - reg_area.address = (void*)&input_reg_area[0]; - reg_area.size = sizeof(input_reg_area) << 1; - ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area)); - - -At least one area descriptor per each Modbus register type must be set in order to provide register access to its area. If the master tries to access an undefined area, the stack will generate a Modbus exception. - -Direct access to register area from user application must be protected by critical section: - -.. code:: c - - portENTER_CRITICAL(¶m_lock); - holding_reg_area[2] += 10; - portEXIT_CRITICAL(¶m_lock); - - -.. _modbus_api_slave_setup_communication_options: - -Slave Communication Options -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The function initializes the Modbus controller interface and its active context (tasks, RTOS objects and other resources). - -:cpp:func:`mbc_slave_setup` - -The function is used to setup communication parameters of the Modbus stack. - -Example initialization of Modbus TCP communication: - -.. code:: c - - esp_netif_init(); - ... - - mb_communication_info_t comm_info = { - .ip_port = MB_TCP_PORT, // Modbus TCP port number (default = 502) - .ip_addr_type = MB_IPV4, // version of IP protocol - .ip_mode = MB_MODE_TCP, // Port communication mode - .ip_addr = NULL, // This field keeps the client IP address to bind, NULL - bind to any client - .ip_netif_ptr = esp_netif_ptr // esp_netif_ptr - pointer to the corresponding network interface - }; - - // Setup communication parameters and start stack - ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info)); - -Example initialization of Modbus serial communication: - -.. code:: c - - #define MB_SLAVE_DEV_SPEED 9600 - #define MB_SLAVE_ADDR 1 - #define MB_SLAVE_PORT_NUM 2 - ... - - // Setup communication parameters and start stack - mb_communication_info_t comm_info = { - .mode = MB_MODE_RTU, // Communication type - .slave_addr = MB_SLAVE_ADDR, // Short address of the slave - .port = MB_SLAVE_PORT_NUM, // UART physical port number - .baudrate = MB_SLAVE_DEV_SPEED, // Baud rate for communication - .parity = MB_PARITY_NONE // Parity option - }; - - ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info)); - -.. _modbus_api_slave_communication: - -Slave Communication -^^^^^^^^^^^^^^^^^^^ - -The function below is used to start Modbus controller interface and allows communication. - -:cpp:func:`mbc_slave_start` - -.. code:: c - - ESP_ERROR_CHECK(mbc_slave_start()); - -:cpp:func:`mbc_slave_check_event` - -The blocking call to function waits for a event specified (represented as an event mask parameter). Once the master accesses the parameter and the event mask matches the parameter type, the application task will be unblocked and function will return the corresponding event :cpp:type:`mb_event_group_t` which describes the type of register access being done. - -:cpp:func:`mbc_slave_get_param_info` - -The function gets information about accessed parameters from the Modbus controller event queue. The KConfig :ref:`CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE` key can be used to configure the notification queue size. The timeout parameter allows a timeout to be specified when waiting for a notification. The :cpp:type:`mb_param_info_t` structure contains information about accessed parameter. - -.. list-table:: Table 4 Description of the register info structure: :cpp:type:`mb_param_info_t` - :widths: 10 90 - :header-rows: 1 - - * - Field - - Description - * - ``time_stamp`` - - the time stamp of the event when defined parameter is accessed - * - ``mb_offset`` - - start Modbus register accessed by master - * - ``type`` - - type of the Modbus register area being accessed (See the :cpp:type:`mb_event_group_t` for more information) - * - ``address`` - - memory address that corresponds to accessed register in defined area descriptor - * - ``size`` - - number of registers being accessed by master - -Example to get event when holding or input registers accessed in the slave: - -.. code:: c - - #define MB_READ_MASK (MB_EVENT_INPUT_REG_RD | MB_EVENT_HOLDING_REG_RD) - #define MB_WRITE_MASK (MB_EVENT_HOLDING_REG_WR) - #define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK) - #define MB_PAR_INFO_GET_TOUT (10 / portTICK_PERIOD_MS) - .... - - // The function blocks while waiting for register access - mb_event_group_t event = mbc_slave_check_event(MB_READ_WRITE_MASK); - - // Get information about data accessed from master - ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT)); - const char* rw_str = (event & MB_READ_MASK) ? "READ" : "WRITE"; - - // Filter events and process them accordingly - if (event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) { - ESP_LOGI(TAG, "HOLDING %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u", - rw_str, - (uint32_t)reg_info.time_stamp, - (uint32_t)reg_info.mb_offset, - (uint32_t)reg_info.type, - (uint32_t)reg_info.address, - (uint32_t)reg_info.size); - } else if (event & (MB_EVENT_INPUT_REG_RD)) { - ESP_LOGI(TAG, "INPUT %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u", - rw_str, - (uint32_t)reg_info.time_stamp, - (uint32_t)reg_info.mb_offset, - (uint32_t)reg_info.type, - (uint32_t)reg_info.address, - (uint32_t)reg_info.size); - } - -.. _modbus_api_slave_destroy: - -Modbus Slave Teardown -^^^^^^^^^^^^^^^^^^^^^ - -This function stops the Modbus communication stack, destroys the controller interface, and frees all used active objects allocated for the slave. - -:cpp:func:`mbc_slave_destroy` - -.. code:: c - - ESP_ERROR_CHECK(mbc_slave_destroy()); - -Possible Communication Issues And Solutions -------------------------------------------- - -If the examples do not work as expected and slave and master boards are not able to communicate correctly, it is possible to find the reason for errors. The most important errors are described in master example output and formatted as below: - -.. highlight:: none - -:: - - E (1692332) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x107) (ESP_ERR_TIMEOUT). - - -.. list-table:: Table 5 Modbus error codes and troubleshooting - :widths: 5 30 65 - :header-rows: 1 - - * - Error - - Description - - Possible solution - * - 0x106 - - ``ESP_ERR_NOT_SUPPORTED`` - Invalid register request - slave returned an exception because the requested register is not supported. - - Refer to slave register map. Check the master data dictionary for correctness. - * - 0x107 - - ``ESP_ERR_TIMEOUT`` - Slave response timeout - Modbus slave did not send response during configured slave response timeout. - - Measure and increase the maximum slave response timeout `idf.py menuconfig`, option :ref:`CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND`. - Check physical connection or network configuration and make sure that the slave response can reach the master side. - If the application has some high performance tasks with higher priority than :ref:`CONFIG_FMB_PORT_TASK_PRIO` it is recommended to place Modbus tasks on the other core using an option :ref:`CONFIG_FMB_PORT_TASK_AFFINITY`. - Configure the Modbus task's priority :ref:`CONFIG_FMB_PORT_TASK_PRIO` to ensure that the task gets sufficient processing time to handle Modbus stack events. - * - 0x108 - - ``ESP_ERR_INVALID_RESPONSE`` - Received unsupported response from slave or frame check failure. Master can not execute command handler because the command is either not supported or is incorrect. - - Check the physical connection then refer to register map of your slave to configure the master data dictionary properly. - * - 0x103 - - ``ESP_ERR_INVALID_STATE`` - Critical failure or FSM sequence failure or master FSM is busy processing previous request. - - Make sure your physical connection is working properly. Increase task stack size and check Modbus initialization sequence. - -Application Example -------------------- - -The examples below use the FreeModbus library port for serial TCP slave and master implementations accordingly. The selection of stack is performed through KConfig menu option "Enable Modbus stack support ..." for appropriate communication mode and related configuration keys. - -- :example:`protocols/modbus/serial/mb_slave` -- :example:`protocols/modbus/serial/mb_master` -- :example:`protocols/modbus/tcp/mb_tcp_slave` -- :example:`protocols/modbus/tcp/mb_tcp_master` - -Please refer to the specific example README.md for details. - -Protocol References -------------------- - - - ``https://modbus.org/specs.php``: Modbus Organization with protocol specifications. - -API Reference -------------- - -.. include-build-file:: inc/esp_modbus_common.inc -.. include-build-file:: inc/esp_modbus_master.inc -.. include-build-file:: inc/esp_modbus_slave.inc - diff --git a/docs/en/applications_and_references.rst b/docs/en/applications_and_references.rst new file mode 100644 index 0000000..ca5dbf1 --- /dev/null +++ b/docs/en/applications_and_references.rst @@ -0,0 +1,72 @@ +Possible Communication Issues And Solutions +------------------------------------------- + +If the examples do not work as expected and slave and master boards are not able to communicate correctly, it is possible to find the reason for errors. The most important errors are described in master example output and formatted as below: + +.. highlight:: none + +:: + + E (1692332) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x107) (ESP_ERR_TIMEOUT). + + +.. list-table:: Table 5 Modbus error codes and troubleshooting + :widths: 5 30 65 + :header-rows: 1 + + * - Error + - Description + - Possible solution + * - 0x106 + - ``ESP_ERR_NOT_SUPPORTED`` - Invalid register request - slave returned an exception because the requested register is not supported. + - Refer to slave register map. Check the master data dictionary for correctness. + * - 0x107 + - ``ESP_ERR_TIMEOUT`` - Slave response timeout - Modbus slave did not send response during configured slave response timeout. + - Measure and increase the maximum slave response timeout `idf.py menuconfig`, option ``CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND``. + Check physical connection or network configuration and make sure that the slave response can reach the master side. + If the application has some high performance tasks with higher priority than ``CONFIG_FMB_PORT_TASK_PRIO`` it is recommended to place Modbus tasks on the other core using an option ``CONFIG_FMB_PORT_TASK_AFFINITY``. + Configure the Modbus task's priority ``CONFIG_FMB_PORT_TASK_PRIO`` to ensure that the task gets sufficient processing time to handle Modbus stack events. + * - 0x108 + - ``ESP_ERR_INVALID_RESPONSE`` - Received unsupported response from slave or frame check failure. Master can not execute command handler because the command is either not supported or is incorrect. + - Check the physical connection then refer to register map of your slave to configure the master data dictionary properly. + * - 0x103 + - ``ESP_ERR_INVALID_STATE`` - Critical failure or FSM sequence failure or master FSM is busy processing previous request. + - Make sure your physical connection is working properly. Increase task stack size and check Modbus initialization sequence. + +Application Example +------------------- + +The examples below use the FreeModbus library port for serial TCP slave and master implementations accordingly. The selection of stack is performed through KConfig menu option "Enable Modbus stack support ..." for appropriate communication mode and related configuration keys. + +.. _example_mb_slave: + +- `Modbus serial slave example `__ + +.. _example_mb_master: + +- `Modbus serial master example `__ + +.. _example_mb_tcp_master: + +- `Modbus TCP master example `__ + +.. _example_mb_tcp_slave: + +- `Modbus TCP slave example `__ + +Please refer to the specific example README.md for details. + +.. _modbus_organization: + +Protocol References +------------------- + + - `Modbus Organization with protocol specifications `__ + +API Reference +------------- + +.. include-build-file:: inc/esp_modbus_common.inc +.. include-build-file:: inc/esp_modbus_master.inc +.. include-build-file:: inc/esp_modbus_slave.inc + diff --git a/docs/en/conf.py b/docs/en/conf.py new file mode 100644 index 0000000..90f2495 --- /dev/null +++ b/docs/en/conf.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# +# English Language RTD & Sphinx config file +# +# Uses ../conf_common.py for most non-language-specific settings. + +# Importing conf_common adds all the non-language-specific +# parts to this conf module +try: + from conf_common import * # noqa: F403,F401 +except ImportError: + import os + import sys + sys.path.insert(0, os.path.abspath('../')) + from conf_common import * # noqa: F403,F401 + +import datetime + +current_year = datetime.datetime.now().year + +# General information about the project. +project = u'ESP-Modbus Programming Guide' +copyright = u'2019 - {}, Espressif Systems (Shanghai) Co., Ltd'.format(current_year) + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +language = 'en' diff --git a/docs/en/index.rst b/docs/en/index.rst new file mode 100644 index 0000000..a392044 --- /dev/null +++ b/docs/en/index.rst @@ -0,0 +1,14 @@ +ESP-Modbus Library +================== + +An Espressif ESP-Modbus Library (esp-modbus) is a library to support Modbus communication in the networks based on RS485 or Ethernet interfaces. +The Modbus is a data communications protocol originally published by Modicon (now Schneider Electric) in 1979 for use with its programmable logic controllers (PLCs). + +.. toctree:: + :maxdepth: 1 + + The Overview, Messaging Model And Data Mapping + Modbus Port Initialization + Modbus Master API + Modbus Slave API + Applications and References \ No newline at end of file diff --git a/docs/en/master_api_overview.rst b/docs/en/master_api_overview.rst new file mode 100644 index 0000000..ba49481 --- /dev/null +++ b/docs/en/master_api_overview.rst @@ -0,0 +1,300 @@ +.. _modbus_api_master_overview: + +Modbus Master API Overview +-------------------------- + +The following overview describes how to setup Modbus master communication. The overview reflects a typical programming workflow and is broken down into the sections provided below: + +1. :ref:`modbus_api_port_initialization` - Initialization of Modbus controller interface for the selected port. +2. :ref:`modbus_api_master_configure_descriptor` - Configure data descriptors to access slave parameters. +3. :ref:`modbus_api_master_setup_communication_options` - Allows to setup communication options for selected port. +4. :ref:`modbus_api_master_start_communication` - Start stack and sending / receiving data. +5. :ref:`modbus_api_master_destroy` - Destroy Modbus controller and its resources. + +.. _modbus_api_master_configure_descriptor: + +Configuring Master Data Access +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The architectural approach of ESP_Modbus includes one level above standard Modbus IO driver. +The additional layer is called Modbus controller and its goal is to add an abstraction such as CID - characteristic identifier. +The CID is linked to a corresponding Modbus registers through the table called Data Dictionary and represents device physical parameter (such as temperature, humidity, etc.) in specific Modbus slave device. +This approach allows the upper layer (e.g., MESH or MQTT) to be isolated from Modbus specifics thus simplify Modbus integration with other protocols/networks. + +The Data Dictionary is the list in the Modbus master which shall be defined by user to link each CID to its corresponding Modbus registers representation using Register Mapping table of the Modbus slave being used. +Each element in this data dictionary is of type :cpp:type:`mb_parameter_descriptor_t` and represents the description of one physical characteristic: + +.. list-table:: Table 1 Modbus master Data Dictionary description + :widths: 8 10 82 + :header-rows: 1 + + * - Field + - Description + - Detailed information + * - ``cid`` + - Characteristic ID + - The identifier of characteristic (must be unique). + * - ``param_key`` + - Characteristic Name + - String description of the characteristic. + * - ``param_units`` + - Characteristic Units + - Physical Units of the characteristic. + * - ``mb_slave_addr`` + - Modbus Slave Address + - The short address of the device with correspond parameter UID. + * - ``mb_param_type`` + - Modbus Register Type + - Type of Modbus register area. + :cpp:enumerator:`MB_PARAM_INPUT`, :cpp:enumerator:`MB_PARAM_HOLDING`, :cpp:enumerator:`MB_PARAM_COIL`, :cpp:enumerator:`MB_PARAM_DISCRETE` - represents Input , Holding, Coil and Discrete input register area accordingly; + * - ``mb_reg_start`` + - Modbus Register Start + - Relative register address of the characteristic in the register area. + * - ``mb_size`` + - Modbus Register Size + - Length of characteristic in registers. + * - ``param_offset`` + - Instance Offset + - Offset to instance of the characteristic in bytes. It is used to calculate the absolute address to the characteristic in the storage structure. + It is optional field and can be set to zero if the parameter is not used in the application. + * - ``param_type`` + - Data Type + - Specifies type of the characteristic. + :cpp:enumerator:`PARAM_TYPE_U8`, :cpp:enumerator:`PARAM_TYPE_U16`, :cpp:enumerator:`PARAM_TYPE_U32` - Unsigned integer 8/16/32 bit type; + :cpp:enumerator:`PARAM_TYPE_FLOAT` - IEEE754 floating point format; + :cpp:enumerator:`PARAM_TYPE_ASCII` - ASCII string or binary data; + * - ``param_size`` + - Data Size + - The storage size of the characteristic (bytes). + * - ``param_opts`` + - Parameter Options + - Limits, options of characteristic used during processing of alarm in user application (optional) + * - ``access`` + - Parameter access type + - Can be used in user application to define the behavior of the characteristic during processing of data in user application; + :cpp:enumerator:`PAR_PERMS_READ_WRITE_TRIGGER`, :cpp:enumerator:`PAR_PERMS_READ`, :cpp:enumerator:`PAR_PERMS_READ_WRITE_TRIGGER`; + +.. note:: The ``cid`` and ``param_key`` have to be unique. Please use the prefix to the parameter key if you have several similar parameters in your register map table. + +.. list-table:: Table 2 Example Register mapping table of Modbus slave + :widths: 5 5 2 10 5 5 68 + :header-rows: 1 + + * - CID + - Register + - Length + - Range + - Type + - Units + - Description + * - 0 + - 30000 + - 4 + - MAX_UINT + - U32 + - Not defined + - Serial number of device (4 bytes) read-only + * - 1 + - 30002 + - 2 + - MAX_UINT + - U16 + - Not defined + - Software version (4 bytes) read-only + * - 2 + - 40000 + - 4 + - -20..40 + - FLOAT + - DegC + - Room temperature in DegC. Writing a temperature value to this register for single point calibration. + +.. code:: c + + // Enumeration of modbus slave addresses accessed by master device + enum { + MB_DEVICE_ADDR1 = 1, + MB_DEVICE_ADDR2, + MB_SLAVE_COUNT + }; + + // Enumeration of all supported CIDs for device + enum { + CID_SER_NUM1 = 0, + CID_SW_VER1, + CID_TEMP_DATA_1, + CID_SER_NUM2, + CID_SW_VER2, + CID_TEMP_DATA_2 + }; + + // Example Data Dictionary for Modbus parameters in 2 slaves in the segment + mb_parameter_descriptor_t device_parameters[] = { + // 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_SER_NUM1, STR("Serial_number_1"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2, + 0, PARAM_TYPE_U32, 4, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, + { CID_SW_VER1, STR("Software_version_1"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 1, + 0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, + { CID_TEMP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2, + 0, PARAM_TYPE_FLOAT, 4, OPTS( 16, 30, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }, + { CID_SER_NUM2, STR("Serial_number_2"), STR("--"), MB_DEVICE_ADDR2, MB_PARAM_INPUT, 0, 2, + 0, PARAM_TYPE_U32, 4, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, + { CID_SW_VER2, STR("Software_version_2"), STR("--"), MB_DEVICE_ADDR2, MB_PARAM_INPUT, 2, 1, + 0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, + { CID_TEMP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 0, 2, + 0, PARAM_TYPE_FLOAT, 4, OPTS( 20, 30, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }, + }; + // Calculate number of parameters in the table + uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0])); + +During initialization of the Modbus stack, a pointer to the Data Dictionary (called descriptor) must be provided as the parameter of the function below. + +:cpp:func:`mbc_master_set_descriptor`: Initialization of master descriptor. + +.. code:: c + + ESP_ERROR_CHECK(mbc_master_set_descriptor(&device_parameters[0], num_device_parameters)); + +The Data Dictionary can be initialized from SD card, MQTT or other source before start of stack. Once the initialization and setup is done, the Modbus controller allows the reading of complex parameters from any slave included in descriptor table using its CID. + +.. _modbus_api_master_setup_communication_options: + +Master Communication Options +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Calling the setup function allows for specific communication options to be defined for port. + +:cpp:func:`mbc_master_setup` + +The communication structure provided as a parameter is different for serial and TCP communication mode. + +Example setup for serial port: + +.. code:: c + + mb_communication_info_t comm_info = { + .port = MB_PORT_NUM, // Serial port number + .mode = MB_MODE_RTU, // Modbus mode of communication (MB_MODE_RTU or MB_MODE_ASCII) + .baudrate = 9600, // Modbus communication baud rate + .parity = MB_PARITY_NONE // parity option for serial port + }; + + ESP_ERROR_CHECK(mbc_master_setup((void*)&comm_info)); + +Modbus master TCP port requires additional definition of IP address table where number of addresses should be equal to number of unique slave addresses in master Modbus Data Dictionary: + +The order of IP address string corresponds to short slave address in the Data Dictionary. + +.. code:: c + + #define MB_SLAVE_COUNT 2 // Number of slaves in the segment being accessed (as defined in Data Dictionary) + + char* slave_ip_address_table[MB_SLAVE_COUNT] = { + "192.168.1.2", // Address corresponds to UID1 and set to predefined value by user + "192.168.1.3", // corresponds to UID2 in the segment + NULL // end of table + }; + + mb_communication_info_t comm_info = { + .ip_port = MB_TCP_PORT, // Modbus TCP port number (default = 502) + .ip_addr_type = MB_IPV4, // version of IP protocol + .ip_mode = MB_MODE_TCP, // Port communication mode + .ip_addr = (void*)slave_ip_address_table, // assign table of IP addresses + .ip_netif_ptr = esp_netif_ptr // esp_netif_ptr pointer to the corresponding network interface + }; + + ESP_ERROR_CHECK(mbc_master_setup((void*)&comm_info)); + +.. note:: Refer to `esp_netif component `__ for more information about network interface initialization. + +The slave IP addresses in the table can be assigned automatically using mDNS service as described in the example. +Refer to :ref:`example TCP master ` for more information. + +.. note:: RS485 communication requires call to UART specific APIs to setup communication mode and pins. Refer to the `UART communication section `__ in documentation. + + +.. _modbus_api_master_start_communication: + +Master Communication +^^^^^^^^^^^^^^^^^^^^ + +The starting of the Modbus controller is the final step in enabling communication. This is performed using function below: + +:cpp:func:`mbc_master_start` + +.. code:: c + + esp_err_t err = mbc_master_start(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "mb controller start fail, err=%x.", err); + } + +The list of functions below are used by the Modbus master stack from a user's application: + +:cpp:func:`mbc_master_send_request`: This function executes a blocking Modbus request. The master sends a data request (as defined in parameter request structure :cpp:type:`mb_param_request_t`) and then blocks until a response from corresponding slave and returns the status of command execution. This function provides a standard way for read/write access to Modbus devices in the network. + +:cpp:func:`mbc_master_get_cid_info`: The function gets information about each characteristic supported in the data dictionary and returns the characteristic's description in the form of the :cpp:type:`mb_parameter_descriptor_t` structure. Each characteristic is accessed using its CID. + +:cpp:func:`mbc_master_get_parameter`: The function reads the data of a characteristic defined in the parameters of a Modbus slave device. The additional data for request is taken from parameter description table. + +Example: + +.. code:: c + + const mb_parameter_descriptor_t* param_descriptor = NULL; + uint8_t temp_data[4] = {0}; // temporary buffer to hold maximum CID size + uint8_t type = 0; + .... + + // Get the information for characteristic cid from data dictionary + esp_err_t err = mbc_master_get_cid_info(cid, ¶m_descriptor); + if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) { + err = mbc_master_get_parameter(param_descriptor->cid, (char*)param_descriptor->param_key, (uint8_t*)temp_data, &type); + if (err == ESP_OK) { + ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%08x) read successful.", + param_descriptor->cid, + (char*)param_descriptor->param_key, + (char*)param_descriptor->param_units, + *(uint32_t*)temp_data); + } else { + ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).", + param_descriptor->cid, + (char*)param_descriptor->param_key, + (int)err, + (char*)esp_err_to_name(err)); + } + } else { + ESP_LOGE(TAG, "Could not get information for characteristic %d.", cid); + } + + +:cpp:func:`mbc_master_set_parameter` + +The function writes characteristic's value defined as a name and cid parameter in corresponded slave device. The additional data for parameter request is taken from master parameter description table. + +.. code:: c + + uint8_t type = 0; // Type of parameter + uint8_t temp_data[4] = {0}; // temporary buffer + + esp_err_t err = mbc_master_set_parameter(CID_TEMP_DATA_2, "Temperature_2", (uint8_t*)temp_data, &type); + if (err == ESP_OK) { + ESP_LOGI(TAG, "Set parameter data successfully."); + } else { + ESP_LOGE(TAG, "Set data fail, err = 0x%x (%s).", (int)err, (char*)esp_err_to_name(err)); + } + + +.. _modbus_api_master_destroy: + +Modbus Master Teardown +^^^^^^^^^^^^^^^^^^^^^^ + +This function stops Modbus communication stack and destroys controller interface and free all used active objects. + +:cpp:func:`mbc_master_destroy` + +.. code:: c + + ESP_ERROR_CHECK(mbc_master_destroy()); diff --git a/docs/en/overview_messaging_and_mapping.rst b/docs/en/overview_messaging_and_mapping.rst new file mode 100644 index 0000000..f62ca32 --- /dev/null +++ b/docs/en/overview_messaging_and_mapping.rst @@ -0,0 +1,47 @@ +ESP-Modbus +========== + +Overview +-------- + +The Modbus serial communication protocol is de facto standard protocol widely used to connect industrial electronic devices. Modbus allows communication among many devices connected to the same network, for example, a system that measures temperature and humidity and communicates the results to a computer. The Modbus protocol uses several types of data: Holding Registers, Input Registers, Coils (single bit output), Discrete Inputs. Versions of the Modbus protocol exist for serial port and for Ethernet and other protocols that support the Internet protocol suite. There are many variants of Modbus protocols, some of them are: + + * ``Modbus RTU`` — This is used in serial communication and makes use of a compact, binary representation of the data for protocol communication. The RTU format follows the commands/data with a cyclic redundancy check checksum as an error check mechanism to ensure the reliability of data. Modbus RTU is the most common implementation available for Modbus. A Modbus RTU message must be transmitted continuously without inter-character hesitations. Modbus messages are framed (separated) by idle (silent) periods. The RS-485 interface communication is usually used for this type. + * ``Modbus ASCII`` — This is used in serial communication and makes use of ASCII characters for protocol communication. The ASCII format uses a longitudinal redundancy check checksum. Modbus ASCII messages are framed by leading colon (":") and trailing newline (CR/LF). + * ``Modbus TCP/IP or Modbus TCP`` — This is a Modbus variant used for communications over TCP/IP networks, connecting over port 502. It does not require a checksum calculation, as lower layers already provide checksum protection. + +.. note:: This documentation (and included code snippets) requires some familiarity with the Modbus protocol. Refer to the Modbus Organization's with protocol specifications for specifics :ref:`modbus_organization`. + +Messaging Model And Data Mapping +-------------------------------- + +Modbus is an application protocol that defines rules for messaging structure and data organization that are independent of the data transmission medium. Traditional serial Modbus is a register-based protocol that defines message transactions that occur between master(s) and slave devices (multiple masters are allowed on using Modbus TCP/IP). The slave devices listen for communication from the master and simply respond as instructed. The master(s) always controls communication and may communicate directly to one slave, or all connected slaves, but the slaves cannot communicate directly with each other. + +.. figure:: ../_static/modbus-segment.png + :align: center + :scale: 80% + :alt: Modbus segment diagram + :figclass: align-center + + Modbus segment diagram + +.. note:: It is assumed that the number of slaves and their register maps are known by the Modbus master before the start of stack. + +The register map of each slave device is usually part of its device manual. A Slave device usually permits configuration of its short slave address and communication options that are used within the device's network segment. + +The Modbus protocol allows devices to map data to four types of registers (Holding, Input, Discrete, Coil). The figure below illustrates an example mapping of a device's data to the four types of registers. + +.. figure:: ../_static/modbus-data-mapping.png + :align: center + :scale: 80% + :alt: Modbus data mapping + :figclass: align-center + + Modbus data mapping + +The following sections give an overview of how to use the ESP_Modbus component found under `components/freemodbus`. The sections cover initialization of a Modbus port, and the setup a master or slave device accordingly: + +- :ref:`modbus_api_port_initialization` +- :ref:`modbus_api_slave_overview` +- :ref:`modbus_api_master_overview` + diff --git a/docs/en/port_initialization.rst b/docs/en/port_initialization.rst new file mode 100644 index 0000000..668c5f4 --- /dev/null +++ b/docs/en/port_initialization.rst @@ -0,0 +1,35 @@ +.. _modbus_api_port_initialization: + +Modbus Port Initialization +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ESP_Modbus supports Modbus SERIAL and TCP ports and a port must be initialized before calling any other Modbus API. The functions below are used to create and then initialize Modbus controller interface (either master or slave) over a particular transmission medium (either Serial or TCP/IP): + +- :cpp:func:`mbc_slave_init` +- :cpp:func:`mbc_master_init` +- :cpp:func:`mbc_slave_init_tcp` +- :cpp:func:`mbc_master_init_tcp` + +The API call uses the first parameter to recognize the type of port being initialized. Supported enumeration for different ports: :cpp:enumerator:`MB_PORT_SERIAL_MASTER`, :cpp:enumerator:`MB_PORT_SERIAL_SLAVE` accordingly. +The parameters :cpp:enumerator:`MB_PORT_TCP_MASTER`, :cpp:enumerator:`MB_PORT_TCP_SLAVE` are reserved for internal usage. + +.. code:: c + + void* master_handler = NULL; // Pointer to allocate interface structure + // Initialization of Modbus master for serial port + esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler); + if (master_handler == NULL || err != ESP_OK) { + ESP_LOGE(TAG, "mb controller initialization fail."); + } + +This example code to initialize slave port: + +.. code:: c + + void* slave_handler = NULL; // Pointer to allocate interface structure + // Initialization of Modbus slave for TCP + esp_err_t err = mbc_slave_init_tcp(&slave_handler); + if (slave_handler == NULL || err != ESP_OK) { + // Error handling is performed here + ESP_LOGE(TAG, "mb controller initialization fail."); + } \ No newline at end of file diff --git a/docs/en/slave_api_overview.rst b/docs/en/slave_api_overview.rst new file mode 100644 index 0000000..bc46d0d --- /dev/null +++ b/docs/en/slave_api_overview.rst @@ -0,0 +1,215 @@ +.. _modbus_api_slave_overview: + +Modbus Slave API Overview +------------------------- + +The sections below represent typical programming workflow for the slave API which should be called in following order: + +1. :ref:`modbus_api_port_initialization` - Initialization of Modbus controller interface for the selected port. +2. :ref:`modbus_api_slave_configure_descriptor` - Configure data descriptors to access slave parameters. +3. :ref:`modbus_api_slave_setup_communication_options` - Allows to setup communication options for selected port. +4. :ref:`modbus_api_slave_communication` - Start stack and sending / receiving data. Filter events when master accesses the register areas. +5. :ref:`modbus_api_slave_destroy` - Destroy Modbus controller and its resources. + +.. _modbus_api_slave_configure_descriptor: + +Configuring Slave Data Access +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following functions must be called when the Modbus controller slave port is already initialized. Refer to :ref:`modbus_api_port_initialization`. + +The slave stack requires the user to define structures (memory storage areas) that store the Modbus parameters accessed by stack. These structures should be prepared by the user and be assigned to the Modbus controller interface using :cpp:func:`mbc_slave_set_descriptor` API call before the start of communication. The slave task can call the :cpp:func:`mbc_slave_check_event` function which will block until the Modbus master access the slave. The slave task can then get information about the data being accessed. + +.. note:: One slave can define several area descriptors per each type of Modbus register area with different start_offset. + +Register area is defined by using the :cpp:type:`mb_register_area_descriptor_t` structure. + +.. list-table:: Table 3 Modbus register area descriptor + :widths: 8 92 + :header-rows: 1 + + * - Field + - Description + * - ``start_offset`` + - Zero based register relative offset for defined register area. Example: register address = 40002 ( 4x register area - Function 3 - holding register ), start_offset = 2 + * - ``type`` + - Type of the Modbus register area. Refer to :cpp:type:`mb_param_type_t` for more information. + * - ``address`` + - A pointer to the memory area which is used to store the register data for this area descriptor. + * - ``size`` + - The size of the memory area in bytes which is used to store register data. + +:cpp:func:`mbc_slave_set_descriptor` + +The function initializes Modbus communication descriptors for each type of Modbus register area (Holding Registers, Input Registers, Coils (single bit output), Discrete Inputs). Once areas are initialized and the :cpp:func:`mbc_slave_start()` API is called the Modbus stack can access the data in user data structures by request from master. + +.. code:: c + + #define MB_REG_INPUT_START_AREA0 (0) + #define MB_REG_HOLDING_START_AREA0 (0) + #define MB_REG_HOLD_CNT (100) + #define MB_REG_INPUT_CNT (100) + + mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure + unit16_t holding_reg_area[MB_REG_HOLD_CNT] = {0}; // storage area for holding registers + unit16_t input_reg_area[MB_REG_INPUT_CNT] = {0}; // storage area for input registers + + 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_area[0]; // Set pointer to storage instance + reg_area.size = sizeof(holding_reg_area) << 1; // Set the size of register storage area in bytes + ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area)); + + reg_area.type = MB_PARAM_INPUT; + reg_area.start_offset = MB_REG_INPUT_START_AREA0; + reg_area.address = (void*)&input_reg_area[0]; + reg_area.size = sizeof(input_reg_area) << 1; + ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area)); + + +At least one area descriptor per each Modbus register type must be set in order to provide register access to its area. If the master tries to access an undefined area, the stack will generate a Modbus exception. + +Direct access to register area from user application must be protected by critical section: + +.. code:: c + + portENTER_CRITICAL(¶m_lock); + holding_reg_area[2] += 10; + portEXIT_CRITICAL(¶m_lock); + + +.. _modbus_api_slave_setup_communication_options: + +Slave Communication Options +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The function initializes the Modbus controller interface and its active context (tasks, RTOS objects and other resources). + +:cpp:func:`mbc_slave_setup` + +The function is used to setup communication parameters of the Modbus stack. + +Example initialization of Modbus TCP communication: + +.. code:: c + + esp_netif_init(); + ... + + mb_communication_info_t comm_info = { + .ip_port = MB_TCP_PORT, // Modbus TCP port number (default = 502) + .ip_addr_type = MB_IPV4, // version of IP protocol + .ip_mode = MB_MODE_TCP, // Port communication mode + .ip_addr = NULL, // This field keeps the client IP address to bind, NULL - bind to any client + .ip_netif_ptr = esp_netif_ptr // esp_netif_ptr - pointer to the corresponding network interface + }; + + // Setup communication parameters and start stack + ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info)); + +Example initialization of Modbus serial communication: + +.. code:: c + + #define MB_SLAVE_DEV_SPEED 9600 + #define MB_SLAVE_ADDR 1 + #define MB_SLAVE_PORT_NUM 2 + ... + + // Setup communication parameters and start stack + mb_communication_info_t comm_info = { + .mode = MB_MODE_RTU, // Communication type + .slave_addr = MB_SLAVE_ADDR, // Short address of the slave + .port = MB_SLAVE_PORT_NUM, // UART physical port number + .baudrate = MB_SLAVE_DEV_SPEED, // Baud rate for communication + .parity = MB_PARITY_NONE // Parity option + }; + + ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info)); + +.. _modbus_api_slave_communication: + +Slave Communication +^^^^^^^^^^^^^^^^^^^ + +The function below is used to start Modbus controller interface and allows communication. + +:cpp:func:`mbc_slave_start` + +.. code:: c + + ESP_ERROR_CHECK(mbc_slave_start()); + +:cpp:func:`mbc_slave_check_event` + +The blocking call to function waits for a event specified (represented as an event mask parameter). Once the master accesses the parameter and the event mask matches the parameter type, the application task will be unblocked and function will return the corresponding event :cpp:type:`mb_event_group_t` which describes the type of register access being done. + +:cpp:func:`mbc_slave_get_param_info` + +The function gets information about accessed parameters from the Modbus controller event queue. The KConfig ``CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE`` key can be used to configure the notification queue size. The timeout parameter allows a timeout to be specified when waiting for a notification. The :cpp:type:`mb_param_info_t` structure contains information about accessed parameter. + +.. list-table:: Table 4 Description of the register info structure: :cpp:type:`mb_param_info_t` + :widths: 10 90 + :header-rows: 1 + + * - Field + - Description + * - ``time_stamp`` + - the time stamp of the event when defined parameter is accessed + * - ``mb_offset`` + - start Modbus register accessed by master + * - ``type`` + - type of the Modbus register area being accessed (See the :cpp:type:`mb_event_group_t` for more information) + * - ``address`` + - memory address that corresponds to accessed register in defined area descriptor + * - ``size`` + - number of registers being accessed by master + +Example to get event when holding or input registers accessed in the slave: + +.. code:: c + + #define MB_READ_MASK (MB_EVENT_INPUT_REG_RD | MB_EVENT_HOLDING_REG_RD) + #define MB_WRITE_MASK (MB_EVENT_HOLDING_REG_WR) + #define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK) + #define MB_PAR_INFO_GET_TOUT (10 / portTICK_RATE_MS) + .... + + // The function blocks while waiting for register access + mb_event_group_t event = mbc_slave_check_event(MB_READ_WRITE_MASK); + + // Get information about data accessed from master + ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT)); + const char* rw_str = (event & MB_READ_MASK) ? "READ" : "WRITE"; + + // Filter events and process them accordingly + if (event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) { + ESP_LOGI(TAG, "HOLDING %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u", + rw_str, + (uint32_t)reg_info.time_stamp, + (uint32_t)reg_info.mb_offset, + (uint32_t)reg_info.type, + (uint32_t)reg_info.address, + (uint32_t)reg_info.size); + } else if (event & (MB_EVENT_INPUT_REG_RD)) { + ESP_LOGI(TAG, "INPUT %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u", + rw_str, + (uint32_t)reg_info.time_stamp, + (uint32_t)reg_info.mb_offset, + (uint32_t)reg_info.type, + (uint32_t)reg_info.address, + (uint32_t)reg_info.size); + } + +.. _modbus_api_slave_destroy: + +Modbus Slave Teardown +^^^^^^^^^^^^^^^^^^^^^ + +This function stops the Modbus communication stack, destroys the controller interface, and frees all used active objects allocated for the slave. + +:cpp:func:`mbc_slave_destroy` + +.. code:: c + + ESP_ERROR_CHECK(mbc_slave_destroy()); \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..3b07ded --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1 @@ +esp-docs==0.2.0 \ No newline at end of file diff --git a/docs/utils.sh b/docs/utils.sh new file mode 100644 index 0000000..84f3748 --- /dev/null +++ b/docs/utils.sh @@ -0,0 +1,18 @@ +# Bash helper functions for adding SSH keys + +function add_ssh_keys() { + local key_string="${1}" + mkdir -p ~/.ssh + chmod 700 ~/.ssh + echo -n "${key_string}" >~/.ssh/id_rsa_base64 + base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 >~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa +} + +function add_doc_server_ssh_keys() { + local key_string="${1}" + local server_url="${2}" + local server_user="${3}" + add_ssh_keys "${key_string}" + echo -e "Host ${server_url}\n\tStrictHostKeyChecking no\n\tUser ${server_user}\n" >>~/.ssh/config +} diff --git a/examples/protocols/modbus/serial/README.md b/examples/protocols/modbus/serial/README.md deleted file mode 100644 index 2e5d0b7..0000000 --- a/examples/protocols/modbus/serial/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# Modbus Master-Slave Example - -## Overview - -These two projects illustrate the communication between Modbus master and slave device in the segment. -Master initializes Modbus interface driver and then reads parameters from slave device in the segment. -After several successful read attempts slave sets the alarm relay (end of test condition). -Once master reads the alarm it stops communication and destroy driver. - -The examples: - -* `examples/protocols/modbus/serial/mb_master` - Modbus serial master ASCII/RTU -* `examples/protocols/modbus/serial/mb_slave` - Modbus serial slave ASCII/RTU - -See README.md for each individual project for more information. - -## How to use example - -### Hardware Required - -This example can be run on any commonly available ESP32 development board. -The master and slave boards should be connected to each other through the RS485 interface line driver. -See the connection schematic in README.md files of each example. - -### Configure the project - -This example test requires communication mode setting for master and slave be the same and slave address set to 1. -Please refer to README.md files of each example project for more information. - -## About common_component in this example - -The folder "mb_example_common" includes definitions of parameter structures for master and slave device (both projects share the same parameters). -However, currently it is for example purpose only and can be modified for particular application. - -## Example Output - -Example of Slave output: - -``` -I (343) SLAVE_TEST: Modbus slave stack initialized. -I (343) SLAVE_TEST: Start modbus test... -I (81463) SLAVE_TEST: HOLDING READ (81150420 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 -I (82463) SLAVE_TEST: HOLDING READ (82150720 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 -I (83573) SLAVE_TEST: HOLDING READ (83260630 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 -I (84603) SLAVE_TEST: HOLDING READ (84290530 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 -I (85703) SLAVE_TEST: HOLDING READ (85396692 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 -``` - -Example of Modbus Master output: - -``` -I (399) MASTER_TEST: Modbus master stack initialized... -I (499) MASTER_TEST: Start modbus test... -I (549) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.230000 (0x3f9d70a4) read successful. -I (629) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 12.100000 (0x4141999a) read successful. -I (709) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 3.560000 (0x4063d70a) read successful. -I (769) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 23.400000 (0x41bb3333) read successful. -I (829) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 5.890000 (0x40bc7ae1) read successful. -I (889) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 34.500000 (0x420a0000) read successful. -E (949) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x108) (ESP_ERR_INVALID_RESPONSE). -E (949) MASTER_TEST: Characteristic #6 (RelayP1) read fail, err = 264 (ESP_ERR_INVALID_RESPONSE). -E (1029) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x108) (ESP_ERR_INVALID_RESPONSE). -E (1029) MASTER_TEST: Characteristic #7 (RelayP2) read fail, err = 264 (ESP_ERR_INVALID_RESPONSE). -``` - -## Troubleshooting - -If the examples do not work as expected and slave and master boards are not able to communicate correctly it is possible to find the reason for errors. -The most important errors are described in master example output and formatted as below: - -``` -E (1692332) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x107) (ESP_ERR_TIMEOUT). -``` - -ESP_ERR_TIMEOUT (0x107) - Modbus slave device does not respond during configured timeout. Check the connection and ability for communication using uart_echo_rs485 example or increase -Kconfig value CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND (CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS). - -ESP_ERR_NOT_SUPPORTED (0x106), ESP_ERR_INVALID_RESPONSE (0x108) - Modbus slave device does not support requested command or register and sent exeption response. - -ESP_ERR_INVALID_STATE (0x103) - Modbus stack is not configured correctly or can't work correctly due to critical failure. - - diff --git a/examples/protocols/modbus/serial/example_test.py b/examples/protocols/modbus/serial/example_test.py deleted file mode 100644 index 4a8b15d..0000000 --- a/examples/protocols/modbus/serial/example_test.py +++ /dev/null @@ -1,290 +0,0 @@ -# SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD -# SPDX-License-Identifier: Apache-2.0 - -# Need Python 3 string formatting functions -from __future__ import print_function - -import logging -import os -import re -from threading import Thread - -import ttfw_idf - -LOG_LEVEL = logging.DEBUG -LOGGER_NAME = 'modbus_test' - -# Allowed parameter reads -TEST_READ_MIN_COUNT = 10 # Minimum number of correct readings -TEST_READ_MAX_ERR_COUNT = 2 # Maximum allowed read errors during initialization - -TEST_THREAD_EXPECT_TIMEOUT = 120 # Test theread expect timeout in seconds -TEST_THREAD_JOIN_TIMEOUT = 180 # Test theread join timeout in seconds - -# Test definitions -TEST_MASTER_RTU = 'master_rtu' -TEST_SLAVE_RTU = 'slave_rtu' - -TEST_MASTER_ASCII = 'master_ascii' -TEST_SLAVE_ASCII = 'slave_ascii' - -# Define tuple of strings to expect for each DUT. -# -master_expect = ('MASTER_TEST: Modbus master stack initialized...', 'MASTER_TEST: Start modbus test...', 'MASTER_TEST: Destroy master...') -slave_expect = ('SLAVE_TEST: Modbus slave stack initialized.', 'SLAVE_TEST: Start modbus test...', 'SLAVE_TEST: Modbus controller destroyed.') - -# The dictionary for expected values in listing -expect_dict_master_ok = {'START': (), - 'READ_PAR_OK': (), - 'ALARM_MSG': (u'7',)} - -expect_dict_master_err = {'READ_PAR_ERR': (u'263', u'ESP_ERR_TIMEOUT'), - 'READ_STK_ERR': (u'107', u'ESP_ERR_TIMEOUT')} - -# The dictionary for regular expression patterns to check in listing -pattern_dict_master_ok = {'START': (r'.*I \([0-9]+\) MASTER_TEST: Start modbus test...'), - 'READ_PAR_OK': (r'.*I\s\([0-9]+\) MASTER_TEST: Characteristic #[0-9]+ [a-zA-Z0-9_]+' - r'\s\([a-zA-Z\%\/]+\) value = [a-zA-Z0-9\.\s]*\(0x[a-zA-Z0-9]+\) read successful.'), - 'ALARM_MSG': (r'.*I \([0-9]*\) MASTER_TEST: Alarm triggered by cid #([0-9]+).')} - -pattern_dict_master_err = {'READ_PAR_ERR_TOUT': (r'.*E \([0-9]+\) MASTER_TEST: Characteristic #[0-9]+' - r'\s\([a-zA-Z0-9_]+\) read fail, err = [0-9]+ \([_A-Z]+\).'), - 'READ_STK_ERR_TOUT': (r'.*E \([0-9]+\) MB_CONTROLLER_MASTER: [a-zA-Z0-9_]+\([0-9]+\):\s' - r'SERIAL master get parameter failure error=\(0x([a-zA-Z0-9]+)\) \(([_A-Z]+)\).')} - -# The dictionary for expected values in listing -expect_dict_slave_ok = {'START': (), - 'READ_PAR_OK': (), - 'DESTROY': ()} - -# The dictionary for regular expression patterns to check in listing -pattern_dict_slave_ok = {'START': (r'.*I \([0-9]+\) SLAVE_TEST: Start modbus test...'), - 'READ_PAR_OK': (r'.*I\s\([0-9]+\) SLAVE_TEST: [A-Z]+ READ \([a-zA-Z0-9_]+ us\),\s' - r'ADDR:[0-9]+, TYPE:[0-9]+, INST_ADDR:0x[a-zA-Z0-9]+, SIZE:[0-9]+'), - 'DESTROY': (r'.*I\s\([0-9]+\) SLAVE_TEST: Modbus controller destroyed.')} - -logger = logging.getLogger(LOGGER_NAME) - - -class DutTestThread(Thread): - def __init__(self, dut=None, name=None, expect=None): - """ Initialize the thread parameters - """ - self.tname = name - self.dut = dut - self.expected = expect - self.result = False - self.data = None - super(DutTestThread, self).__init__() - - def run(self): - """ The function implements thread functionality - """ - # Must reset again as flashing during start_app will reset multiple times, causing unexpected results - self.dut.reset() - - # Capture output from the DUT - self.dut.start_capture_raw_data() - - # Check expected strings in the listing - for string in self.expected: - self.dut.expect(string, TEST_THREAD_EXPECT_TIMEOUT) - - # Check DUT exceptions - dut_exceptions = self.dut.get_exceptions() - if 'Guru Meditation Error:' in dut_exceptions: - raise Exception('%s generated an exception: %s\n' % (str(self.dut), dut_exceptions)) - - # Mark thread has run to completion without any exceptions - self.data = self.dut.stop_capture_raw_data() - self.result = True - - -def test_filter_output(data=None, start_pattern=None, end_pattern=None): - """Use patters to filter output - """ - start_index = str(data).find(start_pattern) - end_index = str(data).find(end_pattern) - logger.debug('Listing start index= %d, end=%d' % (start_index, end_index)) - if start_index == -1 or end_index == -1: - return data - return data[start_index:end_index + len(end_pattern)] - - -def test_expect_re(data, pattern): - """ - Check if re pattern is matched in data cache - :param data: data to process - :param pattern: compiled RegEx pattern - :return: match groups if match succeed otherwise None - """ - ret = None - if isinstance(pattern, type(u'')): - pattern = pattern.encode('utf-8') - regex = re.compile(pattern) - if isinstance(data, type(u'')): - data = data.encode('utf-8') - match = regex.search(data) - if match: - ret = tuple(None if x is None else x.decode() for x in match.groups()) - index = match.end() - else: - index = None - return ret, index - - -def test_check_output(data=None, check_dict=None, expect_dict=None): - """ Check output for the test - Check log using regular expressions: - """ - global logger - match_count = 0 - index = 0 - data_lines = data.splitlines() - for key, pattern in check_dict.items(): - if key not in expect_dict: - break - # Check pattern in the each line - for line in data_lines: - group, index = test_expect_re(line, pattern) - if index is not None: - logger.debug('Found key{%s}=%s, line: \n%s' % (key, group, line)) - if expect_dict[key] == group: - logger.debug('The result is correct for the key:%s, expected:%s == returned:%s' % (key, str(expect_dict[key]), str(group))) - match_count += 1 - return match_count - - -def test_check_mode(dut=None, mode_str=None, value=None): - """ Check communication mode for dut - """ - global logger - try: - opt = dut.app.get_sdkconfig()[mode_str] - logger.info('%s {%s} = %s.\n' % (str(dut), mode_str, opt)) - return value == opt - except Exception: - logger.info('ENV_TEST_FAILURE: %s: Cannot find option %s in sdkconfig.' % (str(dut), mode_str)) - return False - - -@ttfw_idf.idf_example_test(env_tag='Example_T2_RS485', target=['esp32']) -def test_modbus_serial_communication(env, comm_mode): - global logger - - # Get device under test. "dut1 - master", "dut2 - slave" must be properly connected through RS485 interface driver - dut_master = env.get_dut('modbus_master', 'examples/protocols/modbus/serial/mb_master', dut_class=ttfw_idf.ESP32DUT) - dut_slave = env.get_dut('modbus_slave', 'examples/protocols/modbus/serial/mb_slave', dut_class=ttfw_idf.ESP32DUT) - - try: - logger.debug('Environment vars: %s\r\n' % os.environ) - logger.debug('DUT slave sdkconfig: %s\r\n' % dut_slave.app.get_sdkconfig()) - logger.debug('DUT master sdkconfig: %s\r\n' % dut_master.app.get_sdkconfig()) - - # Check Kconfig configuration options for each built example - if test_check_mode(dut_master, 'CONFIG_MB_COMM_MODE_ASCII', 'y') and test_check_mode(dut_slave, 'CONFIG_MB_COMM_MODE_ASCII', 'y'): - logger.info('ENV_TEST_INFO: Modbus ASCII test mode selected in the configuration. \n') - slave_name = TEST_SLAVE_ASCII - master_name = TEST_MASTER_ASCII - elif test_check_mode(dut_master, 'CONFIG_MB_COMM_MODE_RTU', 'y') and test_check_mode(dut_slave, 'CONFIG_MB_COMM_MODE_RTU', 'y'): - logger.info('ENV_TEST_INFO: Modbus RTU test mode selected in the configuration. \n') - slave_name = TEST_SLAVE_RTU - master_name = TEST_MASTER_RTU - else: - logger.error("ENV_TEST_FAILURE: Communication mode in master and slave configuration don't match.\n") - raise Exception("ENV_TEST_FAILURE: Communication mode in master and slave configuration don't match.\n") - # Check if slave address for example application is default one to be able to communicate - if not test_check_mode(dut_slave, 'CONFIG_MB_SLAVE_ADDR', '1'): - logger.error('ENV_TEST_FAILURE: Slave address option is incorrect.\n') - raise Exception('ENV_TEST_FAILURE: Slave address option is incorrect.\n') - - # Flash app onto each DUT - dut_master.start_app() - dut_slave.start_app() - - # Create thread for each dut - dut_master_thread = DutTestThread(dut=dut_master, name=master_name, expect=master_expect) - dut_slave_thread = DutTestThread(dut=dut_slave, name=slave_name, expect=slave_expect) - - # Start each thread - dut_slave_thread.start() - dut_master_thread.start() - - # Wait for threads to complete - dut_slave_thread.join(timeout=TEST_THREAD_JOIN_TIMEOUT) - dut_master_thread.join(timeout=TEST_THREAD_JOIN_TIMEOUT) - - if dut_slave_thread.isAlive(): - logger.error('ENV_TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n' % - (dut_slave_thread.tname, TEST_THREAD_JOIN_TIMEOUT)) - raise Exception('ENV_TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n' % - (dut_slave_thread.tname, TEST_THREAD_JOIN_TIMEOUT)) - - if dut_master_thread.isAlive(): - logger.error('ENV_TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n' % - (dut_master_thread.tname, TEST_THREAD_JOIN_TIMEOUT)) - raise Exception('ENV_TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n' % - (dut_master_thread.tname, TEST_THREAD_JOIN_TIMEOUT)) - finally: - dut_master.close() - dut_slave.close() - - # Check if test threads completed successfully and captured data - if not dut_slave_thread.result or dut_slave_thread.data is None: - logger.error('The thread %s was not run successfully.' % dut_slave_thread.tname) - raise Exception('The thread %s was not run successfully.' % dut_slave_thread.tname) - - if not dut_master_thread.result or dut_master_thread.data is None: - logger.error('The thread %s was not run successfully.' % dut_slave_thread.tname) - raise Exception('The thread %s was not run successfully.' % dut_master_thread.tname) - - # Filter output to get test messages - master_output = test_filter_output(dut_master_thread.data, master_expect[0], master_expect[len(master_expect) - 1]) - if master_output is not None: - logger.info('The data for master thread is captured.') - logger.debug(master_output) - - slave_output = test_filter_output(dut_slave_thread.data, slave_expect[0], slave_expect[len(slave_expect) - 1]) - if slave_output is not None: - logger.info('The data for slave thread is captured.') - logger.debug(slave_output) - - # Check if parameters are read correctly by master - match_count = test_check_output(master_output, pattern_dict_master_ok, expect_dict_master_ok) - if match_count < TEST_READ_MIN_COUNT: - logger.error('There are errors reading parameters from %s, %d' % (dut_master_thread.tname, match_count)) - raise Exception('There are errors reading parameters from %s, %d' % (dut_master_thread.tname, match_count)) - logger.info('OK pattern test for %s, match_count=%d.' % (dut_master_thread.tname, match_count)) - - # If the test completed successfully (alarm triggered) but there are some errors during reading of parameters - match_count = test_check_output(master_output, pattern_dict_master_err, expect_dict_master_err) - if match_count > TEST_READ_MAX_ERR_COUNT: - logger.error('There are errors reading parameters from %s, %d' % (dut_master_thread.tname, match_count)) - raise Exception('There are errors reading parameters from %s, %d' % (dut_master_thread.tname, match_count)) - logger.info('ERROR pattern test for %s, match_count=%d.' % (dut_master_thread.tname, match_count)) - - match_count = test_check_output(slave_output, pattern_dict_slave_ok, expect_dict_slave_ok) - if match_count < TEST_READ_MIN_COUNT: - logger.error('There are errors reading parameters from %s, %d' % (dut_slave_thread.tname, match_count)) - raise Exception('There are errors reading parameters from %s, %d' % (dut_slave_thread.tname, match_count)) - logger.info('OK pattern test for %s, match_count=%d.' % (dut_slave_thread.tname, match_count)) - - -if __name__ == '__main__': - logger = logging.getLogger(LOGGER_NAME) - # create file handler which logs even debug messages - fh = logging.FileHandler('modbus_test.log') - fh.setLevel(logging.DEBUG) - logger.setLevel(logging.DEBUG) - # create console handler - ch = logging.StreamHandler() - ch.setLevel(logging.INFO) - # set format of output for both handlers - formatter = logging.Formatter('%(levelname)s:%(message)s') - ch.setFormatter(formatter) - fh.setFormatter(formatter) - logger.addHandler(fh) - logger.addHandler(ch) - logger.info('Start script %s.' % os.path.basename(__file__)) - test_modbus_serial_communication() - logging.shutdown() diff --git a/examples/protocols/modbus/serial/mb_master/CMakeLists.txt b/examples/protocols/modbus/serial/mb_master/CMakeLists.txt deleted file mode 100644 index 51733e0..0000000 --- a/examples/protocols/modbus/serial/mb_master/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# 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(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/modbus/mb_example_common) - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(modbus_master) diff --git a/examples/protocols/modbus/serial/mb_slave/CMakeLists.txt b/examples/protocols/modbus/serial/mb_slave/CMakeLists.txt deleted file mode 100644 index ef50208..0000000 --- a/examples/protocols/modbus/serial/mb_slave/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/modbus/mb_example_common) - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) - -project(modbus_slave) diff --git a/examples/protocols/modbus/tcp/README.md b/examples/protocols/modbus/tcp/README.md deleted file mode 100644 index 5e62a00..0000000 --- a/examples/protocols/modbus/tcp/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# Modbus TCP Master-Slave Example - -## Overview - -These two projects illustrate the communication between Modbus master and slave device in the segment. -Master initializes Modbus interface driver and then reads parameters from slave device in the segment. -After several successful read attempts slave sets the alarm relay (end of test condition). -Once master reads the alarm it stops communication and destroy driver. - -The examples: - -* `examples/protocols/modbus/tcp/mb_tcp_master` - Modbus TCP master -* `examples/protocols/modbus/tcp/mb_tcp_slave` - Modbus TCP slave - -See README.md for each individual project for more information. - -## How to use example - -### Hardware Required - -This example can be run on any commonly available ESP32(-S2) development board. -The master and slave boards should be connected to the same network (see the README.md file in example folder) and slave address `CONFIG_MB_SLAVE_ADDR` be defined for slave board(s). -See the connection schematic in README.md files of each example. - -### Configure the project - -This example test requires communication mode setting for master and slave be the same and slave address set to 1. -Please refer to README.md files of each example project for more information. This example uses the default option `CONFIG_MB_SLAVE_IP_FROM_STDIN` to resolve slave IP address and supports IPv4 address type for communication in this case. - -## About common_component in this example - -The folder "mb_example_common" one level above includes definitions of parameter structures for master and slave device (both projects share the same parameters). -However, currently it is for example purpose only and can be modified for particular application. - -## Example Output - -Refer to README.md file in the appropriate example folder for more information about master and slave log output. - -## Troubleshooting - -If the examples do not work as expected and slave and master boards are not able to communicate correctly it is possible to find the reason for errors. -The most important errors are described in master example output and formatted as below: - -``` -E (1692332) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x107) (ESP_ERR_TIMEOUT). -``` - -ESP_ERR_TIMEOUT (0x107) - Modbus slave device does not respond during configured timeout. -Check ability for communication pinging each slave configured in the master parameter description table or use command on your host machine to find modbus slave using mDNS (requires `CONFIG_MB_MDNS_IP_RESOLVER` option be enabled): -```>dns-sd -L mb_slave_tcp_XX _modbus._tcp .``` -where XX is the short slave address (index) of the slave configured in the Kconfig of slave example. -Also it is possible to increase Kconfig value `CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND` to compensate network communication delays between master and slaves. - -ESP_ERR_NOT_SUPPORTED (0x106), ESP_ERR_INVALID_RESPONSE (0x108) - Modbus slave device does not support requested command or register and sent exeption response. - -ESP_ERR_INVALID_STATE (0x103) - Modbus stack is not configured correctly or can't work correctly due to critical failure. - - diff --git a/examples/protocols/modbus/tcp/example_test.py b/examples/protocols/modbus/tcp/example_test.py deleted file mode 100644 index 925b465..0000000 --- a/examples/protocols/modbus/tcp/example_test.py +++ /dev/null @@ -1,308 +0,0 @@ -# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD -# SPDX-License-Identifier: Apache-2.0 - -import logging -import os -import re -from threading import Thread - -import ttfw_idf -from tiny_test_fw import DUT - -LOG_LEVEL = logging.DEBUG -LOGGER_NAME = 'modbus_test' - -# Allowed options for the test -TEST_READ_MAX_ERR_COUNT = 3 # Maximum allowed read errors during initialization -TEST_THREAD_JOIN_TIMEOUT = 60 # Test theread join timeout in seconds -TEST_EXPECT_STR_TIMEOUT = 30 # Test expect timeout in seconds -TEST_MASTER_TCP = 'mb_tcp_master' -TEST_SLAVE_TCP = 'mb_tcp_slave' - -STACK_DEFAULT = 0 -STACK_IPV4 = 1 -STACK_IPV6 = 2 -STACK_INIT = 3 -STACK_CONNECT = 4 -STACK_START = 5 -STACK_PAR_OK = 6 -STACK_PAR_FAIL = 7 -STACK_DESTROY = 8 - -pattern_dict_slave = {STACK_IPV4: (r'.*I \([0-9]+\) example_connect: - IPv4 address: ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*'), - STACK_IPV6: (r'.*I \([0-9]+\) example_connect: - IPv6 address: (([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}).*'), - STACK_INIT: (r'.*I \(([0-9]+)\) MB_TCP_SLAVE_PORT: (Protocol stack initialized).'), - STACK_CONNECT: (r'.*I\s\(([0-9]+)\) MB_TCP_SLAVE_PORT: Socket \(#[0-9]+\), accept client connection from address: ' - r'([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*'), - STACK_START: (r'.*I\s\(([0-9]+)\) SLAVE_TEST: (Start modbus test).*'), - STACK_PAR_OK: (r'.*I\s\(([0-9]+)\) SLAVE_TEST: ([A-Z]+ [A-Z]+) \([a-zA-Z0-9_]+ us\),\s' - r'ADDR:([0-9]+), TYPE:[0-9]+, INST_ADDR:0x[a-zA-Z0-9]+, SIZE:[0-9]+'), - STACK_PAR_FAIL: (r'.*E \(([0-9]+)\) SLAVE_TEST: Response time exceeds configured [0-9]+ [ms], ignore packet.*'), - STACK_DESTROY: (r'.*I\s\(([0-9]+)\) SLAVE_TEST: (Modbus controller destroyed).')} - -pattern_dict_master = {STACK_IPV4: (r'.*I \([0-9]+\) example_connect: - IPv4 address: ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*'), - STACK_IPV6: (r'.*I \([0-9]+\) example_connect: - IPv6 address: (([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}).*'), - STACK_INIT: (r'.*I \(([0-9]+)\) MASTER_TEST: (Modbus master stack initialized).*'), - STACK_CONNECT: (r'.*.*I\s\(([0-9]+)\) MB_TCP_MASTER_PORT: (Connected [0-9]+ slaves), start polling.*'), - STACK_START: (r'.*I \(([0-9]+)\) MASTER_TEST: (Start modbus test).*'), - STACK_PAR_OK: (r'.*I\s\(([0-9]+)\) MASTER_TEST: Characteristic #[0-9]+ ([a-zA-Z0-9_]+)' - r'\s\([a-zA-Z\%\/]+\) value = [a-zA-Z0-9\.]+ \(0x[a-zA-Z0-9]+\) read successful.*'), - STACK_PAR_FAIL: (r'.*E \(([0-9]+)\) MASTER_TEST: Characteristic #[0-9]+\s\(([a-zA-Z0-9_]+)\)\s' - r'read fail, err = [0-9]+ \([_A-Z]+\).*'), - STACK_DESTROY: (r'.*I\s\(([0-9]+)\) MASTER_TEST: (Destroy master).*')} - -logger = logging.getLogger(LOGGER_NAME) - - -class DutTestThread(Thread): - """ Test thread class - """ - def __init__(self, dut=None, name=None, ip_addr=None, expect=None): - """ Initialize the thread parameters - """ - self.tname = name - self.dut = dut - self.expected = expect - self.data = None - self.ip_addr = ip_addr - self.test_finish = False - self.param_fail_count = 0 - self.param_ok_count = 0 - self.test_stage = STACK_DEFAULT - super(DutTestThread, self).__init__() - - def __enter__(self): - logger.debug('Restart %s.', self.tname) - # Reset DUT first - self.dut.reset() - # Capture output from the DUT - self.dut.start_capture_raw_data(capture_id=self.dut.name) - return self - - def __exit__(self, exc_type, exc_value, traceback): - """ The exit method of context manager - """ - if exc_type is not None or exc_value is not None: - logger.info('Thread %s rised an exception type: %s, value: %s', self.tname, str(exc_type), str(exc_value)) - - def run(self): - """ The function implements thread functionality - """ - # Initialize slave IP for master board - if (self.ip_addr is not None): - self.set_ip(0) - - # Check expected strings in the listing - self.test_start(TEST_EXPECT_STR_TIMEOUT) - - # Check DUT exceptions - dut_exceptions = self.dut.get_exceptions() - if 'Guru Meditation Error:' in dut_exceptions: - raise RuntimeError('%s generated an exception(s): %s\n' % (str(self.dut), dut_exceptions)) - - # Mark thread has run to completion without any exceptions - self.data = self.dut.stop_capture_raw_data(capture_id=self.dut.name) - - def set_ip(self, index=0): - """ The method to send slave IP to master application - """ - message = r'.*Waiting IP([0-9]{1,2}) from stdin.*' - # Read all data from previous restart to get prompt correctly - self.dut.read() - result = self.dut.expect(re.compile(message), timeout=TEST_EXPECT_STR_TIMEOUT) - if int(result[0]) != index: - raise RuntimeError('Incorrect index of IP=%s for %s\n' % (result[0], str(self.dut))) - # Use the same slave IP address for all characteristics during the test - self.dut.write('IP0=' + self.ip_addr, '\n', False) - self.dut.write('IP1=' + self.ip_addr, '\n', False) - self.dut.write('IP2=' + self.ip_addr, '\n', False) - logger.debug('Set IP address=%s for %s', self.ip_addr, self.tname) - message = r'.*IP\([0-9]+\) = \[([0-9a-zA-Z\.\:]+)\] set from stdin.*' - result = self.dut.expect(re.compile(message), timeout=TEST_EXPECT_STR_TIMEOUT) - logger.debug('Thread %s initialized with slave IP=%s.', self.tname, self.ip_addr) - - def test_start(self, timeout_value): - """ The method to initialize and handle test stages - """ - def handle_get_ip4(data): - """ Handle get_ip v4 - """ - logger.debug('%s[STACK_IPV4]: %s', self.tname, str(data)) - self.test_stage = STACK_IPV4 - - def handle_get_ip6(data): - """ Handle get_ip v6 - """ - logger.debug('%s[STACK_IPV6]: %s', self.tname, str(data)) - self.test_stage = STACK_IPV6 - - def handle_init(data): - """ Handle init - """ - logger.debug('%s[STACK_INIT]: %s', self.tname, str(data)) - self.test_stage = STACK_INIT - - def handle_connect(data): - """ Handle connect - """ - logger.debug('%s[STACK_CONNECT]: %s', self.tname, str(data)) - self.test_stage = STACK_CONNECT - - def handle_test_start(data): - """ Handle connect - """ - logger.debug('%s[STACK_START]: %s', self.tname, str(data)) - self.test_stage = STACK_START - - def handle_par_ok(data): - """ Handle parameter ok - """ - logger.debug('%s[READ_PAR_OK]: %s', self.tname, str(data)) - if self.test_stage >= STACK_START: - self.param_ok_count += 1 - self.test_stage = STACK_PAR_OK - - def handle_par_fail(data): - """ Handle parameter fail - """ - logger.debug('%s[READ_PAR_FAIL]: %s', self.tname, str(data)) - self.param_fail_count += 1 - self.test_stage = STACK_PAR_FAIL - - def handle_destroy(data): - """ Handle destroy - """ - logger.debug('%s[DESTROY]: %s', self.tname, str(data)) - self.test_stage = STACK_DESTROY - self.test_finish = True - - while not self.test_finish: - try: - self.dut.expect_any((re.compile(self.expected[STACK_IPV4]), handle_get_ip4), - (re.compile(self.expected[STACK_IPV6]), handle_get_ip6), - (re.compile(self.expected[STACK_INIT]), handle_init), - (re.compile(self.expected[STACK_CONNECT]), handle_connect), - (re.compile(self.expected[STACK_START]), handle_test_start), - (re.compile(self.expected[STACK_PAR_OK]), handle_par_ok), - (re.compile(self.expected[STACK_PAR_FAIL]), handle_par_fail), - (re.compile(self.expected[STACK_DESTROY]), handle_destroy), - timeout=timeout_value) - except DUT.ExpectTimeout: - logger.debug('%s, expect timeout on stage #%d (%s seconds)', self.tname, self.test_stage, timeout_value) - self.test_finish = True - - -def test_check_mode(dut=None, mode_str=None, value=None): - """ Check communication mode for dut - """ - global logger - try: - opt = dut.app.get_sdkconfig()[mode_str] - logger.debug('%s {%s} = %s.\n', str(dut), mode_str, opt) - return value == opt - except Exception: - logger.error('ENV_TEST_FAILURE: %s: Cannot find option %s in sdkconfig.', str(dut), mode_str) - return False - - -@ttfw_idf.idf_example_test(env_tag='Example_Modbus_TCP', target=['esp32']) -def test_modbus_tcp_communication(env, comm_mode): - global logger - - rel_project_path = os.path.join('examples', 'protocols', 'modbus', 'tcp') - # Get device under test. Both duts must be able to be connected to WiFi router - dut_master = env.get_dut('modbus_tcp_master', os.path.join(rel_project_path, TEST_MASTER_TCP)) - dut_slave = env.get_dut('modbus_tcp_slave', os.path.join(rel_project_path, TEST_SLAVE_TCP)) - log_file = os.path.join(env.log_path, 'modbus_tcp_test.log') - print('Logging file name: %s' % log_file) - - try: - # create file handler which logs even debug messages - logger.setLevel(logging.DEBUG) - fh = logging.FileHandler(log_file) - fh.setLevel(logging.DEBUG) - # set format of output for both handlers - formatter = logging.Formatter('%(levelname)s:%(message)s') - fh.setFormatter(formatter) - logger.addHandler(fh) - # create console handler - ch = logging.StreamHandler() - ch.setLevel(logging.INFO) - # set format of output for both handlers - formatter = logging.Formatter('%(levelname)s:%(message)s') - ch.setFormatter(formatter) - logger.addHandler(ch) - - # Check Kconfig configuration options for each built example - if (test_check_mode(dut_master, 'CONFIG_FMB_COMM_MODE_TCP_EN', 'y') and - test_check_mode(dut_slave, 'CONFIG_FMB_COMM_MODE_TCP_EN', 'y')): - slave_name = TEST_SLAVE_TCP - master_name = TEST_MASTER_TCP - else: - logger.error('ENV_TEST_FAILURE: IP resolver mode do not match in the master and slave implementation.\n') - raise RuntimeError('ENV_TEST_FAILURE: IP resolver mode do not match in the master and slave implementation.\n') - address = None - if test_check_mode(dut_master, 'CONFIG_MB_SLAVE_IP_FROM_STDIN', 'y'): - logger.info('ENV_TEST_INFO: Set slave IP address through STDIN.\n') - # Flash app onto DUT (Todo: Debug case when the slave flashed before master then expect does not work correctly for no reason - dut_slave.start_app() - dut_master.start_app() - if test_check_mode(dut_master, 'CONFIG_EXAMPLE_CONNECT_IPV6', 'y'): - address = dut_slave.expect(re.compile(pattern_dict_slave[STACK_IPV6]), TEST_EXPECT_STR_TIMEOUT) - else: - address = dut_slave.expect(re.compile(pattern_dict_slave[STACK_IPV4]), TEST_EXPECT_STR_TIMEOUT) - if address is not None: - print('Found IP slave address: %s' % address[0]) - else: - raise RuntimeError('ENV_TEST_FAILURE: Slave IP address is not found in the output. Check network settings.\n') - else: - raise RuntimeError('ENV_TEST_FAILURE: Slave IP resolver is not configured correctly.\n') - - # Create thread for each dut - with DutTestThread(dut=dut_master, name=master_name, ip_addr=address[0], expect=pattern_dict_master) as dut_master_thread: - with DutTestThread(dut=dut_slave, name=slave_name, ip_addr=None, expect=pattern_dict_slave) as dut_slave_thread: - - # Start each thread - dut_slave_thread.start() - dut_master_thread.start() - - # Wait for threads to complete - dut_slave_thread.join(timeout=TEST_THREAD_JOIN_TIMEOUT) - dut_master_thread.join(timeout=TEST_THREAD_JOIN_TIMEOUT) - - if dut_slave_thread.is_alive(): - logger.error('ENV_TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n', - dut_slave_thread.tname, TEST_THREAD_JOIN_TIMEOUT) - raise RuntimeError('ENV_TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n' % - (dut_slave_thread.tname, TEST_THREAD_JOIN_TIMEOUT)) - - if dut_master_thread.is_alive(): - logger.error('TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n', - dut_master_thread.tname, TEST_THREAD_JOIN_TIMEOUT) - raise RuntimeError('TEST_FAILURE: The thread %s is not completed successfully after %d seconds.\n' % - (dut_master_thread.tname, TEST_THREAD_JOIN_TIMEOUT)) - - logger.info('TEST_INFO: %s error count = %d, %s error count = %d.\n', - dut_master_thread.tname, dut_master_thread.param_fail_count, - dut_slave_thread.tname, dut_slave_thread.param_fail_count) - logger.info('TEST_INFO: %s ok count = %d, %s ok count = %d.\n', - dut_master_thread.tname, dut_master_thread.param_ok_count, - dut_slave_thread.tname, dut_slave_thread.param_ok_count) - - if ((dut_master_thread.param_fail_count > TEST_READ_MAX_ERR_COUNT) or - (dut_slave_thread.param_fail_count > TEST_READ_MAX_ERR_COUNT) or - (dut_slave_thread.param_ok_count == 0) or - (dut_master_thread.param_ok_count == 0)): - raise RuntimeError('TEST_FAILURE: %s parameter read error(ok) count = %d(%d), %s parameter read error(ok) count = %d(%d).\n' % - (dut_master_thread.tname, dut_master_thread.param_fail_count, dut_master_thread.param_ok_count, - dut_slave_thread.tname, dut_slave_thread.param_fail_count, dut_slave_thread.param_ok_count)) - logger.info('TEST_SUCCESS: The Modbus parameter test is completed successfully.\n') - - finally: - dut_master.close() - dut_slave.close() - logging.shutdown() - - -if __name__ == '__main__': - test_modbus_tcp_communication() diff --git a/components/freemodbus/common/esp_modbus_callbacks.h b/freemodbus/common/esp_modbus_callbacks.h similarity index 100% rename from components/freemodbus/common/esp_modbus_callbacks.h rename to freemodbus/common/esp_modbus_callbacks.h diff --git a/components/freemodbus/common/esp_modbus_master.c b/freemodbus/common/esp_modbus_master.c similarity index 100% rename from components/freemodbus/common/esp_modbus_master.c rename to freemodbus/common/esp_modbus_master.c diff --git a/components/freemodbus/common/esp_modbus_master_serial.c b/freemodbus/common/esp_modbus_master_serial.c similarity index 100% rename from components/freemodbus/common/esp_modbus_master_serial.c rename to freemodbus/common/esp_modbus_master_serial.c diff --git a/components/freemodbus/common/esp_modbus_master_tcp.c b/freemodbus/common/esp_modbus_master_tcp.c similarity index 100% rename from components/freemodbus/common/esp_modbus_master_tcp.c rename to freemodbus/common/esp_modbus_master_tcp.c diff --git a/components/freemodbus/common/esp_modbus_slave.c b/freemodbus/common/esp_modbus_slave.c similarity index 100% rename from components/freemodbus/common/esp_modbus_slave.c rename to freemodbus/common/esp_modbus_slave.c diff --git a/components/freemodbus/common/esp_modbus_slave_serial.c b/freemodbus/common/esp_modbus_slave_serial.c similarity index 100% rename from components/freemodbus/common/esp_modbus_slave_serial.c rename to freemodbus/common/esp_modbus_slave_serial.c diff --git a/components/freemodbus/common/esp_modbus_slave_tcp.c b/freemodbus/common/esp_modbus_slave_tcp.c similarity index 100% rename from components/freemodbus/common/esp_modbus_slave_tcp.c rename to freemodbus/common/esp_modbus_slave_tcp.c diff --git a/components/freemodbus/common/include/esp_modbus_common.h b/freemodbus/common/include/esp_modbus_common.h similarity index 98% rename from components/freemodbus/common/include/esp_modbus_common.h rename to freemodbus/common/include/esp_modbus_common.h index b44ca1b..f443286 100644 --- a/components/freemodbus/common/include/esp_modbus_common.h +++ b/freemodbus/common/include/esp_modbus_common.h @@ -15,6 +15,7 @@ extern "C" { #if __has_include("esp_check.h") #include "esp_check.h" +#include "esp_log.h" #define MB_RETURN_ON_FALSE(a, err_code, tag, format, ...) ESP_RETURN_ON_FALSE(a, err_code, tag, format __VA_OPT__(,) __VA_ARGS__) @@ -24,7 +25,7 @@ extern "C" { #define MB_RETURN_ON_FALSE(a, err_code, tag, format, ...) do { \ if (!(a)) { \ - MB_LOGE(tag, "%s(%d): " format, __FUNCTION__, __LINE__ __VA_OPT__(,) __VA_ARGS__); \ + ESP_LOGE(tag, "%s(%d): " format, __FUNCTION__, __LINE__ __VA_OPT__(,) __VA_ARGS__); \ return err_code; \ } \ } while(0) diff --git a/components/freemodbus/common/include/esp_modbus_master.h b/freemodbus/common/include/esp_modbus_master.h similarity index 99% rename from components/freemodbus/common/include/esp_modbus_master.h rename to freemodbus/common/include/esp_modbus_master.h index 9b874a0..d11ade7 100644 --- a/components/freemodbus/common/include/esp_modbus_master.h +++ b/freemodbus/common/include/esp_modbus_master.h @@ -9,6 +9,7 @@ #include // for standard int types definition #include // for NULL and std defines +#include "soc/soc.h" // for BITN definitions #include "esp_modbus_common.h" // for common types #ifdef __cplusplus diff --git a/components/freemodbus/common/include/esp_modbus_slave.h b/freemodbus/common/include/esp_modbus_slave.h similarity index 98% rename from components/freemodbus/common/include/esp_modbus_slave.h rename to freemodbus/common/include/esp_modbus_slave.h index 962dbda..7d79b51 100644 --- a/components/freemodbus/common/include/esp_modbus_slave.h +++ b/freemodbus/common/include/esp_modbus_slave.h @@ -10,6 +10,7 @@ // Public interface header for slave #include // for standard int types definition #include // for NULL and std defines +#include "soc/soc.h" // for BITN definitions #include "freertos/FreeRTOS.h" // for task creation and queues access #include "freertos/event_groups.h" // for event groups #include "esp_modbus_common.h" // for common types diff --git a/components/freemodbus/common/include/mbcontroller.h b/freemodbus/common/include/mbcontroller.h similarity index 100% rename from components/freemodbus/common/include/mbcontroller.h rename to freemodbus/common/include/mbcontroller.h diff --git a/components/freemodbus/common/mbc_master.h b/freemodbus/common/mbc_master.h similarity index 100% rename from components/freemodbus/common/mbc_master.h rename to freemodbus/common/mbc_master.h diff --git a/components/freemodbus/common/mbc_slave.h b/freemodbus/common/mbc_slave.h similarity index 100% rename from components/freemodbus/common/mbc_slave.h rename to freemodbus/common/mbc_slave.h diff --git a/components/freemodbus/modbus/ascii/mbascii.c b/freemodbus/modbus/ascii/mbascii.c similarity index 94% rename from components/freemodbus/modbus/ascii/mbascii.c rename to freemodbus/modbus/ascii/mbascii.c index 462a641..4400c25 100644 --- a/components/freemodbus/modbus/ascii/mbascii.c +++ b/freemodbus/modbus/ascii/mbascii.c @@ -151,26 +151,33 @@ eMBErrorCode eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) { eMBErrorCode eStatus = MB_ENOERR; + UCHAR *pucMBASCIIFrame = ( UCHAR* ) ucASCIIBuf; + USHORT usFrameLength = usRcvBufferPos; + if( xMBSerialPortGetRequest( &pucMBASCIIFrame, &usFrameLength ) == FALSE ) + { + return MB_EIO; + } + ENTER_CRITICAL_SECTION( ); - assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); + assert( usFrameLength < MB_SER_PDU_SIZE_MAX ); /* Length and CRC check */ - if( ( usRcvBufferPos >= MB_ASCII_SER_PDU_SIZE_MIN ) - && ( prvucMBLRC( ( UCHAR * ) ucASCIIBuf, usRcvBufferPos ) == 0 ) ) + if( ( usFrameLength >= MB_ASCII_SER_PDU_SIZE_MIN ) + && ( prvucMBLRC( ( UCHAR * ) pucMBASCIIFrame, usFrameLength ) == 0 ) ) { /* Save the address field. All frames are passed to the upper layed * and the decision if a frame is used is done there. */ - *pucRcvAddress = ucASCIIBuf[MB_SER_PDU_ADDR_OFF]; + *pucRcvAddress = pucMBASCIIFrame[MB_SER_PDU_ADDR_OFF]; /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus * size of address field and CRC checksum. */ - *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC ); + *pusLength = ( USHORT )( usFrameLength - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC ); /* Return the start of the Modbus PDU to the caller. */ - *pucFrame = ( UCHAR * ) & ucASCIIBuf[MB_SER_PDU_PDU_OFF]; + *pucFrame = ( UCHAR * ) & pucMBASCIIFrame[MB_SER_PDU_PDU_OFF]; } else { @@ -186,13 +193,14 @@ eMBASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) eMBErrorCode eStatus = MB_ENOERR; UCHAR usLRC; - ENTER_CRITICAL_SECTION( ); + /* Check if the receiver is still in idle state. If not we where too * slow with processing the received frame and the master sent another * frame on the network. We have to abort sending the frame. */ if( eRcvState == STATE_RX_IDLE ) { + ENTER_CRITICAL_SECTION( ); /* First byte before the Modbus-PDU is the slave address. */ pucSndBufferCur = ( UCHAR * ) pucFrame - 1; usSndBufferCount = 1; @@ -207,6 +215,13 @@ eMBASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) /* Activate the transmitter. */ eSndState = STATE_TX_START; + EXIT_CRITICAL_SECTION( ); + + if ( xMBSerialPortSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE ) + { + eStatus = MB_EIO; + } + vMBPortSerialEnable( FALSE, TRUE ); } else diff --git a/components/freemodbus/modbus/ascii/mbascii.h b/freemodbus/modbus/ascii/mbascii.h similarity index 100% rename from components/freemodbus/modbus/ascii/mbascii.h rename to freemodbus/modbus/ascii/mbascii.h diff --git a/components/freemodbus/modbus/ascii/mbascii_m.c b/freemodbus/modbus/ascii/mbascii_m.c similarity index 93% rename from components/freemodbus/modbus/ascii/mbascii_m.c rename to freemodbus/modbus/ascii/mbascii_m.c index 8106598..849ab65 100644 --- a/components/freemodbus/modbus/ascii/mbascii_m.c +++ b/freemodbus/modbus/ascii/mbascii_m.c @@ -154,29 +154,34 @@ eMBErrorCode eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) { eMBErrorCode eStatus = MB_ENOERR; + UCHAR *pucMBASCIIFrame = ( UCHAR* ) ucMasterASCIIRcvBuf; + USHORT usFrameLength = usMasterRcvBufferPos; + if( xMBMasterSerialPortGetResponse( &pucMBASCIIFrame, &usFrameLength ) == FALSE ) + { + return MB_EIO; + } ENTER_CRITICAL_SECTION( ); - assert( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX ); + assert( usFrameLength < MB_SER_PDU_SIZE_MAX ); + assert( pucMBASCIIFrame ); /* Length and CRC check */ - if( ( usMasterRcvBufferPos >= MB_ASCII_SER_PDU_SIZE_MIN ) - && ( prvucMBLRC( ( UCHAR * ) ucMasterASCIIRcvBuf, usMasterRcvBufferPos ) == 0 ) ) + if( ( usFrameLength >= MB_ASCII_SER_PDU_SIZE_MIN ) + && ( prvucMBLRC( ( UCHAR * ) pucMBASCIIFrame, usFrameLength ) == 0 ) ) { /* Save the address field. All frames are passed to the upper layed - * and the decision if a frame is used is done there. - */ - *pucRcvAddress = ucMasterASCIIRcvBuf[MB_SER_PDU_ADDR_OFF]; + * and the decision if a frame is used is done there. + */ + *pucRcvAddress = pucMBASCIIFrame[MB_SER_PDU_ADDR_OFF]; - /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus - * size of address field and CRC checksum. - */ - *pusLength = ( USHORT )( usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC ); + /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus + * size of address field and CRC checksum. + */ + *pusLength = ( USHORT )( usFrameLength - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC ); - /* Return the start of the Modbus PDU to the caller. */ - *pucFrame = ( UCHAR * ) & ucMasterASCIIRcvBuf[MB_SER_PDU_PDU_OFF]; - } - else - { + /* Return the start of the Modbus PDU to the caller. */ + *pucFrame = ( UCHAR * ) & pucMBASCIIFrame[MB_SER_PDU_PDU_OFF]; + } else { eStatus = MB_EIO; } EXIT_CRITICAL_SECTION( ); @@ -191,13 +196,13 @@ eMBMasterASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLengt if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL; - ENTER_CRITICAL_SECTION( ); /* Check if the receiver is still in idle state. If not we where too * slow with processing the received frame and the master sent another * frame on the network. We have to abort sending the frame. */ if(eRcvState == STATE_M_RX_IDLE) { + ENTER_CRITICAL_SECTION( ); /* First byte before the Modbus-PDU is the slave address. */ pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1; usMasterSndBufferCount = 1; @@ -208,17 +213,22 @@ eMBMasterASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLengt /* Calculate LRC checksum for Modbus-Serial-Line-PDU. */ usLRC = prvucMBLRC( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ); - ucMasterASCIISndBuf[usMasterSndBufferCount++] = usLRC; + pucMasterSndBufferCur[usMasterSndBufferCount++] = usLRC; /* Activate the transmitter. */ eSndState = STATE_M_TX_START; + EXIT_CRITICAL_SECTION( ); + + if ( xMBMasterSerialPortSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE ) + { + eStatus = MB_EIO; + } vMBMasterPortSerialEnable( FALSE, TRUE ); } else { eStatus = MB_EIO; } - EXIT_CRITICAL_SECTION( ); return eStatus; } @@ -447,7 +457,7 @@ xMBMasterASCIITransmitFSM( void ) return xNeedPoll; } -BOOL +BOOL MB_PORT_ISR_ATTR xMBMasterASCIITimerT1SExpired( void ) { BOOL xNeedPoll = FALSE; @@ -457,7 +467,6 @@ xMBMasterASCIITimerT1SExpired( void ) /* Timer t35 expired. Startup phase is finished. */ case STATE_M_RX_INIT: xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY); - ESP_EARLY_LOGI("xMBMasterASCIITimerT1SExpired", "RX_INIT_EXPIRED"); break; /* Start of message is not received during respond timeout. diff --git a/components/freemodbus/modbus/functions/mbfunccoils.c b/freemodbus/modbus/functions/mbfunccoils.c similarity index 100% rename from components/freemodbus/modbus/functions/mbfunccoils.c rename to freemodbus/modbus/functions/mbfunccoils.c diff --git a/components/freemodbus/modbus/functions/mbfunccoils_m.c b/freemodbus/modbus/functions/mbfunccoils_m.c similarity index 100% rename from components/freemodbus/modbus/functions/mbfunccoils_m.c rename to freemodbus/modbus/functions/mbfunccoils_m.c diff --git a/components/freemodbus/modbus/functions/mbfuncdiag.c b/freemodbus/modbus/functions/mbfuncdiag.c similarity index 100% rename from components/freemodbus/modbus/functions/mbfuncdiag.c rename to freemodbus/modbus/functions/mbfuncdiag.c diff --git a/components/freemodbus/modbus/functions/mbfuncdisc.c b/freemodbus/modbus/functions/mbfuncdisc.c similarity index 100% rename from components/freemodbus/modbus/functions/mbfuncdisc.c rename to freemodbus/modbus/functions/mbfuncdisc.c diff --git a/components/freemodbus/modbus/functions/mbfuncdisc_m.c b/freemodbus/modbus/functions/mbfuncdisc_m.c similarity index 100% rename from components/freemodbus/modbus/functions/mbfuncdisc_m.c rename to freemodbus/modbus/functions/mbfuncdisc_m.c diff --git a/components/freemodbus/modbus/functions/mbfuncholding.c b/freemodbus/modbus/functions/mbfuncholding.c similarity index 100% rename from components/freemodbus/modbus/functions/mbfuncholding.c rename to freemodbus/modbus/functions/mbfuncholding.c diff --git a/components/freemodbus/modbus/functions/mbfuncholding_m.c b/freemodbus/modbus/functions/mbfuncholding_m.c similarity index 100% rename from components/freemodbus/modbus/functions/mbfuncholding_m.c rename to freemodbus/modbus/functions/mbfuncholding_m.c diff --git a/components/freemodbus/modbus/functions/mbfuncinput.c b/freemodbus/modbus/functions/mbfuncinput.c similarity index 100% rename from components/freemodbus/modbus/functions/mbfuncinput.c rename to freemodbus/modbus/functions/mbfuncinput.c diff --git a/components/freemodbus/modbus/functions/mbfuncinput_m.c b/freemodbus/modbus/functions/mbfuncinput_m.c similarity index 100% rename from components/freemodbus/modbus/functions/mbfuncinput_m.c rename to freemodbus/modbus/functions/mbfuncinput_m.c diff --git a/components/freemodbus/modbus/functions/mbfuncother.c b/freemodbus/modbus/functions/mbfuncother.c similarity index 100% rename from components/freemodbus/modbus/functions/mbfuncother.c rename to freemodbus/modbus/functions/mbfuncother.c diff --git a/components/freemodbus/modbus/functions/mbutils.c b/freemodbus/modbus/functions/mbutils.c similarity index 100% rename from components/freemodbus/modbus/functions/mbutils.c rename to freemodbus/modbus/functions/mbutils.c diff --git a/components/freemodbus/modbus/include/mb.h b/freemodbus/modbus/include/mb.h similarity index 100% rename from components/freemodbus/modbus/include/mb.h rename to freemodbus/modbus/include/mb.h diff --git a/components/freemodbus/modbus/include/mb_m.h b/freemodbus/modbus/include/mb_m.h similarity index 100% rename from components/freemodbus/modbus/include/mb_m.h rename to freemodbus/modbus/include/mb_m.h diff --git a/components/freemodbus/modbus/include/mbconfig.h b/freemodbus/modbus/include/mbconfig.h similarity index 96% rename from components/freemodbus/modbus/include/mbconfig.h rename to freemodbus/modbus/include/mbconfig.h index 897cc6c..268b748 100644 --- a/components/freemodbus/modbus/include/mbconfig.h +++ b/freemodbus/modbus/include/mbconfig.h @@ -41,6 +41,10 @@ #include "sdkconfig.h" // for KConfig options +#if __has_include("esp_idf_version.h") +#include "esp_idf_version.h" +#endif + #ifdef __cplusplus PR_BEGIN_EXTERN_C #endif @@ -73,6 +77,15 @@ PR_BEGIN_EXTERN_C #error "None of Modbus communication mode is enabled. Please enable one of (ASCII, RTU, TCP) mode in Kconfig." #endif +#ifdef ESP_IDF_VERSION + +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) +// Features supported from 4.4 +#define MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD 1 +#endif + +#endif + /*! \brief This option defines the number of data bits per ASCII character. * * A parity bit is added before the stop bit which keeps the actual byte size at 10 bits. diff --git a/components/freemodbus/modbus/include/mbframe.h b/freemodbus/modbus/include/mbframe.h similarity index 100% rename from components/freemodbus/modbus/include/mbframe.h rename to freemodbus/modbus/include/mbframe.h diff --git a/components/freemodbus/modbus/include/mbfunc.h b/freemodbus/modbus/include/mbfunc.h similarity index 100% rename from components/freemodbus/modbus/include/mbfunc.h rename to freemodbus/modbus/include/mbfunc.h diff --git a/components/freemodbus/modbus/include/mbport.h b/freemodbus/modbus/include/mbport.h similarity index 95% rename from components/freemodbus/modbus/include/mbport.h rename to freemodbus/modbus/include/mbport.h index 42773be..acaf908 100644 --- a/components/freemodbus/modbus/include/mbport.h +++ b/freemodbus/modbus/include/mbport.h @@ -145,6 +145,10 @@ BOOL xMBPortSerialGetByte( CHAR * pucByte ); BOOL xMBPortSerialPutByte( CHAR ucByte ); +BOOL xMBSerialPortGetRequest( UCHAR **ppucMBSerialFrame, USHORT * pusSerialLength ) __attribute__ ((weak)); + +BOOL xMBSerialPortSendResponse( UCHAR *pucMBSerialFrame, USHORT usSerialLength ) __attribute__ ((weak)); + #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED BOOL xMBMasterPortSerialInit( UCHAR ucPort, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ); @@ -158,6 +162,11 @@ void vMBMasterPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ); BOOL xMBMasterPortSerialGetByte( CHAR * pucByte ); BOOL xMBMasterPortSerialPutByte( CHAR ucByte ); + +BOOL xMBMasterSerialPortGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength ); + +BOOL xMBMasterSerialPortSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength ); + #endif /* ----------------------- Timers functions ---------------------------------*/ diff --git a/components/freemodbus/modbus/include/mbproto.h b/freemodbus/modbus/include/mbproto.h similarity index 100% rename from components/freemodbus/modbus/include/mbproto.h rename to freemodbus/modbus/include/mbproto.h diff --git a/components/freemodbus/modbus/include/mbutils.h b/freemodbus/modbus/include/mbutils.h similarity index 100% rename from components/freemodbus/modbus/include/mbutils.h rename to freemodbus/modbus/include/mbutils.h diff --git a/components/freemodbus/modbus/mb.c b/freemodbus/modbus/mb.c similarity index 100% rename from components/freemodbus/modbus/mb.c rename to freemodbus/modbus/mb.c diff --git a/components/freemodbus/modbus/mb_m.c b/freemodbus/modbus/mb_m.c similarity index 98% rename from components/freemodbus/modbus/mb_m.c rename to freemodbus/modbus/mb_m.c index aa5922a..1f13b9a 100644 --- a/components/freemodbus/modbus/mb_m.c +++ b/freemodbus/modbus/mb_m.c @@ -70,18 +70,18 @@ /* ----------------------- Static variables ---------------------------------*/ -static UCHAR ucMBMasterDestAddress; -static BOOL xMBRunInMasterMode = FALSE; +static UCHAR ucMBMasterDestAddress; +static BOOL xMBRunInMasterMode = FALSE; static volatile eMBMasterErrorEventType eMBMasterCurErrorType; -static volatile USHORT usMasterSendPDULength; +static volatile USHORT usMasterSendPDULength; static volatile eMBMode eMBMasterCurrentMode; /*------------------------ Shared variables ---------------------------------*/ -volatile UCHAR ucMasterSndBuf[MB_SERIAL_BUF_SIZE]; -volatile UCHAR ucMasterRcvBuf[MB_SERIAL_BUF_SIZE]; +volatile UCHAR ucMasterSndBuf[MB_SERIAL_BUF_SIZE]; +volatile UCHAR ucMasterRcvBuf[MB_SERIAL_BUF_SIZE]; volatile eMBMasterTimerMode eMasterCurTimerMode; -volatile BOOL xFrameIsBroadcast = FALSE; +volatile BOOL xFrameIsBroadcast = FALSE; static enum { @@ -152,7 +152,7 @@ static xMBFunctionHandler xMasterFuncHandlers[MB_FUNC_HANDLERS_MAX] = { }; /* ----------------------- Start implementation -----------------------------*/ -#if MB_MASTER_TCP_ENABLED > 0 +#if MB_MASTER_TCP_ENABLED eMBErrorCode eMBMasterTCPInit( USHORT ucTCPPort ) { diff --git a/components/freemodbus/modbus/rtu/mbcrc.c b/freemodbus/modbus/rtu/mbcrc.c similarity index 98% rename from components/freemodbus/modbus/rtu/mbcrc.c rename to freemodbus/modbus/rtu/mbcrc.c index c46e7d1..f413453 100644 --- a/components/freemodbus/modbus/rtu/mbcrc.c +++ b/freemodbus/modbus/rtu/mbcrc.c @@ -39,7 +39,7 @@ #include "port.h" #include "mbconfig.h" -#if MB_MASTER_RTU_ENABLED || MB_SLAVE_RTU_ENABLED +#if (MB_MASTER_RTU_ENABLED || MB_SLAVE_RTU_ENABLED || CONFIG_MB_UTEST) static const UCHAR aucCRCHi[] = { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, diff --git a/components/freemodbus/modbus/rtu/mbcrc.h b/freemodbus/modbus/rtu/mbcrc.h similarity index 100% rename from components/freemodbus/modbus/rtu/mbcrc.h rename to freemodbus/modbus/rtu/mbcrc.h diff --git a/components/freemodbus/modbus/rtu/mbrtu.c b/freemodbus/modbus/rtu/mbrtu.c similarity index 92% rename from components/freemodbus/modbus/rtu/mbrtu.c rename to freemodbus/modbus/rtu/mbrtu.c index 180bde2..144e208 100644 --- a/components/freemodbus/modbus/rtu/mbrtu.c +++ b/freemodbus/modbus/rtu/mbrtu.c @@ -155,26 +155,33 @@ eMBErrorCode eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) { eMBErrorCode eStatus = MB_ENOERR; + UCHAR *pucMBRTUFrame = ( UCHAR* ) ucRTUBuf; + USHORT usFrameLength = usRcvBufferPos; + + if( xMBSerialPortGetRequest( &pucMBRTUFrame, &usFrameLength ) == FALSE ) + { + return MB_EIO; + } ENTER_CRITICAL_SECTION( ); - assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); + assert( usFrameLength < MB_SER_PDU_SIZE_MAX ); /* Length and CRC check */ - if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN ) - && ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) ) + if( ( usFrameLength >= MB_SER_PDU_SIZE_MIN ) + && ( usMBCRC16( ( UCHAR * ) pucMBRTUFrame, usFrameLength ) == 0 ) ) { /* Save the address field. All frames are passed to the upper layed * and the decision if a frame is used is done there. */ - *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF]; + *pucRcvAddress = pucMBRTUFrame[MB_SER_PDU_ADDR_OFF]; /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus * size of address field and CRC checksum. */ - *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); + *pusLength = ( USHORT )( usFrameLength - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); /* Return the start of the Modbus PDU to the caller. */ - *pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF]; + *pucFrame = ( UCHAR * ) & pucMBRTUFrame[MB_SER_PDU_PDU_OFF]; } else { @@ -191,14 +198,13 @@ eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) eMBErrorCode eStatus = MB_ENOERR; USHORT usCRC16; - ENTER_CRITICAL_SECTION( ); - /* Check if the receiver is still in idle state. If not we where to * slow with processing the received frame and the master sent another * frame on the network. We have to abort sending the frame. */ if( eRcvState == STATE_RX_IDLE ) { + ENTER_CRITICAL_SECTION( ); /* First byte before the Modbus-PDU is the slave address. */ pucSndBufferCur = ( UCHAR * ) pucFrame - 1; usSndBufferCount = 1; @@ -214,13 +220,19 @@ eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) /* Activate the transmitter. */ eSndState = STATE_TX_XMIT; + EXIT_CRITICAL_SECTION( ); + + if( xMBSerialPortSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE ) + { + eStatus = MB_EIO; + } + vMBPortSerialEnable( FALSE, TRUE ); } else { eStatus = MB_EIO; } - EXIT_CRITICAL_SECTION( ); return eStatus; } @@ -272,7 +284,7 @@ xMBRTUReceiveFSM( void ) case STATE_RX_RCV: if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ) { - if ( xStatus ) { + if( xStatus ) { ucRTUBuf[usRcvBufferPos++] = ucByte; } } diff --git a/components/freemodbus/modbus/rtu/mbrtu.h b/freemodbus/modbus/rtu/mbrtu.h similarity index 100% rename from components/freemodbus/modbus/rtu/mbrtu.h rename to freemodbus/modbus/rtu/mbrtu.h diff --git a/components/freemodbus/modbus/rtu/mbrtu_m.c b/freemodbus/modbus/rtu/mbrtu_m.c similarity index 90% rename from components/freemodbus/modbus/rtu/mbrtu_m.c rename to freemodbus/modbus/rtu/mbrtu_m.c index 47a5513..65ac4b7 100644 --- a/components/freemodbus/modbus/rtu/mbrtu_m.c +++ b/freemodbus/modbus/rtu/mbrtu_m.c @@ -161,26 +161,34 @@ eMBErrorCode eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) { eMBErrorCode eStatus = MB_ENOERR; + UCHAR *pucMBRTUFrame = ( UCHAR* ) ucMasterRTURcvBuf; + USHORT usFrameLength = usMasterRcvBufferPos; + + if( xMBMasterSerialPortGetResponse( &pucMBRTUFrame, &usFrameLength ) == FALSE ) + { + return MB_EIO; + } ENTER_CRITICAL_SECTION( ); - assert( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX ); + assert( usFrameLength < MB_SER_PDU_SIZE_MAX ); + assert( pucMBRTUFrame ); /* Length and CRC check */ - if( ( usMasterRcvBufferPos >= MB_RTU_SER_PDU_SIZE_MIN ) - && ( usMBCRC16( ( UCHAR * ) ucMasterRTURcvBuf, usMasterRcvBufferPos ) == 0 ) ) + if( ( usFrameLength >= MB_RTU_SER_PDU_SIZE_MIN ) + && ( usMBCRC16( ( UCHAR * ) pucMBRTUFrame, usFrameLength ) == 0 ) ) { - /* Save the address field. All frames are passed to the upper layed + /* Save the address field. All frames are passed to the upper layer * and the decision if a frame is used is done there. */ - *pucRcvAddress = ucMasterRTURcvBuf[MB_SER_PDU_ADDR_OFF]; + *pucRcvAddress = pucMBRTUFrame[MB_SER_PDU_ADDR_OFF]; /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus * size of address field and CRC checksum. */ - *pusLength = ( USHORT )( usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); + *pusLength = ( USHORT )( usFrameLength - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); /* Return the start of the Modbus PDU to the caller. */ - *pucFrame = ( UCHAR * ) & ucMasterRTURcvBuf[MB_SER_PDU_PDU_OFF]; + *pucFrame = ( UCHAR * ) & pucMBRTUFrame[MB_SER_PDU_PDU_OFF]; } else { @@ -199,14 +207,13 @@ eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL; - ENTER_CRITICAL_SECTION( ); - /* Check if the receiver is still in idle state. If not we where to * slow with processing the received frame and the master sent another * frame on the network. We have to abort sending the frame. */ if( eRcvState == STATE_M_RX_IDLE ) { + ENTER_CRITICAL_SECTION( ); /* First byte before the Modbus-PDU is the slave address. */ pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1; usMasterSndBufferCount = 1; @@ -217,11 +224,18 @@ eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */ usCRC16 = usMBCRC16( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ); - ucMasterRTUSndBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF ); - ucMasterRTUSndBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 ); + pucMasterSndBufferCur[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF ); + pucMasterSndBufferCur[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 ); + EXIT_CRITICAL_SECTION( ); /* Activate the transmitter. */ eSndState = STATE_M_TX_XMIT; + + if ( xMBMasterSerialPortSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE ) + { + eStatus = MB_EIO; + } + // The place to enable RS485 driver vMBMasterPortSerialEnable( FALSE, TRUE ); } @@ -229,7 +243,6 @@ eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength { eStatus = MB_EIO; } - EXIT_CRITICAL_SECTION( ); return eStatus; } @@ -273,11 +286,15 @@ xMBMasterRTUReceiveFSM( void ) eSndState = STATE_M_TX_IDLE; usMasterRcvBufferPos = 0; - ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte; - eRcvState = STATE_M_RX_RCV; + if( xStatus && ucByte ) { + ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte; + eRcvState = STATE_M_RX_RCV; + } /* Enable t3.5 timers. */ +#if CONFIG_FMB_TIMER_PORT_ENABLED vMBMasterPortTimersT35Enable( ); +#endif break; /* We are currently receiving a frame. Reset the timer after @@ -296,7 +313,9 @@ xMBMasterRTUReceiveFSM( void ) { eRcvState = STATE_M_RX_ERROR; } +#if CONFIG_FMB_TIMER_PORT_ENABLED vMBMasterPortTimersT35Enable( ); +#endif break; } return xStatus; diff --git a/components/freemodbus/modbus/tcp/mbtcp.c b/freemodbus/modbus/tcp/mbtcp.c similarity index 100% rename from components/freemodbus/modbus/tcp/mbtcp.c rename to freemodbus/modbus/tcp/mbtcp.c diff --git a/components/freemodbus/modbus/tcp/mbtcp.h b/freemodbus/modbus/tcp/mbtcp.h similarity index 100% rename from components/freemodbus/modbus/tcp/mbtcp.h rename to freemodbus/modbus/tcp/mbtcp.h diff --git a/components/freemodbus/modbus/tcp/mbtcp_m.c b/freemodbus/modbus/tcp/mbtcp_m.c similarity index 100% rename from components/freemodbus/modbus/tcp/mbtcp_m.c rename to freemodbus/modbus/tcp/mbtcp_m.c diff --git a/components/freemodbus/modbus/tcp/mbtcp_m.h b/freemodbus/modbus/tcp/mbtcp_m.h similarity index 100% rename from components/freemodbus/modbus/tcp/mbtcp_m.h rename to freemodbus/modbus/tcp/mbtcp_m.h diff --git a/components/freemodbus/port/port.c b/freemodbus/port/port.c similarity index 93% rename from components/freemodbus/port/port.c rename to freemodbus/port/port.c index 1959104..4f672a9 100644 --- a/components/freemodbus/port/port.c +++ b/freemodbus/port/port.c @@ -38,6 +38,7 @@ /* ----------------------- Modbus includes ----------------------------------*/ #include "freertos/FreeRTOS.h" +#include "freertos/queue.h" #include "sys/lock.h" #include "port.h" @@ -72,6 +73,13 @@ vMBPortSetMode( UCHAR ucMode ) EXIT_CRITICAL_SECTION(); } +BOOL xMBPortSerialWaitEvent(QueueHandle_t xMbUartQueue, uart_event_t* pxEvent, ULONG xTimeout) +{ + BOOL xResult = (BaseType_t)xQueueReceive(xMbUartQueue, (void*)pxEvent, (TickType_t) xTimeout); + ESP_LOGD(__func__, "UART event: %d ", pxEvent->type); + return xResult; +} + #if MB_TCP_DEBUG // This function is kept to realize legacy freemodbus frame logging functionality diff --git a/components/freemodbus/port/port.h b/freemodbus/port/port.h similarity index 95% rename from components/freemodbus/port/port.h rename to freemodbus/port/port.h index 42ae780..b5d8858 100644 --- a/components/freemodbus/port/port.h +++ b/freemodbus/port/port.h @@ -38,8 +38,18 @@ #define PORT_COMMON_H_ #include "freertos/FreeRTOS.h" +#include "freertos/queue.h" // for queue + #include "esp_log.h" // for ESP_LOGE macro #include "esp_timer.h" +#include "driver/uart.h" // for uart_event_t + +#if __has_include("driver/gptimer.h") +#include "driver/gptimer.h" +#else +#include "driver/timer.h" +#endif + #include "mbconfig.h" #define INLINE inline @@ -174,6 +184,8 @@ void prvvMBTCPLogFrame( const CHAR * pucMsg, UCHAR * pucFrame, USHORT usFrameLen void vMBPortSetMode( UCHAR ucMode ); UCHAR ucMBPortGetMode( void ); +BOOL xMBPortSerialWaitEvent(QueueHandle_t xMbUartQueue, uart_event_t* pxEvent, ULONG xTimeout); + #ifdef __cplusplus PR_END_EXTERN_C #endif /* __cplusplus */ diff --git a/components/freemodbus/port/portevent.c b/freemodbus/port/portevent.c similarity index 100% rename from components/freemodbus/port/portevent.c rename to freemodbus/port/portevent.c diff --git a/components/freemodbus/port/portevent_m.c b/freemodbus/port/portevent_m.c similarity index 100% rename from components/freemodbus/port/portevent_m.c rename to freemodbus/port/portevent_m.c diff --git a/components/freemodbus/port/portother.c b/freemodbus/port/portother.c similarity index 100% rename from components/freemodbus/port/portother.c rename to freemodbus/port/portother.c diff --git a/components/freemodbus/port/portother_m.c b/freemodbus/port/portother_m.c similarity index 100% rename from components/freemodbus/port/portother_m.c rename to freemodbus/port/portother_m.c diff --git a/components/freemodbus/port/portserial.c b/freemodbus/port/portserial.c similarity index 94% rename from components/freemodbus/port/portserial.c rename to freemodbus/port/portserial.c index a57a6c6..aae45d9 100644 --- a/components/freemodbus/port/portserial.c +++ b/freemodbus/port/portserial.c @@ -33,6 +33,8 @@ * * File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $ */ + +#include "driver/uart.h" #include "port.h" #include "driver/uart.h" #include "freertos/queue.h" // for queue support @@ -85,15 +87,14 @@ static USHORT usMBPortSerialRxPoll(size_t xEventSize) if (bRxStateEnabled) { // Get received packet into Rx buffer - while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) { + while(xReadStatus && (usCnt++ <= xEventSize)) { // Call the Modbus stack callback function and let it fill the buffers. xReadStatus = pxMBFrameCBByteReceived(); // callback to execute receive FSM } uart_flush_input(ucUartNumber); // Send event EV_FRAME_RECEIVED to allow stack process packet #if !CONFIG_FMB_TIMER_PORT_ENABLED - // Let the stack know that T3.5 time is expired and data is received - (void)pxMBPortCBTimerExpired(); // calls callback xMBRTUTimerT35Expired(); + pxMBPortCBTimerExpired(); #endif ESP_LOGD(TAG, "RX: %d bytes\n", usCnt); } @@ -126,7 +127,7 @@ static void vUartTask(void *pvParameters) uart_event_t xEvent; USHORT usResult = 0; for(;;) { - if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) { + if (xMBPortSerialWaitEvent(xMbUartQueue, (void*)&xEvent, portMAX_DELAY)) { ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber); switch(xEvent.type) { //Event of UART receving data @@ -135,6 +136,8 @@ static void vUartTask(void *pvParameters) // This flag set in the event means that no more // data received during configured timeout and UART TOUT feature is triggered if (xEvent.timeout_flag) { + // Get buffered data length + ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size)); // Read received data and send it to modbus stack usResult = usMBPortSerialRxPoll(xEvent.size); ESP_LOGD(TAG,"Timeout occured, processed: %d bytes", usResult); @@ -219,7 +222,7 @@ BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .rx_flow_ctrl_thresh = 2, - .source_clk = UART_SCLK_APB, + .source_clk = UART_SCLK_APB }; // Set UART config xErr = uart_param_config(ucUartNumber, &xUartConfig); @@ -257,6 +260,17 @@ BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, return TRUE; } +BOOL xMBSerialPortGetRequest( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength ) +{ + BOOL eStatus = TRUE; + return eStatus; +} + +BOOL xMBSerialPortSendResponse( UCHAR *pucMBSerialFrame, USHORT usSerialLength ) +{ + return TRUE; +} + void vMBPortSerialClose(void) { (void)vTaskSuspend(xMbTaskHandle); diff --git a/components/freemodbus/port/portserial_m.c b/freemodbus/port/portserial_m.c similarity index 93% rename from components/freemodbus/port/portserial_m.c rename to freemodbus/port/portserial_m.c index b0324e3..777eee8 100644 --- a/components/freemodbus/port/portserial_m.c +++ b/freemodbus/port/portserial_m.c @@ -89,7 +89,7 @@ static USHORT usMBMasterPortSerialRxPoll(size_t xEventSize) USHORT usCnt = 0; if (bRxStateEnabled) { - while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) { + while(xReadStatus && (usCnt++ <= xEventSize)) { // Call the Modbus stack callback function and let it fill the stack buffers. xReadStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM } @@ -134,7 +134,7 @@ static void vUartTask(void* pvParameters) uart_event_t xEvent; USHORT usResult = 0; for(;;) { - if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) { + if (xMBPortSerialWaitEvent(xMbUartQueue, (void*)&xEvent, portMAX_DELAY)) { ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber); switch(xEvent.type) { //Event of UART receiving data @@ -143,6 +143,8 @@ static void vUartTask(void* pvParameters) // This flag set in the event means that no more // data received during configured timeout and UART TOUT feature is triggered if (xEvent.timeout_flag) { + // Get buffered data length + ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size)); // Read received data and send it to modbus stack usResult = usMBMasterPortSerialRxPoll(xEvent.size); ESP_LOGD(TAG,"Timeout occured, processed: %d bytes", usResult); @@ -266,6 +268,24 @@ BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, return TRUE; } +/* + * The function is called from ASCII/RTU module to get processed data buffer. Sets the + * received buffer and its length using parameters. + */ +BOOL xMBMasterSerialPortGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength ) +{ + return TRUE; +} + +/* + * The function is called from ASCII/RTU module to set processed data buffer + * to be sent in transmitter state machine. + */ +BOOL xMBMasterSerialPortSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength ) +{ + return TRUE; +} + void vMBMasterPortSerialClose(void) { (void)vTaskDelete(xMbTaskHandle); diff --git a/components/freemodbus/port/porttimer.c b/freemodbus/port/porttimer.c similarity index 98% rename from components/freemodbus/port/porttimer.c rename to freemodbus/port/porttimer.c index 9e2673f..4fbfbfd 100644 --- a/components/freemodbus/port/porttimer.c +++ b/freemodbus/port/porttimer.c @@ -74,7 +74,7 @@ BOOL xMBPortTimersInit(USHORT usTimeOut50us) esp_timer_create_args_t xTimerConf = { .callback = vTimerAlarmCBHandler, .arg = NULL, -#if CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD +#if (MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD && CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD) .dispatch_method = ESP_TIMER_ISR, #else .dispatch_method = ESP_TIMER_TASK, diff --git a/components/freemodbus/port/porttimer_m.c b/freemodbus/port/porttimer_m.c similarity index 98% rename from components/freemodbus/port/porttimer_m.c rename to freemodbus/port/porttimer_m.c index 3982f27..f1595a6 100644 --- a/components/freemodbus/port/porttimer_m.c +++ b/freemodbus/port/porttimer_m.c @@ -71,7 +71,7 @@ BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us) esp_timer_create_args_t xTimerConf = { .callback = vTimerAlarmCBHandler, .arg = NULL, -#if CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD +#if (MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD && CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD) .dispatch_method = ESP_TIMER_ISR, #else .dispatch_method = ESP_TIMER_TASK, diff --git a/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c b/freemodbus/serial_master/modbus_controller/mbc_serial_master.c similarity index 99% rename from components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c rename to freemodbus/serial_master/modbus_controller/mbc_serial_master.c index 3a3f2da..94bec3f 100644 --- a/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c +++ b/freemodbus/serial_master/modbus_controller/mbc_serial_master.c @@ -30,7 +30,7 @@ extern BOOL xMBMasterPortSerialTxPoll(void); #define MB_RESPONSE_TICS pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND + 10) -static mb_master_interface_t* mbm_interface_ptr = NULL; //&default_interface_inst; +static mb_master_interface_t* mbm_interface_ptr = NULL; static const char *TAG = "MB_CONTROLLER_MASTER"; // Modbus event processing task @@ -101,7 +101,7 @@ static esp_err_t mbc_serial_master_start(void) "mb stack initialization failure, eMBInit() returns (0x%x).", status); status = eMBMasterEnable(); MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE, - "mb stack set slave ID failure, eMBEnable() returned (0x%x).", (uint32_t)status); + "mb stack set slave ID failure, eMBMasterEnable() returned (0x%x).", (uint32_t)status); // Set the mbcontroller start flag EventBits_t flag = xEventGroupSetBits(mbm_opts->mbm_event_group, (EventBits_t)MB_EVENT_STACK_STARTED); diff --git a/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.h b/freemodbus/serial_master/modbus_controller/mbc_serial_master.h similarity index 100% rename from components/freemodbus/serial_master/modbus_controller/mbc_serial_master.h rename to freemodbus/serial_master/modbus_controller/mbc_serial_master.h diff --git a/components/freemodbus/serial_master/port/port_serial_master.h b/freemodbus/serial_master/port/port_serial_master.h similarity index 100% rename from components/freemodbus/serial_master/port/port_serial_master.h rename to freemodbus/serial_master/port/port_serial_master.h diff --git a/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c b/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c similarity index 100% rename from components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c rename to freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c diff --git a/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.h b/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.h similarity index 100% rename from components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.h rename to freemodbus/serial_slave/modbus_controller/mbc_serial_slave.h diff --git a/components/freemodbus/serial_slave/port/port_serial_slave.h b/freemodbus/serial_slave/port/port_serial_slave.h similarity index 100% rename from components/freemodbus/serial_slave/port/port_serial_slave.h rename to freemodbus/serial_slave/port/port_serial_slave.h diff --git a/components/freemodbus/tcp_master/modbus_controller/mbc_tcp_master.c b/freemodbus/tcp_master/modbus_controller/mbc_tcp_master.c similarity index 98% rename from components/freemodbus/tcp_master/modbus_controller/mbc_tcp_master.c rename to freemodbus/tcp_master/modbus_controller/mbc_tcp_master.c index bdcfe14..f1f3916 100644 --- a/components/freemodbus/tcp_master/modbus_controller/mbc_tcp_master.c +++ b/freemodbus/tcp_master/modbus_controller/mbc_tcp_master.c @@ -159,7 +159,7 @@ static esp_err_t mbc_tcp_master_start(void) status = eMBMasterEnable(); MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE, - "mb stack set slave ID failure, eMBMasterEnable() returned (0x%x).", (uint32_t)status); + "mb stack enable failure, eMBMasterEnable() returned (0x%x).", (uint32_t)status); // Add slave IP address for each slave to initialize connection mb_slave_addr_entry_t *p_slave_info; @@ -172,13 +172,12 @@ static esp_err_t mbc_tcp_master_start(void) // Add end of list condition (void)xMBTCPPortMasterAddSlaveIp(0xFF, NULL, 0xFF); - // Wait for connection done event bool start = (bool)xMBTCPPortMasterWaitEvent(mbm_opts->mbm_event_group, - (EventBits_t)MB_EVENT_STACK_STARTED, MB_TCP_CONNECTION_TOUT); + (EventBits_t)MB_EVENT_STACK_STARTED, MB_TCP_CONNECTION_TOUT); MB_MASTER_CHECK((start), ESP_ERR_INVALID_STATE, - "mb stack could not connect to slaves for %d seconds.", - CONFIG_FMB_TCP_CONNECTION_TOUT_SEC); + "mb stack could not connect to slaves for %d seconds.", + CONFIG_FMB_TCP_CONNECTION_TOUT_SEC); return ESP_OK; } diff --git a/components/freemodbus/tcp_master/modbus_controller/mbc_tcp_master.h b/freemodbus/tcp_master/modbus_controller/mbc_tcp_master.h similarity index 100% rename from components/freemodbus/tcp_master/modbus_controller/mbc_tcp_master.h rename to freemodbus/tcp_master/modbus_controller/mbc_tcp_master.h diff --git a/components/freemodbus/tcp_master/port/port_tcp_master.c b/freemodbus/tcp_master/port/port_tcp_master.c similarity index 100% rename from components/freemodbus/tcp_master/port/port_tcp_master.c rename to freemodbus/tcp_master/port/port_tcp_master.c diff --git a/components/freemodbus/tcp_master/port/port_tcp_master.h b/freemodbus/tcp_master/port/port_tcp_master.h similarity index 100% rename from components/freemodbus/tcp_master/port/port_tcp_master.h rename to freemodbus/tcp_master/port/port_tcp_master.h diff --git a/components/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.c b/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.c similarity index 100% rename from components/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.c rename to freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.c diff --git a/components/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.h b/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.h similarity index 100% rename from components/freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.h rename to freemodbus/tcp_slave/modbus_controller/mbc_tcp_slave.h diff --git a/components/freemodbus/tcp_slave/port/port_tcp_slave.c b/freemodbus/tcp_slave/port/port_tcp_slave.c similarity index 99% rename from components/freemodbus/tcp_slave/port/port_tcp_slave.c rename to freemodbus/tcp_slave/port/port_tcp_slave.c index e7046d6..c5eee09 100644 --- a/components/freemodbus/tcp_slave/port/port_tcp_slave.c +++ b/freemodbus/tcp_slave/port/port_tcp_slave.c @@ -560,6 +560,12 @@ static void vMBTCPPortServerTask(void *pvParameters) pxClientInfo->xSockId, pxClientInfo->pcIpAddr, xErr); break; } + + if (xShutdownSemaphore) { + xSemaphoreGive(xShutdownSemaphore); + vTaskDelete(NULL); + } + // Close client connection xMBTCPPortCloseConnection(pxClientInfo); diff --git a/components/freemodbus/tcp_slave/port/port_tcp_slave.h b/freemodbus/tcp_slave/port/port_tcp_slave.h similarity index 100% rename from components/freemodbus/tcp_slave/port/port_tcp_slave.h rename to freemodbus/tcp_slave/port/port_tcp_slave.h diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000..57cb047 --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,11 @@ +version: "1.0.0" +description: ESP-MODBUS is the official Modbus library for Espressif SoCs. +url: https://github.com/espressif/esp-modbus +dependencies: + idf: ">=4.1" +files: + exclude: + - "docs/_build/**/*" + - "docs/_build" + - "test/**/build/**/*" + - "test/**/build" diff --git a/components/freemodbus/sdkconfig.rename b/sdkconfig.rename similarity index 100% rename from components/freemodbus/sdkconfig.rename rename to sdkconfig.rename diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..e8931d4 --- /dev/null +++ b/test/README.md @@ -0,0 +1,9 @@ +# Test implementation + +This directory contains a set of ESP-IDF projects to be used as tests only, which aim to exercise various +configuration of components to check completely arbitrary functionality should it be building only, executing under +various conditions or combination with other components, including custom test frameworks. + +The tests in this folder are not intended to demonstrate the ESP-IDF functionality in any way. + +The examples can be found here: https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus diff --git a/examples/protocols/modbus/mb_example_common/CMakeLists.txt b/test/mb_example_common/CMakeLists.txt similarity index 70% rename from examples/protocols/modbus/mb_example_common/CMakeLists.txt rename to test/mb_example_common/CMakeLists.txt index 9b12675..c4854e9 100644 --- a/examples/protocols/modbus/mb_example_common/CMakeLists.txt +++ b/test/mb_example_common/CMakeLists.txt @@ -2,5 +2,4 @@ # 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" - PRIV_REQUIRES freemodbus) + INCLUDE_DIRS "include") diff --git a/examples/protocols/modbus/mb_example_common/README.md b/test/mb_example_common/README.md similarity index 100% rename from examples/protocols/modbus/mb_example_common/README.md rename to test/mb_example_common/README.md diff --git a/test/mb_example_common/component.mk b/test/mb_example_common/component.mk new file mode 100644 index 0000000..f0dc18c --- /dev/null +++ b/test/mb_example_common/component.mk @@ -0,0 +1,5 @@ +# +# Component Makefile +# +COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_SRCDIRS := . diff --git a/examples/protocols/modbus/mb_example_common/include/modbus_params.h b/test/mb_example_common/include/modbus_params.h similarity index 100% rename from examples/protocols/modbus/mb_example_common/include/modbus_params.h rename to test/mb_example_common/include/modbus_params.h diff --git a/examples/protocols/modbus/mb_example_common/modbus_params.c b/test/mb_example_common/modbus_params.c similarity index 100% rename from examples/protocols/modbus/mb_example_common/modbus_params.c rename to test/mb_example_common/modbus_params.c diff --git a/test/serial/mb_serial_master/CMakeLists.txt b/test/serial/mb_serial_master/CMakeLists.txt new file mode 100644 index 0000000..c4188f9 --- /dev/null +++ b/test/serial/mb_serial_master/CMakeLists.txt @@ -0,0 +1,13 @@ +# 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(EXTRA_COMPONENT_DIRS "../../../") +set(EXCLUDE_COMPONENTS examples test_app test freemodbus) + +# 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_master) diff --git a/test/serial/mb_serial_master/Makefile b/test/serial/mb_serial_master/Makefile new file mode 100644 index 0000000..1159aea --- /dev/null +++ b/test/serial/mb_serial_master/Makefile @@ -0,0 +1,12 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := modbus_master + +EXTRA_COMPONENT_DIRS := ../../../ +EXTRA_COMPONENT_DIRS += ../../mb_example_common +EXCLUDE_COMPONENTS := examples test freemodbus + +include $(IDF_PATH)/make/project.mk diff --git a/examples/protocols/modbus/serial/mb_master/README.md b/test/serial/mb_serial_master/README.md similarity index 100% rename from examples/protocols/modbus/serial/mb_master/README.md rename to test/serial/mb_serial_master/README.md diff --git a/examples/protocols/modbus/serial/mb_master/main/CMakeLists.txt b/test/serial/mb_serial_master/main/CMakeLists.txt similarity index 100% rename from examples/protocols/modbus/serial/mb_master/main/CMakeLists.txt rename to test/serial/mb_serial_master/main/CMakeLists.txt diff --git a/examples/protocols/modbus/serial/mb_master/main/Kconfig.projbuild b/test/serial/mb_serial_master/main/Kconfig.projbuild similarity index 100% rename from examples/protocols/modbus/serial/mb_master/main/Kconfig.projbuild rename to test/serial/mb_serial_master/main/Kconfig.projbuild diff --git a/test/serial/mb_serial_master/main/component.mk b/test/serial/mb_serial_master/main/component.mk new file mode 100644 index 0000000..a98f634 --- /dev/null +++ b/test/serial/mb_serial_master/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/examples/protocols/modbus/serial/mb_master/main/master.c b/test/serial/mb_serial_master/main/master.c similarity index 100% rename from examples/protocols/modbus/serial/mb_master/main/master.c rename to test/serial/mb_serial_master/main/master.c diff --git a/examples/protocols/modbus/serial/mb_master/sdkconfig.defaults b/test/serial/mb_serial_master/sdkconfig.defaults similarity index 61% rename from examples/protocols/modbus/serial/mb_master/sdkconfig.defaults rename to test/serial/mb_serial_master/sdkconfig.defaults index 4773f5b..3c9c6de 100644 --- a/examples/protocols/modbus/serial/mb_master/sdkconfig.defaults +++ b/test/serial/mb_serial_master/sdkconfig.defaults @@ -3,10 +3,8 @@ # CONFIG_MB_COMM_MODE_ASCII=y CONFIG_MB_UART_BAUD_RATE=115200 -CONFIG_FMB_TIMER_PORT_ENABLED=y -CONFIG_FMB_TIMER_GROUP=0 -CONFIG_FMB_TIMER_INDEX=0 +CONFIG_FMB_TIMER_PORT_ENABLED=n CONFIG_FMB_TIMER_ISR_IN_IRAM=y CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 -CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150 +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=400 CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y diff --git a/test/serial/mb_serial_slave/CMakeLists.txt b/test/serial/mb_serial_slave/CMakeLists.txt new file mode 100644 index 0000000..f4117d0 --- /dev/null +++ b/test/serial/mb_serial_slave/CMakeLists.txt @@ -0,0 +1,14 @@ +# 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(EXTRA_COMPONENT_DIRS "../../../") +set(EXCLUDE_COMPONENTS examples test_app test freemodbus) + +# 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_slave) diff --git a/test/serial/mb_serial_slave/Makefile b/test/serial/mb_serial_slave/Makefile new file mode 100644 index 0000000..8abdf51 --- /dev/null +++ b/test/serial/mb_serial_slave/Makefile @@ -0,0 +1,12 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := modbus_slave + +EXTRA_COMPONENT_DIRS := ../../../ +EXTRA_COMPONENT_DIRS += ../../mb_example_common +EXCLUDE_COMPONENTS := examples test freemodbus + +include $(IDF_PATH)/make/project.mk diff --git a/examples/protocols/modbus/serial/mb_slave/README.md b/test/serial/mb_serial_slave/README.md similarity index 100% rename from examples/protocols/modbus/serial/mb_slave/README.md rename to test/serial/mb_serial_slave/README.md diff --git a/examples/protocols/modbus/serial/mb_slave/main/CMakeLists.txt b/test/serial/mb_serial_slave/main/CMakeLists.txt similarity index 100% rename from examples/protocols/modbus/serial/mb_slave/main/CMakeLists.txt rename to test/serial/mb_serial_slave/main/CMakeLists.txt diff --git a/examples/protocols/modbus/serial/mb_slave/main/Kconfig.projbuild b/test/serial/mb_serial_slave/main/Kconfig.projbuild similarity index 100% rename from examples/protocols/modbus/serial/mb_slave/main/Kconfig.projbuild rename to test/serial/mb_serial_slave/main/Kconfig.projbuild diff --git a/test/serial/mb_serial_slave/main/component.mk b/test/serial/mb_serial_slave/main/component.mk new file mode 100644 index 0000000..a98f634 --- /dev/null +++ b/test/serial/mb_serial_slave/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/examples/protocols/modbus/serial/mb_slave/main/slave.c b/test/serial/mb_serial_slave/main/slave.c similarity index 100% rename from examples/protocols/modbus/serial/mb_slave/main/slave.c rename to test/serial/mb_serial_slave/main/slave.c diff --git a/examples/protocols/modbus/serial/mb_slave/sdkconfig.defaults b/test/serial/mb_serial_slave/sdkconfig.defaults similarity index 69% rename from examples/protocols/modbus/serial/mb_slave/sdkconfig.defaults rename to test/serial/mb_serial_slave/sdkconfig.defaults index 6507dfa..14806eb 100644 --- a/examples/protocols/modbus/serial/mb_slave/sdkconfig.defaults +++ b/test/serial/mb_serial_slave/sdkconfig.defaults @@ -4,8 +4,6 @@ CONFIG_MB_COMM_MODE_ASCII=y CONFIG_MB_SLAVE_ADDR=1 CONFIG_MB_UART_BAUD_RATE=115200 -CONFIG_FMB_TIMER_PORT_ENABLED=y -CONFIG_FMB_TIMER_GROUP=0 -CONFIG_FMB_TIMER_INDEX=0 +CONFIG_FMB_TIMER_PORT_ENABLED=n CONFIG_FMB_TIMER_ISR_IN_IRAM=y CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y diff --git a/examples/protocols/modbus/tcp/mb_tcp_master/CMakeLists.txt b/test/tcp/mb_tcp_master/CMakeLists.txt similarity index 63% rename from examples/protocols/modbus/tcp/mb_tcp_master/CMakeLists.txt rename to test/tcp/mb_tcp_master/CMakeLists.txt index ab7d677..8debb49 100644 --- a/examples/protocols/modbus/tcp/mb_tcp_master/CMakeLists.txt +++ b/test/tcp/mb_tcp_master/CMakeLists.txt @@ -2,7 +2,13 @@ # in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) -set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/modbus/mb_example_common) +set(EXTRA_COMPONENT_DIRS "../../../") +set(EXCLUDE_COMPONENTS examples test_app test freemodbus) + +# 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}") + # (Not part of the boilerplate) # 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) diff --git a/test/tcp/mb_tcp_master/Makefile b/test/tcp/mb_tcp_master/Makefile new file mode 100644 index 0000000..bc15e1a --- /dev/null +++ b/test/tcp/mb_tcp_master/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/examples/protocols/modbus/tcp/mb_tcp_master/README.md b/test/tcp/mb_tcp_master/README.md similarity index 100% rename from examples/protocols/modbus/tcp/mb_tcp_master/README.md rename to test/tcp/mb_tcp_master/README.md diff --git a/examples/protocols/modbus/tcp/mb_tcp_master/main/CMakeLists.txt b/test/tcp/mb_tcp_master/main/CMakeLists.txt similarity index 100% rename from examples/protocols/modbus/tcp/mb_tcp_master/main/CMakeLists.txt rename to test/tcp/mb_tcp_master/main/CMakeLists.txt diff --git a/examples/protocols/modbus/tcp/mb_tcp_master/main/Kconfig.projbuild b/test/tcp/mb_tcp_master/main/Kconfig.projbuild similarity index 100% rename from examples/protocols/modbus/tcp/mb_tcp_master/main/Kconfig.projbuild rename to test/tcp/mb_tcp_master/main/Kconfig.projbuild diff --git a/test/tcp/mb_tcp_master/main/component.mk b/test/tcp/mb_tcp_master/main/component.mk new file mode 100644 index 0000000..a98f634 --- /dev/null +++ b/test/tcp/mb_tcp_master/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/examples/protocols/modbus/tcp/mb_tcp_master/main/tcp_master.c b/test/tcp/mb_tcp_master/main/tcp_master.c similarity index 100% rename from examples/protocols/modbus/tcp/mb_tcp_master/main/tcp_master.c rename to test/tcp/mb_tcp_master/main/tcp_master.c diff --git a/examples/protocols/modbus/tcp/mb_tcp_master/sdkconfig.defaults b/test/tcp/mb_tcp_master/sdkconfig.defaults similarity index 91% rename from examples/protocols/modbus/tcp/mb_tcp_master/sdkconfig.defaults rename to test/tcp/mb_tcp_master/sdkconfig.defaults index 968d332..dc1172d 100644 --- a/examples/protocols/modbus/tcp/mb_tcp_master/sdkconfig.defaults +++ b/test/tcp/mb_tcp_master/sdkconfig.defaults @@ -11,8 +11,6 @@ CONFIG_FMB_COMM_MODE_ASCII_EN=n CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=2000 CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300 CONFIG_FMB_TIMER_PORT_ENABLED=y -CONFIG_FMB_TIMER_GROUP=0 -CONFIG_FMB_TIMER_INDEX=0 CONFIG_FMB_TIMER_ISR_IN_IRAM=y CONFIG_MB_MDNS_IP_RESOLVER=n CONFIG_MB_SLAVE_IP_FROM_STDIN=y diff --git a/examples/protocols/modbus/tcp/mb_tcp_slave/CMakeLists.txt b/test/tcp/mb_tcp_slave/CMakeLists.txt similarity index 63% rename from examples/protocols/modbus/tcp/mb_tcp_slave/CMakeLists.txt rename to test/tcp/mb_tcp_slave/CMakeLists.txt index e039962..bdd736e 100644 --- a/examples/protocols/modbus/tcp/mb_tcp_slave/CMakeLists.txt +++ b/test/tcp/mb_tcp_slave/CMakeLists.txt @@ -2,8 +2,12 @@ # in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) -# This component includes modbus example common definitions -set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/modbus/mb_example_common) +set(EXTRA_COMPONENT_DIRS "../../../") +set(EXCLUDE_COMPONENTS examples test_app test freemodbus) + +# 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}") # (Not part of the boilerplate) # This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. diff --git a/test/tcp/mb_tcp_slave/Makefile b/test/tcp/mb_tcp_slave/Makefile new file mode 100644 index 0000000..f4b3e9b --- /dev/null +++ b/test/tcp/mb_tcp_slave/Makefile @@ -0,0 +1,12 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# +PROJECT_NAME := modbus_tcp_slave + +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/examples/protocols/modbus/tcp/mb_tcp_slave/README.md b/test/tcp/mb_tcp_slave/README.md similarity index 95% rename from examples/protocols/modbus/tcp/mb_tcp_slave/README.md rename to test/tcp/mb_tcp_slave/README.md index a69b916..0fcebb8 100644 --- a/examples/protocols/modbus/tcp/mb_tcp_slave/README.md +++ b/test/tcp/mb_tcp_slave/README.md @@ -4,8 +4,8 @@ # Modbus Slave Example This example demonstrates using of FreeModbus TCP slave stack port implementation for supported ESP32 target chips. The external Modbus host is able to read/write device parameters using Modbus protocol transport. The parameters accessible thorough Modbus are located in `mb_example_common/modbus_params.h\c` files and can be updated by user. -These are represented in structures holding_reg_params, input_reg_params, coil_reg_params, discrete_reg_params for holding registers, input parameters, coils and discrete inputs accordingly. The app_main application demonstrates how to setup Modbus stack and use notifications about parameters change from host system. -The FreeModbus stack located in `components/freemodbus` folder and contain `/port` folder inside which contains FreeModbus stack port for ESP32 target chips. There are some parameters that can be configured in KConfig file to start stack correctly (See description below for more information). +These are represented in structures holding_reg_params, input_reg_params, coil_reg_params, discrete_reg_params for holding registers, input parameters, coils and discrete inputs accordingly. The app_main application demonstrates how to setup Modbus stack and use notifications about parameters change from host system. +The FreeModbus stack located in `components/freemodbus` folder and contain `/port` folder inside which contains FreeModbus stack port for ESP32. There are some parameters that can be configured in KConfig file to start stack correctly (See description below for more information). The slave example uses shared parameter structures defined in ```examples/protocols/modbus/mb_example_common``` folder. @@ -37,7 +37,7 @@ Configure the external Modbus master software according to port configuration pa As an example the Modbus Poll application can be used with this example. Option 2: Setup ESP32 based development board and set modbus_tcp_master example configuration as described in its README.md file. -Setup one or more slave boards and connect them into the same Modbus segment (See configuration above). +Setup one or more slave boards and connect them into the same Modbus segment (See configuration above). ### Build and flash software Build the project and flash it to the board, then run monitor tool to view serial output: diff --git a/examples/protocols/modbus/tcp/mb_tcp_slave/main/CMakeLists.txt b/test/tcp/mb_tcp_slave/main/CMakeLists.txt similarity index 100% rename from examples/protocols/modbus/tcp/mb_tcp_slave/main/CMakeLists.txt rename to test/tcp/mb_tcp_slave/main/CMakeLists.txt diff --git a/examples/protocols/modbus/tcp/mb_tcp_slave/main/Kconfig.projbuild b/test/tcp/mb_tcp_slave/main/Kconfig.projbuild similarity index 100% rename from examples/protocols/modbus/tcp/mb_tcp_slave/main/Kconfig.projbuild rename to test/tcp/mb_tcp_slave/main/Kconfig.projbuild diff --git a/test/tcp/mb_tcp_slave/main/component.mk b/test/tcp/mb_tcp_slave/main/component.mk new file mode 100644 index 0000000..a98f634 --- /dev/null +++ b/test/tcp/mb_tcp_slave/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/examples/protocols/modbus/tcp/mb_tcp_slave/main/tcp_slave.c b/test/tcp/mb_tcp_slave/main/tcp_slave.c similarity index 100% rename from examples/protocols/modbus/tcp/mb_tcp_slave/main/tcp_slave.c rename to test/tcp/mb_tcp_slave/main/tcp_slave.c diff --git a/examples/protocols/modbus/tcp/mb_tcp_slave/sdkconfig.defaults b/test/tcp/mb_tcp_slave/sdkconfig.defaults similarity index 92% rename from examples/protocols/modbus/tcp/mb_tcp_slave/sdkconfig.defaults rename to test/tcp/mb_tcp_slave/sdkconfig.defaults index bf2d645..5899a48 100644 --- a/examples/protocols/modbus/tcp/mb_tcp_slave/sdkconfig.defaults +++ b/test/tcp/mb_tcp_slave/sdkconfig.defaults @@ -11,8 +11,6 @@ CONFIG_FMB_COMM_MODE_ASCII_EN=n CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=1000 CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300 CONFIG_FMB_TIMER_PORT_ENABLED=y -CONFIG_FMB_TIMER_GROUP=0 -CONFIG_FMB_TIMER_INDEX=0 CONFIG_FMB_TIMER_ISR_IN_IRAM=y CONFIG_MB_MDNS_IP_RESOLVER=n CONFIG_MB_SLAVE_IP_FROM_STDIN=y