Compare commits

...

63 Commits

Author SHA1 Message Date
b9140da683 fix timer use isr dispatch method 2023-09-29 11:11:52 +00:00
6cdcc8e337 change version of the component for release 2023-09-29 11:11:52 +00:00
38f64e5299 Merge branch 'bugfix/fix_master_event_close_sequence' into 'master'
fix master event close issue

Closes IDFGH-11055

See merge request idf/esp-modbus!45
2023-09-29 18:13:32 +08:00
7a19da9016 fix master event close issue 2023-09-26 14:22:34 +00:00
5fa0b710d1 Merge branch 'feature/change_test_env_image' into 'master'
fix test image and configuration for all esp-idf builds

Closes IDF-8272

See merge request idf/esp-modbus!48
2023-09-26 22:21:23 +08:00
10618c3d64 fix test image and configuration for all esp-idf builds 2023-09-26 22:21:23 +08:00
d311198e26 Merge branch 'bugfix/modbus_master_fix_event_loop_and_kconfig' into 'master'
* Added modbus support for target esp32h2 (IDF-7461)
* Fix master rtu, ascii deadlock when null received with CONFIG_FMB_TIMER_PORT_ENABLED enabled (IDFGH-9942)
* Fix Wno-format issues (IDF-6437)
* Fix incorrectly removed option CONFIG_FMB_TIMER_ISR_IN_IRAM (IDFGH-9807)
* Fix main state machine to handle timestamps of events posted

Closes IDFGH-10168

See merge request idf/esp-modbus!42
2023-06-20 02:38:53 +08:00
bf34b5bb8d modbus master fix event loop and kconfig 2023-06-20 02:38:53 +08:00
2d79f8cc67 Merge branch 'bugfix/fix_modbus_examples_print_format' into 'master'
esp_modbus: Fix Wno-format issues

See merge request idf/esp-modbus!39
2023-06-19 02:26:01 +08:00
906a437248 esp_modbus: Fix Wno-format issues 2023-06-15 16:05:44 +00:00
680d2cb4a8 Merge branch 'bugfix/master_rtu_ascii_fix_deadlock_when_null_received' into 'master'
Bugfix/master rtu ascii fix deadlock when null received

Closes IDFGH-9942

See merge request idf/esp-modbus!40
2023-06-15 23:43:37 +08:00
7cdafc2468 Bugfix/master rtu ascii fix deadlock when null received 2023-06-15 23:43:37 +08:00
e943cec30f Merge branch 'bugfix/modbus_fix_timer_in_iram_option' into 'master'
fix incorrectly removed option CONFIG_FMB_TIMER_ISR_IN_IRAM

Closes IDFGH-9807

See merge request idf/esp-modbus!38
2023-06-15 22:47:41 +08:00
7183cc5f52 fix incorrectly removed option CONFIG_FMB_TIMER_ISR_IN_IRAM 2023-06-15 14:57:29 +02:00
cd34b914d3 Merge branch 'feture/modbus_add_support_esp32h2' into 'master'
modbus add support for target esp32h2

See merge request idf/esp-modbus!41
2023-06-14 15:01:13 +08:00
52b692293b modbus add support for esp32h2 2023-06-14 15:01:13 +08:00
7d16826aef Merge branch 'bugfix/clean_build_artifacts_before_deploy' into 'master'
skip build artifacts during component deploying

See merge request idf/esp-modbus!37
2023-03-01 22:30:22 +08:00
1741a27a87 skip build artifacts during component deploying 2023-03-01 22:30:21 +08:00
8e3771847f Merge branch 'bugfix/modbus_tcp_fix_uid_settings' into 'master'
modbus tcp fix uid settings

Closes IDFGH-9119, IDFGH-8919, and IDFGH-9436

See merge request idf/esp-modbus!29
2023-02-28 16:29:12 +08:00
2db0cfcf69 modbus tcp fix uid settings 2023-02-28 16:29:12 +08:00
15e24c16b9 Merge branch 'bugfix/serial_master_fix_parity_check' into 'master'
fix master parity flush buffer on fail

Closes IDFGH-9296

See merge request idf/esp-modbus!35
2023-02-28 15:59:08 +08:00
f18a0409a1 fix master parity flush buffer on fail 2023-02-27 15:39:28 +00:00
cea014cad1 Merge branch 'bugfix/fix_stuck_in_init_on_incorrect_data_input' into 'master'
Bugfix/fix stuck in init on incorrect data input

Closes IDFGH-9193

See merge request idf/esp-modbus!30
2023-02-24 22:05:40 +08:00
234fbcf641 Bugfix/fix stuck in init on incorrect data input 2023-02-24 22:05:40 +08:00
a854d3b188 Merge branch 'bugfix/fix_data_shift_issue' into 'master'
bugfix next message shifting

See merge request idf/esp-modbus!31
2023-02-22 21:54:23 +08:00
3d386c942e bugfix next message shifting 2023-02-22 21:54:23 +08:00
f48f361dc6 Merge branch 'bugfix/modbus_add_pytest_support' into 'master'
add pytest testing for esp-modbus

See merge request idf/esp-modbus!20
2023-02-21 23:55:24 +08:00
926b10e7cb add pytest testing for esp-modbus 2023-02-21 23:55:24 +08:00
3f6537fc94 Merge branch 'bugfix/fix_rx_sema_destroy' into 'master'
Modbus fix rx semaphore destroy

See merge request idf/esp-modbus!26
2023-01-12 03:24:58 +08:00
6e1c088e15 change component version 2023-01-11 18:59:42 +00:00
2540becd42 fix rx sema destroy 2023-01-11 18:59:42 +00:00
e491b161d5 Merge branch 'feature/support_for_esp32c6_target' into 'master'
Add support for esp32c6 target

See merge request idf/esp-modbus!28
2023-01-12 02:52:29 +08:00
7a064f6851 Add support for esp32c6 target 2023-01-12 02:52:28 +08:00
aca48fa758 Merge branch 'feature/support_for_esp32c2_target' into 'master'
Add support for esp32c2 target

Closes IDFGH-9062

See merge request idf/esp-modbus!27
2023-01-12 02:10:03 +08:00
cb1a315ee5 Add support for esp32c2 target 2023-01-12 02:10:03 +08:00
0e1fe58fbf Merge branch 'feature/sync_subrepo_with_esp_idf' into 'master'
fix synchronization issues of subrepo in esp-idf

Closes IDFCI-1541

See merge request idf/esp-modbus!23
2022-12-08 22:54:28 +08:00
e18c4fb329 fix synchronization issues of subrepo in esp-idf 2022-12-08 22:54:28 +08:00
77c2646f53 Merge branch 'bugfix/freemodbus_fix_dereferencing_to_freed_element_issue' into 'master'
Bugfix/freemodbus fix dereferencing to freed element issue

Closes IDF-5402

See merge request idf/esp-modbus!24
2022-12-08 22:32:29 +08:00
d42604ab82 Bugfix/freemodbus fix dereferencing to freed element issue 2022-12-08 22:32:29 +08:00
8370c9e962 Merge branch 'bugfix/modbus_serial_fix_event_misalingnment' into 'master'
fix modbus serial event misalingnment

Closes IDFGH-5107 and IDFGH-5108

See merge request idf/esp-modbus!9
2022-12-06 22:33:06 +08:00
c5c6a7788d fix modbus serial event misalingnment 2022-12-06 22:33:06 +08:00
9ab2c68e84 Merge branch 'bugfix/fix_resource_release_failure' into 'master'
Bugfix/fix resource release failure

See merge request idf/esp-modbus!21
2022-10-27 15:48:20 +08:00
fc8a50297d Bugfix/fix resource release failure 2022-10-27 15:48:19 +08:00
556d3ad512 Merge branch 'bugfix/master_fix_getting_stuck_with_one_zero_byte_in_response' into 'master'
serial master: fix getting stuck when just one zero byte in response

See merge request idf/esp-modbus!22
2022-10-26 19:52:12 +08:00
b324ba8436 serial master: fix getting stuck when just one zero byte in response 2022-10-26 19:52:11 +08:00
71cc71b3fe Merge branch 'fix/ignore-format-warnings' into 'master'
Fix/ignore format warnings

See merge request idf/esp-modbus!16
2022-07-18 02:40:52 +08:00
5a55930b02 Fix/ignore format warnings 2022-07-18 02:40:52 +08:00
0b1f0d41d9 Merge branch 'bugfix/modbus_fix_crash_when_read_failed_slave' into 'master'
modbus fix WDT reset when reading unresponsive slave

Closes IDFGH-7204

See merge request idf/esp-modbus!10
2022-07-16 03:33:10 +08:00
1795c6476f fix modbus read dead slave cause wdt timeout 2022-07-15 21:17:15 +02:00
28ebfb1712 Merge branch 'bugfix/modbus_tcp_slave_fix_task_affinity' into 'master'
modbus tcp slave fix task affinity

Closes IDFGH-7653

See merge request idf/esp-modbus!12
2022-07-16 01:33:08 +08:00
9f58c2518d modbus slave tcp fix the affinity option missing for server task 2022-07-15 19:18:49 +02:00
40a2161320 Merge branch 'bugfix/modbus_test_fix_include_mac_headers' into 'master'
test fix include mac header

See merge request idf/esp-modbus!13
2022-07-16 01:18:38 +08:00
4bc17965ac test fix include mac header 2022-07-15 19:04:19 +02:00
72c889b2c9 Merge branch 'bugfix/modbus_serial_fsm_fix_address_offset' into 'master'
Bugfix/modbus serial fsm fix address offset

See merge request idf/esp-modbus!18
2022-07-16 01:02:27 +08:00
29254b2b86 master fix send fsm broadcast frame detection 2022-07-15 18:45:51 +02:00
db34247764 Merge branch 'bugfix/fix_docker_image_uses_old_component_manager' into 'master'
fix docker image uses incorrect version of component manager

See merge request idf/esp-modbus!19
2022-07-15 23:52:16 +08:00
a3cae7dca8 fix docker image uses incorrect component manager version 2022-07-15 16:58:00 +02:00
d9397a9c85 fix mdns component dependency 2022-07-12 14:38:55 +02:00
0faa194efd Merge branch 'fix/tcp_protocol_data_corruption' into 'master'
fix tcp layer data corruption

Closes IDFCI-1286

See merge request idf/esp-modbus!14
2022-07-01 15:39:32 +08:00
96316533a6 fix tcp layer data corruption 2022-07-01 15:39:31 +08:00
a9b0ba2eda Merge branch 'bugfix/esp_netif_deps' into 'master'
cmake: Add explicit dependency on esp_netif

See merge request idf/esp-modbus!11
2022-06-23 20:38:36 +08:00
3892c77c26 version: Bump version number to 1.0.2 2022-06-20 09:59:56 +02:00
963c3dc5b0 cmake: Add explicit dependency on esp_netif
esp_netif component is a private requeirements for some freemodbus
sources. This works without declaring a depencency, since esp_netif is
implicitely dependent on esp_event. This will be cleaned up in IDF v5.0.
This change makes the depencency explicit.
2022-06-20 09:53:50 +02:00
82 changed files with 2128 additions and 1061 deletions

View File

@ -1,11 +1,13 @@
stages:
- build
- target_test
- 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"
TEST_DIR: "$CI_PROJECT_DIR/test"
# GitLab-CI environment
GET_SOURCES_ATTEMPTS: "10"
@ -28,58 +30,153 @@ after_script:
# Just for cleaning space, no other causes
- git clean -ffdx
# This template gets expanded multiple times, once for every IDF version.
# IDF version is specified by setting the espressif/idf image tag.
#
# EXAMPLE_TARGETS sets the list of IDF_TARGET values to build examples for.
# It should be equal to the list of targets supported by the specific IDF version.
#
# TEST_TARGETS sets the list of IDF_TARGET values to build the test_app for.
# It should contain only the targets with optimized assembly implementations.
#
.build_template:
stage: build
tags:
- build
- internet
script:
- ./build_all.sh
variables:
EXAMPLE_TARGETS: "esp32"
SIZE_INFO_LOCATION: "${TEST_DIR}/size_info.txt"
IDF_CCACHE_ENABLE: "1"
after_script:
# Show ccache statistics if enabled globally
- test "$CI_CCACHE_STATS" == 1 && test -n "$(which ccache)" && ccache --show-stats || true
dependencies: []
.before_script_build_jobs:
before_script:
- pip install idf-component-manager --upgrade
- pip install "idf_build_apps~=1.0.1"
.check_idf_ver: &check_idf_ver |
export IDF_PATH=$(find /opt -type d -name "*idf*" \
\( -exec test -f '{}/tools/idf.py' \; -and -exec test -f '{}/tools/idf_tools.py' \; \
\) -print -quit)
if [ -z "${IDF_PATH}" ];then
echo "IDF version is not found."
else
cd ${IDF_PATH}
export IDF_DESCRIBE=$(git describe)
export IDF_VERSION=${IDF_DESCRIBE%-*}
echo "ESP-IDF: $IDF_VERSION" >> $TEST_DIR/idf_version_info.txt
echo "ESP-IDF: $IDF_VERSION"
fi
# This template gets expanded multiple times, once for every IDF version.
# IDF version is specified by setting the espressif/idf image tag.
#
# TEST_TARGETS sets the list of IDF_TARGET values to build the test for.
# It should contain only the targets with optimized assembly implementations.
#
.build_pytest_template:
stage: build
extends:
- .build_template
- .before_script_build_jobs
artifacts:
paths:
- "**/build*/size.json"
- "**/build*/build.log"
- "**/build*/build_log.txt"
- "**/build*/*.bin"
- "**/build*/*.elf"
- "**/build*/*.map"
- "**/build*/flasher_args.json"
- "**/build*/flash_project_args"
- "**/build*/config/sdkconfig.json"
- "**/build*/bootloader/*.bin"
- "**/build*/partition_table/*.bin"
- "**/idf_version_info.txt"
- $SIZE_INFO_LOCATION
when: always
expire_in: 3 weeks
script:
# CI specific options start from "--collect-size-info xxx". could ignore when running locally
# The script below will build all test applications defined in environment variable $TEST_TARGETS
- *check_idf_ver
- cd ${TEST_DIR}
- python -m idf_build_apps build -v -p .
--recursive
--target all
--default-build-targets ${TEST_TARGETS}
--config "sdkconfig.ci.*=" --build-dir "build_@t_@w"
--check-warnings
--ignore-warning-file ../tools/ignore_build_warnings.txt
--collect-size-info $SIZE_INFO_LOCATION
--manifest-rootpath .
--manifest-file .build-test-rules.yml
--parallel-count ${CI_NODE_TOTAL:-1}
--parallel-index ${CI_NODE_INDEX:-1}
variables:
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
build_idf_master:
extends: .build_pytest_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
TEST_TARGETS: "esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 esp32h2"
build_idf_v5.0:
extends: .build_pytest_template
image: espressif/idf:release-v5.0
variables:
TEST_TARGETS: "esp32 esp32s2 esp32s3 esp32c2 esp32c3"
build_idf_v4.4:
extends: .build_pytest_template
image: espressif/idf:release-v4.4
variables:
TEST_TARGETS: "esp32 esp32s2 esp32s3 esp32c3"
.target_test_template:
stage: target_test
timeout: 1 hour
variables:
GIT_DEPTH: 1
SUBMODULES_TO_FETCH: "none"
cache:
# Usually do not need submodule-cache in target_test
- key: pip-cache
paths:
- .cache/pip
policy: pull
.before_script_pytest_jobs:
before_script:
# Install pytest-embedded to perform test cases
- pip install --only-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf
.test_template:
extends:
- .before_script_pytest_jobs
tags:
- multi_dut_modbus_${TEST_PORT}
artifacts:
paths:
- "${TEST_DIR}/*/*.log"
- "${TEST_DIR}/*.txt"
- "${TEST_DIR}/*/results_*.xml"
- "${TEST_DIR}/pytest_embedded_log/"
reports:
junit: ${TEST_DIR}/${TEST_PORT}/results_${IDF_TARGET}_${IDF_BRANCH}.xml
when: always
expire_in: 1 week
script:
- cd ${TEST_DIR}/${TEST_PORT}
- echo "Start test for [esp-idf_${IDF_BRANCH}_${IDF_TARGET}_${TEST_PORT}]"
- python -m pytest --junit-xml=${TEST_DIR}/${TEST_PORT}/results_${IDF_TARGET}_${IDF_BRANCH}.xml --target=${IDF_TARGET}
- ls -lh > ${TEST_DIR}/test_dir.txt
target_test:
stage: target_test
image: "$CI_DOCKER_REGISTRY/target-test-env-v5.2:2"
extends: .test_template
needs: [build_idf_master, build_idf_v4.4, build_idf_v5.0]
parallel:
matrix:
- IDF_BRANCH: ["master", "v4.4", "v5.0"]
IDF_TARGET: ["esp32"]
TEST_PORT: ["serial", "tcp"]
after_script: []
build_docs:
stage: build
@ -97,7 +194,7 @@ build_docs:
script:
- cd docs
- pip install -r requirements.txt
- build-docs -l en -t esp32
- ./generate_docs
.deploy_docs_template:
stage: deploy
@ -158,4 +255,3 @@ upload_to_component_manager:
- 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

@ -69,4 +69,6 @@ endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}"
PRIV_INCLUDE_DIRS "${priv_include_dirs}"
REQUIRES ${requires})
REQUIRES ${requires}
PRIV_REQUIRES esp_netif)

18
Kconfig
View File

@ -16,7 +16,7 @@ menu "Modbus configuration"
config FMB_TCP_PORT_MAX_CONN
int "Maximum allowed connections for TCP stack"
range 1 6
range 1 8
default 5
depends on FMB_COMM_MODE_TCP_EN
help
@ -34,6 +34,14 @@ menu "Modbus configuration"
Modbus TCP connection timeout in seconds.
Once expired the current connection with the client will be closed
and Modbus slave will be waiting for new connection to accept.
config FMB_TCP_UID_ENABLED
bool "Modbus TCP enable UID (Unit Identifier) support"
default n
depends on FMB_COMM_MODE_TCP_EN
help
If this option is set the Modbus stack uses UID (Unit Identifier) field in MBAP frame.
Else the UID is ignored by master and slave.
config FMB_COMM_MODE_RTU_EN
bool "Enable Modbus stack support for RTU mode"
@ -49,8 +57,8 @@ menu "Modbus configuration"
config FMB_MASTER_TIMEOUT_MS_RESPOND
int "Slave respond timeout (Milliseconds)"
default 150
range 50 3000
default 3000
range 150 15000
help
If master sends a frame which is not broadcast, it has to wait sometime for slave response.
if slave is not respond in this time, the master will process timeout error.
@ -58,7 +66,7 @@ menu "Modbus configuration"
config FMB_MASTER_DELAY_MS_CONVERT
int "Slave conversion delay (Milliseconds)"
default 200
range 50 400
range 150 2000
help
If master sends a broadcast frame, it has to wait conversion time to delay,
then master can send next frame.
@ -99,7 +107,7 @@ menu "Modbus configuration"
config FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS
int "Response timeout for ASCII communication mode (ms)"
default 1000
range 300 2000
range 200 5000
depends on FMB_COMM_MODE_ASCII_EN
help
This option defines response timeout of slave in milliseconds for ASCII communication mode.

View File

@ -59,10 +59,15 @@ function build_for_targets
echo "${STARS}"
echo "Building in $PWD with CMake for ${IDF_TARGET}"
preview_target=
if [[ ${IDF_TARGET} == "esp32c6" ]]
then
preview_target="--preview"
fi
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}"
idf.py ${preview_target} set-target "${IDF_TARGET}"
fi
idf.py build || die "CMake build in ${PWD} has failed for ${IDF_TARGET}"
idf.py fullclean

View File

@ -1,16 +1,14 @@
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 }
{ name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c2", "esp32c3", "esp32c6", "esp32h2" ] },
],
IDF_TARGETS: [
{ text: "ESP32", value: "esp32"},
{ text: "ESP32-S2", value: "esp32s2"},
{ text: "ESP32-S3", value: "esp32s3"},
{ text: "ESP32-C3", value: "esp32c3"}
{ text: "ESP32-C2", value: "esp32c2"},
{ text: "ESP32-C3", value: "esp32c3"},
{ text: "ESP32-C6", value: "esp32c6"},
{ text: "ESP32-H2", value: "esp32h2"}
]
};

View File

@ -1,3 +1,14 @@
# -*- 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',
@ -12,12 +23,11 @@ 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"]
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']
idf_targets = [ 'esp32' ]
languages = ['en']

27
docs/generate_docs Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash
rm -rf docs
build-docs --target esp32 --language en
# Modifes target field of html files
ELEMENT="<script type='text/javascript'>
window.onload =(function() {
var myAnchor = document.getElementById('target-select');
var mySpan = document.createElement('input');
mySpan.style.float = 'left';
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'all targets';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
})();
</script>"
FILES=$(find . -path "*/_build/en/esp32/html/*.html")
for FILE in ${FILES}
do
echo ${ELEMENT} >> "${FILE}"
done

View File

@ -36,7 +36,7 @@ esp_err_t mbc_master_destroy(void)
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master destroy failure, error=(0x%x).",
error);
(int)error);
return error;
}
@ -53,7 +53,7 @@ esp_err_t mbc_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t*
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master get cid info failure, error=(0x%x).",
error);
(int)error);
return error;
}
@ -73,7 +73,7 @@ esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uin
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master get parameter failure, error=(0x%x) (%s).",
error, esp_err_to_name(error));
(int)error, esp_err_to_name(error));
return error;
}
@ -93,7 +93,7 @@ esp_err_t mbc_master_send_request(mb_param_request_t* request, void* data_ptr)
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master send request failure error=(0x%x) (%s).",
error, esp_err_to_name(error));
(int)error, esp_err_to_name(error));
return ESP_OK;
}
@ -114,7 +114,7 @@ esp_err_t mbc_master_set_descriptor(const mb_parameter_descriptor_t* descriptor,
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master set descriptor failure, error=(0x%x) (%s).",
error, esp_err_to_name(error));
(int)error, esp_err_to_name(error));
return ESP_OK;
}
@ -134,7 +134,7 @@ esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uin
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master set parameter failure, error=(0x%x) (%s).",
error, esp_err_to_name(error));
(int)error, esp_err_to_name(error));
return ESP_OK;
}
@ -154,7 +154,7 @@ esp_err_t mbc_master_setup(void* comm_info)
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master setup failure, error=(0x%x) (%s).",
error, esp_err_to_name(error));
(int)error, esp_err_to_name(error));
return ESP_OK;
}
@ -174,7 +174,7 @@ esp_err_t mbc_master_start(void)
MB_MASTER_CHECK((error == ESP_OK),
error,
"Master start failure, error=(0x%x) (%s).",
error, esp_err_to_name(error));
(int)error, esp_err_to_name(error));
return ESP_OK;
}

View File

