freemodbus: remove component files from esp-idf (backport v4.4)

This commit is contained in:
Alex Lisitsyn 2022-12-22 17:05:55 +08:00 committed by Michael (XIAO Xufeng)
parent 56efeb2c76
commit ea646a9c22
157 changed files with 5710 additions and 1577 deletions

View File

@ -153,6 +153,7 @@ exclude =
components/tinyusb,
components/unity/unity,
components/spiffs/spiffs,
components/freemodbus,
examples/build_system/cmake/import_lib/main/lib/tinyxml2,
examples/peripherals/secure_element/atecc608_ecdsa/components/esp-cryptoauthlib,
# autogenerated scripts
@ -168,4 +169,5 @@ exclude =
per-file-ignores =
# Sphinx conf.py files use star imports to setup config variables
docs/conf_common.py: F405
docs/conf_common.py: F405,
components/freemodbus/docs/conf_common.py: F405

View File

@ -0,0 +1,20 @@
name: Sync issue comments to JIRA
# This workflow will be triggered when new issue comment is created (including PR comments)
on: issue_comment
jobs:
sync_issue_comments_to_jira:
name: Sync Issue Comments to Jira
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Sync issue comments to JIRA
uses: espressif/github-actions/sync_issues_to_jira@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JIRA_PASS: ${{ secrets.JIRA_PASS }}
JIRA_PROJECT: IDFGH
JIRA_COMPONENT: modbus
JIRA_URL: ${{ secrets.JIRA_URL }}
JIRA_USER: ${{ secrets.JIRA_USER }}

View File

@ -0,0 +1,20 @@
name: Sync issues to Jira
# This workflow will be triggered when a new issue is opened
on: issues
jobs:
sync_issues_to_jira:
name: Sync issues to Jira
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Sync GitHub issues to Jira project
uses: espressif/github-actions/sync_issues_to_jira@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JIRA_PASS: ${{ secrets.JIRA_PASS }}
JIRA_PROJECT: IDFGH
JIRA_COMPONENT: modbus
JIRA_URL: ${{ secrets.JIRA_URL }}
JIRA_USER: ${{ secrets.JIRA_USER }}

View File

@ -0,0 +1,25 @@
name: Sync remain PRs to Jira
# This workflow will be triggered every hour, to sync remaining PRs (i.e. PRs with zero comment) to Jira project
# Note that, PRs can also get synced when new PR comment is created
on:
schedule:
- cron: "0 * * * *"
jobs:
sync_prs_to_jira:
name: Sync PRs to Jira
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Sync PRs to Jira project
uses: espressif/github-actions/sync_issues_to_jira@master
with:
cron_job: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JIRA_PASS: ${{ secrets.JIRA_PASS }}
JIRA_PROJECT: IDFGH
JIRA_COMPONENT: modbus
JIRA_URL: ${{ secrets.JIRA_URL }}
JIRA_USER: ${{ secrets.JIRA_USER }}

49
components/freemodbus/.gitignore vendored Normal file
View File

@ -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/

View File

@ -0,0 +1,161 @@
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
.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:
- pip install idf-component-manager --upgrade
- ./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"
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

View File

@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme
;
[subrepo]
remote = https://github.com/espressif/esp-modbus.git
branch = master
commit = 0e1fe58fbf775edc5a7b6dcd46cc07d477120cf6
parent = 24e597928cca93c926ffcae0ecaf0c99463a397f
method = merge
cmdver = 0.4.5

View File

