Compare commits

...

101 Commits

Author SHA1 Message Date
f81ef8229e tests: add transaction time handling 2024-06-06 12:06:02 +02:00
cfa8767c18 fix atomic access to state variables 2024-06-05 15:56:07 +02:00
7a4e37ad82 update documentaion after review 2024-05-27 12:18:21 +02:00
437f2aa292 update as per review notes 2024-05-27 10:35:38 +02:00
d19b603b00 update documentation 2024-05-16 16:14:21 +02:00
f50e220dae add the function to expose the transaction info 2024-05-16 15:36:46 +02:00
17de1dd9dd master add modbus buffer propagation 2024-05-10 17:48:21 +02:00
5e7a26b640 Merge branch 'bugfix/fix_component_upload_issues' into 'master'
fix component upload issues

See merge request idf/esp-modbus!67
2024-04-30 22:39:48 +08:00
01145ec7a2 fix component upload issues
This component is equivalent to version v1.0.14 with minor changes
2024-04-30 22:39:48 +08:00
20c34b20d0 Merge branch 'bugfix/fix_master_broadcast_request_issue' into 'master'
fix master broadcast request issue

Closes IDFGH-12540

See merge request idf/esp-modbus!61
2024-04-30 15:27:36 +08:00
c63a6d7114 fix master broadcast request issue 2024-04-30 15:27:36 +08:00
e26c0c2f5a Merge branch 'feature/doc_extend_serial_port_options' into 'master'
doc: describe on how to override modbus serial comm opts

Closes IDFGH-11906, IDFGH-6341, IDFGH-9713, and IDFGH-12260

See merge request idf/esp-modbus!62
2024-04-29 22:44:08 +08:00
78c809142a doc: describe on how to override modbus serial comm opts 2024-04-29 22:44:08 +08:00
77d0155675 Merge branch 'bugfix/modbus_rtu_skips_first_packet_t35meas_is_disabled' into 'master'
Bugfix/modbus rtu skips first packet t35meas is disabled

Closes IDFGH-12105

See merge request idf/esp-modbus!58
2024-04-29 20:51:21 +08:00
a64720b677 Bugfix/modbus rtu skips first packet t35meas is disabled 2024-04-29 20:51:20 +08:00
0f67f0bb82 Merge branch 'bugfix/fix_ci_issues_log_file' into 'master'
fix ci issues - log file and build time limitation

See merge request idf/esp-modbus!66
2024-04-29 17:14:33 +08:00
b968c36daf fix ci issues - log file and build time limitation 2024-04-29 17:14:32 +08:00
4c98083acc Merge branch 'bugfix/test_fix_ci_issues' into 'master'
text tcp master slave fix ip resolution

See merge request idf/esp-modbus!64
2024-04-23 23:12:18 +08:00
2ee8330b3a text tcp master slave fix ip resolution 2024-04-23 09:58:39 +02:00
8480ff7636 Merge branch 'feature/add_extended_float_support' into 'master'
modbus add extended float/uint support for custom and third party devices

See merge request idf/esp-modbus!59
2024-04-19 17:11:08 +08:00
0cf6bb2c6a modbus add extended float/integer support for custom and third party devices 2024-04-19 17:11:08 +08:00
430217563f Merge branch 'feature/modbus_add_esp32p4_target_support' into 'master'
modbus add esp32p4 target support

See merge request idf/esp-modbus!56
2024-01-26 18:13:02 +08:00
469eb71e07 modbus add esp32p4 target support 2024-01-26 18:13:02 +08:00
94e9664aaa Merge branch 'bugfix/fix_serial_slave_delay_before_send' into 'master'
fix modbus slave wait before send option

Closes IDFGH-11279

See merge request idf/esp-modbus!54
2023-12-22 17:38:19 +08:00
e12c7ff02c fix modbus slave wait before send option 2023-12-22 17:38:18 +08:00
64c5400492 Merge branch 'bugfix/fix_tcp_master_close_sockets' into 'master'
fix for closing tcp connections and watchdog timeout

Closes IDFGH-11539 and IDFGH-11690

See merge request idf/esp-modbus!53
2023-12-22 17:07:33 +08:00
bf271f4286 modbus master tcp port: fix to close sockets correctly, minor additions for the PR
https://github.com/espressif/esp-modbus/pull/44
2023-12-21 00:39:38 +08:00
135a8f8165 fix for closing tcp connections and watchdog timeout
Signed-off-by: aleks <aleks@espressif.com>
2023-12-21 00:39:38 +08:00
f0e78b6d27 Merge branch 'bugfix/fix_incorrect_settings_from_ci_config' into 'master'
fix sdkconfig settings for ci

See merge request idf/esp-modbus!55
2023-12-20 23:56:29 +08:00
a889ada556 fix sdkconfig settings for ci 2023-12-20 12:07:36 +01:00
ea3f06cf2b Merge branch 'bugfix/fix_timer_use_isr_dispatch_method' into 'master'
fix timer use isr dispatch method

Closes IDFGH-11059

See merge request idf/esp-modbus!46
2023-09-30 02:21:04 +08:00
4fe2fe8bc6 fix timer use isr dispatch method 2023-09-30 02:21:04 +08:00
e24ea4e820 Merge branch 'bugfix/fix_coil_start_bit_readout' into 'master'
Modbus coils do not shift the readout by n%8  (merge PR #35)

Closes IDFGH-10998

See merge request idf/esp-modbus!50
2023-09-30 01:48:02 +08:00
8950d1ca1a test coils & discrete reg mapping 2023-09-29 17:04:18 +02:00
a1e96033d8 fix mbc (tcp, serial) master coils callbacks 2023-09-29 17:04:18 +02:00
8dadaac490 Merges https://github.com/espressif/esp-modbus/pull/35
(coils/discrete) do not shift the readout/write by n%8 for RTU and TCP (#1)

Co-authored-by: Mateusz Grzywacz <mateusz.grzywacz@evalan.com>
2023-09-29 17:04:18 +02:00
aa93eecf57 Merge branch 'bugfix/fix_tcp_slave_start_destroy_sequence' into 'master'
modbus tcp master, slave fix start - destroy sequence

See merge request idf/esp-modbus!43
2023-09-29 21:17:36 +08:00
401dbc8f06 modbus tcp master, slave fix start - destroy sequence 2023-09-29 21:17:36 +08:00
567fe19a8b Merge branch 'feature/docs_add_complex_parameter_description' into 'master'
add ascii/bin parameter description into documentation

Closes IDFGH-11120

See merge request idf/esp-modbus!49
2023-09-29 20:12:01 +08:00
3c9106aaf2 add ascii/bin parameter description into documentation 2023-09-29 20:12:01 +08: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
106 changed files with 5910 additions and 1560 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,163 @@ 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 ${SUBDIR}
--recursive
--target all
--default-build-targets ${TEST_TARGETS}
--config "sdkconfig.ci.*=" --build-dir "build_@t_@w"
--build-log build_log.txt
--check-warnings
--ignore-warning-file ../tools/ignore_build_warnings.txt
--collect-size-info $SIZE_INFO_LOCATION
--manifest-rootpath .
--manifest-file .build-test-rules.yml
# delete all other build artifacts, except esp32
- echo "Delete build folders:" $(find . -type d -regex '^\./.*build_esp32[a-z]+[0-9]+[_a-z]*' -print -exec rm -rf {} +)
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
parallel:
matrix:
- SUBDIR: ["serial", "tcp", "generic"]
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 esp32p4"
build_idf_v5.0:
extends: .build_pytest_template
image: espressif/idf:release-v5.0
parallel:
matrix:
- SUBDIR: ["serial", "tcp", "generic"]
variables:
TEST_TARGETS: "esp32 esp32s2 esp32s3 esp32c2 esp32c3"
build_idf_v5.2:
extends: .build_pytest_template
image: espressif/idf:release-v5.2
parallel:
matrix:
- SUBDIR: ["serial", "tcp", "generic"]
variables:
TEST_TARGETS: "esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 esp32h2"
.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 target 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_v5.2, build_idf_v5.0]
parallel:
matrix:
- IDF_BRANCH: ["master", "v5.2", "v5.0"]
IDF_TARGET: ["esp32"]
TEST_PORT: ["serial", "tcp", "generic"]
after_script: []
build_docs:
stage: build
@ -97,7 +204,7 @@ build_docs:
script:
- cd docs
- pip install -r requirements.txt
- build-docs -l en -t esp32
- ./generate_docs
.deploy_docs_template:
stage: deploy
@ -157,5 +264,5 @@ upload_to_component_manager:
script:
- pip install idf-component-manager
- export IDF_COMPONENT_API_TOKEN=${ESP_MODBUS_API_KEY}
- python -m idf_component_manager upload-component --allow-existing --name=esp-modbus --namespace=espressif
- compote component upload --namespace=espressif --name=esp-modbus --allow-existing

View File

@ -53,6 +53,10 @@ list(APPEND priv_include_dirs serial_slave/port serial_slave/modbus_controller
tcp_slave/port tcp_slave/modbus_controller
tcp_master/port tcp_master/modbus_controller)
if(CONFIG_FMB_EXT_TYPE_SUPPORT)
list(APPEND srcs "common/mb_endianness_utils.c")
endif()
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/freemodbus/" ${srcs})
add_prefix(include_dirs "${CMAKE_CURRENT_LIST_DIR}/freemodbus/" ${include_dirs})
add_prefix(priv_include_dirs "${CMAKE_CURRENT_LIST_DIR}/freemodbus/" ${priv_include_dirs})
@ -69,4 +73,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)

36
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.
@ -96,10 +104,19 @@ menu "Modbus configuration"
help
This option defines the number of data bits per ASCII character.
config FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS
int "Wait before send for ASCII communication mode (ms)"
default 0
range 0 1000
depends on FMB_COMM_MODE_ASCII_EN
help
This option defines timeout before slave sends the response in ASCII communication mode.
This allows to work with slow masters. Zero means delay before send is disabled.
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.
@ -204,4 +221,13 @@ menu "Modbus configuration"
This option has dependency with the UART_ISR_IN_IRAM option which places UART interrupt
handler into IRAM to prevent delays related to processing of UART events.
config FMB_EXT_TYPE_SUPPORT
bool "Modbus uses extended types to support third party devices"
default n
help
If this option is set the Modbus stack supports extended list of types
in data dictionary and conversion API to work with the extended types
otherwise the only legacy types are supported. The extended types include
integer, float, double types with different endianness and size.
endmenu

View File

@ -52,6 +52,8 @@ Issue reports and feature requests can be submitted using Github Issues: https:/
Contributions in the form of pull requests should follow ESP-IDF project's [contribution guidelines](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/contribute/index.html). We kindly ask developers to start a discussion on an issue before proposing large changes to the project.
See the beta version of stack v2.0.0_beta introduced [here](https://github.com/espressif/esp-modbus/discussions/45)
## Licence
ESP-Modbus project is based on [FreeMODBUS library](https://github.com/cwalter-at/freemodbus), Copyright (c) 2006 Christian Walter and licensed under the BSD 3-clause license.

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

@ -13,17 +13,20 @@
# http://doxygen.nl/manual/config.html
PROJECT_NAME = "IDF Programming Guide"
PROJECT_NAME = "ESP-MODBUS Programming Guide"
## The 'INPUT' statement below is used as input by script 'gen-df-input.py'
## to automatically generate API reference list files heder_file.inc
## These files are placed in '_inc' directory
## and used to include in API reference documentation
##
INPUT = \
$(PROJECT_PATH)/freemodbus/common/include/esp_modbus_common.h \
$(PROJECT_PATH)/freemodbus/common/include/esp_modbus_slave.h \
$(PROJECT_PATH)/freemodbus/common/include/esp_modbus_master.h \
$(PROJECT_PATH)/freemodbus/common/include/mb_endianness_utils.h
## Get warnings for functions that have no documentation for their parameters or return value
##

37
docs/_static/diag_frame.diag vendored Normal file
View File

@ -0,0 +1,37 @@
# Modbus float_abcd frame structure diagram
blockdiag mb_float_frame {
# global properties
span_width = 2;
span_height = 5;
node_height = 25;
default_fontsize = 16;
default_group_color = lightgrey;
class spacer [shape=none, width=10];
# tuning node properties and connections
0,1,2 [class=spacer];
0; note
1; header
2; response -- uid -- cmd -- len -- fl_abcd -- crc
group float_abcd_packet {
label = "PDU";
color = gray;
shape = line;
style = dashed;
group{uid,resp_uid};group{cmd,resp_cmd};group{len,resp_len};group{crc,resp_crc};
group float_abcd{
color = blue;
shape = line;
style = dashed;
fl_abcd;dt_abcd;
}
}
note[label="1: Unit Identificator, 2: Function code, 3: Data length, 4: Float data array, 5: Checksum",colwidth=6,color=lightyellow,shape=roundedbox]
header[label="FLOAT_ABCD = 0x4640e400 = 12345.0",colwidth=6,color=lightgreen]
response[label="RX:",color=yellow];
uid[label="UID",numbered=1];cmd[label="FC",numbered=2];
len[label="LENGTH",numbered=3];crc[label="CRC",numbered=5];
resp_uid[label="0x01"];resp_cmd[label="0x03"];resp_len[label="0x08"];resp_crc[label="0x9065"];
fl_abcd[label="FLOAT_ABCD",color=lightgreen,numbered=4];
dt_abcd[label="0xE4004640",shape=note];
}

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"}
]
};

61
docs/_static/modbus_frame_examples.diag vendored Normal file
View File