@ -69,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);
}
@ -106,7 +106,7 @@ esp_err_t mbc_slave_destroy(void)
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave destroy failure error=(0x%x).",
error);
(int)error);
// Destroy all opened descriptors
mbc_slave_free_descriptors();
free(slave_interface_ptr);
@ -130,7 +130,7 @@ esp_err_t mbc_slave_setup(void* comm_info)
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave setup failure error=(0x%x).",
error);
(int)error);
return error;
}
@ -155,7 +155,7 @@ esp_err_t mbc_slave_start(void)
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave start failure error=(0x%x).",
error);
(int)error);
return error;
}
@ -190,7 +190,7 @@ esp_err_t mbc_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave get parameter info failure error=(0x%x).",
error);
(int)error);
return error;
}
@ -209,7 +209,7 @@ esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data)
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave set descriptor failure error=(0x%x).",
(uint16_t)error);
(int)error);
} else {
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
// Check if the address is already in the descriptor list
@ -252,8 +252,8 @@ static esp_err_t mbc_slave_send_param_info(mb_event_group_t par_type, uint16_t m
par_info.mb_offset = mb_offset;
BaseType_t status = xQueueSend(mbs_opts->mbs_notification_queue_handle, &par_info, MB_PAR_INFO_TOUT);
if (pdTRUE == status) {
ESP_LOGD(TAG, "Queue send parameter info (type, address, size): %d, 0x%.4x, %d",
par_type, (uint32_t)par_address, par_size);
ESP_LOGD(TAG, "Queue send parameter info (type, address, size): %d, 0x%" PRIx32 ", %u",
(int)par_type, (uint32_t)par_address, (unsigned)par_size);
error = ESP_OK;
} else if (errQUEUE_FULL == status) {
ESP_LOGD(TAG, "Parameter queue is overflowed.");
@ -269,7 +269,7 @@ static esp_err_t mbc_slave_send_param_access_notification(mb_event_group_t event
esp_err_t err = ESP_FAIL;
mb_event_group_t bits = (mb_event_group_t)xEventGroupSetBits(mbs_opts->mbs_event_group, (EventBits_t)event);
if (bits & event) {
ESP_LOGD(TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (uint8_t)event);
ESP_LOGD(TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (int)event);
err = ESP_OK;
}
return err;

View File

@ -7,6 +7,7 @@
#ifndef _MB_IFACE_COMMON_H
#define _MB_IFACE_COMMON_H
#include <inttypes.h> // needs to be included for default system types (such as PRIxx)
#include "driver/uart.h" // for UART types
#ifdef __cplusplus
@ -23,11 +24,11 @@ extern "C" {
// if cannot include esp_check then use custom check macro
#define MB_RETURN_ON_FALSE(a, err_code, tag, format, ...) do { \
if (!(a)) { \
ESP_LOGE(tag, "%s(%d): " format, __FUNCTION__, __LINE__ __VA_OPT__(,) __VA_ARGS__); \
return err_code; \
} \
#define MB_RETURN_ON_FALSE(a, err_code, tag, format, ...) do { \
if (!(a)) { \
ESP_LOGE(tag, "%s(%" PRIu32 "): " format, __FUNCTION__, __LINE__ __VA_OPT__(,) __VA_ARGS__); \
return err_code; \
} \
} while(0)
#endif
@ -143,6 +144,7 @@ typedef union {
// TCP/UDP communication structure
struct {
mb_mode_type_t ip_mode; /*!< Modbus communication mode */
uint8_t slave_uid; /*!< Modbus slave address field for UID */
uint16_t ip_port; /*!< Modbus port */
mb_tcp_addr_type_t ip_addr_type; /*!< Modbus address type */
void* ip_addr; /*!< Modbus address table for connection */

View File

@ -19,7 +19,7 @@ extern "C" {
#define MB_MASTER_CHECK(a, err_code, format, ...) MB_RETURN_ON_FALSE(a, err_code, TAG, format __VA_OPT__(,) __VA_ARGS__)
#define MB_MASTER_ASSERT(con) do { \
if (!(con)) { ESP_LOGE(TAG, "assert errno:%d, errno_str: !(%s)", errno, strerror(errno)); assert(0 && #con); } \
if (!(con)) { ESP_LOGE(TAG, "assert errno:%u, errno_str: !(%s)", (unsigned)errno, strerror(errno)); assert(0 && #con); } \
} while (0)
/*!

View File

@ -22,7 +22,7 @@ extern "C" {
#define MB_SLAVE_CHECK(a, err_code, format, ...) MB_RETURN_ON_FALSE(a, err_code, TAG, format __VA_OPT__(,) __VA_ARGS__)
#define MB_SLAVE_ASSERT(con) do { \
if (!(con)) { ESP_LOGE(TAG, "assert errno:%d, errno_str: !(%s)", errno, strerror(errno)); assert(0 && #con); } \
if (!(con)) { ESP_LOGE(TAG, "assert errno:%u, errno_str: !(%s)", (unsigned)errno, strerror(errno)); assert(0 && #con); } \
} while (0)
/**

View File

@ -154,11 +154,11 @@ eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
UCHAR *pucMBASCIIFrame = ( UCHAR* ) ucASCIIBuf;
USHORT usFrameLength = usRcvBufferPos;
if( xMBSerialPortGetRequest( &pucMBASCIIFrame, &usFrameLength ) == FALSE )
if( xMBPortSerialGetRequest( &pucMBASCIIFrame, &usFrameLength ) == FALSE )
{
return MB_EIO;
}
ENTER_CRITICAL_SECTION( );
assert( usFrameLength < MB_SER_PDU_SIZE_MAX );
@ -193,7 +193,7 @@ eMBASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
eMBErrorCode eStatus = MB_ENOERR;
UCHAR usLRC;
/* 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.
@ -217,7 +217,7 @@ eMBASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
eSndState = STATE_TX_START;
EXIT_CRITICAL_SECTION( );
if ( xMBSerialPortSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE )
if ( xMBPortSerialSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}

View File

@ -137,7 +137,7 @@ eMBMasterASCIIStart( void )
ENTER_CRITICAL_SECTION( );
eRcvState = STATE_M_RX_IDLE;
vMBMasterPortSerialEnable( TRUE, FALSE );
vMBMasterPortTimersT35Enable( );
xMBMasterPortEventPost(EV_MASTER_READY);
EXIT_CRITICAL_SECTION( );
}
@ -157,7 +157,7 @@ eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLen
UCHAR *pucMBASCIIFrame = ( UCHAR* ) ucMasterASCIIRcvBuf;
USHORT usFrameLength = usMasterRcvBufferPos;
if( xMBMasterSerialPortGetResponse( &pucMBASCIIFrame, &usFrameLength ) == FALSE )
if( xMBMasterPortSerialGetResponse( &pucMBASCIIFrame, &usFrameLength ) == FALSE )
{
return MB_EIO;
}
@ -181,8 +181,8 @@ eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLen
/* Return the start of the Modbus PDU to the caller. */
*pucFrame = ( UCHAR * ) & pucMBASCIIFrame[MB_SER_PDU_PDU_OFF];
} else {
eStatus = MB_EIO;
} else {
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
@ -219,7 +219,7 @@ eMBMasterASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLengt
eSndState = STATE_M_TX_START;
EXIT_CRITICAL_SECTION( );
if ( xMBMasterSerialPortSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE )
if ( xMBMasterPortSerialSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
@ -274,8 +274,8 @@ xMBMasterASCIIReceiveFSM( void )
usMasterRcvBufferPos = 0;
eBytePos = BYTE_HIGH_NIBBLE;
eRcvState = STATE_M_RX_RCV;
eSndState = STATE_M_TX_IDLE;
}
eSndState = STATE_M_TX_IDLE;
break;
/* A new character is received. If the character is a ':' the input
@ -437,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,

View File

@ -110,7 +110,7 @@ eMBMasterReqReadCoils( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usNCoils, LONG
ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF ] = usNCoils >> 8;
ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] = usNCoils;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
@ -217,7 +217,7 @@ eMBMasterReqWriteCoil( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usCoilData, LO
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF ] = usCoilData >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usCoilData;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
@ -327,7 +327,7 @@ eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr,
*ucMBFrame++ = pucDataBuffer[usRegIndex++];
}
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + ucByteCount );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;

View File

@ -92,7 +92,7 @@ eMBMasterReqReadDiscreteInputs( UCHAR ucSndAddr, USHORT usDiscreteAddr, USHORT u
ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF ] = usNDiscreteIn >> 8;
ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF + 1] = usNDiscreteIn;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;

View File

@ -122,7 +122,7 @@ eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usRe
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF] = usRegData >> 8;
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usRegData ;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
@ -200,7 +200,7 @@ eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr,
*ucMBFrame++ = pusDataBuffer[usRegIndex++] ;
}
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + 2*usNRegs );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
@ -286,7 +286,7 @@ eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRe
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] = usNRegs >> 8;
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] = usNRegs;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;
@ -392,7 +392,7 @@ eMBMasterReqReadWriteMultipleHoldingRegister( UCHAR ucSndAddr,
*ucMBFrame++ = pusDataBuffer[usRegIndex++] ;
}
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READWRITE_SIZE_MIN + 2*usNWriteRegs );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;

View File

@ -93,7 +93,7 @@ eMBMasterReqReadInputRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] = usNRegs >> 8;
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] = usNRegs;
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT );
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT | EV_MASTER_TRANS_START );
eErrStatus = eMBMasterWaitRequestFinish( );
}
return eErrStatus;

View File

@ -165,6 +165,7 @@ eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress,
* frame processing is still disabled until eMBEnable( ) is called.
*
* \param usTCPPort The TCP port to listen on.
* \param ucSlaveUid The UID field for slave to listen on.
* \return If the protocol stack has been initialized correctly the function
* returns eMBErrorCode::MB_ENOERR. Otherwise one of the following error
* codes is returned:
@ -172,7 +173,7 @@ eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress,
* slave addresses are in the range 1 - 247.
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
*/
eMBErrorCode eMBTCPInit( USHORT usTCPPort );
eMBErrorCode eMBTCPInit( UCHAR ucSlaveUid, USHORT usTCPPort );
/*! \ingroup modbus
* \brief Release resources used by the protocol stack.

View File

@ -103,7 +103,7 @@ typedef enum
MB_TMODE_T35, /*!< Master receive frame T3.5 timeout. */
MB_TMODE_RESPOND_TIMEOUT, /*!< Master wait respond for slave. */
MB_TMODE_CONVERT_DELAY /*!< Master sent broadcast ,then delay sometime.*/
}eMBMasterTimerMode;
} eMBMasterTimerMode;
/* ----------------------- Function prototypes ------------------------------*/
/*! \ingroup modbus
@ -408,6 +408,7 @@ BOOL xMBMasterRequestIsBroadcast( void );
eMBMasterErrorEventType eMBMasterGetErrorType( void );
void vMBMasterSetErrorType( eMBMasterErrorEventType errorType );
eMBMasterReqErrCode eMBMasterWaitRequestFinish( void );
eMBMode ucMBMasterGetCommMode( void );
/* ----------------------- Callback -----------------------------------------*/

View File

@ -39,6 +39,7 @@
#ifndef _MB_CONFIG_H
#define _MB_CONFIG_H
#include <inttypes.h> // needs to be included for default system types (such as PRIxx)
#include "sdkconfig.h" // for KConfig options
#if __has_include("esp_idf_version.h")
@ -86,6 +87,10 @@ PR_BEGIN_EXTERN_C
#endif
/*! \brief The option is required for support of RTU over TCP.
*/
#define MB_TCP_UID_ENABLED ( CONFIG_FMB_TCP_UID_ENABLED )
/*! \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.
@ -94,6 +99,9 @@ PR_BEGIN_EXTERN_C
#define MB_ASCII_BITS_PER_SYMB ( CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB )
#endif
#define MB_EVENT_QUEUE_SIZE ( CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE )
#define MB_EVENT_QUEUE_TIMEOUT ( pdMS_TO_TICKS( CONFIG_FMB_EVENT_QUEUE_TIMEOUT ) )
/*! \brief The character timeout value for Modbus ASCII.
*
* The character timeout value is not fixed for Modbus ASCII and is therefore
@ -167,7 +175,7 @@ PR_BEGIN_EXTERN_C
#define MB_FUNC_READWRITE_HOLDING_ENABLED ( 1 )
/*! \brief Check the option to place timer handler into IRAM */
#define MB_PORT_TIMER_ISR_IN_IRAM ( CONFIG_FMB_TIMER_ISR_IN_IRAM )
#define MB_PORT_TIMER_ISR_IN_IRAM ( CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD )
/*! @} */
#ifdef __cplusplus

View File

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

@ -50,12 +50,10 @@ PR_BEGIN_EXTERN_C
#define MB_PORT_SERIAL_ISR_FLAG ESP_INTR_FLAG_LOWMED
#endif
#if MB_PORT_TIMER_ISR_IN_IRAM
#if CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD && MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
#define MB_PORT_ISR_ATTR IRAM_ATTR
#define MB_PORT_TIMER_ISR_FLAG ESP_INTR_FLAG_IRAM
#else
#define MB_PORT_ISR_ATTR
#define MB_PORT_TIMER_ISR_FLAG ESP_INTR_FLAG_LOWMED
#endif
/* ----------------------- Type definitions ---------------------------------*/
@ -72,17 +70,18 @@ typedef enum
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
typedef enum {
EV_MASTER_NO_EVENT = 0x0000,
EV_MASTER_READY = 0x0001, /*!< Startup finished. */
EV_MASTER_FRAME_RECEIVED = 0x0002, /*!< Frame received. */
EV_MASTER_EXECUTE = 0x0004, /*!< Execute function. */
EV_MASTER_FRAME_SENT = 0x0008, /*!< Frame sent. */
EV_MASTER_FRAME_TRANSMIT = 0x0010, /*!< Frame transmission. */
EV_MASTER_ERROR_PROCESS = 0x0020, /*!< Frame error process. */
EV_MASTER_PROCESS_SUCCESS = 0x0040, /*!< Request process success. */
EV_MASTER_ERROR_RESPOND_TIMEOUT = 0x0080, /*!< Request respond timeout. */
EV_MASTER_ERROR_RECEIVE_DATA = 0x0100, /*!< Request receive data error. */
EV_MASTER_ERROR_EXECUTE_FUNCTION = 0x0200 /*!< Request execute function error. */
} eMBMasterEventType;
EV_MASTER_TRANS_START = 0x0001, /*!< Transaction start flag */
EV_MASTER_READY = 0x0002, /*!< Startup finished. */
EV_MASTER_FRAME_RECEIVED = 0x0004, /*!< Frame received. */
EV_MASTER_EXECUTE = 0x0008, /*!< Execute function. */
EV_MASTER_FRAME_SENT = 0x0010, /*!< Frame sent. */
EV_MASTER_FRAME_TRANSMIT = 0x0020, /*!< Frame transmission. */
EV_MASTER_ERROR_PROCESS = 0x0040, /*!< Frame error process. */
EV_MASTER_PROCESS_SUCCESS = 0x0080, /*!< Request process success. */
EV_MASTER_ERROR_RESPOND_TIMEOUT = 0x0100, /*!< Request respond timeout. */
EV_MASTER_ERROR_RECEIVE_DATA = 0x0200, /*!< Request receive data error. */
EV_MASTER_ERROR_EXECUTE_FUNCTION = 0x0400 /*!< Request execute function error. */
} eMBMasterEventEnum;
typedef enum {
EV_ERROR_INIT, /*!< No error, initial state. */
@ -91,6 +90,14 @@ typedef enum {
EV_ERROR_EXECUTE_FUNCTION, /*!< Execute function error. */
EV_ERROR_OK /*!< No error, processing completed. */
} eMBMasterErrorEventType;
typedef struct _MbEventType {
eMBMasterEventEnum eEvent; /*!< event itself. */
uint64_t xTransactionId; /*!< ID of the transaction */
uint64_t xPostTimestamp; /*!< timestamp of event posted */
uint64_t xGetTimestamp; /*!< timestamp of event get */
} xMBMasterEventType;
#endif
/*! \ingroup modbus
@ -117,18 +124,21 @@ BOOL xMBPortEventGet( /*@out@ */ eMBEventType * eEvent );
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
BOOL xMBMasterPortEventInit( void );
BOOL xMBMasterPortEventPost( eMBMasterEventType eEvent );
BOOL xMBMasterPortEventPost( eMBMasterEventEnum eEvent );
BOOL xMBMasterPortEventGet( /*@out@ */ eMBMasterEventType * eEvent );
BOOL xMBMasterPortEventGet( /*@out@ */ xMBMasterEventType * eEvent );
eMBMasterEventType
xMBMasterPortFsmWaitConfirmation( eMBMasterEventType eEventMask, ULONG ulTimeout);
eMBMasterEventEnum
xMBMasterPortFsmWaitConfirmation( eMBMasterEventEnum eEventMask, ULONG ulTimeout);
void vMBMasterOsResInit( void );
BOOL xMBMasterRunResTake( LONG time );
void vMBMasterRunResRelease( void );
uint64_t xMBMasterPortGetTransactionId( void );
#endif // MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
/* ----------------------- Serial port functions ----------------------------*/
@ -145,9 +155,9 @@ BOOL xMBPortSerialGetByte( CHAR * pucByte );
BOOL xMBPortSerialPutByte( CHAR ucByte );
BOOL xMBSerialPortGetRequest( UCHAR **ppucMBSerialFrame, USHORT * pusSerialLength ) __attribute__ ((weak));
BOOL xMBPortSerialGetRequest( UCHAR **ppucMBSerialFrame, USHORT * pusSerialLength ) __attribute__ ((weak));
BOOL xMBSerialPortSendResponse( UCHAR *pucMBSerialFrame, USHORT usSerialLength ) __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,
@ -163,9 +173,11 @@ BOOL xMBMasterPortSerialGetByte( CHAR * pucByte );
BOOL xMBMasterPortSerialPutByte( CHAR ucByte );
BOOL xMBMasterSerialPortGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength );
BOOL xMBMasterPortSerialGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength );
BOOL xMBMasterSerialPortSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength );
BOOL xMBMasterPortSerialSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength );
void vMBMasterRxFlush( void );
#endif

View File

@ -62,7 +62,7 @@ PR_BEGIN_EXTERN_C
#define MB_FUNC_OTHER_REPORT_SLAVEID ( 17 )
#define MB_FUNC_ERROR ( 128u )
/* ----------------------- Type definitions ---------------------------------*/
typedef enum
typedef enum
{
MB_EX_NONE = 0x00,
MB_EX_ILLEGAL_FUNCTION = 0x01,

View File

@ -202,11 +202,16 @@ eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eM
#if MB_TCP_ENABLED > 0
eMBErrorCode
eMBTCPInit( USHORT ucTCPPort )
eMBTCPInit( UCHAR ucSlaveUid, USHORT ucTCPPort )
{
eMBErrorCode eStatus = MB_ENOERR;
if( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR )
/* Check preconditions */
if( ucSlaveUid > MB_ADDRESS_MAX )
{
eStatus = MB_EINVAL;
}
else if( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR )
{
eMBState = STATE_DISABLED;
}
@ -222,7 +227,7 @@ eMBTCPInit( USHORT ucTCPPort )
peMBFrameReceiveCur = eMBTCPReceive;
peMBFrameSendCur = eMBTCPSend;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBTCPPortClose : NULL;
ucMBAddress = MB_TCP_PSEUDO_ADDRESS;
ucMBAddress = ucSlaveUid;
eMBCurrentMode = MB_TCP;
eMBState = STATE_DISABLED;
}
@ -371,7 +376,8 @@ eMBPoll( void )
if( eStatus == MB_ENOERR )
{
/* Check if the frame is for us. If not ignore the frame. */
if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST )
|| ( ucRcvAddress == MB_TCP_PSEUDO_ADDRESS ) )
{
( void )xMBPortEventPost( EV_EXECUTE );
ESP_LOG_BUFFER_HEX_LEVEL(MB_PORT_TAG, &ucMBFrame[MB_PDU_FUNC_OFF], usLength, ESP_LOG_DEBUG);
@ -401,8 +407,8 @@ eMBPoll( void )
}
/* If the request was not sent to the broadcast address we
* return a reply. */
if( ucRcvAddress != MB_ADDRESS_BROADCAST )
* return a reply. In case of TCP the slave answers to broadcast address. */
if( ( ucRcvAddress != MB_ADDRESS_BROADCAST ) || ( eMBCurrentMode == MB_TCP ) )
{
if( eException != MB_EX_NONE )
{

View File

@ -36,8 +36,9 @@
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
@ -177,7 +178,7 @@ eMBMasterTCPInit( USHORT ucTCPPort )
// initialize the OS resource for modbus master.
vMBMasterOsResInit();
if( xMBMasterPortTimersInit( MB_MASTER_TIMEOUT_MS_RESPOND * MB_TIMER_TICS_PER_MS ) != TRUE )
if (xMBMasterPortTimersInit(MB_MASTER_TIMEOUT_MS_RESPOND * MB_TIMER_TICS_PER_MS) != TRUE)
{
eStatus = MB_EPORTERR;
}
@ -310,160 +311,177 @@ 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 uint64_t xCurTransactionId = 0;
int i;
int j;
eMBErrorCode eStatus = MB_ENOERR;
eMBMasterEventType eEvent;
xMBMasterEventType xEvent;
eMBMasterErrorEventType errorType;
/* Check if the protocol stack is ready. */
if( eMBState != STATE_ENABLED )
{
if( eMBState != STATE_ENABLED ) {
return MB_EILLSTATE;
}
/* Check if there is a event available. If not return control to caller.
* Otherwise we will handle the event. */
if ( xMBMasterPortEventGet( &eEvent ) == TRUE )
{
while( eEvent ) {
if ( xMBMasterPortEventGet( &xEvent ) == TRUE ) {
switch( xEvent.eEvent ) {
// In some cases it is possible that more than one event set
// together (even from one subset mask) than process them consistently
if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_READY ) ) {
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_READY", __func__);
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_READY );
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_FRAME_TRANSMIT ) ) {
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_FRAME_TRANSMIT", __func__);
case EV_MASTER_READY:
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_READY", xEvent.xTransactionId);
vMBMasterSetErrorType( EV_ERROR_INIT );
vMBMasterRunResRelease( );
break;
case EV_MASTER_FRAME_TRANSMIT:
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_FRAME_TRANSMIT", xEvent.xTransactionId);
/* 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() );
if (eStatus != MB_ENOERR)
{
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);
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);
// 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
{
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_LOGD( MB_PORT_TAG, "%s: Packet data receive failed (addr=%u)(%u).",
__func__, ucRcvAddress, eStatus);
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ":Frame send error = %d", xEvent.xTransactionId, (unsigned)eStatus );
}
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_FRAME_RECEIVED );
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_EXECUTE ) ) {
if ( !ucMBFrame )
{
return MB_EILLSTATE;
xCurTransactionId = xEvent.xTransactionId;
break;
case EV_MASTER_FRAME_SENT:
if (xCurTransactionId == xEvent.xTransactionId) {
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_FRAME_SENT", xEvent.xTransactionId );
ESP_LOG_BUFFER_HEX_LEVEL("POLL sent buffer", (void*)ucMBSendFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
}
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_EXECUTE", __func__);
ucFunctionCode = ucMBFrame[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];
} else {
for ( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
/* No more function handlers registered. Abort. */
if (xMasterFuncHandlers[i].ucFunctionCode == 0)
{
break;
break;
case EV_MASTER_FRAME_RECEIVED:
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_FRAME_RECEIVED", xEvent.xTransactionId );
eStatus = peMBMasterFrameReceiveCur( &ucRcvAddress, &ucMBRcvFrame, &usLength);
if (xCurTransactionId == xEvent.xTransactionId) {
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, "%" PRIu64 ": Packet data received successfully (%u).", xEvent.xTransactionId, (unsigned)eStatus);
ESP_LOG_BUFFER_HEX_LEVEL("POLL receive buffer", (void*)ucMBRcvFrame, (uint16_t)usLength, ESP_LOG_DEBUG);
( 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 );
}
if (xMasterFuncHandlers[i].ucFunctionCode == ucFunctionCode)
{
vMBMasterSetCBRunInMasterMode(TRUE);
/* If master request is broadcast,
* the master need execute function for all slave.
*/
if ( xMBMasterRequestIsBroadcast() )
{
usLength = usMBMasterGetPDUSndLength();
for(j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++)
{
vMBMasterSetDestAddress(j);
eException = xMasterFuncHandlers[i].pxHandler(ucMBFrame, &usLength);
}
}
else
{
eException = xMasterFuncHandlers[i].pxHandler( ucMBFrame, &usLength );
}
vMBMasterSetCBRunInMasterMode( FALSE );
break;
}
}
}
/* If master has exception, will send error process event. Otherwise the master is idle.*/
if ( eException != MB_EX_NONE )
{
vMBMasterSetErrorType( EV_ERROR_EXECUTE_FUNCTION );
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
}
else
{
if ( eMBMasterGetErrorType( ) == EV_ERROR_INIT ) {
vMBMasterSetErrorType(EV_ERROR_OK);
ESP_LOGD( MB_PORT_TAG, "%s: set event EV_ERROR_OK", __func__ );
} else {
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ": Packet data receive failed (addr=%u)(%u).",
xEvent.xTransactionId, (unsigned)ucRcvAddress, (unsigned)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 (%" PRIu64 ")", xEvent.xTransactionId );
}
break;
case EV_MASTER_EXECUTE:
if (xCurTransactionId == xEvent.xTransactionId) {
MB_PORT_CHECK(ucMBRcvFrame, MB_EILLSTATE, "receive buffer initialization fail.");
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_EXECUTE", xEvent.xTransactionId);
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)ucMBRcvFrame[MB_PDU_DATA_OFF];
} else {
for ( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
/* No more function handlers registered. Abort. */
if (xMasterFuncHandlers[i].ucFunctionCode == 0) {
break;
}
if (xMasterFuncHandlers[i].ucFunctionCode == ucFunctionCode) {
vMBMasterSetCBRunInMasterMode(TRUE);
/* If master request is broadcast,
* the master need execute function for all slave.
*/
if ( xMBMasterRequestIsBroadcast() ) {
usLength = usMBMasterGetPDUSndLength();
for(j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++)
{
vMBMasterSetDestAddress(j);
eException = xMasterFuncHandlers[i].pxHandler(ucMBRcvFrame, &usLength);
}
} else {
eException = xMasterFuncHandlers[i].pxHandler( ucMBRcvFrame, &usLength );
}
vMBMasterSetCBRunInMasterMode( FALSE );
break;
}
}
}
/* If master has exception, will send error process event. Otherwise the master is idle.*/
if ( eException != MB_EX_NONE ) {
vMBMasterSetErrorType( EV_ERROR_EXECUTE_FUNCTION );
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
} else {
if ( eMBMasterGetErrorType( ) == EV_ERROR_INIT ) {
vMBMasterSetErrorType(EV_ERROR_OK);
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":set event EV_ERROR_OK", xEvent.xTransactionId );
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
}
}
} else {
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_EXECUTE is expired", xEvent.xTransactionId );
}
break;
case EV_MASTER_ERROR_PROCESS:
if (xCurTransactionId == xEvent.xTransactionId) {
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_ERROR_PROCESS", xEvent.xTransactionId);
/* Execute specified error process callback function. */
errorType = eMBMasterGetErrorType( );
vMBMasterGetPDUSndBuf( &ucMBSendFrame );
switch ( errorType )
{
case EV_ERROR_RESPOND_TIMEOUT:
vMBMasterErrorCBRespondTimeout( ucMBMasterGetDestAddress( ),
ucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_RECEIVE_DATA:
vMBMasterErrorCBReceiveData( ucMBMasterGetDestAddress( ),
ucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_EXECUTE_FUNCTION:
vMBMasterErrorCBExecuteFunction( ucMBMasterGetDestAddress( ),
ucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_OK:
vMBMasterCBRequestSuccess( );
break;
default:
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ":incorrect error type = %d.", xEvent.xTransactionId, (int)errorType);
break;
}
}
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_EXECUTE );
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_ERROR_PROCESS ) ) {
ESP_LOGD( MB_PORT_TAG, "%s:EV_MASTER_ERROR_PROCESS", __func__ );
/* Execute specified error process callback function. */
errorType = eMBMasterGetErrorType( );
vMBMasterGetPDUSndBuf( &ucMBFrame );
switch ( errorType )
{
case EV_ERROR_RESPOND_TIMEOUT:
vMBMasterErrorCBRespondTimeout( ucMBMasterGetDestAddress( ),
ucMBFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_RECEIVE_DATA:
vMBMasterErrorCBReceiveData( ucMBMasterGetDestAddress( ),
ucMBFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_EXECUTE_FUNCTION:
vMBMasterErrorCBExecuteFunction( ucMBMasterGetDestAddress( ),
ucMBFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_OK:
vMBMasterCBRequestSuccess( );
break;
default:
ESP_LOGE( MB_PORT_TAG, "%s: incorrect error type = %d.", __func__, errorType);
break;
}
vMBMasterPortTimersDisable( );
uint64_t xProcTime = xCurTransactionId ? ( xEvent.xPostTimestamp - xCurTransactionId ) : 0;
ESP_LOGD( MB_PORT_TAG, "Transaction (%" PRIu64 "), processing time(us) = %" PRId64, xCurTransactionId, xProcTime );
xCurTransactionId = 0;
vMBMasterSetErrorType( EV_ERROR_INIT );
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_ERROR_PROCESS );
vMBMasterRunResRelease( );
}
break;
default:
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ":Unexpected event triggered 0x%02x.", xEvent.xTransactionId, (int)xEvent.eEvent );
break;
}
} else {
// Something went wrong and task unblocked but there are no any correct events set
ESP_LOGE( MB_PORT_TAG, "%s: Unexpected event triggered 0x%02x.", __func__, eEvent );
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ": Unexpected event triggered 0x%02x.", xEvent.xTransactionId, (int)xEvent.eEvent );
eStatus = MB_EILLSTATE;
}
return eStatus;
@ -472,67 +490,67 @@ eMBMasterPoll( void )
// Get whether the Modbus Master is run in master mode.
BOOL xMBMasterGetCBRunInMasterMode( void )
{
return xMBRunInMasterMode;
return atomic_load(&xMBRunInMasterMode);
}
// Set whether the Modbus Master is run in master mode.
void vMBMasterSetCBRunInMasterMode( BOOL IsMasterMode )
{
xMBRunInMasterMode = IsMasterMode;
atomic_store(&(xMBRunInMasterMode), IsMasterMode);
}
// Get Modbus Master send destination address.
UCHAR ucMBMasterGetDestAddress( void )
{
return ucMBMasterDestAddress;
return atomic_load(&ucMBMasterDestAddress);
}
// Set Modbus Master send destination address.
void vMBMasterSetDestAddress( UCHAR Address )
{
ucMBMasterDestAddress = Address;
atomic_store(&(ucMBMasterDestAddress), Address);
}
// Get Modbus Master current error event type.
eMBMasterErrorEventType inline eMBMasterGetErrorType( void )
{
return eMBMasterCurErrorType;
return atomic_load(&eMBMasterCurErrorType);
}
// Set Modbus Master current error event type.
void IRAM_ATTR vMBMasterSetErrorType( eMBMasterErrorEventType errorType )
{
eMBMasterCurErrorType = errorType;
atomic_store(&(eMBMasterCurErrorType), 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.*/
void vMBMasterSetPDUSndLength( USHORT SendPDULength )
{
usMasterSendPDULength = SendPDULength;
atomic_store(&(usMasterSendPDULength), SendPDULength);
}
/* Get Modbus Master send PDU's buffer length.*/
USHORT usMBMasterGetPDUSndLength( void )
{
return usMasterSendPDULength;
return atomic_load(&usMasterSendPDULength);
}
/* Set Modbus Master current timer mode.*/
void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode )
{
eMasterCurTimerMode = eMBTimerMode;
atomic_store(&(eMasterCurTimerMode), eMBTimerMode);
}
/* Get Modbus Master current timer mode.*/
eMBMasterTimerMode MB_PORT_ISR_ATTR xMBMasterGetCurTimerMode( void )
{
return eMasterCurTimerMode;
return atomic_load(&eMasterCurTimerMode);
}
/* The master request is broadcast? */
@ -542,8 +560,15 @@ BOOL MB_PORT_ISR_ATTR xMBMasterRequestIsBroadcast( void )
}
/* The master request is broadcast? */
void vMBMasterRequestSetType( BOOL xIsBroadcast ){
xFrameIsBroadcast = xIsBroadcast;
void vMBMasterRequestSetType( BOOL xIsBroadcast )
{
atomic_store(&(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

@ -158,7 +158,7 @@ eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
UCHAR *pucMBRTUFrame = ( UCHAR* ) ucRTUBuf;
USHORT usFrameLength = usRcvBufferPos;
if( xMBSerialPortGetRequest( &pucMBRTUFrame, &usFrameLength ) == FALSE )
if( xMBPortSerialGetRequest( &pucMBRTUFrame, &usFrameLength ) == FALSE )
{
return MB_EIO;
}
@ -222,7 +222,7 @@ eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
eSndState = STATE_TX_XMIT;
EXIT_CRITICAL_SECTION( );
if( xMBSerialPortSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE )
if( xMBPortSerialSendResponse( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}

View File

@ -141,7 +141,7 @@ eMBMasterRTUStart( void )
* to STATE_M_RX_IDLE. This makes sure that we delay startup of the
* modbus protocol stack until the bus is free.
*/
eRcvState = STATE_M_RX_IDLE; //STATE_M_RX_INIT (We start processing immediately)
eRcvState = STATE_M_RX_INIT;
vMBMasterPortSerialEnable( TRUE, FALSE );
vMBMasterPortTimersT35Enable( );
@ -164,7 +164,7 @@ eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLengt
UCHAR *pucMBRTUFrame = ( UCHAR* ) ucMasterRTURcvBuf;
USHORT usFrameLength = usMasterRcvBufferPos;
if( xMBMasterSerialPortGetResponse( &pucMBRTUFrame, &usFrameLength ) == FALSE )
if( xMBMasterPortSerialGetResponse( &pucMBRTUFrame, &usFrameLength ) == FALSE )
{
return MB_EIO;
}
@ -230,12 +230,12 @@ eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength
/* Activate the transmitter. */
eSndState = STATE_M_TX_XMIT;
if ( xMBMasterSerialPortSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE )
if ( xMBMasterPortSerialSendRequest( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ) == FALSE )
{
eStatus = MB_EIO;
}
// The place to enable RS485 driver
vMBMasterPortSerialEnable( FALSE, TRUE );
}
@ -252,7 +252,9 @@ xMBMasterRTUReceiveFSM( void )
BOOL xStatus = FALSE;
UCHAR ucByte;
assert(( eSndState == STATE_M_TX_IDLE ) || ( eSndState == STATE_M_TX_XFWR ));
if ( ( eSndState != STATE_M_TX_IDLE ) && ( eSndState != STATE_M_TX_XFWR ) ) {
return FALSE;
}
/* Always read the character. */
xStatus = xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte );
@ -264,6 +266,7 @@ xMBMasterRTUReceiveFSM( void )
*/
case STATE_M_RX_INIT:
vMBMasterPortTimersT35Enable( );
ESP_LOGD("DBG", "Start initialization phase.");
break;
/* In the error state we wait until all characters in the
@ -283,12 +286,12 @@ xMBMasterRTUReceiveFSM( void )
* Disable timer of respond timeout and change the transmiter state to idle.
*/
vMBMasterPortTimersDisable( );
eSndState = STATE_M_TX_IDLE;
usMasterRcvBufferPos = 0;
if( xStatus && ucByte ) {
ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
eRcvState = STATE_M_RX_RCV;
eSndState = STATE_M_TX_IDLE;
}
/* Enable t3.5 timers. */
@ -327,7 +330,9 @@ xMBMasterRTUTransmitFSM( void )
BOOL xNeedPoll = TRUE;
BOOL xFrameIsBroadcast = FALSE;
assert( eRcvState == STATE_M_RX_IDLE );
if ( eRcvState != STATE_M_RX_IDLE ) {
return FALSE;
}
switch ( eSndState )
{
@ -350,7 +355,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,
@ -380,6 +386,7 @@ xMBMasterRTUTimerExpired(void)
/* Timer t35 expired. Startup phase is finished. */
case STATE_M_RX_INIT:
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY);
ESP_EARLY_LOGD("DBG", "RTU timer, init FSM state.");
break;
/* A frame was received and t35 expired. Notify the listener that

View File

@ -124,10 +124,14 @@ eMBTCPReceive( UCHAR * pucRcvAddress, UCHAR ** ppucFrame, USHORT * pusLength )
*pusLength = usLength - MB_TCP_FUNC;
eStatus = MB_ENOERR;
/* Modbus TCP does not use any addresses. Fake the source address such
* that the processing part deals with this frame.
/* The regular Modbus TCP does not use any addresses. Fake the MBAP UID in this case.
* The MBAP UID field support is used for RTU over TCP option if enabled.
*/
#if MB_TCP_UID_ENABLED
*pucRcvAddress = pucMBTCPFrame[MB_TCP_UID];
#else
*pucRcvAddress = MB_TCP_PSEUDO_ADDRESS;
#endif
}
}
else
@ -152,6 +156,7 @@ eMBTCPSend( UCHAR _unused, const UCHAR * pucFrame, USHORT usLength )
*/
pucMBTCPFrame[MB_TCP_LEN] = ( usLength + 1 ) >> 8U;
pucMBTCPFrame[MB_TCP_LEN + 1] = ( usLength + 1 ) & 0xFF;
if( xMBTCPPortSendResponse( pucMBTCPFrame, usTCPLength ) == FALSE )
{
eStatus = MB_EIO;

View File

@ -123,10 +123,14 @@ eMBMasterTCPReceive( UCHAR * pucRcvAddress, UCHAR ** ppucFrame, USHORT * pusLeng
*pusLength = usLength - MB_TCP_FUNC;
eStatus = MB_ENOERR;
/* Modbus TCP does not use any addresses. Fake the source address such
* that the processing part deals with this frame.
/* Get MBAP UID field if its support is enabled.
* Otherwise just ignore this field.
*/
#if MB_TCP_UID_ENABLED
*pucRcvAddress = pucMBTCPFrame[MB_TCP_UID];
#else
*pucRcvAddress = MB_TCP_PSEUDO_ADDRESS;
#endif
}
}
else
@ -137,20 +141,27 @@ eMBMasterTCPReceive( UCHAR * pucRcvAddress, UCHAR ** ppucFrame, USHORT * pusLeng
}
eMBErrorCode
eMBMasterTCPSend( UCHAR _unused, const UCHAR * pucFrame, USHORT usLength )
eMBMasterTCPSend( UCHAR ucAddress, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
UCHAR *pucMBTCPFrame = ( UCHAR * ) pucFrame - MB_TCP_FUNC;
USHORT usTCPLength = usLength + MB_TCP_FUNC;
/* The MBAP header is already initialized because the caller calls this
* function with the buffer returned by the previous call. Therefore we
* only have to update the length in the header. Note that the length
* header includes the size of the Modbus PDU and the UID Byte. Therefore
* the length is usLength plus one.
/* Note that the length in the MBAP header includes the size of the Modbus PDU
* and the UID Byte. Therefore the length is usLength plus one.
*/
pucMBTCPFrame[MB_TCP_LEN] = ( usLength + 1 ) >> 8U;
pucMBTCPFrame[MB_TCP_LEN + 1] = ( usLength + 1 ) & 0xFF;
/* Set UID field in the MBAP if it is supported.
* If the RTU over TCP is not supported, the UID = 0 or 0xFF.
*/
#if MB_TCP_UID_ENABLED
pucMBTCPFrame[MB_TCP_UID] = ucAddress;
#else
pucMBTCPFrame[MB_TCP_UID] = 0x00;
#endif
if( xMBMasterTCPPortSendResponse( pucMBTCPFrame, usTCPLength ) == FALSE )
{
eStatus = MB_EIO;

View File

@ -50,7 +50,7 @@ void eMBMasterTCPStart( void );
void eMBMasterTCPStop( void );
eMBErrorCode eMBMasterTCPReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame,
USHORT * pusLength );
eMBErrorCode eMBMasterTCPSend( UCHAR _unused, const UCHAR * pucFrame,
eMBErrorCode eMBMasterTCPSend( UCHAR ucAddress, const UCHAR * pucFrame,
USHORT usLength );
BOOL xMBMasterTCPTimerExpired(void);

View File

@ -73,13 +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(__func__, "UART event: %d ", pxEvent->type);
ESP_LOGD(MB_PORT_TAG, "%s, UART event: %u ", __func__, (unsigned)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

@ -91,6 +91,10 @@
#define MB_TCP_SEND_TIMEOUT (pdMS_TO_TICKS(MB_TCP_SEND_TIMEOUT_MS))
#define MB_TCP_PORT_MAX_CONN (CONFIG_FMB_TCP_PORT_MAX_CONN)
// Set the API unlock time to maximum response time
// The actual release time will be dependent on the timer time
#define MB_MAX_RESPONSE_TIME_MS (5000)
#define MB_TCP_FRAME_LOG_BUFSIZE (256)
#define MB_PORT_HAS_CLOSE (1) // Define to explicitly close port on destroy

View File

@ -44,14 +44,11 @@
#include "mb.h"
#include "mbport.h"
#include "port.h"
#include "sdkconfig.h"
#include "mbconfig.h"
#include "port_serial_slave.h"
/* ----------------------- Variables ----------------------------------------*/
static QueueHandle_t xQueueHdl;
#define MB_EVENT_QUEUE_SIZE (6)
#define MB_EVENT_QUEUE_TIMEOUT (pdMS_TO_TICKS(CONFIG_FMB_EVENT_QUEUE_TIMEOUT))
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit( void )
@ -89,7 +86,7 @@ xMBPortEventPost( eMBEventType eEvent )
portYIELD_FROM_ISR();
}
if (xStatus != pdTRUE) {
ESP_EARLY_LOGV(MB_PORT_TAG, "%s: Post message failure = %d.", __func__, xStatus);
ESP_EARLY_LOGV(MB_PORT_TAG, "%s: Post message failure = %u.", __func__, (unsigned)xStatus);
return FALSE;
}
}

View File

@ -35,27 +35,24 @@
*/
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbport.h"
#include "mbconfig.h"
#include <stdatomic.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#include "mb_m.h"
#include "mbport.h"
#include "mbconfig.h"
#include "port.h"
#include "mbport.h"
#include "freertos/semphr.h"
#include "port_serial_master.h"
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
/* ----------------------- Defines ------------------------------------------*/
// Event bit mask for xMBMasterPortEventGet()
#define MB_EVENT_POLL_MASK (EventBits_t)( EV_MASTER_READY | \
EV_MASTER_FRAME_RECEIVED | \
EV_MASTER_EXECUTE | \
EV_MASTER_FRAME_SENT | \
EV_MASTER_FRAME_TRANSMIT | \
EV_MASTER_ERROR_PROCESS )
// Event bit mask for eMBMasterWaitRequestFinish()
#define MB_EVENT_REQ_MASK (EventBits_t)( EV_MASTER_PROCESS_SUCCESS | \
@ -63,12 +60,13 @@
EV_MASTER_ERROR_RECEIVE_DATA | \
EV_MASTER_ERROR_EXECUTE_FUNCTION )
#define MB_EVENT_RESOURCE (EventBits_t)( 0x0080 )
/* ----------------------- Variables ----------------------------------------*/
static EventGroupHandle_t xResourceMasterHdl;
static SemaphoreHandle_t xResourceMasterHdl;
static EventGroupHandle_t xEventGroupMasterHdl;
static EventGroupHandle_t xEventGroupMasterConfirmHdl;
static QueueHandle_t xQueueMasterHdl;
static uint64_t xTransactionID = 0;
/* ----------------------- Start implementation -----------------------------*/
@ -79,45 +77,60 @@ xMBMasterPortEventInit( void )
xEventGroupMasterConfirmHdl = xEventGroupCreate();
MB_PORT_CHECK((xEventGroupMasterHdl != NULL) && (xEventGroupMasterConfirmHdl != NULL),
FALSE, "mb stack event group creation error.");
xQueueMasterHdl = xQueueCreate(MB_EVENT_QUEUE_SIZE, sizeof(xMBMasterEventType));
MB_PORT_CHECK(xQueueMasterHdl, FALSE, "mb stack event group creation error.");
vQueueAddToRegistry(xQueueMasterHdl, "MbMasterPortEventQueue");
xTransactionID = 0;
return TRUE;
}
BOOL MB_PORT_ISR_ATTR
xMBMasterPortEventPost( eMBMasterEventType eEvent )
xMBMasterPortEventPost( eMBMasterEventEnum eEvent)
{
BOOL bStatus = FALSE;
eMBMasterEventType eTempEvent = eEvent;
BaseType_t xStatus, xHigherPriorityTaskWoken = pdFALSE;
assert(xQueueMasterHdl != NULL);
xMBMasterEventType xEvent;
xEvent.xPostTimestamp = esp_timer_get_time();
if (eEvent & EV_MASTER_TRANS_START) {
atomic_store(&(xTransactionID), xEvent.xPostTimestamp);
}
xEvent.eEvent = (eEvent & ~EV_MASTER_TRANS_START);
if( (BOOL)xPortInIsrContext() == TRUE )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t xResult = xEventGroupSetBitsFromISR( xEventGroupMasterHdl,
(EventBits_t) eTempEvent,
&xHigherPriorityTaskWoken );
// Was the message posted successfully?
if( xResult == pdPASS ) {
// If xHigherPriorityTaskWoken is now set to pdTRUE
// then a context switch should be requested.
if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR();
bStatus = TRUE;
} else {
bStatus = FALSE;
if( (BOOL)xPortInIsrContext() == TRUE ) {
xStatus = xQueueSendFromISR(xQueueMasterHdl, (const void*)&xEvent, &xHigherPriorityTaskWoken);
if ( xHigherPriorityTaskWoken ) {
portYIELD_FROM_ISR();
}
if (xStatus != pdTRUE) {
ESP_EARLY_LOGV(MB_PORT_TAG, "%s: Post message failure = %d.", __func__, xStatus);
return FALSE;
}
} else {
xStatus = xQueueSend(xQueueMasterHdl, (const void*)&xEvent, MB_EVENT_QUEUE_TIMEOUT);
MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "%s: Post message failure.", __func__);
}
else
{
// Set event bits if the function is called from task
// The return result is not checked here because
// It might be that event bit was cleared automatically as a
// task that was waiting for the bit was removed from the Blocked state.
(void) xEventGroupSetBits( xEventGroupMasterHdl, (EventBits_t)eTempEvent );
bStatus = TRUE;
}
return bStatus;
return TRUE;
}
eMBMasterEventType
xMBMasterPortFsmWaitConfirmation( eMBMasterEventType eEventMask, ULONG ulTimeout)
BOOL
xMBMasterPortEventGet(xMBMasterEventType *peEvent)
{
assert(xQueueMasterHdl != NULL);
BOOL xEventHappened = FALSE;
if (xQueueReceive(xQueueMasterHdl, peEvent, portMAX_DELAY) == pdTRUE) {
peEvent->xTransactionId = atomic_load(&xTransactionID);
// Set event bits in confirmation group (for synchronization with port task)
xEventGroupSetBits(xEventGroupMasterConfirmHdl, peEvent->eEvent);
peEvent->xGetTimestamp = esp_timer_get_time();
xEventHappened = TRUE;
}
return xEventHappened;
}
eMBMasterEventEnum
xMBMasterPortFsmWaitConfirmation( eMBMasterEventEnum eEventMask, ULONG ulTimeout)
{
EventBits_t uxBits;
uxBits = xEventGroupWaitBits( xEventGroupMasterConfirmHdl, // The event group being tested.
@ -129,39 +142,19 @@ xMBMasterPortFsmWaitConfirmation( eMBMasterEventType eEventMask, ULONG ulTimeout
// Clear confirmation events that where set in the mask
xEventGroupClearBits( xEventGroupMasterConfirmHdl, (uxBits & eEventMask) );
}
return (eMBMasterEventType)(uxBits & eEventMask);
return (eMBMasterEventEnum)(uxBits & eEventMask);
}
BOOL
xMBMasterPortEventGet( eMBMasterEventType* eEvent )
uint64_t xMBMasterPortGetTransactionId( )
{
EventBits_t uxBits;
BOOL xEventHappened = FALSE;
uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested.
MB_EVENT_POLL_MASK, // The bits within the event group to wait for.
pdTRUE, // Masked bits should be cleared before returning.
pdFALSE, // Don't wait for both bits, either bit will do.
portMAX_DELAY); // Wait forever for either bit to be set.
// Check if poll event is correct
if (MB_PORT_CHECK_EVENT(uxBits, MB_EVENT_POLL_MASK)) {
*eEvent = (eMBMasterEventType)(uxBits & MB_EVENT_POLL_MASK);
// Set event bits in confirmation group (for synchronization with port task)
xEventGroupSetBits( xEventGroupMasterConfirmHdl, *eEvent );
xEventHappened = TRUE;
} else {
ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event triggered = %d.", __func__, uxBits);
*eEvent = (eMBMasterEventType)uxBits;
xEventHappened = FALSE;
}
return xEventHappened;
return atomic_load(&xTransactionID);
}
// This function is initialize the OS resource for modbus master.
void vMBMasterOsResInit( void )
{
xResourceMasterHdl = xEventGroupCreate();
xEventGroupSetBits(xResourceMasterHdl, MB_EVENT_RESOURCE);
MB_PORT_CHECK((xResourceMasterHdl != NULL), ; , "Resource create error.");
xResourceMasterHdl = xSemaphoreCreateBinary();
MB_PORT_CHECK((xResourceMasterHdl != NULL), ; , "%s: Resource create error.", __func__);
}
/**
@ -174,26 +167,24 @@ void vMBMasterOsResInit( void )
*/
BOOL xMBMasterRunResTake( LONG lTimeOut )
{
EventBits_t uxBits;
uxBits = xEventGroupWaitBits( xResourceMasterHdl, // The event group being tested.
MB_EVENT_RESOURCE, // The bits within the event group to wait for.
pdTRUE, // Masked bits should be cleared before returning.
pdFALSE, // Don't wait for both bits, either bit will do.
lTimeOut); // Resource wait timeout.
MB_PORT_CHECK((uxBits == MB_EVENT_RESOURCE), FALSE , "Take resource failure.");
ESP_LOGD(MB_PORT_TAG,"%s:Take resource (%x) (%lu ticks).", __func__, uxBits, lTimeOut);
BaseType_t xStatus = pdTRUE;
xStatus = xSemaphoreTake( xResourceMasterHdl, lTimeOut );
MB_PORT_CHECK((xStatus == pdTRUE), FALSE , "%s: Resource take failure.", __func__);
ESP_LOGD(MB_PORT_TAG,"%s:Take MB resource (%lu ticks).", __func__, lTimeOut);
return TRUE;
}
/**
* 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);
BaseType_t xStatus = pdFALSE;
xStatus = xSemaphoreGive( xResourceMasterHdl );
if (xStatus != pdTRUE) {
ESP_LOGD(MB_PORT_TAG,"%s: Release resource fail.", __func__);
}
}
/**
@ -207,8 +198,7 @@ void vMBMasterRunResRelease( void )
*/
void vMBMasterErrorCBRespondTimeout(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
{
BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_RESPOND_TIMEOUT);
MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_RESPOND_TIMEOUT' failed!", __func__);
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_RESPOND_TIMEOUT );
ESP_LOGD(MB_PORT_TAG,"%s:Callback respond timeout.", __func__);
}
@ -222,10 +212,9 @@ void vMBMasterErrorCBRespondTimeout(UCHAR ucDestAddress, const UCHAR* pucPDUData
*/
void vMBMasterErrorCBReceiveData(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
{
BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_RECEIVE_DATA);
MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_RECEIVE_DATA' failed!", __func__);
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_RECEIVE_DATA );
ESP_LOGD(MB_PORT_TAG,"%s:Callback receive data timeout failure.", __func__);
ESP_LOG_BUFFER_HEX_LEVEL("Err rcv buf", (void*)pucPDUData, (uint16_t)ucPDULength, ESP_LOG_DEBUG);
ESP_LOG_BUFFER_HEX_LEVEL("Err rcv buf", (void *)pucPDUData, (USHORT)ucPDULength, ESP_LOG_DEBUG);
}
/**
@ -240,10 +229,9 @@ void vMBMasterErrorCBReceiveData(UCHAR ucDestAddress, const UCHAR* pucPDUData, U
*/
void vMBMasterErrorCBExecuteFunction(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
{
BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_EXECUTE_FUNCTION);
MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_EXECUTE_FUNCTION' failed!", __func__);
xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_EXECUTE_FUNCTION );
ESP_LOGD(MB_PORT_TAG,"%s:Callback execute data handler failure.", __func__);
ESP_LOG_BUFFER_HEX_LEVEL("Exec func buf", (void*)pucPDUData, (uint16_t)ucPDULength, ESP_LOG_DEBUG);
ESP_LOG_BUFFER_HEX_LEVEL("Exec func buf", (void*)pucPDUData, (USHORT)ucPDULength, ESP_LOG_DEBUG);
}
/**
@ -251,13 +239,9 @@ void vMBMasterErrorCBExecuteFunction(UCHAR ucDestAddress, const UCHAR* pucPDUDat
* @note There functions will block modbus master poll while execute OS waiting.
* So,for real-time of system. Do not execute too much waiting process.
*/
void vMBMasterCBRequestSuccess( void ) {
/**
* @note This code is use OS's event mechanism for modbus master protocol stack.
* If you don't use OS, you can change it.
*/
BOOL ret = xMBMasterPortEventPost(EV_MASTER_PROCESS_SUCCESS);
MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_PROCESS_SUCCESS' failed!", __func__);
void vMBMasterCBRequestSuccess( void )
{
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_PROCESS_SUCCESS );
ESP_LOGD(MB_PORT_TAG,"%s: Callback request success.", __func__);
}
@ -272,19 +256,19 @@ void vMBMasterCBRequestSuccess( void ) {
*/
eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
eMBMasterEventType xRecvedEvent;
eMBMasterEventEnum xRecvedEvent;
EventBits_t uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested.
MB_EVENT_REQ_MASK, // The bits within the event group to wait for.
pdTRUE, // Masked bits should be cleared before returning.
pdFALSE, // Don't wait for both bits, either bit will do.
portMAX_DELAY ); // Wait forever for either bit to be set.
xRecvedEvent = (eMBMasterEventType)(uxBits);
xRecvedEvent = (eMBMasterEventEnum)(uxBits);
if (xRecvedEvent) {
ESP_LOGD(MB_PORT_TAG,"%s: returned event = 0x%x", __func__, xRecvedEvent);
ESP_LOGD(MB_PORT_TAG,"%s: returned event = 0x%x", __func__, (int)xRecvedEvent);
if (!(xRecvedEvent & MB_EVENT_REQ_MASK)) {
// if we wait for certain event bits but get from poll subset
ESP_LOGE(MB_PORT_TAG,"%s: incorrect event set = 0x%x", __func__, xRecvedEvent);
ESP_LOGE(MB_PORT_TAG,"%s: incorrect event set = 0x%x", __func__, (int)xRecvedEvent);
}
xEventGroupSetBits( xEventGroupMasterConfirmHdl, (xRecvedEvent & MB_EVENT_REQ_MASK) );
if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_PROCESS_SUCCESS)) {
@ -297,7 +281,7 @@ eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
eErrStatus = MB_MRE_EXE_FUN;
}
} else {
ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event or timeout xRecvedEvent = 0x%x", __func__, uxBits);
ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event or timeout xRecvedEvent = 0x%x", __func__, (int)uxBits);
// https://github.com/espressif/esp-idf/issues/5275
// if a no event is received, that means vMBMasterPortEventClose()
// has been closed, so event group has been deleted by FreeRTOS, which
@ -310,9 +294,22 @@ eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
void vMBMasterPortEventClose(void)
{
vEventGroupDelete(xEventGroupMasterHdl);
vEventGroupDelete(xEventGroupMasterConfirmHdl);
vEventGroupDelete(xResourceMasterHdl);
if (xEventGroupMasterHdl) {
vEventGroupDelete(xEventGroupMasterHdl);
xEventGroupMasterHdl = NULL;
}
if (xQueueMasterHdl) {
vQueueDelete(xQueueMasterHdl);
xQueueMasterHdl = NULL;
}
if (xEventGroupMasterConfirmHdl) {
vEventGroupDelete(xEventGroupMasterConfirmHdl);
xEventGroupMasterConfirmHdl = NULL;
}
if (xResourceMasterHdl) {
vSemaphoreDelete(xResourceMasterHdl);
xResourceMasterHdl = NULL;
}
}
#endif

View File

@ -96,7 +96,7 @@ static USHORT usMBPortSerialRxPoll(size_t xEventSize)
#if !CONFIG_FMB_TIMER_PORT_ENABLED
pxMBPortCBTimerExpired();
#endif
ESP_LOGD(TAG, "RX: %d bytes\n", usCnt);
ESP_LOGD(TAG, "RX: %u bytes\n", (unsigned)usCnt);
}
return usCnt;
}
@ -112,7 +112,7 @@ BOOL xMBPortSerialTxPoll(void)
// Calls the modbus stack callback function to let it fill the UART transmit buffer.
bNeedPoll = pxMBFrameCBTransmitterEmpty( ); // callback to transmit FSM
}
ESP_LOGD(TAG, "MB_TX_buffer send: (%d) bytes\n", (uint16_t)usCount);
ESP_LOGD(TAG, "MB_TX_buffer send: (%u) bytes\n", (unsigned)usCount);
// Waits while UART sending the packet
esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
vMBPortSerialEnable(TRUE, FALSE);
@ -128,11 +128,11 @@ static void vUartTask(void *pvParameters)
USHORT usResult = 0;
for(;;) {
if (xMBPortSerialWaitEvent(xMbUartQueue, (void*)&xEvent, portMAX_DELAY)) {
ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber);
ESP_LOGD(TAG, "MB_uart[%u] event:", (unsigned)ucUartNumber);
switch(xEvent.type) {
//Event of UART receving data
case UART_DATA:
ESP_LOGD(TAG,"Data event, length: %d", xEvent.size);
ESP_LOGD(TAG,"Data event, length: %u", (unsigned)xEvent.size);
// 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) {
@ -140,34 +140,34 @@ static void vUartTask(void *pvParameters)
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);
ESP_LOGD(TAG,"Timeout occured, processed: %u bytes", (unsigned)usResult);
}
break;
//Event of HW FIFO overflow detected
case UART_FIFO_OVF:
ESP_LOGD(TAG, "hw fifo overflow\n");
ESP_LOGD(TAG, "hw fifo overflow");
xQueueReset(xMbUartQueue);
break;
//Event of UART ring buffer full
case UART_BUFFER_FULL:
ESP_LOGD(TAG, "ring buffer full\n");
ESP_LOGD(TAG, "ring buffer full");
xQueueReset(xMbUartQueue);
uart_flush_input(ucUartNumber);
break;
//Event of UART RX break detected
case UART_BREAK:
ESP_LOGD(TAG, "uart rx break\n");
ESP_LOGD(TAG, "uart rx break");
break;
//Event of UART parity check error
case UART_PARITY_ERR:
ESP_LOGD(TAG, "uart parity error\n");
ESP_LOGD(TAG, "uart parity error");
break;
//Event of UART frame error
case UART_FRAME_ERR:
ESP_LOGD(TAG, "uart frame error\n");
ESP_LOGD(TAG, "uart frame error");
break;
default:
ESP_LOGD(TAG, "uart event type: %d\n", xEvent.type);
ESP_LOGD(TAG, "uart event type: %u", (unsigned)xEvent.type);
break;
}
}
@ -195,7 +195,7 @@ BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate,
ucParity = UART_PARITY_EVEN;
break;
default:
ESP_LOGE(TAG, "Incorrect parity option: %d", eParity);
ESP_LOGE(TAG, "Incorrect parity option: %u", (unsigned)eParity);
return FALSE;
}
switch(ucDataBits){
@ -222,22 +222,26 @@ 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
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
.source_clk = UART_SCLK_DEFAULT,
#else
.source_clk = UART_SCLK_APB,
#endif
};
// Set UART config
xErr = uart_param_config(ucUartNumber, &xUartConfig);
MB_PORT_CHECK((xErr == ESP_OK),
FALSE, "mb config failure, uart_param_config() returned (0x%x).", xErr);
FALSE, "mb config failure, uart_param_config() returned (0x%x).", (int)xErr);
// Install UART driver, and get the queue.
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
"mb serial driver failure, uart_driver_install() returned (0x%x).", xErr);
"mb serial driver failure, uart_driver_install() returned (0x%x).", (int)xErr);
#if !CONFIG_FMB_TIMER_PORT_ENABLED
// Set timeout for TOUT interrupt (T3.5 modbus time)
xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", xErr);
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", (int)xErr);
#endif
// Set always timeout flag to trigger timeout interrupt even after rx fifo full
@ -253,24 +257,13 @@ BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate,
// Force exit from function with failure
MB_PORT_CHECK(FALSE, FALSE,
"mb stack serial task creation error. xTaskCreate() returned (0x%x).",
xStatus);
(int)xStatus);
} else {
vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
}
return TRUE;
}
BOOL xMBSerialPortGetRequest( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength )
{
BOOL eStatus = TRUE;
return eStatus;
}
BOOL xMBSerialPortSendResponse( UCHAR *pucMBSerialFrame, USHORT usSerialLength )
{
return TRUE;
}
void vMBPortSerialClose(void)
{
(void)vTaskSuspend(xMbTaskHandle);

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,75 @@ 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 void vMBMasterPortRxSemaClose( void )
{
if (xMasterSemaRxHandle) {
vSemaphoreDelete(xMasterSemaRxHandle);
xMasterSemaRxHandle = NULL;
}
}
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 (%" PRIu64 " ticks).", __func__, (uint64_t)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.", (int)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.", (int)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,24 +147,29 @@ 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++ <= xEventSize)) {
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);
ESP_LOGD(TAG, "Received data: %u(bytes in buffer)", (unsigned)usCnt);
#if !CONFIG_FMB_TIMER_PORT_ENABLED
vMBMasterSetCurTimerMode(MB_TMODE_T35);
pxMBMasterPortCBTimerExpired();
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);
ESP_LOGE(TAG, "%s: bRxState disabled but junk data (%u bytes) received. ",
__func__, (unsigned)xEventSize);
}
return usCnt;
}
@ -118,7 +185,7 @@ BOOL xMBMasterPortSerialTxPoll(void)
// Calls the modbus stack callback function to let it fill the UART transmit buffer.
bNeedPoll = pxMBMasterFrameCBTransmitterEmpty( ); // callback to transmit FSM
}
ESP_LOGD(TAG, "MB_TX_buffer sent: (%d) bytes.", (uint16_t)(usCount - 1));
ESP_LOGD(TAG, "MB_TX_buffer sent: (%u) bytes.", (unsigned)(usCount - 1));
// Waits while UART sending the packet
esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
vMBMasterPortSerialEnable(TRUE, FALSE);
@ -135,21 +202,25 @@ static void vUartTask(void* pvParameters)
USHORT usResult = 0;
for(;;) {
if (xMBPortSerialWaitEvent(xMbUartQueue, (void*)&xEvent, portMAX_DELAY)) {
ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber);
ESP_LOGD(TAG, "MB_uart[%u] event:", (unsigned)ucUartNumber);
switch(xEvent.type) {
//Event of UART receiving data
case UART_DATA:
ESP_LOGD(TAG,"Data event, len: %d.", xEvent.size);
ESP_LOGD(TAG,"Data event, len: %u.", (unsigned)xEvent.size);
// 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);
ESP_LOGD(TAG,"Timeout occured, processed: %u bytes", (unsigned)usResult);
}
break;
//Event of HW FIFO overflow detected
@ -170,13 +241,17 @@ static void vUartTask(void* pvParameters)
//Event of UART parity check error
case UART_PARITY_ERR:
ESP_LOGD(TAG, "uart parity error.");
xQueueReset(xMbUartQueue);
uart_flush_input(ucUartNumber);
break;
//Event of UART frame error
case UART_FRAME_ERR:
ESP_LOGD(TAG, "uart frame error.");
xQueueReset(xMbUartQueue);
uart_flush_input(ucUartNumber);
break;
default:
ESP_LOGD(TAG, "uart event type: %d.", xEvent.type);
ESP_LOGD(TAG, "uart event type: %u.", (unsigned)xEvent.type);
break;
}
}
@ -204,7 +279,7 @@ BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
ucParity = UART_PARITY_EVEN;
break;
default:
ESP_LOGE(TAG, "Incorrect parity option: %d", eParity);
ESP_LOGE(TAG, "Incorrect parity option: %u", (unsigned)eParity);
return FALSE;
}
switch(ucDataBits){
@ -231,25 +306,30 @@ BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 2,
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
.source_clk = UART_SCLK_DEFAULT,
#else
.source_clk = UART_SCLK_APB,
#endif
};
// Set UART config
xErr = uart_param_config(ucUartNumber, &xUartConfig);
MB_PORT_CHECK((xErr == ESP_OK),
FALSE, "mb config failure, uart_param_config() returned (0x%x).", xErr);
FALSE, "mb config failure, uart_param_config() returned (0x%x).", (int)xErr);
// Install UART driver, and get the queue.
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
"mb serial driver failure, uart_driver_install() returned (0x%x).", xErr);
"mb serial driver failure, uart_driver_install() returned (0x%x).", (int)xErr);
// Set timeout for TOUT interrupt (T3.5 modbus time)
xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", xErr);
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", (int)xErr);
// 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,
@ -259,8 +339,7 @@ BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
vTaskDelete(xMbTaskHandle);
// Force exit from function with failure
MB_PORT_CHECK(FALSE, FALSE,
"mb stack serial task creation error. xTaskCreate() returned (0x%x).",
xStatus);
"mb stack serial task creation error. xTaskCreate() returned (0x%x).", (int)xStatus);
} else {
vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
}
@ -268,26 +347,9 @@ BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
return TRUE;
}
/*
* The function is called from ASCII/RTU module to get processed data buffer. Sets the
* received buffer and its length using parameters.
*/
BOOL xMBMasterSerialPortGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength )
{
return TRUE;
}
/*
* The function is called from ASCII/RTU module to set processed data buffer
* to be sent in transmitter state machine.
*/
BOOL xMBMasterSerialPortSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength )
{
return TRUE;
}
void vMBMasterPortSerialClose(void)
{
vMBMasterPortRxSemaClose();
(void)vTaskDelete(xMbTaskHandle);
ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
}