@ -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,29 @@ 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}.")
set(requires driver lwip)
# esp_timer component was introduced in v4.2
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.1")
list(APPEND requires esp_timer)
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}"
PRIV_INCLUDE_DIRS "${priv_include_dirs}"
REQUIRES driver)
REQUIRES ${requires}
PRIV_REQUIRES esp_netif)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -193,44 +193,14 @@ menu "Modbus configuration"
If this option is set the Modbus stack uses timer for T3.5 time measurement.
Else the internal UART TOUT timeout is used for 3.5T symbol time measurement.
config FMB_TIMER_GROUP
int "Slave Timer group number"
range 0 1
default 0
help
Modbus slave Timer group number that is used for timeout measurement.
config FMB_TIMER_INDEX
int "Slave Timer index in the group"
range 0 1
default 0
help
Modbus slave Timer Index in the group that is used for timeout measurement.
config FMB_MASTER_TIMER_GROUP
int "Master Timer group number"
range 0 1
default FMB_TIMER_GROUP
help
Modbus master Timer group number that is used for timeout measurement.
config FMB_MASTER_TIMER_INDEX
int "Master Timer index"
range 0 1
default FMB_TIMER_INDEX
help
Modbus master Timer Index in the group that is used for timeout measurement.
Note: Modbus master and slave should have different timer index to be able to work simultaneously.
config FMB_TIMER_ISR_IN_IRAM
bool "Place timer interrupt handler into IRAM"
config FMB_TIMER_USE_ISR_DISPATCH_METHOD
bool "Modbus timer uses ISR dispatch method"
default n
select ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
select UART_ISR_IN_IRAM
help
This option places Modbus timer IRQ handler into IRAM.
This allows to avoid delays related to processing of non-IRAM-safe interrupts
during a flash write operation (NVS updating a value, or some other
flash API which has to perform an read/write operation and disable CPU cache).
If this option is set the Modbus stack uses ISR dispatch method
to send timeout events from the callback function called from ISR.
This option has dependency with the UART_ISR_IN_IRAM option which places UART interrupt
handler into IRAM to prevent delays related to processing of UART events.

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,63 @@
# ESP-Modbus Library
## Overview
An Espressif ESP-Modbus Library (esp-modbus) is a library to support Modbus communication in the networks based on RS485, WiFi, 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).
* [ESP-Modbus component on GitHub](https://www.github.com/espressif/esp-modbus)
This library is to be used with Espressifs IoT Development Framework, [ESP_IDF](https://github.com/espressif/esp-idf). The packages from this repository are uploaded to Espressifs component repository.
* [esp-modbus component in component repository](https://components.espressif.com/component/espressif/esp-modbus)
You can add the component to your project via `idf.py add-dependency`. More information about idf-component-manager can be found in [Espressif API guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html) or [PyPi registry](https://pypi.org/project/idf-component-manager).
The ESP-Modbus library can be used with ESP-IDF v4.1 and later. ESP-IDF v4.x releases include an earlier version of ESP-Modbus library inside freemodbus component. To use ESP-Modbus with these releases, users need to exclude the built-in freemodbus component from the build process, and update application components to depend on esp-modbus component instead. To exclude freemodbus component from compilation, add the following line to the project CMakeLists.txt file:
```
set(EXCLUDE_COMPONENTS freemodbus)
```
ESP-IDF v5.x and later releases do not include freemodbus component, so no extra steps are necessary when adding esp-modbus component.
## Documentation
The documentation can be found on the link below:
* [ESP-Modbus documentation (English)](https://docs.espressif.com/projects/esp-modbus)
## Application Examples
The examples below demonstrate the ESP-Modbus library of serial, TCP ports for slave and master implementations accordingly.
- [Modbus serial slave example](https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/serial/mb_slave)
- [Modbus serial master example](https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/serial/mb_master)
- [Modbus TCP master example](https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/tcp/mb_tcp_master)
- [Modbus TCP slave example](https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/tcp/mb_tcp_slave)
Please refer to the specific example README.md for details.
## Protocol References
- [Modbus Organization with protocol specifications](https://modbus.org/specs.php)
## Contributing
We welcome contributions to this project in the form of bug reports, feature requests and pull requests.
Issue reports and feature requests can be submitted using Github Issues: https://github.com/espressif/esp-modbus/issues. Please check if the issue has already been reported before opening a new one.
Contributions in the form of pull requests should follow ESP-IDF project's [contribution guidelines](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/contribute/index.html). We kindly ask developers to start a discussion on an issue before proposing large changes to the project.
## Licence
ESP-Modbus project is based on [FreeMODBUS library](https://github.com/cwalter-at/freemodbus), Copyright (c) 2006 Christian Walter and licensed under the BSD 3-clause license.
Modbus Master related code is Copyright (c) 2013 Armink and licensed under BSD 3-clause license.
All original code in this repository is Copyright (c) 2016-2022 Espressif Systems (Shanghai) Co. Ltd.
The project is distributed under Apache 2.0 license. See the accompanying [LICENSE file](https://github.com/espressif/esp-modbus/blob/master/LICENSE) for a copy.

View File

@ -0,0 +1,91 @@
#!/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 <target list>
# 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}"

View File

@ -1,29 +0,0 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Stack callback functions prototypes
#ifndef _ESP_MODBUS_CALLBACKS_H_
#define _ESP_MODBUS_CALLBACKS_H_
#include "mb.h"
#include "mb_m.h"
typedef eMBErrorCode (*reg_input_cb)(UCHAR*, USHORT, USHORT);
typedef eMBErrorCode (*reg_holding_cb)(UCHAR*, USHORT, USHORT, eMBRegisterMode);
typedef eMBErrorCode (*reg_coils_cb)(UCHAR*, USHORT, USHORT, eMBRegisterMode);
typedef eMBErrorCode (*reg_discrete_cb)(UCHAR*, USHORT, USHORT);
#endif /* _ESP_MODBUS_CALLBACKS_H_ */

View File

@ -1,33 +0,0 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "esp_err.h" // for esp_err_t
#include "esp_modbus_master.h" // for public interface defines
#include "mbc_tcp_master.h" // for public interface defines
/**
* Initialization of Modbus TCP Master controller interface
*/
esp_err_t mbc_master_init_tcp(void** handler)
{
void* port_handler = NULL;
esp_err_t error = mbc_tcp_master_create(&port_handler);
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_master_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@ -1,33 +0,0 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "esp_err.h" // for esp_err_t
#include "esp_modbus_slave.h" // for public slave defines
#include "mbc_tcp_slave.h" // for public interface defines
/**
* Initialization of Modbus TCP Slave controller
*/
esp_err_t mbc_slave_init_tcp(void** handler)
{
void* port_handler = NULL;
esp_err_t error = mbc_tcp_slave_create(&port_handler);
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_slave_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@ -1,14 +1,26 @@
COMPONENT_ADD_INCLUDEDIRS := common/include
COMPONENT_PRIV_INCLUDEDIRS := common port modbus modbus/ascii modbus/functions
COMPONENT_PRIV_INCLUDEDIRS += modbus/rtu modbus/tcp modbus/include
COMPONENT_PRIV_INCLUDEDIRS += serial_slave/port serial_slave/modbus_controller
COMPONENT_PRIV_INCLUDEDIRS += serial_master/port serial_master/modbus_controller
COMPONENT_PRIV_INCLUDEDIRS += tcp_slave/port tcp_slave/modbus_controller
COMPONENT_PRIV_INCLUDEDIRS += tcp_master/port tcp_master/modbus_controller
COMPONENT_SRCDIRS := common
COMPONENT_SRCDIRS += modbus modbus/ascii modbus/functions modbus/rtu modbus/tcp
COMPONENT_SRCDIRS += serial_slave/port serial_slave/modbus_controller
COMPONENT_SRCDIRS += serial_master/port serial_master/modbus_controller
COMPONENT_SRCDIRS += tcp_slave/port tcp_slave/modbus_controller
COMPONENT_SRCDIRS += tcp_master/port tcp_master/modbus_controller
COMPONENT_SRCDIRS += port
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) \
)

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,260 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="图层_1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 580"
style="enable-background:new 0 0 1000 580;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:url(#polygon12_1_);}
.st2{fill:url(#polygon21_1_);}
.st3{opacity:0.27;fill:url(#circle42_1_);enable-background:new ;}
.st4{fill:url(#polygon58_1_);}
.st5{fill:#444444;stroke:#FFFFFF;stroke-width:0.834;stroke-miterlimit:10;}
.st6{fill:none;stroke:#FFFFFF;stroke-width:1.1033;stroke-miterlimit:10;}
.st7{fill:none;stroke:#353535;stroke-width:1.1033;stroke-miterlimit:10;}
.st8{fill:#FFFFFF;stroke:#444444;stroke-width:0.834;stroke-miterlimit:10;}
.st9{fill:#444444;stroke:#FFFFFF;stroke-width:0.8485;stroke-miterlimit:10;}
.st10{fill:none;stroke:#FFFFFF;stroke-width:1.1226;stroke-miterlimit:10;}
.st11{fill:none;stroke:#353535;stroke-width:1.1226;stroke-miterlimit:10;}
.st12{fill:#FFFFFF;stroke:#444444;stroke-width:0.8485;stroke-miterlimit:10;}
.st13{fill:#353535;}
.st14{fill:#444444;stroke:#FFFFFF;stroke-width:0.9321;stroke-miterlimit:10;}
.st15{fill:none;stroke:#FFFFFF;stroke-width:1.046;stroke-miterlimit:10;}
.st16{fill:none;stroke:#353535;stroke-width:1.046;stroke-miterlimit:10;}
.st17{fill:#FFFFFF;stroke:#444444;stroke-width:0.7906;stroke-miterlimit:10;}
.st18{opacity:0.59;fill:#E0E0E0;enable-background:new ;}
.st19{fill:#FFFFFF;stroke:#444444;stroke-width:2;stroke-miterlimit:10;}
.st20{fill:none;stroke:#444444;stroke-width:2;stroke-miterlimit:10;}
.st21{fill:none;stroke:#444444;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st22{enable-background:new ;}
.st23{fill:#4D4D4D;}
</style>
<rect id="BG_2_" x="-1" y="-9.5" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476" class="st0" width="1012.9" height="600.4">
</rect>
<linearGradient id="polygon12_1_" gradientUnits="userSpaceOnUse" x1="1014.7582" y1="90.2012" x2="1077.5918" y2="356.7023" gradientTransform="matrix(0.9556 0.295 -0.2974 0.9605 -400.3649 -336.724)">
<stop offset="4.835800e-02" style="stop-color:#9FA0A0"/>
<stop offset="0.5227" style="stop-color:#D7D8D8;stop-opacity:0.4381"/>
<stop offset="0.8926" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<polygon id="polygon12" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476" class="st1" points="608.7,371.9
511.9,134.7 490.9,134.1 430.7,377.1 ">
</polygon>
<linearGradient id="polygon21_1_" gradientUnits="userSpaceOnUse" x1="197.9478" y1="434.8972" x2="282.0578" y2="791.6389" gradientTransform="matrix(0.9983 -5.887031e-02 5.887031e-02 0.9983 28.0536 -430.7623)">
<stop offset="4.835800e-02" style="stop-color:#898989"/>
<stop offset="0.5874" style="stop-color:#D7D7D7;stop-opacity:0.3616"/>
<stop offset="0.8926" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<polygon id="polygon21" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476" class="st2" points="325.4,155.1
289.2,163.9 291.9,437 480.9,450.4 ">
</polygon>
<radialGradient id="circle42_1_" cx="836.3" cy="506.5986" r="65.7125" gradientTransform="matrix(1 0 0 1 12 -19.0997)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</radialGradient>
<circle id="circle42" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476" class="st3" cx="848.3" cy="487.5" r="68.3">
</circle>
<linearGradient id="polygon58_1_" gradientUnits="userSpaceOnUse" x1="1863.538" y1="415.4688" x2="1929.7196" y2="696.1702" gradientTransform="matrix(0.8607 0.5092 -0.5092 0.8607 -635.7186 -1225.6498)">
<stop offset="4.835800e-02" style="stop-color:#898989"/>
<stop offset="0.5874" style="stop-color:#D7D7D7;stop-opacity:0.3616"/>
<stop offset="0.8926" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<polygon id="polygon58" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476" class="st4" points="547.4,383
763.1,428.2 713.7,163.4 683.8,156 ">
</polygon>
<g id="g94" transform="rotate(9.0573675,796.06564,263.99283)" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476">
<path id="path60" inkscape:connector-curvature="0" class="st5" d="M520,160.2l-75.8,10.4c-5.6,0.8-10.8-3.2-11.5-8.7l0,0
c-0.8-5.6,3.2-10.8,8.7-11.5l75.8-10.4c5.6-0.8,10.8,3.2,11.5,8.7l0,0C529.5,154.3,525.6,159.4,520,160.2L520,160.2z"/>
<ellipse id="circle62" transform="matrix(0.1574 -0.9875 0.9875 0.1574 220.2397 577.3303)" class="st6" cx="448.4" cy="159.6" rx="5.6" ry="5.6"/>
<ellipse id="circle64" transform="matrix(0.1574 -0.9875 0.9875 0.1574 241.8959 596.7076)" class="st6" cx="470.6" cy="156.6" rx="5.6" ry="5.6"/>
<ellipse id="circle66" transform="matrix(0.1574 -0.9875 0.9875 0.1574 304.0202 547.2599)" class="st7" cx="472.7" cy="95.5" rx="3.9" ry="3.9"/>
<ellipse id="circle68" transform="matrix(0.1574 -0.9875 0.9875 0.1574 263.9857 616.4264)" class="st6" cx="493.2" cy="153.5" rx="5.6" ry="5.6"/>
<ellipse id="circle70" transform="matrix(0.1574 -0.9875 0.9875 0.1574 285.6079 635.2634)" class="st6" cx="515.1" cy="150.3" rx="5.6" ry="5.6"/>
<path id="path72" inkscape:connector-curvature="0" class="st8" d="M510,139.5l-61.3,8.4l-0.4-2.8c-0.2-1.5,0.8-2.9,2.3-3.1
l55.7-7.7c1.5-0.2,2.9,0.8,3.1,2.3L510,139.5z"/>
<path id="path74" inkscape:connector-curvature="0" class="st8" d="M519.9,161.7L444.9,172l0.3,2.4c0.3,1.7,1.8,2.8,3.4,2.6
l69.1-9.5c1.7-0.3,2.8-1.8,2.6-3.4L519.9,161.7z"/>
<path id="path76" inkscape:connector-curvature="0" class="st7" d="M508.2,170.4l-50.5,6.9l0.4,3c0.2,1.3,1.5,2.3,2.8,2.2l45.5-6.3
c1.3-0.2,2.3-1.5,2.2-2.8L508.2,170.4z"/>
<path id="path78" inkscape:connector-curvature="0" class="st7" d="M499.5,133.7l-43.7,6l-1.8-13.2c-1-7.3,4.1-14,11.3-15l17.3-2.4
c7.3-1,14,4.1,15,11.3L499.5,133.7z"/>
<line id="line80" class="st7" x1="473.5" y1="100.6" x2="474.6" y2="109.1"/>
<line id="line82" class="st7" x1="471.6" y1="119.4" x2="464.5" y2="131.7"/>
<line id="line84" class="st7" x1="485.9" y1="117.9" x2="478.7" y2="130.2"/>
<path id="path86" inkscape:connector-curvature="0" class="st8" d="M470.7,182.8L467,193c-0.2,0.5,0.5,0.9,0.8,0.5l10.5-11.3
c0.3-0.3,0-0.9-0.4-0.8l-6.8,1C471,182.5,470.8,182.6,470.7,182.8z"/>
<path id="path88" inkscape:connector-curvature="0" class="st8" d="M460.7,184l-6.3,9.2c-0.3,0.5,0.3,1,0.8,0.7l12.8-10.1
c0.4-0.3,0.2-0.9-0.4-0.9l-6.6,0.9C460.9,183.9,460.8,184,460.7,184L460.7,184z"/>
<path id="path90" inkscape:connector-curvature="0" class="st8" d="M496.6,179l6.3,8.8c0.3,0.4-0.2,1-0.7,0.8l-13.2-8
c-0.4-0.3-0.3-0.8,0.2-0.9l6.8-0.8C496.3,178.8,496.5,178.9,496.6,179L496.6,179z"/>
<path id="path92" inkscape:connector-curvature="0" class="st8" d="M506.7,177.5l8.5,7.2c0.4,0.3,0,1.1-0.5,0.8l-15.1-6.3
c-0.5-0.2-0.4-0.9,0.1-0.9l6.6-0.9C506.4,177.4,506.5,177.4,506.7,177.5L506.7,177.5z"/>
</g>
<g id="g130" transform="translate(-131.09867,-443.26745)" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476">
<path id="path96" inkscape:connector-curvature="0" class="st9" d="M875,594.1l-76.7-13.2c-5.6-0.9-9.4-6.4-8.5-12l0,0
c0.9-5.6,6.4-9.4,12-8.5l76.7,13.2c5.6,0.9,9.4,6.4,8.5,12l0,0C885.9,591.3,880.6,595.1,875,594.1z"/>
<circle id="circle98" class="st10" cx="805.6" cy="571.5" r="5.7"/>
<circle id="circle100" class="st10" cx="828.2" cy="575.4" r="5.7"/>
<circle id="circle102" class="st11" cx="849" cy="516.8" r="4"/>
<circle id="circle104" class="st10" cx="851" cy="579.3" r="5.7"/>
<circle id="circle106" class="st10" cx="873.2" cy="583" r="5.7"/>
<path id="path108" inkscape:connector-curvature="0" class="st12" d="M871.6,570.9l-61.9-10.7l0.5-2.8c0.3-1.5,1.7-2.5,3.2-2.3
l56.4,9.8c1.5,0.3,2.5,1.7,2.3,3.2L871.6,570.9z"/>
<path id="path110" inkscape:connector-curvature="0" class="st12" d="M874.4,595.5l-76-13.2l-0.4,2.5c-0.3,1.7,0.8,3.3,2.5,3.6
l69.8,12c1.7,0.3,3.3-0.8,3.6-2.5L874.4,595.5z"/>
<path id="path112" inkscape:connector-curvature="0" class="st13" d="M860.3,600.3l-51.1-8.8l-0.5,3.1c-0.3,1.4,0.7,2.7,2.1,3l46,8
c1.4,0.3,2.7-0.7,3-2.1L860.3,600.3z"/>
<path id="path114" inkscape:connector-curvature="0" class="st11" d="M863.2,562.2l-44.2-7.6l2.3-13.3c1.3-7.3,8.3-12.3,15.6-11
l17.5,3.1c7.3,1.3,12.3,8.3,11,15.6L863.2,562.2z"/>
<line id="line116" class="st11" x1="848.1" y1="522.1" x2="846.6" y2="530.6"/>
<line id="line118" class="st11" x1="840.5" y1="539.7" x2="829.8" y2="549.5"/>
<line id="line120" class="st11" x1="854.8" y1="542.6" x2="844.2" y2="552.3"/>
<path id="path122" inkscape:connector-curvature="0" class="st12" d="M820.1,600.8l-6.8,8.7c-0.3,0.4,0.2,1,0.7,0.8l13.7-7.6
c0.4-0.3,0.3-0.8-0.2-0.9l-7-1.1C820.5,600.6,820.3,600.7,820.1,600.8L820.1,600.8z"/>
<path id="path124" inkscape:connector-curvature="0" class="st12" d="M810,599l-8.9,7c-0.4,0.3-0.1,1.1,0.5,0.8l15.5-5.9
c0.5-0.2,0.4-0.8-0.1-1l-6.6-1.2C810.3,598.8,810.1,598.9,810,599L810,599z"/>
<path id="path126" inkscape:connector-curvature="0" class="st12" d="M846.4,605.1l3.5,10.5c0.2,0.5-0.5,0.9-0.8,0.5l-10.4-11.8
c-0.3-0.3,0-0.9,0.5-0.8l6.9,1.3C846.3,604.9,846.4,605,846.4,605.1L846.4,605.1z"/>
<path id="path128" inkscape:connector-curvature="0" class="st12" d="M856.6,606.8l6,9.6c0.3,0.5-0.3,1-0.8,0.7l-12.7-10.7
c-0.4-0.3-0.1-1,0.4-0.8l6.6,1.2C856.4,606.6,856.5,606.7,856.6,606.8L856.6,606.8z"/>
</g>
<g id="g166" transform="translate(6.564267,-535.67492)" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476">
<path id="path132" inkscape:connector-curvature="0" class="st14" d="M331.5,670.9l-70.2,18.3c-5.1,1.3-10.4-1.8-11.7-6.9l0,0
c-1.3-5.1,1.8-10.4,6.9-11.7l70.2-18.3c5.1-1.3,10.4,1.8,11.7,6.9l0,0C339.7,664.2,336.7,669.6,331.5,670.9z"/>
<circle id="circle134" class="st15" cx="264.1" cy="678.3" r="5.3"/>
<circle id="circle136" class="st15" cx="284.7" cy="673" r="5.3"/>
<circle id="circle138" class="st16" cx="279.7" cy="615.2" r="3.7"/>
<circle id="circle140" class="st15" cx="305.5" cy="667.5" r="5.3"/>
<circle id="circle142" class="st15" cx="325.8" cy="662.1" r="5.3"/>
<path id="path144" inkscape:connector-curvature="0" class="st17" d="M319.7,652.5L263,667.3l-0.7-2.5c-0.4-1.4,0.5-2.8,1.9-3.2
l51.6-13.4c1.4-0.4,2.8,0.5,3.2,1.9L319.7,652.5z"/>
<path id="path146" inkscape:connector-curvature="0" class="st17" d="M331.5,672.3L262,690.4l0.6,2.2c0.4,1.6,2,2.5,3.5,2.1
l63.9-16.7c1.6-0.4,2.5-2,2.1-3.5L331.5,672.3z"/>
<path id="path148" inkscape:connector-curvature="0" class="st13" d="M321.5,681.8L274.8,694l0.7,2.8c0.4,1.3,1.7,2.1,2.9,1.7
l42.1-11c1.3-0.4,2.1-1.7,1.7-2.9L321.5,681.8z"/>
<path id="path150" inkscape:connector-curvature="0" class="st16" d="M309.3,648.2l-40.5,10.5l-3.2-12.2c-1.8-6.7,2.3-13.6,9-15.4
l16-4.2c6.7-1.8,13.6,2.3,15.4,9L309.3,648.2z"/>
<line id="line152" class="st16" x1="281" y1="620.1" x2="283.1" y2="627.9"/>
<line id="line154" class="st16" x1="281.4" y1="637.9" x2="276.1" y2="650.3"/>
<line id="line156" class="st16" x1="294.6" y1="634.9" x2="289.3" y2="647.3"/>
<path id="path158" inkscape:connector-curvature="0" class="st17" d="M287.6,697.6l-2.3,10.1c-0.1,0.5,0.6,0.8,0.8,0.4l8.7-11.7
c0.3-0.4-0.1-0.8-0.5-0.7l-6.3,1.8C287.8,697.3,287.6,697.4,287.6,697.6L287.6,697.6z"/>
<path id="path160" inkscape:connector-curvature="0" class="st17" d="M278.3,699.9l-4.8,9.3c-0.3,0.5,0.4,0.9,0.7,0.6l10.9-10.9
c0.4-0.4,0-0.9-0.5-0.8l-6.1,1.6C278.5,699.8,278.4,699.8,278.3,699.9L278.3,699.9z"/>
<path id="path162" inkscape:connector-curvature="0" class="st17" d="M311.6,691.2l7,7.6c0.4,0.4-0.1,0.9-0.6,0.7l-13.3-6.1
c-0.4-0.2-0.4-0.7,0.1-0.9l6.3-1.6C311.3,691,311.5,691.1,311.6,691.2z"/>
<path id="path164" inkscape:connector-curvature="0" class="st17" d="M320.8,688.7l8.8,5.8c0.5,0.3,0.1,1-0.4,0.8l-14.9-4.2
c-0.5-0.1-0.5-0.8,0-0.9l6.1-1.6C320.6,688.6,320.7,688.6,320.8,688.7z"/>
</g>
<path id="path168" inkscape:connector-curvature="0" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476" class="st18" d="
M241.1,524.1h-1c0.4-1.2,0.6-2.4,0.6-3.7c0-7-5.7-12.7-12.7-12.7h-3.3c-2.7,0-5.2,0.9-7.2,2.3c-1.2-0.8-2.7-1.3-4.3-1.3
c-0.7,0-1.4,0.1-2.1,0.3c-0.1-3.9-3.3-7.1-7.2-7.1h-0.3c-2.2,0-4.1,1-5.4,2.5l0,0c2.7-3.8,4.4-8.5,4.4-13.6
c0-13-10.6-23.5-23.5-23.5c-13,0-23.5,10.6-23.5,23.5c0,0.7,0,1.3,0.1,1.9c-2.9,0.3-5.7,1.3-8,2.9c-3.2-4.3-8.2-7.1-13.9-7.1l0,0
c-9.5,0-17.3,7.8-17.3,17.3c0,0.1,0,0.3,0,0.4c-1.8-0.9-3.7-1.5-5.9-1.5c-6.9,0-12.6,5.7-12.6,12.6c0,0.7,0.1,1.5,0.2,2.2H92
c-3,0-5.5,1.9-6.5,4.5h-9.9c-2.5,0-4.5,2-4.5,4.5s2,4.5,4.5,4.5h149.2h3.3h13.1c2.5,0,4.5-2,4.5-4.5
C245.6,526.1,243.6,524.1,241.1,524.1L241.1,524.1z"/>
<path id="path170" inkscape:connector-curvature="0" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476" class="st18" d="
M898.4,516.3h-0.9c0.3-1,0.5-2.1,0.5-3.3c0-6.2-5.1-11.3-11.3-11.3h-2.9c-2.4,0-4.6,0.8-6.4,2c-1.1-0.7-2.4-1.1-3.8-1.1
c-0.6,0-1.2,0.1-1.8,0.3c-0.1-3.5-2.9-6.3-6.4-6.3H865c-1.9,0-3.6,0.9-4.8,2.2l0,0c2.4-3.4,3.9-7.6,3.9-12
c0-11.5-9.4-20.9-20.9-20.9s-20.9,9.4-20.9,20.9c0,0.6,0,1.2,0.1,1.7c-2.6,0.2-5,1.2-7.1,2.6c-2.8-3.8-7.3-6.3-12.3-6.3l0,0
c-8.5,0-15.4,6.9-15.4,15.4c0,0.1,0,0.2,0,0.4c-1.6-0.8-3.3-1.4-5.2-1.4c-6.2,0-11.2,5-11.2,11.2c0,0.7,0.1,1.3,0.2,1.9h-5.5
c-2.6,0-4.9,1.7-5.8,4h-8.8c-2.2,0-4,1.8-4,4s1.8,4,4,4h132.5h2.9h11.6c2.2,0,4-1.8,4-4C902.4,518.1,900.6,516.3,898.4,516.3
L898.4,516.3z"/>
<g id="g184" transform="translate(10.641067,-115.56078)" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476">
<g id="g178">
<path id="path172" inkscape:connector-curvature="0" class="st19" d="M149.5,586c2.4,4.6,6.2,8.3,10.8,10.8c3.6,1.9,7.6,3,11.9,3
c14.2,0,25.7-11.5,25.7-25.7c0-4.3-1.1-8.4-3-11.9c-2.4-4.6-6.2-8.3-10.8-10.8c-3.6-1.9-7.6-3-11.9-3c-14.2,0-25.7,11.5-25.7,25.7
C146.6,578.4,147.7,582.4,149.5,586z"/>
<path id="path174" inkscape:connector-curvature="0" class="st18" d="M194.1,562.5c-2.3-4.4-6-8.1-10.4-10.4
c-3.4-1.8-7.4-2.9-11.5-2.9c-9.5,0-17.7,5.3-21.8,13.1c4-2.8,8.9-4.4,14.1-4.4c4.2,0,8.1,1,11.5,2.9c4.4,2.3,8.1,6,10.4,10.4
c1.8,3.4,2.9,7.4,2.9,11.5c0,4.2-1.1,8.2-2.9,11.7c6.4-4.5,10.6-11.9,10.6-20.3C197,569.8,196,565.9,194.1,562.5L194.1,562.5z"/>
<path id="path176" inkscape:connector-curvature="0" class="st20" d="M149.5,586c-7.7,10-11.6,17.8-9.3,20.1s10.1-1.6,20.1-9.3
c5.6-4.4,12-9.9,18.3-16.3c6.4-6.4,11.9-12.7,16.3-18.3c7.7-10,11.6-17.8,9.3-20.1s-10.1,1.6-20.1,9.3"/>
</g>
<path id="path180" inkscape:connector-curvature="0" class="st21" d="M154,566.3c0.5-1.2,1.1-2.3,1.8-3.4c0.7-1.1,1.5-2,2.4-2.9
s1.9-1.7,2.9-2.4c1.1-0.7,2.2-1.3,3.4-1.8s2.4-0.9,3.7-1.2s2.6-0.4,4-0.4"/>
<path id="path182" inkscape:connector-curvature="0" class="st21" d="M152.4,574.1c0-1.4,0.1-2.7,0.4-4"/>
</g>
<g id="g192" transform="translate(-0.2304,235.22748)" inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476">
<polygon id="polygon186" class="st19" points="843.4,216.1 850,204.1 856.6,216.1 868.7,222.7 856.6,229.3 850,241.4 843.4,229.3
831.4,222.7 "/>
<polygon id="polygon188" class="st19" points="868.4,248.1 873.4,239.1 878.3,248.1 887.4,253.1 878.3,258 873.4,267.1 868.4,258
859.4,253.1 "/>
<polygon id="polygon190" class="st19" points="884.1,207.8 887.4,201.7 890.7,207.8 896.7,211.1 890.7,214.4 887.4,220.4
884.1,214.4 878,211.1 "/>
</g>
<g inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476" class="st22">
<path class="st23" d="M332.8,346.5l65.5-89.9h24.1v89.6h17.5v20.8h-17.5v28.8h-27.9v-28.8h-61.7V346.5z M358.9,346.1h35.6v-34.4
c0-5.9,0.2-11.6,0.8-17h-0.9c-4.9,7.8-8,12.7-9.4,14.6L358.9,346.1z"/>
<path class="st23" d="M450.4,326.2c0-11.1,1.1-21,3.2-29.6c2.1-8.6,4.9-15.5,8.2-20.7c3.3-5.2,7.3-9.4,11.9-12.8
c4.6-3.3,9.1-5.6,13.5-6.8c4.4-1.2,9-1.8,13.7-1.8c16.3,0,28.8,6.4,37.5,19.1c8.7,12.7,13.1,30.3,13.1,52.6
c0,22.1-4.4,39.6-13.1,52.4c-8.7,12.8-21.2,19.2-37.4,19.2c-4.5,0-8.9-0.6-13.2-1.7c-4.3-1.1-8.7-3.3-13.4-6.6
c-4.6-3.2-8.7-7.4-12.1-12.5c-3.5-5.1-6.3-12-8.6-20.8C451.5,347.6,450.4,337.6,450.4,326.2z M479.4,326.2
c0,33.8,7.2,50.7,21.6,50.7c14.2,0,21.3-16.9,21.3-50.7c0-33.8-7.2-50.7-21.5-50.7C486.5,275.5,479.4,292.4,479.4,326.2z"/>
<path class="st23" d="M562.4,346.5l65.5-89.9h24.1v89.6h17.5v20.8h-17.5v28.8h-27.8v-28.8h-61.7V346.5z M588.5,346.1h35.6v-34.4
c0-5.9,0.2-11.6,0.8-17h-0.9c-4.9,7.8-8,12.7-9.4,14.6L588.5,346.1z"/>
</g>
<g inkscape:export-xdpi="96.009476" inkscape:export-ydpi="96.009476">
<path class="st23" d="M435.8,197.2c0-6.4,1.6-11.6,4.9-15.5c3.2-3.9,7.5-5.9,12.8-5.9c5.3,0,9.6,2,12.8,5.9
c3.2,3.9,4.8,9.1,4.8,15.5c0,6.4-1.6,11.6-4.8,15.4c-3.2,3.9-7.5,5.8-12.8,5.8c-5.3,0-9.6-1.9-12.8-5.8
C437.5,208.8,435.8,203.6,435.8,197.2z M443.6,197.2c0,4.6,0.9,8.2,2.6,10.8c1.7,2.7,4.2,4,7.3,4c3.1,0,5.5-1.3,7.2-4
c1.7-2.7,2.6-6.3,2.6-10.9c0-4.6-0.9-8.2-2.6-10.8c-1.7-2.7-4.1-4-7.2-4c-3.1,0-5.5,1.3-7.2,4C444.5,189,443.6,192.7,443.6,197.2z"
/>
<path class="st23" d="M474.8,202.7c0-4.4,1.2-8.2,3.5-11.2c2.3-3.1,5.8-4.6,10.4-4.6c3.1,0,5.7,0.8,7.9,2.3
c2.2,1.6,3.7,3.5,4.6,5.8c0.9,2.3,1.4,4.9,1.4,7.7c0,1.4-0.1,2.7-0.4,4c-0.2,1.3-0.7,2.7-1.3,4.2c-0.7,1.5-1.5,2.7-2.5,3.8
c-1,1.1-2.3,2-4,2.7c-1.7,0.7-3.6,1.1-5.7,1.1c-2.1,0-4-0.3-5.6-1c-1.7-0.7-3-1.5-4-2.6c-1-1.1-1.8-2.3-2.5-3.7
c-0.7-1.4-1.1-2.8-1.4-4.2C475,205.6,474.8,204.1,474.8,202.7z M482.3,202.7c0,3.4,0.6,5.8,1.9,7.3c1.3,1.5,2.8,2.3,4.5,2.3
c1.7,0,3.1-0.8,4.5-2.3c1.3-1.5,2-4,2-7.3c0-3.4-0.7-5.8-2-7.4c-1.3-1.5-2.8-2.3-4.5-2.3c-1.7,0-3.2,0.8-4.5,2.3
C482.9,196.8,482.3,199.3,482.3,202.7z"/>
<path class="st23" d="M507.9,229.5v-41.8h6.9v3.3c1.9-2.8,4.3-4.3,7.4-4.3c3.6,0,6.7,1.4,9.1,4.3c2.4,2.8,3.6,6.7,3.6,11.5
c0,2.8-0.4,5.2-1.2,7.3c-0.8,2.1-1.8,3.8-3.1,5c-1.3,1.2-2.6,2.1-4.1,2.7c-1.4,0.6-2.9,0.9-4.4,0.9c-1.8,0-3.3-0.4-4.5-1.2
c-1.2-0.8-2.1-1.7-2.7-2.7v14.9H507.9z M515,202.6c0,3,0.5,5.3,1.6,7.1c1.1,1.7,2.6,2.6,4.6,2.6c1.9,0,3.4-0.8,4.6-2.5
c1.1-1.7,1.7-4.1,1.7-7.2c0-3-0.6-5.3-1.7-7c-1.1-1.7-2.7-2.6-4.6-2.6c-2,0-3.5,0.9-4.6,2.7C515.6,197.4,515,199.8,515,202.6z"/>
<path class="st23" d="M537.8,211.3l5.1-3c2,3,4.6,4.5,7.9,4.5c1.5,0,2.6-0.3,3.4-0.9c0.8-0.6,1.2-1.4,1.2-2.3c0-0.3,0-0.6-0.1-0.8
c-0.1-0.3-0.2-0.5-0.4-0.7c-0.2-0.2-0.4-0.4-0.6-0.6c-0.2-0.2-0.4-0.4-0.8-0.6c-0.4-0.2-0.7-0.4-0.9-0.5c-0.2-0.1-0.6-0.3-1.1-0.5
c-0.5-0.2-0.9-0.3-1.2-0.4c-0.3-0.1-0.7-0.2-1.3-0.4c-0.6-0.2-1-0.3-1.3-0.4c-2.6-0.8-4.6-1.9-6.2-3.2c-1.6-1.3-2.3-3.2-2.3-5.7
c0-2.7,1.1-4.8,3.4-6.4c2.3-1.6,5-2.4,8.3-2.4c2.5,0,4.8,0.6,7,1.9c2.2,1.3,3.8,3,4.9,5l-4.8,2.9c-2.1-2.7-4.5-4-7.1-4
c-1.4,0-2.5,0.3-3.2,0.8c-0.8,0.6-1.1,1.3-1.1,2.2c0,0.3,0,0.6,0.1,0.9c0.1,0.3,0.2,0.5,0.4,0.8c0.2,0.2,0.4,0.4,0.6,0.6
c0.2,0.2,0.5,0.4,0.8,0.6c0.4,0.2,0.7,0.4,0.9,0.5c0.3,0.1,0.6,0.3,1.1,0.4c0.5,0.2,0.8,0.3,1.1,0.4c0.3,0.1,0.7,0.2,1.2,0.4
c0.5,0.2,0.9,0.3,1.2,0.4c5.9,2,8.9,4.9,8.9,8.9c0,2.5-1,4.6-3.1,6.4c-2.1,1.7-5,2.6-8.8,2.6c-2.9,0-5.6-0.7-7.9-2
S539.1,213.4,537.8,211.3z"/>
<path class="st23" d="M568.2,217.7v-8.3h9.3v8.3H568.2z M576.4,205.9h-7l-0.5-29.4h8L576.4,205.9z"/>
</g>
<g>
<path class="st23" d="M333.4,464.8v-27.1h12c3.2,0,5.7,0.8,7.5,2.3c1.8,1.5,2.7,3.6,2.7,6.1c0,2.6-0.9,4.6-2.8,6.1
s-4.3,2.2-7.4,2.2H339v10.3H333.4z M339,450.2h5.7c1.6,0,2.9-0.4,3.8-1.1c0.9-0.7,1.4-1.7,1.4-3c0-1.3-0.4-2.3-1.3-3
c-0.9-0.7-2.2-1.1-3.8-1.1H339V450.2z"/>
<path class="st23" d="M354.1,464.8l10.8-27.1h6l10.8,27.1h-5.9l-2.3-6.4h-11.2l-2.3,6.4H354.1z M363.7,454.4h8.3l-2.2-6.4
c-0.5-1.5-1.1-3.3-1.8-5.5h-0.2c-0.2,0.7-0.5,1.6-0.9,2.8c-0.4,1.2-0.7,2.1-0.9,2.6L363.7,454.4z"/>
<path class="st23" d="M383,451.2c0-4.1,1.3-7.5,3.8-10.2c2.5-2.6,5.9-4,10-4c1.3,0,2.5,0.2,3.7,0.5c1.2,0.3,2.1,0.7,2.9,1.2
c0.8,0.5,1.5,1,2.2,1.7c0.7,0.6,1.2,1.2,1.5,1.7c0.4,0.5,0.7,1,0.9,1.5l-4.8,1.4c-0.4-0.5-0.8-1-1.1-1.3c-0.3-0.3-0.7-0.7-1.2-1.2
s-1.1-0.8-1.8-1s-1.4-0.3-2.3-0.3c-2.5,0-4.5,0.9-5.9,2.7c-1.4,1.8-2.1,4.2-2.1,7.2c0,3,0.7,5.4,2.2,7.2c1.5,1.8,3.3,2.7,5.6,2.7
c2,0,3.5-0.5,4.6-1.4c1.1-1,1.8-2.2,2-3.8c0.1-1.3,0.2-2,0.2-2.1h-7.1v-4.3h12.5v15.3H405l-0.5-2.4c-1.7,2-4.4,3-8,3
c-3.8,0-7-1.3-9.6-3.8C384.3,459,383,455.5,383,451.2z"/>
<path class="st23" d="M413.9,464.8v-27.1H434v4.3h-14.6v6.7h13.1v4.2h-13.1v7.6h14.9v4.3H413.9z"/>
<path class="st23" d="M448.1,464.8v-27.1h5.8l8.5,14.1l3.3,5.5h0.2c-0.4-2.6-0.6-5.2-0.6-7.9v-11.7h5.6v27.1h-5.8l-8.5-13.8
l-3.3-5.7h-0.2c0.4,2.5,0.6,5.1,0.6,7.9v11.6H448.1z"/>
<path class="st23" d="M475.4,451.3c0-4.3,1.2-7.7,3.6-10.3c2.4-2.6,5.6-3.9,9.5-3.9c4,0,7.1,1.3,9.5,3.9c2.4,2.6,3.6,6,3.6,10.3
c0,4.3-1.2,7.7-3.6,10.3c-2.4,2.6-5.6,3.9-9.5,3.9s-7.1-1.3-9.5-3.9C476.6,458.9,475.4,455.5,475.4,451.3z M481.2,451.3
c0,3,0.6,5.4,1.9,7.2c1.3,1.8,3.1,2.7,5.4,2.7c2.3,0,4.1-0.9,5.4-2.6c1.3-1.8,1.9-4.2,1.9-7.2c0-3-0.6-5.4-1.9-7.2
c-1.3-1.8-3.1-2.7-5.4-2.7c-2.3,0-4.1,0.9-5.4,2.7C481.9,445.8,481.2,448.2,481.2,451.3z"/>
<path class="st23" d="M502.7,442v-4.3h22.7v4.3h-8.6v22.8h-5.6V442H502.7z"/>
<path class="st23" d="M538.3,464.8v-27.1h19.6v4.3h-13.9v7.1h12.5v4.2h-12.5v11.4H538.3z"/>
<path class="st23" d="M559.9,451.3c0-4.3,1.2-7.7,3.6-10.3c2.4-2.6,5.6-3.9,9.5-3.9c4,0,7.1,1.3,9.5,3.9c2.4,2.6,3.6,6,3.6,10.3
c0,4.3-1.2,7.7-3.6,10.3c-2.4,2.6-5.6,3.9-9.5,3.9c-4,0-7.1-1.3-9.5-3.9C561.1,458.9,559.9,455.5,559.9,451.3z M565.7,451.3
c0,3,0.6,5.4,1.9,7.2c1.3,1.8,3.1,2.7,5.4,2.7c2.3,0,4.1-0.9,5.4-2.6c1.3-1.8,1.9-4.2,1.9-7.2c0-3-0.6-5.4-1.9-7.2
c-1.3-1.8-3.1-2.7-5.4-2.7c-2.3,0-4.1,0.9-5.4,2.7C566.3,445.8,565.7,448.2,565.7,451.3z"/>
<path class="st23" d="M590.5,454.6v-16.9h5.6v16.9c0,4.4,1.9,6.5,5.7,6.5c3.8,0,5.7-2.2,5.7-6.5v-16.9h5.6v16.9
c0,3.5-0.9,6.2-2.8,8c-1.9,1.9-4.7,2.8-8.5,2.8c-3.6,0-6.4-0.9-8.4-2.7S590.5,458.2,590.5,454.6z"/>
<path class="st23" d="M619.3,464.8v-27.1h5.8l8.5,14.1l3.3,5.5h0.2c-0.4-2.6-0.6-5.2-0.6-7.9v-11.7h5.6v27.1h-5.8l-8.5-13.8
l-3.3-5.7h-0.2c0.4,2.5,0.6,5.1,0.6,7.9v11.6H619.3z"/>
<path class="st23" d="M648.3,464.8v-27.1h9.2c4.6,0,8.2,1.2,10.7,3.5s3.7,5.7,3.7,10c0,1.4-0.1,2.8-0.4,4c-0.3,1.3-0.8,2.5-1.4,3.7
c-0.7,1.2-1.6,2.2-2.6,3c-1.1,0.8-2.4,1.5-4.1,2c-1.7,0.5-3.6,0.8-5.8,0.8H648.3z M653.9,460.5h3c3,0,5.3-0.7,6.8-2.2
c1.5-1.5,2.3-3.8,2.3-7.1c0-3.3-0.8-5.7-2.4-7.2s-3.8-2.1-6.7-2.1h-3.1V460.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,16 @@
var DOCUMENTATION_VERSIONS = {
DEFAULTS: { has_targets: true,
supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ]
},
VERSIONS: [
{ name: "latest" },
{ name: "v1.0.1", old:false },
{ name: "v1.0.0", old:true }
],
IDF_TARGETS: [
{ text: "ESP32", value: "esp32"},
{ text: "ESP32-S2", value: "esp32s2"},
{ text: "ESP32-S3", value: "esp32s3"},
{ text: "ESP32-C3", value: "esp32c3"}
]
};

View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Common (non-language-specific) configuration for Sphinx
#
# type: ignore
# pylint: disable=wildcard-import
# pylint: disable=undefined-variable
from __future__ import print_function, unicode_literals
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'
html_static_path = ['../_static']
# Extra options required by sphinx_idf_theme
project_slug = 'esp-modbus'
versions_url = './_static/modbus_docs_versions.js'
idf_targets = ['esp32', 'esp32s2', 'esp32c3']
languages = ['en']

View File

@ -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 <https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/serial/mb_slave>`__
.. _example_mb_master:
- `Modbus serial master example <https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/serial/mb_master>`__
.. _example_mb_tcp_master:
- `Modbus TCP master example <https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/tcp/mb_tcp_master>`__
.. _example_mb_tcp_slave:
- `Modbus TCP slave example <https://github.com/espressif/esp-idf/tree/master/examples/protocols/modbus/tcp/mb_tcp_slave>`__
Please refer to the specific example README.md for details.
.. _modbus_organization:
Protocol References
-------------------
- `Modbus Organization with protocol specifications <https://modbus.org/specs.php>`__
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

View File

@ -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'

View File

@ -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 <overview_messaging_and_mapping>
Modbus Port Initialization <port_initialization>
Modbus Master API <master_api_overview>
Modbus Slave API <slave_api_overview>
Applications and References <applications_and_references>

View File

@ -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 <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_netif.html>`__ 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 <example_mb_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 <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html#uart-api-running-uart-communication>`__ 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, &param_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());

View File

@ -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`

View File

@ -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.");
}

View File

@ -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(&param_lock);
holding_reg_area[2] += 10;
portEXIT_CRITICAL(&param_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(&reg_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());

View File

@ -0,0 +1 @@
esp-docs==0.2.4

View File

@ -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
}

View File

@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// Stack callback functions prototypes
#ifndef _ESP_MODBUS_CALLBACKS_H_
#define _ESP_MODBUS_CALLBACKS_H_
#include "mb.h"
#include "mb_m.h"
typedef eMBErrorCode (*reg_input_cb)(UCHAR*, USHORT, USHORT);
typedef eMBErrorCode (*reg_holding_cb)(UCHAR*, USHORT, USHORT, eMBRegisterMode);
typedef eMBErrorCode (*reg_coils_cb)(UCHAR*, USHORT, USHORT, eMBRegisterMode);
typedef eMBErrorCode (*reg_discrete_cb)(UCHAR*, USHORT, USHORT);
#endif /* _ESP_MODBUS_CALLBACKS_H_ */

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t

View File

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "esp_modbus_master.h" // for public interface defines
#include "mbc_tcp_master.h" // for public interface defines
/**
* Initialization of Modbus TCP Master controller interface
*/
esp_err_t mbc_master_init_tcp(void** handler)
{
void* port_handler = NULL;
esp_err_t error = mbc_tcp_master_create(&port_handler);
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_master_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
@ -78,7 +69,7 @@ static void mbc_slave_free_descriptors(void) {
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
for (int descr_type = 0; descr_type < MB_PARAM_COUNT; descr_type++) {
for (it = LIST_FIRST(&mbs_opts->mbs_area_descriptors[descr_type]); it != NULL; it = LIST_NEXT(it, entries)) {
while ((it = LIST_FIRST(&mbs_opts->mbs_area_descriptors[descr_type]))) {
LIST_REMOVE(it, entries);
free(it);
}

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t

View File

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "esp_modbus_slave.h" // for public slave defines
#include "mbc_tcp_slave.h" // for public interface defines
/**
* Initialization of Modbus TCP Slave controller
*/
esp_err_t mbc_slave_init_tcp(void** handler)
{
void* port_handler = NULL;
esp_err_t error = mbc_tcp_slave_create(&port_handler);
if ((port_handler != NULL) && (error == ESP_OK)) {
mbc_slave_init_iface(port_handler);
*handler = port_handler;
}
return error;
}

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MB_IFACE_COMMON_H
@ -24,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__)
@ -44,10 +36,10 @@ extern "C" {
#define MB_CONTROLLER_PRIORITY (CONFIG_FMB_PORT_TASK_PRIO - 1) // priority of MB controller task
// Default port defines
#define MB_DEVICE_ADDRESS (1) // Default slave device address in Modbus
#define MB_DEVICE_SPEED (115200) // Default Modbus speed for now hard defined
#define MB_DEVICE_ADDRESS (1) // Default slave device address in Modbus
#define MB_DEVICE_SPEED (115200) // Default Modbus speed for now hard defined
#define MB_UART_PORT (UART_NUM_MAX - 1) // Default UART port number
#define MB_PAR_INFO_TOUT (10) // Timeout for get parameter info
#define MB_PAR_INFO_TOUT (10) // Timeout for get parameter info
#define MB_PARITY_NONE (UART_PARITY_DISABLE)
// The Macros below handle the endianness while transfer N byte data into buffer

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_MB_MASTER_INTERFACE_H

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_MB_SLAVE_INTERFACE_H

View File

@ -1,17 +1,8 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
* SPDX-License-Identifier: Apache-2.0
*/
// mbcontroller.h
// mbcontroller - common Modbus controller header file

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MB_CONTROLLER_MASTER_H

View File

@ -1,17 +1,9 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MB_CONTROLLER_SLAVE_H
#define _MB_CONTROLLER_SLAVE_H

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
@ -144,26 +151,33 @@ eMBErrorCode
eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBASCIIFrame = ( UCHAR* ) ucASCIIBuf;
USHORT usFrameLength = usRcvBufferPos;
if( xMBPortSerialGetRequest( &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
{
@ -179,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;
@ -200,6 +215,13 @@ eMBASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
/* Activate the transmitter. */
eSndState = STATE_TX_START;
EXIT_CRITICAL_SECTION( );
if ( xMBPortSerialSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
vMBPortSerialEnable( FALSE, TRUE );
}
else

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
@ -149,30 +154,35 @@ eMBErrorCode
eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBASCIIFrame = ( UCHAR* ) ucMasterASCIIRcvBuf;
USHORT usFrameLength = usMasterRcvBufferPos;
if( xMBMasterPortSerialGetResponse( &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
{
eStatus = MB_EIO;
/* 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( );
return eStatus;
@ -186,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;
@ -203,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 ( xMBMasterPortSerialSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
vMBMasterPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
@ -422,7 +437,8 @@ xMBMasterASCIITransmitFSM( void )
/* Notify the task which called eMBMasterASCIISend that the frame has
* been sent. */
case STATE_M_TX_NOTIFY:
xFrameIsBroadcast = ( ucMasterASCIISndBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
xFrameIsBroadcast = ( ucMasterASCIISndBuf[MB_SEND_BUF_PDU_OFF - MB_SER_PDU_PDU_OFF]
== MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
vMBMasterRequestSetType( xFrameIsBroadcast );
eSndState = STATE_M_TX_XFWR;
/* If the frame is broadcast ,master will enable timer of convert delay,
@ -442,7 +458,7 @@ xMBMasterASCIITransmitFSM( void )
return xNeedPoll;
}
BOOL
BOOL MB_PORT_ISR_ATTR
xMBMasterASCIITimerT1SExpired( void )
{
BOOL xNeedPoll = FALSE;
@ -452,7 +468,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.

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
@ -28,8 +35,6 @@
* File: $Id: mbfuncdisc_m.c,v 1.60 2013/10/15 8:48:20 Armink Add Master Functions Exp $
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
@ -401,6 +408,7 @@ BOOL xMBMasterRequestIsBroadcast( void );
eMBMasterErrorEventType eMBMasterGetErrorType( void );
void vMBMasterSetErrorType( eMBMasterErrorEventType errorType );
eMBMasterReqErrCode eMBMasterWaitRequestFinish( void );
eMBMode ucMBMasterGetCommMode( void );
/* ----------------------- Callback -----------------------------------------*/

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
@ -34,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
@ -66,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.

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
@ -80,6 +87,12 @@ PR_BEGIN_EXTERN_C
#define MB_TCP_UID 6
#define MB_TCP_FUNC 7
#if MB_MASTER_TCP_ENABLED
#define MB_SEND_BUF_PDU_OFF MB_TCP_FUNC
#else
#define MB_SEND_BUF_PDU_OFF MB_SER_PDU_PDU_OFF
#endif
#define MB_TCP_PSEUDO_ADDRESS 255
/* ----------------------- Prototypes 0-------------------------------------*/

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
@ -138,6 +145,10 @@ BOOL xMBPortSerialGetByte( CHAR * pucByte );
BOOL xMBPortSerialPutByte( CHAR ucByte );
BOOL xMBPortSerialGetRequest( UCHAR **ppucMBSerialFrame, USHORT * pusSerialLength ) __attribute__ ((weak));
BOOL xMBPortSerialSendResponse( 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 );
@ -151,6 +162,13 @@ void vMBMasterPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable );
BOOL xMBMasterPortSerialGetByte( CHAR * pucByte );
BOOL xMBMasterPortSerialPutByte( CHAR ucByte );
BOOL xMBMasterPortSerialGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength );
BOOL xMBMasterPortSerialSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength );
void vMBMasterRxFlush( void );
#endif
/* ----------------------- Timers functions ---------------------------------*/

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
@ -63,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
{
@ -145,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 )
{
@ -303,11 +310,13 @@ eMBMasterDisable( void )
eMBErrorCode
eMBMasterPoll( void )
{
static UCHAR *ucMBFrame = NULL;
static UCHAR *ucMBSendFrame = NULL;
static UCHAR *ucMBRcvFrame = NULL;
static UCHAR ucRcvAddress;
static UCHAR ucFunctionCode;
static USHORT usLength;
static eMBException eException;
static BOOL xTransactionIsActive = FALSE;
int i;
int j;
eMBErrorCode eStatus = MB_ENOERR;
@ -333,49 +342,61 @@ eMBMasterPoll( void )
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_FRAME_TRANSMIT ) ) {
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_FRAME_TRANSMIT", __func__);
/* Master is busy now. */
vMBMasterGetPDUSndBuf( &ucMBFrame );
ESP_LOG_BUFFER_HEX_LEVEL("POLL transmit buffer", (void*)ucMBFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
eStatus = peMBMasterFrameSendCur( ucMBMasterGetDestAddress(), ucMBFrame, usMBMasterGetPDUSndLength() );
vMBMasterGetPDUSndBuf( &ucMBSendFrame );
ESP_LOG_BUFFER_HEX_LEVEL("POLL transmit buffer", (void*)ucMBSendFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
eStatus = peMBMasterFrameSendCur( ucMBMasterGetDestAddress(), ucMBSendFrame, usMBMasterGetPDUSndLength() );
if (eStatus != MB_ENOERR)
{
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
ESP_LOGE( MB_PORT_TAG, "%s:Frame send error. %d", __func__, eStatus );
}
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_FRAME_TRANSMIT );
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_FRAME_SENT ) ) {
ESP_LOGD( MB_PORT_TAG, "%s:EV_MASTER_FRAME_SENT", __func__ );
ESP_LOG_BUFFER_HEX_LEVEL("POLL sent buffer", (void*)ucMBFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
ESP_LOG_BUFFER_HEX_LEVEL("POLL sent buffer", (void*)ucMBSendFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
xTransactionIsActive = TRUE;
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_FRAME_SENT );
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_FRAME_RECEIVED ) ) {
eStatus = peMBMasterFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength);
if (xTransactionIsActive) {
eStatus = peMBMasterFrameReceiveCur( &ucRcvAddress, &ucMBRcvFrame, &usLength);
MB_PORT_CHECK(ucMBRcvFrame, MB_EILLSTATE, "Receive buffer initialization fail.");
MB_PORT_CHECK(ucMBSendFrame, MB_EILLSTATE, "Send buffer initialization fail.");
// Check if the frame is for us. If not ,send an error process event.
if ( ( eStatus == MB_ENOERR ) && ( ( ucRcvAddress == ucMBMasterGetDestAddress() )
|| ( ucRcvAddress == MB_TCP_PSEUDO_ADDRESS) ) ) {
if ( ( ucMBRcvFrame[MB_PDU_FUNC_OFF] & ~MB_FUNC_ERROR ) == ( ucMBSendFrame[MB_PDU_FUNC_OFF] ) ) {
ESP_LOGD(MB_PORT_TAG, "%s: Packet data received successfully (%u).", __func__, eStatus);
ESP_LOG_BUFFER_HEX_LEVEL("POLL receive buffer", (void*)ucMBRcvFrame, (uint16_t)usLength, ESP_LOG_DEBUG);
// Check if the frame is for us. If not ,send an error process event.
if ( ( eStatus == MB_ENOERR ) && ( ( ucRcvAddress == ucMBMasterGetDestAddress() )
|| ( ucRcvAddress == MB_TCP_PSEUDO_ADDRESS ) ) )
{
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
ESP_LOGD(MB_PORT_TAG, "%s: Packet data received successfully (%u).", __func__, eStatus);
ESP_LOG_BUFFER_HEX_LEVEL("POLL receive buffer", (void*)ucMBFrame, (uint16_t)usLength, ESP_LOG_DEBUG);
}
else
{
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
ESP_LOGD( MB_PORT_TAG, "%s: Packet data receive failed (addr=%u)(%u).",
__func__, ucRcvAddress, eStatus);
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
} else {
ESP_LOGE( MB_PORT_TAG, "Drop incorrect frame, receive_func(%u) != send_func(%u)",
ucMBRcvFrame[MB_PDU_FUNC_OFF], ucMBSendFrame[MB_PDU_FUNC_OFF]);
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
}
} else {
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
ESP_LOGD( MB_PORT_TAG, "%s: Packet data receive failed (addr=%u)(%u).",
__func__, ucRcvAddress, eStatus);
}
} else {
// Ignore the `EV_MASTER_FRAME_RECEIVED` event because the respond timeout occurred
// and this is likely respond to previous transaction
ESP_LOGE( MB_PORT_TAG, "Drop data received outside of transaction.");
}
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_FRAME_RECEIVED );
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_EXECUTE ) ) {
if ( !ucMBFrame )
{
return MB_EILLSTATE;
}
MB_PORT_CHECK(ucMBRcvFrame, MB_EILLSTATE, "receive buffer initialization fail.");
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_EXECUTE", __func__);
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
ucFunctionCode = ucMBRcvFrame[MB_PDU_FUNC_OFF];
eException = MB_EX_ILLEGAL_FUNCTION;
/* If receive frame has exception. The receive function code highest bit is 1.*/
if (ucFunctionCode & MB_FUNC_ERROR)
{
eException = (eMBException)ucMBFrame[MB_PDU_DATA_OFF];
eException = (eMBException)ucMBRcvFrame[MB_PDU_DATA_OFF];
} else {
for ( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
@ -396,12 +417,12 @@ eMBMasterPoll( void )
for(j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++)
{
vMBMasterSetDestAddress(j);
eException = xMasterFuncHandlers[i].pxHandler(ucMBFrame, &usLength);
eException = xMasterFuncHandlers[i].pxHandler(ucMBRcvFrame, &usLength);
}
}
else
{
eException = xMasterFuncHandlers[i].pxHandler( ucMBFrame, &usLength );
eException = xMasterFuncHandlers[i].pxHandler( ucMBRcvFrame, &usLength );
}
vMBMasterSetCBRunInMasterMode( FALSE );
break;
@ -427,20 +448,20 @@ eMBMasterPoll( void )
ESP_LOGD( MB_PORT_TAG, "%s:EV_MASTER_ERROR_PROCESS", __func__ );
/* Execute specified error process callback function. */
errorType = eMBMasterGetErrorType( );
vMBMasterGetPDUSndBuf( &ucMBFrame );
vMBMasterGetPDUSndBuf( &ucMBSendFrame );
switch ( errorType )
{
case EV_ERROR_RESPOND_TIMEOUT:
vMBMasterErrorCBRespondTimeout( ucMBMasterGetDestAddress( ),
ucMBFrame, usMBMasterGetPDUSndLength( ) );
ucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_RECEIVE_DATA:
vMBMasterErrorCBReceiveData( ucMBMasterGetDestAddress( ),
ucMBFrame, usMBMasterGetPDUSndLength( ) );
ucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_EXECUTE_FUNCTION:
vMBMasterErrorCBExecuteFunction( ucMBMasterGetDestAddress( ),
ucMBFrame, usMBMasterGetPDUSndLength( ) );
ucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_OK:
vMBMasterCBRequestSuccess( );
@ -449,6 +470,7 @@ eMBMasterPoll( void )
ESP_LOGE( MB_PORT_TAG, "%s: incorrect error type = %d.", __func__, errorType);
break;
}
vMBMasterPortTimersDisable( );
vMBMasterSetErrorType( EV_ERROR_INIT );
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_ERROR_PROCESS );
vMBMasterRunResRelease( );
@ -501,7 +523,7 @@ void IRAM_ATTR vMBMasterSetErrorType( eMBMasterErrorEventType errorType )
/* Get Modbus Master send PDU's buffer address pointer.*/
void vMBMasterGetPDUSndBuf( UCHAR ** pucFrame )
{
*pucFrame = ( UCHAR * ) &ucMasterSndBuf[MB_SER_PDU_PDU_OFF];
*pucFrame = ( UCHAR * ) &ucMasterSndBuf[MB_SEND_BUF_PDU_OFF];
}
/* Set Modbus Master send PDU's buffer length.*/
@ -535,8 +557,15 @@ BOOL MB_PORT_ISR_ATTR xMBMasterRequestIsBroadcast( void )
}
/* The master request is broadcast? */
void vMBMasterRequestSetType( BOOL xIsBroadcast ){
void vMBMasterRequestSetType( BOOL xIsBroadcast )
{
xFrameIsBroadcast = xIsBroadcast;
}
// Get Modbus Master communication mode.
eMBMode ucMBMasterGetCommMode(void)
{
return eMBMasterCurrentMode;
}
#endif // MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
@ -32,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,

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
@ -148,26 +155,33 @@ eMBErrorCode
eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBRTUFrame = ( UCHAR* ) ucRTUBuf;
USHORT usFrameLength = usRcvBufferPos;
if( xMBPortSerialGetRequest( &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
{
@ -184,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;
@ -207,13 +220,19 @@ eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
/* Activate the transmitter. */
eSndState = STATE_TX_XMIT;
EXIT_CRITICAL_SECTION( );
if( xMBPortSerialSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
vMBPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
@ -265,7 +284,7 @@ xMBRTUReceiveFSM( void )
case STATE_RX_RCV:
if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )
{
if ( xStatus ) {
if( xStatus ) {
ucRTUBuf[usRcvBufferPos++] = ucByte;
}
}

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2013 China Beijing Armink <armink.ztl@gmail.com>
@ -154,26 +161,34 @@ eMBErrorCode
eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBRTUFrame = ( UCHAR* ) ucMasterRTURcvBuf;
USHORT usFrameLength = usMasterRcvBufferPos;
if( xMBMasterPortSerialGetResponse( &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
{
@ -192,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;
@ -210,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 ( xMBMasterPortSerialSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
// The place to enable RS485 driver
vMBMasterPortSerialEnable( FALSE, TRUE );
}
@ -222,7 +243,6 @@ eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
@ -266,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
@ -289,7 +313,9 @@ xMBMasterRTUReceiveFSM( void )
{
eRcvState = STATE_M_RX_ERROR;
}
#if CONFIG_FMB_TIMER_PORT_ENABLED
vMBMasterPortTimersT35Enable( );
#endif
break;
}
return xStatus;
@ -324,7 +350,8 @@ xMBMasterRTUTransmitFSM( void )
}
else
{
xFrameIsBroadcast = ( ucMasterRTUSndBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
xFrameIsBroadcast = ( ucMasterRTUSndBuf[MB_SEND_BUF_PDU_OFF - MB_SER_PDU_PDU_OFF]
== MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
vMBMasterRequestSetType( xFrameIsBroadcast );
eSndState = STATE_M_TX_XFWR;
/* If the frame is broadcast ,master will enable timer of convert delay,

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,10 @@
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>

View File

@ -38,6 +38,7 @@
/* ----------------------- Modbus includes ----------------------------------*/
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "sys/lock.h"
#include "port.h"
@ -72,6 +73,61 @@ vMBPortSetMode( UCHAR ucMode )
EXIT_CRITICAL_SECTION();
}
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
BOOL xMBPortSerialWaitEvent(QueueHandle_t xMbUartQueue, uart_event_t* pxEvent, ULONG xTimeout)
{
BOOL xResult = (BaseType_t)xQueueReceive(xMbUartQueue, (void*)pxEvent, (TickType_t) xTimeout);
ESP_LOGD(MB_PORT_TAG, "%s, UART event: %d ", __func__, pxEvent->type);
return xResult;
}
#endif
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
/*
* The function is called from ASCII/RTU module to get processed data buffer. Sets the
* received buffer and its length using parameters.
*/
__attribute__ ((weak))
BOOL xMBMasterPortSerialGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength )
{
ESP_LOGD(MB_PORT_TAG, " %s default", __func__);
return TRUE;
}
/*
* The function is called from ASCII/RTU module to set processed data buffer
* to be sent in transmitter state machine.
*/
__attribute__ ((weak))
BOOL xMBMasterPortSerialSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength )
{
ESP_LOGD(MB_PORT_TAG, "%s default", __func__);
return TRUE;
}
#endif
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
__attribute__ ((weak))
BOOL xMBPortSerialGetRequest( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength )
{
ESP_LOGD(MB_PORT_TAG, "%s default", __func__);
return TRUE;
}
__attribute__ ((weak))
BOOL xMBPortSerialSendResponse( UCHAR *pucMBSerialFrame, USHORT usSerialLength )
{
ESP_LOGD(MB_PORT_TAG, "%s default", __func__);
return TRUE;
}
#endif
#if MB_TCP_DEBUG
// This function is kept to realize legacy freemodbus frame logging functionality

View File

@ -38,7 +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
@ -86,6 +97,7 @@
// Define number of timer reloads per 1 mS
#define MB_TIMER_TICS_PER_MS (20UL)
#define MB_TIMER_TICK_TIME_US (1000 / MB_TIMER_TICS_PER_MS) // 50uS = one discreet for timer
#define MB_TCP_DEBUG (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) // Enable legacy debug output in TCP module.
@ -141,6 +153,12 @@ typedef enum {
MB_PORT_IPV6 = 1 /*!< TCP IPV6 addressing */
} eMBPortIpVer;
typedef struct {
esp_timer_handle_t xTimerIntHandle;
USHORT usT35Ticks;
BOOL xTimerState;
} xTimerContext_t;
void vMBPortEnterCritical(void);
void vMBPortExitCritical(void);
@ -166,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 */

View File

@ -1,16 +1,9 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* SPDX-License-Identifier: BSD-3-Clause
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port Demo Application
@ -54,7 +47,7 @@
#include "sdkconfig.h"
#include "port_serial_slave.h"
/* ----------------------- Variables ----------------------------------------*/
static xQueueHandle xQueueHdl;
static QueueHandle_t xQueueHdl;
#define MB_EVENT_QUEUE_SIZE (6)
#define MB_EVENT_QUEUE_TIMEOUT (pdMS_TO_TICKS(CONFIG_FMB_EVENT_QUEUE_TIMEOUT))
@ -120,7 +113,7 @@ xMBPortEventGet(eMBEventType * peEvent)
return xEventHappened;
}
xQueueHandle
QueueHandle_t
xMBPortEventGetHandle(void)
{
if(xQueueHdl != NULL)

View File

@ -187,13 +187,16 @@ BOOL xMBMasterRunResTake( LONG lTimeOut )
/**
* This function is release Modbus Master running resource.
* Note:The resource is define by Operating System.If you not use OS this function can be empty.
* Note:The resource is define by Operating System. If you not use OS this function can be empty.
*/
void vMBMasterRunResRelease( void )
{
EventBits_t uxBits = xEventGroupSetBits( xResourceMasterHdl, MB_EVENT_RESOURCE );
MB_PORT_CHECK((uxBits == MB_EVENT_RESOURCE), ; , "Resource release failure.");
ESP_LOGD(MB_PORT_TAG,"%s: Release resource (%x).", __func__, uxBits);
if (uxBits != MB_EVENT_RESOURCE) {
// The returned resource mask may be = 0, if the task waiting for it is unblocked.
// This is not an error but expected behavior.
ESP_LOGD(MB_PORT_TAG,"%s: Release resource (%x) fail.", __func__, uxBits);
}
}
/**

View File

@ -1,16 +1,9 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* SPDX-License-Identifier: BSD-3-Clause
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Demo Application

View File

@ -1,16 +1,9 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2006 Christian Walter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* SPDX-License-Identifier: BSD-3-Clause
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Demo Application

View File

@ -1,16 +1,9 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* SPDX-License-Identifier: BSD-3-Clause
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port
@ -40,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
@ -92,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);
}
@ -133,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
@ -142,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);
@ -226,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);

View File

@ -52,6 +52,9 @@
#include "port_serial_master.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_SERIAL_RX_SEMA_TOUT_MS (1000)
#define MB_SERIAL_RX_SEMA_TOUT (pdMS_TO_TICKS(MB_SERIAL_RX_SEMA_TOUT_MS))
#define MB_SERIAL_RX_FLUSH_RETRY (2)
/* ----------------------- Static variables ---------------------------------*/
static const CHAR *TAG = "MB_MASTER_SERIAL";
@ -66,16 +69,67 @@ static UCHAR ucUartNumber = UART_NUM_MAX - 1;
static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag
static SemaphoreHandle_t xMasterSemaRxHandle; // Rx blocking semaphore handle
static BOOL xMBMasterPortRxSemaInit( void )
{
xMasterSemaRxHandle = xSemaphoreCreateBinary();
MB_PORT_CHECK((xMasterSemaRxHandle != NULL), FALSE , "%s: RX semaphore create failure.", __func__);
return TRUE;
}
static BOOL xMBMasterPortRxSemaTake( LONG lTimeOut )
{
BaseType_t xStatus = pdTRUE;
xStatus = xSemaphoreTake(xMasterSemaRxHandle, lTimeOut );
MB_PORT_CHECK((xStatus == pdTRUE), FALSE , "%s: RX semaphore take failure.", __func__);
ESP_LOGV(MB_PORT_TAG,"%s:Take RX semaphore (%lu ticks).", __func__, lTimeOut);
return TRUE;
}
static void vMBMasterRxSemaRelease( void )
{
BaseType_t xStatus = pdFALSE;
xStatus = xSemaphoreGive(xMasterSemaRxHandle);
if (xStatus != pdTRUE) {
ESP_LOGD(MB_PORT_TAG,"%s:RX semaphore is free.", __func__);
}
}
static BOOL vMBMasterRxSemaIsBusy( void )
{
BaseType_t xStatus = pdFALSE;
xStatus = (uxSemaphoreGetCount(xMasterSemaRxHandle) == 0) ? TRUE : FALSE;
return xStatus;
}
void vMBMasterRxFlush( void )
{
size_t xSize = 1;
esp_err_t xErr = ESP_OK;
for (int xCount = 0; (xCount < MB_SERIAL_RX_FLUSH_RETRY) && xSize; xCount++) {
xErr = uart_get_buffered_data_len(ucUartNumber, &xSize);
MB_PORT_CHECK((xErr == ESP_OK), ; , "mb flush serial fail, error = 0x%x.", xErr);
BaseType_t xStatus = xQueueReset(xMbUartQueue);
if (xStatus) {
xErr = uart_flush_input(ucUartNumber);
MB_PORT_CHECK((xErr == ESP_OK), ; , "mb flush serial fail, error = 0x%x.", xErr);
}
}
}
void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
{
// This function can be called from xMBRTUTransmitFSM() of different task
if (bTxEnable) {
vMBMasterRxFlush();
bTxStateEnabled = TRUE;
} else {
bTxStateEnabled = FALSE;
}
if (bRxEnable) {
bRxStateEnabled = TRUE;
vMBMasterRxSemaRelease();
vTaskResume(xMbTaskHandle); // Resume receiver task
} else {
vTaskSuspend(xMbTaskHandle); // Block receiver task
@ -85,17 +139,26 @@ void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
static USHORT usMBMasterPortSerialRxPoll(size_t xEventSize)
{
BOOL xReadStatus = TRUE;
BOOL xStatus = TRUE;
USHORT usCnt = 0;
if (bRxStateEnabled) {
while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) {
xStatus = xMBMasterPortRxSemaTake(MB_SERIAL_RX_SEMA_TOUT);
if (xStatus) {
while(xStatus && (usCnt++ <= xEventSize)) {
// Call the Modbus stack callback function and let it fill the stack buffers.
xReadStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM
xStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM
}
// The buffer is transferred into Modbus stack and is not needed here any more
uart_flush_input(ucUartNumber);
ESP_LOGD(TAG, "Received data: %d(bytes in buffer)\n", (uint32_t)usCnt);
#if !CONFIG_FMB_TIMER_PORT_ENABLED
vMBMasterSetCurTimerMode(MB_TMODE_T35);
xStatus = pxMBMasterPortCBTimerExpired();
if (!xStatus) {
xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
ESP_LOGD(TAG, "Send additional RX ready event.");
}
#endif
} else {
ESP_LOGE(TAG, "%s: bRxState disabled but junk data (%d bytes) received. ", __func__, xEventSize);
}
@ -129,7 +192,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
@ -138,11 +201,17 @@ 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) {
// Response is received but previous packet processing is pending
// Do not wait completion of processing and just discard received data as incorrect
if (vMBMasterRxSemaIsBusy()) {
vMBMasterRxFlush();
break;
}
// 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);
// Block receiver task until data is not processed
vTaskSuspend(NULL);
}
break;
//Event of HW FIFO overflow detected
@ -242,7 +311,8 @@ BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
// Set always timeout flag to trigger timeout interrupt even after rx fifo full
uart_set_always_rx_timeout(ucUartNumber, true);
MB_PORT_CHECK((xMBMasterPortRxSemaInit()), FALSE,
"mb serial RX semaphore create fail.");
// Create a task to handle UART events
BaseType_t xStatus = xTaskCreatePinnedToCore(vUartTask, "uart_queue_task",
MB_SERIAL_TASK_STACK_SIZE,

View File

@ -0,0 +1,127 @@
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port Demo Application
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
*/
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "sdkconfig.h"
#if CONFIG_FMB_TIMER_PORT_ENABLED
static const char *TAG = "MBS_TIMER";
static xTimerContext_t* pxTimerContext = NULL;
/* ----------------------- Start implementation -----------------------------*/
static void IRAM_ATTR vTimerAlarmCBHandler(void *param)
{
pxMBPortCBTimerExpired(); // Timer expired callback function
pxTimerContext->xTimerState = TRUE;
ESP_EARLY_LOGD(TAG, "Slave timeout triggered.");
}
#endif
BOOL xMBPortTimersInit(USHORT usTimeOut50us)
{
#if CONFIG_FMB_TIMER_PORT_ENABLED
MB_PORT_CHECK((usTimeOut50us > 0), FALSE,
"Modbus timeout discreet is incorrect.");
MB_PORT_CHECK(!pxTimerContext, FALSE,
"Modbus timer is already created.");
pxTimerContext = calloc(1, sizeof(xTimerContext_t));
if (!pxTimerContext) {
return FALSE;
}
pxTimerContext->xTimerIntHandle = NULL;
// Save timer reload value for Modbus T35 period
pxTimerContext->usT35Ticks = usTimeOut50us;
esp_timer_create_args_t xTimerConf = {
.callback = vTimerAlarmCBHandler,
.arg = NULL,
#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,
#endif
.name = "MBS_T35timer"
};
// Create Modbus timer
esp_err_t xErr = esp_timer_create(&xTimerConf, &(pxTimerContext->xTimerIntHandle));
if (xErr) {
return FALSE;
}
#endif
return TRUE;
}
void vMBPortTimersEnable(void)
{
#if CONFIG_FMB_TIMER_PORT_ENABLED
MB_PORT_CHECK((pxTimerContext && pxTimerContext->xTimerIntHandle), ; ,
"timer is not initialized.");
uint64_t xToutUs = (pxTimerContext->usT35Ticks * MB_TIMER_TICK_TIME_US);
esp_timer_stop(pxTimerContext->xTimerIntHandle);
esp_timer_start_once(pxTimerContext->xTimerIntHandle, xToutUs);
pxTimerContext->xTimerState = FALSE;
#endif
}
void MB_PORT_ISR_ATTR
vMBPortTimersDisable(void)
{
#if CONFIG_FMB_TIMER_PORT_ENABLED
// Disable timer alarm
esp_timer_stop(pxTimerContext->xTimerIntHandle);
#endif
}
void vMBPortTimerClose(void)
{
#if CONFIG_FMB_TIMER_PORT_ENABLED
// Delete active timer
if (pxTimerContext) {
if (pxTimerContext->xTimerIntHandle) {
esp_timer_stop(pxTimerContext->xTimerIntHandle);
esp_timer_delete(pxTimerContext->xTimerIntHandle);
}
free(pxTimerContext);
pxTimerContext = NULL;
}
#endif
}

View File

@ -0,0 +1,152 @@
/*
* SPDX-FileCopyrightText: 2013 Armink
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: porttimer_m.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions$
*/
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbport.h"
#include "sdkconfig.h"
static const char *TAG = "MBM_TIMER";
/* ----------------------- Variables ----------------------------------------*/
static xTimerContext_t* pxTimerContext = NULL;
/* ----------------------- Start implementation -----------------------------*/
static void IRAM_ATTR vTimerAlarmCBHandler(void *param)
{
pxMBMasterPortCBTimerExpired(); // Timer expired callback function
pxTimerContext->xTimerState = TRUE;
ESP_EARLY_LOGD(TAG, "Timer mode: (%d) triggered", xMBMasterGetCurTimerMode());
}
BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
{
MB_PORT_CHECK((usTimeOut50us > 0), FALSE,
"Modbus timeout discreet is incorrect.");
MB_PORT_CHECK(!pxTimerContext, FALSE,
"Modbus timer is already created.");
pxTimerContext = calloc(1, sizeof(xTimerContext_t));
if (!pxTimerContext) {
return FALSE;
}
pxTimerContext->xTimerIntHandle = NULL;
// Save timer reload value for Modbus T35 period
pxTimerContext->usT35Ticks = usTimeOut50us;
esp_timer_create_args_t xTimerConf = {
.callback = vTimerAlarmCBHandler,
.arg = NULL,
#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,
#endif
.name = "MBM_T35timer"
};
// Create Modbus timer
esp_err_t xErr = esp_timer_create(&xTimerConf, &(pxTimerContext->xTimerIntHandle));
if (xErr) {
return FALSE;
}
return TRUE;
}
// Set timer alarm value
static BOOL xMBMasterPortTimersEnable(uint64_t xToutUs)
{
MB_PORT_CHECK(pxTimerContext && (pxTimerContext->xTimerIntHandle), FALSE,
"timer is not initialized.");
MB_PORT_CHECK((xToutUs > 0), FALSE,
"incorrect tick value for timer = (0x%llu).", xToutUs);
esp_timer_stop(pxTimerContext->xTimerIntHandle);
esp_timer_start_once(pxTimerContext->xTimerIntHandle, xToutUs);
pxTimerContext->xTimerState = FALSE;
return TRUE;
}
void vMBMasterPortTimersT35Enable(void)
{
#if CONFIG_FMB_TIMER_PORT_ENABLED
uint64_t xToutUs = (pxTimerContext->usT35Ticks * MB_TIMER_TICK_TIME_US);
// Set current timer mode, don't change it.
vMBMasterSetCurTimerMode(MB_TMODE_T35);
// Set timer alarm
(void)xMBMasterPortTimersEnable(xToutUs);
#endif
}
void vMBMasterPortTimersConvertDelayEnable(void)
{
// Covert time in milliseconds into ticks
uint64_t xToutUs = (MB_MASTER_DELAY_MS_CONVERT * 1000);
// Set current timer mode
vMBMasterSetCurTimerMode(MB_TMODE_CONVERT_DELAY);
ESP_LOGD(MB_PORT_TAG,"%s Convert delay enable.", __func__);
(void)xMBMasterPortTimersEnable(xToutUs);
}
void vMBMasterPortTimersRespondTimeoutEnable(void)
{
uint64_t xToutUs = (MB_MASTER_TIMEOUT_MS_RESPOND * 1000);
vMBMasterSetCurTimerMode(MB_TMODE_RESPOND_TIMEOUT);
ESP_LOGD(MB_PORT_TAG,"%s Respond enable timeout.", __func__);
(void)xMBMasterPortTimersEnable(xToutUs);
}
void MB_PORT_ISR_ATTR
vMBMasterPortTimersDisable()
{
// Disable timer alarm
esp_timer_stop(pxTimerContext->xTimerIntHandle);
}
void vMBMasterPortTimerClose(void)
{
// Delete active timer
if (pxTimerContext) {
if (pxTimerContext->xTimerIntHandle) {
esp_timer_stop(pxTimerContext->xTimerIntHandle);
esp_timer_delete(pxTimerContext->xTimerIntHandle);
}
free(pxTimerContext);
pxTimerContext = NULL;
}
}

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_serial_master.c
@ -39,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
@ -110,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);

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_serial_master.h Modbus controller serial master implementation header file

View File

@ -1,16 +1,9 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* SPDX-License-Identifier: BSD-3-Clause
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port Demo Application
@ -46,7 +39,6 @@
/* ----------------------- Platform includes --------------------------------*/
#include "driver/uart.h"
#include "driver/timer.h"
#include "esp_log.h" // for ESP_LOGE macro
#include "mb_m.h"
#include "port.h"

View File

@ -1,18 +1,8 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_serial_slave.c
// Implementation of the Modbus controller serial slave

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_serial_slave.h Modbus controller serial slave implementation header file

View File

@ -1,16 +1,9 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2010 Christian Walter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* SPDX-License-Identifier: BSD-3-Clause
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 Port Demo Application
@ -47,7 +40,6 @@
/* ----------------------- Platform includes --------------------------------*/
#include "driver/uart.h"
#include "driver/timer.h"
#include "esp_log.h"
#include "port.h"

View File

@ -1,16 +1,7 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_tcp_master.c
@ -90,7 +81,7 @@ static void mbc_tcp_master_free_slave_list(void)
// Initialize interface properties
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
LIST_FOREACH(it, &mbm_opts->mbm_slave_list, entries) {
while ((it = LIST_FIRST(&mbm_opts->mbm_slave_list))) {
LIST_REMOVE(it, entries);
mbm_opts->mbm_slave_list_count--;
free(it);
@ -168,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;
@ -181,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;
}

View File

@ -1,17 +1,8 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_tcp_master.h Modbus controller TCP master implementation header file

View File

@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 TCP Port
@ -281,23 +281,27 @@ static int vMBTCPPortMasterRxCheck(int xSd, fd_set* pxFdSet, int xTimeMs)
return xRes;
}
static int xMBTCPPortMasterGetBuf(MbSlaveInfo_t* pxInfo, UCHAR* pucDstBuf, USHORT usLength)
static int xMBTCPPortMasterGetBuf(MbSlaveInfo_t* pxInfo, UCHAR* pucDstBuf, USHORT usLength, uint16_t xTimeMs)
{
int xLength = 0;
UCHAR* pucBuf = pucDstBuf;
USHORT usBytesLeft = usLength;
struct timeval xTime;
MB_PORT_CHECK((pxInfo && pxInfo->xSockId > -1), -1, "Try to read incorrect socket = #%d.", pxInfo->xSockId);
// Set receive timeout for socket <= slave respond time
xTime.tv_sec = xTimeMs / 1000;
xTime.tv_usec = (xTimeMs % 1000) * 1000;
setsockopt(pxInfo->xSockId, SOL_SOCKET, SO_RCVTIMEO, &xTime, sizeof(xTime));
// Receive data from connected client
while (usBytesLeft > 0) {
xMBTCPPortMasterCheckShutdown();
// none blocking read from socket with timeout
xLength = recv(pxInfo->xSockId, pucBuf, usBytesLeft, MSG_DONTWAIT);
xLength = recv(pxInfo->xSockId, pucBuf, usBytesLeft, 0);
if (xLength < 0) {
if (errno == EAGAIN) {
// Read timeout occurred, continue reading
continue;
// Read timeout occurred, check the timeout and return
} else if (errno == ENOTCONN) {
// Socket connection closed
ESP_LOGE(TAG, "Socket(#%d)(%s) connection closed.",
@ -316,7 +320,6 @@ static int xMBTCPPortMasterGetBuf(MbSlaveInfo_t* pxInfo, UCHAR* pucDstBuf, USHOR
if (xMBTCPPortMasterGetRespTimeLeft(pxInfo) == 0) {
return ERR_TIMEOUT;
}
vTaskDelay(1);
}
return usLength;
}
@ -331,7 +334,8 @@ static int vMBTCPPortMasterReadPacket(MbSlaveInfo_t* pxInfo)
if (pxInfo) {
MB_PORT_CHECK((pxInfo->xSockId > 0), -1, "Try to read incorrect socket = #%d.", pxInfo->xSockId);
// Read packet header
xRet = xMBTCPPortMasterGetBuf(pxInfo, &pxInfo->pucRcvBuf[0], MB_TCP_UID);
xRet = xMBTCPPortMasterGetBuf(pxInfo, &pxInfo->pucRcvBuf[0],
MB_TCP_UID, xMBTCPPortMasterGetRespTimeLeft(pxInfo));
if (xRet < 0) {
pxInfo->xRcvErr = xRet;
return xRet;
@ -344,7 +348,8 @@ static int vMBTCPPortMasterReadPacket(MbSlaveInfo_t* pxInfo)
// If we have received the MBAP header we can analyze it and calculate
// the number of bytes left to complete the current request.
xLength = (int)MB_TCP_GET_FIELD(pxInfo->pucRcvBuf, MB_TCP_LEN);
xRet = xMBTCPPortMasterGetBuf(pxInfo, &pxInfo->pucRcvBuf[MB_TCP_UID], xLength);
xRet = xMBTCPPortMasterGetBuf(pxInfo, &pxInfo->pucRcvBuf[MB_TCP_UID],
xLength, xMBTCPPortMasterGetRespTimeLeft(pxInfo));
if (xRet < 0) {
pxInfo->xRcvErr = xRet;
return xRet;
@ -847,12 +852,13 @@ static void vMBTCPPortMasterTask(void *pvParameters)
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
} else if ((xRet == ERR_TIMEOUT) || (xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo) == 0)) {
// Timeout occurred when receiving frame, process respond timeout
xMBTCPPortMasterFsmSetError(EV_ERROR_RESPOND_TIMEOUT, EV_MASTER_ERROR_PROCESS);
ESP_LOGD(TAG, MB_SLAVE_FMT(", frame read timeout."),
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
} else if (xRet == ERR_BUF) {
// After retries a response with incorrect TID received, process failure.
xMBTCPPortMasterFsmSetError(EV_ERROR_RECEIVE_DATA, EV_MASTER_ERROR_PROCESS);
ESP_LOGD(TAG, MB_SLAVE_FMT(", frame error."),
ESP_LOGW(TAG, MB_SLAVE_FMT(", frame error."),
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
} else {
ESP_LOGE(TAG, MB_SLAVE_FMT(", critical error=%d."),

View File

@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-3-Clause
*
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
/*
* FreeModbus Libary: ESP32 TCP Port

View File

@ -1,17 +1,8 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_tcp_slave.c
// Implementation of the Modbus controller TCP slave

View File

@ -1,17 +1,8 @@
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_tcp_slave.h Modbus controller TCP slave implementation header file

Some files were not shown because too many files have changed in this diff Show More