@ -0,0 +1,61 @@
# Modbus frame packaging examples
blockdiag mb_master_frames {
# global properties
span_width = 5;
span_height = 5;
node_height = 25;
default_group_color = lightgrey;
default_fontsize = 15;
# tuning node properties and connections
group 16bit_packets {
label = "16bit frame";
color = red;
shape = line;
style = dashed;
16bit_notes;
}
group 32bit_packets {
label = "32bit frame";
color = green;
shape = line;
style = dashed;
group{32bit_notes};
}
group 64bit_packets {
label = "64bit frame";
color = blue;
shape = line;
style = dashed;
64bit_notes;
}
16bit_notes[label="UINT16, INT16 VALUE = 0x3039 = (uint16_t)12345", width=600, color=orange, shape = roundedbox];
req_u16_hd1[label= "TX:| UID | FC | REG_START | REG_LEN | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=16bit_packets];
req_u16_frm1[label="TX:| 01 | 03 | 00 04 | 00 02 | 85 CA |", color=lightgrey, width=380, colwidth=2,group=16bit_packets];
rsp_u16_hd1[label= "RX:| UID | FC | LEN | UINT16_AB1 | UINT16_AB2 | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=16bit_packets];
rsp_u16_frm1[label="RX:| 01 | 03 | 04 | 30 39 | 30 39 | F1 2C |", color=lightgrey, width=380, colwidth=2,group=16bit_packets];
rsp_u16_hd2[label= "RX:| UID | FC | LEN | UINT16_BA1 | UINT16_BA2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=16bit_packets];
rsp_u16_frm2[label="RX:| 01 | 03 | 04 | 39 30 | 39 30 | E4 E4 |\n", color=lightgrey, width=380, colwidth=2,group=16bit_packets];
32bit_notes[label="(UINT32, INT32) FLOAT32 VALUE = 0x4640e400 = 12345.0", width=600, color=lightgreen, shape = roundedbox];
req_fl_hd1[label= "TX:| UID | FC | REG_START | REG_LEN | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=32bit_packets];
req_fl_frm1[label="TX:| 01 | 03 | 00 XX | 00 04 | C5 CB |", color=lightgrey, width=380, colwidth=2,group=32bit_packets];
rsp_fl_hd1[label= "RX:| UID | FC | LEN | FLOAT_ABCD1 | FLOAT_ABCD2 | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=32bit_packets];
rsp_fl_frm1[label="RX:| 01 | 03 | 08 | E4 00 46 40 | E4 00 46 40 | 90 65 |", color=lightgrey, width=380, colwidth=2,group=32bit_packets];
rsp_fl_hd2[label= "RX:| UID | FC | LEN | FLOAT_CDAB1 | FLOAT_CDAB2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=32bit_packets];
rsp_fl_frm2[label="RX:| 01 | 03 | 08 | 46 40 E4 00 | 46 40 E4 00 | 18 71 |\n", color=lightgrey, width=380, colwidth=2,group=32bit_packets];
rsp_fl_hd3[label= "RX:| UID | FC | LEN | FLOAT_BADC1 | FLOAT_BADC2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=32bit_packets];
rsp_fl_frm3[label="RX:| 01 | 03 | 08 | 00 E4 40 46 | 00 E4 40 46 | 46 D3 |\n", color=lightgrey, width=380, colwidth=2,group=32bit_packets];
rsp_fl_hd4[label= "RX:| UID | FC | LEN | FLOAT_DCAB1 | FLOAT_DCAB2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=32bit_packets];
rsp_fl_frm4[label="RX:| 01 | 03 | 08 | 40 46 00 E4 | 40 46 00 E4 | 32 6B |\n", color=lightgrey, width=380, colwidth=2,group=32bit_packets];
64bit_notes[label="(UINT64, INT64) FLOAT64 VALUE = 0x40c81c8000000000 = 12345.0", width=600, color=lightblue, shape = roundedbox];
req_dbl_hd1[label= "TX:| UID | FC | REG_START | REG_LEN | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=64bit_packets];
req_dbl_frm1[label="TX:| 01 | 03 | 00 28 | 00 08 | C4 04 |", color=lightgrey, width=380, colwidth=2,group=64bit_packets];
rsp_dbl_hd1[label= "RX:| UID | FC | LEN | DOUBLE_ABCDEFGH1 | DOUBLE_ABCDEFGH2 | CRC |", color=lightyellow, width=380, colwidth=2, shape = roundedbox ,group=64bit_packets];
rsp_dbl_frm1[label="RX:| 01 | 03 | 10 | 00 00 00 00 1C 80 40 C8 | 00 00 00 00 1C 80 40 C8 | 9F 4B |", color=lightgrey, width=380, colwidth=2,group=64bit_packets];
rsp_dbl_hd2[label= "RX:| UID | FC | LEN | DOUBLE_HGFEDCBA1 | DOUBLE_HGFEDCBA2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=64bit_packets];
rsp_dbl_frm2[label="RX:| 01 | 03 | 10 | C8 40 80 1C 00 00 00 00 | C8 40 80 1C 00 00 00 00 | DF D3 |\n", color=lightgrey, width=380, colwidth=2,group=64bit_packets];
rsp_dbl_hd3[label= "RX:| UID | FC | LEN | DOUBLE_GHEFCDAB1 | DOUBLE_GHEFCDAB2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=64bit_packets];
rsp_dbl_frm3[label="RX:| 01 | 03 | 10 | 40 C8 1C 80 00 00 00 00 | 40 C8 1C 80 00 00 00 00 | B1 9C |\n", color=lightgrey, width=380, colwidth=2,group=64bit_packets];
rsp_dbl_hd4[label= "RX:| UID | FC | LEN | DOUBLE_BADCFEHG1 | DOUBLE_BADCFEHG2 | CRC |\n ", color=lightyellow, width=380, colwidth=2, shape = roundedbox, group=64bit_packets];
rsp_dbl_frm4[label="RX:| 01 | 03 | 10 | 00 00 00 00 80 1C C8 40 | 00 00 00 00 80 1C C8 40 | 86 94 |\n", color=lightgrey, width=380, colwidth=2,group=64bit_packets];
}

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

View File

@ -70,3 +70,9 @@ API Reference
.. include-build-file:: inc/esp_modbus_master.inc
.. include-build-file:: inc/esp_modbus_slave.inc
.. _modbus_api_endianness_conversion:
Modbus Endianness Conversion API Reference
------------------------------------------
.. include-build-file:: inc/mb_endianness_utils.inc

View File

@ -11,4 +11,4 @@ The Modbus is a data communications protocol originally published by Modicon (no
Modbus Port Initialization <port_initialization>
Modbus Master API <master_api_overview>
Modbus Slave API <slave_api_overview>
Applications and References <applications_and_references>
Applications and References <applications_and_references>

View File

@ -9,17 +9,15 @@ The following overview describes how to setup Modbus master communication. The o
2. :ref:`modbus_api_master_configure_descriptor` - Configure data descriptors to access slave parameters.
3. :ref:`modbus_api_master_setup_communication_options` - Allows to setup communication options for selected port.
4. :ref:`modbus_api_master_start_communication` - Start stack and sending / receiving data.
5. :ref:`modbus_api_master_destroy` - Destroy Modbus controller and its resources.
5. :ref:`modbus_api_master_expose_information` - Expose extra information from stack.
6. :ref:`modbus_api_master_destroy` - Destroy Modbus controller and its resources.
.. _modbus_api_master_configure_descriptor:
Configuring Master Data Access
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The architectural approach of ESP_Modbus includes one level above standard Modbus IO driver.
The additional layer is called Modbus controller and its goal is to add an abstraction such as CID - characteristic identifier.
The CID is linked to a corresponding Modbus registers through the table called Data Dictionary and represents device physical parameter (such as temperature, humidity, etc.) in specific Modbus slave device.
This approach allows the upper layer (e.g., MESH or MQTT) to be isolated from Modbus specifics thus simplify Modbus integration with other protocols/networks.
The architectural approach of ESP_Modbus includes one level above standard Modbus IO driver. The additional layer is called Modbus controller and its goal is to add an abstraction such as CID - characteristic identifier. The CID is linked to a corresponding Modbus registers through the table called Data Dictionary and represents device physical parameter (such as temperature, humidity, etc.) in specific Modbus slave device. This approach allows the upper layer (e.g., MESH or MQTT) to be isolated from Modbus specifics thus simplify Modbus integration with other protocols/networks.
The Data Dictionary is the list in the Modbus master which shall be defined by user to link each CID to its corresponding Modbus registers representation using Register Mapping table of the Modbus slave being used.
Each element in this data dictionary is of type :cpp:type:`mb_parameter_descriptor_t` and represents the description of one physical characteristic:
@ -52,20 +50,17 @@ Each element in this data dictionary is of type :cpp:type:`mb_parameter_descript
- Relative register address of the characteristic in the register area.
* - ``mb_size``
- Modbus Register Size
- Length of characteristic in registers.
- Length of characteristic in registers (two bytes).
* - ``param_offset``
- Instance Offset
- Offset to instance of the characteristic in bytes. It is used to calculate the absolute address to the characteristic in the storage structure.
It is optional field and can be set to zero if the parameter is not used in the application.
* - ``param_type``
- Data Type
- Specifies type of the characteristic.
:cpp:enumerator:`PARAM_TYPE_U8`, :cpp:enumerator:`PARAM_TYPE_U16`, :cpp:enumerator:`PARAM_TYPE_U32` - Unsigned integer 8/16/32 bit type;
:cpp:enumerator:`PARAM_TYPE_FLOAT` - IEEE754 floating point format;
:cpp:enumerator:`PARAM_TYPE_ASCII` - ASCII string or binary data;
- Data Type
- Specifies type of the characteristic. Possible types are described in the section :ref:`modbus_mapping_complex_data_types`.
* - ``param_size``
- Data Size
- The storage size of the characteristic (bytes).
- The storage size of the characteristic (in bytes) describes the size of data to keep into data instance during mapping. For the :ref:`modbus_mapping_complex_data_types` this allows to define the data container of the corresponded type.
* - ``param_opts``
- Parameter Options
- Limits, options of characteristic used during processing of alarm in user application (optional)
@ -76,6 +71,13 @@ Each element in this data dictionary is of type :cpp:type:`mb_parameter_descript
.. note:: The ``cid`` and ``param_key`` have to be unique. Please use the prefix to the parameter key if you have several similar parameters in your register map table.
Examples Of Mapping
@@@@@@@@@@@@@@@@@@@
Please refer to section :ref:`modbus_mapping_complex_data_types` for more information about used data types.
Example 1: Configure access to legacy parameter types is described below.
.. list-table:: Table 2 Example Register mapping table of Modbus slave
:widths: 5 5 2 10 5 5 68
:header-rows: 1
@ -108,7 +110,14 @@ Each element in this data dictionary is of type :cpp:type:`mb_parameter_descript
- FLOAT
- DegC
- Room temperature in DegC. Writing a temperature value to this register for single point calibration.
* - 3
- 40002
- 16
- 1..100 bytes
- ASCII or binary array
- Not defined
- Device name (16 bytes) ASCII string. The type of `PARAM_TYPE_ASCII` allows to read/write complex parameter (string or binary data) that corresponds to one CID.
.. code:: c
// Enumeration of modbus slave addresses accessed by master device
@ -122,9 +131,11 @@ Each element in this data dictionary is of type :cpp:type:`mb_parameter_descript
enum {
CID_SER_NUM1 = 0,
CID_SW_VER1,
CID_DEV_NAME1,
CID_TEMP_DATA_1,
CID_SER_NUM2,
CID_SW_VER2,
CID_DEV_NAME2,
CID_TEMP_DATA_2
};
@ -136,27 +147,161 @@ Each element in this data dictionary is of type :cpp:type:`mb_parameter_descript
0, PARAM_TYPE_U32, 4, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_SW_VER1, STR("Software_version_1"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 1,
0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_DEV_NAME1, STR("Device name"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 8,
0, PARAM_TYPE_ASCII, 16, OPTS( 0, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_TEMP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
0, PARAM_TYPE_FLOAT, 4, OPTS( 16, 30, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_SER_NUM2, STR("Serial_number_2"), STR("--"), MB_DEVICE_ADDR2, MB_PARAM_INPUT, 0, 2,
0, PARAM_TYPE_U32, 4, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_SW_VER2, STR("Software_version_2"), STR("--"), MB_DEVICE_ADDR2, MB_PARAM_INPUT, 2, 1,
0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_DEV_NAME2, STR("Device name"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 8,
0, PARAM_TYPE_ASCII, 16, OPTS( 0, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_TEMP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 0, 2,
0, PARAM_TYPE_FLOAT, 4, OPTS( 20, 30, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
};
// Calculate number of parameters in the table
uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0]));
Example 2: Configure access using extended parameter types for third-party devices.
.. list-table:: Table 3 Example Register mapping table of Modbus slave
:widths: 2 4 2 10 3 68
:header-rows: 1
* - CID
- Register
- Length
- Range
- Units
- Description
* - 0
- 40000
- 4
- 0 ... 255
- No units
- :cpp:enumerator:`PARAM_TYPE_U8_A` - unsigned integer 8-bit
* - 1
- 40002
- 4
- 0 ... 65535
- No Units
- :cpp:enumerator:`PARAM_TYPE_U16_AB` uinsigned integer 16-bit
* - 3
- 40004
- 8
- 0 ... Unsigned integer 32-bit range
- No units
- :cpp:enumerator:`PARAM_TYPE_U32_ABCD` - unsigned integer 32-bit in ABCD format
* - 4
- 40008
- 8
- 0 ... Unsigned integer 32-bit range
- No units
- :cpp:enumerator:`PARAM_TYPE_FLOAT_CDAB` - FLOAT 32-bit value in CDAB format
* - 5
- 400012
- 16
- 0 ... Unsigned integer 64-bit range
- No units
- :cpp:enumerator:`PARAM_TYPE_U64_ABCDEFGH` - Unsigned integer 64-bit value in ABCDEFGH format
* - 6
- 400020
- 16
- 0 ... Unsigned integer 64-bit range
- No units
- :cpp:enumerator:`PARAM_TYPE_DOUBLE_HGFEDCBA` - Double precision 64-bit value in HGFEDCBA format
.. code:: c
#include "limits.h"
#include "mbcontroller.h"
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
#define HOLD_REG_START(field) (HOLD_OFFSET(field) >> 1)
#define HOLD_REG_SIZE(field) (sizeof(((holding_reg_params_t *)0)->field) >> 1)
#pragma pack(push, 1)
// Example structure that contains parameter arrays of different types
// with different options of endianness.
typedef struct
{
uint16_t holding_u8_a[2];
uint16_t holding_u16_ab[2];
uint32_t holding_uint32_abcd[2];
float holding_float_cdab[2];
double holding_uint64_abcdefgh[2];
double holding_double_hgfedcba[2];
} holding_reg_params_t;
#pragma pack(pop)
// Enumeration of modbus slave addresses accessed by master device
enum {
MB_DEVICE_ADDR1 = 1, // Short address of Modbus slave device
MB_SLAVE_COUNT
};
// Enumeration of all supported CIDs for device (used in parameter definition table)
enum {
CID_HOLD_U8_A = 0,
CID_HOLD_U16_AB,
CID_HOLD_UINT32_ABCD,
CID_HOLD_FLOAT_CDAB,
CID_HOLD_UINT64_ABCDEFGH,
CID_HOLD_DOUBLE_HGFEDCBA,
CID_COUNT
};
// Example Data Dictionary for to address parameters from slaves with different options of endianness
mb_parameter_descriptor_t device_parameters[] = {
// CID, Name, Units, Modbus addr, register type, Modbus Reg Start Addr, Modbus Reg read length,
// Instance offset (NA), Instance type, Instance length (bytes), Options (NA), Permissions
{ CID_HOLD_U8_A, STR("U8_A"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_u8_a), HOLD_REG_SIZE(holding_u8_a),
HOLD_OFFSET(holding_u8_a), PARAM_TYPE_U8_A, (HOLD_REG_SIZE(holding_u8_a) << 1),
OPTS( 0, UCHAR_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U16_AB, STR("U16_AB"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_u16_ab), HOLD_REG_SIZE(holding_u16_ab),
HOLD_OFFSET(holding_u16_ab), PARAM_TYPE_U16_AB, (HOLD_REG_SIZE(holding_u16_ab) << 1),
OPTS( 0, USHRT_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_ABCD, STR("UINT32_ABCD"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_uint32_abcd), HOLD_REG_SIZE(holding_uint32_abcd),
HOLD_OFFSET(holding_uint32_abcd), PARAM_TYPE_U32_ABCD, (HOLD_REG_SIZE(holding_uint32_abcd) << 1),
OPTS( 0, ULONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_CDAB, STR("FLOAT_CDAB"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_float_cdab), HOLD_REG_SIZE(holding_float_cdab),
HOLD_OFFSET(holding_float_cdab), PARAM_TYPE_FLOAT_CDAB, (HOLD_REG_SIZE(holding_float_cdab) << 1),
OPTS( 0, ULONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT64_ABCDEFGH, STR("UINT64_ABCDEFGH"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_uint64_abcdefgh), HOLD_REG_SIZE(holding_uint64_abcdefgh),
HOLD_OFFSET(holding_uint64_abcdefgh), PARAM_TYPE_UINT64_ABCDEFGH, (HOLD_REG_SIZE(holding_uint64_abcdefgh) << 1),
OPTS( 0, ULLONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_HGFEDCBA, STR("DOUBLE_HGFEDCBA"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
HOLD_REG_START(holding_double_hgfedcba), HOLD_REG_SIZE(holding_double_hgfedcba),
HOLD_OFFSET(holding_double_hgfedcba), PARAM_TYPE_DOUBLE_HGFEDCBA, (HOLD_REG_SIZE(holding_double_hgfedcba) << 1),
OPTS( 0, ULLONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }
};
uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0]));
The example above describes the definition of just several extended types. The types described in the :ref:`modbus_mapping_complex_data_types` allow to address the most useful value formats from devices of known third-party vendors.
Once the type of characteristic is defined in data dictionary the stack is responsible for conversion of values to/from the corresponding type option into the format recognizable by compiler.
.. note:: Please refer to your vendor device manual and its mapping table to select the types suitable for your device.
The Modbus stack contains also the :ref:`modbus_api_endianness_conversion` - endianness conversion API functions that allow to convert values from/to each extended type into compiler representation.
During initialization of the Modbus stack, a pointer to the Data Dictionary (called descriptor) must be provided as the parameter of the function below.
:cpp:func:`mbc_master_set_descriptor`: Initialization of master descriptor.
Initialization of master descriptor. The descriptor represents an array of type :cpp:type:`mb_parameter_descriptor_t` and describes all the characteristics accessed by master.
.. code:: c
ESP_ERROR_CHECK(mbc_master_set_descriptor(&device_parameters[0], num_device_parameters));
The Data Dictionary can be initialized from SD card, MQTT or other source before start of stack. Once the initialization and setup is done, the Modbus controller allows the reading of complex parameters from any slave included in descriptor table using its CID.
Refer to :ref:`example TCP master <example_mb_tcp_master>`, :ref:`example Serial master <example_mb_master>` for more information.
.. _modbus_api_master_setup_communication_options:
@ -182,6 +327,14 @@ Example setup for serial port:
ESP_ERROR_CHECK(mbc_master_setup((void*)&comm_info));
The communication options supported by this library are described in the section :ref:`modbus_supported_communication_options`.
However, it is possible to override the serial communication options calling the function :cpp:func:`uart_param_config` right after :cpp:func:`mbc_slave_setup`.
.. note:: Refer to `UART driver documentation <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html#set-communication-parameters>`__ for more information about UART peripheral configuration.
.. note:: RS485 communication requires call to UART specific APIs to setup communication mode and pins. Refer to the `UART communication section <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html#uart-api-running-uart-communication>`__ in documentation.
Modbus master TCP port requires additional definition of IP address table where number of addresses should be equal to number of unique slave addresses in master Modbus Data Dictionary:
The order of IP address string corresponds to short slave address in the Data Dictionary.
@ -211,9 +364,6 @@ The order of IP address string corresponds to short slave address in the Data Di
The slave IP addresses in the table can be assigned automatically using mDNS service as described in the example.
Refer to :ref:`example TCP master <example_mb_tcp_master>` for more information.
.. note:: RS485 communication requires call to UART specific APIs to setup communication mode and pins. Refer to the `UART communication section <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html#uart-api-running-uart-communication>`__ in documentation.
.. _modbus_api_master_start_communication:
Master Communication
@ -252,7 +402,7 @@ Example:
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
err = mbc_master_get_parameter(param_descriptor->cid, (char*)param_descriptor->param_key, (uint8_t*)temp_data, &type);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%08x) read successful.",
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx32 ") read successful.",
param_descriptor->cid,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
@ -268,7 +418,6 @@ Example:
ESP_LOGE(TAG, "Could not get information for characteristic %d.", cid);
}
:cpp:func:`mbc_master_set_parameter`
The function writes characteristic's value defined as a name and cid parameter in corresponded slave device. The additional data for parameter request is taken from master parameter description table.
@ -285,6 +434,109 @@ The function writes characteristic's value defined as a name and cid parameter i
ESP_LOGE(TAG, "Set data fail, err = 0x%x (%s).", (int)err, (char*)esp_err_to_name(err));
}
.. _modbus_api_master_expose_information:
Expose Extra Information
^^^^^^^^^^^^^^^^^^^^^^^^
In case the does not clarify some information, such as slave exception code returned in the response, the functions below can be useful.
:cpp:func:`mbc_master_get_transaction_info`
Allows to return the below information as a :cpp:type:`mb_trans_info_t` structure.
.. list-table:: Table 4 Transaction extended information
:widths: 2 68
:header-rows: 1
* - Field
- Description
* - uint64_t ``trans_id``
- The unique transaction identificator stored as uint64_t timestamp.
* - uint8_t ``dest_addr``
- Destination short address (or UID - Unit Identificator) of the slave being accessed.
* - uint8_t ``func_code``
- The last transaction function code.
* - uint8_t ``exception``
- The last transaction exception code returned by slave. :cpp:type:`eMBException`.
* - uint16_t ``err_type``
- The last transaction error type.
:cpp:enumerator:`EV_ERROR_INIT` = 0, No error, initial state or the request is in progress.
:cpp:enumerator:`EV_ERROR_RESPOND_TIMEOUT` = 1, Slave respond timeout. No response during response timeout.
:cpp:enumerator:`EV_ERROR_RECEIVE_DATA` = 2, Receive frame data error.
:cpp:enumerator:`EV_ERROR_EXECUTE_FUNCTION` = 3, Execute function error. Function is not supported or slave returned an error.
:cpp:enumerator:`EV_ERROR_OK` = 4, No error, processing completed successfully.
.. warning:: The functionality described in this section is for advanced users and should to be handled correctly.
.. note:: The above function returns the latest transaction information which may not be actual if another IO call is performed from higher priority task right before the :cpp:func:`mbc_master_get_transaction_info`. In this case the ``trans_id`` field can clarify if the returned information is obsolete. The transaction ID is just a timestamp of type `uint64_t` returned by function `esp_timer_get_time()`. In this case it is possible determining if the information retrieved corresponds to the actual request using timestamp kept before the IO call and transaction identificator.
.. code:: c
#define MAX_TRANSACTION_TOUT_US 640000
uint64_t start_timestamp = esp_timer_get_time(); // Get current timestamp in microseconds
esp_err_t err = mbc_master_get_parameter(param_descriptor->cid, (char*)param_descriptor->param_key, (uint8_t*)temp_data, &type);
mb_trans_info_t tinfo = {0};
if (mbc_master_get_transaction_info(&tinfo) == ESP_OK) {
ESP_LOGI("TRANSACTION_INFO", "Id: %" PRIu64 ", Addr: %x, FC: %x, Exp: %u, Err: %x",
(uint64_t)tinfo.trans_id, (int)tinfo.dest_addr,
(unsigned)tinfo.func_code, (unsigned)tinfo.exception,
(int)tinfo.err_type);
}
if (tinfo.trans_id >= (start_timestamp + MAX_TRANSACTION_TOUT_US)) {
ESP_LOGI("TRANSACTION_INFO", "Transaction Id: %" PRIu64 " is expired", tinfo.trans_id);
}
Below is the way to expose the transaction information and request/response buffers defining the user error handling function. This funcion defined as described in the code below will be executed from internal final state machine before returning from blocking :cpp:func:`mbc_master_set_parameter` or :cpp:func:`mbc_master_get_parameter` functions and expose the internal parameters.
.. code:: c
#define MB_PDU_DATA_OFF 1
#define EV_ERROR_EXECUTE_FUNCTION 3
void vMBMasterErrorCBUserHandler( uint64_t trans_id, uint16_t err_type, uint8_t dest_addr, const uint8_t *precv_buf, uint16_t recv_length,
const uint8_t *psent_buf, uint16_t sent_length )
{
ESP_LOGW("USER_ERR_CB", "The transaction %" PRIu64 ", error type: %u", trans_id, err_type);
if ((err_type == EV_ERROR_EXECUTE_FUNCTION) && precv_buf && recv_length) {
ESP_LOGW("USER_ERR_CB", "The command is unsupported or an exception on slave happened: %x", (int)precv_buf[MB_PDU_DATA_OFF]);
}
if (precv_buf && recv_length) {
ESP_LOG_BUFFER_HEX_LEVEL("Received buffer", (void *)precv_buf, (uint16_t)recv_length, ESP_LOG_WARN);
}
if (psent_buf && sent_length) {
ESP_LOG_BUFFER_HEX_LEVEL("Sent buffer", (void *)psent_buf, (uint16_t)sent_length, ESP_LOG_WARN);
}
}
.. list-table:: Table 5 Transaction user handler parameters
:widths: 2 68
:header-rows: 1
* - Field
- Description
* - uint64_t ``trans_id``;
- The unique transaction identificator stored as uint64_t timestamp.
* - uint16_t ``err_type``;
- The last transaction error type.
* - uint8_t ``dest_addr``;
- Destination short address (or UID - Unit Identificator) of the slave being accessed.
* - ``precv_buf``;
- The last transaction internal receive buffer pointer that points to the Modbus PDU frame. NULL - not actual.
* - ``recv_length``;
- The last transaction receive buffer length.
* - ``psent_buf``;
- The last transaction internal sent buffer pointer that points to the Modbus PDU frame. NULL - not actual.
* - ``sent_length``;
- The last transaction sent buffer length.
The user handler function can be useful to check the Modbus frame buffers and expose some information right before returning from the call :cpp:func:`mbc_master_set_parameter` or :cpp:func:`mbc_master_get_parameter` functions.
.. warning:: The above handler function may prevent the Modbus FSM to work properly! The body of the handler needs to be as short as possible and contain just simple functionality that will not block processing for relatively long time. This is user software responcibility to not break the Modbus functionality using the function.
.. _modbus_api_master_destroy:

View File

@ -12,6 +12,39 @@ The Modbus serial communication protocol is de facto standard protocol widely us
.. note:: This documentation (and included code snippets) requires some familiarity with the Modbus protocol. Refer to the Modbus Organization's with protocol specifications for specifics :ref:`modbus_organization`.
.. _modbus_supported_communication_options:
Modbus Supported Communication Options
--------------------------------------
The Modbus library supports the standard communication options as per Modbus specification stated below.
.. list-table:: Standard Modbus communication options
:widths: 10 90
:header-rows: 1
* - Modbus option
- Description of the option
* - RTU communication
- * 1 start bit
* 8 data bits, least significant bit sent first
* 1 bit for even / odd parity-no bit for no parity
* 1 stop bit if parity is used, 2 stop bits if no parity
* Cyclical Redundancy Check (CRC)
* - ASCII communication
- * 1 start bit
* 7-8 data bits, least significant bit sent first
* 1 bit for even / odd parity-no bit for no parity
* 1 stop bit if parity is used, 2 stop bits if no parity
* Longitudinal Redundancy Check (LRC)
* - TCP communication
- * Communications between client (master) - server (slave) over TCP/IP networks
* Connection uses the standard port 502
* The frames do not require checksum calculation (provided by lower layers)
Some vendors may use subset of communication options. In this case the detailed information is clarified in the device manual and it is possible to override the standard communication options for support of such devices.
Please refer to :ref:`modbus_api_slave_setup_communication_options`, :ref:`modbus_api_master_setup_communication_options` for more information.
Messaging Model And Data Mapping
--------------------------------
@ -39,9 +72,212 @@ The Modbus protocol allows devices to map data to four types of registers (Holdi
Modbus data mapping
.. _modbus_mapping_complex_data_types:
Mapping Of Complex Data Types
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
As per section 4.2 of Modbus specification, "MODBUS uses a ``big-Endian`` representation for addresses and data items. This means that when a numerical quantity larger than a single byte is transmitted, the most significant byte is sent first". The biggest official structure defined by the Modbus specification is a 16-bit word register, which is 2 bytes. However, vendors sometimes group two or even four 16-bit registers together to be interpretted as 32-bit or 64-bit values, respectively. It is also possible when the Modbus vendors group many registers together for serial numbers, text strings, time/date, etc. Regardless of how the vendor intends the data to be interpreted, the Modbus protocol itself simply transfers 16-bit word registers. These values grouped from registers may use either little-endian or big-endian register order.
.. note:: Each individual 16-bit register, is encoded in big-endian order (assuming the Modbus device abides by the Modbus specification). However, the 32-bit and 64-bit types naming conventions like ABCD or ABCDEFGH, does not take into account the network format byte order of frame. For example: the ABCD prefix for 32-bit values means the common Modbus mapping format and corresponds to the CDAB on network format (order in the frame).
Common Data Types Supported By Modbus Vendors
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.. list-table:: Table 1 basic types used by Modbus vendors
:widths: 8 3 20
:header-rows: 1
* - Type
- Range
- Format description
* - U8, I8 - Unsigned/Signed 8-bit type
- (0 .. 255)/(-128 .. 127)
- Common unsigned 8-bit type that is stored usually in one Modbus register. The value can be stored in HI or LO byte of the register or packed with the next byte into one 16 - bit register.
* - U16 - Unsigned integer 16-bit type
- 0 - 65535
- Stored in one 16-bit register. The values can be stored with AB or BA endianness.
* - I16 - Signed integer 16-bit type
- -32768 to 32767 is allowed.
- Stored in one 16-bit register. The values can be stored with AB or BA forendiannessmat.
* - I32 - Signed long integer 32-bit type
- -2147483648 to 2147483647 is allowed.
- Stored in two consecutive 16-bit register. The values can be stored with ABCD - DCBA endianness (see below).
* - U32 - Unsigned long integer 32-bit type
- 0 to 4294967295 is allowed.
- Stored in two consecutive 16-bit register. The values can be stored with ABCD - DCBA endianness.
* - U64 Unsigned Long long integers (Unsigned integer 64)
- 0 to 18446744073709551615 is allowed.
- Stored in four consecutive 16-bit register. The values can be stored with ABCDEFGH - BADCFEHG endianness.
* - I64 Signed Long long integers (Signed integer 64)
- -9223372036854775808 to 9223372036854775807 is allowed.
- Stored in four consecutive 16-bit register. The values can be stored with ABCDEFGH - BADCFEHG endianness.
* - Floating point single precision 32-bit
- 1.17549435E-38 to 3.40282347E+38 is allowed.
- Stored in two consecutive 16-bit register per IEEE754. The values can be stored with ABCD - DCBA endianness.
* - Floating point double precision 64-bit
- +/-5.0E-324 to +/-1.7E+308 is allowed.
- Stored in four consecutive 16-bit register per IEEE754. The values can be stored with ABCDEFGH - BADCFEHG endianness.
As showed in the table above the float and double types do not fit to the 16-bit register and reguire several consecutive registers be used to store the value. However, different manufacturers store the consecutive bytes in different order (not standardized). For example: The DCBA prefix means inversed Modbus format (BADC order on network format).
.. list-table:: Table 2 Modbus byte order for extended types
:widths: 3 28
:header-rows: 1
* - Postfix
- Format description
* - ABCD
- Big endian, high order byte first
* - CDAB
- Big endian, reversed register order (Little endian with byte swap)
* - BADC
- Little endian, reversed register order (Big endian with byte swap)
* - DCBA
- Little endian (Low order byte first)
The extended data types are used to define all possible combinations of groupped values are represented below and correspond to ``param_type`` field of the data dictionary as described in the table below:
.. list-table:: Table 3 Modbus extended data types of characteristics
:widths: 6 28 10
:header-rows: 1
* - Type
- Format type description (common format)
- Format type (network format)
* - :cpp:enumerator:`PARAM_TYPE_U8`
- compatibility type corresponds to :cpp:enumerator:`PARAM_TYPE_U8_A`
- Unsigned integer 8 bit type
* - :cpp:enumerator:`PARAM_TYPE_U16`
- Unsigned integer 16 bit type, corresponds to :cpp:enumerator:`PARAM_TYPE_U16_AB`
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_U32`
- Default unsigned integer 32 bit type, corresponds to :cpp:enumerator:`PARAM_TYPE_U32_ABCD`
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_FLOAT`
- Default unsigned integer 32 bit type, corresponds to :cpp:enumerator:`PARAM_TYPE_FLOAT_ABCD`
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_ASCII`
- Default ASCII string format
- Packed ASCII string data
* - :cpp:enumerator:`PARAM_TYPE_BIN`
- Binary data type
- Default type for binary packed data
* - :cpp:enumerator:`PARAM_TYPE_I8_A`
- I8 signed integer in low byte of register, high byte is zero
- I8 signed integer LO
* - :cpp:enumerator:`PARAM_TYPE_I8_B`
- I8 signed integer in high byte of register, low byte is zero
- I8 signed integer HI
* - :cpp:enumerator:`PARAM_TYPE_U8_A`
- U8 unsigned integer written to low byte of register, high byte is zero
- U8 unsigned integer LO
* - :cpp:enumerator:`PARAM_TYPE_U8_B`
- U8 unsigned integer written to hi byte of register, low byte is zero
- U8 unsigned integer HI
* - :cpp:enumerator:`PARAM_TYPE_I16_AB`
- I16 signed integer, big endian
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_I16_BA`
- I16 signed integer, little endian
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_U16_AB`
- U16 unsigned integer, big endian
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_U16_BA`
- U16 unsigned integer, little endian
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_I32_ABCD`
- I32 ABCD signed integer, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_I32_CDAB`
- I32 CDAB signed integer, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_I32_BADC`
- I32 BADC signed integer, little endian, reversed register order
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_I32_DCBA`
- I32 DCBA signed integer, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_U32_ABCD`
- U32 ABCD unsigned integer, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_U32_CDAB`
- U32 CDAB unsigned integer, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_U32_BADC`
- U32 BADC unsigned integer, little endian, reversed register order
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_U32_DCBA`
- U32 DCBA unsigned integer, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_FLOAT_ABCD`
- Float ABCD floating point, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_FLOAT_CDAB`
- Float CDAB floating point, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_FLOAT_BADC`
- Float BADC floating point, little endian, reversed register order
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_FLOAT_DCBA`
- Float DCBA floating point, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_I64_ABCDEFGH`
- I64, ABCDEFGH signed integer, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_I64_HGFEDCBA`
- I64, HGFEDCBA signed integer, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_I64_GHEFCDAB`
- I64, GHEFCDAB signed integer, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_I64_BADCFEHG`
- I64, BADCFEHG signed integer, little endian, reversed register order
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_U64_ABCDEFGH`
- U64, ABCDEFGH unsigned integer, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_U64_HGFEDCBA`
- U64, HGFEDCBA unsigned integer, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_U64_GHEFCDAB`
- U64, GHEFCDAB unsigned integer, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_U64_BADCFEHG`
- U64, BADCFEHG unsigned integer, little endian, reversed register order
- Little endian
* - :cpp:enumerator:`PARAM_TYPE_DOUBLE_ABCDEFGH`
- Double ABCDEFGH floating point, big endian
- Little endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_DOUBLE_HGFEDCBA`
- Double HGFEDCBA floating point, little endian
- Big endian byte swap
* - :cpp:enumerator:`PARAM_TYPE_DOUBLE_GHEFCDAB`
- Double GHEFCDAB floating point, big endian, reversed register order
- Big endian
* - :cpp:enumerator:`PARAM_TYPE_DOUBLE_BADCFEHG`
- Double BADCFEHG floating point, little endian, reversed register order
- Little endian
.. note:: The support for the extended data types should be enabled using the option ``CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND`` in kconfig menu.
The below diagrams show how the extended data types appear on network layer.
.. blockdiag:: /../_static/diag_frame.diag
:scale: 80%
:caption: Modbus master response with ABCD frame
:align: center
.. blockdiag:: /../_static/modbus_frame_examples.diag
:scale: 80%
:caption: Modbus frame packaging examples (16-bit, 32-bit, 64-bit data)
:align: center
The approach showed above can be used to pack the data into MBAP frames used by Modbus TCP as well as for other types with similar size.
The following sections give an overview of how to use the ESP_Modbus component found under `components/freemodbus`. The sections cover initialization of a Modbus port, and the setup a master or slave device accordingly:
- :ref:`modbus_api_port_initialization`
- :ref:`modbus_api_slave_overview`
- :ref:`modbus_api_master_overview`

View File

@ -32,4 +32,4 @@ This example code to initialize slave port:
if (slave_handler == NULL || err != ESP_OK) {
// Error handling is performed here
ESP_LOGE(TAG, "mb controller initialization fail.");
}
}

View File

@ -77,6 +77,31 @@ Direct access to register area from user application must be protected by critic
holding_reg_area[2] += 10;
portEXIT_CRITICAL(&param_lock);
The stack supports the extended data types when enabled through the the option ``CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND`` in kconfig menu.
In this case the mapped data values can be initialized to specific format using :ref:`modbus_api_endianness_conversion`.
Please refer to secton :ref:`modbus_mapping_complex_data_types` for more information about data types.
Example initialization of mapped values:
.. code:: c
#include "mbcontroller.h" // for mbcontroller defines and api
val_32_arr holding_float_abcd[2] = {0};
val_64_arr holding_double_ghefcdab[2] = {0};
...
// set the Modbus parameter to specific format
portENTER_CRITICAL(&param_lock); // critical section is required if the stack is active
mb_set_float_abcd(&holding_float_abcd[0], (float)12345.0);
mb_set_float_abcd(&holding_float_abcd[1], (float)12345.0);
mb_set_double_ghefcdab(&holding_double_ghefcdab[0], (double)12345.0);
portEXIT_CRITICAL(&param_lock);
...
// The actual abcd formatted value can be converted to actual float represenatation as below
ESP_LOGI("TEST", "Test value abcd: %f", mb_get_float_abcd(&holding_float_abcd[0]));
ESP_LOGI("TEST", "Test value abcd: %f", mb_get_float_abcd(&holding_float_abcd[1]));
ESP_LOGI("TEST", "Test value ghefcdab: %lf", mb_get_double_ghefcdab(&holding_double_ghefcdab[0]));
...
.. _modbus_api_slave_setup_communication_options:
@ -127,6 +152,14 @@ Example initialization of Modbus serial communication:
ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info));
The communication options supported by this library are described in the section :ref:`modbus_supported_communication_options`.
However, it is possible to override the serial communication options calling the function :cpp:func:`uart_param_config` right after :cpp:func:`mbc_slave_setup`.
.. note:: Refer to `UART driver documentation <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html#set-communication-parameters>`__ for more information about UART peripheral configuration.
.. note:: RS485 communication requires call to UART specific APIs to setup communication mode and pins. Refer to the `UART communication section <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html#uart-api-running-uart-communication>`__ in documentation.
.. _modbus_api_slave_communication:
Slave Communication
@ -176,14 +209,14 @@ Example to get event when holding or input registers accessed in the slave:
....
// The function blocks while waiting for register access
mb_event_group_t event = mbc_slave_check_event(MB_READ_WRITE_MASK);
(void)mbc_slave_check_event(MB_READ_WRITE_MASK);
// Get information about data accessed from master
ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
const char* rw_str = (event & MB_READ_MASK) ? "READ" : "WRITE";
const char* rw_str = (reg_info.type & MB_READ_MASK) ? "READ" : "WRITE";
// Filter events and process them accordingly
if (event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
if (reg_info.type & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
ESP_LOGI(TAG, "HOLDING %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
rw_str,
(uint32_t)reg_info.time_stamp,
@ -191,7 +224,7 @@ Example to get event when holding or input registers accessed in the slave:
(uint32_t)reg_info.type,
(uint32_t)reg_info.address,
(uint32_t)reg_info.size);
} else if (event & (MB_EVENT_INPUT_REG_RD)) {
} else if (reg_info.type & (MB_EVENT_INPUT_REG_RD)) {
ESP_LOGI(TAG, "INPUT %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
rw_str,
(uint32_t)reg_info.time_stamp,

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

@ -1 +1 @@
esp-docs==0.2.4
esp-docs>=1.8,<2.0

View File

@ -4,10 +4,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h" // for esp_err_t
#include "mbc_master.h" // for master interface define
#include "esp_modbus_master.h" // for public interface defines
#include "esp_err.h" // for esp_err_t
#include "mbc_master.h" // for master interface define
#include "esp_modbus_master.h" // for public interface defines
#include "esp_modbus_callbacks.h" // for callback functions
#include "sdkconfig.h"
static const char TAG[] __attribute__((unused)) = "MB_CONTROLLER_MASTER";
@ -36,7 +37,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 +54,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 +74,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 +94,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 +115,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 +135,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 +155,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,12 +175,12 @@ 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;
}
eMBErrorCode eMBMasterRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete)
USHORT usNDiscrete)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
@ -193,7 +194,7 @@ eMBErrorCode eMBMasterRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
}
eMBErrorCode eMBMasterRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
@ -208,7 +209,7 @@ eMBErrorCode eMBMasterRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
}
eMBErrorCode eMBMasterRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
@ -223,7 +224,7 @@ eMBErrorCode eMBMasterRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
}
eMBErrorCode eMBMasterRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs)
USHORT usNRegs)
{
eMBErrorCode error = MB_ENOERR;
MB_MASTER_CHECK((master_interface_ptr != NULL),
@ -235,3 +236,297 @@ eMBErrorCode eMBMasterRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
error = master_interface_ptr->master_reg_cb_input(pucRegBuffer, usAddress, usNRegs);
return error;
}
/**
* Helper function to get current transaction info
*/
esp_err_t mbc_master_get_transaction_info(mb_trans_info_t *ptinfo)
{
MB_MASTER_CHECK((ptinfo),
ESP_ERR_INVALID_ARG,
"Wrong argument.");
MB_MASTER_CHECK(xMBMasterGetLastTransactionInfo(&ptinfo->trans_id, &ptinfo->dest_addr,
&ptinfo->func_code, &ptinfo->exception,
&ptinfo->err_type),
ESP_ERR_INVALID_STATE,
"Master can not get transaction info.");
return ESP_OK;
}
// Helper function to set parameter buffer according to its type
esp_err_t mbc_master_set_param_data(void* dest, void* src, mb_descr_type_t param_type, size_t param_size)
{
esp_err_t err = ESP_OK;
MB_RETURN_ON_FALSE((src), ESP_ERR_INVALID_STATE, TAG,"incorrect data pointer.");
MB_RETURN_ON_FALSE((dest), ESP_ERR_INVALID_STATE, TAG,"incorrect data pointer.");
void *pdest = dest;
void *psrc = src;
// Transfer parameter data into value of characteristic
switch(param_type)
{
case PARAM_TYPE_U8:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8) {
*((uint8_t*)pdest) = *((uint8_t*)psrc);
}
break;
case PARAM_TYPE_U16:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U16) {
*((uint16_t*)pdest) = *((uint16_t*)psrc);
}
break;
case PARAM_TYPE_U32:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) {
*((uint32_t*)pdest) = *((uint32_t*)psrc);
}
break;
case PARAM_TYPE_FLOAT:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) {
*((float*)pdest) = *(float*)psrc;
}
break;
case PARAM_TYPE_ASCII:
case PARAM_TYPE_BIN:
memcpy((void*)dest, (void*)src, (size_t)param_size);
break;
#if CONFIG_FMB_EXT_TYPE_SUPPORT
case PARAM_TYPE_I8_A:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) {
mb_set_int8_a((val_16_arr *)pdest, (*(int8_t*)psrc));
ESP_LOGV(TAG, "Convert uint8 B[%d] 0x%04" PRIx16 " = 0x%04" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_I8_B:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) {
mb_set_int8_b((val_16_arr *)pdest, (int8_t)((*(uint16_t*)psrc) >> 8));
ESP_LOGV(TAG, "Convert int8 A[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_U8_A:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) {
mb_set_uint8_a((val_16_arr *)pdest, (*(uint8_t*)psrc));
ESP_LOGV(TAG, "Convert uint8 A[%d] 0x%02" PRIx16 " = %02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_U8_B:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) {
uint8_t data = (uint8_t)((*(uint16_t*)psrc) >> 8);
mb_set_uint8_b((val_16_arr *)pdest, data);
ESP_LOGV(TAG, "Convert uint8 B[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_I16_AB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I16) {
mb_set_int16_ab((val_16_arr *)pdest, *(int16_t*)psrc);
ESP_LOGV(TAG, "Convert int16 AB[%d] 0x%04" PRIx16 " = 0x%04" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_I16_BA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I16) {
mb_set_int16_ba((val_16_arr *)pdest, *(int16_t*)psrc);
ESP_LOGV(TAG, "Convert int16 BA[%d] 0x%04" PRIx16 " = 0x%04" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_U16_AB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U16) {
mb_set_uint16_ab((val_16_arr *)pdest, *(uint16_t*)psrc);
ESP_LOGV(TAG, "Convert uint16 AB[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_U16_BA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U16) {
mb_set_uint16_ba((val_16_arr *)pdest, *(uint16_t*)psrc);
ESP_LOGV(TAG, "Convert uint16 BA[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest);
}
break;
case PARAM_TYPE_I32_ABCD:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) {
mb_set_int32_abcd((val_32_arr *)pdest, *(int32_t *)psrc);
ESP_LOGV(TAG, "Convert int32 ABCD[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_U32_ABCD:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) {
mb_set_uint32_abcd((val_32_arr *)pdest, *(uint32_t *)psrc);
ESP_LOGV(TAG, "Convert uint32 ABCD[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_FLOAT_ABCD:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) {
mb_set_float_abcd((val_32_arr *)pdest, *(float *)psrc);
ESP_LOGV(TAG, "Convert float ABCD[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_I32_CDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) {
mb_set_int32_cdab((val_32_arr *)pdest, *(int32_t *)psrc);
ESP_LOGV(TAG, "Convert int32 CDAB[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_U32_CDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) {
mb_set_uint32_cdab((val_32_arr *)pdest, *(uint32_t *)psrc);
ESP_LOGV(TAG, "Convert uint32 CDAB[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_FLOAT_CDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) {
mb_set_float_cdab((val_32_arr *)pdest, *(float *)psrc);
ESP_LOGV(TAG, "Convert float CDAB[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_I32_BADC:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) {
mb_set_int32_badc((val_32_arr *)pdest, *(int32_t *)psrc);
ESP_LOGV(TAG, "Convert int32 BADC[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_U32_BADC:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) {
mb_set_uint32_badc((val_32_arr *)pdest, *(uint32_t *)psrc);
ESP_LOGV(TAG, "Convert uint32 BADC[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_FLOAT_BADC:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) {
mb_set_float_badc((val_32_arr *)pdest, *(float *)psrc);
ESP_LOGV(TAG, "Convert float BADC[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_I32_DCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) {
mb_set_int32_dcba((val_32_arr *)pdest, *(int32_t *)psrc);
ESP_LOGV(TAG, "Convert int32 DCBA[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_U32_DCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) {
mb_set_uint32_dcba((val_32_arr *)pdest, *(uint32_t *)psrc);
ESP_LOGV(TAG, "Convert uint32 DCBA[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_FLOAT_DCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) {
mb_set_float_dcba((val_32_arr *)pdest, *(float *)psrc);
ESP_LOGV(TAG, "Convert float DCBA[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest);
}
break;
case PARAM_TYPE_I64_ABCDEFGH:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) {
mb_set_int64_abcdefgh((val_64_arr *)pdest, *(int64_t *)psrc);
ESP_LOGV(TAG, "Convert int64 ABCDEFGH[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_U64_ABCDEFGH:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) {
mb_set_uint64_abcdefgh((val_64_arr *)pdest, *(uint64_t *)psrc);
ESP_LOGV(TAG, "Convert double ABCDEFGH[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_DOUBLE_ABCDEFGH:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) {
mb_set_double_abcdefgh((val_64_arr *)pdest, *(double *)psrc);
ESP_LOGV(TAG, "Convert double ABCDEFGH[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_I64_HGFEDCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) {
mb_set_int64_hgfedcba((val_64_arr *)pdest, *(int64_t *)psrc);
ESP_LOGV(TAG, "Convert int64 HGFEDCBA[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_U64_HGFEDCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) {
mb_set_uint64_hgfedcba((val_64_arr *)pdest, *(uint64_t *)psrc);
ESP_LOGV(TAG, "Convert double HGFEDCBA[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_DOUBLE_HGFEDCBA:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) {
mb_set_double_hgfedcba((val_64_arr *)pdest, *(double *)psrc);
ESP_LOGV(TAG, "Convert double HGFEDCBA[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_I64_GHEFCDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) {
mb_set_int64_ghefcdab((val_64_arr *)pdest, *(int64_t *)psrc);
ESP_LOGV(TAG, "Convert int64 GHEFCDAB[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_U64_GHEFCDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) {
mb_set_uint64_ghefcdab((val_64_arr *)pdest, *(uint64_t *)psrc);
ESP_LOGV(TAG, "Convert uint64 GHEFCDAB[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_DOUBLE_GHEFCDAB:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) {
mb_set_double_ghefcdab((val_64_arr *)pdest, *(double *)psrc);
ESP_LOGV(TAG, "Convert double GHEFCDAB[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_I64_BADCFEHG:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) {
mb_set_int64_badcfehg((val_64_arr *)pdest, *(int64_t *)psrc);
ESP_LOGV(TAG, "Convert int64 BADCFEHG[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_U64_BADCFEHG:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) {
mb_set_uint64_badcfehg((val_64_arr *)pdest, *(uint64_t *)psrc);
ESP_LOGV(TAG, "Convert uint64 BADCFEHG[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
case PARAM_TYPE_DOUBLE_BADCFEHG:
for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) {
mb_set_double_badcfehg((val_64_arr *)pdest, *(double *)psrc);
ESP_LOGV(TAG, "Convert double BADCFEHG[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest);
}
break;
#endif
default:
ESP_LOGE(TAG, "%s: Incorrect param type (%u).",
__FUNCTION__, (unsigned)param_type);
err = ESP_ERR_NOT_SUPPORTED;
break;
}
return err;
}

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,7 +7,13 @@
#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
#include "sdkconfig.h"
#if CONFIG_FMB_EXT_TYPE_SUPPORT
#include "mb_endianness_utils.h"
#endif
#ifdef __cplusplus
extern "C" {
@ -23,11 +29,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
@ -42,28 +48,13 @@ extern "C" {
#define MB_PAR_INFO_TOUT (10) // Timeout for get parameter info
#define MB_PARITY_NONE (UART_PARITY_DISABLE)
// The Macros below handle the endianness while transfer N byte data into buffer
#define _XFER_4_RD(dst, src) { \
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 1); \
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 0); \
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 3); \
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 2); \
(src) += 4; \
}
// The Macros below handle the endianness while transfer N byte data into buffer (convert from network byte order)
#define _XFER_2_RD(dst, src) { \
*(uint8_t *)(dst)++ = *(uint8_t *)(src + 1); \
*(uint8_t *)(dst)++ = *(uint8_t *)(src + 0); \
(src) += 2; \
}
#define _XFER_4_WR(dst, src) { \
*(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
*(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
*(uint8_t *)(dst + 3) = *(uint8_t *)(src)++; \
*(uint8_t *)(dst + 2) = *(uint8_t *)(src)++ ; \
}
#define _XFER_2_WR(dst, src) { \
*(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
*(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
@ -143,6 +134,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,9 +19,15 @@ 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)
/*!
* \brief The macro to access arrays of elements for type conversion.
*/
#define MB_EACH_ELEM(psrc, pdest, arr_size, elem_size) \
(int i = 0; (i < (arr_size / elem_size)); i++, pdest += elem_size, psrc += elem_size)
/*!
* \brief Modbus descriptor table parameter type defines.
*/
@ -30,7 +36,40 @@ typedef enum {
PARAM_TYPE_U16 = 0x01, /*!< Unsigned 16 */
PARAM_TYPE_U32 = 0x02, /*!< Unsigned 32 */
PARAM_TYPE_FLOAT = 0x03, /*!< Float type */
PARAM_TYPE_ASCII = 0x04 /*!< ASCII type */
PARAM_TYPE_ASCII = 0x04, /*!< ASCII type */
PARAM_TYPE_BIN = 0x07, /*!< BIN type */
PARAM_TYPE_I8_A = 0x0A, /*!< I8 signed integer in high byte of register */
PARAM_TYPE_I8_B = 0x0B, /*!< I8 signed integer in low byte of register */
PARAM_TYPE_U8_A = 0x0C, /*!< U8 unsigned integer written to hi byte of register */
PARAM_TYPE_U8_B = 0x0D, /*!< U8 unsigned integer written to low byte of register */
PARAM_TYPE_I16_AB = 0x0E, /*!< I16 signed integer, big endian */
PARAM_TYPE_I16_BA = 0x0F, /*!< I16 signed integer, little endian */
PARAM_TYPE_U16_AB = 0x10, /*!< U16 unsigned integer, big endian*/
PARAM_TYPE_U16_BA = 0x11, /*!< U16 unsigned integer, little endian */
PARAM_TYPE_I32_ABCD = 0x12, /*!< I32 ABCD signed integer, big endian */
PARAM_TYPE_I32_CDAB = 0x13, /*!< I32 CDAB signed integer, big endian, reversed register order */
PARAM_TYPE_I32_BADC = 0x14, /*!< I32 BADC signed integer, little endian, reversed register order */
PARAM_TYPE_I32_DCBA = 0x15, /*!< I32 DCBA signed integer, little endian */
PARAM_TYPE_U32_ABCD = 0x16, /*!< U32 ABCD unsigned integer, big endian */
PARAM_TYPE_U32_CDAB = 0x17, /*!< U32 CDAB unsigned integer, big endian, reversed register order */
PARAM_TYPE_U32_BADC = 0x18, /*!< U32 BADC unsigned integer, little endian, reversed register order */
PARAM_TYPE_U32_DCBA = 0x19, /*!< U32 DCBA unsigned integer, little endian */
PARAM_TYPE_FLOAT_ABCD = 0x1A, /*!< Float ABCD floating point, big endian */
PARAM_TYPE_FLOAT_CDAB = 0x1B, /*!< Float CDAB floating point big endian, reversed register order */
PARAM_TYPE_FLOAT_BADC = 0x1C, /*!< Float BADC floating point, little endian, reversed register order */
PARAM_TYPE_FLOAT_DCBA = 0x1D, /*!< Float DCBA floating point, little endian */
PARAM_TYPE_I64_ABCDEFGH = 0x1E, /*!< I64, ABCDEFGH signed integer, big endian */
PARAM_TYPE_I64_HGFEDCBA = 0x1F, /*!< I64, HGFEDCBA signed integer, little endian */
PARAM_TYPE_I64_GHEFCDAB = 0x20, /*!< I64, GHEFCDAB signed integer, big endian, reversed register order */
PARAM_TYPE_I64_BADCFEHG = 0x21, /*!< I64, BADCFEHG signed integer, little endian, reversed register order */
PARAM_TYPE_U64_ABCDEFGH = 0x22, /*!< U64, ABCDEFGH unsigned integer, big endian */
PARAM_TYPE_U64_HGFEDCBA = 0x23, /*!< U64, HGFEDCBA unsigned integer, little endian */
PARAM_TYPE_U64_GHEFCDAB = 0x24, /*!< U64, GHEFCDAB unsigned integer, big endian, reversed register order */
PARAM_TYPE_U64_BADCFEHG = 0x25, /*!< U64, BADCFEHG unsigned integer, little endian, reversed register order */
PARAM_TYPE_DOUBLE_ABCDEFGH = 0x26, /*!< Double ABCDEFGH floating point, big endian*/
PARAM_TYPE_DOUBLE_HGFEDCBA = 0x27, /*!< Double HGFEDCBA floating point, little endian*/
PARAM_TYPE_DOUBLE_GHEFCDAB = 0x28, /*!< Double GHEFCDAB floating point, big endian, reversed register order */
PARAM_TYPE_DOUBLE_BADCFEHG = 0x29 /*!< Double BADCFEHG floating point, little endian, reversed register order */
} mb_descr_type_t;
/*!
@ -38,11 +77,18 @@ typedef enum {
*/
typedef enum {
PARAM_SIZE_U8 = 0x01, /*!< Unsigned 8 */
PARAM_SIZE_U8_REG = 0x02, /*!< Unsigned 8, register value */
PARAM_SIZE_I8_REG = 0x02, /*!< Signed 8, register value */
PARAM_SIZE_I16 = 0x02, /*!< Unsigned 16 */
PARAM_SIZE_U16 = 0x02, /*!< Unsigned 16 */
PARAM_SIZE_I32 = 0x04, /*!< Signed 32 */
PARAM_SIZE_U32 = 0x04, /*!< Unsigned 32 */
PARAM_SIZE_FLOAT = 0x04, /*!< Float size */
PARAM_SIZE_ASCII = 0x08, /*!< ASCII size */
PARAM_SIZE_FLOAT = 0x04, /*!< Float 32 size */
PARAM_SIZE_ASCII = 0x08, /*!< ASCII size default*/
PARAM_SIZE_ASCII24 = 0x18, /*!< ASCII24 size */
PARAM_SIZE_I64 = 0x08, /*!< Signed integer 64 size */
PARAM_SIZE_U64 = 0x08, /*!< Unsigned integer 64 size */
PARAM_SIZE_DOUBLE = 0x08, /*!< Double 64 size */
PARAM_MAX_SIZE
} mb_descr_size_t;
@ -104,6 +150,17 @@ typedef struct {
uint16_t reg_size; /*!< Modbus number of registers */
} mb_param_request_t;
/**
* @brief Modbus transacion info structure
*/
typedef struct {
uint64_t trans_id; /*!< Modbus unique transaction identificator */
uint16_t err_type; /*!< Modbus last transaction error type */
uint8_t dest_addr; /*!< Modbus destination short address (or UID) */
uint8_t func_code; /*!< Modbus last transaction function code */
uint8_t exception; /*!< Modbus last transaction exception code returned by slave */
} mb_trans_info_t;
/**
* @brief Initialize Modbus controller and stack for TCP port
*
@ -259,6 +316,34 @@ esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uin
*/
esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type);
/**
* @brief The helper function to set data of parameters according to its type
*
* @param[in] dest the destination address of the parameter
* @param[in] src the source address of the parameter
* @param[out] param_type type of parameter from data dictionary
* @param[out] param_size the storage size of the characteristic (in bytes).
* Describes the size of data to keep into data instance during mapping.
*
* @return
* - esp_err_t ESP_OK - request was successful and value was saved in the slave device registers
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
*/
esp_err_t mbc_master_set_param_data(void* dest, void* src, mb_descr_type_t param_type, size_t param_size);
/**
* @brief The helper function to expose transaction info from modbus layer
*
* @param[in] ptinfo the pointer to transaction info structure
*
* @return
* - esp_err_t ESP_OK - the transaction info is saved in the appropriate parameter structure
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
*/
esp_err_t mbc_master_get_transaction_info(mb_trans_info_t *ptinfo);
#ifdef __cplusplus
}
#endif

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

@ -0,0 +1,556 @@
/*
* SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
/**
* @brief Defines the constant values based on native compiler byte ordering.
*/
#define MB_BO16_0 0
#define MB_BO16_1 1
#define MB_BO32_0 0
#define MB_BO32_1 1
#define MB_BO32_2 2
#define MB_BO32_3 3
#define MB_BO64_0 0
#define MB_BO64_1 1
#define MB_BO64_2 2
#define MB_BO64_3 3
#define MB_BO64_4 4
#define MB_BO64_5 5
#define MB_BO64_6 6
#define MB_BO64_7 7
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The sized array types used for mapping of extended values
*/
typedef uint8_t val_16_arr[2];
typedef uint8_t val_32_arr[4];
typedef uint8_t val_64_arr[8];
/**
* @brief Get int8_t (low byte) value represenatation from register
*
* @return
* - int8_t value of converted from register value
*/
int8_t mb_get_int8_a(val_16_arr *pi16);
/**
* @brief Set i8 value to the register value pointed by pi16
*
* @return
* - uint16_t value which represents the actual hex value of the register
*/
uint16_t mb_set_int8_a(val_16_arr *pi16, int8_t i8);
/**
* @brief Get int8_t (high byte) value from the register value pointed by pi16
*
* @return
* - uint16_t value which represents the actual hex value of the register
*/
int8_t mb_get_int8_b(val_16_arr *pi16);
/**
* @brief Set i8 (high byte) value from the register value pointed by pi16
*
* @return
* - uint16_t value which represents the actual hex value of the register
*/
uint16_t mb_set_int8_b(val_16_arr *pi16, int8_t i8);
/**
* @brief Get uint8_t (low byte) value represenatation from register poined by pu16
*
* @return
* - uint8_t the value of converted from register value
*/
uint8_t mb_get_uint8_a(val_16_arr *pu16);
/**
* @brief Set u8 (low byte) value into the register value pointed by pu16
*
* @return
* - uint16_t the value which represents the actual hex value of the register
*/
uint16_t mb_set_uint8_a(val_16_arr *pu16, uint8_t u8);
/**
* @brief Get uint8_t (high byte) value from the register value pointed by pu16
*
* @return
* - uint16_t the value which represents the actual hex value of the register
*/
uint8_t mb_get_uint8_b(val_16_arr *pu16);
/**
* @brief Set u8 (high byte) value into the register value pointed by pu16
*
* @return
* - uint16_t the value which represents the actual hex value of the register
*/
uint16_t mb_set_uint8_b(val_16_arr *pu16, uint8_t u8);
/**
* @brief Get int16_t value from the register value pointed by pu16 with ab endianness
*
* @return
* - int16_t the value which represents the converted value from register
*/
int16_t mb_get_int16_ab(val_16_arr *pi16);
/**
* @brief Set i16 value to the register pointed by pi16 with ab endianness
*
* @return
* - int16_t the value which represents the converted value from register
*/
uint16_t mb_set_int16_ab(val_16_arr *pi16, int16_t i16);
/**
* @brief Get uint16_t value from the register value pointed by pu16 with ab endianness
*
* @return
* - uint16_t value which represents the converted register value
*/
uint16_t mb_get_uint16_ab(val_16_arr *pu16);
/**
* @brief Set u16 value to the register pointed by pu16 with ab endianness
*
* @return
* - uint16_t value which represents the converted value from register
*/
uint16_t mb_set_uint16_ab(val_16_arr *pu16, uint16_t u16);
/**
* @brief Get int16_t value from the register value pointed by pu16 with ba endianness
*
* @return
* - int16_t value which represents the converted register value
*/
int16_t mb_get_int16_ba(val_16_arr *pi16);
/**
* @brief Set i16 value to the register pointed by pi16 with ba endianness
*
* @return
* - uint16_t value which represents the converted value from register
*/
uint16_t mb_set_int16_ba(val_16_arr *pi16, int16_t i16);
/**
* @brief Get uint16_t value from the register value pointed by pu16 with ba endianness
*
* @return
* - uint16_t value which represents the converted register value
*/
uint16_t mb_get_uint16_ba(val_16_arr *pu16);
/**
* @brief Set u16 value to the register pointed by pu16 with ba endianness
*
* @return
* - uint16_t value which represents the converted value from register
*/
uint16_t mb_set_uint16_ba(val_16_arr *pu16, uint16_t u16);
/**
* @brief Get int32_t value from the register value pointed by pi32 with abcd endianness
*
* @return
* - int32_t value which represents the converted register value
*/
int32_t mb_get_int32_abcd(val_32_arr *pi32);
/**
* @brief Set i32 value to the register pointed by pi32 with abcd endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_int32_abcd(val_32_arr *pi32, int32_t i32);
/**
* @brief Get uint32_t value from the register value pointed by pu32 with abcd endianness
*
* @return
* - uint32_t value which represents the converted register value
*/
uint32_t mb_get_uint32_abcd(val_32_arr *pu32);
/**
* @brief Set u32 value to the register pointed by pu32 with abcd endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_uint32_abcd(val_32_arr *pu32, uint32_t u32);
/**
* @brief Get int32_t value from the register value pointed by pi32 with badc endianness
*
* @return
* - int32_t value which represents the converted register value
*/
int32_t mb_get_int32_badc(val_32_arr *pi32);
/**
* @brief Set i32 value to the register pointed by pi32 with badc endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_int32_badc(val_32_arr *pi32, int32_t i32);
/**
* @brief Get uint32_t value from the register value pointed by pu32 with badc endianness
*
* @return
* - unt32_t value which represents the converted register value
*/
uint32_t mb_get_uint32_badc(val_32_arr *pu32);
/**
* @brief Set u32 value to the register pointed by pu32 with badc endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_uint32_badc(val_32_arr *pu32, uint32_t u32);
/**
* @brief Get int32_t value from the register value pointed by pi32 with cdab endianness
*
* @return
* - int32_t value which represents the converted register value
*/
int32_t mb_get_int32_cdab(val_32_arr *pi32);
/**
* @brief Set i32 value to the register pointed by pi32 with cdab endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_int32_cdab(val_32_arr *pi32, int32_t i32);
/**
* @brief Get uint32_t value from the register value pointed by pu32 with cdab endianness
*
* @return
* - int32_t value which represents the converted register value
*/
uint32_t mb_get_uint32_cdab(val_32_arr *pu32);
/**
* @brief Set u32 value to the register pointed by pu32 with cdab endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_uint32_cdab(val_32_arr *pu32, uint32_t u32);
/**
* @brief Get int32_t value from the register value pointed by pi32 with dcba endianness
*
* @return
* - int32_t value which represents the converted register value
*/
int32_t mb_get_int32_dcba(val_32_arr *pi32);
/**
* @brief Set i32 value to the register pointed by pi32 with dcba endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_int32_dcba(val_32_arr *pi32, int32_t i32);
/**
* @brief Get uint32_t value from the register value pointed by pu32 with dcba endianness
*
* @return
* - uint32_t value which represents the converted register value
*/
uint32_t mb_get_uint32_dcba(val_32_arr *pu32);
/**
* @brief Set u32 value to the register pointed by pu32 with dcba endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_uint32_dcba(val_32_arr *pu32, uint32_t u32);
/**
* @brief Get float value from the register pointed by pf with abcd endianness
*
* @return
* - float value which represents the converted register value
*/
float mb_get_float_abcd(val_32_arr *pf);
/**
* @brief Set f value to the register pointed by pf with abcd endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_float_abcd(val_32_arr *pf, float f);
/**
* @brief Get float value from the register pointed by pf with badc endianness
*
* @return
* - float value which represents the converted register value
*/
float mb_get_float_badc(val_32_arr *pf);
/**
* @brief Set f value to the register pointed by pf with badc endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_float_badc(val_32_arr *pf, float f);
/**
* @brief Get float value from the register pointed by pf with cdab endianness
*
* @return
* - float value which represents the converted register value
*/
float mb_get_float_cdab(val_32_arr *pf);
/**
* @brief Set f value to the register pointed by pf with cdab endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_float_cdab(val_32_arr *pf, float f);
/**
* @brief Get float value from the register pointed by pf with dcba endianness
*
* @return
* - float value which represents the converted register value
*/
float mb_get_float_dcba(val_32_arr *pf);
/**
* @brief Set f value to the register pointed by pf with dcba endianness
*
* @return
* - uint32_t value which represents the converted value from register
*/
uint32_t mb_set_float_dcba(val_32_arr *pf, float f);
/**
* @brief Get double value from the register pointed by pd with abcdefgh endianness
*
* @return
* - double value which represents the converted register value
*/
double mb_get_double_abcdefgh(val_64_arr *pd);
/**
* @brief Set d value to the register pointed by pd with abcdefgh endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_double_abcdefgh(val_64_arr *pd, double d);
/**
* @brief Get double value from the register pointed by pd with hgfedcba endianness
*
* @return
* - double value which represents the converted register value
*/
double mb_get_double_hgfedcba(val_64_arr *pd);
/**
* @brief Set d value to the register pointed by pd with hgfedcba endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_double_hgfedcba(val_64_arr *pd, double d);
/**
* @brief Get double value from the register pointed by pd with ghefcdab endianness
*
* @return
* - double value which represents the converted register value
*/
double mb_get_double_ghefcdab(val_64_arr *pd);
/**
* @brief Set d value to the register pointed by pd with ghefcdab endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_double_ghefcdab(val_64_arr *pd, double d);
/**
* @brief Get double value from the register pointed by pd with badcfehg endianness
*
* @return
* - double value which represents the converted register value
*/
double mb_get_double_badcfehg(val_64_arr *pd);
/**
* @brief Set d value to the register pointed by pd with badcfehg endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_double_badcfehg(val_64_arr *pd, double d);
/**
* @brief Get int64_t value from the register pointed by pi64 with abcdefgh endianness
*
* @return
* - int64_t value which represents the converted register value
*/
int64_t mb_get_int64_abcdefgh(val_64_arr *pi64);
/**
* @brief Set i value to the register pointed by pi with abcdefgh endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_int64_abcdefgh(val_64_arr *pi, int64_t i);
/**
* @brief Get int64_t value from the register pointed by pi64 with ghefcdab endianness
*
* @return
* - int64_t value which represents the converted register value
*/
int64_t mb_get_int64_ghefcdab(val_64_arr *pi64);
/**
* @brief Set i value to the register pointed by pi with ghefcdab endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_int64_ghefcdab(val_64_arr *pi, int64_t i);
/**
* @brief Get int64_t value from the register pointed by pi64 with hgfedcba endianness
*
* @return
* - int64_t value which represents the converted register value
*/
int64_t mb_get_int64_hgfedcba(val_64_arr *pi64);
/**
* @brief Set i value to the register pointed by pi with hgfedcba endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_int64_hgfedcba(val_64_arr *pi, int64_t i);
/**
* @brief Get int64_t value from the register pointed by pi64 with badcfehg endianness
*
* @return
* - int64_t value which represents the converted register value
*/
int64_t mb_get_int64_badcfehg(val_64_arr *pi64);
/**
* @brief Set i value to the register pointed by pi with badcfehg endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_int64_badcfehg(val_64_arr *pi, int64_t i);
/**
* @brief Get uint64_t value from the register pointed by pui with abcdefgh endianness
*
* @return
* - uint64_t value which represents the converted register value
*/
uint64_t mb_get_uint64_abcdefgh(val_64_arr *pui);
/**
* @brief Set ui value to the register pointed by pi with abcdefgh endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_uint64_abcdefgh(val_64_arr *pui, uint64_t ui);
/**
* @brief Get uint64_t value from the register pointed by pui with hgfedcba endianness
*
* @return
* - uint64_t value which represents the converted register value
*/
uint64_t mb_get_uint64_hgfedcba(val_64_arr *pui);
/**
* @brief Set ui value to the register pointed by pui with hgfedcba endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_uint64_hgfedcba(val_64_arr *pui, uint64_t ui);
/**
* @brief Get uint64_t value from the register pointed by pui with ghefcdab endianness
*
* @return
* - uint64_t value which represents the converted register value
*/
uint64_t mb_get_uint64_ghefcdab(val_64_arr *pui);
/**
* @brief Set ui value to the register pointed by pui with ghefcdab endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_uint64_ghefcdab(val_64_arr *pui, uint64_t ui);
/**
* @brief Get uint64_t value from the register pointed by pui with badcfehg endianness
*
* @return
* - uint64_t value which represents the converted register value
*/
uint64_t mb_get_uint64_badcfehg(val_64_arr *pui);
/**
* @brief Set ui value to the register pointed by pui with badcfehg endianness
*
* @return
* - uint64_t value which represents the converted value from register
*/
uint64_t mb_set_uint64_badcfehg(val_64_arr *pui, uint64_t ui);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,683 @@
/*
* SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include "mb_endianness_utils.h"
#define INLINE inline __attribute__((always_inline))
static INLINE int16_t mb_get_int16_generic(int n0, int n1, val_16_arr *psrc)
{
val_16_arr *pv = psrc;
union {
val_16_arr arr;
int16_t value;
} bov;
bov.arr[n0] = (*pv)[MB_BO16_0];
bov.arr[n1] = (*pv)[MB_BO16_1];
return (bov.value);
}
static INLINE uint16_t mb_get_uint16_generic(int n0, int n1, val_16_arr *psrc)
{
val_16_arr *pv = psrc;
union {
val_16_arr arr;
uint16_t value;
} bov;
bov.arr[n0] = (*pv)[MB_BO16_0];
bov.arr[n1] = (*pv)[MB_BO16_1];
return (bov.value);
}
static INLINE uint16_t mb_set_uint16_generic(int n0, int n1, val_16_arr *pdest, uint16_t val)
{
val_16_arr *pv = pdest;
union {
val_16_arr arr;
uint16_t value;
} bov;
bov.value = val;
(*pv)[MB_BO16_0] = bov.arr[n0];
(*pv)[MB_BO16_1] = bov.arr[n1];
return (*((uint16_t *)pv));
}
static INLINE int16_t mb_set_int16_generic(int n0, int n1, val_16_arr *pdest, int16_t val)
{
val_16_arr *pv = pdest;
union {
val_16_arr arr;
int16_t value;
} bov;
bov.value = val;
(*pv)[MB_BO16_0] = bov.arr[n0];
(*pv)[MB_BO16_1] = bov.arr[n1];
return (*((uint16_t *)pv));
}
static INLINE uint32_t mb_get_uint32_generic(int n0, int n1, int n2, int n3, val_32_arr *psrc)
{
val_32_arr *pv = psrc;
union {
val_32_arr arr;
uint32_t value;
} bov;
bov.arr[n0] = (*pv)[MB_BO32_0];
bov.arr[n1] = (*pv)[MB_BO32_1];
bov.arr[n2] = (*pv)[MB_BO32_2];
bov.arr[n3] = (*pv)[MB_BO32_3];
return (bov.value);
}
static INLINE int32_t mb_get_int32_generic(int n0, int n1, int n2, int n3, val_32_arr *psrc)
{
val_32_arr *pv = psrc;
union {
val_32_arr arr;
int32_t value;
} bov;
bov.arr[n0] = (*pv)[MB_BO32_0];
bov.arr[n1] = (*pv)[MB_BO32_1];
bov.arr[n2] = (*pv)[MB_BO32_2];
bov.arr[n3] = (*pv)[MB_BO32_3];
return (bov.value);
}
static INLINE float mb_get_float_generic(int n0, int n1, int n2, int n3, val_32_arr *psrc)
{
val_32_arr *pv = psrc;
union {
val_32_arr arr;
float value;
} bov;
bov.arr[n0] = (*pv)[MB_BO32_0];
bov.arr[n1] = (*pv)[MB_BO32_1];
bov.arr[n2] = (*pv)[MB_BO32_2];
bov.arr[n3] = (*pv)[MB_BO32_3];
return (bov.value);
}
static INLINE uint32_t mb_set_int32_generic(int n0, int n1, int n2, int n3, val_32_arr *pdest, int32_t val)
{
val_32_arr *pv = pdest;
union {
val_32_arr arr;
int32_t value;
} bov;
bov.value = val;
(*pv)[MB_BO32_0] = bov.arr[n0];
(*pv)[MB_BO32_1] = bov.arr[n1];
(*pv)[MB_BO32_2] = bov.arr[n2];
(*pv)[MB_BO32_3] = bov.arr[n3];
return (*((uint32_t *)pv));
}
static INLINE uint32_t mb_set_uint32_generic(int n0, int n1, int n2, int n3, val_32_arr *pdest, uint32_t val)
{
val_32_arr *pv = pdest;
union {
val_32_arr arr;
uint32_t value;
} bov;
bov.value = val;
(*pv)[MB_BO32_0] = bov.arr[n0];
(*pv)[MB_BO32_1] = bov.arr[n1];
(*pv)[MB_BO32_2] = bov.arr[n2];
(*pv)[MB_BO32_3] = bov.arr[n3];
return (*((uint32_t *)pv));
}
static INLINE uint32_t mb_set_float_generic(int n0, int n1, int n2, int n3, val_32_arr *pdest, float val)
{
val_32_arr *pv = pdest;
union {
val_32_arr arr;
float value;
} bov;
bov.value = val;
(*pv)[MB_BO32_0] = bov.arr[n0];
(*pv)[MB_BO32_1] = bov.arr[n1];
(*pv)[MB_BO32_2] = bov.arr[n2];
(*pv)[MB_BO32_3] = bov.arr[n3];
return (*((uint32_t *)pv));
}
static INLINE int64_t mb_get_int64_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *psrc)
{
val_64_arr *pv64 = psrc;
union {
val_64_arr arr;
int64_t value;
} bo64;
bo64.arr[n0] = (*pv64)[MB_BO64_0];
bo64.arr[n1] = (*pv64)[MB_BO64_1];
bo64.arr[n2] = (*pv64)[MB_BO64_2];
bo64.arr[n3] = (*pv64)[MB_BO64_3];
bo64.arr[n4] = (*pv64)[MB_BO64_4];
bo64.arr[n5] = (*pv64)[MB_BO64_5];
bo64.arr[n6] = (*pv64)[MB_BO64_6];
bo64.arr[n7] = (*pv64)[MB_BO64_7];
return (bo64.value);
}
static INLINE uint64_t mb_get_uint64_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *psrc)
{
val_64_arr *pv64 = psrc;
union {
val_64_arr arr;
uint64_t value;
} bo64;
bo64.arr[n0] = (*pv64)[MB_BO64_0];
bo64.arr[n1] = (*pv64)[MB_BO64_1];
bo64.arr[n2] = (*pv64)[MB_BO64_2];
bo64.arr[n3] = (*pv64)[MB_BO64_3];
bo64.arr[n4] = (*pv64)[MB_BO64_4];
bo64.arr[n5] = (*pv64)[MB_BO64_5];
bo64.arr[n6] = (*pv64)[MB_BO64_6];
bo64.arr[n7] = (*pv64)[MB_BO64_7];
return (bo64.value);
}
static INLINE double mb_get_double_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *psrc)
{
val_64_arr *pv64 = psrc;
union {
val_64_arr arr;
double value;
} bo64;
bo64.arr[n0] = (*pv64)[MB_BO64_0];
bo64.arr[n1] = (*pv64)[MB_BO64_1];
bo64.arr[n2] = (*pv64)[MB_BO64_2];
bo64.arr[n3] = (*pv64)[MB_BO64_3];
bo64.arr[n4] = (*pv64)[MB_BO64_4];
bo64.arr[n5] = (*pv64)[MB_BO64_5];
bo64.arr[n6] = (*pv64)[MB_BO64_6];
bo64.arr[n7] = (*pv64)[MB_BO64_7];
return (bo64.value);
}
static INLINE uint64_t mb_set_int64_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *pdest, int64_t val)
{
val_64_arr *pv = pdest;
union {
val_64_arr arr;
int64_t value;
} bo64;
bo64.value = val;
(*pv)[MB_BO64_0] = bo64.arr[n0];
(*pv)[MB_BO64_1] = bo64.arr[n1];
(*pv)[MB_BO64_2] = bo64.arr[n2];
(*pv)[MB_BO64_3] = bo64.arr[n3];
(*pv)[MB_BO64_4] = bo64.arr[n4];
(*pv)[MB_BO64_5] = bo64.arr[n5];
(*pv)[MB_BO64_6] = bo64.arr[n6];
(*pv)[MB_BO64_7] = bo64.arr[n7];
return (*((uint64_t *)pv));
}
static INLINE uint64_t mb_set_uint64_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *pdest, uint64_t val)
{
val_64_arr *pv = pdest;
union {
val_64_arr arr;
uint64_t value;
} bo64;
bo64.value = val;
(*pv)[MB_BO64_0] = bo64.arr[n0];
(*pv)[MB_BO64_1] = bo64.arr[n1];
(*pv)[MB_BO64_2] = bo64.arr[n2];
(*pv)[MB_BO64_3] = bo64.arr[n3];
(*pv)[MB_BO64_4] = bo64.arr[n4];
(*pv)[MB_BO64_5] = bo64.arr[n5];
(*pv)[MB_BO64_6] = bo64.arr[n6];
(*pv)[MB_BO64_7] = bo64.arr[n7];
return (*((uint64_t *)pv));
}
static INLINE uint64_t mb_set_double_generic(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, val_64_arr *pdest, double val)
{
val_64_arr *pv = pdest;
union {
val_64_arr arr;
double value;
} bo64;
bo64.value = val;
(*pv)[MB_BO64_0] = bo64.arr[n0];
(*pv)[MB_BO64_1] = bo64.arr[n1];
(*pv)[MB_BO64_2] = bo64.arr[n2];
(*pv)[MB_BO64_3] = bo64.arr[n3];
(*pv)[MB_BO64_4] = bo64.arr[n4];
(*pv)[MB_BO64_5] = bo64.arr[n5];
(*pv)[MB_BO64_6] = bo64.arr[n6];
(*pv)[MB_BO64_7] = bo64.arr[n7];
return (*((uint64_t *)pv));
}
int8_t mb_get_int8_a(pi16)
val_16_arr *pi16;
{
return((int8_t)(*pi16)[MB_BO16_0]);
}
uint16_t mb_set_int8_a(pi16, i8)
val_16_arr *pi16;
int8_t i8;
{
(*pi16)[MB_BO16_0] = (uint8_t)i8;
(*pi16)[MB_BO16_1] = 0;
return (*((uint16_t *)pi16));
}
int8_t mb_get_int8_b(pi16)
val_16_arr *pi16;
{
return((int8_t)(*pi16)[MB_BO16_1]);
}
uint16_t mb_set_int8_b(pi16, i8)
val_16_arr *pi16;
int8_t i8;
{
(*pi16)[MB_BO16_0] = 0;
(*pi16)[MB_BO16_1] = (int8_t)i8;
return (*((uint16_t *)pi16));
}
uint8_t mb_get_uint8_a(pu16)
val_16_arr *pu16;
{
return((uint8_t)(*pu16)[MB_BO16_0]);
}
uint16_t mb_set_uint8_a(pu16, u8)
val_16_arr *pu16;
uint8_t u8;
{
(*pu16)[MB_BO16_0] = (uint8_t)u8;
(*pu16)[MB_BO16_1] = 0;
return (*((uint16_t *)pu16));
}
uint8_t mb_get_uint8_b(pu16)
val_16_arr *pu16;
{
return((uint8_t)(*pu16)[MB_BO16_1]);
}
uint16_t mb_set_uint8_b(pu16, u8)
val_16_arr *pu16;
uint8_t u8;
{
(*pu16)[MB_BO16_0] = 0;
(*pu16)[MB_BO16_1] = (uint8_t)u8;
return (*((uint16_t *)pu16));
}
int16_t mb_get_int16_ab(pi16)
val_16_arr *pi16;
{
return mb_get_int16_generic(0, 1, pi16);
}
uint16_t mb_set_int16_ab(pi16, i16)
val_16_arr *pi16;
int16_t i16;
{
return mb_set_int16_generic(0, 1, pi16, i16);
}
uint16_t mb_get_uint16_ab(pu16)
val_16_arr *pu16;
{
return mb_get_uint16_generic(0, 1, pu16);
}
uint16_t mb_set_uint16_ab(pu16, u16)
val_16_arr *pu16;
uint16_t u16;
{
return mb_set_uint16_generic(0, 1, pu16, u16);
}
int16_t mb_get_int16_ba(pi16)
val_16_arr *pi16;
{
return mb_get_int16_generic(1, 0, pi16);
}
uint16_t mb_set_int16_ba(pi16, i16)
val_16_arr *pi16;
int16_t i16;
{
return mb_set_int16_generic(1, 0, pi16, i16);
}
uint16_t mb_get_uint16_ba(pu16)
val_16_arr *pu16;
{
return mb_get_int16_generic(1, 0, pu16);
}
uint16_t mb_set_uint16_ba(pu16, u16)
val_16_arr *pu16;
uint16_t u16;
{
return mb_set_int16_generic(1, 0, pu16, u16);
}
int32_t mb_get_int32_abcd(pi32)
val_32_arr *pi32;
{
return mb_get_int32_generic(0, 1, 2, 3, pi32);
}
uint32_t mb_set_int32_abcd(pi32, i32)
val_32_arr *pi32;
int32_t i32;
{
return mb_set_int32_generic(0, 1, 2, 3, pi32, i32);
}
uint32_t mb_get_uint32_abcd(pu32)
val_32_arr *pu32;
{
return mb_get_uint32_generic(0, 1, 2, 3, pu32);
}
uint32_t mb_set_uint32_abcd(pu32, u32)
val_32_arr *pu32;
uint32_t u32;
{
return mb_set_uint32_generic(0, 1, 2, 3, pu32, u32);
}
int32_t mb_get_int32_badc(pi32)
val_32_arr *pi32;
{
return mb_get_int32_generic(1, 0, 3, 2, pi32);
}
uint32_t mb_set_int32_badc(pi32, i32)
val_32_arr *pi32;
int32_t i32;
{
return mb_set_int32_generic(1, 0, 3, 2, pi32, i32);
}
uint32_t mb_get_uint32_badc(pu32)
val_32_arr *pu32;
{
return mb_get_uint32_generic(1, 0, 3, 2, pu32);
}
uint32_t mb_set_uint32_badc(pu32, u32)
val_32_arr *pu32;
uint32_t u32;
{
return mb_set_uint32_generic(1, 0, 3, 2, pu32, u32);
}
int32_t mb_get_int32_cdab(pi32)
val_32_arr *pi32;
{
return mb_get_int32_generic(2, 3, 0, 1, pi32);
}
uint32_t mb_set_int32_cdab(pi32, i32)
val_32_arr *pi32;
int32_t i32;
{
return mb_set_int32_generic(2, 3, 0, 1, pi32, i32);
}
uint32_t mb_get_uint32_cdab(pu32)
val_32_arr *pu32;
{
return mb_get_uint32_generic(2, 3, 0, 1, pu32);
}
uint32_t mb_set_uint32_cdab(pu32, u32)
val_32_arr *pu32;
uint32_t u32;
{
return mb_set_uint32_generic(2, 3, 0, 1, pu32, u32);
}
int32_t mb_get_int32_dcba(pi32)
val_32_arr *pi32;
{
return mb_get_int32_generic(3, 2, 1, 0, pi32);
}
uint32_t mb_set_int32_dcba(pi32, i32)
val_32_arr *pi32;
int32_t i32;
{
return mb_set_int32_generic(3, 2, 1, 0, pi32, i32);
}
uint32_t mb_get_uint32_dcba(pu32)
val_32_arr *pu32;
{
return mb_get_uint32_generic(3, 2, 1, 0, pu32);
}
uint32_t mb_set_uint32_dcba(pu32, u32)
val_32_arr *pu32;
uint32_t u32;
{
return mb_set_uint32_generic(3, 2, 1, 0, pu32, u32);
}
float mb_get_float_abcd(pf)
val_32_arr *pf;
{
return mb_get_float_generic(0, 1, 2, 3, pf);
}
uint32_t mb_set_float_abcd(pf, f)
val_32_arr *pf;
float f;
{
return mb_set_float_generic(0, 1, 2, 3, pf, f);
}
float mb_get_float_badc(pf)
val_32_arr *pf;
{
return mb_get_float_generic(1, 0, 3, 2, pf);
}
uint32_t mb_set_float_badc(pf, f)
val_32_arr *pf;
float f;
{
return mb_set_float_generic(1, 0, 3, 2, pf, f);
}
float mb_get_float_cdab(pf)
val_32_arr *pf;
{
return mb_get_float_generic(2, 3, 0, 1, pf);
}
uint32_t mb_set_float_cdab(pf, f)
val_32_arr *pf;
float f;
{
return mb_set_float_generic(2, 3, 0, 1, pf, f);
}
float mb_get_float_dcba(pf)
val_32_arr *pf;
{
return mb_get_float_generic(3, 2, 1, 0, pf);
}
uint32_t mb_set_float_dcba(pf, f)
val_32_arr *pf;
float f;
{
return mb_set_float_generic(3, 2, 1, 0, pf, f);
}
double mb_get_double_abcdefgh(pd)
val_64_arr *pd;
{
return mb_get_double_generic(0, 1, 2, 3, 4, 5, 6, 7, pd);
}
uint64_t mb_set_double_abcdefgh(pd, d)
val_64_arr *pd;
double d;
{
return mb_set_double_generic(0, 1, 2, 3, 4, 5, 6, 7, pd, d);
}
double mb_get_double_hgfedcba(pd)
val_64_arr *pd;
{
return mb_get_double_generic(7, 6, 5, 4, 3, 2, 1, 0, pd);
}
uint64_t mb_set_double_hgfedcba(pd, d)
val_64_arr *pd;
double d;
{
return mb_set_double_generic(7, 6, 5, 4, 3, 2, 1, 0, pd, d);
}
double mb_get_double_ghefcdab(pd)
val_64_arr *pd;
{
return mb_get_double_generic(6, 7, 4, 5, 2, 3, 0, 1, pd);
}
uint64_t mb_set_double_ghefcdab(pd, d)
val_64_arr *pd;
double d;
{
return mb_set_double_generic(6, 7, 4, 5, 2, 3, 0, 1, pd, d);
}
double mb_get_double_badcfehg(pd)
val_64_arr *pd;
{
return mb_get_double_generic(1, 0, 3, 2, 5, 4, 7, 6, pd);
}
uint64_t mb_set_double_badcfehg(pd, d)
val_64_arr *pd;
double d;
{
return mb_set_double_generic(1, 0, 3, 2, 5, 4, 7, 6, pd, d);
}
int64_t mb_get_int64_abcdefgh(pi64)
val_64_arr *pi64;
{
return mb_get_int64_generic(0, 1, 2, 3, 4, 5, 6, 7, pi64);
}
uint64_t mb_set_int64_abcdefgh(pi, i)
val_64_arr *pi;
int64_t i;
{
return mb_set_int64_generic(0, 1, 2, 3, 4, 5, 6, 7, pi, i);
}
int64_t mb_get_int64_hgfedcba(pi64)
val_64_arr *pi64;
{
return mb_get_int64_generic(7, 6, 5, 4, 3, 2, 1, 0, pi64);
}
uint64_t mb_set_int64_hgfedcba(pi, i)
val_64_arr *pi;
int64_t i;
{
return mb_set_int64_generic(7, 6, 5, 4, 3, 2, 1, 0, pi, i);
}
int64_t mb_get_int64_ghefcdab(pi64)
val_64_arr *pi64;
{
return mb_get_int64_generic(6, 7, 4, 5, 2, 3, 0, 1, pi64);
}
uint64_t mb_set_int64_ghefcdab(pi, i)
val_64_arr *pi;
int64_t i;
{
return mb_set_int64_generic(6, 7, 4, 5, 2, 3, 0, 1, pi, i);
}
int64_t mb_get_int64_badcfehg(pi64)
val_64_arr *pi64;
{
return mb_get_int64_generic(1, 0, 3, 2, 5, 4, 7, 6, pi64);
}
uint64_t mb_set_int64_badcfehg(pi, i)
val_64_arr *pi;
int64_t i;
{
return mb_set_int64_generic(1, 0, 3, 2, 5, 4, 7, 6, pi, i);
}
uint64_t mb_get_uint64_abcdefgh(pui)
val_64_arr *pui;
{
return mb_get_uint64_generic(0, 1, 2, 3, 4, 5, 6, 7, pui);
}
uint64_t mb_set_uint64_abcdefgh(pui, ui)
val_64_arr *pui;
uint64_t ui;
{
return mb_set_uint64_generic(0, 1, 2, 3, 4, 5, 6, 7, pui, ui);
}
uint64_t mb_get_uint64_hgfedcba(pui)
val_64_arr *pui;
{
return mb_get_uint64_generic(7, 6, 5, 4, 3, 2, 1, 0, pui);
}
uint64_t mb_set_uint64_hgfedcba(pui, ui)
val_64_arr *pui;
uint64_t ui;
{
return mb_set_uint64_generic(7, 6, 5, 4, 3, 2, 1, 0, pui, ui);
}
uint64_t mb_get_uint64_ghefcdab(pui)
val_64_arr *pui;
{
return mb_get_uint64_generic(6, 7, 4, 5, 2, 3, 0, 1, pui);
}
uint64_t mb_set_uint64_ghefcdab(pui, ui)
val_64_arr *pui;
uint64_t ui;
{
return mb_set_uint64_generic(6, 7, 4, 5, 2, 3, 0, 1, pui, ui);
}
uint64_t mb_get_uint64_badcfehg(pui)
val_64_arr *pui;
{
return mb_get_int64_generic(1, 0, 3, 2, 5, 4, 7, 6, pui);
}
uint64_t mb_set_uint64_badcfehg(pui, ui)
val_64_arr *pui;
uint64_t ui;
{
return mb_set_uint64_generic(1, 0, 3, 2, 5, 4, 7, 6, pui, ui);
}

View File

@ -18,6 +18,7 @@
#include "esp_modbus_common.h" // for common types
#include "esp_modbus_master.h" // for public master types
#include "esp_modbus_callbacks.h"
#include "mb_m.h" // this is required to expose current transaction info
/* ----------------------- Defines ------------------------------------------*/

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

@ -83,7 +83,6 @@ typedef enum
/* These Modbus values are shared in ASCII mode*/
extern volatile UCHAR ucMasterRcvBuf[];
extern volatile UCHAR ucMasterSndBuf[];
extern volatile eMBMasterTimerMode eMasterCurTimerMode;
/* ----------------------- Static functions ---------------------------------*/
static UCHAR prvucMBCHAR2BIN( UCHAR ucCharacter );
@ -137,7 +136,7 @@ eMBMasterASCIIStart( void )
ENTER_CRITICAL_SECTION( );
eRcvState = STATE_M_RX_IDLE;
vMBMasterPortSerialEnable( TRUE, FALSE );
vMBMasterPortTimersT35Enable( );
xMBMasterPortEventPost(EV_MASTER_READY);
EXIT_CRITICAL_SECTION( );
}
@ -157,7 +156,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 +180,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 +218,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 +273,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 +436,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

@ -95,15 +95,32 @@ typedef enum
MB_MRE_EXE_FUN /*!< execute function error. */
} eMBMasterReqErrCode;
/*! \ingroup modbus
* \brief Transaction information structure.
*/
typedef struct
{
uint64_t xTransId;
UCHAR ucDestAddr;
UCHAR ucFuncCode;
eMBException eException;
UCHAR ucFrameError;
} TransactionInfo_t;
/*! \ingroup modbus
* \brief TimerMode is Master 3 kind of Timer modes.
*/
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;
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;
extern _lock_t xMBMLock; // Modbus lock object
#define MB_ATOMIC_SECTION CRITICAL_SECTION(xMBMLock)
/* ----------------------- Function prototypes ------------------------------*/
/*! \ingroup modbus
@ -128,7 +145,7 @@ typedef enum
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
*/
eMBErrorCode eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort,
ULONG ulBaudRate, eMBParity eParity );
ULONG ulBaudRate, eMBParity eParity );
/*! \ingroup modbus
* \brief Initialize the Modbus Master protocol stack for Modbus TCP.
@ -220,7 +237,7 @@ eMBErrorCode eMBMasterPoll( void );
* valid it returns eMBErrorCode::MB_EINVAL.
*/
eMBErrorCode eMBMasterRegisterCB( UCHAR ucFunctionCode,
pxMBFunctionHandler pxHandler );
pxMBFunctionHandler pxHandler );
/* ----------------------- Callback -----------------------------------------*/
@ -261,7 +278,7 @@ eMBErrorCode eMBMasterRegisterCB( UCHAR ucFunctionCode,
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs );
USHORT usNRegs );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Holding Register</em> value is
@ -290,7 +307,7 @@ eMBErrorCode eMBMasterRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress,
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode );
USHORT usNRegs, eMBRegisterMode eMode );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Coil Register</em> value is
@ -319,7 +336,7 @@ eMBErrorCode eMBMasterRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress,
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode );
USHORT usNCoils, eMBRegisterMode eMode );
/*! \ingroup modbus_registers
* \brief Callback function used if a <em>Input Discrete Register</em> value is
@ -342,7 +359,7 @@ eMBErrorCode eMBMasterRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
*/
eMBErrorCode eMBMasterRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete );
USHORT usNDiscrete );
/*! \ingroup modbus
*\brief These Modbus functions are called for user when Modbus run in Master Mode.
@ -353,20 +370,20 @@ eMBMasterReqErrCode
eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usRegData, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr,
USHORT usNRegs, USHORT * pusDataBuffer, LONG lTimeOut );
USHORT usNRegs, USHORT * pusDataBuffer, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqReadWriteMultipleHoldingRegister( UCHAR ucSndAddr,
USHORT usReadRegAddr, USHORT usNReadRegs, USHORT * pusDataBuffer,
USHORT usWriteRegAddr, USHORT usNWriteRegs, LONG lTimeOut );
USHORT usReadRegAddr, USHORT usNReadRegs, USHORT * pusDataBuffer,
USHORT usWriteRegAddr, USHORT usNWriteRegs, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqReadCoils( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usNCoils, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqWriteCoil( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usCoilData, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr,
USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut );
USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut );
eMBMasterReqErrCode
eMBMasterReqReadDiscreteInputs( UCHAR ucSndAddr, USHORT usDiscreteAddr, USHORT usNDiscreteIn, LONG lTimeOut );
@ -408,6 +425,10 @@ BOOL xMBMasterRequestIsBroadcast( void );
eMBMasterErrorEventType eMBMasterGetErrorType( void );
void vMBMasterSetErrorType( eMBMasterErrorEventType errorType );
eMBMasterReqErrCode eMBMasterWaitRequestFinish( void );
eMBMode ucMBMasterGetCommMode( void );
BOOL xMBMasterGetLastTransactionInfo( uint64_t *pxTransId, UCHAR *pucDestAddress,
UCHAR *pucFunctionCode, UCHAR *pucException,
USHORT *pusErrorType );
/* ----------------------- 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
@ -113,8 +121,10 @@ PR_BEGIN_EXTERN_C
* transmitting the frame. If the master is to slow with enabling its
* receiver then he will not receive the response correctly.
*/
#ifndef MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS
#ifndef CONFIG_FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS
#define MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ( 0 )
#else
#define MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ( CONFIG_FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
#endif
/*! \brief Maximum number of Modbus functions codes the protocol stack
@ -167,7 +177,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

@ -40,6 +40,7 @@
#include "mbconfig.h" // for options
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif
@ -50,12 +51,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 +71,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 +91,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 +125,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 +156,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 +174,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
@ -195,16 +208,20 @@ void vMBMasterPortTimersDisable( void );
/* ----------------- Callback for the master error process ------------------*/
void vMBMasterErrorCBRespondTimeout( UCHAR ucDestAddress, const UCHAR* pucPDUData,
USHORT ucPDULength );
void vMBMasterErrorCBRespondTimeout( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucSendData, USHORT ucSendLength );
void vMBMasterErrorCBReceiveData( UCHAR ucDestAddress, const UCHAR* pucPDUData,
USHORT ucPDULength );
void vMBMasterErrorCBReceiveData( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength );
void vMBMasterErrorCBExecuteFunction( UCHAR ucDestAddress, const UCHAR* pucPDUData,
USHORT ucPDULength );
void vMBMasterErrorCBExecuteFunction( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength );
void vMBMasterCBRequestSuccess( void );
void vMBMasterCBRequestSuccess( uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength );
#endif
/* ----------------------- Callback for the protocol stack ------------------*/
/*!

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,14 +36,14 @@
*/
/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
#include <stdlib.h>
#include <string.h>
#include <stdatomic.h>
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb_m.h"
#include "mbconfig.h"
#include "mbframe.h"
@ -68,20 +68,29 @@
#define MB_PORT_HAS_CLOSE 1
#endif
/* ----------------------- Static variables ---------------------------------*/
static UCHAR ucMBMasterDestAddress;
static BOOL xMBRunInMasterMode = FALSE;
static volatile eMBMasterErrorEventType eMBMasterCurErrorType;
static volatile USHORT usMasterSendPDULength;
static volatile eMBMode eMBMasterCurrentMode;
/*------------------------ Shared variables ---------------------------------*/
_lock_t xMBMLock; // base modbus object lock
volatile UCHAR ucMasterSndBuf[MB_SERIAL_BUF_SIZE];
volatile UCHAR ucMasterRcvBuf[MB_SERIAL_BUF_SIZE];
volatile eMBMasterTimerMode eMasterCurTimerMode;
volatile BOOL xFrameIsBroadcast = FALSE;
static _Atomic USHORT usMasterSendPDULength = 0;
static _Atomic eMBMasterErrorEventType eMBMasterCurErrorType = EV_ERROR_INIT;
static _Atomic BOOL xMBRunInMasterMode = FALSE;
static _Atomic UCHAR ucMBMasterDestAddress = 0;
static _Atomic BOOL xFrameIsBroadcast = FALSE;
static _Atomic eMBMasterTimerMode eMasterCurTimerMode;
/* ----------------------- Static variables ---------------------------------*/
static uint64_t xCurTransactionId = 0;
static UCHAR *pucMBSendFrame = NULL;
static UCHAR *pucMBRecvFrame = NULL;
static UCHAR ucRecvAddress = 0;
static eMBMode eMBMasterCurrentMode;
/* The transaction information structure which keep last processing state */
static TransactionInfo_t xTransactionInfo = {0};
static enum
{
@ -171,13 +180,25 @@ eMBMasterTCPInit( USHORT ucTCPPort )
peMBMasterFrameSendCur = eMBMasterTCPSend;
pxMBMasterPortCBTimerExpired = xMBMasterTCPTimerExpired;
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterTCPPortClose : NULL;
ucMBMasterDestAddress = MB_TCP_PSEUDO_ADDRESS;
eMBMasterCurrentMode = MB_TCP;
eMBState = STATE_DISABLED;
ucRecvAddress = MB_TCP_PSEUDO_ADDRESS;
xCurTransactionId = 0;
xTransactionInfo.xTransId = 0;
xTransactionInfo.ucDestAddr = 0;
xTransactionInfo.ucFuncCode = 0;
xTransactionInfo.eException = MB_EX_NONE;
xTransactionInfo.ucFrameError = 0;
/* initialize the state values. */
atomic_init(&usMasterSendPDULength, 0);
atomic_init(&eMBMasterCurErrorType, EV_ERROR_INIT);
atomic_init(&xMBRunInMasterMode, FALSE);
atomic_init(&ucMBMasterDestAddress, MB_TCP_PSEUDO_ADDRESS);
// 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;
}
@ -204,7 +225,7 @@ eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eP
pxMBMasterFrameCBByteReceived = xMBMasterRTUReceiveFSM;
pxMBMasterFrameCBTransmitterEmpty = xMBMasterRTUTransmitFSM;
pxMBMasterPortCBTimerExpired = xMBMasterRTUTimerExpired;
eMBMasterCurrentMode = MB_ASCII;
eMBMasterCurrentMode = eMode;
eStatus = eMBMasterRTUInit(ucPort, ulBaudRate, eParity);
break;
@ -219,7 +240,7 @@ eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eP
pxMBMasterFrameCBByteReceived = xMBMasterASCIIReceiveFSM;
pxMBMasterFrameCBTransmitterEmpty = xMBMasterASCIITransmitFSM;
pxMBMasterPortCBTimerExpired = xMBMasterASCIITimerT1SExpired;
eMBMasterCurrentMode = MB_RTU;
eMBMasterCurrentMode = eMode;
eStatus = eMBMasterASCIIInit(ucPort, ulBaudRate, eParity );
break;
@ -239,6 +260,19 @@ eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eP
else
{
eMBState = STATE_DISABLED;
ucRecvAddress = 0;
xCurTransactionId = 0;
xTransactionInfo.xTransId = 0;
xTransactionInfo.ucDestAddr = 0;
xTransactionInfo.ucFuncCode = 0;
xTransactionInfo.eException = MB_EX_NONE;
xTransactionInfo.ucFrameError = 0;
/* initialize the state values. */
atomic_init(&usMasterSendPDULength, 0);
atomic_init(&eMBMasterCurErrorType, EV_ERROR_INIT);
atomic_init(&xMBRunInMasterMode, FALSE);
atomic_init(&ucMBMasterDestAddress, 0);
}
/* initialize the OS resource for modbus master. */
vMBMasterOsResInit();
@ -310,160 +344,192 @@ eMBMasterDisable( void )
eMBErrorCode
eMBMasterPoll( void )
{
static UCHAR *ucMBFrame = NULL;
static UCHAR ucRcvAddress;
static UCHAR ucFunctionCode;
static USHORT usLength;
static eMBException eException;
int i;
int j;
eMBErrorCode eStatus = MB_ENOERR;
eMBMasterEventType eEvent;
eMBMasterErrorEventType errorType;
int i;
int j;
eMBErrorCode eStatus = MB_ENOERR;
xMBMasterEventType xEvent;
eMBMasterErrorEventType errorType = EV_ERROR_INIT;
static eMBException eException = MB_EX_NONE;
static UCHAR ucFunctionCode = 0;
static USHORT usRecvLength = 0;
/* 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( &pucMBSendFrame );
ESP_LOG_BUFFER_HEX_LEVEL("POLL transmit buffer", (void*)pucMBSendFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
eStatus = peMBMasterFrameSendCur( ucMBMasterGetDestAddress(), pucMBSendFrame, 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*)pucMBSendFrame, 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( &ucRecvAddress, &pucMBRecvFrame, &usRecvLength);
if (xCurTransactionId == xEvent.xTransactionId) {
MB_PORT_CHECK(pucMBSendFrame, MB_EILLSTATE, "Send buffer initialization fail.");
// Check if the frame is for us. If not ,send an error process event.
if ( ( eStatus == MB_ENOERR ) && ( ( ucRecvAddress == ucMBMasterGetDestAddress() )
|| ( ucRecvAddress == MB_TCP_PSEUDO_ADDRESS) ) ) {
if ( ( pucMBRecvFrame[MB_PDU_FUNC_OFF] & ~MB_FUNC_ERROR ) == ( pucMBSendFrame[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*)pucMBRecvFrame, (uint16_t)usRecvLength, ESP_LOG_DEBUG);
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
} else {
ESP_LOGE( MB_PORT_TAG, "Drop incorrect frame, receive_func(%u) != send_func(%u)",
pucMBRecvFrame[MB_PDU_FUNC_OFF], pucMBSendFrame[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)ucRecvAddress, (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) {
if ( xMBMasterRequestIsBroadcast()
&& (( ucMBMasterGetCommMode() == MB_RTU ) || ( ucMBMasterGetCommMode() == MB_ASCII ) ) ) {
pucMBRecvFrame = pucMBSendFrame;
}
MB_PORT_CHECK(pucMBRecvFrame, MB_EILLSTATE, "receive buffer initialization fail.");
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_EXECUTE", xEvent.xTransactionId);
ucFunctionCode = pucMBRecvFrame[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)pucMBRecvFrame[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() ) {
USHORT usLength = usMBMasterGetPDUSndLength();
for(j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++)
{
vMBMasterSetDestAddress(j);
eException = xMasterFuncHandlers[i].pxHandler(pucMBRecvFrame, &usLength);
}
} else {
eException = xMasterFuncHandlers[i].pxHandler(pucMBRecvFrame, &usRecvLength);
}
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( &pucMBSendFrame );
switch ( errorType )
{
case EV_ERROR_RESPOND_TIMEOUT:
vMBMasterErrorCBRespondTimeout( xEvent.xTransactionId,
ucMBMasterGetDestAddress( ),
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_RECEIVE_DATA:
vMBMasterErrorCBReceiveData( xEvent.xTransactionId,
ucMBMasterGetDestAddress( ),
pucMBRecvFrame, usRecvLength,
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_EXECUTE_FUNCTION:
vMBMasterErrorCBExecuteFunction( xEvent.xTransactionId,
ucMBMasterGetDestAddress( ),
pucMBRecvFrame, usRecvLength,
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
break;
case EV_ERROR_OK:
vMBMasterCBRequestSuccess( xEvent.xTransactionId,
ucMBMasterGetDestAddress( ),
pucMBRecvFrame, usRecvLength,
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
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 );
MB_ATOMIC_SECTION {
xTransactionInfo.xTransId = xCurTransactionId;
xTransactionInfo.ucDestAddr = ucMBMasterGetDestAddress();
xTransactionInfo.ucFuncCode = ucFunctionCode;
xTransactionInfo.eException = eException;
xTransactionInfo.ucFrameError = errorType;
}
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,78 +538,104 @@ 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? */
BOOL MB_PORT_ISR_ATTR xMBMasterRequestIsBroadcast( void )
{
return xFrameIsBroadcast;
return atomic_load(&xFrameIsBroadcast);
}
/* 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;
}
/* Get current transaction information */
BOOL xMBMasterGetLastTransactionInfo( uint64_t *pxTransId, UCHAR *pucDestAddress,
UCHAR *pucFunctionCode, UCHAR *pucException,
USHORT *pusErrorType )
{
BOOL xState = (eMBState == STATE_ENABLED);
if (xState && pxTransId && pucDestAddress && pucFunctionCode
&& pucException && pusErrorType) {
MB_ATOMIC_SECTION {
*pxTransId = xTransactionInfo.xTransId;
*pucDestAddress = xTransactionInfo.ucDestAddr;
*pucFunctionCode = xTransactionInfo.ucFuncCode;
*pucException = xTransactionInfo.eException;
*pusErrorType = xTransactionInfo.ucFrameError;
}
}
return xState;
}
#endif // MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED

View File

@ -137,8 +137,11 @@ eMBRTUStart( void )
*/
eRcvState = STATE_RX_INIT;
vMBPortSerialEnable( TRUE, FALSE );
vMBPortTimersEnable( );
#if CONFIG_FMB_TIMER_PORT_ENABLED
vMBPortTimersEnable( );
#else
pxMBPortCBTimerExpired();
#endif
EXIT_CRITICAL_SECTION( );
}
@ -158,7 +161,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 +225,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

@ -39,7 +39,6 @@
/* ----------------------- Modbus includes ----------------------------------*/
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "sys/lock.h"
#include "port.h"
/* ----------------------- Variables ----------------------------------------*/
@ -47,13 +46,24 @@ static _lock_t s_port_lock;
static UCHAR ucPortMode = 0;
/* ----------------------- Start implementation -----------------------------*/
inline void
INLINE int lock_obj(_lock_t *plock)
{
_lock_acquire(plock);
return 1;
}
INLINE void unlock_obj(_lock_t *plock)
{
_lock_release(plock);
}
INLINE void
vMBPortEnterCritical(void)
{
_lock_acquire(&s_port_lock);
}
inline void
INLINE void
vMBPortExitCritical(void)
{
_lock_release(&s_port_lock);
@ -73,13 +83,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

@ -37,6 +37,8 @@
#ifndef PORT_COMMON_H_
#define PORT_COMMON_H_
#include "sys/lock.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h" // for queue
@ -52,7 +54,7 @@
#include "mbconfig.h"
#define INLINE inline
#define INLINE inline __attribute__((always_inline))
#define PR_BEGIN_EXTERN_C extern "C" {
#define PR_END_EXTERN_C }
@ -91,6 +93,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
@ -101,6 +107,8 @@
#define MB_TCP_DEBUG (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) // Enable legacy debug output in TCP module.
#define MB_ATTR_WEAK __attribute__ ((weak))
#define MB_TCP_GET_FIELD(buffer, field) ((USHORT)((buffer[field] << 8U) | buffer[field + 1]))
#define MB_PORT_CHECK(a, ret_val, str, ...) \
@ -109,6 +117,43 @@
return ret_val; \
}
// Macro to check if stack shutdown event is active
#define TCP_PORT_CHECK_SHDN(sema_ptr, callback_func) do { \
if (sema_ptr) { \
ESP_LOGD(TAG, "Shutdown stack from %s(%d)", __func__, __LINE__); \
callback_func(); \
} \
} while(0)
int lock_obj(_lock_t *plock);
void unlock_obj(_lock_t *plock);
#define CRITICAL_SECTION_INIT(lock) \
do \
{ \
_lock_init((_lock_t *)&lock); \
} while (0)
#define CRITICAL_SECTION_CLOSE(lock) \
do \
{ \
_lock_close((_lock_t *)&lock); \
} while (0)
#define CRITICAL_SECTION_LOCK(lock) \
do \
{ \
lock_obj((_lock_t *)&lock); \
} while (0)
#define CRITICAL_SECTION_UNLOCK(lock) \
do \
{ \
unlock_obj((_lock_t *)&lock); \
} while (0)
#define CRITICAL_SECTION(lock) for (int st = lock_obj((_lock_t *)&lock); (st > 0); unlock_obj((_lock_t *)&lock), st = -1)
#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif /* __cplusplus */
@ -186,6 +231,22 @@ UCHAR ucMBPortGetMode( void );
BOOL xMBPortSerialWaitEvent(QueueHandle_t xMbUartQueue, uart_event_t* pxEvent, ULONG xTimeout);
/**
* This is modbus master user error handling funcion.
* If it is defined in the user application, then helps to handle the errors
* and received/sent buffers to transfer as well as handle the slave exception codes.
*
* @param xTransId - the identification of the trasaction
* @param ucDestAddress destination salve address
* @param usError - the error code, see the enumeration eMBMasterErrorEventType
* @param pucRecvData current receive data pointer
* @param ucRecvLength current length of receive buffer
* @param pucSendData Send buffer data
* @param ucSendLength Send buffer length
*/
void vMBMasterErrorCBUserHandler( uint64_t xTransId, USHORT usError, UCHAR ucDestAddress, const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength ) MB_ATTR_WEAK;
#ifdef __cplusplus
PR_END_EXTERN_C
#endif /* __cplusplus */

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,23 @@
*/
/* ----------------------- 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 +59,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 _Atomic uint64_t xTransactionID = 0;
/* ----------------------- Start implementation -----------------------------*/
@ -79,45 +76,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");
atomic_init(&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 +141,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,58 +166,73 @@ 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__);
}
}
/**
* This is modbus master respond timeout error process callback function.
* @note There functions will block modbus master poll while execute OS waiting.
*
* @param xTransId - the identification of the trasaction
* @param ucDestAddress destination salve address
* @param pucPDUData PDU buffer data
* @param ucPDULength PDU buffer length
* @param pucRecvData current receive data pointer
* @param ucRecvLength current length of receive buffer
* @param pucSendData Send buffer data
* @param ucSendLength Send buffer length
*
*/
void vMBMasterErrorCBRespondTimeout(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
void vMBMasterErrorCBRespondTimeout(uint64_t xTransId, UCHAR ucDestAddress, const UCHAR* pucSendData, USHORT ucSendLength)
{
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__);
if (vMBMasterErrorCBUserHandler) {
vMBMasterErrorCBUserHandler( xTransId,
(USHORT)EV_ERROR_RESPOND_TIMEOUT, ucDestAddress,
NULL, 0,
pucSendData, ucSendLength );
}
}
/**
* This is modbus master receive data error process callback function.
* @note There functions will block modbus master poll while execute OS waiting.
*
* @param xTransId - the identification of the trasaction
* @param ucDestAddress destination salve address
* @param pucPDUData PDU buffer data
* @param ucPDULength PDU buffer length
* @param pucRecvData current receive data pointer
* @param ucRecvLength current length of receive buffer
* @param pucSendData Send buffer data
* @param ucSendLength Send buffer length
*/
void vMBMasterErrorCBReceiveData(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
void vMBMasterErrorCBReceiveData(uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength)
{
BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_RECEIVE_DATA);
MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_RECEIVE_DATA' failed!", __func__);
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);
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_RECEIVE_DATA );
ESP_LOGD(MB_PORT_TAG,"%s:Callback receive data failure.", __func__);
if (vMBMasterErrorCBUserHandler) {
vMBMasterErrorCBUserHandler( xTransId,
(USHORT)EV_ERROR_RECEIVE_DATA, ucDestAddress,
pucRecvData, ucRecvLength,
pucSendData, ucSendLength );
}
}
/**
@ -233,32 +240,52 @@ void vMBMasterErrorCBReceiveData(UCHAR ucDestAddress, const UCHAR* pucPDUData, U
* @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.
*
* @param xTransId - the identification of the trasaction
* @param ucDestAddress destination salve address
* @param pucPDUData PDU buffer data
* @param ucPDULength PDU buffer length
* @param pucRecvData current receive data pointer
* @param ucRecvLength current length of receive buffer
* @param pucSendData Send buffer data
* @param ucSendLength Send buffer length
*
*/
void vMBMasterErrorCBExecuteFunction(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
void vMBMasterErrorCBExecuteFunction(uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength)
{
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);
if (vMBMasterErrorCBUserHandler) {
vMBMasterErrorCBUserHandler( xTransId,
(USHORT)EV_ERROR_EXECUTE_FUNCTION, ucDestAddress,
pucRecvData, ucRecvLength,
pucSendData, ucSendLength );
}
}
/**
* This is modbus master request process success callback function.
* @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.
*
* @param xTransId - the identification of the trasaction
* @param ucDestAddress destination salve address
* @param pucRecvData current receive data pointer
* @param ucRecvLength current length of receive buffer
* @param pucSendData Send buffer data
* @param ucSendLength Send buffer length
*/
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(uint64_t xTransId, UCHAR ucDestAddress,
const UCHAR* pucRecvData, USHORT ucRecvLength,
const UCHAR* pucSendData, USHORT ucSendLength)
{
(void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_PROCESS_SUCCESS );
ESP_LOGD(MB_PORT_TAG,"%s: Callback request success.", __func__);
if (vMBMasterErrorCBUserHandler) {
vMBMasterErrorCBUserHandler( xTransId,
(USHORT)EV_ERROR_OK, ucDestAddress,
pucRecvData, ucRecvLength,
pucSendData, ucSendLength );
}
}
/**
@ -272,19 +299,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 +324,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 +337,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

@ -125,3 +125,8 @@ void vMBPortTimerClose(void)
}
#endif
}
void vMBPortTimersDelay(USHORT usTimeOutMS)
{
vTaskDelay(usTimeOutMS / portTICK_PERIOD_MS);
}

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;
@ -358,13 +358,17 @@ static esp_err_t mbc_serial_master_set_request(char* name, mb_param_mode_t mode,
// Compare the name of parameter with parameter key from table
int comp_result = memcmp((const void*)name, (const void*)reg_ptr->param_key, (size_t)param_key_len);
if (comp_result == 0) {
// Returns an error in case of broadcast read request
if (!reg_ptr->mb_slave_addr && (mode == MB_PARAM_READ)) {
error = ESP_ERR_INVALID_ARG;
break;
}
// The correct line is found in the table and reg_ptr points to the found parameter description
request->slave_addr = reg_ptr->mb_slave_addr;
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
@ -376,69 +380,99 @@ static esp_err_t mbc_serial_master_set_request(char* name, mb_param_mode_t mode,
return error;
}
// Get parameter data for corresponding characteristic
static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name,
uint8_t* value_ptr, uint8_t *type)
static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type)
{
MB_MASTER_CHECK((name != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
MB_MASTER_CHECK((type != NULL),
ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
MB_MASTER_CHECK((type != NULL), ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
MB_MASTER_CHECK((value != NULL), ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
mb_param_request_t request ;
mb_parameter_descriptor_t reg_info = { 0 };
uint8_t* pdata = NULL;
error = mbc_serial_master_set_request(name, MB_PARAM_READ, &request, &reg_info);
if ((error == ESP_OK) && (cid == reg_info.cid)) {
// Send request to read characteristic data
error = mbc_serial_master_send_request(&request, value_ptr);
MB_MASTER_CHECK(((reg_info.mb_size << 1) >= reg_info.param_size),
MB_EILLSTATE,
"Incorrect characteristic data.");
// alloc buffer to store parameter data
pdata = calloc(1, (reg_info.mb_size << 1));
if (!pdata) {
return ESP_ERR_INVALID_STATE;
}
error = mbc_serial_master_send_request(&request, pdata);
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));
// If data pointer is NULL then we don't need to set value (it is still in the cache of cid)
if (value != NULL) {
error = mbc_master_set_param_data((void*)value, (void*)pdata,
reg_info.param_type, reg_info.param_size);
if (error != ESP_OK) {
ESP_LOGE(TAG, "fail to set parameter data.");
error = ESP_ERR_INVALID_STATE;
} else {
ESP_LOGD(TAG, "%s: Good response for get cid(%u) = %s",
__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));
}
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 requested cid(%u) is not configured correctly. Check data dictionary for correctness.",
__FUNCTION__, (unsigned)cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
}
// Set parameter value for characteristic selected by name and cid
static esp_err_t mbc_serial_master_set_parameter(uint16_t cid, char* name,
uint8_t* value_ptr, uint8_t *type)
static esp_err_t mbc_serial_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type)
{
MB_MASTER_CHECK((name != NULL),
ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
MB_MASTER_CHECK((value_ptr != NULL),
ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
MB_MASTER_CHECK((type != NULL),
ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
MB_MASTER_CHECK((value != NULL), ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
MB_MASTER_CHECK((type != NULL), ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
mb_param_request_t request ;
mb_parameter_descriptor_t reg_info = { 0 };
uint8_t* pdata = NULL;
error = mbc_serial_master_set_request(name, MB_PARAM_WRITE, &request, &reg_info);
if ((error == ESP_OK) && (cid == reg_info.cid)) {
MB_MASTER_CHECK(((reg_info.mb_size << 1) >= reg_info.param_size),
MB_EILLSTATE,
"Incorrect characteristic data.");
pdata = calloc(1, (reg_info.mb_size << 1)); // alloc parameter buffer
if (!pdata) {
return ESP_ERR_INVALID_STATE;
}
// Transfer value of characteristic into parameter buffer
error = mbc_master_set_param_data((void*)pdata, (void*)value,
reg_info.param_type, reg_info.param_size);
if (error != ESP_OK) {
ESP_LOGE(TAG, "fail to set parameter data.");
free(pdata);
return ESP_ERR_INVALID_STATE;
}
// Send request to write characteristic data
error = mbc_serial_master_send_request(&request, value_ptr);
error = mbc_serial_master_send_request(&request, pdata);
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;
free(pdata);
} else {
ESP_LOGE(TAG, "%s: The requested cid(%u) not found in the data dictionary.",
__FUNCTION__, reg_info.cid);
ESP_LOGE(TAG, "%s: The requested cid(%u) is not configured correctly. Check data dictionary for correctness.",
__FUNCTION__, (unsigned)cid);
error = ESP_ERR_INVALID_ARG;
}
return error;
@ -550,9 +584,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;
@ -567,7 +601,7 @@ eMBErrorCode eMBRegCoilsCBSerialMaster(UCHAR* pucRegBuffer, USHORT usAddress,
switch (eMode) {
case MB_REG_WRITE:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits((UCHAR*)pucRegCoilsBuf, iRegIndex, 1);
UCHAR ucResult = xMBUtilGetBits(pucRegCoilsBuf, iRegIndex - (usAddress % 8), 1);
xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress % 8) , 1, ucResult);
iRegIndex++;
usCoils--;
@ -576,7 +610,7 @@ eMBErrorCode eMBRegCoilsCBSerialMaster(UCHAR* pucRegBuffer, USHORT usAddress,
case MB_REG_READ:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits(pucRegBuffer, iRegIndex - (usAddress % 8), 1);
xMBUtilSetBits((uint8_t*)pucRegCoilsBuf, iRegIndex, 1, ucResult);
xMBUtilSetBits(pucRegCoilsBuf, iRegIndex - (usAddress % 8), 1, ucResult);
iRegIndex++;
usCoils--;
}
@ -622,7 +656,7 @@ eMBErrorCode eMBRegDiscreteCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress
iRegBitIndex = (USHORT)(usAddress) % 8; // Get bit index
while (iNReg > 1)
{
xMBUtilSetBits(pucDiscreteInputBuf++, iRegBitIndex, 8, *pucRegBuffer++);
xMBUtilSetBits(pucDiscreteInputBuf++, iRegBitIndex - (usAddress % 8), 8, *pucRegBuffer++);
iNReg--;
}
// last discrete
@ -630,7 +664,7 @@ eMBErrorCode eMBRegDiscreteCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress
// xMBUtilSetBits has bug when ucNBits is zero
if (usNDiscrete != 0)
{
xMBUtilSetBits(pucDiscreteInputBuf, iRegBitIndex, usNDiscrete, *pucRegBuffer++);
xMBUtilSetBits(pucDiscreteInputBuf, iRegBitIndex - (usAddress % 8), usNDiscrete, *pucRegBuffer++);
}
} else {
eStatus = MB_ENOREG;
@ -675,8 +709,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;
@ -181,7 +181,6 @@ static esp_err_t mbc_tcp_master_start(void)
return ESP_OK;
}
// Modbus controller destroy function
static esp_err_t mbc_tcp_master_destroy(void)
{
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
@ -189,17 +188,21 @@ static esp_err_t mbc_tcp_master_destroy(void)
MB_MASTER_CHECK((mbm_opts != NULL), ESP_ERR_INVALID_ARG, "mb incorrect options pointer.");
eMBErrorCode mb_error = MB_ENOERR;
// Disable and then destroy the Modbus stack
mb_error = eMBMasterDisable();
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);
// Stop polling by clearing correspondent bit in the event group
xEventGroupClearBits(mbm_opts->mbm_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
// Disable and then destroy the Modbus port
mb_error = eMBMasterDisable();
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
(void)vTaskDelete(mbm_opts->mbm_task_handle);
mbm_opts->mbm_task_handle = NULL;
mbm_opts->mbm_task_handle = NULL;
mb_error = eMBMasterClose();
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack close failure returned (0x%x).", (int)mb_error);
(void)vEventGroupDelete(mbm_opts->mbm_event_group);
mbm_opts->mbm_event_group = NULL;
mbc_tcp_master_free_slave_list();
@ -237,7 +240,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 +258,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 +350,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,49 +394,16 @@ 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;
}
// Helper function to set parameter buffer according to its type
static esp_err_t mbc_tcp_master_set_param_data(void* dest, void* src, mb_descr_type_t param_type, size_t param_size)
{
esp_err_t err = ESP_OK;
MB_MASTER_CHECK((dest != NULL), ESP_ERR_INVALID_ARG, "incorrect parameter pointer.");
MB_MASTER_CHECK((src != NULL), ESP_ERR_INVALID_ARG, "incorrect parameter pointer.");
// Transfer parameter data into value of characteristic
switch(param_type)
{
case PARAM_TYPE_U8:
*((uint8_t*)dest) = *((uint8_t*)src);
break;
case PARAM_TYPE_U16:
*((uint16_t*)dest) = *((uint16_t*)src);
break;
case PARAM_TYPE_U32:
*((uint32_t*)dest) = *((uint32_t*)src);
break;
case PARAM_TYPE_FLOAT:
*((float*)dest) = *(float*)src;
break;
case PARAM_TYPE_ASCII:
memcpy((void*)dest, (void*)src, (size_t)param_size);
break;
default:
ESP_LOGE(TAG, "%s: Incorrect param type (%u).",
__FUNCTION__, (uint16_t)param_type);
err = ESP_ERR_NOT_SUPPORTED;
break;
}
return err;
}
// Helper to search parameter by name in the parameter description table and fills Modbus request fields accordingly
static esp_err_t mbc_tcp_master_set_request(char* name, mb_param_mode_t mode, mb_param_request_t* request,
mb_parameter_descriptor_t* reg_data)
@ -491,7 +465,7 @@ static esp_err_t mbc_tcp_master_get_parameter(uint16_t cid, char* name, uint8_t*
if (error == ESP_OK) {
// If data pointer is NULL then we don't need to set value (it is still in the cache of cid)
if (value != NULL) {
error = mbc_tcp_master_set_param_data((void*)value, (void*)pdata,
error = mbc_master_set_param_data((void*)value, (void*)pdata,
reg_info.param_type, reg_info.param_size);
if (error != ESP_OK) {
ESP_LOGE(TAG, "fail to set parameter data.");
@ -503,15 +477,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;
@ -536,7 +508,7 @@ static esp_err_t mbc_tcp_master_set_parameter(uint16_t cid, char* name, uint8_t*
return ESP_ERR_INVALID_STATE;
}
// Transfer value of characteristic into parameter buffer
error = mbc_tcp_master_set_param_data((void*)pdata, (void*)value,
error = mbc_master_set_param_data((void*)pdata, (void*)value,
reg_info.param_type, reg_info.param_size);
if (error != ESP_OK) {
ESP_LOGE(TAG, "fail to set parameter data.");
@ -550,14 +522,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;
@ -680,7 +652,7 @@ eMBErrorCode eMBRegCoilsCBTcpMaster(UCHAR *pucRegBuffer, USHORT usAddress,
switch (eMode) {
case MB_REG_WRITE:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits((UCHAR*)pucRegCoilsBuf, iRegIndex, 1);
UCHAR ucResult = xMBUtilGetBits(pucRegCoilsBuf, iRegIndex - (usAddress % 8), 1);
xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress % 8) , 1, ucResult);
iRegIndex++;
usCoils--;
@ -689,7 +661,7 @@ eMBErrorCode eMBRegCoilsCBTcpMaster(UCHAR *pucRegBuffer, USHORT usAddress,
case MB_REG_READ:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits(pucRegBuffer, iRegIndex - (usAddress % 8), 1);
xMBUtilSetBits((uint8_t*)pucRegCoilsBuf, iRegIndex, 1, ucResult);
xMBUtilSetBits(pucRegCoilsBuf, iRegIndex - (usAddress % 8), 1, ucResult);
iRegIndex++;
usCoils--;
}
@ -733,7 +705,7 @@ eMBErrorCode eMBRegDiscreteCBTcpMaster(UCHAR * pucRegBuffer, USHORT usAddress,
iRegBitIndex = (USHORT)(usAddress) % 8; // Get bit index
while (iNReg > 1)
{
xMBUtilSetBits(pucDiscreteInputBuf++, iRegBitIndex, 8, *pucRegBuffer++);
xMBUtilSetBits(pucDiscreteInputBuf++, iRegBitIndex - (usAddress % 8), 8, *pucRegBuffer++);
iNReg--;
}
// last discrete
@ -741,7 +713,7 @@ eMBErrorCode eMBRegDiscreteCBTcpMaster(UCHAR * pucRegBuffer, USHORT usAddress,
// xMBUtilSetBits has bug when ucNBits is zero
if (usNDiscrete != 0)
{
xMBUtilSetBits(pucDiscreteInputBuf, iRegBitIndex, usNDiscrete, *pucRegBuffer++);
xMBUtilSetBits(pucDiscreteInputBuf, iRegBitIndex - (usAddress % 8), usNDiscrete, *pucRegBuffer++);
}
} else {
eStatus = MB_ENOREG;
@ -782,8 +754,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

@ -68,19 +68,20 @@
#define MB_EVENT_REQ_ERR_MASK ( EV_MASTER_PROCESS_SUCCESS )
#define MB_EVENT_WAIT_TOUT_MS ( 3000 )
#define MB_SHDN_WAIT_TOUT_MS ( 5000 )
#define MB_TCP_READ_TICK_MS ( 1 )
#define MB_TCP_READ_BUF_RETRY_CNT ( 4 )
#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";
static MbPortConfig_t xMbPortConfig;
static EventGroupHandle_t xMasterEventHandle = NULL;
static SemaphoreHandle_t xShutdownSemaphore = NULL;
static SemaphoreHandle_t xShutdownSema = NULL;
static EventBits_t xMasterEvent = 0;
/* ----------------------- Static functions ---------------------------------*/
@ -94,10 +95,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 +130,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);
"mbm_port_tcp_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.");
@ -143,12 +144,10 @@ xMBMasterTCPPortInit( USHORT usTCPPort )
ESP_LOGI(TAG, "TCP master stack initialized.");
bOkay = TRUE;
}
vTaskSuspend(xMbPortConfig.xMbTcpTaskHandle);
return bOkay;
}
static MbSlaveInfo_t* vMBTCPPortMasterFindSlaveInfo(UCHAR ucSlaveAddr)
static MbSlaveInfo_t *vMBTCPPortMasterFindSlaveInfo(UCHAR ucSlaveAddr)
{
int xIndex;
BOOL xFound = false;
@ -161,12 +160,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,32 +216,44 @@ static void vMBTCPPortMasterMStoTimeVal(USHORT usTimeoutMs, struct timeval *tv)
tv->tv_usec = (usTimeoutMs - (tv->tv_sec * 1000)) * 1000;
}
static void xMBTCPPortMasterCheckShutdown(void) {
// First check if the task is not flagged for shutdown
if (xShutdownSemaphore) {
xSemaphoreGive(xShutdownSemaphore);
vTaskDelete(NULL);
}
}
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)
static void xMBTCPPortMasterShutdown(void)
{
xSemaphoreGive(xShutdownSema);
for (USHORT ucCnt = 0; ucCnt < MB_TCP_PORT_MAX_CONN; ucCnt++) {
MbSlaveInfo_t* pxInfo = xMbPortConfig.pxMbSlaveInfo[ucCnt];
if (pxInfo) {
xMBTCPPortMasterCloseConnection(pxInfo);
if (pxInfo->pucRcvBuf) {
free(pxInfo->pucRcvBuf);
}
free(pxInfo);
xMbPortConfig.pxMbSlaveInfo[ucCnt] = NULL;
}
}
free(xMbPortConfig.pxMbSlaveInfo);
vTaskDelete(NULL);
xMbPortConfig.xMbTcpTaskHandle = NULL;
}
void vMBTCPPortMasterSetNetOpt(void *pvNetIf, eMBPortIpVer xIpVersion, eMBPortProto xProto)
{
xMbPortConfig.pvNetIface = pvNetIf;
xMbPortConfig.eMbProto = xProto;
@ -250,7 +261,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 +272,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 +292,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);
TCP_PORT_CHECK_SHDN(xShutdownSema, xMBTCPPortMasterShutdown);
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 +331,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 +345,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 +377,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 +399,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 +432,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 +446,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 +461,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 +503,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 +515,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 +530,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 +569,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 +592,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 +605,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 +620,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 +635,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 +652,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 +665,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 +674,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;
@ -672,28 +688,30 @@ static void vMBTCPPortMasterTask(void *pvParameters)
while (1) {
MbSlaveAddrInfo_t xSlaveAddrInfo = { 0 };
BaseType_t xStatus = xQueueReceive(xMbPortConfig.xConnectQueue, (void*)&xSlaveAddrInfo, pdMS_TO_TICKS(MB_EVENT_WAIT_TOUT_MS));
xMBTCPPortMasterCheckShutdown();
TCP_PORT_CHECK_SHDN(xShutdownSema, xMBTCPPortMasterShutdown);
if (xStatus != pdTRUE) {
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,13 +747,15 @@ 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;
}
break;
}
putchar(ucDot);
// if we don't yield we run the risk of hogging CPU
vTaskDelay(pdMS_TO_TICKS(MB_TCP_CONNECTION_TIMEOUT_MS));
xErr = xMBTCPPortMasterConnect(pxInfo);
switch(xErr)
{
@ -744,9 +764,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,29 +778,29 @@ 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) {
pxInfo->xError = xErr;
}
xMBTCPPortMasterCheckShutdown();
TCP_PORT_CHECK_SHDN(xShutdownSema, xMBTCPPortMasterShutdown);
}
}
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
@ -789,6 +809,7 @@ static void vMBTCPPortMasterTask(void *pvParameters)
xReadSet = xConnSet;
// Check transmission event to clear appropriate bit.
xMBMasterPortFsmWaitConfirmation(EV_MASTER_FRAME_TRANSMIT, pdMS_TO_TICKS(MB_EVENT_WAIT_TOUT_MS));
TCP_PORT_CHECK_SHDN(xShutdownSema, xMBTCPPortMasterShutdown);
// Synchronize state machine with send packet event
if (xMBMasterPortFsmWaitConfirmation(EV_MASTER_FRAME_SENT, pdMS_TO_TICKS(MB_EVENT_WAIT_TOUT_MS))) {
ESP_LOGD(TAG, "FSM Synchronized with sent event.");
@ -797,36 +818,36 @@ 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();
TCP_PORT_CHECK_SHDN(xShutdownSema, xMBTCPPortMasterShutdown);
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));
xMBTCPPortMasterCheckShutdown();
TCP_PORT_CHECK_SHDN(xShutdownSema, xMBTCPPortMasterShutdown);
continue;
} 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));
// Stop polling process
vMBTCPPortMasterStopPoll();
xMBTCPPortMasterCheckShutdown();
TCP_PORT_CHECK_SHDN(xShutdownSema, xMBTCPPortMasterShutdown);
// Check disconnected slaves, do not need a result just to print information.
xMBTCPPortMasterCheckConnState(&xConnSet);
break;
@ -844,39 +865,40 @@ 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();
TCP_PORT_CHECK_SHDN(xShutdownSema, xMBTCPPortMasterShutdown);
// Check disconnected slaves, do not need a result just to print information.
xMBTCPPortMasterCheckConnState(&xConnSet);
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();
TCP_PORT_CHECK_SHDN(xShutdownSema, xMBTCPPortMasterShutdown);
} // while(usMbSlaveInfoCount)
} // while (1)
vTaskDelete(NULL);
@ -885,43 +907,28 @@ 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
xShutdownSemaphore = xSemaphoreCreateBinary();
xShutdownSema = xSemaphoreCreateBinary();
// if no semaphore (alloc issues) or couldn't acquire it, just delete the task
if (xShutdownSemaphore == NULL || xSemaphoreTake(xShutdownSemaphore, pdMS_TO_TICKS(MB_EVENT_WAIT_TOUT_MS)) != pdTRUE) {
if (xShutdownSema == NULL || xSemaphoreTake(xShutdownSema, pdMS_TO_TICKS(MB_SHDN_WAIT_TOUT_MS)) != pdTRUE) {
ESP_LOGW(TAG, "Modbus port task couldn't exit gracefully within timeout -> abruptly deleting the task.");
vTaskDelete(xMbPortConfig.xMbTcpTaskHandle);
}
if (xShutdownSemaphore) {
vSemaphoreDelete(xShutdownSemaphore);
xShutdownSemaphore = NULL;
if (xShutdownSema) {
vSemaphoreDelete(xShutdownSema);
xShutdownSema = NULL;
}
for (USHORT ucCnt = 0; ucCnt < MB_TCP_PORT_MAX_CONN; ucCnt++) {
MbSlaveInfo_t* pxInfo = xMbPortConfig.pxMbSlaveInfo[ucCnt];
if (pxInfo) {
xMBTCPPortMasterCloseConnection(pxInfo);
if (pxInfo->pucRcvBuf) {
free(pxInfo->pucRcvBuf);
}
free(pxInfo);
xMbPortConfig.pxMbSlaveInfo[ucCnt] = NULL;
}
}
free(xMbPortConfig.pxMbSlaveInfo);
}
void
vMBMasterTCPPortClose(void)
void vMBMasterTCPPortClose(void)
{
vQueueDelete(xMbPortConfig.xConnectQueue);
vMBMasterPortTimerClose();
@ -929,10 +936,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 +952,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 +1007,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 +1019,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 +1032,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,16 +170,16 @@ 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",
"mbs_port_tcp_task",
MB_CONTROLLER_STACK_SIZE,
NULL,
MB_CONTROLLER_PRIORITY,
@ -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 ---------------------------------------*/
@ -70,7 +70,7 @@ void vMBPortEventClose( void );
/* ----------------------- Static variables ---------------------------------*/
static const char *TAG = "MB_TCP_SLAVE_PORT";
static int xListenSock = -1;
static SemaphoreHandle_t xShutdownSemaphore = NULL;
static SemaphoreHandle_t xShutdownSema = NULL;
static MbSlavePortConfig_t xConfig = { 0 };
/* ----------------------- Static functions ---------------------------------*/
@ -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,13 +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);
vTaskSuspend(xConfig.xMbTcpTaskHandle);
&xConfig.xMbTcpTaskHandle,
MB_PORT_TASK_AFFINITY);
if (xErr != pdTRUE)
{
ESP_LOGE(TAG, "Server task creation failure.");
@ -192,7 +193,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 +205,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 +225,16 @@ 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);
// Empty tcp buffer before shutdown
(void)recv(pxInfo->xSockId, &pxInfo->pucTCPBuf[0], MB_PDU_SIZE_MAX, MSG_DONTWAIT);
if (shutdown(pxInfo->xSockId, SHUT_RDWR) == -1)
{
ESP_LOGE(TAG, "Socket (#%d), shutdown failed: errno %u", (int)pxInfo->xSockId, (unsigned)errno);
}
close(pxInfo->xSockId);
pxInfo->xSockId = -1;
@ -236,7 +246,37 @@ static BOOL xMBTCPPortCloseConnection(MbClientInfo_t* pxInfo)
return TRUE;
}
static int xMBTCPPortRxPoll(MbClientInfo_t* pxClientInfo, ULONG xTimeoutMs)
static void vMBTCPPortFreeClientInfo(MbClientInfo_t *pxClientInfo)
{
if (pxClientInfo) {
if (pxClientInfo->pucTCPBuf) {
free((void *)pxClientInfo->pucTCPBuf);
}
if (pxClientInfo->pcIpAddr) {
free((void *)pxClientInfo->pcIpAddr);
}
free((void *)pxClientInfo);
}
}
static void vMBTCPPortShutdown(void)
{
xSemaphoreGive(xShutdownSema);
vTaskDelete(NULL);
xConfig.xMbTcpTaskHandle = NULL;
for (int i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
MbClientInfo_t *pxClientInfo = xConfig.pxMbClientInfo[i];
if ((pxClientInfo != NULL) && (pxClientInfo->xSockId > 0)) {
xMBTCPPortCloseConnection(pxClientInfo);
vMBTCPPortFreeClientInfo(pxClientInfo);
xConfig.pxMbClientInfo[i] = NULL;
}
}
free(xConfig.pxMbClientInfo);
}
static int xMBTCPPortRxPoll(MbClientInfo_t *pxClientInfo, ULONG xTimeoutMs)
{
int xRet = ERR_CLSD;
struct timeval xTimeVal;
@ -257,32 +297,34 @@ static int xMBTCPPortRxPoll(MbClientInfo_t* pxClientInfo, ULONG xTimeoutMs)
{
// If select an error occurred
xRet = ERR_CLSD;
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
break;
} 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;
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
break;
}
}
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 +348,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 +433,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 +441,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;
}
@ -408,21 +450,6 @@ vMBTCPPortBindAddr(const CHAR* pcBindIp)
return(xListenSockFd);
}
static void
vMBTCPPortFreeClientInfo(MbClientInfo_t* pxClientInfo)
{
if (pxClientInfo) {
if (pxClientInfo->pucTCPBuf) {
free((void*)pxClientInfo->pucTCPBuf);
}
if (pxClientInfo->pcIpAddr) {
free((void*)pxClientInfo->pcIpAddr);
}
free((void*)pxClientInfo);
}
}
static void vMBTCPPortServerTask(void *pvParameters)
{
int xErr = 0;
@ -436,14 +463,15 @@ static void vMBTCPPortServerTask(void *pvParameters)
// Create listen socket
xListenSock = vMBTCPPortBindAddr(xConfig.pcBindAddr);
if (xListenSock < 0) {
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
continue;
}
// Connections handling cycle
while (1) {
//clear the socket set
// clear the socket set
FD_ZERO(&xReadSet);
//add master socket to set
// add master socket to set
FD_SET(xListenSock, &xReadSet);
int xMaxSd = xListenSock;
xConfig.usClientCount = 0;
@ -452,6 +480,7 @@ static void vMBTCPPortServerTask(void *pvParameters)
// Initialize read set and file descriptor according to
// all registered connected clients
for (i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
if ((xConfig.pxMbClientInfo[i] != NULL) && (xConfig.pxMbClientInfo[i]->xSockId > 0)) {
// calculate max file descriptor for select
xMaxSd = (xConfig.pxMbClientInfo[i]->xSockId > xMaxSd) ?
@ -461,20 +490,19 @@ static void vMBTCPPortServerTask(void *pvParameters)
}
}
// Wait for an activity on one of the sockets, timeout is NULL, so wait indefinitely
xErr = select(xMaxSd + 1 , &xReadSet , NULL , NULL , NULL);
vxMBTCPPortMStoTimeVal(MB_TCP_RESP_TIMEOUT_MS, &xTimeVal);
// Wait for an activity on one of the sockets during timeout
xErr = select(xMaxSd + 1, &xReadSet, NULL, NULL, &xTimeVal);
if ((xErr < 0) && (errno != EINTR)) {
// First check if the task is not flagged for shutdown
if (xListenSock == -1 && xShutdownSemaphore) {
xSemaphoreGive(xShutdownSemaphore);
vTaskDelete(NULL);
}
// error occurred during wait for read
ESP_LOGE(TAG, "select() errno = %d.", errno);
ESP_LOGE(TAG, "select() errno = %u.", (unsigned)errno);
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
continue;
} else if (xErr == 0) {
// If timeout happened, something is wrong
ESP_LOGE(TAG, "select() timeout, errno = %d.", errno);
ESP_LOGD(TAG, "select() timeout, errno = %u.", (unsigned)errno);
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
}
// If something happened on the master socket, then its an incoming connection.
@ -490,7 +518,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 +532,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 +540,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,25 +574,22 @@ 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);
}
TCP_PORT_CHECK_SHDN(xShutdownSema, vMBTCPPortShutdown);
// Close client connection
xMBTCPPortCloseConnection(pxClientInfo);
@ -588,35 +613,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 +649,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)
@ -638,46 +663,36 @@ static void vMBTCPPortServerTask(void *pvParameters)
void
vMBTCPPortClose( )
{
// Release resources for the event queue.
// 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
xShutdownSemaphore = xSemaphoreCreateBinary();
vTaskResume(xConfig.xMbTcpTaskHandle);
if (xShutdownSemaphore == NULL || // if no semaphore (alloc issues) or couldn't acquire it, just delete the task
xSemaphoreTake(xShutdownSemaphore, 2*pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND)) != pdTRUE) {
xShutdownSema = xSemaphoreCreateBinary();
if (xShutdownSema == NULL || // if no semaphore (alloc issues) or couldn't acquire it, just delete the task
xSemaphoreTake(xShutdownSema, 2 * pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND)) != pdTRUE) {
ESP_LOGE(TAG, "Task couldn't exit gracefully within timeout -> abruptly deleting the task");
vTaskDelete(xConfig.xMbTcpTaskHandle);
}
if (xShutdownSemaphore) {
vSemaphoreDelete(xShutdownSemaphore);
xShutdownSemaphore = NULL;
}
vMBPortEventClose( );
close(xListenSock);
xListenSock = -1;
vMBTCPPortRespQueueDelete(xConfig.xRespQueueHandle);
if (xShutdownSema) {
vSemaphoreDelete(xShutdownSema);
xShutdownSema = NULL;
}
vMBPortEventClose();
}
void vMBTCPPortEnable( void )
{
vTaskResume(xConfig.xMbTcpTaskHandle);
}
void
vMBTCPPortDisable( void )
{
vTaskSuspend(xConfig.xMbTcpTaskHandle);
for (int i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
MbClientInfo_t* pxClientInfo = xConfig.pxMbClientInfo[i];
if ((pxClientInfo != NULL) && (pxClientInfo->xSockId > 0)) {
xMBTCPPortCloseConnection(pxClientInfo);
vMBTCPPortFreeClientInfo(pxClientInfo);
xConfig.pxMbClientInfo[i] = NULL;
}
}
free(xConfig.pxMbClientInfo);
close(xListenSock);
xListenSock = -1;
vMBTCPPortRespQueueDelete(xConfig.xRespQueueHandle);
}
BOOL
@ -714,8 +729,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 +741,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,13 @@
version: "1.0.1"
version: "1.0.15"
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"
- "arch"
- "arch/**/*"