View File

@ -52,7 +52,7 @@ static void IRAM_ATTR vTimerAlarmCBHandler(void *param)
{
pxMBMasterPortCBTimerExpired(); // Timer expired callback function
pxTimerContext->xTimerState = TRUE;
ESP_EARLY_LOGD(TAG, "Timer mode: (%d) triggered", xMBMasterGetCurTimerMode());
ESP_EARLY_LOGD(TAG, "Timer mode: (%u) triggered", (unsigned)xMBMasterGetCurTimerMode());
}
BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
@ -101,14 +101,12 @@ static BOOL xMBMasterPortTimersEnable(uint64_t xToutUs)
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)

View File

@ -27,8 +27,8 @@
extern BOOL xMBMasterPortSerialTxPoll(void);
/*-----------------------Master mode use these variables----------------------*/
#define MB_RESPONSE_TICS pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND + 10)
// Actual wait time depends on the response timer
#define MB_SERIAL_API_RESP_TICS (pdMS_TO_TICKS(MB_MAX_RESPONSE_TIME_MS))
static mb_master_interface_t* mbm_interface_ptr = NULL;
static const char *TAG = "MB_CONTROLLER_MASTER";
@ -71,12 +71,11 @@ static esp_err_t mbc_serial_master_setup(void* comm_info)
const mb_master_comm_info_t* comm_info_ptr = (mb_master_comm_info_t*)comm_info;
// Check communication options
MB_MASTER_CHECK(((comm_info_ptr->mode == MB_MODE_RTU) || (comm_info_ptr->mode == MB_MODE_ASCII)),
ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).",
(uint32_t)comm_info_ptr->mode);
ESP_ERR_INVALID_ARG, "mb incorrect mode = (%u).", (unsigned)comm_info_ptr->mode);
MB_MASTER_CHECK((comm_info_ptr->port <= UART_NUM_MAX), ESP_ERR_INVALID_ARG,
"mb wrong port to set = (0x%x).", (uint32_t)comm_info_ptr->port);
"mb wrong port to set = (%u).", (unsigned)comm_info_ptr->port);
MB_MASTER_CHECK((comm_info_ptr->parity <= UART_PARITY_ODD), ESP_ERR_INVALID_ARG,
"mb wrong parity option = (0x%x).", (uint32_t)comm_info_ptr->parity);
"mb wrong parity option = (%u).", (unsigned)comm_info_ptr->parity);
// Save the communication options
mbm_opts->mbm_comm = *(mb_communication_info_t*)comm_info_ptr;
return ESP_OK;
@ -98,10 +97,10 @@ static esp_err_t mbc_serial_master_start(void)
MB_PORT_PARITY_GET(comm_info->parity));
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack initialization failure, eMBInit() returns (0x%x).", status);
"mb stack initialization failure, eMBInit() returns (0x%x).", (int)status);
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 set slave ID failure, eMBMasterEnable() returned (0x%x).", (int)status);
// Set the mbcontroller start flag
EventBits_t flag = xEventGroupSetBits(mbm_opts->mbm_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
@ -122,7 +121,7 @@ static esp_err_t mbc_serial_master_destroy(void)
EventBits_t flag = xEventGroupClearBits(mbm_opts->mbm_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_MASTER_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
// Desable and then destroy the Modbus stack
mb_error = eMBMasterDisable();
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
@ -130,7 +129,7 @@ static esp_err_t mbc_serial_master_destroy(void)
(void)vEventGroupDelete(mbm_opts->mbm_event_group);
mb_error = eMBMasterClose();
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack close failure returned (0x%x).", (uint32_t)mb_error);
"mb stack close failure returned (0x%x).", (int)mb_error);
free(mbm_interface_ptr); // free the memory allocated for options
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
mbm_interface_ptr = NULL;
@ -174,66 +173,70 @@ static esp_err_t mbc_serial_master_send_request(mb_param_request_t* request, voi
MB_MASTER_CHECK((data_ptr != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect data pointer.");
eMBMasterReqErrCode mb_error = MB_MRE_NO_REG;
eMBMasterReqErrCode mb_error = MB_MRE_MASTER_BUSY;
esp_err_t error = ESP_FAIL;
uint8_t mb_slave_addr = request->slave_addr;
uint8_t mb_command = request->command;
uint16_t mb_offset = request->reg_start;
uint16_t mb_size = request->reg_size;
if (xMBMasterRunResTake(MB_SERIAL_API_RESP_TICS)) {
uint8_t mb_slave_addr = request->slave_addr;
uint8_t mb_command = request->command;
uint16_t mb_offset = request->reg_start;
uint16_t mb_size = request->reg_size;
// Set the buffer for callback function processing of received data
mbm_opts->mbm_reg_buffer_ptr = (uint8_t*)data_ptr;
mbm_opts->mbm_reg_buffer_size = mb_size;
// Set the buffer for callback function processing of received data
mbm_opts->mbm_reg_buffer_ptr = (uint8_t*)data_ptr;
mbm_opts->mbm_reg_buffer_size = mb_size;
vMBMasterRunResRelease();
// Calls appropriate request function to send request and waits response
switch(mb_command)
{
case MB_FUNC_READ_COILS:
mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size , (LONG)MB_RESPONSE_TICS );
break;
case MB_FUNC_WRITE_SINGLE_COIL:
mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS );
break;
case MB_FUNC_WRITE_MULTIPLE_COILS:
mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (UCHAR*)data_ptr, (LONG)MB_RESPONSE_TICS);
break;
case MB_FUNC_READ_DISCRETE_INPUTS:
mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_RESPONSE_TICS );
break;
case MB_FUNC_READ_HOLDING_REGISTER:
mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_RESPONSE_TICS );
break;
case MB_FUNC_WRITE_REGISTER:
mb_error = eMBMasterReqWriteHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS );
break;
// Calls appropriate request function to send request and waits response
switch(mb_command)
{
case MB_FUNC_READ_COILS:
mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size , (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_WRITE_SINGLE_COIL:
mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_WRITE_MULTIPLE_COILS:
mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (UCHAR*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS);
break;
case MB_FUNC_READ_DISCRETE_INPUTS:
mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_READ_HOLDING_REGISTER:
mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_WRITE_REGISTER:
mb_error = eMBMasterReqWriteHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr,
(USHORT)mb_offset, (USHORT)mb_size,
(USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS );
break;
case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqReadWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (USHORT*)data_ptr,
(USHORT)mb_offset, (USHORT)mb_size,
(LONG)MB_RESPONSE_TICS );
break;
case MB_FUNC_READ_INPUT_REGISTER:
mb_error = eMBMasterReqReadInputRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG) MB_RESPONSE_TICS );
break;
default:
ESP_LOGE(TAG, "%s: Incorrect function in request (%u) ",
__FUNCTION__, mb_command);
mb_error = MB_MRE_NO_REG;
break;
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr,
(USHORT)mb_offset, (USHORT)mb_size,
(USHORT*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqReadWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (USHORT*)data_ptr,
(USHORT)mb_offset, (USHORT)mb_size,
(LONG)MB_SERIAL_API_RESP_TICS );
break;
case MB_FUNC_READ_INPUT_REGISTER:
mb_error = eMBMasterReqReadInputRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG) MB_SERIAL_API_RESP_TICS );
break;
default:
ESP_LOGE(TAG, "%s: Incorrect function in request (%u) ", __FUNCTION__, (unsigned)mb_command);
mb_error = MB_MRE_NO_REG;
break;
}
}
// Propagate the Modbus errors to higher level
@ -261,8 +264,7 @@ static esp_err_t mbc_serial_master_send_request(mb_param_request_t* request, voi
break;
default:
ESP_LOGE(TAG, "%s: Incorrect return code (%x) ",
__FUNCTION__, mb_error);
ESP_LOGE(TAG, "%s: Incorrect return code (%x) ", __FUNCTION__, (int)mb_error);
error = ESP_FAIL;
break;
}
@ -316,13 +318,11 @@ static uint8_t mbc_serial_master_get_command(mb_param_type_t param_type, mb_para
if (mode != MB_PARAM_WRITE) {
command = MB_FUNC_READ_DISCRETE_INPUTS;
} else {
ESP_LOGE(TAG, "%s: Incorrect mode (%u)",
__FUNCTION__, (uint8_t)mode);
ESP_LOGE(TAG, "%s: Incorrect mode (%u)", __FUNCTION__, (unsigned)mode);
}
break;
default:
ESP_LOGE(TAG, "%s: Incorrect param type (%u)",
__FUNCTION__, param_type);
ESP_LOGE(TAG, "%s: Incorrect param type (%u)", __FUNCTION__, (unsigned)param_type);
break;
}
return command;
@ -363,8 +363,7 @@ static esp_err_t mbc_serial_master_set_request(char* name, mb_param_mode_t mode,
request->reg_start = reg_ptr->mb_reg_start;
request->reg_size = reg_ptr->mb_size;
request->command = mbc_serial_master_get_command(reg_ptr->mb_param_type, mode);
MB_MASTER_CHECK((request->command > 0),
ESP_ERR_INVALID_ARG,
MB_MASTER_CHECK((request->command > 0), ESP_ERR_INVALID_ARG,
"mb incorrect command or parameter type.");
if (reg_data != NULL) {
*reg_data = *reg_ptr; // Set the cid registered parameter data
@ -394,16 +393,16 @@ static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name,
error = mbc_serial_master_send_request(&request, value_ptr);
if (error == ESP_OK) {
ESP_LOGD(TAG, "%s: Good response for get cid(%u) = %s",
__FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error));
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
} else {
ESP_LOGD(TAG, "%s: Bad response to get cid(%u) = %s",
__FUNCTION__, reg_info.cid, (char*)esp_err_to_name(error));
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
}
// Set the type of parameter found in the table
*type = reg_info.param_type;
} else {
ESP_LOGE(TAG, "%s: The cid(%u) not found in the data dictionary.",
__FUNCTION__, reg_info.cid);
__FUNCTION__, (unsigned)reg_info.cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
@ -429,16 +428,16 @@ static esp_err_t mbc_serial_master_set_parameter(uint16_t cid, char* name,
error = mbc_serial_master_send_request(&request, value_ptr);
if (error == ESP_OK) {
ESP_LOGD(TAG, "%s: Good response for set cid(%u) = %s",
__FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error));
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
} else {
ESP_LOGD(TAG, "%s: Bad response to set cid(%u) = %s",
__FUNCTION__, reg_info.cid, (char*)esp_err_to_name(error));
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
}
// Set the type of parameter found in the table
*type = reg_info.param_type;
} else {
ESP_LOGE(TAG, "%s: The requested cid(%u) not found in the data dictionary.",
__FUNCTION__, reg_info.cid);
__FUNCTION__, (unsigned)reg_info.cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
@ -550,9 +549,9 @@ eMBErrorCode eMBRegCoilsCBSerialMaster(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
MB_EILLSTATE, "Master interface uninitialized.");
MB_EILLSTATE, "Master interface uninitialized.");
MB_MASTER_CHECK((pucRegBuffer != NULL),
MB_EINVAL, "Master stack processing error.");
MB_EINVAL, "Master stack processing error.");
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
USHORT usRegCoilNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
UCHAR* pucRegCoilsBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
@ -675,8 +674,7 @@ esp_err_t mbc_serial_master_create(void** handler)
if (status != pdPASS) {
vTaskDelete(mbm_opts->mbm_task_handle);
MB_MASTER_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
"mb controller task creation error, xTaskCreate() returns (0x%x).",
(uint32_t)status);
"mb controller task creation error, xTaskCreate() returns (0x%x).", (int)status);
}
MB_MASTER_ASSERT(mbm_opts->mbm_task_handle != NULL); // The task is created but handle is incorrect