51
pytest.ini Normal file
View File

@ -0,0 +1,51 @@
[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
esp32c6: support esp32c6 target
esp32h2: support esp32h2 target
esp32p4: support esp32p4 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
multi_dut_modbus_serial: Modbus SERIAL runner with two duts (master, slave - additional)
multi_dut_modbus_generic: Modbus generic runner
# 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,23 @@
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
test_apps/ext_types:
disable:
- if: IDF_TARGET != "esp32"
reason: this is workaround to decrease of artifacts size

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'.*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

@ -0,0 +1,8 @@
#This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mb_endianness_utils)

View File

@ -0,0 +1,3 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |

View File

@ -0,0 +1,7 @@
set(srcs "test_mb_endianness_utils.c")
idf_component_register(SRCS ${srcs}
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES esp-modbus test_utils unity)

View File

@ -0,0 +1,6 @@
dependencies:
idf: ">=4.3"
espressif/esp-modbus:
version: "^1.0"
override_path: "../../../../"

View File

@ -0,0 +1,149 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdlib.h>
#include <stdbool.h>
#include "unity.h"
#include "test_utils.h"
#include "sdkconfig.h"
#include "mb_endianness_utils.h"
#define TAG "MB_ENDIANNESS_TEST"
// The below is the data used for endianness conversion test
const uint16_t TEST_INT8_A = 0x00F6;
const uint16_t TEST_INT8_B = 0xF600;
const uint16_t TEST_UINT8_A = 0x0037;
const uint16_t TEST_UINT8_B = 0x3700;
const uint16_t TEST_UINT16_AB = 0x3039;
const uint16_t TEST_UINT16_BA = 0x3930;
const uint16_t TEST_INT16_AB = 0x3039;
const uint16_t TEST_INT16_BA = 0x3930;
const uint32_t TEST_FLOAT_ABCD = 0x4640e400;
const uint32_t TEST_FLOAT_DCBA = 0x00e44046;
const uint32_t TEST_FLOAT_BADC = 0x404600e4;
const uint32_t TEST_FLOAT_CDAB = 0xe4004640;
const uint32_t TEST_UINT32_ABCD = 0x11223344;
const uint32_t TEST_UINT32_DCBA = 0x44332211;
const uint32_t TEST_UINT32_BADC = 0x22114433;
const uint32_t TEST_UINT32_CDAB = 0x33441122;
const uint64_t TEST_DOUBLE_ABCDEFGH = 0x40c81c8000000000;
const uint64_t TEST_DOUBLE_HGFEDCBA = 0x00000000801cc840;
const uint64_t TEST_DOUBLE_GHEFCDAB = 0x000000001c8040c8;
const uint64_t TEST_DOUBLE_BADCFEHG = 0xc840801c00000000;
const uint64_t TEST_INT64_ABCDEFGH = 0xffffffffffffcfc7;
const uint64_t TEST_INT64_HGFEDCBA = 0xc7cfffffffffffff;
const uint64_t TEST_INT64_GHEFCDAB = 0xcfc7ffffffffffff;
const uint64_t TEST_INT64_BADCFEHG = 0xffffffffffffc7cf;
const uint64_t TEST_UINT64_ABCDEFGH = 0x1122334455667788;
const uint64_t TEST_UINT64_HGFEDCBA = 0x8877665544332211;
const uint64_t TEST_UINT64_GHEFCDAB = 0x7788556633441122;
const uint64_t TEST_UINT64_BADCFEHG = 0x2211443366558877;
TEST_CASE("Test endianness conversion for all extended Modbus types.", "[MB_ENDIANNESS]")
{
val_16_arr arr_16 = {0};
val_32_arr arr_32 = {0};
val_64_arr arr_64 = {0};
TEST_ASSERT(mb_set_uint8_a(&arr_16, (uint8_t)55) == TEST_UINT8_A);
TEST_ASSERT(mb_get_uint8_a(&arr_16) == (uint8_t)55);
TEST_ASSERT(mb_set_int8_a(&arr_16, (int8_t)-10) == TEST_INT8_A);
TEST_ASSERT(mb_get_int8_a(&arr_16) == (int8_t)-10);
TEST_ASSERT(mb_set_uint8_b(&arr_16, (uint8_t)55) == TEST_UINT8_B);
TEST_ASSERT(mb_get_uint8_b(&arr_16) == (uint8_t)55);
TEST_ASSERT(mb_set_int8_b(&arr_16, (int8_t)-10) == TEST_INT8_B);
TEST_ASSERT(mb_get_int8_b(&arr_16) == (int8_t)-10);
TEST_ASSERT(mb_set_uint16_ab(&arr_16, (uint16_t)12345) == TEST_UINT16_AB);
TEST_ASSERT(mb_get_uint16_ab(&arr_16) == (uint16_t)12345);
TEST_ASSERT(mb_set_int16_ab(&arr_16, (int16_t)12345) == TEST_INT16_AB);
TEST_ASSERT(mb_get_int16_ab(&arr_16) == (int16_t)12345);
TEST_ASSERT(mb_set_uint16_ba(&arr_16, (uint16_t)12345) == TEST_UINT16_BA);
TEST_ASSERT(mb_get_uint16_ba(&arr_16) == (uint16_t)12345);
TEST_ASSERT(mb_set_int16_ba(&arr_16, (int16_t)12345) == TEST_INT16_BA);
TEST_ASSERT(mb_get_int16_ba(&arr_16) == (int16_t)12345);
TEST_ASSERT(mb_set_uint16_ab(&arr_16, (uint16_t)12345) == TEST_UINT16_AB);
TEST_ASSERT(mb_get_uint16_ab(&arr_16) == (uint16_t)12345);
TEST_ASSERT(mb_set_int16_ab(&arr_16, (int16_t)12345) == TEST_INT16_AB);
TEST_ASSERT(mb_get_int16_ab(&arr_16) == (int16_t)12345);
TEST_ASSERT(mb_set_float_abcd(&arr_32, (float)12345.0) == TEST_FLOAT_ABCD);
TEST_ASSERT(mb_get_float_abcd(&arr_32) == (float)12345.0);
TEST_ASSERT(mb_set_float_badc(&arr_32, (float)12345.0) == TEST_FLOAT_BADC);
TEST_ASSERT(mb_get_float_badc(&arr_32) == (float)12345.0);
TEST_ASSERT(mb_set_float_cdab(&arr_32, (float)12345.0) == TEST_FLOAT_CDAB);
TEST_ASSERT(mb_get_float_cdab(&arr_32) == (float)12345.0);
TEST_ASSERT(mb_set_float_dcba(&arr_32, (float)12345.0) == TEST_FLOAT_DCBA);
TEST_ASSERT(mb_get_float_dcba(&arr_32) == (float)12345.0);
TEST_ASSERT(mb_set_uint32_abcd(&arr_32, (uint32_t)0x11223344) == TEST_UINT32_ABCD);
TEST_ASSERT(mb_get_uint32_abcd(&arr_32) == (uint32_t)0x11223344);
TEST_ASSERT(mb_set_int32_abcd(&arr_32, (int32_t)0x11223344) == TEST_UINT32_ABCD);
TEST_ASSERT(mb_get_int32_abcd(&arr_32) == (int32_t)0x11223344);
TEST_ASSERT(mb_set_uint32_badc(&arr_32, (uint32_t)0x11223344) == TEST_UINT32_BADC);
TEST_ASSERT(mb_get_uint32_badc(&arr_32) == (uint32_t)0x11223344);
TEST_ASSERT(mb_set_int32_badc(&arr_32, (int32_t)0x11223344) == TEST_UINT32_BADC);
TEST_ASSERT(mb_get_int32_badc(&arr_32) == (int32_t)0x11223344);
TEST_ASSERT(mb_set_uint32_cdab(&arr_32, (uint32_t)0x11223344) == TEST_UINT32_CDAB);
TEST_ASSERT(mb_get_uint32_cdab(&arr_32) == (uint32_t)0x11223344);
TEST_ASSERT(mb_set_int32_cdab(&arr_32, (int32_t)0x11223344) == TEST_UINT32_CDAB);
TEST_ASSERT(mb_get_int32_cdab(&arr_32) == (int32_t)0x11223344);
TEST_ASSERT(mb_set_uint32_dcba(&arr_32, (uint32_t)0x11223344) == TEST_UINT32_DCBA);
TEST_ASSERT(mb_get_uint32_dcba(&arr_32) == (uint32_t)0x11223344);
TEST_ASSERT(mb_set_int32_dcba(&arr_32, (int32_t)0x11223344) == TEST_UINT32_DCBA);
TEST_ASSERT(mb_get_int32_dcba(&arr_32) == (int32_t)0x11223344);
TEST_ASSERT(mb_set_double_abcdefgh(&arr_64, (double)12345.0) == TEST_DOUBLE_ABCDEFGH);
TEST_ASSERT(mb_get_double_abcdefgh(&arr_64) == (double)12345.0);
TEST_ASSERT(mb_set_uint64_abcdefgh(&arr_64, (uint64_t)0x1122334455667788) == TEST_UINT64_ABCDEFGH);
TEST_ASSERT(mb_get_uint64_abcdefgh(&arr_64) == (uint64_t)0x1122334455667788);
TEST_ASSERT(mb_set_int64_abcdefgh(&arr_64, (int64_t)-12345) == TEST_INT64_ABCDEFGH);
TEST_ASSERT(mb_get_int64_abcdefgh(&arr_64) == (int64_t)-12345);
TEST_ASSERT(mb_set_double_hgfedcba(&arr_64, (double)12345.0) == TEST_DOUBLE_HGFEDCBA);
TEST_ASSERT(mb_get_double_hgfedcba(&arr_64) == (double)12345.0);
TEST_ASSERT(mb_set_uint64_hgfedcba(&arr_64, (uint64_t)0x1122334455667788) == TEST_UINT64_HGFEDCBA);
TEST_ASSERT(mb_get_uint64_hgfedcba(&arr_64) == (uint64_t)0x1122334455667788);
TEST_ASSERT(mb_set_int64_hgfedcba(&arr_64, (int64_t)-12345) == TEST_INT64_HGFEDCBA);
TEST_ASSERT(mb_get_int64_hgfedcba(&arr_64) == (int64_t)-12345);
TEST_ASSERT(mb_set_double_ghefcdab(&arr_64, (double)12345.0) == TEST_DOUBLE_GHEFCDAB);
TEST_ASSERT(mb_get_double_ghefcdab(&arr_64) == (double)12345.0);
TEST_ASSERT(mb_set_uint64_ghefcdab(&arr_64, (uint64_t)0x1122334455667788) == TEST_UINT64_GHEFCDAB);
TEST_ASSERT(mb_get_uint64_ghefcdab(&arr_64) == (uint64_t)0x1122334455667788);
TEST_ASSERT(mb_set_int64_ghefcdab(&arr_64, (int64_t)-12345) == TEST_INT64_GHEFCDAB);
TEST_ASSERT(mb_get_int64_ghefcdab(&arr_64) == (int64_t)-12345);
TEST_ASSERT(mb_set_double_badcfehg(&arr_64, (double)12345.0) == TEST_DOUBLE_BADCFEHG);
TEST_ASSERT(mb_get_double_badcfehg(&arr_64) == (double)12345.0);
TEST_ASSERT(mb_set_uint64_badcfehg(&arr_64, (uint64_t)0x1122334455667788) == TEST_UINT64_BADCFEHG);
TEST_ASSERT(mb_get_uint64_badcfehg(&arr_64) == (uint64_t)0x1122334455667788);
TEST_ASSERT(mb_set_int64_badcfehg(&arr_64, (int64_t)-12345) == TEST_INT64_BADCFEHG);
TEST_ASSERT(mb_get_int64_badcfehg(&arr_64) == (int64_t)-12345);
}
void app_main(void)
{
unity_run_menu();
}

View File

@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.generic
def test_mb_endianness_utils(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@ -0,0 +1,2 @@
# General options for test
CONFIG_FMB_EXT_TYPE_SUPPORT=y

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

@ -13,6 +13,8 @@
#ifndef _DEVICE_PARAMS
#define _DEVICE_PARAMS
#include "sdkconfig.h"
// This file defines structure of modbus parameters which reflect correspond modbus address space
// for each modbus register type (coils, discreet inputs, holding registers, input registers)
#pragma pack(push, 1)
@ -26,7 +28,8 @@ typedef struct
uint8_t discrete_input5:1;
uint8_t discrete_input6:1;
uint8_t discrete_input7:1;
uint8_t discrete_input_port1:8;
uint8_t discrete_input_port1;
uint8_t discrete_input_port2;
} discrete_reg_params_t;
#pragma pack(pop)
@ -35,6 +38,7 @@ typedef struct
{
uint8_t coils_port0;
uint8_t coils_port1;
uint8_t coils_port2;
} coil_reg_params_t;
#pragma pack(pop)
@ -57,6 +61,25 @@ typedef struct
#pragma pack(push, 1)
typedef struct
{
#if CONFIG_FMB_EXT_TYPE_SUPPORT
uint16_t holding_u8_a[2];
uint16_t holding_u8_b[2];
uint16_t holding_u16_ab[2];
uint16_t holding_u16_ba[2];
uint32_t holding_uint32_abcd[2];
uint32_t holding_uint32_cdab[2];
uint32_t holding_uint32_badc[2];
uint32_t holding_uint32_dcba[2];
float holding_float_abcd[2];
float holding_float_cdab[2];
float holding_float_badc[2];
float holding_float_dcba[2];
double holding_double_abcdefgh[2];
double holding_double_hgfedcba[2];
double holding_double_ghefcdab[2];
double holding_double_badcfehg[2];
uint32_t holding_area2_end;
#endif
float holding_data0;
float holding_data1;
float holding_data2;
@ -66,6 +89,7 @@ typedef struct
float holding_data5;
float holding_data6;
float holding_data7;
uint32_t holding_area1_end;
} holding_reg_params_t;
#pragma pack(pop)

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

@ -2,3 +2,4 @@ set(PROJECT_NAME "modbus_master")
idf_component_register(SRCS "master.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,16 @@ 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
range 0 56 if IDF_TARGET_ESP32P4
default 22 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32P4
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
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 +46,16 @@ 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
range 0 56 if IDF_TARGET_ESP32P4
default 23 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32P4
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
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 +63,17 @@ 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
range 0 56 if IDF_TARGET_ESP32P4
default 20 if IDF_TARGET_ESP32P4
default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
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

@ -6,6 +6,7 @@
#include "string.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "modbus_params.h" // for modbus parameters structures
#include "mbcontroller.h"
#include "sdkconfig.h"
@ -20,7 +21,7 @@
#define MASTER_MAX_CIDS num_device_parameters
// Number of reading of parameters from slave
#define MASTER_MAX_RETRY 30
#define MASTER_MAX_RETRY 10
// Timeout to update cid over Modbus
#define UPDATE_CIDS_TIMEOUT_MS (500)
@ -37,10 +38,22 @@
// Discrete offset macro
#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
#define STR(fieldname) ((const char*)( fieldname ))
#define STR(fieldname) ((const char *)( fieldname ))
#define TEST_HOLD_REG_START(field) (HOLD_OFFSET(field) >> 1)
#define TEST_HOLD_REG_SIZE(field) (sizeof(((holding_reg_params_t *)0)->field) >> 1)
#define TEST_INPUT_REG_START(field) (INPUT_OFFSET(field) >> 1)
#define TEST_INPUT_REG_SIZE(field) (sizeof(((input_reg_params_t *)0)->field) >> 1)
#define TEST_VALUE 12345 // default test value
#define TEST_ASCII_BIN 0xAAAAAAAA
// Options can be used as bit masks or parameter limits
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
#define EACH_ITEM(array, length) \
(typeof(*(array)) *pitem = (array); (pitem < &((array)[length])); pitem++)
static const char *TAG = "MASTER_TEST";
// Enumeration of modbus device addresses accessed by master device
@ -59,6 +72,25 @@ enum {
CID_HOLD_TEST_REG,
CID_RELAY_P1,
CID_RELAY_P2,
CID_DISCR_P1,
#if CONFIG_FMB_EXT_TYPE_SUPPORT
CID_HOLD_U8_A,
CID_HOLD_U8_B,
CID_HOLD_U16_AB,
CID_HOLD_U16_BA,
CID_HOLD_UINT32_ABCD,
CID_HOLD_UINT32_CDAB,
CID_HOLD_UINT32_BADC,
CID_HOLD_UINT32_DCBA,
CID_HOLD_FLOAT_ABCD,
CID_HOLD_FLOAT_CDAB,
CID_HOLD_FLOAT_BADC,
CID_HOLD_FLOAT_DCBA,
CID_HOLD_DOUBLE_ABCDEFGH,
CID_HOLD_DOUBLE_HGFEDCBA,
CID_HOLD_DOUBLE_GHEFCDAB,
CID_HOLD_DOUBLE_BADCFEHG,
#endif
CID_COUNT
};
@ -73,24 +105,109 @@ enum {
// Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc).
const mb_parameter_descriptor_t device_parameters[] = {
// { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2,
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4, OPTS( -10, 10, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 2,
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 2,
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 4, 2,
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 2,
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 10, 58,
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 0, 8,
COIL_OFFSET(coils_port0), PARAM_TYPE_U16, 2, OPTS( BIT1, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P2, STR("RelayP2"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 8, 8,
COIL_OFFSET(coils_port1), PARAM_TYPE_U16, 2, OPTS( BIT0, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data0), TEST_INPUT_REG_SIZE(input_data0),
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data0), TEST_HOLD_REG_SIZE(holding_data0),
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data1), TEST_INPUT_REG_SIZE(input_data1),
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4,
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data1), TEST_HOLD_REG_SIZE(holding_data1),
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data2), TEST_INPUT_REG_SIZE(input_data2),
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4,
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data2), TEST_HOLD_REG_SIZE(holding_data2),
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(test_regs), 58,
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116,
OPTS( 0, 100, TEST_ASCII_BIN ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 2, 6,
COIL_OFFSET(coils_port0), PARAM_TYPE_U8, 1,
OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P2, STR("RelayP2"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 10, 6,
COIL_OFFSET(coils_port1), PARAM_TYPE_U8, 1,
OPTS( 0x55, 0x2A, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_DISCR_P1, STR("DiscreteInpP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_DISCRETE, 2, 7,
DISCR_OFFSET(discrete_input_port1), PARAM_TYPE_U8, 1,
OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
#if CONFIG_FMB_EXT_TYPE_SUPPORT
{ CID_HOLD_U8_A, STR("U8_A"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u8_a), TEST_HOLD_REG_SIZE(holding_u8_a),
HOLD_OFFSET(holding_u8_a), PARAM_TYPE_U8_A, (TEST_HOLD_REG_SIZE(holding_u8_a) << 1),
OPTS( CHAR_MIN, 0x0055, 0x0055 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U8_B, STR("U8_B"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u8_b), TEST_HOLD_REG_SIZE(holding_u8_b),
HOLD_OFFSET(holding_u8_b), PARAM_TYPE_U8_B, (TEST_HOLD_REG_SIZE(holding_u8_b) << 1),
OPTS( 0, 0x5500, 0x5500 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U16_AB, STR("U16_AB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u16_ab), TEST_HOLD_REG_SIZE(holding_u16_ab),
HOLD_OFFSET(holding_u16_ab), PARAM_TYPE_U16_AB, (TEST_HOLD_REG_SIZE(holding_u16_ab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U16_BA, STR("U16_BA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u16_ba), TEST_HOLD_REG_SIZE(holding_u16_ba),
HOLD_OFFSET(holding_u16_ba), PARAM_TYPE_U16_BA, (TEST_HOLD_REG_SIZE(holding_u16_ab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_ABCD, STR("UINT32_ABCD"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_abcd), TEST_HOLD_REG_SIZE(holding_uint32_abcd),
HOLD_OFFSET(holding_uint32_abcd), PARAM_TYPE_U32_ABCD, (TEST_HOLD_REG_SIZE(holding_uint32_abcd) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_CDAB, STR("UINT32_CDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_cdab), TEST_HOLD_REG_SIZE(holding_uint32_cdab),
HOLD_OFFSET(holding_uint32_cdab), PARAM_TYPE_U32_CDAB, (TEST_HOLD_REG_SIZE(holding_uint32_cdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_BADC, STR("UINT32_BADC"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_badc), TEST_HOLD_REG_SIZE(holding_uint32_badc),
HOLD_OFFSET(holding_uint32_badc), PARAM_TYPE_U32_BADC, (TEST_HOLD_REG_SIZE(holding_uint32_badc) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_DCBA, STR("UINT32_DCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_dcba), TEST_HOLD_REG_SIZE(holding_uint32_dcba),
HOLD_OFFSET(holding_uint32_dcba), PARAM_TYPE_U32_DCBA, (TEST_HOLD_REG_SIZE(holding_uint32_dcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_ABCD, STR("FLOAT_ABCD"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_abcd), TEST_HOLD_REG_SIZE(holding_float_abcd),
HOLD_OFFSET(holding_float_abcd), PARAM_TYPE_FLOAT_ABCD, (TEST_HOLD_REG_SIZE(holding_float_abcd) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_CDAB, STR("FLOAT_CDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_cdab), TEST_HOLD_REG_SIZE(holding_float_cdab),
HOLD_OFFSET(holding_float_cdab), PARAM_TYPE_FLOAT_CDAB, (TEST_HOLD_REG_SIZE(holding_float_cdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_BADC, STR("FLOAT_BADC"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_badc), TEST_HOLD_REG_SIZE(holding_float_badc),
HOLD_OFFSET(holding_float_badc), PARAM_TYPE_FLOAT_BADC, (TEST_HOLD_REG_SIZE(holding_float_badc) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_DCBA, STR("FLOAT_DCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_dcba), TEST_HOLD_REG_SIZE(holding_float_dcba),
HOLD_OFFSET(holding_float_dcba), PARAM_TYPE_FLOAT_DCBA, (TEST_HOLD_REG_SIZE(holding_float_dcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_ABCDEFGH, STR("DOUBLE_ABCDEFGH"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_abcdefgh), TEST_HOLD_REG_SIZE(holding_double_abcdefgh),
HOLD_OFFSET(holding_double_abcdefgh), PARAM_TYPE_DOUBLE_ABCDEFGH, (TEST_HOLD_REG_SIZE(holding_double_abcdefgh) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_HGFEDCBA, STR("DOUBLE_HGFEDCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_hgfedcba), TEST_HOLD_REG_SIZE(holding_double_hgfedcba),
HOLD_OFFSET(holding_double_hgfedcba), PARAM_TYPE_DOUBLE_HGFEDCBA, (TEST_HOLD_REG_SIZE(holding_double_hgfedcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_GHEFCDAB, STR("DOUBLE_GHEFCDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_ghefcdab), TEST_HOLD_REG_SIZE(holding_double_ghefcdab),
HOLD_OFFSET(holding_double_ghefcdab), PARAM_TYPE_DOUBLE_GHEFCDAB, (TEST_HOLD_REG_SIZE(holding_double_ghefcdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_BADCFEHG, STR("DOUBLE_BADCFEHG"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_badcfehg), TEST_HOLD_REG_SIZE(holding_double_badcfehg),
HOLD_OFFSET(holding_double_badcfehg), PARAM_TYPE_DOUBLE_BADCFEHG, (TEST_HOLD_REG_SIZE(holding_double_badcfehg) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER }
#endif
};
// Calculate number of parameters in the table
@ -105,33 +222,84 @@ static void* master_get_param_data(const mb_parameter_descriptor_t* param_descri
switch(param_descriptor->mb_param_type)
{
case MB_PARAM_HOLDING:
instance_ptr = ((void*)&holding_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&holding_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_INPUT:
instance_ptr = ((void*)&input_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&input_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_COIL:
instance_ptr = ((void*)&coil_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&coil_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_DISCRETE:
instance_ptr = ((void*)&discrete_reg_params + param_descriptor->param_offset - 1);
instance_ptr = ((void *)&discrete_reg_params + param_descriptor->param_offset - 1);
break;
default:
instance_ptr = NULL;
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;
}
#define TEST_VERIFY_VALUES(pdescr, pinst) (__extension__( \
{ \
assert(pinst); \
assert(pdescr); \
uint8_t type = 0; \
esp_err_t err = ESP_FAIL; \
err = mbc_master_get_parameter(pdescr->cid, (char *)pdescr->param_key, \
(uint8_t *)pinst, &type); \
if (err == ESP_OK) { \
bool is_correct = true; \
if (pdescr->param_opts.opt3) { \
for EACH_ITEM(pinst, pdescr->param_size / sizeof(*pitem)) { \
if (*pitem != (typeof(*(pinst)))pdescr->param_opts.opt3) { \
*pitem = (typeof(*(pinst)))pdescr->param_opts.opt3; \
ESP_LOGD(TAG, "Characteristic #%d (%s), initialize to 0x%" PRIx16 ".", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(uint16_t)pdescr->param_opts.opt3); \
is_correct = false; \
} \
} \
} \
if (!is_correct) { \
ESP_LOGE(TAG, "Characteristic #%d (%s), initialize.", \
(int)pdescr->cid, \
(char *)pdescr->param_key); \
err = mbc_master_set_parameter(cid, (char *)pdescr->param_key, \
(uint8_t *)pinst, &type); \
if (err != ESP_OK) { \
ESP_LOGE(TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(int)err, \
(char *)esp_err_to_name(err)); \
} else { \
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (..) write successful.", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(char *)pdescr->param_units); \
} \
} \
} else { \
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(int)err, \
(char *)esp_err_to_name(err)); \
} \
(err); \
} \
))
// User operation function to read slave values and check alarm
static void master_operation_func(void *arg)
{
esp_err_t err = ESP_OK;
float value = 0;
bool alarm_state = false;
const mb_parameter_descriptor_t* param_descriptor = NULL;
@ -139,103 +307,145 @@ static void master_operation_func(void *arg)
for(uint16_t retry = 0; retry <= MASTER_MAX_RETRY && (!alarm_state); retry++) {
// Read all found characteristics from slave(s)
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++)
{
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++) {
// Get data from parameters description table
// and use this information to fill the characteristics description table
// and having all required fields in just one table
err = mbc_master_get_cid_info(cid, &param_descriptor);
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
void* temp_data_ptr = master_get_param_data(param_descriptor);
void *temp_data_ptr = master_get_param_data(param_descriptor);
assert(temp_data_ptr);
uint8_t type = 0;
if ((param_descriptor->param_type == PARAM_TYPE_ASCII) &&
(param_descriptor->cid == CID_HOLD_TEST_REG)) {
// Check for long array of registers of type PARAM_TYPE_ASCII
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);
// 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);
*(uint32_t*)temp_data_ptr = 0xAAAAAAAA;
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,
(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,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
}
}
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
if (TEST_VERIFY_VALUES(param_descriptor, (uint32_t *)temp_data_ptr) == ESP_OK) {
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);
}
} else {
err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
(uint8_t*)&value, &type);
if (err == ESP_OK) {
*(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,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
value,
*(uint32_t*)temp_data_ptr);
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min))) {
alarm_state = true;
break;
}
} else {
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,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
(const char*)rw_str,
*(uint16_t*)temp_data_ptr);
if (state & param_descriptor->param_opts.opt1) {
alarm_state = true;
break;
}
#if CONFIG_FMB_EXT_TYPE_SUPPORT
} else if ((param_descriptor->cid >= CID_HOLD_U16_AB)
&& (param_descriptor->cid <= CID_HOLD_U16_BA)) {
// Check the uint16 parameters
if (TEST_VERIFY_VALUES(param_descriptor, (uint16_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx16 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint16_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_U8_A)
&& (param_descriptor->cid <= CID_HOLD_U8_B)) {
// Check the uint8 parameters
if (TEST_VERIFY_VALUES(param_descriptor, (uint16_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx16 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint16_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_UINT32_ABCD)
&& (param_descriptor->cid <= CID_HOLD_UINT32_DCBA)) {
// Check the uint32 parameters
if (TEST_VERIFY_VALUES(param_descriptor, (uint32_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %" PRIu32 " (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint32_t *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_FLOAT_ABCD)
&& (param_descriptor->cid <= CID_HOLD_FLOAT_DCBA)) {
// Check the float parameters
if (TEST_VERIFY_VALUES(param_descriptor, (float *)temp_data_ptr) == ESP_OK) {
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,
*(float *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
} else if (param_descriptor->cid >= CID_HOLD_DOUBLE_ABCDEFGH) {
// Check the double parameters
if (TEST_VERIFY_VALUES(param_descriptor, (double *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %lf (0x%" PRIx64 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(double *)temp_data_ptr,
*(uint64_t *)temp_data_ptr);
}
#endif
} else if (cid <= CID_HOLD_DATA_2) {
uint64_t start_timestamp = esp_timer_get_time(); // Get current timestamp in microseconds
if (TEST_VERIFY_VALUES(param_descriptor, (float *)temp_data_ptr) == ESP_OK) {
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,
*(float *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
float value = *(float *)temp_data_ptr;
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min))) {
alarm_state = true;
break;
}
mb_trans_info_t tinfo = {0};
if (mbc_master_get_transaction_info(&tinfo) == ESP_OK) {
bool trans_is_expired = (tinfo.trans_id >= (start_timestamp + (CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND * 1000)));
ESP_LOGW("TRANS_INFO", "Id: %" PRIu64 ", Addr: %x, FC: 0x%x, Exception: %u, Err: %u %s",
(uint64_t)tinfo.trans_id, (int)tinfo.dest_addr,
(int)tinfo.func_code, (unsigned)tinfo.exception,
(int)tinfo.err_type,
trans_is_expired ? "(EXPIRED)" : "");
// Check if the response time is expired sinse start of transaction,
// or the other IO is performed from different thread.
if (trans_is_expired) {
ESP_LOGE("TRANS_INFO", "Transaction Id: %" PRIu64 ", is expired.", tinfo.trans_id);
alarm_state = true;
break;
}
}
} else if ((cid >= CID_RELAY_P1) && (cid <= CID_DISCR_P1)) {
if (TEST_VERIFY_VALUES(param_descriptor, (uint8_t *)temp_data_ptr) == ESP_OK) {
uint8_t state = *(uint8_t *)temp_data_ptr;
const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
if ((state & param_descriptor->param_opts.opt2) == param_descriptor->param_opts.opt2) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %s (0x%" PRIx8 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
(const char *)rw_str,
*(uint8_t *)temp_data_ptr);
} else {
ESP_LOGE(TAG, "Characteristic #%d %s (%s) value = %s (0x%" PRIx8 "), unexpected value.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
(const char *)rw_str,
*(uint8_t *)temp_data_ptr);
alarm_state = true;
break;
}
if (state & param_descriptor->param_opts.opt1) {
alarm_state = true;
break;
}
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
}
}
vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
}
}
vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS); //
vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS);
}
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,38 +474,52 @@ 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);
err = mbc_master_setup((void*)&comm);
"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,
CONFIG_MB_UART_RTS, UART_PIN_NO_CHANGE);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set pin failure, uart_set_pin() returned (0x%x).", (int)err);
err = mbc_master_start();
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller start fail, 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);
// 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;
}
#define MB_PDU_DATA_OFF 1
#define EV_ERROR_EXECUTE_FUNCTION 3
void vMBMasterErrorCBUserHandler( uint64_t xTransId, uint16_t usError, uint8_t ucDestAddress, const uint8_t *pucRecvData, uint16_t ucRecvLength,
const uint8_t *pucSendData, uint16_t ucSendLength )
{
ESP_LOGW("USER_ERR_CB", "The transaction %" PRIu64 ", error type: %u", xTransId, usError);
if ((usError == EV_ERROR_EXECUTE_FUNCTION) && pucRecvData && ucRecvLength) {
ESP_LOGW("USER_ERR_CB", "The command is unsupported or an exception on slave happened: %x", (int)pucRecvData[MB_PDU_DATA_OFF]);
}
if (pucRecvData && ucRecvLength) {
ESP_LOG_BUFFER_HEX_LEVEL("Received buffer", (void *)pucRecvData, (uint16_t)ucRecvLength, ESP_LOG_WARN);
}
if (pucSendData && ucSendLength) {
ESP_LOG_BUFFER_HEX_LEVEL("Sent buffer", (void *)pucSendData, (uint16_t)ucSendLength, ESP_LOG_WARN);
}
}
void app_main(void)
{
// Initialization of device peripheral and objects

View File

@ -0,0 +1,6 @@
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
CONFIG_FMB_EXT_TYPE_SUPPORT=y

View File

@ -0,0 +1,7 @@
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
CONFIG_FMB_EXT_TYPE_SUPPORT=y

View File

@ -3,8 +3,7 @@
#
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
CONFIG_FMB_EXT_TYPE_SUPPORT=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-P4 | 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,16 @@ 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
range 0 56 if IDF_TARGET_ESP32P4
default 22 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32P4
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
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 +46,16 @@ 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
range 0 56 if IDF_TARGET_ESP32P4
default 23 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32P4
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
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 +63,17 @@ 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
range 0 56 if IDF_TARGET_ESP32P4
default 20 if IDF_TARGET_ESP32P4
default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6
range 0 46 if IDF_TARGET_ESP32S2
range 0 47 if IDF_TARGET_ESP32S3
range 0 19 if IDF_TARGET_ESP32C3
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

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -29,7 +29,11 @@
#define MB_REG_INPUT_START_AREA0 (INPUT_OFFSET(input_data0)) // register offset input area 0
#define MB_REG_INPUT_START_AREA1 (INPUT_OFFSET(input_data4)) // register offset input area 1
#define MB_REG_HOLDING_START_AREA0 (HOLD_OFFSET(holding_data0))
#define MB_REG_HOLDING_START_AREA0_SIZE ((size_t)((HOLD_OFFSET(holding_data4) - HOLD_OFFSET(holding_data0)) << 1))
#define MB_REG_HOLDING_START_AREA1 (HOLD_OFFSET(holding_data4))
#define MB_REG_HOLDING_START_AREA1_SIZE ((size_t)((HOLD_OFFSET(holding_area1_end) - HOLD_OFFSET(holding_data4)) << 1))
#define MB_REG_HOLDING_START_AREA2 (HOLD_OFFSET(holding_u8_a))
#define MB_REG_HOLDING_START_AREA2_SIZE ((size_t)((HOLD_OFFSET(holding_area2_end) - HOLD_OFFSET(holding_u8_a)) << 1))
#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
#define MB_CHAN_DATA_MAX_VAL (6)
@ -41,6 +45,7 @@
#define MB_WRITE_MASK (MB_EVENT_HOLDING_REG_WR \
| MB_EVENT_COILS_WR)
#define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK)
#define MB_TEST_VALUE 12345.0
static const char *TAG = "SLAVE_TEST";
@ -69,6 +74,44 @@ static void setup_reg_data(void)
holding_reg_params.holding_data6 = 7.79;
holding_reg_params.holding_data7 = 8.80;
#if CONFIG_FMB_EXT_TYPE_SUPPORT
mb_set_uint8_a((val_16_arr *)&holding_reg_params.holding_u8_a[0], (uint8_t)0x55);
mb_set_uint8_a((val_16_arr *)&holding_reg_params.holding_u8_a[1], (uint8_t)0x55);
mb_set_uint8_b((val_16_arr *)&holding_reg_params.holding_u8_b[0], (uint8_t)0x55);
mb_set_uint8_b((val_16_arr *)&holding_reg_params.holding_u8_b[1], (uint8_t)0x55);
mb_set_uint16_ab((val_16_arr *)&holding_reg_params.holding_u16_ab[1], (uint16_t)MB_TEST_VALUE);
mb_set_uint16_ab((val_16_arr *)&holding_reg_params.holding_u16_ab[0], (uint16_t)MB_TEST_VALUE);
mb_set_uint16_ba((val_16_arr *)&holding_reg_params.holding_u16_ba[0], (uint16_t)MB_TEST_VALUE);
mb_set_uint16_ba((val_16_arr *)&holding_reg_params.holding_u16_ba[1], (uint16_t)MB_TEST_VALUE);
mb_set_float_abcd((val_32_arr *)&holding_reg_params.holding_float_abcd[0], (float)MB_TEST_VALUE);
mb_set_float_abcd((val_32_arr *)&holding_reg_params.holding_float_abcd[1], (float)MB_TEST_VALUE);
mb_set_float_cdab((val_32_arr *)&holding_reg_params.holding_float_cdab[0], (float)MB_TEST_VALUE);
mb_set_float_cdab((val_32_arr *)&holding_reg_params.holding_float_cdab[1], (float)MB_TEST_VALUE);
mb_set_float_badc((val_32_arr *)&holding_reg_params.holding_float_badc[0], (float)MB_TEST_VALUE);
mb_set_float_badc((val_32_arr *)&holding_reg_params.holding_float_badc[1], (float)MB_TEST_VALUE);
mb_set_float_dcba((val_32_arr *)&holding_reg_params.holding_float_dcba[0], (float)MB_TEST_VALUE);
mb_set_float_dcba((val_32_arr *)&holding_reg_params.holding_float_dcba[1], (float)MB_TEST_VALUE);
mb_set_uint32_abcd((val_32_arr *)&holding_reg_params.holding_uint32_abcd[0], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_abcd((val_32_arr *)&holding_reg_params.holding_uint32_abcd[1], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_cdab((val_32_arr *)&holding_reg_params.holding_uint32_cdab[0], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_cdab((val_32_arr *)&holding_reg_params.holding_uint32_cdab[1], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_badc((val_32_arr *)&holding_reg_params.holding_uint32_badc[0], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_badc((val_32_arr *)&holding_reg_params.holding_uint32_badc[1], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_dcba((val_32_arr *)&holding_reg_params.holding_uint32_dcba[0], (uint32_t)MB_TEST_VALUE);
mb_set_uint32_dcba((val_32_arr *)&holding_reg_params.holding_uint32_dcba[1], (uint32_t)MB_TEST_VALUE);
mb_set_double_abcdefgh((val_64_arr *)&holding_reg_params.holding_double_abcdefgh[0], (double)MB_TEST_VALUE);
mb_set_double_abcdefgh((val_64_arr *)&holding_reg_params.holding_double_abcdefgh[1], (double)MB_TEST_VALUE);
mb_set_double_hgfedcba((val_64_arr *)&holding_reg_params.holding_double_hgfedcba[0], (double)MB_TEST_VALUE);
mb_set_double_hgfedcba((val_64_arr *)&holding_reg_params.holding_double_hgfedcba[1], (double)MB_TEST_VALUE);
mb_set_double_ghefcdab((val_64_arr *)&holding_reg_params.holding_double_ghefcdab[0], (double)MB_TEST_VALUE);
mb_set_double_ghefcdab((val_64_arr *)&holding_reg_params.holding_double_ghefcdab[1], (double)MB_TEST_VALUE);
mb_set_double_badcfehg((val_64_arr *)&holding_reg_params.holding_double_badcfehg[0], (double)MB_TEST_VALUE);
mb_set_double_badcfehg((val_64_arr *)&holding_reg_params.holding_double_badcfehg[1], (double)MB_TEST_VALUE);
#endif
coil_reg_params.coils_port0 = 0x55;
coil_reg_params.coils_port1 = 0xAA;
@ -120,21 +163,33 @@ void app_main(void)
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START_AREA0; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params.holding_data0; // Set pointer to storage instance
// Set the size of register storage instance = 150 holding registers
reg_area.size = (size_t)(HOLD_OFFSET(holding_data4) - HOLD_OFFSET(test_regs));
// Set the size of register storage instance in bytes
reg_area.size = MB_REG_HOLDING_START_AREA0_SIZE;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
// The second register area
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START_AREA1; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params.holding_data4; // Set pointer to storage instance
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
reg_area.start_offset = MB_REG_HOLDING_START_AREA1;
reg_area.address = (void*)&holding_reg_params.holding_data4;
reg_area.size = MB_REG_HOLDING_START_AREA1_SIZE;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
#if CONFIG_FMB_EXT_TYPE_SUPPORT
// The extended parameters register area
reg_area.type = MB_PARAM_HOLDING;
reg_area.start_offset = MB_REG_HOLDING_START_AREA2;
reg_area.address = (void*)&holding_reg_params.holding_u8_a;
reg_area.size = MB_REG_HOLDING_START_AREA2_SIZE;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
#endif
// Initialization of Input Registers area
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = MB_REG_INPUT_START_AREA0;
reg_area.address = (void*)&input_reg_params.input_data0;
reg_area.size = sizeof(float) << 2;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = MB_REG_INPUT_START_AREA1;
reg_area.address = (void*)&input_reg_params.input_data4;
@ -175,22 +230,21 @@ void app_main(void)
// incremented each access cycle reaches the CHAN_DATA_MAX_VAL value.
for(;holding_reg_params.holding_data0 < MB_CHAN_DATA_MAX_VAL;) {
// Check for read/write events of Modbus master for certain events
mb_event_group_t event = mbc_slave_check_event(MB_READ_WRITE_MASK);
const char* rw_str = (event & MB_READ_MASK) ? "READ" : "WRITE";
(void)mbc_slave_check_event(MB_READ_WRITE_MASK);
ESP_ERROR_CHECK_WITHOUT_ABORT(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
const char* rw_str = (reg_info.type & MB_READ_MASK) ? "READ" : "WRITE";
// Filter events and process them accordingly
if(event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
if(reg_info.type & (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",
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);
if (reg_info.address == (uint8_t*)&holding_reg_params.holding_data0)
{
ESP_LOGI(TAG, "HOLDING %s (%" PRIu32 " us), ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
rw_str,
reg_info.time_stamp,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(uint32_t)reg_info.address,
(unsigned)reg_info.size);
if (reg_info.address == (uint8_t*)&holding_reg_params.holding_data0) {
portENTER_CRITICAL(&param_lock);
holding_reg_params.holding_data0 += MB_CHAN_DATA_OFFSET;
if (holding_reg_params.holding_data0 >= (MB_CHAN_DATA_MAX_VAL - MB_CHAN_DATA_OFFSET)) {
@ -198,31 +252,28 @@ void app_main(void)
}
portEXIT_CRITICAL(&param_lock);
}
} 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);
} 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);
} 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);
} else if (reg_info.type & MB_EVENT_INPUT_REG_RD) {
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,
(unsigned)reg_info.size);
} else if (reg_info.type & MB_EVENT_DISCRETE_RD) {
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,
(unsigned)reg_info.size);
} else if (reg_info.type & (MB_EVENT_COILS_RD | MB_EVENT_COILS_WR)) {
ESP_LOGI(TAG, "COILS %s (%" PRIu32 " us), ADDR:%u, TYPE:%u, INST_ADDR:0x%" PRIx32 ", SIZE:%u",
rw_str,
reg_info.time_stamp,
(unsigned)reg_info.mb_offset,
(unsigned)reg_info.type,
(uint32_t)reg_info.address,
(unsigned)reg_info.size);
if (coil_reg_params.coils_port1 == 0xFF) break;
}
}

View File

@ -0,0 +1,5 @@
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
CONFIG_FMB_EXT_TYPE_SUPPORT=y

View File

@ -0,0 +1,6 @@
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
CONFIG_FMB_EXT_TYPE_SUPPORT=y

View File

@ -4,6 +4,5 @@
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
CONFIG_FMB_EXT_TYPE_SUPPORT=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_[a-z]+: - 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_[a-z]+: - 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_[a-z]+: - 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_[a-z]+: - 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-H2 | ESP32-P4 | 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 ".")

View File

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

View File

@ -9,12 +9,18 @@
#include <string.h>
#include <sys/queue.h>
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#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"
@ -28,7 +34,7 @@
#define MASTER_MAX_CIDS num_device_parameters
// Number of reading of parameters from slave
#define MASTER_MAX_RETRY (30)
#define MASTER_MAX_RETRY (10)
// Timeout to update cid over Modbus
#define UPDATE_CIDS_TIMEOUT_MS (500)
@ -44,7 +50,16 @@
#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
#define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1))
#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
#define STR(fieldname) ((const char*)( fieldname ))
#define STR(fieldname) ((const char *)( fieldname ))
#define TEST_HOLD_REG_START(field) (HOLD_OFFSET(field) >> 1)
#define TEST_HOLD_REG_SIZE(field) (sizeof(((holding_reg_params_t *)0)->field) >> 1)
#define TEST_INPUT_REG_START(field) (INPUT_OFFSET(field) >> 1)
#define TEST_INPUT_REG_SIZE(field) (sizeof(((input_reg_params_t *)0)->field) >> 1)
#define TEST_VALUE 12345 // default test value
#define TEST_ASCII_BIN 0xAAAAAAAA
// Options can be used as bit masks or parameter limits
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
@ -63,12 +78,16 @@
#endif
#define MB_MDNS_INSTANCE(pref) pref"mb_master_tcp"
#define EACH_ITEM(array, length) \
(typeof(*(array)) *pitem = (array); (pitem < &((array)[length])); pitem++)
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
};
@ -84,6 +103,25 @@ enum {
CID_HOLD_TEST_REG,
CID_RELAY_P1,
CID_RELAY_P2,
CID_DISCR_P1,
#if CONFIG_FMB_EXT_TYPE_SUPPORT
CID_HOLD_U8_A,
CID_HOLD_U8_B,
CID_HOLD_U16_AB,
CID_HOLD_U16_BA,
CID_HOLD_UINT32_ABCD,
CID_HOLD_UINT32_CDAB,
CID_HOLD_UINT32_BADC,
CID_HOLD_UINT32_DCBA,
CID_HOLD_FLOAT_ABCD,
CID_HOLD_FLOAT_CDAB,
CID_HOLD_FLOAT_BADC,
CID_HOLD_FLOAT_DCBA,
CID_HOLD_DOUBLE_ABCDEFGH,
CID_HOLD_DOUBLE_HGFEDCBA,
CID_HOLD_DOUBLE_GHEFCDAB,
CID_HOLD_DOUBLE_BADCFEHG,
#endif
CID_COUNT
};
@ -98,24 +136,109 @@ enum {
// Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc).
const mb_parameter_descriptor_t device_parameters[] = {
// { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2,
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4, OPTS( -10, 10, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 2,
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 2, 2,
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR2, MB_PARAM_INPUT, 4, 2,
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR3, MB_PARAM_HOLDING, 4, 2,
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 8, 100,
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 200, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 0, 8,
COIL_OFFSET(coils_port0), PARAM_TYPE_U16, 1, OPTS( BIT1, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P2, STR("RelayP2"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 8, 8,
COIL_OFFSET(coils_port1), PARAM_TYPE_U16, 1, OPTS( BIT0, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }
{ CID_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data0), TEST_INPUT_REG_SIZE(input_data0),
INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data0), TEST_HOLD_REG_SIZE(holding_data0),
HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data1), TEST_INPUT_REG_SIZE(input_data1),
INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4,
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data1), TEST_HOLD_REG_SIZE(holding_data1),
HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR2, MB_PARAM_INPUT,
TEST_INPUT_REG_START(input_data2), TEST_INPUT_REG_SIZE(input_data2),
INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4,
OPTS( -40, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR3, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_data2), TEST_HOLD_REG_SIZE(holding_data2),
HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4,
OPTS( 0, 100, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(test_regs), 58,
HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116,
OPTS( 0, 100, TEST_ASCII_BIN ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 2, 6,
COIL_OFFSET(coils_port0), PARAM_TYPE_U8, 1,
OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_RELAY_P2, STR("RelayP2"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 10, 6,
COIL_OFFSET(coils_port1), PARAM_TYPE_U8, 1,
OPTS( 0x55, 0x2A, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_DISCR_P1, STR("DiscreteInpP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_DISCRETE, 2, 7,
DISCR_OFFSET(discrete_input_port1), PARAM_TYPE_U8, 1,
OPTS( 0xAA, 0x15, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
#if CONFIG_FMB_EXT_TYPE_SUPPORT
{ CID_HOLD_U8_A, STR("U8_A"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u8_a), TEST_HOLD_REG_SIZE(holding_u8_a),
HOLD_OFFSET(holding_u8_a), PARAM_TYPE_U8_A, (TEST_HOLD_REG_SIZE(holding_u8_a) << 1),
OPTS( CHAR_MIN, 0x0055, 0x0055 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U8_B, STR("U8_B"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u8_b), TEST_HOLD_REG_SIZE(holding_u8_b),
HOLD_OFFSET(holding_u8_b), PARAM_TYPE_U8_B, (TEST_HOLD_REG_SIZE(holding_u8_b) << 1),
OPTS( 0, 0x5500, 0x5500 ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U16_AB, STR("U16_AB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u16_ab), TEST_HOLD_REG_SIZE(holding_u16_ab),
HOLD_OFFSET(holding_u16_ab), PARAM_TYPE_U16_AB, (TEST_HOLD_REG_SIZE(holding_u16_ab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_U16_BA, STR("U16_BA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_u16_ba), TEST_HOLD_REG_SIZE(holding_u16_ba),
HOLD_OFFSET(holding_u16_ba), PARAM_TYPE_U16_BA, (TEST_HOLD_REG_SIZE(holding_u16_ab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_ABCD, STR("UINT32_ABCD"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_abcd), TEST_HOLD_REG_SIZE(holding_uint32_abcd),
HOLD_OFFSET(holding_uint32_abcd), PARAM_TYPE_U32_ABCD, (TEST_HOLD_REG_SIZE(holding_uint32_abcd) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_CDAB, STR("UINT32_CDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_cdab), TEST_HOLD_REG_SIZE(holding_uint32_cdab),
HOLD_OFFSET(holding_uint32_cdab), PARAM_TYPE_U32_CDAB, (TEST_HOLD_REG_SIZE(holding_uint32_cdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_BADC, STR("UINT32_BADC"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_badc), TEST_HOLD_REG_SIZE(holding_uint32_badc),
HOLD_OFFSET(holding_uint32_badc), PARAM_TYPE_U32_BADC, (TEST_HOLD_REG_SIZE(holding_uint32_badc) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_UINT32_DCBA, STR("UINT32_DCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_uint32_dcba), TEST_HOLD_REG_SIZE(holding_uint32_dcba),
HOLD_OFFSET(holding_uint32_dcba), PARAM_TYPE_U32_DCBA, (TEST_HOLD_REG_SIZE(holding_uint32_dcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_ABCD, STR("FLOAT_ABCD"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_abcd), TEST_HOLD_REG_SIZE(holding_float_abcd),
HOLD_OFFSET(holding_float_abcd), PARAM_TYPE_FLOAT_ABCD, (TEST_HOLD_REG_SIZE(holding_float_abcd) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_CDAB, STR("FLOAT_CDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_cdab), TEST_HOLD_REG_SIZE(holding_float_cdab),
HOLD_OFFSET(holding_float_cdab), PARAM_TYPE_FLOAT_CDAB, (TEST_HOLD_REG_SIZE(holding_float_cdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_BADC, STR("FLOAT_BADC"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_badc), TEST_HOLD_REG_SIZE(holding_float_badc),
HOLD_OFFSET(holding_float_badc), PARAM_TYPE_FLOAT_BADC, (TEST_HOLD_REG_SIZE(holding_float_badc) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_FLOAT_DCBA, STR("FLOAT_DCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_float_dcba), TEST_HOLD_REG_SIZE(holding_float_dcba),
HOLD_OFFSET(holding_float_dcba), PARAM_TYPE_FLOAT_DCBA, (TEST_HOLD_REG_SIZE(holding_float_dcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_ABCDEFGH, STR("DOUBLE_ABCDEFGH"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_abcdefgh), TEST_HOLD_REG_SIZE(holding_double_abcdefgh),
HOLD_OFFSET(holding_double_abcdefgh), PARAM_TYPE_DOUBLE_ABCDEFGH, (TEST_HOLD_REG_SIZE(holding_double_abcdefgh) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_HGFEDCBA, STR("DOUBLE_HGFEDCBA"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_hgfedcba), TEST_HOLD_REG_SIZE(holding_double_hgfedcba),
HOLD_OFFSET(holding_double_hgfedcba), PARAM_TYPE_DOUBLE_HGFEDCBA, (TEST_HOLD_REG_SIZE(holding_double_hgfedcba) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_GHEFCDAB, STR("DOUBLE_GHEFCDAB"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_ghefcdab), TEST_HOLD_REG_SIZE(holding_double_ghefcdab),
HOLD_OFFSET(holding_double_ghefcdab), PARAM_TYPE_DOUBLE_GHEFCDAB, (TEST_HOLD_REG_SIZE(holding_double_ghefcdab) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER },
{ CID_HOLD_DOUBLE_BADCFEHG, STR("DOUBLE_BADCFEHG"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING,
TEST_HOLD_REG_START(holding_double_badcfehg), TEST_HOLD_REG_SIZE(holding_double_badcfehg),
HOLD_OFFSET(holding_double_badcfehg), PARAM_TYPE_DOUBLE_BADCFEHG, (TEST_HOLD_REG_SIZE(holding_double_badcfehg) << 1),
OPTS( 0, TEST_VALUE, TEST_VALUE ), PAR_PERMS_READ_WRITE_TRIGGER }
#endif
};
// Calculate number of parameters in the table
@ -180,7 +303,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 +312,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 +325,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 +424,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 +446,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 +485,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 +502,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 +510,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 +547,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,17 +589,68 @@ 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;
}
#define TEST_VERIFY_VALUES(pdescr, pinst) (__extension__( \
{ \
assert(pinst); \
assert(pdescr); \
uint8_t type = 0; \
esp_err_t err = ESP_FAIL; \
err = mbc_master_get_parameter(pdescr->cid, (char *)pdescr->param_key, \
(uint8_t *)pinst, &type); \
if (err == ESP_OK) { \
bool is_correct = true; \
if (pdescr->param_opts.opt3) { \
for EACH_ITEM(pinst, pdescr->param_size / sizeof(*pitem)) { \
if (*pitem != (typeof(*(pinst)))pdescr->param_opts.opt3) { \
*pitem = (typeof(*(pinst)))pdescr->param_opts.opt3; \
ESP_LOGD(TAG, "Characteristic #%d (%s), initialize to 0x%" PRIx16 ".", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(uint16_t)pdescr->param_opts.opt3); \
is_correct = false; \
} \
} \
} \
if (!is_correct) { \
ESP_LOGE(TAG, "Characteristic #%d (%s), initialize.", \
(int)pdescr->cid, \
(char *)pdescr->param_key); \
err = mbc_master_set_parameter(cid, (char *)pdescr->param_key, \
(uint8_t *)pinst, &type); \
if (err != ESP_OK) { \
ESP_LOGE(TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(int)err, \
(char *)esp_err_to_name(err)); \
} else { \
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (..) write successful.", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(char *)pdescr->param_units); \
} \
} \
} else { \
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).", \
(int)pdescr->cid, \
(char *)pdescr->param_key, \
(int)err, \
(char *)esp_err_to_name(err)); \
} \
(err); \
} \
))
// User operation function to read slave values and check alarm
static void master_operation_func(void *arg)
{
esp_err_t err = ESP_OK;
float value = 0;
bool alarm_state = false;
const mb_parameter_descriptor_t* param_descriptor = NULL;
@ -484,92 +658,134 @@ static void master_operation_func(void *arg)
for(uint16_t retry = 0; retry <= MASTER_MAX_RETRY && (!alarm_state); retry++) {
// Read all found characteristics from slave(s)
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++)
{
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++) {
// Get data from parameters description table
// and use this information to fill the characteristics description table
// and having all required fields in just one table
err = mbc_master_get_cid_info(cid, &param_descriptor);
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
void* temp_data_ptr = master_get_param_data(param_descriptor);
void *temp_data_ptr = master_get_param_data(param_descriptor);
assert(temp_data_ptr);
uint8_t type = 0;
if ((param_descriptor->param_type == PARAM_TYPE_ASCII) &&
(param_descriptor->cid == CID_HOLD_TEST_REG)) {
// Check for long array of registers of type PARAM_TYPE_ASCII
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);
// 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);
*(uint32_t*)temp_data_ptr = 0xAAAAAAAA;
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,
(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,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
}
}
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
if (TEST_VERIFY_VALUES(param_descriptor, (uint32_t *)temp_data_ptr) == ESP_OK) {
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);
}
} else {
err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
(uint8_t*)temp_data_ptr, &type);
if (err == ESP_OK) {
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,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
value,
*(uint32_t*)temp_data_ptr);
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min))) {
alarm_state = true;
break;
}
} else {
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,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
(const char*)rw_str,
*(uint8_t*)temp_data_ptr);
if (state & param_descriptor->param_opts.opt1) {
alarm_state = true;
break;
}
#if CONFIG_FMB_EXT_TYPE_SUPPORT
} else if ((param_descriptor->cid >= CID_HOLD_U16_AB)
&& (param_descriptor->cid <= CID_HOLD_U16_BA)) {
// Check the uint16 parameters
if (TEST_VERIFY_VALUES(param_descriptor, (uint16_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx16 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint16_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_U8_A)
&& (param_descriptor->cid <= CID_HOLD_U8_B)) {
// Check the uint8 parameters
if (TEST_VERIFY_VALUES(param_descriptor, (uint16_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx16 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint16_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_UINT32_ABCD)
&& (param_descriptor->cid <= CID_HOLD_UINT32_DCBA)) {
// Check the uint32 parameters
if (TEST_VERIFY_VALUES(param_descriptor, (uint32_t *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %" PRIu32 " (0x%" PRIx32 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(uint32_t *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
} else if ((param_descriptor->cid >= CID_HOLD_FLOAT_ABCD)
&& (param_descriptor->cid <= CID_HOLD_FLOAT_DCBA)) {
// Check the float parameters
if (TEST_VERIFY_VALUES(param_descriptor, (float *)temp_data_ptr) == ESP_OK) {
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,
*(float *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
} else if (param_descriptor->cid >= CID_HOLD_DOUBLE_ABCDEFGH) {
// Check the double parameters
if (TEST_VERIFY_VALUES(param_descriptor, (double *)temp_data_ptr) == ESP_OK) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %lf (0x%" PRIx64 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
*(double *)temp_data_ptr,
*(uint64_t *)temp_data_ptr);
}
#endif
} else if (cid <= CID_HOLD_DATA_2) {
uint64_t start_timestamp = esp_timer_get_time(); // Get current timestamp in microseconds
if (TEST_VERIFY_VALUES(param_descriptor, (float *)temp_data_ptr) == ESP_OK) {
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,
*(float *)temp_data_ptr,
*(uint32_t *)temp_data_ptr);
}
float value = *(float *)temp_data_ptr;
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min))) {
alarm_state = true;
break;
}
mb_trans_info_t tinfo = {0}; // The transaction information structure
if (mbc_master_get_transaction_info(&tinfo) == ESP_OK) {
bool trans_is_expired = (tinfo.trans_id >= (start_timestamp + (CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND * 1000)));
ESP_LOGW("TRANS_INFO", "Id: %" PRIu64 ", Addr: %x, FC: 0x%x, Exception: %u, Err: %u %s",
(uint64_t)tinfo.trans_id, (int)tinfo.dest_addr,
(int)tinfo.func_code, (unsigned)tinfo.exception,
(int)tinfo.err_type,
trans_is_expired ? "(EXPIRED)" : "");
// Check if the response time is expired sinse start of transaction,
// or the other IO is performed from different thread.
if (trans_is_expired) {
ESP_LOGE("TRANS_INFO", "Transaction Id: %" PRIu64 ", is expired.", tinfo.trans_id);
alarm_state = true;
break;
}
}
} else if ((cid >= CID_RELAY_P1) && (cid <= CID_DISCR_P1)) {
if (TEST_VERIFY_VALUES(param_descriptor, (uint8_t *)temp_data_ptr) == ESP_OK) {
uint8_t state = *(uint8_t *)temp_data_ptr;
const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
if ((state & param_descriptor->param_opts.opt2) == param_descriptor->param_opts.opt2) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %s (0x%" PRIx8 ") read successful.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
(const char *)rw_str,
*(uint8_t *)temp_data_ptr);
} else {
ESP_LOGE(TAG, "Characteristic #%d %s (%s) value = %s (0x%" PRIx8 "), unexpected value.",
(int)param_descriptor->cid,
(char *)param_descriptor->param_key,
(char *)param_descriptor->param_units,
(const char *)rw_str,
*(uint8_t *)temp_data_ptr);
alarm_state = true;
break;
}
if (state & param_descriptor->param_opts.opt1) {
alarm_state = true;
break;
}
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
}
}
vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
@ -580,7 +796,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 +815,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 +837,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 +851,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 +859,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 +877,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 +908,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 +938,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;
}
@ -735,6 +951,7 @@ void app_main(void)
#else
ip_addr_type = MB_IPV6;
#endif
ESP_ERROR_CHECK(init_services(ip_addr_type));
mb_communication_info_t comm_info = { 0 };
@ -745,7 +962,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,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_TCP_UID_ENABLED=n
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_FMB_EXT_TYPE_SUPPORT=y
CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=120
CONFIG_MB_MDNS_IP_RESOLVER=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
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_FMB_EXT_TYPE_SUPPORT=y
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,13 @@ 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_FMB_EXT_TYPE_SUPPORT=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-H2 | ESP32-P4 | 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 ".")

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"

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