View File

@ -59,15 +59,14 @@ static esp_err_t mbc_serial_slave_setup(void* comm_info)
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
mb_slave_comm_info_t* comm_settings = (mb_slave_comm_info_t*)comm_info;
MB_SLAVE_CHECK(((comm_settings->mode == MB_MODE_RTU) || (comm_settings->mode == MB_MODE_ASCII)),
ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).",
(uint32_t)comm_settings->mode);
ESP_ERR_INVALID_ARG, "mb incorrect mode = (%u).",
(unsigned)comm_settings->mode);
MB_SLAVE_CHECK((comm_settings->slave_addr <= MB_ADDRESS_MAX),
ESP_ERR_INVALID_ARG, "mb wrong slave address = (0x%x).",
(uint32_t)comm_settings->slave_addr);
ESP_ERR_INVALID_ARG, "mb wrong slave address = (%u).", (unsigned)comm_settings->slave_addr);
MB_SLAVE_CHECK((comm_settings->port < UART_NUM_MAX), ESP_ERR_INVALID_ARG,
"mb wrong port to set = (0x%x).", (uint32_t)comm_settings->port);
"mb wrong port to set = (%u).", (unsigned)comm_settings->port);
MB_SLAVE_CHECK((comm_settings->parity <= UART_PARITY_ODD), ESP_ERR_INVALID_ARG,
"mb wrong parity option = (0x%x).", (uint32_t)comm_settings->parity);
"mb wrong parity option = (%u).", (unsigned)comm_settings->parity);
// Set communication options of the controller
mbs_opts->mbs_comm = *(mb_communication_info_t*)comm_settings;
@ -92,10 +91,10 @@ static esp_err_t mbc_serial_slave_start(void)
MB_PORT_PARITY_GET(comm_info->parity));
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack initialization failure, eMBInit() returns (0x%x).", status);
"mb stack initialization failure, eMBInit() returns (0x%x).", (int)status);
status = eMBEnable();
MB_SLAVE_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, eMBEnable() returned (0x%x).", (int)status);
// Set the mbcontroller start flag
EventBits_t flag = xEventGroupSetBits(mbs_opts->mbs_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
@ -154,7 +153,7 @@ static esp_err_t mbc_serial_slave_destroy(void)
EventBits_t flag = xEventGroupClearBits(mbs_opts->mbs_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
// Disable and then destroy the Modbus stack
mb_error = eMBDisable();
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
@ -163,7 +162,7 @@ static esp_err_t mbc_serial_slave_destroy(void)
(void)vEventGroupDelete(mbs_opts->mbs_event_group);
mb_error = eMBClose();
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack close failure returned (0x%x).", (uint32_t)mb_error);
"mb stack close failure returned (0x%x).", (int)mb_error);
mbs_interface_ptr = NULL;
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
return ESP_OK;
@ -201,7 +200,7 @@ esp_err_t mbc_serial_slave_create(void** handler)
MB_CONTROLLER_NOTIFY_QUEUE_SIZE,
sizeof(mb_param_info_t));
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
ESP_ERR_NO_MEM, "mb notify queue creation error.");
ESP_ERR_NO_MEM, "mb notify queue creation error.");
// Create Modbus controller task
status = xTaskCreatePinnedToCore((void*)&modbus_slave_task,
"modbus_slave_task",
@ -213,8 +212,7 @@ esp_err_t mbc_serial_slave_create(void** handler)
if (status != pdPASS) {
vTaskDelete(mbs_opts->mbs_task_handle);
MB_SLAVE_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
"mb controller task creation error, xTaskCreate() returns (0x%x).",
(uint32_t)status);
"mb controller task creation error, xTaskCreate() returns (0x%x).", (int)status);
}
MB_SLAVE_ASSERT(mbs_opts->mbs_task_handle != NULL); // The task is created but handle is incorrect

View File

@ -29,9 +29,10 @@
/*-----------------------Master mode use these variables----------------------*/
// The response time is average processing time + data transmission
#define MB_RESPONSE_TIMEOUT pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND)
#define MB_TCP_CONNECTION_TOUT pdMS_TO_TICKS(CONFIG_FMB_TCP_CONNECTION_TOUT_SEC * 1000)
#define MB_TCP_CONNECTION_TOUT (pdMS_TO_TICKS(CONFIG_FMB_TCP_CONNECTION_TOUT_SEC * 1000))
// Actual wait time depends on the response timer
#define MB_TCP_API_RESP_TICS (pdMS_TO_TICKS(MB_MAX_RESPONSE_TIME_MS))
static mb_master_interface_t* mbm_interface_ptr = NULL;
static const char *TAG = "MB_CONTROLLER_MASTER";
@ -81,7 +82,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);
@ -120,12 +121,11 @@ static esp_err_t mbc_tcp_master_setup(void* comm_info)
const mb_communication_info_t* comm_info_ptr = (mb_communication_info_t*)comm_info;
// Check communication options
MB_MASTER_CHECK((comm_info_ptr->ip_mode == MB_MODE_TCP),
ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).",
(uint32_t)comm_info_ptr->ip_mode);
ESP_ERR_INVALID_ARG, "mb incorrect mode = (%u).", (unsigned)comm_info_ptr->ip_mode);
MB_MASTER_CHECK((comm_info_ptr->ip_addr != NULL),
ESP_ERR_INVALID_ARG, "mb wrong slave ip address table.");
MB_MASTER_CHECK(((comm_info_ptr->ip_addr_type == MB_IPV4) || (comm_info_ptr->ip_addr_type == MB_IPV6)),
ESP_ERR_INVALID_ARG, "mb incorrect addr type = (0x%x).", (uint8_t)comm_info_ptr->ip_addr_type);
ESP_ERR_INVALID_ARG, "mb incorrect addr type = (%u).", (unsigned)comm_info_ptr->ip_addr_type);
MB_MASTER_CHECK((comm_info_ptr->ip_netif_ptr != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect iface address.");
// Save the communication options
@ -159,7 +159,7 @@ static esp_err_t mbc_tcp_master_start(void)
status = eMBMasterEnable();
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack enable failure, eMBMasterEnable() returned (0x%x).", (uint32_t)status);
"mb stack enable failure, eMBMasterEnable() returned (0x%x).", (int)status);
// Add slave IP address for each slave to initialize connection
mb_slave_addr_entry_t *p_slave_info;
@ -194,7 +194,7 @@ static esp_err_t mbc_tcp_master_destroy(void)
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
mb_error = eMBMasterClose();
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack close failure returned (0x%x).", (uint32_t)mb_error);
"mb stack close failure returned (0x%x).", (int)mb_error);
// Stop polling by clearing correspondent bit in the event group
xEventGroupClearBits(mbm_opts->mbm_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
@ -237,7 +237,7 @@ static esp_err_t mbc_tcp_master_set_descriptor(const mb_parameter_descriptor_t*
// Add it to slave list if not there.
if (!p_slave) {
// Is the IP address correctly defined for the slave?
MB_MASTER_CHECK((comm_ip_table[slave_cnt]), ESP_ERR_INVALID_STATE, "mb missing IP address for cid #%d.", reg_ptr->cid);
MB_MASTER_CHECK((comm_ip_table[slave_cnt]), ESP_ERR_INVALID_STATE, "mb missing IP address for cid #%u.", (unsigned)reg_ptr->cid);
// Add slave to the list
MB_MASTER_ASSERT(mbc_tcp_master_add_slave(idx, reg_ptr->mb_slave_addr, comm_ip_table[slave_cnt++]) == ESP_OK);
}
@ -255,68 +255,72 @@ static esp_err_t mbc_tcp_master_send_request(mb_param_request_t* request, void*
MB_MASTER_CHECK((request != NULL), ESP_ERR_INVALID_ARG, "mb request structure.");
MB_MASTER_CHECK((data_ptr != NULL), ESP_ERR_INVALID_ARG, "mb incorrect data pointer.");
eMBMasterReqErrCode mb_error = MB_MRE_NO_REG;
eMBMasterReqErrCode mb_error = MB_MRE_MASTER_BUSY;
esp_err_t error = ESP_FAIL;
uint8_t mb_slave_addr = request->slave_addr;
uint8_t mb_command = request->command;
uint16_t mb_offset = request->reg_start;
uint16_t mb_size = request->reg_size;
if (xMBMasterRunResTake(MB_TCP_API_RESP_TICS)) {
uint8_t mb_slave_addr = request->slave_addr;
uint8_t mb_command = request->command;
uint16_t mb_offset = request->reg_start;
uint16_t mb_size = request->reg_size;
// Set the buffer for callback function processing of received data
mbm_opts->mbm_reg_buffer_ptr = (uint8_t*)data_ptr;
mbm_opts->mbm_reg_buffer_size = mb_size;
// Set the buffer for callback function processing of received data
mbm_opts->mbm_reg_buffer_ptr = (uint8_t*)data_ptr;
mbm_opts->mbm_reg_buffer_size = mb_size;
vMBMasterRunResRelease();
// Calls appropriate request function to send request and waits response
switch(mb_command)
{
case MB_FUNC_READ_COILS:
mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_RESPONSE_TIMEOUT);
break;
case MB_FUNC_WRITE_SINGLE_COIL:
mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT *)data_ptr, (LONG)MB_RESPONSE_TIMEOUT);
break;
case MB_FUNC_WRITE_MULTIPLE_COILS:
mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (UCHAR *)data_ptr,
(LONG)MB_RESPONSE_TIMEOUT);
break;
case MB_FUNC_READ_DISCRETE_INPUTS:
mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_RESPONSE_TIMEOUT);
break;
case MB_FUNC_READ_HOLDING_REGISTER:
mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_RESPONSE_TIMEOUT);
break;
case MB_FUNC_WRITE_REGISTER:
mb_error = eMBMasterReqWriteHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT *)data_ptr, (LONG)MB_RESPONSE_TIMEOUT);
break;
// Calls appropriate request function to send request and waits response
switch(mb_command)
{
case MB_FUNC_READ_COILS:
mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_WRITE_SINGLE_COIL:
mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT *)data_ptr, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_WRITE_MULTIPLE_COILS:
mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (UCHAR *)data_ptr,
(LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_READ_DISCRETE_INPUTS:
mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_READ_HOLDING_REGISTER:
mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_WRITE_REGISTER:
mb_error = eMBMasterReqWriteHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
*(USHORT *)data_ptr, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqWriteMultipleHoldingRegister((UCHAR)mb_slave_addr,
(USHORT)mb_offset, (USHORT)mb_size,
(USHORT *)data_ptr, (LONG)MB_RESPONSE_TIMEOUT);
break;
case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqReadWriteMultipleHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (USHORT *)data_ptr,
(USHORT)mb_offset, (USHORT)mb_size,
(LONG)MB_RESPONSE_TIMEOUT);
break;
case MB_FUNC_READ_INPUT_REGISTER:
mb_error = eMBMasterReqReadInputRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_RESPONSE_TIMEOUT);
break;
default:
ESP_LOGE(TAG, "%s: Incorrect function in request (%u) ",
__FUNCTION__, mb_command);
mb_error = MB_MRE_NO_REG;
break;
}
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqWriteMultipleHoldingRegister((UCHAR)mb_slave_addr,
(USHORT)mb_offset, (USHORT)mb_size,
(USHORT *)data_ptr, (LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
mb_error = eMBMasterReqReadWriteMultipleHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (USHORT *)data_ptr,
(USHORT)mb_offset, (USHORT)mb_size,
(LONG)MB_TCP_API_RESP_TICS);
break;
case MB_FUNC_READ_INPUT_REGISTER:
mb_error = eMBMasterReqReadInputRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
(USHORT)mb_size, (LONG)MB_TCP_API_RESP_TICS);
break;
default:
ESP_LOGE(TAG, "%s: Incorrect function in request (%u) ", __FUNCTION__, (unsigned)mb_command);
mb_error = MB_MRE_NO_REG;
break;
}
}
// Propagate the Modbus errors to higher level
switch(mb_error)
@ -343,7 +347,7 @@ static esp_err_t mbc_tcp_master_send_request(mb_param_request_t* request, void*
break;
default:
ESP_LOGE(TAG, "%s: Incorrect return code (%x) ", __FUNCTION__, mb_error);
ESP_LOGE(TAG, "%s: Incorrect return code (0x%x) ", __FUNCTION__, (unsigned)mb_error);
error = ESP_FAIL;
break;
}
@ -387,11 +391,11 @@ static uint8_t mbc_tcp_master_get_command(mb_param_type_t param_type, mb_param_m
if (mode != MB_PARAM_WRITE) {
command = MB_FUNC_READ_DISCRETE_INPUTS;
} else {
ESP_LOGE(TAG, "%s: Incorrect mode (%u)", __FUNCTION__, (uint8_t)mode);
ESP_LOGE(TAG, "%s: Incorrect mode (%u)", __FUNCTION__, (unsigned)mode);
}
break;
default:
ESP_LOGE(TAG, "%s: Incorrect param type (%u)", __FUNCTION__, param_type);
ESP_LOGE(TAG, "%s: Incorrect param type (%u)", __FUNCTION__, (unsigned)param_type);
break;
}
return command;
@ -423,7 +427,7 @@ static esp_err_t mbc_tcp_master_set_param_data(void* dest, void* src, mb_descr_t
break;
default:
ESP_LOGE(TAG, "%s: Incorrect param type (%u).",
__FUNCTION__, (uint16_t)param_type);
__FUNCTION__, (unsigned)param_type);
err = ESP_ERR_NOT_SUPPORTED;
break;
}
@ -503,15 +507,13 @@ static esp_err_t mbc_tcp_master_get_parameter(uint16_t cid, char* name, uint8_t*
}
} else {
ESP_LOGD(TAG, "%s: Bad response to get cid(%u) = %s",
__FUNCTION__, reg_info.cid, (char*)esp_err_to_name(error));
error = ESP_ERR_INVALID_RESPONSE;
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
}
free(pdata);
// Set the type of parameter found in the table
*type = reg_info.param_type;
} else {
ESP_LOGE(TAG, "%s: The cid(%u) not found in the data dictionary.",
__FUNCTION__, reg_info.cid);
ESP_LOGE(TAG, "%s: The cid(%u) not found in the data dictionary.", __FUNCTION__, reg_info.cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
@ -550,14 +552,14 @@ static esp_err_t mbc_tcp_master_set_parameter(uint16_t cid, char* name, uint8_t*
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
} else {
ESP_LOGD(TAG, "%s: Bad response to set cid(%u) = %s",
__FUNCTION__, reg_info.cid, (char*)esp_err_to_name(error));
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
}
free(pdata);
// Set the type of parameter found in the table
*type = reg_info.param_type;
} else {
ESP_LOGE(TAG, "%s: The requested cid(%u) not found in the data dictionary.",
__FUNCTION__, reg_info.cid);
__FUNCTION__, (unsigned)reg_info.cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
@ -782,8 +784,7 @@ esp_err_t mbc_tcp_master_create(void** handler)
if (status != pdPASS) {
vTaskDelete(mbm_opts->mbm_task_handle);
MB_MASTER_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
"mb controller task creation error, xTaskCreate() returns (0x%x).",
(uint32_t)status);
"mb controller task creation error, xTaskCreate() returns (%u).", (unsigned)status);
}
MB_MASTER_ASSERT(mbm_opts->mbm_task_handle != NULL); // The task is created but handle is incorrect

View File

@ -74,7 +74,7 @@
#define MB_SLAVE_FMT(fmt) "Slave #%d, Socket(#%d)(%s)"fmt
/* ----------------------- Types & Prototypes --------------------------------*/
void vMBPortEventClose( void );
void vMBPortEventClose(void);
/* ----------------------- Static variables ---------------------------------*/
static const char *TAG = "MB_TCP_MASTER_PORT";
@ -94,10 +94,10 @@ BOOL xMBTCPPortMasterWaitEvent(EventGroupHandle_t xEventHandle, EventBits_t xEve
xMasterEventHandle = xEventHandle;
xMasterEvent = xEvent;
BaseType_t status = xEventGroupWaitBits(xMasterEventHandle,
(BaseType_t)(xEvent),
pdFALSE, // do not clear start bit
pdFALSE,
usTimeout);
(BaseType_t)(xEvent),
pdFALSE, // do not clear start bit
pdFALSE,
usTimeout);
return (BOOL)(status & xEvent);
}
@ -129,12 +129,12 @@ xMBMasterTCPPortInit( USHORT usTCPPort )
// Create task for packet processing
BaseType_t xErr = xTaskCreatePinnedToCore(vMBTCPPortMasterTask,
"tcp_master_task",
MB_TCP_STACK_SIZE,
NULL,
MB_TCP_TASK_PRIO,
&xMbPortConfig.xMbTcpTaskHandle,
MB_PORT_TASK_AFFINITY);
"tcp_master_task",
MB_TCP_STACK_SIZE,
NULL,
MB_TCP_TASK_PRIO,
&xMbPortConfig.xMbTcpTaskHandle,
MB_PORT_TASK_AFFINITY);
if (xErr != pdTRUE)
{
ESP_LOGE(TAG, "TCP master task creation failure.");
@ -148,7 +148,7 @@ xMBMasterTCPPortInit( USHORT usTCPPort )
return bOkay;
}
static MbSlaveInfo_t* vMBTCPPortMasterFindSlaveInfo(UCHAR ucSlaveAddr)
static MbSlaveInfo_t *vMBTCPPortMasterFindSlaveInfo(UCHAR ucSlaveAddr)
{
int xIndex;
BOOL xFound = false;
@ -161,12 +161,12 @@ static MbSlaveInfo_t* vMBTCPPortMasterFindSlaveInfo(UCHAR ucSlaveAddr)
}
if (!xFound) {
xMbPortConfig.pxMbSlaveCurrInfo = NULL;
ESP_LOGE(TAG, "Slave info for short address %d not found.", ucSlaveAddr);
ESP_LOGE(TAG, "Slave info for short address %u not found.", ucSlaveAddr);
}
return xMbPortConfig.pxMbSlaveCurrInfo;
}
static MbSlaveInfo_t* vMBTCPPortMasterGetCurrInfo(void)
static MbSlaveInfo_t *vMBTCPPortMasterGetCurrInfo(void)
{
if (!xMbPortConfig.pxMbSlaveCurrInfo) {
ESP_LOGE(TAG, "Incorrect current slave info.");
@ -217,7 +217,8 @@ static void vMBTCPPortMasterMStoTimeVal(USHORT usTimeoutMs, struct timeval *tv)
tv->tv_usec = (usTimeoutMs - (tv->tv_sec * 1000)) * 1000;
}
static void xMBTCPPortMasterCheckShutdown(void) {
static void xMBTCPPortMasterCheckShutdown(void)
{
// First check if the task is not flagged for shutdown
if (xShutdownSemaphore) {
xSemaphoreGive(xShutdownSemaphore);
@ -225,24 +226,24 @@ static void xMBTCPPortMasterCheckShutdown(void) {
}
}
static BOOL xMBTCPPortMasterCloseConnection(MbSlaveInfo_t* pxInfo)
static BOOL xMBTCPPortMasterCloseConnection(MbSlaveInfo_t *pxInfo)
{
if (!pxInfo) {
return FALSE;
}
if (pxInfo->xSockId == -1) {
ESP_LOGE(TAG, "Wrong socket info or disconnected socket: %d, skip.", pxInfo->xSockId);
ESP_LOGE(TAG, "Wrong socket info or disconnected socket: %d, skip.", (int)pxInfo->xSockId);
return FALSE;
}
if (shutdown(pxInfo->xSockId, SHUT_RDWR) == -1) {
ESP_LOGV(TAG, "Shutdown failed sock %d, errno=%d", pxInfo->xSockId, errno);
ESP_LOGV(TAG, "Shutdown failed sock %d, errno=%u", (int)pxInfo->xSockId, (unsigned)errno);
}
close(pxInfo->xSockId);
pxInfo->xSockId = -1;
return TRUE;
}
void vMBTCPPortMasterSetNetOpt(void* pvNetIf, eMBPortIpVer xIpVersion, eMBPortProto xProto)
void vMBTCPPortMasterSetNetOpt(void *pvNetIf, eMBPortIpVer xIpVersion, eMBPortProto xProto)
{
xMbPortConfig.pvNetIface = pvNetIf;
xMbPortConfig.eMbProto = xProto;
@ -250,7 +251,7 @@ void vMBTCPPortMasterSetNetOpt(void* pvNetIf, eMBPortIpVer xIpVersion, eMBPortPr
}
// Function returns time left for response processing according to response timeout
static int64_t xMBTCPPortMasterGetRespTimeLeft(MbSlaveInfo_t* pxInfo)
static int64_t xMBTCPPortMasterGetRespTimeLeft(MbSlaveInfo_t *pxInfo)
{
if (!pxInfo) {
return 0;
@ -261,7 +262,7 @@ static int64_t xMBTCPPortMasterGetRespTimeLeft(MbSlaveInfo_t* pxInfo)
}
// Wait socket ready to read state
static int vMBTCPPortMasterRxCheck(int xSd, fd_set* pxFdSet, int xTimeMs)
static int vMBTCPPortMasterRxCheck(int xSd, fd_set *pxFdSet, int xTimeMs)
{
fd_set xReadSet = *pxFdSet;
fd_set xErrorSet = *pxFdSet;
@ -281,32 +282,36 @@ 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;
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);
MB_PORT_CHECK((pxInfo && pxInfo->xSockId > -1), -1, "Try to read incorrect socket = #%d.", (int)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.",
pxInfo->xSockId, pxInfo->pcIpAddr);
(int)pxInfo->xSockId, pxInfo->pcIpAddr);
return ERR_CONN;
} else {
// Other error occurred during receiving
ESP_LOGE(TAG, "Socket(#%d)(%s) receive error, length=%d, errno=%d",
pxInfo->xSockId, pxInfo->pcIpAddr, xLength, errno);
ESP_LOGE(TAG, "Socket(#%d)(%s) receive error, length=%u, errno=%u",
(int)pxInfo->xSockId, pxInfo->pcIpAddr, (unsigned)xLength, (unsigned)errno);
return -1;
}
} else if (xLength) {
@ -316,12 +321,11 @@ static int xMBTCPPortMasterGetBuf(MbSlaveInfo_t* pxInfo, UCHAR* pucDstBuf, USHOR
if (xMBTCPPortMasterGetRespTimeLeft(pxInfo) == 0) {
return ERR_TIMEOUT;
}
vTaskDelay(1);
}
return usLength;
}
static int vMBTCPPortMasterReadPacket(MbSlaveInfo_t* pxInfo)
static int vMBTCPPortMasterReadPacket(MbSlaveInfo_t *pxInfo)
{
int xLength = 0;
int xRet = 0;
@ -331,28 +335,30 @@ 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;
} else if (xRet != MB_TCP_UID) {
ESP_LOGD(TAG, "Socket (#%d)(%s), Fail to read modbus header. ret=%d",
pxInfo->xSockId, pxInfo->pcIpAddr, xRet);
(int)pxInfo->xSockId, pxInfo->pcIpAddr, (int)xRet);
pxInfo->xRcvErr = ERR_VAL;
return ERR_VAL;
}
// 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;
} else if (xRet != xLength) {
// Received incorrect or fragmented packet.
ESP_LOGD(TAG, "Socket(#%d)(%s) incorrect packet, length=%d, TID=0x%02x, errno=%d(%s)",
pxInfo->xSockId, pxInfo->pcIpAddr, pxInfo->usRcvPos,
usTidRcv, errno, strerror(errno));
ESP_LOGD(TAG, "Socket(#%d)(%s) incorrect packet, length=%u, TID=0x%02x, errno=%u(%s)",
(int)pxInfo->xSockId, pxInfo->pcIpAddr, (int)pxInfo->usRcvPos,
(int)usTidRcv, (unsigned)errno, strerror(errno));
pxInfo->xRcvErr = ERR_VAL;
return ERR_VAL;
}
@ -361,21 +367,21 @@ static int vMBTCPPortMasterReadPacket(MbSlaveInfo_t* pxInfo)
// Check transaction identifier field in the incoming packet.
if ((pxInfo->usTidCnt - 1) != usTidRcv) {
ESP_LOGD(TAG, "Socket (#%d)(%s), incorrect TID(0x%02x)!=(0x%02x) received, discard data.",
pxInfo->xSockId, pxInfo->pcIpAddr, usTidRcv, (pxInfo->usTidCnt - 1));
(int)pxInfo->xSockId, pxInfo->pcIpAddr, (int)usTidRcv, (int)(pxInfo->usTidCnt - 1));
pxInfo->xRcvErr = ERR_BUF;
return ERR_BUF;
}
pxInfo->usRcvPos += xRet + MB_TCP_UID;
ESP_LOGD(TAG, "Socket(#%d)(%s) get data, length=%d, TID=0x%02x, errno=%d(%s)",
pxInfo->xSockId, pxInfo->pcIpAddr, pxInfo->usRcvPos,
usTidRcv, errno, strerror(errno));
ESP_LOGD(TAG, "Socket(#%d)(%s) get data, length=%u, TID=0x%02x, errno=%u(%s)",
(int)pxInfo->xSockId, pxInfo->pcIpAddr, (unsigned)pxInfo->usRcvPos,
(unsigned)usTidRcv, (unsigned)errno, strerror(errno));
pxInfo->xRcvErr = ERR_OK;
return pxInfo->usRcvPos;
}
return -1;
}
static err_t xMBTCPPortMasterSetNonBlocking(MbSlaveInfo_t* pxInfo)
static err_t xMBTCPPortMasterSetNonBlocking(MbSlaveInfo_t *pxInfo)
{
if (!pxInfo) {
return ERR_CONN;
@ -383,21 +389,21 @@ static err_t xMBTCPPortMasterSetNonBlocking(MbSlaveInfo_t* pxInfo)
// Set non blocking attribute for socket
ULONG ulFlags = fcntl(pxInfo->xSockId, F_GETFL);
if (fcntl(pxInfo->xSockId, F_SETFL, ulFlags | O_NONBLOCK) == -1) {
ESP_LOGE(TAG, "Socket(#%d)(%s), fcntl() call error=%d",
pxInfo->xSockId, pxInfo->pcIpAddr, errno);
ESP_LOGE(TAG, "Socket(#%d)(%s), fcntl() call error=%u",
(int)pxInfo->xSockId, pxInfo->pcIpAddr, (unsigned)errno);
return ERR_WOULDBLOCK;
}
return ERR_OK;
}
static void vMBTCPPortSetKeepAlive(MbSlaveInfo_t* pxInfo)
static void vMBTCPPortSetKeepAlive(MbSlaveInfo_t *pxInfo)
{
int optval = 1;
setsockopt(pxInfo->xSockId, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
}
// Check connection for timeout helper
static err_t xMBTCPPortMasterCheckAlive(MbSlaveInfo_t* pxInfo, ULONG xTimeoutMs)
static err_t xMBTCPPortMasterCheckAlive(MbSlaveInfo_t *pxInfo, ULONG xTimeoutMs)
{
fd_set xWriteSet;
fd_set xErrorSet;
@ -416,13 +422,13 @@ static err_t xMBTCPPortMasterCheckAlive(MbSlaveInfo_t* pxInfo, ULONG xTimeoutMs)
if (errno == EINPROGRESS) {
xErr = ERR_INPROGRESS;
} else {
ESP_LOGV(TAG, MB_SLAVE_FMT(" connection, select write err(errno) = %d(%d)."),
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, xErr, errno);
ESP_LOGV(TAG, MB_SLAVE_FMT(" connection, select write err(errno) = 0x%x(%u)."),
(int)pxInfo->xIndex, (int)pxInfo->xSockId, pxInfo->pcIpAddr, (int)xErr, (unsigned)errno);
xErr = ERR_CONN;
}
} else if (xErr == 0) {
ESP_LOGV(TAG, "Socket(#%d)(%s), connection timeout occurred, err(errno) = %d(%d).",
pxInfo->xSockId, pxInfo->pcIpAddr, xErr, errno);
ESP_LOGV(TAG, "Socket(#%d)(%s), connection timeout occurred, err(errno) = 0x%x(%u).",
(int)pxInfo->xSockId, pxInfo->pcIpAddr, (int)xErr, (unsigned)errno);
return ERR_INPROGRESS;
} else {
int xOptErr = 0;
@ -430,12 +436,12 @@ static err_t xMBTCPPortMasterCheckAlive(MbSlaveInfo_t* pxInfo, ULONG xTimeoutMs)
// Check socket error
xErr = getsockopt(pxInfo->xSockId, SOL_SOCKET, SO_ERROR, (void*)&xOptErr, (socklen_t*)&ulOptLen);
if (xOptErr != 0) {
ESP_LOGD(TAG, "Socket(#%d)(%s), sock error occurred (%d).",
pxInfo->xSockId, pxInfo->pcIpAddr, xOptErr);
ESP_LOGD(TAG, "Socket(#%d)(%s), sock error occurred (%u).",
(int)pxInfo->xSockId, pxInfo->pcIpAddr, (unsigned)xOptErr);
return ERR_CONN;
}
ESP_LOGV(TAG, "Socket(#%d)(%s), is alive.",
pxInfo->xSockId, pxInfo->pcIpAddr);
(int)pxInfo->xSockId, pxInfo->pcIpAddr);
return ERR_OK;
}
} else {
@ -445,14 +451,14 @@ static err_t xMBTCPPortMasterCheckAlive(MbSlaveInfo_t* pxInfo, ULONG xTimeoutMs)
}
// Resolve host name and/or fill the IP address structure
static BOOL xMBTCPPortMasterCheckHost(const CHAR* pcHostStr, ip_addr_t* pxHostAddr)
static BOOL xMBTCPPortMasterCheckHost(const CHAR *pcHostStr, ip_addr_t *pxHostAddr)
{
MB_PORT_CHECK((pcHostStr), FALSE, "Wrong host name or IP.");
CHAR cStr[45];
CHAR* pcStr = &cStr[0];
CHAR *pcStr = &cStr[0];
ip_addr_t xTargetAddr;
struct addrinfo xHint;
struct addrinfo* pxAddrList;
struct addrinfo *pxAddrList;
memset(&xHint, 0, sizeof(xHint));
// Do name resolution for both protocols
xHint.ai_family = AF_UNSPEC;
@ -487,10 +493,10 @@ static BOOL xMBTCPPortMasterCheckHost(const CHAR* pcHostStr, ip_addr_t* pxHostAd
return TRUE;
}
BOOL xMBTCPPortMasterAddSlaveIp(const USHORT usIndex, const CHAR* pcIpStr, UCHAR ucSlaveAddress)
BOOL xMBTCPPortMasterAddSlaveIp(const USHORT usIndex, const CHAR *pcIpStr, UCHAR ucSlaveAddress)
{
BOOL xRes = FALSE;
MbSlaveAddrInfo_t xSlaveAddrInfo = { 0 };
MbSlaveAddrInfo_t xSlaveAddrInfo = {0};
MB_PORT_CHECK(xMbPortConfig.xConnectQueue != NULL, FALSE, "Wrong slave IP address to add.");
if (pcIpStr && (usIndex != 0xFF)) {
xRes = xMBTCPPortMasterCheckHost(pcIpStr, NULL);
@ -499,14 +505,14 @@ BOOL xMBTCPPortMasterAddSlaveIp(const USHORT usIndex, const CHAR* pcIpStr, UCHAR
xSlaveAddrInfo.pcIPAddr = pcIpStr;
xSlaveAddrInfo.usIndex = usIndex;
xSlaveAddrInfo.ucSlaveAddr = ucSlaveAddress;
BaseType_t xStatus = xQueueSend(xMbPortConfig.xConnectQueue, (void*)&xSlaveAddrInfo, 100);
BaseType_t xStatus = xQueueSend(xMbPortConfig.xConnectQueue, (void *)&xSlaveAddrInfo, 100);
MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "FAIL to add slave IP address: [%s].", pcIpStr);
}
return xRes;
}
// Unblocking connect function
static err_t xMBTCPPortMasterConnect(MbSlaveInfo_t* pxInfo)
static err_t xMBTCPPortMasterConnect(MbSlaveInfo_t *pxInfo)
{
if (!pxInfo) {
return ERR_CONN;
@ -514,15 +520,15 @@ static err_t xMBTCPPortMasterConnect(MbSlaveInfo_t* pxInfo)
err_t xErr = ERR_OK;
CHAR cStr[128];
CHAR* pcStr = NULL;
CHAR *pcStr = NULL;
ip_addr_t xTargetAddr;
struct addrinfo xHint;
struct addrinfo* pxAddrList;
struct addrinfo* pxCurAddr;
struct addrinfo *pxAddrList;
struct addrinfo *pxCurAddr;
memset(&xHint, 0, sizeof(xHint));
// Do name resolution for both protocols
//xHint.ai_family = AF_UNSPEC; Todo: Find a reason why AF_UNSPEC does not work
// xHint.ai_family = AF_UNSPEC; Todo: Find a reason why AF_UNSPEC does not work
xHint.ai_flags = AI_ADDRCONFIG; // get IPV6 address if supported, otherwise IPV4
xHint.ai_family = (xMbPortConfig.eMbIpVer == MB_PORT_IPV4) ? AF_INET : AF_INET6;
xHint.ai_socktype = (pxInfo->xMbProto == MB_PROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
@ -553,19 +559,19 @@ static err_t xMBTCPPortMasterConnect(MbSlaveInfo_t* pxInfo)
inet6_addr_to_ip6addr(ip_2_ip6(&xTargetAddr), &addr6);
pcStr = ip6addr_ntoa_r(ip_2_ip6(&xTargetAddr), cStr, sizeof(cStr));
// Set scope id to fix routing issues with local address
((struct sockaddr_in6 *) (pxCurAddr->ai_addr))->sin6_scope_id =
esp_netif_get_netif_impl_index(xMbPortConfig.pvNetIface);
((struct sockaddr_in6 *)(pxCurAddr->ai_addr))->sin6_scope_id =
esp_netif_get_netif_impl_index(xMbPortConfig.pvNetIface);
}
#endif
if (pxInfo->xSockId <= 0) {
pxInfo->xSockId = socket(pxCurAddr->ai_family, pxCurAddr->ai_socktype, pxCurAddr->ai_protocol);
if (pxInfo->xSockId < 0) {
ESP_LOGE(TAG, "Unable to create socket: #%d, errno %d", pxInfo->xSockId, errno);
ESP_LOGE(TAG, "Unable to create socket: #%d, errno %u", (int)pxInfo->xSockId, (unsigned)errno);
xErr = ERR_IF;
continue;
}
} else {
ESP_LOGV(TAG, "Socket (#%d)(%s) created.", pxInfo->xSockId, cStr);
ESP_LOGV(TAG, "Socket (#%d)(%s) created.", (int)pxInfo->xSockId, cStr);
}
// Set non blocking attribute for socket
@ -576,8 +582,8 @@ static err_t xMBTCPPortMasterConnect(MbSlaveInfo_t* pxInfo)
xErr = connect(pxInfo->xSockId, (struct sockaddr*)pxCurAddr->ai_addr, pxCurAddr->ai_addrlen);
if ((xErr < 0) && (errno == EINPROGRESS || errno == EALREADY)) {
// The unblocking connect is pending (check status later) or already connected
ESP_LOGV(TAG, "Socket(#%d)(%s) connection is pending, errno %d (%s).",
pxInfo->xSockId, cStr, errno, strerror(errno));
ESP_LOGV(TAG, "Socket(#%d)(%s) connection is pending, errno %u (%s).",
(int)pxInfo->xSockId, cStr, (unsigned)errno, strerror(errno));
// Set keep alive flag in socket options
vMBTCPPortSetKeepAlive(pxInfo);
@ -589,13 +595,13 @@ static err_t xMBTCPPortMasterConnect(MbSlaveInfo_t* pxInfo)
continue;
} else if (xErr != ERR_OK) {
// Other error occurred during connection
ESP_LOGV(TAG, MB_SLAVE_FMT(" unable to connect, error=%d, errno %d (%s)"),
pxInfo->xIndex, pxInfo->xSockId, cStr, xErr, errno, strerror(errno));
ESP_LOGV(TAG, MB_SLAVE_FMT(" unable to connect, error=0x%x, errno %u (%s)"),
(int)pxInfo->xIndex, (int)pxInfo->xSockId, cStr, (int)xErr, (unsigned)errno, strerror(errno));
xMBTCPPortMasterCloseConnection(pxInfo);
xErr = ERR_CONN;
} else {
ESP_LOGI(TAG, MB_SLAVE_FMT(", successfully connected."),
pxInfo->xIndex, pxInfo->xSockId, cStr);
(int)pxInfo->xIndex, (int)pxInfo->xSockId, cStr);
continue;
}
}
@ -604,9 +610,9 @@ static err_t xMBTCPPortMasterConnect(MbSlaveInfo_t* pxInfo)
}
// Find the first slave info whose descriptor is set in xFdSet
static MbSlaveInfo_t* xMBTCPPortMasterGetSlaveReady(fd_set* pxFdSet)
static MbSlaveInfo_t *xMBTCPPortMasterGetSlaveReady(fd_set *pxFdSet)
{
MbSlaveInfo_t* pxInfo = NULL;
MbSlaveInfo_t *pxInfo = NULL;
// Slave connection loop
for (int xIndex = 0; (xIndex < MB_TCP_PORT_MAX_CONN); xIndex++) {
@ -619,13 +625,13 @@ static MbSlaveInfo_t* xMBTCPPortMasterGetSlaveReady(fd_set* pxFdSet)
}
}
}
return (MbSlaveInfo_t*)NULL;
return (MbSlaveInfo_t *)NULL;
}
static int xMBTCPPortMasterCheckConnState(fd_set* pxFdSet)
static int xMBTCPPortMasterCheckConnState(fd_set *pxFdSet)
{
fd_set xConnSetCheck = *pxFdSet;
MbSlaveInfo_t* pxInfo = NULL;
MbSlaveInfo_t *pxInfo = NULL;
int64_t xTime = 0;
int xErr = 0;
int xCount = 0;
@ -636,12 +642,12 @@ static int xMBTCPPortMasterCheckConnState(fd_set* pxFdSet)
xErr = xMBTCPPortMasterCheckAlive(pxInfo, 0);
if ((xErr < 0) && (((xTime - pxInfo->xRecvTimeStamp) > MB_TCP_RECONNECT_TIMEOUT) ||
((xTime - pxInfo->xSendTimeStamp) > MB_TCP_RECONNECT_TIMEOUT))) {
ESP_LOGI(TAG, MB_SLAVE_FMT(", slave is down, off_time[r][w](us) = [%ju][%ju]."),
pxInfo->xIndex,
pxInfo->xSockId,
pxInfo->pcIpAddr,
(int64_t)(xTime - pxInfo->xRecvTimeStamp),
(int64_t)(xTime - pxInfo->xSendTimeStamp));
ESP_LOGI(TAG, MB_SLAVE_FMT(", slave is down, off_time[r][w](us) = [%" PRIu64 "][%" PRIu64 "]."),
(int)pxInfo->xIndex,
(int)pxInfo->xSockId,
pxInfo->pcIpAddr,
(int64_t)(xTime - pxInfo->xRecvTimeStamp),
(int64_t)(xTime - pxInfo->xSendTimeStamp));
xCount++;
}
}
@ -649,7 +655,7 @@ static int xMBTCPPortMasterCheckConnState(fd_set* pxFdSet)
return xCount;
}
static void xMBTCPPortMasterFsmSetError(eMBMasterErrorEventType xErrType, eMBMasterEventType xPostEvent)
static void xMBTCPPortMasterFsmSetError(eMBMasterErrorEventType xErrType, eMBMasterEventEnum xPostEvent)
{
vMBMasterPortTimersDisable();
vMBMasterSetErrorType(xErrType);
@ -658,8 +664,8 @@ static void xMBTCPPortMasterFsmSetError(eMBMasterErrorEventType xErrType, eMBMas
static void vMBTCPPortMasterTask(void *pvParameters)
{
MbSlaveInfo_t* pxInfo;
MbSlaveInfo_t* pxCurrInfo;
MbSlaveInfo_t *pxInfo;
MbSlaveInfo_t *pxCurrInfo;
fd_set xConnSet;
fd_set xReadSet;
@ -677,23 +683,25 @@ static void vMBTCPPortMasterTask(void *pvParameters)
ESP_LOGE(TAG, "Fail to register slave IP.");
} else {
if (xSlaveAddrInfo.pcIPAddr == NULL && xMbPortConfig.usMbSlaveInfoCount && xSlaveAddrInfo.usIndex == 0xFF) {
// Init start timeout that allows to initialize the main FSM
xMBMasterPortEventPost(EV_MASTER_READY);
break;
}
if (xMbPortConfig.usMbSlaveInfoCount > MB_TCP_PORT_MAX_CONN) {
ESP_LOGE(TAG, "Exceeds maximum connections limit=%d.", MB_TCP_PORT_MAX_CONN);
ESP_LOGE(TAG, "Exceeds maximum connections limit=%u.", (unsigned)MB_TCP_PORT_MAX_CONN);
break;
}
pxInfo = calloc(1, sizeof(MbSlaveInfo_t));
if (!pxInfo) {
ESP_LOGE(TAG, "Slave(#%d), info structure allocation fail.",
xMbPortConfig.usMbSlaveInfoCount);
ESP_LOGE(TAG, "Slave(#%u), info structure allocation fail.",
(unsigned)xMbPortConfig.usMbSlaveInfoCount);
free(pxInfo);
break;
}
pxInfo->pucRcvBuf = calloc(MB_TCP_BUF_SIZE, sizeof(UCHAR));
if (!pxInfo->pucRcvBuf) {
ESP_LOGE(TAG, "Slave(#%d), receive buffer allocation fail.",
xMbPortConfig.usMbSlaveInfoCount);
ESP_LOGE(TAG, "Slave(#%u), receive buffer allocation fail.",
(unsigned)xMbPortConfig.usMbSlaveInfoCount);
free(pxInfo->pucRcvBuf);
break;
}
@ -729,7 +737,7 @@ static void vMBTCPPortMasterTask(void *pvParameters)
pxInfo = xMbPortConfig.pxMbSlaveInfo[ucCnt];
// if slave descriptor is NULL then it is end of list or connection closed.
if (!pxInfo) {
ESP_LOGV(TAG, "Index: %d is not initialized, skip.", ucCnt);
ESP_LOGV(TAG, "Index: % is not initialized, skip.", ucCnt);
if (xMbPortConfig.usMbSlaveInfoCount) {
continue;
}
@ -744,9 +752,9 @@ static void vMBTCPPortMasterTask(void *pvParameters)
// In case of connection errors remove the socket from set
if (FD_ISSET(pxInfo->xSockId, &xConnSet)) {
FD_CLR(pxInfo->xSockId, &xConnSet);
ESP_LOGE(TAG, MB_SLAVE_FMT(" connect failed, error = %d."),
pxInfo->xIndex, pxInfo->xSockId,
(char*)pxInfo->pcIpAddr, xErr);
ESP_LOGE(TAG, MB_SLAVE_FMT(" connect failed, error = 0x%x."),
(int)pxInfo->xIndex, (int)pxInfo->xSockId,
(char*)pxInfo->pcIpAddr, (int)xErr);
if (usSlaveConnCnt) {
usSlaveConnCnt--;
}
@ -758,20 +766,20 @@ static void vMBTCPPortMasterTask(void *pvParameters)
FD_SET(pxInfo->xSockId, &xConnSet);
usSlaveConnCnt++;
xMaxSd = (pxInfo->xSockId > xMaxSd) ? pxInfo->xSockId : xMaxSd;
ESP_LOGD(TAG, MB_SLAVE_FMT(", connected %d slave(s), error = %d."),
pxInfo->xIndex, pxInfo->xSockId,
ESP_LOGD(TAG, MB_SLAVE_FMT(", connected %u slave(s), error = 0x%x."),
(int)pxInfo->xIndex, (int)pxInfo->xSockId,
pxInfo->pcIpAddr,
usSlaveConnCnt, xErr);
(unsigned)usSlaveConnCnt, (int)xErr);
// Update time stamp for connected slaves
pxInfo->xRecvTimeStamp = xMBTCPGetTimeStamp();
pxInfo->xSendTimeStamp = xMBTCPGetTimeStamp();
}
break;
default:
ESP_LOGE(TAG, MB_SLAVE_FMT(", unexpected error = %d."),
pxInfo->xIndex,
pxInfo->xSockId,
pxInfo->pcIpAddr, xErr);
ESP_LOGE(TAG, MB_SLAVE_FMT(", unexpected error = 0x%x."),
(int)pxInfo->xIndex,
(int)pxInfo->xSockId,
pxInfo->pcIpAddr, (int)xErr);
break;
}
if (pxInfo) {
@ -780,7 +788,7 @@ static void vMBTCPPortMasterTask(void *pvParameters)
xMBTCPPortMasterCheckShutdown();
}
}
ESP_LOGI(TAG, "Connected %d slaves, start polling...", usSlaveConnCnt);
ESP_LOGI(TAG, "Connected %u slaves, start polling...", (unsigned)usSlaveConnCnt);
vMBTCPPortMasterStartPoll(); // Send event to start stack
@ -797,21 +805,21 @@ static void vMBTCPPortMasterTask(void *pvParameters)
pxCurrInfo = vMBTCPPortMasterGetCurrInfo();
if (!pxCurrInfo) {
ESP_LOGE(TAG, "Incorrect connection options for slave index: %d.",
xMbPortConfig.ucCurSlaveIndex);
(int)xMbPortConfig.ucCurSlaveIndex);
vMBTCPPortMasterStopPoll();
xMBTCPPortMasterCheckShutdown();
break; // incorrect slave descriptor, reconnect.
}
xTime = xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo);
ESP_LOGD(TAG, "Set select timeout, left time: %ju ms.",
xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo));
ESP_LOGD(TAG, "Set select timeout, left time: %" PRIu64 " ms.",
xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo));
// Wait respond from current slave during respond timeout
int xRes = vMBTCPPortMasterRxCheck(pxCurrInfo->xSockId, &xReadSet, xTime);
if (xRes == ERR_TIMEOUT) {
// No respond from current slave, process timeout.
// Need to drop response later if it is received after timeout.
ESP_LOGD(TAG, "Select timeout, left time: %ju ms.",
xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo));
ESP_LOGD(TAG, "Select timeout, left time: %" PRIu64 " ms.",
xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo));
xTime = xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo);
// Wait completion of last transaction
xMBMasterPortFsmWaitConfirmation(MB_EVENT_REQ_DONE_MASK, pdMS_TO_TICKS(xTime + 1));
@ -820,7 +828,7 @@ static void vMBTCPPortMasterTask(void *pvParameters)
} else if (xRes < 0) {
// Select error (slave connection or r/w failure).
ESP_LOGD(TAG, MB_SLAVE_FMT(", socket select error. Slave disconnected?"),
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
(int)pxCurrInfo->xIndex, (int)pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
xTime = xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo);
// Wait completion of last transaction
xMBMasterPortFsmWaitConfirmation(MB_EVENT_REQ_DONE_MASK, pdMS_TO_TICKS(xTime));
@ -844,19 +852,20 @@ static void vMBTCPPortMasterTask(void *pvParameters)
// Response received correctly, send an event to stack
xMBTCPPortMasterFsmSetError(EV_ERROR_INIT, EV_MASTER_FRAME_RECEIVED);
ESP_LOGD(TAG, MB_SLAVE_FMT(", frame received."),
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
(int)pxCurrInfo->xIndex, (int)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);
(int)pxCurrInfo->xIndex, (int)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."),
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
(int)pxCurrInfo->xIndex, (int)pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
} else {
ESP_LOGE(TAG, MB_SLAVE_FMT(", critical error=%d."),
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr, xRet);
(int)pxCurrInfo->xIndex, (int)pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr, (int)xRet);
// Stop polling process
vMBTCPPortMasterStopPoll();
xMBTCPPortMasterCheckShutdown();
@ -865,15 +874,15 @@ static void vMBTCPPortMasterTask(void *pvParameters)
break;
}
xTime = xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo);
ESP_LOGD(TAG, "Slave #%d, data processing left time %ju [ms].", pxCurrInfo->xIndex, xTime);
ESP_LOGD(TAG, "Slave #%d, data processing left time %" PRIu64 " [ms].", (int)pxCurrInfo->xIndex, xTime);
// Wait completion of Modbus frame processing before start of new transaction.
if (xMBMasterPortFsmWaitConfirmation(MB_EVENT_REQ_DONE_MASK, pdMS_TO_TICKS(xTime))) {
ESP_LOGD(TAG, MB_SLAVE_FMT(", data processing completed."),
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
(int)pxCurrInfo->xIndex, (int)pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
}
xTime = xMBTCPGetTimeStamp() - pxCurrInfo->xSendTimeStamp;
ESP_LOGD(TAG, MB_SLAVE_FMT(", processing time[us] = %ju."),
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr, xTime);
ESP_LOGD(TAG, MB_SLAVE_FMT(", processing time[us] = %" PRIu64 "."),
(int)pxCurrInfo->xIndex, (int)pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr, xTime);
}
}
xMBTCPPortMasterCheckShutdown();
@ -885,14 +894,12 @@ static void vMBTCPPortMasterTask(void *pvParameters)
extern void vMBMasterPortEventClose(void);
extern void vMBMasterPortTimerClose(void);
void
vMBMasterTCPPortEnable(void)
void vMBMasterTCPPortEnable(void)
{
vTaskResume(xMbPortConfig.xMbTcpTaskHandle);
}
void
vMBMasterTCPPortDisable(void)
void vMBMasterTCPPortDisable(void)
{
// Try to exit the task gracefully, so select could release its internal callbacks
// that were allocated on the stack of the task we're going to delete
@ -920,8 +927,7 @@ vMBMasterTCPPortDisable(void)
free(xMbPortConfig.pxMbSlaveInfo);
}
void
vMBMasterTCPPortClose(void)
void vMBMasterTCPPortClose(void)
{
vQueueDelete(xMbPortConfig.xConnectQueue);
vMBMasterPortTimerClose();
@ -929,10 +935,9 @@ vMBMasterTCPPortClose(void)
vMBMasterPortEventClose();
}
BOOL
xMBMasterTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
BOOL xMBMasterTCPPortGetRequest(UCHAR **ppucMBTCPFrame, USHORT *usTCPLength)
{
MbSlaveInfo_t* pxInfo = vMBTCPPortMasterGetCurrInfo();
MbSlaveInfo_t *pxInfo = vMBTCPPortMasterGetCurrInfo();
*ppucMBTCPFrame = pxInfo->pucRcvBuf;
*usTCPLength = pxInfo->usRcvPos;
@ -946,51 +951,50 @@ xMBMasterTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
return FALSE;
}
int xMBMasterTCPPortWritePoll(MbSlaveInfo_t* pxInfo, const UCHAR * pucMBTCPFrame, USHORT usTCPLength, ULONG xTimeout)
int xMBMasterTCPPortWritePoll(MbSlaveInfo_t *pxInfo, const UCHAR *pucMBTCPFrame, USHORT usTCPLength, ULONG xTimeout)
{
// Check if the socket is alive (writable and SO_ERROR == 0)
int xRes = (int)xMBTCPPortMasterCheckAlive(pxInfo, xTimeout);
if ((xRes < 0) && (xRes != ERR_INPROGRESS))
{
ESP_LOGE(TAG, MB_SLAVE_FMT(", is not writable, error: %d, errno %d"),
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, xRes, errno);
if ((xRes < 0) && (xRes != ERR_INPROGRESS)) {
ESP_LOGE(TAG, MB_SLAVE_FMT(", is not writable, error: %d, errno %u"),
(int)pxInfo->xIndex, (int)pxInfo->xSockId, pxInfo->pcIpAddr, (int)xRes, (unsigned)errno);
return xRes;
}
xRes = send(pxInfo->xSockId, pucMBTCPFrame, usTCPLength, TCP_NODELAY);
if (xRes < 0) {
ESP_LOGE(TAG, MB_SLAVE_FMT(", send data error: %d, errno %d"),
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, xRes, errno);
ESP_LOGE(TAG, MB_SLAVE_FMT(", send data error: %d, errno %u"),
(int)pxInfo->xIndex, (int)pxInfo->xSockId, pxInfo->pcIpAddr, (int)xRes, (unsigned)errno);
}
return xRes;
}
BOOL
xMBMasterTCPPortSendResponse( UCHAR * pucMBTCPFrame, USHORT usTCPLength )
BOOL xMBMasterTCPPortSendResponse(UCHAR *pucMBTCPFrame, USHORT usTCPLength)
{
BOOL bFrameSent = FALSE;
USHORT ucCurSlaveIndex = ucMBMasterGetDestAddress();
MbSlaveInfo_t* pxInfo = vMBTCPPortMasterFindSlaveInfo(ucCurSlaveIndex);
MbSlaveInfo_t *pxInfo = vMBTCPPortMasterFindSlaveInfo(ucCurSlaveIndex);
// If the slave is correct and active then send data
// otherwise treat slave as died and skip
if (pxInfo != NULL) {
if (pxInfo->xSockId < 0) {
ESP_LOGD(TAG, MB_SLAVE_FMT(", send to died slave, error = %d"),
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, pxInfo->xError);
ESP_LOGD(TAG, MB_SLAVE_FMT(", send to died slave, error = %u"),
(int)pxInfo->xIndex, (int)pxInfo->xSockId, pxInfo->pcIpAddr, (unsigned)pxInfo->xError);
} else {
// Apply TID field to the frame before send
pucMBTCPFrame[MB_TCP_TID] = (UCHAR)(pxInfo->usTidCnt >> 8U);
pucMBTCPFrame[MB_TCP_TID + 1] = (UCHAR)(pxInfo->usTidCnt & 0xFF);
int xRes = xMBMasterTCPPortWritePoll(pxInfo, pucMBTCPFrame, usTCPLength, MB_TCP_SEND_TIMEOUT_MS);
if (xRes < 0) {
ESP_LOGE(TAG, MB_SLAVE_FMT(", send data failure, err(errno) = %d(%d)."),
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, xRes, errno);
ESP_LOGE(TAG, MB_SLAVE_FMT(", send data failure, err(errno) = %d(%u)."),
(int)pxInfo->xIndex, (int)pxInfo->xSockId, pxInfo->pcIpAddr, (int)xRes, (unsigned)errno);
bFrameSent = FALSE;
pxInfo->xError = xRes;
} else {
bFrameSent = TRUE;
ESP_LOGD(TAG, MB_SLAVE_FMT(", send data successful: TID=0x%02x, %d (bytes), errno %d"),
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, pxInfo->usTidCnt, xRes, errno);
ESP_LOGD(TAG, MB_SLAVE_FMT(", send data successful: TID=0x%02x, %d (bytes), errno %u"),
(int)pxInfo->xIndex, (int)pxInfo->xSockId, pxInfo->pcIpAddr, pxInfo->usTidCnt, (int)xRes, (unsigned)errno);
pxInfo->xError = 0;
pxInfo->usRcvPos = 0;
if (pxInfo->usTidCnt < (USHRT_MAX - 1)) {
@ -1002,7 +1006,7 @@ xMBMasterTCPPortSendResponse( UCHAR * pucMBTCPFrame, USHORT usTCPLength )
pxInfo->xSendTimeStamp = xMBTCPGetTimeStamp();
}
} else {
ESP_LOGD(TAG, "Send data to died slave, address = %d", ucCurSlaveIndex);
ESP_LOGD(TAG, "Send data to died slave, address = %u", (unsigned)ucCurSlaveIndex);
}
vMBMasterPortTimersRespondTimeoutEnable();
xMBMasterPortEventPost(EV_MASTER_FRAME_SENT);
@ -1014,10 +1018,12 @@ BOOL MB_PORT_ISR_ATTR
xMBMasterTCPTimerExpired(void)
{
BOOL xNeedPoll = FALSE;
eMBMasterTimerMode eTimerMode = xMBMasterGetCurTimerMode();
vMBMasterPortTimersDisable();
// If timer mode is respond timeout, the master event then turns EV_MASTER_EXECUTE status.
if (xMBMasterGetCurTimerMode() == MB_TMODE_RESPOND_TIMEOUT) {
if (eTimerMode == MB_TMODE_RESPOND_TIMEOUT) {
vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT);
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
}
@ -1025,4 +1031,4 @@ xMBMasterTCPTimerExpired(void)
return xNeedPoll;
}
#endif //#if MB_MASTER_TCP_ENABLED
#endif // #if MB_MASTER_TCP_ENABLED

View File

@ -54,9 +54,9 @@ static esp_err_t mbc_tcp_slave_setup(void* comm_info)
"mb wrong communication settings.");
mb_communication_info_t* comm_settings = (mb_communication_info_t*)comm_info;
MB_SLAVE_CHECK((comm_settings->ip_mode == MB_MODE_TCP),
ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).", (uint8_t)comm_settings->ip_mode);
ESP_ERR_INVALID_ARG, "mb incorrect mode = (%u).", (unsigned)comm_settings->ip_mode);
MB_SLAVE_CHECK(((comm_settings->ip_addr_type == MB_IPV4) || (comm_settings->ip_addr_type == MB_IPV6)),
ESP_ERR_INVALID_ARG, "mb incorrect addr type = (0x%x).", (uint8_t)comm_settings->ip_addr_type);
ESP_ERR_INVALID_ARG, "mb incorrect addr type = (%u).", (unsigned)comm_settings->ip_addr_type);
MB_SLAVE_CHECK((comm_settings->ip_netif_ptr != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect iface address.");
// Set communication options of the controller
@ -72,9 +72,9 @@ static esp_err_t mbc_tcp_slave_start(void)
eMBErrorCode status = MB_EIO;
// Initialize Modbus stack using mbcontroller parameters
status = eMBTCPInit((USHORT)mbs_opts->mbs_comm.ip_port);
status = eMBTCPInit((UCHAR)mbs_opts->mbs_comm.slave_uid, (USHORT)mbs_opts->mbs_comm.ip_port);
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack initialization failure, eMBInit() returns (0x%x).", status);
"mb stack initialization failure, eMBInit() returns (0x%x).", (int)status);
eMBPortProto proto = (mbs_opts->mbs_comm.ip_mode == MB_MODE_TCP) ? MB_PROTO_TCP : MB_PROTO_UDP;
eMBPortIpVer ip_ver = (mbs_opts->mbs_comm.ip_addr_type == MB_IPV4) ? MB_PORT_IPV4 : MB_PORT_IPV6;
@ -82,12 +82,12 @@ static esp_err_t mbc_tcp_slave_start(void)
status = eMBEnable();
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb TCP stack start failure, eMBEnable() returned (0x%x).", (uint32_t)status);
"mb TCP stack start failure, eMBEnable() returned (0x%x).", (int)status);
// Set the mbcontroller start flag
EventBits_t flag = xEventGroupSetBits(mbs_opts->mbs_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
return ESP_OK;
}
@ -101,7 +101,7 @@ static esp_err_t mbc_tcp_slave_destroy(void)
EventBits_t flag = xEventGroupClearBits(mbs_opts->mbs_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
// Disable and then destroy the Modbus stack
mb_error = eMBDisable();
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
@ -132,7 +132,7 @@ static esp_err_t mbc_tcp_slave_get_param_info(mb_param_info_t* reg_info, uint32_
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
esp_err_t err = ESP_ERR_TIMEOUT;
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
ESP_ERR_INVALID_ARG, "mb queue handle is invalid.");
ESP_ERR_INVALID_ARG, "mb queue handle is invalid.");
MB_SLAVE_CHECK((reg_info != NULL), ESP_ERR_INVALID_ARG, "mb register information is invalid.");
BaseType_t status = xQueueReceive(mbs_opts->mbs_notification_queue_handle,
reg_info, pdMS_TO_TICKS(timeout));
@ -170,13 +170,13 @@ esp_err_t mbc_tcp_slave_create(void** handler)
// Parameter change notification queue
mbs_opts->mbs_event_group = xEventGroupCreate();
MB_SLAVE_CHECK((mbs_opts->mbs_event_group != NULL),
ESP_ERR_NO_MEM, "mb event group error.");
ESP_ERR_NO_MEM, "mb event group error.");
// Parameter change notification queue
mbs_opts->mbs_notification_queue_handle = xQueueCreate(
MB_CONTROLLER_NOTIFY_QUEUE_SIZE,
sizeof(mb_param_info_t));
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
ESP_ERR_NO_MEM, "mb notify queue creation error.");
ESP_ERR_NO_MEM, "mb notify queue creation error.");
// Create Modbus controller task
status = xTaskCreatePinnedToCore((void*)&modbus_tcp_slave_task,
"modbus_tcp_slave_task",
@ -188,8 +188,7 @@ esp_err_t mbc_tcp_slave_create(void** handler)
if (status != pdPASS) {
vTaskDelete(mbs_opts->mbs_task_handle);
MB_SLAVE_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
"mb controller task creation error, xTaskCreate() returns (0x%x).",
(uint32_t)status);
"mb controller task creation error, xTaskCreate() returns (%u).", (unsigned)status);
}
// The task is created but handle is incorrect

View File

@ -61,7 +61,7 @@
/* ----------------------- Defines -----------------------------------------*/
#define MB_TCP_DISCONNECT_TIMEOUT ( CONFIG_FMB_TCP_CONNECTION_TOUT_SEC * 1000000 ) // disconnect timeout in uS
#define MB_TCP_RESP_TIMEOUT_MS ( MB_MASTER_TIMEOUT_MS_RESPOND - 2 ) // slave response time limit
#define MB_TCP_RESP_TIMEOUT_MS ( MB_MASTER_TIMEOUT_MS_RESPOND - 1 ) // slave response time limit
#define MB_TCP_NET_LISTEN_BACKLOG ( SOMAXCONN )
/* ----------------------- Prototypes ---------------------------------------*/
@ -106,8 +106,9 @@ static void* vxMBTCPPortRespQueueRecv(QueueHandle_t xRespQueueHandle)
BaseType_t xStatus = xQueueReceive(xRespQueueHandle,
(void*)&pvResp,
pdMS_TO_TICKS(MB_TCP_RESP_TIMEOUT_MS));
MB_PORT_CHECK((xStatus == pdTRUE), NULL, "Could not get respond confirmation.");
MB_PORT_CHECK((pvResp), NULL, "Incorrect response processing.");
if (xStatus != pdTRUE) {
ESP_LOGD(TAG, "Could not get respond confirmation.");
}
return pvResp;
}
@ -150,12 +151,13 @@ xMBTCPPortInit( USHORT usTCPPort )
xConfig.pcBindAddr = NULL;
// Create task for packet processing
BaseType_t xErr = xTaskCreate(vMBTCPPortServerTask,
"tcp_server_task",
BaseType_t xErr = xTaskCreatePinnedToCore(vMBTCPPortServerTask,
"tcp_slave_task",
MB_TCP_STACK_SIZE,
NULL,
MB_TCP_TASK_PRIO,
&xConfig.xMbTcpTaskHandle);
&xConfig.xMbTcpTaskHandle,
MB_PORT_TASK_AFFINITY);
vTaskSuspend(xConfig.xMbTcpTaskHandle);
if (xErr != pdTRUE)
{
@ -192,7 +194,7 @@ static int xMBTCPPortAcceptConnection(int xListenSockId, char** pcIPAddr)
// Accept new socket connection if not active
xSockId = accept(xListenSockId, (struct sockaddr *)&xSrcAddr, &xSize);
if (xSockId < 0) {
ESP_LOGE(TAG, "Unable to accept connection: errno=%d", errno);
ESP_LOGE(TAG, "Unable to accept connection: errno=%u", (unsigned)errno);
close(xSockId);
} else {
// Get the sender's ip address as string
@ -204,7 +206,11 @@ static int xMBTCPPortAcceptConnection(int xListenSockId, char** pcIPAddr)
inet6_ntoa_r(((struct sockaddr_in6 *)&xSrcAddr)->sin6_addr, cAddrStr, sizeof(cAddrStr) - 1);
}
#endif
ESP_LOGI(TAG, "Socket (#%d), accept client connection from address: %s", xSockId, cAddrStr);
else {
// Make sure ss_family is valid
abort();
}
ESP_LOGI(TAG, "Socket (#%d), accept client connection from address: %s", (int)xSockId, cAddrStr);
pcStr = calloc(1, strlen(cAddrStr) + 1);
if (pcStr && pcIPAddr) {
memcpy(pcStr, cAddrStr, strlen(cAddrStr));
@ -220,11 +226,11 @@ static BOOL xMBTCPPortCloseConnection(MbClientInfo_t* pxInfo)
MB_PORT_CHECK(pxInfo, FALSE, "Client info is NULL.");
if (pxInfo->xSockId == -1) {
ESP_LOGE(TAG, "Wrong socket info or disconnected socket: %d.", pxInfo->xSockId);
ESP_LOGE(TAG, "Wrong socket info or disconnected socket: %d.", (int)pxInfo->xSockId);
return FALSE;
}
if (shutdown(pxInfo->xSockId, SHUT_RDWR) == -1) {
ESP_LOGE(TAG, "Socket (#%d), shutdown failed: errno %d", pxInfo->xSockId, errno);
ESP_LOGE(TAG, "Socket (#%d), shutdown failed: errno %u", (int)pxInfo->xSockId, (unsigned)errno);
}
close(pxInfo->xSockId);
pxInfo->xSockId = -1;
@ -261,7 +267,7 @@ static int xMBTCPPortRxPoll(MbClientInfo_t* pxClientInfo, ULONG xTimeoutMs)
} else if (xRet == 0) {
// timeout occurred
if ((xStartTimeStamp + xTimeoutMs * 1000) > xMBTCPGetTimeStamp()) {
ESP_LOGD(TAG, "Socket (#%d) Read timeout.", pxClientInfo->xSockId);
ESP_LOGD(TAG, "Socket (#%d) Read timeout.", (int)pxClientInfo->xSockId);
xRet = ERR_TIMEOUT;
break;
}
@ -269,20 +275,20 @@ static int xMBTCPPortRxPoll(MbClientInfo_t* pxClientInfo, ULONG xTimeoutMs)
if (FD_ISSET(pxClientInfo->xSockId, &xReadSet)) {
// If new buffer received then read Modbus packet into buffer
MB_PORT_CHECK((pxClientInfo->usTCPBufPos + pxClientInfo->usTCPFrameBytesLeft < MB_TCP_BUF_SIZE),
ERR_BUF, "Socket (#%d), incorrect request buffer size = %d, ignore.",
pxClientInfo->xSockId,
(pxClientInfo->usTCPBufPos + pxClientInfo->usTCPFrameBytesLeft));
ERR_BUF, "Socket (#%d), incorrect request buffer size = %u, ignore.",
(int)pxClientInfo->xSockId,
(unsigned)(pxClientInfo->usTCPBufPos + pxClientInfo->usTCPFrameBytesLeft));
int xLength = recv(pxClientInfo->xSockId, &pxClientInfo->pucTCPBuf[pxClientInfo->usTCPBufPos],
pxClientInfo->usTCPFrameBytesLeft, MSG_DONTWAIT);
if (xLength < 0) {
// If an error occurred during receiving
ESP_LOGE(TAG, "Receive failed: length=%d, errno=%d", xLength, errno);
ESP_LOGE(TAG, "Receive failed: length=%u, errno=%u", (unsigned)xLength, (unsigned)errno);
xRet = (err_t)xLength;
break;
} else if (xLength == 0) {
// Socket connection closed
ESP_LOGD(TAG, "Socket (#%d)(%s), connection closed.",
pxClientInfo->xSockId, pxClientInfo->pcIpAddr);
(int)pxClientInfo->xSockId, pxClientInfo->pcIpAddr);
xRet = ERR_CLSD;
break;
} else {
@ -306,7 +312,7 @@ static int xMBTCPPortRxPoll(MbClientInfo_t* pxClientInfo, ULONG xTimeoutMs)
xRet = pxClientInfo->usTCPBufPos;
break;
} else if ((pxClientInfo->usTCPBufPos + xLength) >= MB_TCP_BUF_SIZE) {
ESP_LOGE(TAG, "Incorrect buffer received (%u) bytes.", xLength);
ESP_LOGE(TAG, "Incorrect buffer received (%u) bytes.", (unsigned)xLength);
// This should not happen. We can't deal with such a client and
// drop the connection for security reasons.
xRet = ERR_BUF;
@ -391,7 +397,7 @@ vMBTCPPortBindAddr(const CHAR* pcBindIp)
{
if (listen(xListenSockFd, MB_TCP_NET_LISTEN_BACKLOG) != 0)
{
ESP_LOGE(TAG, "Error occurred during listen: errno=%d", errno);
ESP_LOGE(TAG, "Error occurred during listen: errno=%u", (unsigned)errno);
close(xListenSockFd);
xListenSockFd = -1;
continue;
@ -399,8 +405,8 @@ vMBTCPPortBindAddr(const CHAR* pcBindIp)
}
// Bind was successful
pcStr = (pxCurAddr->ai_canonname == NULL) ? (CHAR*)"\0" : pxCurAddr->ai_canonname;
ESP_LOGI(TAG, "Socket (#%d), listener %s on port: %d, errno=%d",
xListenSockFd, pcStr, xConfig.usPort, errno);
ESP_LOGI(TAG, "Socket (#%d), listener %s on port: %u, errno=%u",
(int)xListenSockFd, pcStr, (unsigned)xConfig.usPort, (unsigned)errno);
break;
}
@ -470,11 +476,11 @@ static void vMBTCPPortServerTask(void *pvParameters)
vTaskDelete(NULL);
}
// error occurred during wait for read
ESP_LOGE(TAG, "select() errno = %d.", errno);
ESP_LOGE(TAG, "select() errno = %u.", (unsigned)errno);
continue;
} else if (xErr == 0) {
// If timeout happened, something is wrong
ESP_LOGE(TAG, "select() timeout, errno = %d.", errno);
ESP_LOGE(TAG, "select() timeout, errno = %u.", (unsigned)errno);
}
// If something happened on the master socket, then its an incoming connection.
@ -490,7 +496,7 @@ static void vMBTCPPortServerTask(void *pvParameters)
// if request for new connection but no space left
if (pxClientInfo != NULL) {
if (xConfig.pxMbClientInfo[MB_TCP_PORT_MAX_CONN] == NULL) {
ESP_LOGE(TAG, "Fail to accept connection %d, only %d connections supported.", i + 1, MB_TCP_PORT_MAX_CONN);
ESP_LOGE(TAG, "Fail to accept connection %u, only %u connections supported.", (unsigned)(i + 1), (unsigned)MB_TCP_PORT_MAX_CONN);
}
xConfig.pxMbClientInfo[MB_TCP_PORT_MAX_CONN] = pxClientInfo; // set last connection info
} else {
@ -504,7 +510,7 @@ static void vMBTCPPortServerTask(void *pvParameters)
// Accept new client connection
pxClientInfo->xSockId = xMBTCPPortAcceptConnection(xListenSock, &pcClientIp);
if (pxClientInfo->xSockId < 0) {
ESP_LOGE(TAG, "Fail to accept connection for client %d.", (xConfig.usClientCount - 1));
ESP_LOGE(TAG, "Fail to accept connection for client %u.", (unsigned)(xConfig.usClientCount - 1));
// Accept connection fail, then free client info and continue polling.
vMBTCPPortFreeClientInfo(pxClientInfo);
pxClientInfo = NULL;
@ -512,7 +518,7 @@ static void vMBTCPPortServerTask(void *pvParameters)
}
pxClientInfo->pucTCPBuf = calloc(MB_TCP_BUF_SIZE, sizeof(UCHAR));
if (!pxClientInfo->pucTCPBuf) {
ESP_LOGE(TAG, "Fail to allocate buffer for client %d.", (xConfig.usClientCount - 1));
ESP_LOGE(TAG, "Fail to allocate buffer for client %u.", (unsigned)(xConfig.usClientCount - 1));
vMBTCPPortFreeClientInfo(pxClientInfo);
pxClientInfo = NULL;
continue;
@ -546,21 +552,21 @@ static void vMBTCPPortServerTask(void *pvParameters)
switch(xErr)
{
case ERR_TIMEOUT:
ESP_LOGE(TAG, "Socket (#%d)(%s), data receive timeout, time[us]: %d, close active connection.",
pxClientInfo->xSockId, pxClientInfo->pcIpAddr,
(int)(xTimeStamp - pxClientInfo->xRecvTimeStamp));
ESP_LOGE(TAG, "Socket (#%d)(%s), data receive timeout, time[us]: %" PRIu64 ", close active connection.",
(int)pxClientInfo->xSockId, pxClientInfo->pcIpAddr,
(uint64_t)(xTimeStamp - pxClientInfo->xRecvTimeStamp));
break;
case ERR_CLSD:
ESP_LOGE(TAG, "Socket (#%d)(%s), connection closed by peer.",
pxClientInfo->xSockId, pxClientInfo->pcIpAddr);
(int)pxClientInfo->xSockId, pxClientInfo->pcIpAddr);
break;
case ERR_BUF:
default:
ESP_LOGE(TAG, "Socket (#%d)(%s), read data error: %d",
pxClientInfo->xSockId, pxClientInfo->pcIpAddr, xErr);
ESP_LOGE(TAG, "Socket (#%d)(%s), read data error: 0x%x",
(int)pxClientInfo->xSockId, pxClientInfo->pcIpAddr, (int)xErr);
break;
}
if (xShutdownSemaphore) {
xSemaphoreGive(xShutdownSemaphore);
vTaskDelete(NULL);
@ -588,35 +594,35 @@ static void vMBTCPPortServerTask(void *pvParameters)
xMBPortEventPost(EV_FRAME_RECEIVED);
ESP_LOGD(TAG, "Socket (#%d)(%s), get packet TID=0x%X, %d bytes.",
pxClientInfo->xSockId, pxClientInfo->pcIpAddr,
pxClientInfo->usTidCnt, xErr);
(int)pxClientInfo->xSockId, pxClientInfo->pcIpAddr,
(int)pxClientInfo->usTidCnt, (int)xErr);
// Wait while response is not processed by stack by timeout
UCHAR* pucSentBuffer = vxMBTCPPortRespQueueRecv(xConfig.xRespQueueHandle);
if (pucSentBuffer == NULL) {
ESP_LOGE(TAG, "Response time exceeds configured %d [ms], ignore packet.",
MB_TCP_RESP_TIMEOUT_MS);
ESP_LOGD(TAG, "Response is ignored, time exceeds configured %d [ms].",
(unsigned)MB_TCP_RESP_TIMEOUT_MS);
} else {
USHORT usSentTid = MB_TCP_GET_FIELD(pucSentBuffer, MB_TCP_TID);
if (usSentTid != pxClientInfo->usTidCnt) {
ESP_LOGE(TAG, "Sent TID(%x) != Recv TID(%x), ignore packet.",
usSentTid, pxClientInfo->usTidCnt);
(int)usSentTid, (int)pxClientInfo->usTidCnt);
}
}
// Get time stamp of last data update
pxClientInfo->xSendTimeStamp = xMBTCPGetTimeStamp();
ESP_LOGD(TAG, "Client %d, Socket(#%d), processing time = %d (us).",
pxClientInfo->xIndex, pxClientInfo->xSockId,
(int)(pxClientInfo->xSendTimeStamp - pxClientInfo->xRecvTimeStamp));
ESP_LOGD(TAG, "Client %d, Socket(#%d), processing time = %" PRIu64 "(us).",
(int)pxClientInfo->xIndex, (int)pxClientInfo->xSockId,
(uint64_t)(pxClientInfo->xSendTimeStamp - pxClientInfo->xRecvTimeStamp));
}
} else {
if (pxClientInfo) {
// client is not ready to be read
int64_t xTime = xMBTCPGetTimeStamp() - pxClientInfo->xRecvTimeStamp;
if (xTime > MB_TCP_DISCONNECT_TIMEOUT) {
ESP_LOGE(TAG, "Client %d, Socket(#%d) do not answer for %d (us). Drop connection...",
pxClientInfo->xIndex, pxClientInfo->xSockId, (int)(xTime));
ESP_LOGE(TAG, "Client %d, Socket(#%d) do not answer for %" PRIu64 " (us). Drop connection...",
(int)pxClientInfo->xIndex, (int)pxClientInfo->xSockId, (uint64_t)xTime);
xMBTCPPortCloseConnection(pxClientInfo);
// This client does not respond, then delete registered data
@ -624,7 +630,7 @@ static void vMBTCPPortServerTask(void *pvParameters)
xConfig.pxMbClientInfo[i] = NULL;
}
} else {
ESP_LOGE(TAG, "Client %d is disconnected.", i);
ESP_LOGE(TAG, "Client %d is disconnected.", (int)i);
}
}
} // if ((pxClientInfo != NULL)
@ -714,8 +720,8 @@ xMBTCPPortSendResponse( UCHAR * pucMBTCPFrame, USHORT usTCPLength )
// Check if socket writable
xErr = select(xConfig.pxCurClientInfo->xSockId + 1, NULL, &xWriteSet, &xErrorSet, &xTimeVal);
if ((xErr == -1) || FD_ISSET(xConfig.pxCurClientInfo->xSockId, &xErrorSet)) {
ESP_LOGE(TAG, "Socket(#%d) , send select() error = %d.",
xConfig.pxCurClientInfo->xSockId, errno);
ESP_LOGE(TAG, "Socket(#%d) , send select() error = %u.",
(int)xConfig.pxCurClientInfo->xSockId, (unsigned)errno);
return FALSE;
}
@ -726,8 +732,8 @@ xMBTCPPortSendResponse( UCHAR * pucMBTCPFrame, USHORT usTCPLength )
// Write message into socket and disable Nagle's algorithm
xErr = send(xConfig.pxCurClientInfo->xSockId, pucMBTCPFrame, usTCPLength, TCP_NODELAY);
if (xErr < 0) {
ESP_LOGE(TAG, "Socket(#%d), fail to send data, errno = %d",
xConfig.pxCurClientInfo->xSockId, errno);
ESP_LOGE(TAG, "Socket(#%d), fail to send data, errno = %u",
(int)xConfig.pxCurClientInfo->xSockId, (unsigned)errno);
xConfig.pxCurClientInfo->xError = xErr;
} else {
bFrameSent = TRUE;

View File

@ -1,11 +1,11 @@
version: "1.0.1"
version: "1.0.12"
description: ESP-MODBUS is the official Modbus library for Espressif SoCs.
url: https://github.com/espressif/esp-modbus
dependencies:
idf: ">=4.1"
idf: ">=4.3"
files:
exclude:
- "docs/_build/**/*"
- "docs/_build"
- "test/**/build/**/*"
- "test/**/build"
- "docs/**/*"
- "docs"
- "test/**/*"
- "test"

46
pytest.ini Normal file
View File

@ -0,0 +1,46 @@
[pytest]
# only the files with prefix `pytest_` would be recognized as pytest test scripts.
python_files = pytest_*.py
# ignore PytestExperimentalApiWarning for record_xml_attribute
# set traceback to "short" to prevent the overwhelming tracebacks
addopts =
-s
--embedded-services esp,idf
--tb short
--skip-check-coredump y
# ignore DeprecationWarning
filterwarnings =
ignore::DeprecationWarning:matplotlib.*:
ignore::DeprecationWarning:google.protobuf.*:
ignore::_pytest.warning_types.PytestExperimentalApiWarning
markers =
# target markers
esp32: support esp32 target
esp32s2: support esp32s2 target
esp32s3: support esp32s3 target
esp32c3: support esp32c3 target
esp32c2: support esp32c2 target
# env markers
generic: tests should be run on generic runners
# multi-dut markers
multi_dut_generic: tests should be run on generic runners, at least have two duts connected.
multi_dut_modbus_tcp: Modbus TCP runners with two duts connected
multi_dut_modbus_rs485: Modbus RTU/ASCII runners with two duts connected
# log related
log_cli = True
log_cli_level = INFO
log_cli_format = %(asctime)s %(levelname)s %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S
# junit related
junit_family = xunit1
## log all to `system-out` when case fail
junit_logging = stdout
junit_log_passing_tests = False

View File

@ -0,0 +1,18 @@
tcp/mb_tcp_master:
disable_test:
- if: IDF_TARGET != "esp32"
reason: only manual test is performed
disable:
- if: CONFIG_NAME == "wifi" and SOC_WIFI_SUPPORTED != 1
tcp/mb_tcp_slave:
disable_test:
- if: IDF_TARGET != "esp32"
reason: only manual test is performed
disable:
- if: CONFIG_NAME == "wifi" and SOC_WIFI_SUPPORTED != 1

319
test/conftest.py Normal file
View File

@ -0,0 +1,319 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
# pylint: disable=W0621 # redefined-outer-name
import logging
import os
import sys
from datetime import datetime
from enum import Enum
from typing import Any, Callable, Dict, Match, Optional, TextIO, Tuple
import pexpect
import pytest
from _pytest.fixtures import FixtureRequest
from _pytest.monkeypatch import MonkeyPatch
from pytest_embedded.plugin import multi_dut_argument, multi_dut_fixture
from pytest_embedded_idf.app import IdfApp
from pytest_embedded_idf.dut import IdfDut
from pytest_embedded_idf.serial import IdfSerial
class Stages(Enum):
STACK_DEFAULT = 1
STACK_IPV4 = 2
STACK_IPV6 = 3
STACK_INIT = 4
STACK_CONNECT = 5
STACK_START = 6
STACK_PAR_OK = 7
STACK_PAR_FAIL = 8
STACK_DESTROY = 9
DEFAULT_SDKCONFIG = 'default'
ALLOWED_PERCENT_OF_FAILS = 10
class ModbusTestDut(IdfDut):
TEST_IP_PROMPT = r'Waiting IP([0-9]{1,2}) from stdin:\r\r\n'
TEST_IP_SET_CONFIRM = r'.*IP\([0-9]+\) = \[([0-9a-zA-Z\.\:]+)\] set from stdin.*'
TEST_IP_ADDRESS_REGEXP = r'.*example_[a-z]+: .* IPv4 [a-z]+:.* ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}).*'
TEST_APP_NAME = r'I \([0-9]+\) cpu_start: Project name: ([_a-z]*)'
TEST_EXPECT_STR_TIMEOUT = 120
TEST_ACK_TIMEOUT = 60
TEST_MAX_CIDS = 8
app: IdfApp
serial: IdfSerial
def __init__(self, *args, **kwargs) -> None: # type: ignore
super().__init__(*args, **kwargs)
self.logger = logging.getLogger()
self.test_output: Optional[TextIO] = None
self.ip_address: Optional[str] = None
self.app_name: Optional[str] = None
self.param_fail_count = 0
self.param_ok_count = 0
self.test_stage = Stages.STACK_DEFAULT
self.dictionary = None
self.test_finish = False
self.test_status = False
def close(self) -> None:
super().close()
def dut_get_ip(self) -> Optional[str]:
if self.ip_address is None:
expect_address = self.expect(self.TEST_IP_ADDRESS_REGEXP, timeout=self.TEST_EXPECT_STR_TIMEOUT)
if isinstance(expect_address, Match):
self.ip_address = expect_address.group(1).decode('ascii')
return self.ip_address
def dut_get_name(self) -> Optional[str]:
if self.app_name is None:
expect_name = self.expect(self.TEST_APP_NAME, timeout=self.TEST_EXPECT_STR_TIMEOUT)
if isinstance(expect_name, Match):
self.app_name = expect_name.group(1).decode('ascii')
return self.app_name
def dut_send_ip(self, slave_ip: Optional[str]) -> Optional[int]:
''' The function sends the slave IP address defined as a parameter to master
'''
addr_num = 0
self.expect(self.TEST_IP_PROMPT, timeout=self.TEST_ACK_TIMEOUT)
if isinstance(slave_ip, str):
for addr_num in range(0, self.TEST_MAX_CIDS):
message = r'IP{}={}'.format(addr_num, slave_ip)
self.logger.info('{} sent to master'.format(message))
self.write(message)
return addr_num
def get_expect_proc(self) -> Optional[object]:
expect_proc: object = None
try:
expect_proc = self.__getattribute__('pexpect_proc')
except:
expect_proc = self.__getattribute__('_p')
finally:
if (expect_proc and callable(getattr(expect_proc, 'expect'))):
return expect_proc
else :
return None
def expect_any(self, *expect_items: Tuple[str, Callable], timeout: Optional[int]) -> None:
"""
expect_any(*expect_items, timeout=DEFAULT_TIMEOUT)
expect any of the patterns.
will call callback (if provided) if pattern match succeed and then return.
will pass match result to the callback.
:raise ExpectTimeout: failed to match any one of the expect items before timeout
:raise UnsupportedExpectItem: pattern in expect_item is not string or compiled RegEx
:arg expect_items: one or more expect items.
string, compiled RegEx pattern or (string or RegEx(string pattern), callback)
:keyword timeout: timeout for expect
:return: matched item
"""
def process_expected_item(item_raw: Tuple[str, Callable[..., Any]]) -> Dict[str, Any]:
# convert item raw data to standard dict
item = {
'pattern': item_raw[0] if isinstance(item_raw, tuple) else item_raw,
'callback': item_raw[1] if isinstance(item_raw, tuple) else None,
'index': -1,
'ret': None,
}
return item
expect_items_list = [process_expected_item(item) for item in expect_items]
expect_patterns = [item['pattern'] for item in expect_items_list if item['pattern'] is not None]
match_item = None
# Workaround: We need to use the original expect method of pexpect process which returns
# index of matched pattern instead of Match object returned by dut.expect()
expect_proc: Optional[object] = self.get_expect_proc()
if expect_proc is not None:
match_index = expect_proc.expect(expect_patterns, timeout)
if isinstance(match_index, int):
match_item = expect_items_list[match_index] # type: ignore
match_item['index'] = match_index # type: ignore , keep match index
if isinstance(expect_proc.match, Match) and len(expect_proc.match.groups()) > 0:
match_item['ret'] = expect_proc.match.groups()
if match_item['callback']:
match_item['callback'](match_item['ret']) # execution of callback function
else:
self.logger.error('%s: failed to parse output. Please check component versions.', self.app_name)
raise RuntimeError from None
def dut_test_start(self, dictionary: Dict, timeout_value=TEST_EXPECT_STR_TIMEOUT) -> None: # type: ignore
""" The method to initialize and handle test stages
"""
def handle_get_ip4(data: Optional[Any]) -> None:
""" Handle get_ip v4
"""
self.logger.info('%s[STACK_IPV4]: %s', self.app_name, str(data))
self.test_stage = Stages.STACK_IPV4
def handle_get_ip6(data: Optional[Any]) -> None:
""" Handle get_ip v6
"""
self.logger.info('%s[STACK_IPV6]: %s', self.app_name, str(data))
self.test_stage = Stages.STACK_IPV6
def handle_init(data: Optional[Any]) -> None:
""" Handle init
"""
self.logger.info('%s[STACK_INIT]: %s', self.app_name, str(data))
self.test_stage = Stages.STACK_INIT
def handle_connect(data: Optional[Any]) -> None:
""" Handle connect
"""
self.logger.info('%s[STACK_CONNECT]: %s', self.app_name, str(data))
self.test_stage = Stages.STACK_CONNECT
def handle_test_start(data: Optional[Any]) -> None:
""" Handle connect
"""
self.logger.info('%s[STACK_START]: %s', self.app_name, str(data))
self.test_stage = Stages.STACK_START
def handle_par_ok(data: Optional[Any]) -> None:
""" Handle parameter ok
"""
self.logger.info('%s[READ_PAR_OK]: %s', self.app_name, str(data))
if self.test_stage.value >= Stages.STACK_START.value:
self.param_ok_count += 1
self.test_stage = Stages.STACK_PAR_OK
def handle_par_fail(data: Optional[Any]) -> None:
""" Handle parameter fail
"""
self.logger.info('%s[READ_PAR_FAIL]: %s', self.app_name, str(data))
self.param_fail_count += 1
self.test_stage = Stages.STACK_PAR_FAIL
def handle_destroy(data: Optional[Any]) -> None:
""" Handle destroy
"""
self.logger.info('%s[%s]: %s', self.app_name, Stages.STACK_DESTROY.name, str(data))
self.test_stage = Stages.STACK_DESTROY
self.test_finish = True
while not self.test_finish:
try:
self.expect_any((dictionary[Stages.STACK_IPV4], handle_get_ip4),
(dictionary[Stages.STACK_IPV6], handle_get_ip6),
(dictionary[Stages.STACK_INIT], handle_init),
(dictionary[Stages.STACK_CONNECT], handle_connect),
(dictionary[Stages.STACK_START], handle_test_start),
(dictionary[Stages.STACK_PAR_OK], handle_par_ok),
(dictionary[Stages.STACK_PAR_FAIL], handle_par_fail),
(dictionary[Stages.STACK_DESTROY], handle_destroy),
timeout=timeout_value)
except pexpect.TIMEOUT:
self.logger.info('%s, expect timeout on stage %s (%s seconds)', self.app_name, self.test_stage.name, timeout_value)
self.test_finish = True
def dut_check_errors(self) -> None:
''' Verify allowed percentage of errors for the dut
'''
allowed_ok_percentage = ((self.param_ok_count / (self.param_ok_count + self.param_fail_count + 1)) * 100)
if self.param_ok_count and (allowed_ok_percentage > (100 - ALLOWED_PERCENT_OF_FAILS)):
self.logger.info('%s: ok_count: %d, fail count: %d', self.app_name, self.param_ok_count, self.param_fail_count)
else :
self.logger.error('%s: ok_count: %d, number of failed readings %d exceeds %d percent', self.app_name, self.param_ok_count, self.param_fail_count, ALLOWED_PERCENT_OF_FAILS)
raise RuntimeError from None
############
# Fixtures #
############
@pytest.fixture(scope='session', autouse=True)
def session_tempdir() -> str:
_tmpdir = os.path.join(
os.path.dirname(__file__),
'pytest_embedded_log',
datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),
)
os.makedirs(_tmpdir, exist_ok=True)
return _tmpdir
@pytest.fixture(autouse=True)
@multi_dut_fixture
def junit_properties(
test_case_name: str, record_xml_attribute: Callable[[str, object], None]
) -> None:
"""
This fixture is autoused and will modify the junit report test case name to <target>.<config>.<case_name>
"""
record_xml_attribute('name', test_case_name)
@pytest.fixture(scope='module')
def monkeypatch_module(request: FixtureRequest) -> MonkeyPatch:
mp = MonkeyPatch()
request.addfinalizer(mp.undo)
return mp
@pytest.fixture(scope='module', autouse=True)
def replace_dut_class(monkeypatch_module: MonkeyPatch) -> None:
monkeypatch_module.setattr('pytest_embedded_idf.IdfDut', ModbusTestDut)
@pytest.fixture
@multi_dut_argument
def config(request: FixtureRequest) -> str:
return getattr(request, 'param', None) or DEFAULT_SDKCONFIG
@pytest.fixture
@multi_dut_fixture
def build_dir(app_path: str, target: Optional[str], config: Optional[str]) -> str:
"""
Check local build dir with the following priority:
1. build_<target>_<config>
2. build_<target>
3. build_<config>
4. build
Args:
app_path: app path
target: target
config: config
Returns:
valid build directory
"""
check_dirs = []
if target is not None and config is not None:
check_dirs.append(f'build_{target}_{config}')
if target is not None:
check_dirs.append(f'build_{target}')
if config is not None:
check_dirs.append(f'build_{config}')
check_dirs.append('build')
for check_dir in check_dirs:
binary_path = os.path.join(app_path, check_dir)
if os.path.isdir(binary_path):
logging.info(f'find valid binary path: {binary_path}')
return check_dir
logging.warning(
'checking binary path: %s... missing... try another place', binary_path
)
recommend_place = check_dirs[0]
logging.error(
f'no build dir valid. Please build the binary via "idf.py -B {recommend_place} build" and run pytest again'
)
sys.exit(1)

View File

@ -3,3 +3,4 @@
cmake_minimum_required(VERSION 3.5)
idf_component_register(SRCS "modbus_params.c"
INCLUDE_DIRS "include")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -3,7 +3,7 @@
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../../../")
set(EXCLUDE_COMPONENTS examples test_app test freemodbus)
set(EXCLUDE_COMPONENTS freemodbus)
# Include parameters from common modbus folder
set(MB_PARAMS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../mb_example_common")

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
| ----------------- | ----- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
# Modbus Master Example
@ -72,7 +72,7 @@ RS485 example circuit schematic for connection of master and slave devices into
+-------x-------+ +-------x-------+
RXD <------| RO | DIFFERENTIAL | RO|-----> RXD
| B|---------------|B |
TXD ------>| DI MAX485 | \ / | MAX485 DI|<----- TXD
TXD ------>| DI MAX483 | \ / | MAX483 DI|<----- TXD
ESP32 BOARD | | RS-485 side | | External PC (emulator) with USB to serial or
RTS --+--->| DE | / \ | DE|---+ ESP32 BOARD (slave)
| | A|---------------|A | |
@ -95,15 +95,15 @@ Configure the UART pins used for modbus communication using and table below.
Define the communication mode parameter for master and slave in Kconfig - CONFIG_MB_COMM_MODE (must be the same for master and slave devices in one segment).
Configure the slave address for each slave in the Modbus segment (the CONFIG_MB_SLAVE_ADDR in Kconfig).
```
--------------------------------------------------------------------------------------------------------------------------
| UART Interface | #define | Default ESP32 Pin | Default pins for | External RS485 Driver Pin |
| | | | ESP32-S2(S3, C3) | |
| ----------------------|--------------------|-----------------------|-----------------------|---------------------------|
| Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | GPIO9 | DI |
| Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | GPIO8 | RO |
| Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | GPIO10 | ~RE/DE |
| Ground | n/a | GND | GND | GND |
--------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------
| UART Interface | #define | Default pins for | Default pins for | External RS485 Driver Pin |
| | | ESP32 (C6) | ESP32-S2 (S3, C3, C2, H2) | |
| ----------------------|--------------------|-----------------------|---------------------------|---------------------------|
| Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | GPIO9 | DI |
| Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | GPIO8 | RO |
| Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | GPIO10 | ~RE/DE |
| Ground | n/a | GND | GND | GND |
------------------------------------------------------------------------------------------------------------------------------
```
Note: Each target chip has different GPIO pins available for UART connection. Please refer to UART documentation for selected target for more information.

View File

@ -1,11 +1,21 @@
menu "Modbus Example Configuration"
config MB_UART_PORT_ONE
bool
default y
depends on (ESP_CONSOLE_UART_NUM !=1) && (SOC_UART_NUM > 1)
config MB_UART_PORT_TWO
bool
default y
depends on (ESP_CONSOLE_UART_NUM !=2) && (SOC_UART_NUM > 2)
config MB_UART_PORT_NUM
int "UART port number"
range 0 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
default 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
range 0 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3
default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3
range 0 2 if MB_UART_PORT_TWO
default 2 if MB_UART_PORT_TWO
range 0 1 if MB_UART_PORT_ONE
default 1 if MB_UART_PORT_ONE
help
UART communication port number for Modbus example.
@ -19,11 +29,15 @@ menu "Modbus Example Configuration"
config MB_UART_RXD
int "UART RXD pin number"
range 0 34 if IDF_TARGET_ESP32
default 22 if IDF_TARGET_ESP32
range 0 23 if IDF_TARGET_ESP32C6
default 22 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
default 8 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3
range 0 20 if IDF_TARGET_ESP32C2
range 0 27 if IDF_TARGET_ESP32H2
default 8 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 8 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2
help
GPIO number for UART RX pin. See UART documentation for more information
about available pin numbers for UART.
@ -31,11 +45,15 @@ menu "Modbus Example Configuration"
config MB_UART_TXD
int "UART TXD pin number"
range 0 34 if IDF_TARGET_ESP32
default 23 if IDF_TARGET_ESP32
range 0 23 if IDF_TARGET_ESP32C6
default 23 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
default 9 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3
range 0 20 if IDF_TARGET_ESP32C2
range 0 27 if IDF_TARGET_ESP32H2
default 9 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 9 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2
help
GPIO number for UART TX pin. See UART documentation for more information
about available pin numbers for UART.
@ -43,11 +61,15 @@ menu "Modbus Example Configuration"
config MB_UART_RTS
int "UART RTS pin number"
range 0 34 if IDF_TARGET_ESP32
default 18 if IDF_TARGET_ESP32
range 0 23 if IDF_TARGET_ESP32C6
default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
default 10 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3
range 0 20 if IDF_TARGET_ESP32C2
range 0 27 if IDF_TARGET_ESP32H2
default 10 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 10 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2
help
GPIO number for UART RTS pin. This pin is connected to
~RE/DE pin of RS485 transceiver to switch direction.

View File

@ -121,7 +121,7 @@ static void* master_get_param_data(const mb_parameter_descriptor_t* param_descri
break;
}
} else {
ESP_LOGE(TAG, "Wrong parameter offset for CID #%d", param_descriptor->cid);
ESP_LOGE(TAG, "Wrong parameter offset for CID #%d", (int)param_descriptor->cid);
assert(instance_ptr != NULL);
}
return instance_ptr;
@ -155,11 +155,11 @@ static void master_operation_func(void *arg)
err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
(uint8_t*)temp_data_ptr, &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_ptr);
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
*(uint32_t*)temp_data_ptr);
// Initialize data of test array and write to slave
if (*(uint32_t*)temp_data_ptr != 0xAAAAAAAA) {
memset((void*)temp_data_ptr, 0xAA, param_descriptor->param_size);
@ -167,14 +167,14 @@ static void master_operation_func(void *arg)
err = mbc_master_set_parameter(cid, (char*)param_descriptor->param_key,
(uint8_t*)temp_data_ptr, &type);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%08x), write successful.",
param_descriptor->cid,
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx32 "), write successful.",
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
*(uint32_t*)temp_data_ptr);
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).",
param_descriptor->cid,
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
@ -182,7 +182,7 @@ static void master_operation_func(void *arg)
}
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
@ -194,8 +194,8 @@ static void master_operation_func(void *arg)
*(float*)temp_data_ptr = value;
if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
(param_descriptor->mb_param_type == MB_PARAM_INPUT)) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %f (0x%x) read successful.",
param_descriptor->cid,
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %f (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
value,
@ -209,7 +209,7 @@ static void master_operation_func(void *arg)
uint16_t state = *(uint16_t*)temp_data_ptr;
const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %s (0x%x) read successful.",
param_descriptor->cid,
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
(const char*)rw_str,
@ -221,7 +221,7 @@ static void master_operation_func(void *arg)
}
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
@ -235,7 +235,7 @@ static void master_operation_func(void *arg)
if (alarm_state) {
ESP_LOGI(TAG, "Alarm triggered by cid #%d.",
param_descriptor->cid);
(int)param_descriptor->cid);
} else {
ESP_LOGE(TAG, "Alarm is not triggered after %d retries.",
MASTER_MAX_RETRY);
@ -264,12 +264,10 @@ static esp_err_t master_init(void)
MB_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail.");
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail, returns(0x%x).",
(uint32_t)err);
"mb controller initialization fail, returns(0x%x).", (int)err);
err = mbc_master_setup((void*)&comm);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller setup fail, returns(0x%x).",
(uint32_t)err);
"mb controller setup fail, returns(0x%x).", (int)err);
// Set UART pin numbers
err = uart_set_pin(MB_PORT_NUM, CONFIG_MB_UART_TXD, CONFIG_MB_UART_RXD,
@ -277,21 +275,19 @@ static esp_err_t master_init(void)
err = mbc_master_start();
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller start fail, returns(0x%x).",
(uint32_t)err);
"mb controller start fail, returned (0x%x).", (int)err);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set pin failure, uart_set_pin() returned (0x%x).", (uint32_t)err);
"mb serial set pin failure, uart_set_pin() returned (0x%x).", (int)err);
// Set driver mode to Half Duplex
err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set mode failure, uart_set_mode() returned (0x%x).", (uint32_t)err);
"mb serial set mode failure, uart_set_mode() returned (0x%x).", (int)err);
vTaskDelay(5);
err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller set descriptor fail, returns(0x%x).",
(uint32_t)err);
"mb controller set descriptor fail, returns(0x%x).", (int16_t)err);
ESP_LOGI(TAG, "Modbus master stack initialized...");
return err;
}

View File

@ -0,0 +1,5 @@
CONFIG_MB_COMM_MODE_ASCII=y
CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=400
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y

View File

@ -0,0 +1,6 @@
CONFIG_MB_COMM_MODE_ASCII=n
CONFIG_MB_COMM_MODE_RTU=y
CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=400
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y

View File

@ -3,8 +3,6 @@
#
CONFIG_MB_COMM_MODE_ASCII=y
CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_TIMER_PORT_ENABLED=n
CONFIG_FMB_TIMER_ISR_IN_IRAM=y
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=400
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
| ----------------- | ----- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
# Modbus Slave Example
@ -25,7 +25,7 @@ RS485 example circuit schematic:
+-------x-------+ +-------x-------+
RXD <------| RO | DIFFERENTIAL | RO|-----> RXD
| B|---------------|B |
TXD ------>| DI MAX485 | \ / | MAX485 DI|<----- TXD
TXD ------>| DI MAX483 | \ / | MAX483 DI|<----- TXD
ESP32 board | | RS-485 side | | Modbus master
RTS --+--->| DE | / \ | DE|---+
| | A|---------------|A | |
@ -45,15 +45,15 @@ idf.py menuconfig
Select Modbus Example Configuration menu item.
Configure the UART pins used for modbus communication using the command and table below.
```
--------------------------------------------------------------------------------------------------------------------------
| UART Interface | #define | Default ESP32 Pin | Default pins for | External RS485 Driver Pin |
| | | | ESP32-S2(S3, C3) | |
| ----------------------|--------------------|-----------------------|-----------------------|---------------------------|
| Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | GPIO9 | DI |
| Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | GPIO8 | RO |
| Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | GPIO10 | ~RE/DE |
| Ground | n/a | GND | GND | GND |
--------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------
| UART Interface | #define | Default pins for | Default pins for | External RS485 Driver Pin |
| | | ESP32 (C6) | ESP32-S2 (S3, C3, C2, H2) | |
| ----------------------|--------------------|-----------------------|---------------------------|---------------------------|
| Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | GPIO9 | DI |
| Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | GPIO8 | RO |
| Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | GPIO10 | ~RE/DE |
| Ground | n/a | GND | GND | GND |
------------------------------------------------------------------------------------------------------------------------------
```
Note: Each target chip has different GPIO pins available for UART connection. Please refer to UART documentation for selected target for more information.

View File

@ -2,3 +2,4 @@ set(PROJECT_NAME "modbus_slave")
idf_component_register(SRCS "slave.c"
INCLUDE_DIRS ".")

View File

@ -1,11 +1,21 @@
menu "Modbus Example Configuration"
config MB_UART_PORT_ONE
bool
default y
depends on (ESP_CONSOLE_UART_NUM !=1) && (SOC_UART_NUM > 1)
config MB_UART_PORT_TWO
bool
default y
depends on (ESP_CONSOLE_UART_NUM !=2) && (SOC_UART_NUM > 2)
config MB_UART_PORT_NUM
int "UART port number"
range 0 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
default 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
range 0 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3
default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3
range 0 2 if MB_UART_PORT_TWO
default 2 if MB_UART_PORT_TWO
range 0 1 if MB_UART_PORT_ONE
default 1 if MB_UART_PORT_ONE
help
UART communication port number for Modbus example.
@ -19,11 +29,15 @@ menu "Modbus Example Configuration"
config MB_UART_RXD
int "UART RXD pin number"
range 0 34 if IDF_TARGET_ESP32
default 22 if IDF_TARGET_ESP32
range 0 23 if IDF_TARGET_ESP32C6
default 22 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
default 8 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3
range 0 20 if IDF_TARGET_ESP32C2
range 0 27 if IDF_TARGET_ESP32H2
default 8 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 8 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2
help
GPIO number for UART RX pin. See UART documentation for more information
about available pin numbers for UART.
@ -31,11 +45,15 @@ menu "Modbus Example Configuration"
config MB_UART_TXD
int "UART TXD pin number"
range 0 34 if IDF_TARGET_ESP32
default 23 if IDF_TARGET_ESP32
range 0 23 if IDF_TARGET_ESP32C6
default 23 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
default 9 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3
range 0 20 if IDF_TARGET_ESP32C2
range 0 27 if IDF_TARGET_ESP32H2
default 9 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 9 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2
help
GPIO number for UART TX pin. See UART documentation for more information
about available pin numbers for UART.
@ -43,11 +61,15 @@ menu "Modbus Example Configuration"
config MB_UART_RTS
int "UART RTS pin number"
range 0 34 if IDF_TARGET_ESP32
default 18 if IDF_TARGET_ESP32
range 0 23 if IDF_TARGET_ESP32C6
default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
default 10 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3
range 0 20 if IDF_TARGET_ESP32C2
range 0 27 if IDF_TARGET_ESP32H2
default 10 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 10 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2
help
GPIO number for UART RTS pin. This pin is connected to
~RE/DE pin of RS485 transceiver to switch direction.

View File

@ -182,13 +182,13 @@ void app_main(void)
if(event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
// Get parameter information from parameter queue
ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
ESP_LOGI(TAG, "HOLDING %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
ESP_LOGI(TAG, "HOLDING %s (%" PRIu32 " us), ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
rw_str,
(uint32_t)reg_info.time_stamp,
(uint32_t)reg_info.mb_offset,
(uint32_t)reg_info.type,
reg_info.time_stamp,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(uint32_t)reg_info.address,
(uint32_t)reg_info.size);
(unsigned)reg_info.size);
if (reg_info.address == (uint8_t*)&holding_reg_params.holding_data0)
{
portENTER_CRITICAL(&param_lock);
@ -200,29 +200,29 @@ void app_main(void)
}
} else if (event & MB_EVENT_INPUT_REG_RD) {
ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
ESP_LOGI(TAG, "INPUT READ (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
(uint32_t)reg_info.time_stamp,
(uint32_t)reg_info.mb_offset,
(uint32_t)reg_info.type,
ESP_LOGI(TAG, "INPUT READ (%" PRIu32 " us), ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
reg_info.time_stamp,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(uint32_t)reg_info.address,
(uint32_t)reg_info.size);
(unsigned)reg_info.size);
} else if (event & MB_EVENT_DISCRETE_RD) {
ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
ESP_LOGI(TAG, "DISCRETE READ (%u us): ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
(uint32_t)reg_info.time_stamp,
(uint32_t)reg_info.mb_offset,
(uint32_t)reg_info.type,
ESP_LOGI(TAG, "DISCRETE READ (%" PRIu32 " us): ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
reg_info.time_stamp,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(uint32_t)reg_info.address,
(uint32_t)reg_info.size);
(unsigned)reg_info.size);
} else if (event & (MB_EVENT_COILS_RD | MB_EVENT_COILS_WR)) {
ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
ESP_LOGI(TAG, "COILS %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
ESP_LOGI(TAG, "COILS %s (%" PRIu32 " us), ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
rw_str,
(uint32_t)reg_info.time_stamp,
(uint32_t)reg_info.mb_offset,
(uint32_t)reg_info.type,
reg_info.time_stamp,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(uint32_t)reg_info.address,
(uint32_t)reg_info.size);
(unsigned)reg_info.size);
if (coil_reg_params.coils_port1 == 0xFF) break;
}
}

View File

@ -0,0 +1,4 @@
CONFIG_MB_COMM_MODE_ASCII=y
CONFIG_MB_SLAVE_ADDR=1
CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y

View File

@ -0,0 +1,5 @@
CONFIG_MB_COMM_MODE_ASCII=n
CONFIG_MB_COMM_MODE_RTU=y
CONFIG_MB_SLAVE_ADDR=1
CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y

View File

@ -4,6 +4,4 @@
CONFIG_MB_COMM_MODE_ASCII=y
CONFIG_MB_SLAVE_ADDR=1
CONFIG_MB_UART_BAUD_RATE=115200
CONFIG_FMB_TIMER_PORT_ENABLED=n
CONFIG_FMB_TIMER_ISR_IN_IRAM=y
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y

View File

@ -0,0 +1,66 @@
# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
# This is the script to reproduce the issue when the expect() is called from
# main thread in Multi DUT case.
import logging
import os
from typing import Tuple
import pytest
from conftest import ModbusTestDut, Stages
pattern_dict_slave = {Stages.STACK_IPV4: (r'I \([0-9]+\) example_connect: - IPv4 address: ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
Stages.STACK_IPV6: (r'I \([0-9]+\) example_connect: - IPv6 address: (([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})'),
Stages.STACK_INIT: (r'I \(([0-9]+)\) MB_TCP_SLAVE_PORT: (Protocol stack initialized).'),
Stages.STACK_CONNECT: (r'I\s\(([0-9]+)\) MB_TCP_SLAVE_PORT: Socket \(#[0-9]+\), accept client connection from address: '
r'([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
Stages.STACK_START: (r'I\s\(([0-9]+)\) SLAVE_TEST: (Start modbus test)'),
Stages.STACK_PAR_OK: (r'I\s\(([0-9]+)\) SLAVE_TEST: ([A-Z]+ [A-Z]+) \([a-zA-Z0-9_]+ us\),\s'
r'ADDR:([0-9]+), TYPE:[0-9]+, INST_ADDR:0x[a-zA-Z0-9]+, SIZE:[0-9]+'),
Stages.STACK_PAR_FAIL: (r'E \(([0-9]+)\) SLAVE_TEST: Response time exceeds configured [0-9]+ [ms], ignore packet'),
Stages.STACK_DESTROY: (r'I\s\(([0-9]+)\) SLAVE_TEST: (Modbus controller destroyed).')}
pattern_dict_master = {Stages.STACK_IPV4: (r'I \([0-9]+\) example_connect: - IPv4 address: ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
Stages.STACK_IPV6: (r'I \([0-9]+\) example_connect: - IPv6 address: (([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})'),
Stages.STACK_INIT: (r'I \(([0-9]+)\) MASTER_TEST: (Modbus master stack initialized)'),
Stages.STACK_CONNECT: (r'I\s\(([0-9]+)\) MB_TCP_MASTER_PORT: (Connected [0-9]+ slaves), start polling'),
Stages.STACK_START: (r'I \(([0-9]+)\) MASTER_TEST: (Start modbus test)'),
Stages.STACK_PAR_OK: (r'I \(([0-9]+)\) MASTER_TEST: Characteristic #[0-9]+ ([a-zA-Z0-9_]+)'
r'\s\([a-zA-Z\_\%\/]+\) value =[a-zA-Z0-9\.\s]* \((0x[a-zA-Z0-9]+)\)[,\sa-z]+ successful'),
Stages.STACK_PAR_FAIL: (r'.*E \(([0-9]+)\) MASTER_TEST: Characteristic #[0-9]+\s\(([a-zA-Z0-9_]+)\)\s'
r'read fail, err = [0-9]+ \([_A-Z]+\)'),
Stages.STACK_DESTROY: (r'I \(([0-9]+)\) MASTER_TEST: (Destroy master)...')}
LOG_LEVEL = logging.DEBUG
LOGGER_NAME = 'modbus_test'
logger = logging.getLogger(LOGGER_NAME)
test_configs = [
'rtu',
'ascii'
]
@pytest.mark.esp32
@pytest.mark.multi_dut_modbus_serial
@pytest.mark.parametrize('config', test_configs, indirect=True)
@pytest.mark.parametrize(
'count, app_path', [
(2, f'{os.path.join(os.path.dirname(__file__), "mb_serial_master")}|{os.path.join(os.path.dirname(__file__), "mb_serial_slave")}')
],
indirect=True
)
def test_modbus_serial_communication(config: str, dut: Tuple[ModbusTestDut, ModbusTestDut]) -> None:
dut_slave = dut[1]
dut_master = dut[0]
logger.info('DUT: %s start.', dut_master.dut_get_name())
logger.info('DUT: %s start.', dut_slave.dut_get_name())
dut_slave.dut_test_start(dictionary=pattern_dict_slave)
dut_master.dut_test_start(dictionary=pattern_dict_master)
dut_slave.dut_check_errors()
dut_master.dut_check_errors()

View File

@ -9,7 +9,6 @@ set(EXCLUDE_COMPONENTS examples test_app test freemodbus)
set(MB_PARAMS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../mb_example_common")
list(APPEND EXTRA_COMPONENT_DIRS "${MB_PARAMS_DIR}")
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
| ----------------- | ----- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
# Modbus TCP Master Example

View File

@ -2,3 +2,4 @@ set(PROJECT_NAME "modbus_tcp_master")
idf_component_register(SRCS "tcp_master.c"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -0,0 +1,6 @@
dependencies:
idf: ">=4.1"
espressif/mdns:
version: "^1.0.0"
rules:
- if: "idf_version >=5.0"

View File

@ -15,6 +15,11 @@
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#if __has_include("esp_mac.h")
#include "esp_mac.h"
#endif
#include "mdns.h"
#include "protocol_examples_common.h"
@ -68,7 +73,7 @@ static const char *TAG = "MASTER_TEST";
// Enumeration of modbus device addresses accessed by master device
// Each address in the table is a index of TCP slave ip address in mb_communication_info_t::tcp_ip_addr table
enum {
MB_DEVICE_ADDR1 = 1, // Slave address 1
MB_DEVICE_ADDR1 = 1, // Slave UID = 1
MB_DEVICE_ADDR2 = 200,
MB_DEVICE_ADDR3 = 35
};
@ -180,7 +185,7 @@ static int master_get_slave_ip_stdin(char** addr_table)
ESP_ERROR_CHECK(example_configure_stdin_stdout());
while(1) {
if (addr_table[ip_cnt] && strcmp(addr_table[ip_cnt], "FROM_STDIN") == 0) {
printf("Waiting IP%d from stdin:\r\n", ip_cnt);
printf("Waiting IP%d from stdin:\r\n", (int)ip_cnt);
while (fgets(buf, sizeof(buf), stdin) == NULL) {
fputs(buf, stdout);
}
@ -189,7 +194,7 @@ static int master_get_slave_ip_stdin(char** addr_table)
fputc('\n', stdout);
ip_str = master_scan_addr(&index, buf);
if (ip_str != NULL) {
ESP_LOGI(TAG, "IP(%d) = [%s] set from stdin.", ip_cnt, ip_str);
ESP_LOGI(TAG, "IP(%d) = [%s] set from stdin.", (int)ip_cnt, ip_str);
if ((ip_cnt >= ip_table_sz) || (index != ip_cnt)) {
addr_table[ip_cnt] = NULL;
break;
@ -202,10 +207,10 @@ static int master_get_slave_ip_stdin(char** addr_table)
}
} else {
if (addr_table[ip_cnt]) {
ESP_LOGI(TAG, "Leave IP(%d) = [%s] set manually.", ip_cnt, addr_table[ip_cnt]);
ESP_LOGI(TAG, "Leave IP(%d) = [%s] set manually.", (int)ip_cnt, addr_table[ip_cnt]);
ip_cnt++;
} else {
ESP_LOGI(TAG, "IP(%d) is not set in the table.", ip_cnt);
ESP_LOGI(TAG, "IP(%d) is not set in the table.", (int)ip_cnt);
break;
}
}
@ -301,7 +306,7 @@ static esp_err_t master_resolve_slave(uint8_t short_addr, mdns_result_t* result,
char slave_name[22] = {0};
if (sprintf(slave_name, "mb_slave_tcp_%02X", short_addr) < 0) {
ESP_LOGE(TAG, "Fail to create instance name for index: %d", short_addr);
ESP_LOGE(TAG, "Fail to create instance name for index: %d", (int)short_addr);
abort();
}
for (; r ; r = r->next) {
@ -323,7 +328,7 @@ static esp_err_t master_resolve_slave(uint8_t short_addr, mdns_result_t* result,
}
slave_ip = master_get_slave_ip_str(r->addr, addr_type);
if (slave_ip) {
ESP_LOGI(TAG, "Resolved slave %s[%s]:%u", r->hostname, slave_ip, r->port);
ESP_LOGI(TAG, "Resolved slave %s[%s]:%u", r->hostname, slave_ip, (unsigned)r->port);
*resolved_ip = slave_ip;
return ESP_OK;
}
@ -362,7 +367,7 @@ static int master_create_slave_list(mdns_result_t* results, char** addr_table,
// Resolve new slave IP address using its short address
esp_err_t err = master_resolve_slave(slave_addr, results, &slave_ip, addr_type);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Index: %d, sl_addr: %d, failed to resolve!", i, slave_addr);
ESP_LOGE(TAG, "Index: %d, sl_addr: %d, failed to resolve!", (int)i, (int)slave_addr);
// Set correspond index to NULL indicate host not resolved
ip_table[ip_index] = NULL;
continue;
@ -379,7 +384,7 @@ static int master_create_slave_list(mdns_result_t* results, char** addr_table,
LIST_INSERT_HEAD(&slave_addr_list, new_slave_entry, entries);
ip_table[ip_index] = slave_ip;
ESP_LOGI(TAG, "Index: %d, sl_addr: %d, resolved to IP: [%s]",
i, slave_addr, slave_ip);
(int)i, (int)slave_addr, slave_ip);
cid_resolve_cnt++;
if (ip_index < addr_table_size) {
ip_index++;
@ -387,11 +392,11 @@ static int master_create_slave_list(mdns_result_t* results, char** addr_table,
} else {
ip_table[ip_index] = it ? it->ip_address : ip_table[ip_index];
ESP_LOGI(TAG, "Index: %d, sl_addr: %d, set to IP: [%s]",
i, slave_addr, ip_table[ip_index]);
(int)i, (int)slave_addr, ip_table[ip_index]);
cid_resolve_cnt++;
}
}
ESP_LOGI(TAG, "Resolved %d cids, with %d IP addresses", cid_resolve_cnt, ip_index);
ESP_LOGI(TAG, "Resolved %d cids, with %d IP addresses", (int)cid_resolve_cnt, (int)ip_index);
return cid_resolve_cnt;
}
@ -424,7 +429,7 @@ static void master_destroy_slave_list(char** table, size_t ip_table_size)
{
#if CONFIG_MB_MDNS_IP_RESOLVER
slave_addr_entry_t *it;
LIST_FOREACH(it, &slave_addr_list, entries) {
while ((it = LIST_FIRST(&slave_addr_list))) {
LIST_REMOVE(it, entries);
free(it);
}
@ -466,7 +471,7 @@ static void* master_get_param_data(const mb_parameter_descriptor_t* param_descri
break;
}
} else {
ESP_LOGE(TAG, "Wrong parameter offset for CID #%d", param_descriptor->cid);
ESP_LOGE(TAG, "Wrong parameter offset for CID #%d", (int)param_descriptor->cid);
assert(instance_ptr != NULL);
}
return instance_ptr;
@ -500,11 +505,11 @@ static void master_operation_func(void *arg)
err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
(uint8_t*)temp_data_ptr, &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_ptr);
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
*(uint32_t*)temp_data_ptr);
// Initialize data of test array and write to slave
if (*(uint32_t*)temp_data_ptr != 0xAAAAAAAA) {
memset((void*)temp_data_ptr, 0xAA, param_descriptor->param_size);
@ -512,14 +517,14 @@ static void master_operation_func(void *arg)
err = mbc_master_set_parameter(cid, (char*)param_descriptor->param_key,
(uint8_t*)temp_data_ptr, &type);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%08x), write successful.",
param_descriptor->cid,
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x" PRIx32 "), write successful.",
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
*(uint32_t*)temp_data_ptr);
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).",
param_descriptor->cid,
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
@ -527,7 +532,7 @@ static void master_operation_func(void *arg)
}
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
@ -539,8 +544,8 @@ static void master_operation_func(void *arg)
if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
(param_descriptor->mb_param_type == MB_PARAM_INPUT)) {
value = *(float*)temp_data_ptr;
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %f (0x%x) read successful.",
param_descriptor->cid,
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %f (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
value,
@ -554,7 +559,7 @@ static void master_operation_func(void *arg)
uint8_t state = *(uint8_t*)temp_data_ptr;
const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %s (0x%x) read successful.",
param_descriptor->cid,
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
(const char*)rw_str,
@ -566,7 +571,7 @@ static void master_operation_func(void *arg)
}
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
(int)param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
@ -580,7 +585,7 @@ static void master_operation_func(void *arg)
if (alarm_state) {
ESP_LOGI(TAG, "Alarm triggered by cid #%d.",
param_descriptor->cid);
(int)param_descriptor->cid);
} else {
ESP_LOGE(TAG, "Alarm is not triggered after %d retries.",
MASTER_MAX_RETRY);
@ -599,17 +604,17 @@ static esp_err_t init_services(mb_tcp_addr_type_t ip_addr_type)
MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"nvs_flash_init fail, returns(0x%x).",
(uint32_t)result);
(int)result);
result = esp_netif_init();
MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"esp_netif_init fail, returns(0x%x).",
(uint32_t)result);
(int)result);
result = esp_event_loop_create_default();
MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"esp_event_loop_create_default fail, returns(0x%x).",
(uint32_t)result);
(int)result);
#if CONFIG_MB_MDNS_IP_RESOLVER
// Start mdns service and register device
master_start_mdns_service();
@ -621,13 +626,13 @@ static esp_err_t init_services(mb_tcp_addr_type_t ip_addr_type)
MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"example_connect fail, returns(0x%x).",
(uint32_t)result);
(int)result);
#if CONFIG_EXAMPLE_CONNECT_WIFI
result = esp_wifi_set_ps(WIFI_PS_NONE);
MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"esp_wifi_set_ps fail, returns(0x%x).",
(uint32_t)result);
(int)result);
#endif
#if CONFIG_MB_MDNS_IP_RESOLVER
int res = 0;
@ -635,7 +640,7 @@ static esp_err_t init_services(mb_tcp_addr_type_t ip_addr_type)
res = master_query_slave_service("_modbus", "_tcp", ip_addr_type);
}
if (res < num_device_parameters) {
ESP_LOGE(TAG, "Could not resolve one or more slave IP addresses, resolved: %d out of %d.", res, num_device_parameters );
ESP_LOGE(TAG, "Could not resolve one or more slave IP addresses, resolved: %d out of %d.", (uint16_t)res, (uint16_t)num_device_parameters );
ESP_LOGE(TAG, "Make sure you configured all slaves according to device parameter table and they alive in the network.");
return ESP_ERR_NOT_FOUND;
}
@ -643,7 +648,7 @@ static esp_err_t init_services(mb_tcp_addr_type_t ip_addr_type)
#elif CONFIG_MB_SLAVE_IP_FROM_STDIN
int ip_cnt = master_get_slave_ip_stdin(slave_ip_address_table);
if (ip_cnt) {
ESP_LOGI(TAG, "Configured %d IP addresse(s).", ip_cnt);
ESP_LOGI(TAG, "Configured %d IP addresse(s).", (int)ip_cnt);
} else {
ESP_LOGE(TAG, "Fail to get IP address from stdin. Continue.");
return ESP_ERR_NOT_FOUND;
@ -661,22 +666,22 @@ static esp_err_t destroy_services(void)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"example_disconnect fail, returns(0x%x).",
(uint32_t)err);
(int)err);
err = esp_event_loop_delete_default();
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"esp_event_loop_delete_default fail, returns(0x%x).",
(uint32_t)err);
(int)err);
err = esp_netif_deinit();
MB_RETURN_ON_FALSE((err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_STATE,
TAG,
"esp_netif_deinit fail, returns(0x%x).",
(uint32_t)err);
(int)err);
err = nvs_flash_deinit();
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"nvs_flash_deinit fail, returns(0x%x).",
(uint32_t)err);
(int)err);
return err;
}
@ -692,26 +697,26 @@ static esp_err_t master_init(mb_communication_info_t* comm_info)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mb controller initialization fail, returns(0x%x).",
(uint32_t)err);
(int)err);
err = mbc_master_setup((void*)comm_info);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mb controller setup fail, returns(0x%x).",
(uint32_t)err);
(int)err);
err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mb controller set descriptor fail, returns(0x%x).",
(uint32_t)err);
(int)err);
ESP_LOGI(TAG, "Modbus master stack initialized...");
err = mbc_master_start();
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mb controller start fail, returns(0x%x).",
(uint32_t)err);
(int)err);
vTaskDelay(5);
return err;
}
@ -722,7 +727,7 @@ static esp_err_t master_destroy(void)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_master_destroy fail, returns(0x%x).",
(uint32_t)err);
(int)err);
ESP_LOGI(TAG, "Modbus master stack destroy...");
return err;
}
@ -745,7 +750,6 @@ void app_main(void)
comm_info.ip_netif_ptr = (void*)get_example_netif();
ESP_ERROR_CHECK(master_init(&comm_info));
vTaskDelay(50);
master_operation_func(NULL);
ESP_ERROR_CHECK(master_destroy());

View File

@ -0,0 +1,33 @@
#
# Modbus configuration
#
CONFIG_FMB_COMM_MODE_TCP_EN=y
CONFIG_FMB_TCP_PORT_DEFAULT=502
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
CONFIG_FMB_PORT_TASK_PRIO=10
CONFIG_FMB_COMM_MODE_RTU_EN=n
CONFIG_FMB_COMM_MODE_ASCII_EN=n
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_MB_MDNS_IP_RESOLVER=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_MB_SLAVE_ADDR=1
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_ETHERNET_EMAC_TASK_STACK_SIZE=4096
CONFIG_ETH_ENABLED=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ETH_PHY_INTERFACE_RMII=y
CONFIG_ETH_USE_SPI_ETHERNET=n

View File

@ -0,0 +1,19 @@
CONFIG_FMB_COMM_MODE_TCP_EN=y
CONFIG_FMB_TCP_PORT_DEFAULT=502
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
CONFIG_FMB_PORT_TASK_PRIO=10
CONFIG_FMB_COMM_MODE_RTU_EN=n
CONFIG_FMB_COMM_MODE_ASCII_EN=n
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_MB_MDNS_IP_RESOLVER=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
CONFIG_EXAMPLE_CONNECT_WIFI=y
CONFIG_EXAMPLE_WIFI_SSID="${CI_WIFI_SSID}"
CONFIG_EXAMPLE_WIFI_PASSWORD="${CI_WIFI_PASSW}"

View File

@ -10,9 +10,12 @@ CONFIG_FMB_COMM_MODE_RTU_EN=n
CONFIG_FMB_COMM_MODE_ASCII_EN=n
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=2000
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
CONFIG_FMB_TIMER_PORT_ENABLED=y
CONFIG_FMB_TIMER_ISR_IN_IRAM=y
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_MB_MDNS_IP_RESOLVER=n
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
CONFIG_EXAMPLE_CONNECT_WIFI=y
CONFIG_EXAMPLE_WIFI_SSID="${CI_WIFI_SSID}"
CONFIG_EXAMPLE_WIFI_PASSWORD="${CI_WIFI_PASSW}"

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
| ----------------- | ----- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
# Modbus Slave Example

View File

@ -1,4 +1,5 @@
set(PROJECT_NAME "modbus_tcp_slave")
idf_component_register(SRCS "tcp_slave.c"
INCLUDE_DIRS ".")
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -2,7 +2,8 @@ menu "Modbus Example Configuration"
config MB_SLAVE_ADDR
int "Modbus slave address"
range 1 255
range 1 247 if !FMB_TCP_UID_ENABLED
range 0 247 if FMB_TCP_UID_ENABLED
default 1
help
This is the Modbus slave address in the network.

View File

@ -0,0 +1,6 @@
dependencies:
idf: ">=4.1"
espressif/mdns:
version: "^1.0.0"
rules:
- if: "idf_version >=5.0"

View File

@ -15,9 +15,13 @@
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "mdns.h"
#include "esp_netif.h"
#if __has_include("esp_mac.h")
#include "esp_mac.h"
#endif
#include "protocol_examples_common.h"
#include "mbcontroller.h" // for mbcontroller defines and api
@ -48,6 +52,8 @@
| MB_EVENT_COILS_WR)
#define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK)
#define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR)
static const char *TAG = "SLAVE_TEST";
static portMUX_TYPE param_lock = portMUX_INITIALIZER_UNLOCKED;
@ -65,8 +71,6 @@ static portMUX_TYPE param_lock = portMUX_INITIALIZER_UNLOCKED;
#define MB_DEVICE_ID (uint32_t)CONFIG_FMB_CONTROLLER_SLAVE_ID
#endif
#define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR)
#define MB_MDNS_INSTANCE(pref) pref"mb_slave_tcp"
// convert mac from binary format to string
@ -176,11 +180,11 @@ static void slave_operation_func(void *arg)
ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
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);
(unsigned)reg_info.time_stamp,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(int)reg_info.address,
(unsigned)reg_info.size);
if (reg_info.address == (uint8_t*)&holding_reg_params.holding_data0)
{
portENTER_CRITICAL(&param_lock);
@ -193,28 +197,28 @@ static void slave_operation_func(void *arg)
} else if (event & MB_EVENT_INPUT_REG_RD) {
ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
ESP_LOGI(TAG, "INPUT READ (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
(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);
(unsigned)reg_info.time_stamp,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(int)reg_info.address,
(unsigned)reg_info.size);
} else if (event & MB_EVENT_DISCRETE_RD) {
ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
ESP_LOGI(TAG, "DISCRETE READ (%u us): ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
(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);
(unsigned)reg_info.time_stamp,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(int)reg_info.address,
(unsigned)reg_info.size);
} else if (event & (MB_EVENT_COILS_RD | MB_EVENT_COILS_WR)) {
ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
ESP_LOGI(TAG, "COILS %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);
(unsigned)reg_info.time_stamp,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(int)reg_info.address,
(unsigned)reg_info.size);
if (coil_reg_params.coils_port1 == 0xFF) break;
}
}
@ -233,17 +237,17 @@ static esp_err_t init_services(void)
MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"nvs_flash_init fail, returns(0x%x).",
(uint32_t)result);
(int)result);
result = esp_netif_init();
MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"esp_netif_init fail, returns(0x%x).",
(uint32_t)result);
(int)result);
result = esp_event_loop_create_default();
MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"esp_event_loop_create_default fail, returns(0x%x).",
(uint32_t)result);
(int)result);
#if CONFIG_MB_MDNS_IP_RESOLVER
// Start mdns service and register device
start_mdns_service();
@ -255,13 +259,13 @@ static esp_err_t init_services(void)
MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"example_connect fail, returns(0x%x).",
(uint32_t)result);
(int)result);
#if CONFIG_EXAMPLE_CONNECT_WIFI
result = esp_wifi_set_ps(WIFI_PS_NONE);
MB_RETURN_ON_FALSE((result == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"esp_wifi_set_ps fail, returns(0x%x).",
(uint32_t)result);
(int)result);
#endif
return ESP_OK;
}
@ -274,22 +278,22 @@ static esp_err_t destroy_services(void)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"example_disconnect fail, returns(0x%x).",
(uint32_t)err);
(int)err);
err = esp_event_loop_delete_default();
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"esp_event_loop_delete_default fail, returns(0x%x).",
(uint32_t)err);
(int)err);
err = esp_netif_deinit();
MB_RETURN_ON_FALSE((err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_STATE,
TAG,
"esp_netif_deinit fail, returns(0x%x).",
(uint32_t)err);
(int)err);
err = nvs_flash_deinit();
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"nvs_flash_deinit fail, returns(0x%x).",
(uint32_t)err);
(int)err);
#if CONFIG_MB_MDNS_IP_RESOLVER
stop_mdns_service();
#endif
@ -311,13 +315,14 @@ static esp_err_t slave_init(mb_communication_info_t* comm_info)
comm_info->ip_addr = NULL; // Bind to any address
comm_info->ip_netif_ptr = (void*)get_example_netif();
comm_info->slave_uid = MB_SLAVE_ADDR;
// Setup communication parameters and start stack
err = mbc_slave_setup((void*)comm_info);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_setup fail, returns(0x%x).",
(uint32_t)err);
(int)err);
// The code below initializes Modbus register area descriptors
// for Modbus Holding Registers, Input Registers, Coils and Discrete Inputs
@ -333,7 +338,7 @@ static esp_err_t slave_init(mb_communication_info_t* comm_info)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(uint32_t)err);
(int)err);
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START_AREA1; // Offset of register area in Modbus protocol
@ -343,7 +348,7 @@ static esp_err_t slave_init(mb_communication_info_t* comm_info)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(uint32_t)err);
(int)err);
// Initialization of Input Registers area
reg_area.type = MB_PARAM_INPUT;
@ -354,7 +359,7 @@ static esp_err_t slave_init(mb_communication_info_t* comm_info)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(uint32_t)err);
(int)err);
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = MB_REG_INPUT_START_AREA1;
reg_area.address = (void*)&input_reg_params.input_data4;
@ -363,7 +368,7 @@ static esp_err_t slave_init(mb_communication_info_t* comm_info)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(uint32_t)err);
(int)err);
// Initialization of Coils register area
reg_area.type = MB_PARAM_COIL;
@ -374,7 +379,7 @@ static esp_err_t slave_init(mb_communication_info_t* comm_info)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(uint32_t)err);
(int)err);
// Initialization of Discrete Inputs register area
reg_area.type = MB_PARAM_DISCRETE;
@ -385,7 +390,7 @@ static esp_err_t slave_init(mb_communication_info_t* comm_info)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(uint32_t)err);
(int)err);
// Set values into known state
setup_reg_data();
@ -395,7 +400,7 @@ static esp_err_t slave_init(mb_communication_info_t* comm_info)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_start fail, returns(0x%x).",
(uint32_t)err);
(int)err);
vTaskDelay(5);
return err;
}
@ -406,7 +411,7 @@ static esp_err_t slave_destroy(void)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_destroy fail, returns(0x%x).",
(uint32_t)err);
(int)err);
return err;
}
@ -419,6 +424,7 @@ void app_main(void)
ESP_ERROR_CHECK(init_services());
// Set UART log level
esp_log_level_set(TAG, ESP_LOG_INFO);
mb_communication_info_t comm_info = { 0 };

View File

@ -0,0 +1,34 @@
#
# Modbus configuration
#
CONFIG_FMB_COMM_MODE_TCP_EN=y
CONFIG_FMB_TCP_PORT_DEFAULT=502
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
CONFIG_FMB_PORT_TASK_PRIO=10
CONFIG_FMB_COMM_MODE_RTU_EN=n
CONFIG_FMB_COMM_MODE_ASCII_EN=n
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
CONFIG_FMB_TIMER_PORT_ENABLED=y
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_MB_MDNS_IP_RESOLVER=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_MB_SLAVE_ADDR=1
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_ETHERNET_EMAC_TASK_STACK_SIZE=4096
CONFIG_ETH_ENABLED=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ETH_PHY_INTERFACE_RMII=y
CONFIG_ETH_USE_SPI_ETHERNET=n

View File

@ -0,0 +1,20 @@
CONFIG_FMB_COMM_MODE_TCP_EN=y
CONFIG_FMB_TCP_PORT_DEFAULT=502
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20
CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
CONFIG_FMB_PORT_TASK_PRIO=10
CONFIG_FMB_COMM_MODE_RTU_EN=n
CONFIG_FMB_COMM_MODE_ASCII_EN=n
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_MB_MDNS_IP_RESOLVER=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_MB_SLAVE_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
CONFIG_EXAMPLE_CONNECT_WIFI=y
CONFIG_EXAMPLE_WIFI_SSID="${CI_WIFI_SSID}"
CONFIG_EXAMPLE_WIFI_PASSWORD="${CI_WIFI_PASSW}"

View File

@ -8,13 +8,17 @@ CONFIG_FMB_PORT_TASK_STACK_SIZE=4096
CONFIG_FMB_PORT_TASK_PRIO=10
CONFIG_FMB_COMM_MODE_RTU_EN=n
CONFIG_FMB_COMM_MODE_ASCII_EN=n
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=1000
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=2000
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
CONFIG_FMB_TIMER_PORT_ENABLED=y
CONFIG_FMB_TIMER_ISR_IN_IRAM=y
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_MB_MDNS_IP_RESOLVER=n
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_MB_SLAVE_ADDR=1
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_EXAMPLE_WIFI_SSID="${CI_WIFI_SSID}"
CONFIG_EXAMPLE_WIFI_PASSWORD="${CI_WIFI_PASSW}"
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
CONFIG_EXAMPLE_CONNECT_WIFI=y

View File

@ -0,0 +1,69 @@
# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
# This is the script to reproduce the issue when the expect() is called from
# main thread in Multi DUT case.
import logging
import os
from typing import Tuple
import pytest
from conftest import ModbusTestDut, Stages
pattern_dict_slave = {Stages.STACK_IPV4: (r'I \([0-9]+\) example_connect: - IPv4 address: ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
Stages.STACK_IPV6: (r'I \([0-9]+\) example_connect: - IPv6 address: (([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})'),
Stages.STACK_INIT: (r'I \(([0-9]+)\) MB_TCP_SLAVE_PORT: (Protocol stack initialized).'),
Stages.STACK_CONNECT: (r'I\s\(([0-9]+)\) MB_TCP_SLAVE_PORT: Socket \(#[0-9]+\), accept client connection from address: '
r'([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
Stages.STACK_START: (r'I\s\(([0-9]+)\) SLAVE_TEST: (Start modbus test)'),
Stages.STACK_PAR_OK: (r'I\s\(([0-9]+)\) SLAVE_TEST: ([A-Z]+ [A-Z]+) \([a-zA-Z0-9_]+ us\),\s'
r'ADDR:([0-9]+), TYPE:[0-9]+, INST_ADDR:0x[a-zA-Z0-9]+, SIZE:[0-9]+'),
Stages.STACK_PAR_FAIL: (r'E \(([0-9]+)\) SLAVE_TEST: Response time exceeds configured [0-9]+ [ms], ignore packet'),
Stages.STACK_DESTROY: (r'I\s\(([0-9]+)\) SLAVE_TEST: (Modbus controller destroyed).')}
pattern_dict_master = {Stages.STACK_IPV4: (r'I \([0-9]+\) example_connect: - IPv4 address: ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'),
Stages.STACK_IPV6: (r'I \([0-9]+\) example_connect: - IPv6 address: (([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})'),
Stages.STACK_INIT: (r'I \(([0-9]+)\) MASTER_TEST: (Modbus master stack initialized)'),
Stages.STACK_CONNECT: (r'I\s\(([0-9]+)\) MB_TCP_MASTER_PORT: (Connected [0-9]+ slaves), start polling'),
Stages.STACK_START: (r'I \(([0-9]+)\) MASTER_TEST: (Start modbus test)'),
Stages.STACK_PAR_OK: (r'I \(([0-9]+)\) MASTER_TEST: Characteristic #[0-9]+ ([a-zA-Z0-9_]+)'
r'\s\([a-zA-Z\_\%\/]+\) value =[a-zA-Z0-9\.\s]* \((0x[a-zA-Z0-9]+)\)[,\sa-z]+ successful'),
Stages.STACK_PAR_FAIL: (r'.*E \(([0-9]+)\) MASTER_TEST: Characteristic #[0-9]+\s\(([a-zA-Z0-9_]+)\)\s'
r'read fail, err = [x0-9]+ \([_A-Z]+\)'),
Stages.STACK_DESTROY: (r'I \(([0-9]+)\) MASTER_TEST: (Destroy master)...')}
LOG_LEVEL = logging.DEBUG
LOGGER_NAME = 'modbus_test'
logger = logging.getLogger(LOGGER_NAME)
test_configs = [
'wifi',
'ethernet'
]
@pytest.mark.esp32
@pytest.mark.multi_dut_modbus_tcp
@pytest.mark.parametrize('config', test_configs, indirect=True)
@pytest.mark.parametrize(
'count, app_path', [
(2, f'{os.path.join(os.path.dirname(__file__), "mb_tcp_master")}|{os.path.join(os.path.dirname(__file__), "mb_tcp_slave")}')
],
indirect=True
)
def test_modbus_tcp_communication(dut: Tuple[ModbusTestDut, ModbusTestDut]) -> None:
dut_slave = dut[1]
dut_master = dut[0]
logger.info('DUT: %s start.', dut_master.dut_get_name())
logger.info('DUT: %s start.', dut_slave.dut_get_name())
dut_slave_ip_address = dut_slave.dut_get_ip()
dut_master.dut_send_ip(dut_slave_ip_address)
dut_slave.dut_test_start(dictionary=pattern_dict_slave)
dut_master.dut_test_start(dictionary=pattern_dict_master)
dut_slave.dut_check_errors()
dut_master.dut_check_errors()

View File

@ -0,0 +1,16 @@
library/error\.o
/.*error\S*\.o
.*error.*\.c\.obj
.*error.*\.c
.*error.*\.cpp\.obj
.*error.*\.cxx\.obj
.*error.*\.cc\.obj
-Werror
error\.d
/.*error\S*.d
reassigning to symbol
changes choice state
crosstool_version_check\.cmake
CryptographyDeprecationWarning
Warning: \d+/\d+ app partitions are too small for binary
CMake Deprecation Warning at main/lib/tinyxml2/CMakeLists\.txt:11 \(cmake_policy\)