diff --git a/.ci/astyle-rules.yml b/.ci/astyle-rules.yml new file mode 100644 index 0000000..f3a897c --- /dev/null +++ b/.ci/astyle-rules.yml @@ -0,0 +1,31 @@ +DEFAULT: + options: >- + --style=otbs + --indent=spaces=4 + --break-blocks + --pad-oper + --pad-comma + --pad-header + --unpad-paren + --delete-empty-lines + --add-braces + --align-pointer=name + --align-reference=name + --max-continuation-indent=120 + --keep-one-line-statements + --break-after-logical + include: + - "*.c" + - "*.h" + - "*.cpp" + - "*.hpp" + +# Exclude mocked components and generated files +mocks_and_generated: + check: false + include: + - "build*/" + - "components/mocks/" + - "managed_components/" + - "*.pb.h" + - "*.pb.c" diff --git a/.ci/check_copyright_config.yaml b/.ci/check_copyright_config.yaml new file mode 100644 index 0000000..d459127 --- /dev/null +++ b/.ci/check_copyright_config.yaml @@ -0,0 +1,46 @@ +# don't modify this section! +DEFAULT: + perform_check: yes # should the check be performed? + # Sections setting this to 'no' don't need to include any other options as they are ignored + # When a file is using a section with the option set to 'no', no checks are performed. + + # what licenses (or license expressions) are allowed for files in this section + # when setting this option in a section, you need to list all the allowed licenses + allowed_licenses: + - Apache-2.0 + license_for_new_files: Apache-2.0 # license to be used when inserting a new copyright notice + new_notice_c: | # notice for new C, CPP, H, HPP and LD files + /* + * SPDX-FileCopyrightText: {years} Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: {license} + */ + new_notice_python: | # notice for new python files + # SPDX-FileCopyrightText: {years} Espressif Systems (Shanghai) CO LTD + # SPDX-License-Identifier: {license} + + # comment lines matching: + # SPDX-FileCopyrightText: year[-year] Espressif Systems + # or + # SPDX-FileContributor: year[-year] Espressif Systems + # are replaced with this template prefixed with the correct comment notation (# or // or *) and SPDX- notation + espressif_copyright: "{years} Espressif Systems (Shanghai) CO LTD" +examples_and_unit_tests: + include: + - "examples/" + - "test/**" + - "**/pytest_*.py" + allowed_licenses: + - Apache-2.0 + - Unlicense + - CC0-1.0 + license_for_new_files: Unlicense OR CC0-1.0 + +third_party_libs: + include: + - "lib/mqtt_msg.c" + - "lib/include/mqtt_msg.h" + - "test/host/mocks/include/sys/queue.h" + allowed_licenses: + - BSD-3-Clause + - Apache-2.0 diff --git a/.github/workflows/build-and-target-test.yml b/.github/workflows/build-and-target-test.yml index dad45e5..686d50e 100644 --- a/.github/workflows/build-and-target-test.yml +++ b/.github/workflows/build-and-target-test.yml @@ -34,4 +34,3 @@ jobs: # target: ${{inputs.target}} # app_name: ${{inputs.app_name}} # app_path: ${{inputs.app_path}} - diff --git a/.github/workflows/test-examples.yml b/.github/workflows/test-examples.yml index 84dd070..0dc1bef 100644 --- a/.github/workflows/test-examples.yml +++ b/.github/workflows/test-examples.yml @@ -49,4 +49,3 @@ jobs: target: ${{matrix.target}} app_name: ${{matrix.example.name}} app_path: $IDF_PATH/examples/protocols/${{matrix.example.path}} - diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6ccfe31..6521076 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,11 +1,24 @@ stages: - - build - - test - - test_deploy - - deploy + - pre_check + - build + - test + - test_deploy + - deploy + +variables: + TEST_BROKER_BRNO_TCP: "${TEST_BROKER_BRNO_TCP}" + TEST_BROKER_BRNO_SSL: "${TEST_BROKER_BRNO_SSL}" + TEST_BROKER_BRNO_WS: "${TEST_BROKER_BRNO_WS}" + TEST_BROKER_BRNO_WSS: "${TEST_BROKER_BRNO_WSS}" + EXAMPLE_MQTT_BROKER_CERTIFICATE: "${EXAMPLE_MQTT_BROKER_CERTIFICATE}" include: - - local: ".gitlab/ci/build.yml" - - local: ".gitlab/ci/test.yml" - - local: ".gitlab/ci/docs.yml" - - local: ".gitlab/ci/deploy.yml" + - local: ".gitlab/ci/build.yml" + - local: ".gitlab/ci/docs.yml" + - local: ".gitlab/ci/test.yml" + - local: ".gitlab/ci/deploy.yml" + +workflow: + rules: + - if: $CI_COMMIT_REF_NAME == "master" + - if: $CI_PIPELINE_SOURCE == "merge_request_event" diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index 84e536a..b9a2a94 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -6,9 +6,12 @@ tags: - build - internet + timeout: 1h script: - pip install -U 'idf-ci<1' - idf-ci build run + --parallel-count ${CI_NODE_TOTAL:-1} + --parallel-index ${CI_NODE_INDEX:-1} build_idf_v5.3: extends: .build_template diff --git a/.gitlab/ci/deploy.yml b/.gitlab/ci/deploy.yml index 38920d1..f65c3a2 100644 --- a/.gitlab/ci/deploy.yml +++ b/.gitlab/ci/deploy.yml @@ -11,19 +11,9 @@ push_master_to_github: variables: GIT_STRATEGY: clone script: - - source ${CI_PROJECT_DIR}/.gitlab/ci/utils.sh - - add_github_remote "$GH_PUSH_KEY" "$GH_PUSH_REPO" + - curl -sSL ${CIT_LOADER_URL} | sh + - source citools/import_functions + - cit_add_ssh_key "${GH_PUSH_KEY}" + - git remote remove github || true + - git remote add github ${GH_PUSH_REPO} - git push github HEAD:${CI_COMMIT_REF_NAME} - -upload_to_component_manager: - stage: deploy - image: python:3.10-alpine - tags: - - deploy - rules: - - if: '$CI_COMMIT_BRANCH == "master"' - script: - - pip install idf-component-manager - - export IDF_COMPONENT_API_TOKEN=${MQTT_COMPONENT_API_KEY} - - export COMP_VERSION=$(grep 'version:' idf_component.yml | head -n 1 | awk '{print $2}' | tr -d '"') - - compote component upload --namespace=espressif --name=mqtt --allow-existing --version=${COMP_VERSION} diff --git a/.gitlab/ci/docs.yml b/.gitlab/ci/docs.yml index 5402d09..0ee004e 100644 --- a/.gitlab/ci/docs.yml +++ b/.gitlab/ci/docs.yml @@ -9,8 +9,6 @@ docs_build: tags: - build_docs variables: - # Set Python buffering for better CI output - PYTHONUNBUFFERED: 1 TYPE: "preview" DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/" artifacts: @@ -63,20 +61,3 @@ deploy_docs_preview: DOCS_DEPLOY_SERVER_USER: "$DOCS_PREVIEW_SERVER_USER" DOCS_DEPLOY_PATH: "$DOCS_PREVIEW_PATH" DOCS_DEPLOY_URL_BASE: "$DOCS_PREVIEW_URL_BASE" - -deploy_docs_prod: - extends: - - .deploy_docs_template - stage: deploy - only: - refs: - - master - needs: - - docs_build - variables: - TYPE: "production" - DOCS_DEPLOY_PRIVATEKEY: "$DOCS_PROD_PRIVATEKEY" - DOCS_DEPLOY_SERVER: "$DOCS_PROD_SERVER" - DOCS_DEPLOY_SERVER_USER: "$DOCS_PROD_SERVER_USER" - DOCS_DEPLOY_PATH: "$DOCS_PROD_PATH" - DOCS_DEPLOY_URL_BASE: "$DOCS_PROD_URL_BASE" diff --git a/.gitlab/ci/pre-check.yml b/.gitlab/ci/pre-check.yml new file mode 100644 index 0000000..77b700d --- /dev/null +++ b/.gitlab/ci/pre-check.yml @@ -0,0 +1,25 @@ +# Pre-check jobs for ESP-MQTT + +.check_pre_commit_template: + stage: pre_check + image: "${CI_DOCKER_REGISTRY}/esp-idf-pre-commit:2" + tags: [build, internet] + variables: + GIT_STRATEGY: fetch + GIT_DEPTH: 1 + SUBMODULES_TO_FETCH: "all" + script: + - pre-commit install + - pre-commit run --all-files + cache: + paths: + - .cache/submodule_archives + policy: pull + +check_pre_commit: + extends: + - .check_pre_commit_template + rules: + - if: '$CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "push"' + when: never + - when: on_success diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b36df72 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,76 @@ +minimum_pre_commit_version: 4.0.0 # Specifies the minimum version of pre-commit required for this configuration +default_install_hook_types: [pre-commit,commit-msg] # Default hook types to install if not specified in individual hooks +default_stages: [pre-commit] + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace # Removes trailing whitespaces from lines + exclude: &whitespace_excludes | + (?x)^( + .+\.(md|rst|map|bin|drawio|elf)| + .+\.log$| + compile_commands\.json$| + dependencies\.lock$| + build.*/.*| + __pycache__/.* + )$ + - id: end-of-file-fixer # Ensures files end with a newline + exclude: *whitespace_excludes + - id: check-executables-have-shebangs # Checks executables have a proper shebang + - id: mixed-line-ending # Detects mixed line endings (CRLF/LF) + args: ['-f=lf'] # Forces files to use LF line endings + - id: double-quote-string-fixer # Converts single quotes to double quotes in strings + + - repo: https://github.com/espressif/check-copyright/ + rev: v1.1.1 + hooks: + - id: check-copyright + args: ['--config', '.ci/check_copyright_config.yaml'] + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.11.5 + hooks: + - id: ruff # Runs ruff linter (replaces flake8) + args: ['--fix', '--exit-non-zero-on-fix'] + files: \.py$ + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.15.0 + hooks: + - id: mypy # Runs mypy for Python type checking + additional_dependencies: ['PyYAML', 'types-PyYAML'] + files: \.py$ + exclude: ^docs/.*/conf\.py$ + + - repo: https://github.com/espressif/conventional-precommit-linter + rev: v1.10.0 + hooks: + - id: conventional-precommit-linter # Lints commit messages for conventional format + stages: [commit-msg] + args: + - --types=ci,docs,feat,fix,perf,refactor,chore + - --scopes=mqtt,mqtt5,examples + - --subject-min-length=10 + + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + hooks: + - id: codespell # Code spell checker + args: ["--write-changes"] + additional_dependencies: [tomli] + exclude: | + (?x)^( + .+\.(bin|elf|map)| + build.*/.*| + dependencies\.lock$| + compile_commands\.json$| + __pycache__/.* + )$ + + - repo: https://github.com/espressif/astyle_py.git + rev: v1.1.0 + hooks: + - id: astyle_py + args: ['--astyle-version=3.4.7', '--rules=.ci/astyle-rules.yml'] diff --git a/.travis.yml b/.travis.yml index 4affec1..a49fe66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,4 +62,4 @@ script: - cd $IDF_PATH/examples/protocols/mqtt/ws - idf.py build - cd $IDF_PATH/examples/protocols/mqtt/wss - - idf.py build \ No newline at end of file + - idf.py build diff --git a/Kconfig b/Kconfig index d438858..5397f70 100644 --- a/Kconfig +++ b/Kconfig @@ -185,6 +185,6 @@ menu "ESP-MQTT Configurations" Set to true to have publish topic in all data events. This changes the behaviour when the message is bigger than the receive buffer size. The first event of the sequence always have the topic. - Note: This will allocate memory to store the topic only in case of messge bigger than the buffer size. + Note: This will allocate memory to store the topic only in case of message bigger than the buffer size. endmenu diff --git a/docs/conf_common.py b/docs/conf_common.py index 7e21f29..3c9aa62 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 # -*- coding: utf-8 -*- # # Common (non-language-specific) configuration for Sphinx @@ -6,6 +8,7 @@ # type: ignore # pylint: disable=wildcard-import # pylint: disable=undefined-variable +# ruff: noqa: F405 from __future__ import print_function, unicode_literals @@ -15,7 +18,7 @@ from esp_docs.conf_docs import * # noqa: F403,F401 # Only required when using ESP-IDF extensions that depend on IDF environment -extensions += ['sphinx_copybutton', +extensions += ['sphinx_copybutton', # noqa: F405 # Needed as a trigger for running doxygen 'esp_docs.esp_extensions.dummy_build_system', 'esp_docs.esp_extensions.run_doxygen' @@ -25,8 +28,8 @@ extensions += ['sphinx_copybutton', github_repo = 'espressif/esp-mqtt' # context used by sphinx_idf_theme -html_context['github_user'] = 'espressif' -html_context['github_repo'] = 'esp-mqtt' +html_context['github_user'] = 'espressif' # noqa: F405 +html_context['github_repo'] = 'esp-mqtt' # noqa: F405 # Extra options required by sphinx_idf_theme project_slug = 'esp-mqtt' @@ -34,6 +37,3 @@ versions_url = './_static/mqtt_docs_versions.js' idf_targets = [ 'esp32' ] languages = ['en'] - - - diff --git a/docs/doxygen-known-warnings.txt b/docs/doxygen-known-warnings.txt index 1ad7d8a..95e1c1b 100644 --- a/docs/doxygen-known-warnings.txt +++ b/docs/doxygen-known-warnings.txt @@ -1,2 +1,2 @@ # Known doxygen warnings for ESP-MQTT documentation build -# Currently no known doxygen warnings expected \ No newline at end of file +# Currently no known doxygen warnings expected diff --git a/docs/en/conf.py b/docs/en/conf.py index be95ec8..6b2f40a 100644 --- a/docs/en/conf.py +++ b/docs/en/conf.py @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 # -*- coding: utf-8 -*- # # English Language RTD & Sphinx config file diff --git a/docs/zh_CN/conf.py b/docs/zh_CN/conf.py index 0eadade..b047b41 100644 --- a/docs/zh_CN/conf.py +++ b/docs/zh_CN/conf.py @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 # -*- coding: utf-8 -*- # # English Language RTD & Sphinx config file diff --git a/examples/custom_outbox/main/app_main.c b/examples/custom_outbox/main/app_main.c index b30bc46..d332a82 100644 --- a/examples/custom_outbox/main/app_main.c +++ b/examples/custom_outbox/main/app_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -27,7 +27,6 @@ static const char *TAG = "MQTT_EXAMPLE"; - static void log_error_if_nonzero(const char *message, int error_code) { if (error_code != 0) { @@ -51,19 +50,18 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; int msg_id; + switch ((esp_mqtt_event_id_t)event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - break; + case MQTT_EVENT_DISCONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); break; @@ -73,27 +71,33 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_UNSUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_PUBLISHED: ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_DATA: ESP_LOGI(TAG, "MQTT_EVENT_DATA"); printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); printf("DATA=%.*s\r\n", event->data_len, event->data); break; + case MQTT_EVENT_ERROR: ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err); log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err); log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno); ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno)); - } + break; + default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; @@ -111,8 +115,10 @@ static void mqtt_app_start(void) if (strcmp(mqtt_cfg.broker.address.uri, "FROM_STDIN") == 0) { int count = 0; printf("Please enter url of mqtt broker\n"); + while (count < 128) { int c = fgetc(stdin); + if (c == '\n') { line[count] = '\0'; break; @@ -120,27 +126,27 @@ static void mqtt_app_start(void) line[count] = c; ++count; } + vTaskDelay(10 / portTICK_PERIOD_MS); } + mqtt_cfg.broker.address.uri = line; printf("Broker url: %s\n", line); } else { ESP_LOGE(TAG, "Configuration mismatch: wrong broker url"); abort(); } -#endif /* CONFIG_BROKER_URL_FROM_STDIN */ +#endif /* CONFIG_BROKER_URL_FROM_STDIN */ esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); - /*Let's enqueue a few messages to the outbox to see the allocations*/ int msg_id; msg_id = esp_mqtt_client_enqueue(client, "/topic/qos1", "data_3", 0, 1, 0, true); ESP_LOGI(TAG, "Enqueued msg_id=%d", msg_id); msg_id = esp_mqtt_client_enqueue(client, "/topic/qos2", "QoS2 message", 0, 2, 0, true); ESP_LOGI(TAG, "Enqueued msg_id=%d", msg_id); - /* Now we start the client and it's possible to see the memory usage for the operations in the outbox. */ esp_mqtt_client_start(client); } @@ -150,7 +156,6 @@ void app_main(void) ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); - esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); esp_log_level_set("MQTT_EXAMPLE", ESP_LOG_VERBOSE); @@ -158,16 +163,13 @@ void app_main(void) esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE); esp_log_level_set("custom_outbox", ESP_LOG_VERBOSE); - ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - mqtt_app_start(); } diff --git a/examples/custom_outbox/main/custom_outbox.cpp b/examples/custom_outbox/main/custom_outbox.cpp index 2db9d7e..07688e2 100644 --- a/examples/custom_outbox/main/custom_outbox.cpp +++ b/examples/custom_outbox/main/custom_outbox.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -24,9 +24,11 @@ constexpr auto TAG = "custom_outbox"; * The trace resource class is created here as an example on how to build a custom memory resource * The class is only needed to show where we are allocating from and to track allocations and deallocations. */ -class trace_resource : public std::pmr::memory_resource { +class trace_resource : public std::pmr::memory_resource +{ public: - explicit trace_resource(std::string resource_name, std::pmr::memory_resource *upstream_resource = std::pmr::get_default_resource()) : upstream{upstream_resource}, name{std::move(resource_name)} {} + explicit trace_resource(std::string resource_name, + std::pmr::memory_resource *upstream_resource = std::pmr::get_default_resource()) : upstream{upstream_resource}, name{std::move(resource_name)} {} [[nodiscard]] std::string_view get_name() const noexcept { return std::string_view(name); @@ -77,11 +79,14 @@ struct outbox_item { outbox_tick_t tick, pending_state_t pending_state, allocator_type alloc = {} - ) : message(std::move(message), alloc), id(msg_id), type(msg_type), qos(msg_qos), tick(tick), pending_state(pending_state) {} + ) : message(std::move(message), alloc), id(msg_id), type(msg_type), qos(msg_qos), tick(tick), + pending_state(pending_state) {} /*Copy and move constructors have an extra allocator parameter, for copy default and allocator aware are the same.*/ - outbox_item(const outbox_item &other, allocator_type alloc = {}) : message(other.message, alloc), id(other.id), type(other.type), qos(other.qos), tick(other.tick), pending_state(other.pending_state) {} - outbox_item(outbox_item &&other, allocator_type alloc) noexcept : message(std::move(other.message), alloc), id(other.id), type(other.type), qos(other.qos), tick(other.tick), pending_state(other.pending_state) + outbox_item(const outbox_item &other, allocator_type alloc = {}) : message(other.message, alloc), id(other.id), + type(other.type), qos(other.qos), tick(other.tick), pending_state(other.pending_state) {} + outbox_item(outbox_item &&other, allocator_type alloc) noexcept : message(std::move(other.message), alloc), + id(other.id), type(other.type), qos(other.qos), tick(other.tick), pending_state(other.pending_state) {} outbox_item(const outbox_item &) = default; @@ -175,6 +180,7 @@ struct outbox_t { total_size -= item.get_size(); return true; } + return false; }); } @@ -228,7 +234,8 @@ struct outbox_t { QUEUED ); total_size += item.get_size(); - ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%" PRIu64, message->msg_id, message->msg_type, message->len + message->remaining_len, outbox_get_size(this)); + ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%" PRIu64, message->msg_id, message->msg_type, + message->len + message->remaining_len, outbox_get_size(this)); return &item; } catch (const std::exception &e) { return nullptr; @@ -244,6 +251,7 @@ struct outbox_t { if (tick != nullptr) { *tick = item->get_tick(); } + return &(*item); } return nullptr; @@ -260,6 +268,7 @@ private: queue.erase(to_erase); return ESP_OK; } + return ESP_FAIL; } std::size_t total_size{}; @@ -273,17 +282,17 @@ extern "C" { /* First we create a fixed size memory buffer to be used. */ static constexpr auto work_memory_size = 16 * 1024; static std::array resource_buffer{}; + try { /* * Since the outbox is managed by a C API we can't rely on C++ automatic cleanup and smart pointers but, on production code it would be better to add the * memory resources to outbox_t, applying RAII principles, and make only outbox_item allocator aware. For the sake of the example we are keeping them - * separated to explictly show the relations. - * First we create the monotonic buffer and add null_memory_resource as upstream. This way if our working memory is exausted an exception is thrown. + * separated to explicitly show the relations. + * First we create the monotonic buffer and add null_memory_resource as upstream. This way if our working memory is exhausted an exception is thrown. */ auto *monotonic_resource = new std::pmr::monotonic_buffer_resource{resource_buffer.data(), resource_buffer.size(), std::pmr::null_memory_resource()}; /*Here we add our custom trace wrapper type to trace allocations and deallocations*/ auto *trace_monotonic = new trace_resource("Monotonic", monotonic_resource); - /* We compose monotonic buffer with pool resource, since the monotonic deallocate is a no-op and we need to remove messages to not go out of memory.*/ auto *pool_resource = new std::pmr::unsynchronized_pool_resource{trace_monotonic}; auto *trace_pool = new trace_resource("Pool", pool_resource); @@ -293,7 +302,6 @@ extern "C" { } catch (const std::exception &e) { ESP_LOGD(TAG, "Not enough memory to construct the outbox, review the resource_buffer size"); return nullptr; - } } @@ -318,13 +326,13 @@ uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t if (item == nullptr) { return nullptr; } + return item->get_data(len, msg_id, msg_type, qos); } esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item_to_delete) { return outbox->erase(item_to_delete); - } esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type) @@ -356,6 +364,7 @@ pending_state_t outbox_item_get_pending(outbox_item_handle_t item) if (item != nullptr) { return item->state(); } + return QUEUED; } @@ -384,7 +393,6 @@ void outbox_destroy(outbox_handle_t outbox) auto *pool_resource = static_cast(trace_pool->upstream_resource()); auto *trace_monotonic = static_cast(pool_resource->upstream_resource()); auto *monotonic_resource = static_cast(trace_monotonic->upstream_resource()); - delete monotonic_resource; delete trace_monotonic; delete pool_resource; diff --git a/examples/mqtt5/main/app_main.c b/examples/mqtt5/main/app_main.c index e82fd70..4890bd6 100644 --- a/examples/mqtt5/main/app_main.c +++ b/examples/mqtt5/main/app_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -26,10 +26,10 @@ static void log_error_if_nonzero(const char *message, int error_code) } static esp_mqtt5_user_property_item_t user_property_arr[] = { - {"board", "esp32"}, - {"u", "user"}, - {"p", "password"} - }; + {"board", "esp32"}, + {"u", "user"}, + {"p", "password"} +}; #define USE_PROPERTY_ARR_SIZE sizeof(user_property_arr)/sizeof(esp_mqtt5_user_property_item_t) @@ -72,8 +72,10 @@ static void print_user_property(mqtt5_user_property_handle_t user_property) { if (user_property) { uint8_t count = esp_mqtt5_client_get_user_property_count(user_property); + if (count) { esp_mqtt5_user_property_item_t *item = malloc(count * sizeof(esp_mqtt5_user_property_item_t)); + if (esp_mqtt5_client_get_user_property(user_property, item, &count) == ESP_OK) { for (int i = 0; i < count; i ++) { esp_mqtt5_user_property_item_t *t = &item[i]; @@ -82,6 +84,7 @@ static void print_user_property(mqtt5_user_property_handle_t user_property) free((char *)t->value); } } + free(item); } } @@ -103,8 +106,9 @@ static void mqtt5_event_handler(void *handler_args, esp_event_base_t base, int32 esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; int msg_id; + ESP_LOGD(TAG, "free heap size is %" PRIu32 ", minimum %" PRIu32, esp_get_free_heap_size(), + esp_get_minimum_free_heap_size()); - ESP_LOGD(TAG, "free heap size is %" PRIu32 ", minimum %" PRIu32, esp_get_free_heap_size(), esp_get_minimum_free_heap_size()); switch ((esp_mqtt_event_id_t)event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); @@ -115,21 +119,18 @@ static void mqtt5_event_handler(void *handler_args, esp_event_base_t base, int32 esp_mqtt5_client_delete_user_property(publish_property.user_property); publish_property.user_property = NULL; ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); - esp_mqtt5_client_set_user_property(&subscribe_property.user_property, user_property_arr, USE_PROPERTY_ARR_SIZE); esp_mqtt5_client_set_subscribe_property(client, &subscribe_property); msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); esp_mqtt5_client_delete_user_property(subscribe_property.user_property); subscribe_property.user_property = NULL; ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - esp_mqtt5_client_set_user_property(&subscribe1_property.user_property, user_property_arr, USE_PROPERTY_ARR_SIZE); esp_mqtt5_client_set_subscribe_property(client, &subscribe1_property); msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 2); esp_mqtt5_client_delete_user_property(subscribe1_property.user_property); subscribe1_property.user_property = NULL; ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - esp_mqtt5_client_set_user_property(&unsubscribe_property.user_property, user_property_arr, USE_PROPERTY_ARR_SIZE); esp_mqtt5_client_set_unsubscribe_property(client, &unsubscribe_property); msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos0"); @@ -137,10 +138,12 @@ static void mqtt5_event_handler(void *handler_args, esp_event_base_t base, int32 esp_mqtt5_client_delete_user_property(unsubscribe_property.user_property); unsubscribe_property.user_property = NULL; break; + case MQTT_EVENT_DISCONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); print_user_property(event->property->user_property); break; + case MQTT_EVENT_SUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d, reason code=0x%02x ", event->msg_id, (uint8_t)*event->data); print_user_property(event->property->user_property); @@ -148,6 +151,7 @@ static void mqtt5_event_handler(void *handler_args, esp_event_base_t base, int32 msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_UNSUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); print_user_property(event->property->user_property); @@ -157,10 +161,12 @@ static void mqtt5_event_handler(void *handler_args, esp_event_base_t base, int32 disconnect_property.user_property = NULL; esp_mqtt_client_disconnect(client); break; + case MQTT_EVENT_PUBLISHED: ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); print_user_property(event->property->user_property); break; + case MQTT_EVENT_DATA: ESP_LOGI(TAG, "MQTT_EVENT_DATA"); print_user_property(event->property->user_property); @@ -171,17 +177,21 @@ static void mqtt5_event_handler(void *handler_args, esp_event_base_t base, int32 ESP_LOGI(TAG, "TOPIC=%.*s", event->topic_len, event->topic); ESP_LOGI(TAG, "DATA=%.*s", event->data_len, event->data); break; + case MQTT_EVENT_ERROR: ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); print_user_property(event->property->user_property); ESP_LOGI(TAG, "MQTT5 return code is %d", event->error_handle->connect_return_code); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err); log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err); log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno); ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno)); } + break; + default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; @@ -204,7 +214,6 @@ static void mqtt5_app_start(void) .correlation_data = "123456", .correlation_data_len = 6, }; - esp_mqtt_client_config_t mqtt5_cfg = { .broker.address.uri = CONFIG_BROKER_URL, .session.protocol_ver = MQTT_PROTOCOL_V_5, @@ -217,15 +226,16 @@ static void mqtt5_app_start(void) .session.last_will.qos = 1, .session.last_will.retain = true, }; - #if CONFIG_BROKER_URL_FROM_STDIN char line[128]; if (strcmp(mqtt5_cfg.uri, "FROM_STDIN") == 0) { int count = 0; printf("Please enter url of mqtt broker\n"); + while (count < 128) { int c = fgetc(stdin); + if (c == '\n') { line[count] = '\0'; break; @@ -233,29 +243,28 @@ static void mqtt5_app_start(void) line[count] = c; ++count; } + vTaskDelay(10 / portTICK_PERIOD_MS); } + mqtt5_cfg.broker.address.uri = line; printf("Broker url: %s\n", line); } else { ESP_LOGE(TAG, "Configuration mismatch: wrong broker url"); abort(); } + #endif /* CONFIG_BROKER_URL_FROM_STDIN */ - esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt5_cfg); - /* Set connection properties and user properties */ esp_mqtt5_client_set_user_property(&connect_property.user_property, user_property_arr, USE_PROPERTY_ARR_SIZE); esp_mqtt5_client_set_user_property(&connect_property.will_user_property, user_property_arr, USE_PROPERTY_ARR_SIZE); esp_mqtt5_client_set_connect_property(client, &connect_property); - /* If you call esp_mqtt5_client_set_user_property to set user properties, DO NOT forget to delete them. * esp_mqtt5_client_set_connect_property will malloc buffer to store the user_property and you can delete it after */ esp_mqtt5_client_delete_user_property(connect_property.user_property); esp_mqtt5_client_delete_user_property(connect_property.will_user_property); - /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt5_event_handler, NULL); esp_mqtt_client_start(client); @@ -263,11 +272,9 @@ static void mqtt5_app_start(void) void app_main(void) { - ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); - esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); esp_log_level_set("mqtt_example", ESP_LOG_VERBOSE); @@ -275,16 +282,13 @@ void app_main(void) esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); esp_log_level_set("transport", ESP_LOG_VERBOSE); esp_log_level_set("outbox", ESP_LOG_VERBOSE); - ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - mqtt5_app_start(); } diff --git a/examples/ssl/main/app_main.c b/examples/ssl/main/app_main.c index ed5e04f..048857d 100644 --- a/examples/ssl/main/app_main.c +++ b/examples/ssl/main/app_main.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* MQTT over SSL Example This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -26,9 +31,9 @@ static const char *TAG = "mqtts_example"; - #if CONFIG_BROKER_CERTIFICATE_OVERRIDDEN == 1 -static const uint8_t mqtt_eclipseprojects_io_pem_start[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_BROKER_CERTIFICATE_OVERRIDE "\n-----END CERTIFICATE-----"; +static const uint8_t mqtt_eclipseprojects_io_pem_start[] = "-----BEGIN CERTIFICATE-----\n" + CONFIG_BROKER_CERTIFICATE_OVERRIDE "\n-----END CERTIFICATE-----"; #else extern const uint8_t mqtt_eclipseprojects_io_pem_start[] asm("_binary_mqtt_eclipseprojects_io_pem_start"); #endif @@ -66,18 +71,18 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; int msg_id; + switch ((esp_mqtt_event_id_t)event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_DISCONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); break; @@ -87,23 +92,30 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_UNSUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_PUBLISHED: ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_DATA: ESP_LOGI(TAG, "MQTT_EVENT_DATA"); printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); printf("DATA=%.*s\r\n", event->data_len, event->data); + if (strncmp(event->data, "send binary please", event->data_len) == 0) { ESP_LOGI(TAG, "Sending the binary"); send_binary(client); } + break; + case MQTT_EVENT_ERROR: ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { ESP_LOGI(TAG, "Last error code reported from esp-tls: 0x%x", event->error_handle->esp_tls_last_esp_err); ESP_LOGI(TAG, "Last tls stack error number: 0x%x", event->error_handle->esp_tls_stack_err); @@ -114,7 +126,9 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ } else { ESP_LOGW(TAG, "Unknown error type: 0x%x", event->error_handle->error_type); } + break; + default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; @@ -129,7 +143,6 @@ static void mqtt_app_start(void) .verification.certificate = (const char *)mqtt_eclipseprojects_io_pem_start }, }; - ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ @@ -142,7 +155,6 @@ void app_main(void) ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); - esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); @@ -150,16 +162,13 @@ void app_main(void) esp_log_level_set("transport_base", ESP_LOG_VERBOSE); esp_log_level_set("transport", ESP_LOG_VERBOSE); esp_log_level_set("outbox", ESP_LOG_VERBOSE); - ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - mqtt_app_start(); } diff --git a/examples/ssl/pytest_mqtt_ssl.py b/examples/ssl/pytest_mqtt_ssl.py index c34cea6..3a8beaa 100644 --- a/examples/ssl/pytest_mqtt_ssl.py +++ b/examples/ssl/pytest_mqtt_ssl.py @@ -11,7 +11,7 @@ from threading import Thread import paho.mqtt.client as mqtt import pexpect import pytest -from pytest_embedded import Dut +from pytest_embedded import Dut # noqa: F401 from pytest_embedded_idf.utils import idf_parametrize event_client_connected = Event() diff --git a/examples/ssl_ds/README.md b/examples/ssl_ds/README.md index f89d172..2796cea 100644 --- a/examples/ssl_ds/README.md +++ b/examples/ssl_ds/README.md @@ -2,14 +2,16 @@ | ----------------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | # ESP-MQTT SSL Mutual Authentication with Digital Signature + (See the README.md file in the upper level 'examples' directory for more information about examples.) Espressif's ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2 and ESP32-P4 MCU have a built-in Digital Signature (DS) Peripheral, which provides hardware acceleration for RSA signature. More details can be found at [Digital Signature with ESP-TLS](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/protocols/esp_tls.html#digital-signature-with-esp-tls). This example connects to the broker test.mosquitto.org using ssl transport with client certificate(RSA) and as a demonstration subscribes/unsubscribes and sends a message on certain topic.The RSA signature operation required in the ssl connection is performed with help of the Digital Signature (DS) peripheral. -(Please note that the public broker is maintained by the community so may not be always available, for details please visit http://test.mosquitto.org) +(Please note that the public broker is maintained by the community so may not be always available, for details please visit ) It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker. + ## How to use example ### Hardware Required @@ -21,9 +23,11 @@ This example can be executed on any of the supported ESP32 family board (which h #### 1) Selecting the target Please select the supported target with the following command: + ``` idf.py set-target /* target */ ``` + More details can be found at [Selecting the target](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html#selecting-the-target). #### 2) Generate your client key and certificate @@ -46,15 +50,18 @@ Paste the generated CSR in the [Mosquitto test certificate signer](https://test. #### 3) Configure the DS peripheral * i) Install the [esp_secure_cert configuration utility](https://github.com/espressif/esp_secure_cert_mgr/tree/main/tools#esp_secure_cert-configuration-tool) with following command: + ``` pip install esp-secure-cert-tool ``` + * ii) The DS peripheral can be configured by executing the following command: ``` configure_esp_secure_cert.py -p /* Serial port */ --device-cert /* Device cert */ --private-key /* RSA priv key */ --target_chip /* target chip */ --configure_ds --skip_flash ``` -This command shall generate a partition named `esp_secure_cert.bin` in the `esp_secure_cert_data` directory. This partition would be aumatically detected by the build system and flashed at appropriate offset when `idf.py flash` command is used. For this process, the command must be executed in the current folder only. + +This command shall generate a partition named `esp_secure_cert.bin` in the `esp_secure_cert_data` directory. This partition would be automatically detected by the build system and flashed at appropriate offset when `idf.py flash` command is used. For this process, the command must be executed in the current folder only. In the command USB COM port is nothing but the serial port to which the ESP chip is connected. see [check serial port](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/establish-serial-connection.html#check-port-on-windows) for more details. @@ -63,6 +70,7 @@ RSA private key is nothing but the client private key ( RSA ) generated in Step > Note: More details about the `esp-secure-cert-tool` utility can be found [here](https://github.com/espressif/esp_secure_cert_mgr/tree/main/tools). #### 4) Connection configuration + * Open the project configuration menu (`idf.py menuconfig`) * Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details. diff --git a/examples/ssl_ds/main/app_main.c b/examples/ssl_ds/main/app_main.c index 7b54cfb..d06622c 100644 --- a/examples/ssl_ds/main/app_main.c +++ b/examples/ssl_ds/main/app_main.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* MQTT Mutual Authentication Example This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -51,19 +56,19 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; int msg_id; + // your_context_t *context = event->context; switch ((esp_mqtt_event_id_t)event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_DISCONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); break; @@ -73,20 +78,25 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_UNSUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_PUBLISHED: ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_DATA: ESP_LOGI(TAG, "MQTT_EVENT_DATA"); printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); printf("DATA=%.*s\r\n", event->data_len, event->data); break; + case MQTT_EVENT_ERROR: ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); break; + default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; @@ -97,14 +107,17 @@ static void mqtt_app_start(void) { /* The context is used by the DS peripheral, should not be freed */ esp_ds_data_ctx_t *ds_data = esp_secure_cert_get_ds_ctx(); + if (ds_data == NULL) { ESP_LOGE(TAG, "Error in reading DS data from NVS"); vTaskDelete(NULL); } + char *device_cert = NULL; esp_err_t ret; uint32_t len; ret = esp_secure_cert_get_device_cert(&device_cert, &len); + if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to obtain the device certificate"); vTaskDelete(NULL); @@ -113,7 +126,7 @@ static void mqtt_app_start(void) const esp_mqtt_client_config_t mqtt_cfg = { .broker = { .address.uri = "mqtts://test.mosquitto.org:8884", - .verification.certificate = (const char *)server_cert_pem_start, + .verification.certificate = (const char *)server_cert_pem_start, }, .credentials = { .authentication = { @@ -123,7 +136,6 @@ static void mqtt_app_start(void) }, }, }; - ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); @@ -135,22 +147,18 @@ void app_main(void) ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); - esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); esp_log_level_set("transport_base", ESP_LOG_VERBOSE); esp_log_level_set("transport", ESP_LOG_VERBOSE); esp_log_level_set("outbox", ESP_LOG_VERBOSE); - ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - mqtt_app_start(); } diff --git a/examples/ssl_mutual_auth/main/app_main.c b/examples/ssl_mutual_auth/main/app_main.c index 90ba71c..4409215 100644 --- a/examples/ssl_mutual_auth/main/app_main.c +++ b/examples/ssl_mutual_auth/main/app_main.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* MQTT Mutual Authentication Example This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -61,18 +66,18 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; int msg_id; + switch ((esp_mqtt_event_id_t)event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_DISCONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); break; @@ -82,27 +87,33 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_UNSUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_PUBLISHED: ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_DATA: ESP_LOGI(TAG, "MQTT_EVENT_DATA"); printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); printf("DATA=%.*s\r\n", event->data_len, event->data); break; + case MQTT_EVENT_ERROR: ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err); log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err); log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno); ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno)); - } + break; + default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; @@ -111,17 +122,16 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ static void mqtt_app_start(void) { - const esp_mqtt_client_config_t mqtt_cfg = { - .broker.address.uri = "mqtts://test.mosquitto.org:8884", - .broker.verification.certificate = (const char *)server_cert_pem_start, - .credentials = { - .authentication = { - .certificate = (const char *)client_cert_pem_start, - .key = (const char *)client_key_pem_start, - }, - } - }; - + const esp_mqtt_client_config_t mqtt_cfg = { + .broker.address.uri = "mqtts://test.mosquitto.org:8884", + .broker.verification.certificate = (const char *)server_cert_pem_start, + .credentials = { + .authentication = { + .certificate = (const char *)client_cert_pem_start, + .key = (const char *)client_key_pem_start, + }, + } + }; ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ @@ -134,22 +144,18 @@ void app_main(void) ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); - esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); esp_log_level_set("transport_base", ESP_LOG_VERBOSE); esp_log_level_set("transport", ESP_LOG_VERBOSE); esp_log_level_set("outbox", ESP_LOG_VERBOSE); - ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - mqtt_app_start(); } diff --git a/examples/ssl_psk/main/app_main.c b/examples/ssl_psk/main/app_main.c index ee9e818..0428737 100644 --- a/examples/ssl_psk/main/app_main.c +++ b/examples/ssl_psk/main/app_main.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* MQTT over SSL Example This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -47,10 +52,10 @@ static const char *TAG = "mqtts_example"; static const uint8_t s_key[] = { 0xBA, 0xD1, 0x23 }; static const psk_hint_key_t psk_hint_key = { - .key = s_key, - .key_size = sizeof(s_key), - .hint = "hint" - }; + .key = s_key, + .key_size = sizeof(s_key), + .hint = "hint" +}; /* * @brief Event handler registered to receive MQTT events @@ -68,55 +73,58 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; int msg_id; + switch ((esp_mqtt_event_id_t)event_id) { - case MQTT_EVENT_CONNECTED: - ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); - ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); + ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); + break; - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); - ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; - msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); - ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); - break; - case MQTT_EVENT_DISCONNECTED: - ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); - break; + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d, return code=0x%02x ", event->msg_id, (uint8_t)*event->data); + msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + break; - case MQTT_EVENT_SUBSCRIBED: - ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d, return code=0x%02x ", event->msg_id, (uint8_t)*event->data); - msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); - ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); - break; - case MQTT_EVENT_UNSUBSCRIBED: - ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); - break; - case MQTT_EVENT_PUBLISHED: - ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); - break; - case MQTT_EVENT_DATA: - ESP_LOGI(TAG, "MQTT_EVENT_DATA"); - printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); - printf("DATA=%.*s\r\n", event->data_len, event->data); - break; - case MQTT_EVENT_ERROR: - ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); - break; - default: - ESP_LOGI(TAG, "Other event id:%d", event->event_id); - break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + break; + + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + break; + + default: + ESP_LOGI(TAG, "Other event id:%d", event->event_id); + break; } } - static void mqtt_app_start(void) { const esp_mqtt_client_config_t mqtt_cfg = { .broker.address.uri = EXAMPLE_BROKER_URI, .broker.verification.psk_hint_key = &psk_hint_key, }; - ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ @@ -129,23 +137,19 @@ void app_main(void) ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); - esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); esp_log_level_set("transport_base", ESP_LOG_VERBOSE); esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); esp_log_level_set("transport", ESP_LOG_VERBOSE); esp_log_level_set("outbox", ESP_LOG_VERBOSE); - ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - mqtt_app_start(); } diff --git a/examples/tcp/main/app_main.c b/examples/tcp/main/app_main.c index 396aca6..6fa4ee3 100644 --- a/examples/tcp/main/app_main.c +++ b/examples/tcp/main/app_main.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* MQTT (over TCP) Example This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -23,7 +28,6 @@ static const char *TAG = "mqtt_example"; - static void log_error_if_nonzero(const char *message, int error_code) { if (error_code != 0) { @@ -47,21 +51,20 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; int msg_id; + switch ((esp_mqtt_event_id_t)event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_DISCONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); break; @@ -71,27 +74,33 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_UNSUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_PUBLISHED: ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_DATA: ESP_LOGI(TAG, "MQTT_EVENT_DATA"); printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); printf("DATA=%.*s\r\n", event->data_len, event->data); break; + case MQTT_EVENT_ERROR: ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err); log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err); log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno); ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno)); - } + break; + default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; @@ -109,8 +118,10 @@ static void mqtt_app_start(void) if (strcmp(mqtt_cfg.broker.address.uri, "FROM_STDIN") == 0) { int count = 0; printf("Please enter url of mqtt broker\n"); + while (count < 128) { int c = fgetc(stdin); + if (c == '\n') { line[count] = '\0'; break; @@ -118,16 +129,18 @@ static void mqtt_app_start(void) line[count] = c; ++count; } + vTaskDelay(10 / portTICK_PERIOD_MS); } + mqtt_cfg.broker.address.uri = line; printf("Broker url: %s\n", line); } else { ESP_LOGE(TAG, "Configuration mismatch: wrong broker url"); abort(); } -#endif /* CONFIG_BROKER_URL_FROM_STDIN */ +#endif /* CONFIG_BROKER_URL_FROM_STDIN */ esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); @@ -139,7 +152,6 @@ void app_main(void) ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); - esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); esp_log_level_set("mqtt_example", ESP_LOG_VERBOSE); @@ -147,16 +159,13 @@ void app_main(void) esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); esp_log_level_set("transport", ESP_LOG_VERBOSE); esp_log_level_set("outbox", ESP_LOG_VERBOSE); - ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - mqtt_app_start(); } diff --git a/examples/ws/main/app_main.c b/examples/ws/main/app_main.c index f518fdf..0afa263 100644 --- a/examples/ws/main/app_main.c +++ b/examples/ws/main/app_main.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* MQTT over Websockets Example This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -54,6 +59,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; int msg_id; + switch ((esp_mqtt_event_id_t)event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); @@ -61,13 +67,12 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_DISCONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); break; @@ -77,27 +82,33 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_UNSUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_PUBLISHED: ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_DATA: ESP_LOGI(TAG, "MQTT_EVENT_DATA"); printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); printf("DATA=%.*s\r\n", event->data_len, event->data); break; + case MQTT_EVENT_ERROR: ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err); log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err); log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno); ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno)); - } + break; + default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; @@ -109,7 +120,6 @@ static void mqtt_app_start(void) const esp_mqtt_client_config_t mqtt_cfg = { .broker.address.uri = CONFIG_BROKER_URI, }; - esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); @@ -121,7 +131,6 @@ void app_main(void) ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); - esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); esp_log_level_set("mqtt_example", ESP_LOG_VERBOSE); @@ -129,16 +138,13 @@ void app_main(void) esp_log_level_set("transport_ws", ESP_LOG_VERBOSE); esp_log_level_set("transport", ESP_LOG_VERBOSE); esp_log_level_set("outbox", ESP_LOG_VERBOSE); - ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - mqtt_app_start(); } diff --git a/examples/wss/main/app_main.c b/examples/wss/main/app_main.c index 3de96c0..566ef5b 100644 --- a/examples/wss/main/app_main.c +++ b/examples/wss/main/app_main.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* MQTT over Secure Websockets Example This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -31,9 +36,9 @@ static const char *TAG = "mqttwss_example"; - #if CONFIG_BROKER_CERTIFICATE_OVERRIDDEN == 1 -static const uint8_t mqtt_eclipseprojects_io_pem_start[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_BROKER_CERTIFICATE_OVERRIDE "\n-----END CERTIFICATE-----"; +static const uint8_t mqtt_eclipseprojects_io_pem_start[] = "-----BEGIN CERTIFICATE-----\n" + CONFIG_BROKER_CERTIFICATE_OVERRIDE "\n-----END CERTIFICATE-----"; #else extern const uint8_t mqtt_eclipseprojects_io_pem_start[] asm("_binary_mqtt_eclipseprojects_io_pem_start"); #endif @@ -43,19 +48,19 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) { esp_mqtt_client_handle_t client = event->client; int msg_id; + // your_context_t *context = event->context; switch (event->event_id) { case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); - msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_DISCONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); break; @@ -65,24 +70,30 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); break; + case MQTT_EVENT_UNSUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_PUBLISHED: ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_DATA: ESP_LOGI(TAG, "MQTT_EVENT_DATA"); printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); printf("DATA=%.*s\r\n", event->data_len, event->data); break; + case MQTT_EVENT_ERROR: ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); break; + default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; } + return ESP_OK; } @@ -99,12 +110,10 @@ static void mqtt_app_start(void) .broker.address.uri = CONFIG_BROKER_URI, .broker.verification.certificate = (const char *)mqtt_eclipseprojects_io_pem_start, }; - ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); - esp_mqtt_client_start(client); } @@ -113,23 +122,19 @@ void app_main(void) ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); - esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); esp_log_level_set("mqtt_example", ESP_LOG_VERBOSE); esp_log_level_set("transport_base", ESP_LOG_VERBOSE); esp_log_level_set("transport", ESP_LOG_VERBOSE); esp_log_level_set("outbox", ESP_LOG_VERBOSE); - ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ ESP_ERROR_CHECK(example_connect()); - mqtt_app_start(); } diff --git a/examples/wss/pytest_mqtt_wss_example.py b/examples/wss/pytest_mqtt_wss_example.py index cb4a22e..308a89e 100644 --- a/examples/wss/pytest_mqtt_wss_example.py +++ b/examples/wss/pytest_mqtt_wss_example.py @@ -13,7 +13,7 @@ from threading import Thread import paho.mqtt.client as mqtt import pexpect import pytest -from pytest_embedded import Dut +from pytest_embedded import Dut # noqa: F401 from pytest_embedded_idf.utils import idf_parametrize event_client_connected = Event() @@ -49,7 +49,7 @@ def on_message(client, userdata, msg): # type: (mqtt.Client, tuple, mqtt.client @pytest.mark.ethernet @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_examples_protocol_mqtt_wss(dut): # type: (Dut) -> None +def test_examples_protocol_mqtt_wss(dut): # type: (Dut) -> None # type: ignore broker_url = '' broker_port = 0 """ diff --git a/idf_component.yml b/idf_component.yml index adf10f4..a2b3622 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -19,5 +19,3 @@ tags: dependencies: idf: version: ">=5.3" - - diff --git a/include/mqtt5_client.h b/include/mqtt5_client.h index 25254c4..0353a7e 100644 --- a/include/mqtt5_client.h +++ b/include/mqtt5_client.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -76,15 +76,18 @@ typedef struct { uint16_t topic_alias_maximum; /*!< The maximum topic alias that we support */ bool request_resp_info; /*!< This value to request Server to return Response information */ bool request_problem_info; /*!< This value to indicate whether the reason string or user properties are sent in case of failures */ - mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */ + mqtt5_user_property_handle_t + user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */ uint32_t will_delay_interval; /*!< The time interval that server delays publishing will message */ uint32_t message_expiry_interval; /*!< The time interval that message expiry */ bool payload_format_indicator; /*!< This value is to indicator will message payload format */ - const char *content_type; /*!< This value is to indicator will message content type, use a MIME content type string */ + const char + *content_type; /*!< This value is to indicator will message content type, use a MIME content type string */ const char *response_topic; /*!< Topic name for a response message */ const char *correlation_data; /*!< Binary data for receiver to match the response message */ uint16_t correlation_data_len; /*!< The length of correlation data */ - mqtt5_user_property_handle_t will_user_property; /*!< The handle for will message user property, call function esp_mqtt5_client_set_user_property to set it */ + mqtt5_user_property_handle_t + will_user_property; /*!< The handle for will message user property, call function esp_mqtt5_client_set_user_property to set it */ } esp_mqtt5_connection_property_config_t; /** @@ -93,12 +96,14 @@ typedef struct { typedef struct { bool payload_format_indicator; /*!< This value is to indicator publish message payload format */ uint32_t message_expiry_interval; /*!< The time interval that message expiry */ - uint16_t topic_alias; /*!< An interger value to identify the topic instead of using topic name string */ + uint16_t topic_alias; /*!< An integer value to identify the topic instead of using topic name string */ const char *response_topic; /*!< Topic name for a response message */ const char *correlation_data; /*!< Binary data for receiver to match the response message */ uint16_t correlation_data_len; /*!< The length of correlation data */ - const char *content_type; /*!< This value is to indicator publish message content type, use a MIME content type string */ - mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */ + const char + *content_type; /*!< This value is to indicator publish message content type, use a MIME content type string */ + mqtt5_user_property_handle_t + user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */ } esp_mqtt5_publish_property_config_t; /** @@ -110,8 +115,10 @@ typedef struct { bool retain_as_published_flag; /*!< Subscription Option to keep the retain flag as published option */ uint8_t retain_handle; /*!< Subscription Option to handle retain option */ bool is_share_subscribe; /*!< Whether subscribe is a shared subscription */ - const char *share_name; /*!< The name of shared subscription which is a part of $share/{share_name}/{topic} */ - mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */ + const char + *share_name; /*!< The name of shared subscription which is a part of $share/{share_name}/{topic} */ + mqtt5_user_property_handle_t + user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */ } esp_mqtt5_subscribe_property_config_t; /** @@ -119,8 +126,10 @@ typedef struct { */ typedef struct { bool is_share_subscribe; /*!< Whether subscribe is a shared subscription */ - const char *share_name; /*!< The name of shared subscription which is a part of $share/{share_name}/{topic} */ - mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */ + const char + *share_name; /*!< The name of shared subscription which is a part of $share/{share_name}/{topic} */ + mqtt5_user_property_handle_t + user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */ } esp_mqtt5_unsubscribe_property_config_t; /** @@ -128,8 +137,9 @@ typedef struct { */ typedef struct { uint32_t session_expiry_interval; /*!< The interval time of session expiry */ - uint8_t disconnect_reason; /*!< The reason that connection disconnet, refer to mqtt5_error_reason_code */ - mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */ + uint8_t disconnect_reason; /*!< The reason that connection disconnect, refer to mqtt5_error_reason_code */ + mqtt5_user_property_handle_t + user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */ } esp_mqtt5_disconnect_property_config_t; /** @@ -144,7 +154,8 @@ typedef struct { char *content_type; /*!< Content type of the message */ int content_type_len; /*!< Content type length of the message */ uint16_t subscribe_id; /*!< Subscription identifier of the message */ - mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_delete_user_property to free the memory */ + mqtt5_user_property_handle_t + user_property; /*!< The handle for user property, call function esp_mqtt5_client_delete_user_property to free the memory */ } esp_mqtt5_event_property_t; /** @@ -166,7 +177,8 @@ typedef struct { * ESP_FAIL on fail * ESP_OK on success */ -esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_connection_property_config_t *connect_property); +esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client, + const esp_mqtt5_connection_property_config_t *connect_property); /** * @brief Set MQTT5 client publish property configuration @@ -181,7 +193,8 @@ esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client * ESP_FAIL on fail * ESP_OK on success */ -esp_err_t esp_mqtt5_client_set_publish_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_publish_property_config_t *property); +esp_err_t esp_mqtt5_client_set_publish_property(esp_mqtt5_client_handle_t client, + const esp_mqtt5_publish_property_config_t *property); /** * @brief Set MQTT5 client subscribe property configuration @@ -196,7 +209,8 @@ esp_err_t esp_mqtt5_client_set_publish_property(esp_mqtt5_client_handle_t client * ESP_FAIL on fail * ESP_OK on success */ -esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_subscribe_property_config_t *property); +esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t client, + const esp_mqtt5_subscribe_property_config_t *property); /** * @brief Set MQTT5 client unsubscribe property configuration @@ -211,7 +225,8 @@ esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t clie * ESP_FAIL on fail * ESP_OK on success */ -esp_err_t esp_mqtt5_client_set_unsubscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_unsubscribe_property_config_t *property); +esp_err_t esp_mqtt5_client_set_unsubscribe_property(esp_mqtt5_client_handle_t client, + const esp_mqtt5_unsubscribe_property_config_t *property); /** * @brief Set MQTT5 client disconnect property configuration @@ -227,7 +242,8 @@ esp_err_t esp_mqtt5_client_set_unsubscribe_property(esp_mqtt5_client_handle_t cl * ESP_FAIL on fail * ESP_OK on success */ -esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_disconnect_property_config_t *property); +esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t client, + const esp_mqtt5_disconnect_property_config_t *property); /** * @brief Set MQTT5 client user property configuration @@ -244,7 +260,8 @@ esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t cli * ESP_FAIL on fail * ESP_OK on success */ -esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_property, esp_mqtt5_user_property_item_t item[], uint8_t item_num); +esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_property, + esp_mqtt5_user_property_item_t item[], uint8_t item_num); /** * @brief Get MQTT5 client user property @@ -261,7 +278,8 @@ esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_ * ESP_FAIL on fail * ESP_OK on success */ -esp_err_t esp_mqtt5_client_get_user_property(mqtt5_user_property_handle_t user_property, esp_mqtt5_user_property_item_t *item, uint8_t *item_num); +esp_err_t esp_mqtt5_client_get_user_property(mqtt5_user_property_handle_t user_property, + esp_mqtt5_user_property_item_t *item, uint8_t *item_num); /** * @brief Get MQTT5 client user property list count diff --git a/include/mqtt_client.h b/include/mqtt_client.h index 4627585..5618f16 100644 --- a/include/mqtt_client.h +++ b/include/mqtt_client.h @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* * This file is subject to the terms and conditions defined in * file 'LICENSE', which is part of this source code package. @@ -215,7 +220,8 @@ typedef struct esp_mqtt_event_t { bool retain; /*!< Retained flag of the message associated with this event */ int qos; /*!< QoS of the messages associated with this event */ bool dup; /*!< dup flag of the message associated with this event */ - esp_mqtt_protocol_ver_t protocol_ver; /*!< MQTT protocol version used for connection, defaults to value from menuconfig*/ + esp_mqtt_protocol_ver_t + protocol_ver; /*!< MQTT protocol version used for connection, defaults to value from menuconfig*/ #ifdef CONFIG_MQTT_PROTOCOL_5 esp_mqtt5_event_property_t *property; /*!< MQTT 5 property associated with this event */ #endif @@ -260,7 +266,8 @@ typedef struct esp_mqtt_client_config_t { documentation for details. */ esp_err_t (*crt_bundle_attach)(void *conf); /*!< Pointer to ESP x509 Certificate Bundle attach function for the usage of certificate bundles. Client only attach the bundle, the clean up must be done by the user. */ - const char *certificate; /*!< Certificate data, default is NULL. It's not copied nor freed by the client, user needs to clean up.*/ + const char + *certificate; /*!< Certificate data, default is NULL. It's not copied nor freed by the client, user needs to clean up.*/ size_t certificate_len; /*!< Length of the buffer pointed to by certificate. */ const struct psk_key_hint *psk_hint_key; /*!< Pointer to PSK struct defined in esp_tls.h to enable PSK authentication (as alternative to certificate verification). @@ -274,7 +281,7 @@ typedef struct esp_mqtt_client_config_t { If NULL, server certificate CN must match hostname. This is ignored if skip_cert_common_name_check=true. It's not copied nor freed by the client, user needs to clean up.*/ - const int *ciphersuites_list; /*!< Pointer to a zero-terminated array of IANA identifiers of TLS cipher suites. + const int *ciphersuites_list; /*!< Pointer to a zero-terminated array of IANA identifiers of TLS cipher suites. Please ensure the validity of the list, and note that it is not copied or freed by the client. */ } verification; /*!< Security verification of the broker */ } broker; /*!< Broker address and security verification */ @@ -343,7 +350,7 @@ typedef struct esp_mqtt_client_config_t { * Network related configuration */ struct network_t { - int reconnect_timeout_ms; /*!< Reconnect to the broker after this value in miliseconds if auto reconnect is not + int reconnect_timeout_ms; /*!< Reconnect to the broker after this value in milliseconds if auto reconnect is not disabled (defaults to 10s) */ int timeout_ms; /*!< Abort network operation if it is not completed after this value, in milliseconds (defaults to 10s). */ @@ -351,8 +358,9 @@ typedef struct esp_mqtt_client_config_t { bool disable_auto_reconnect; /*!< Client will reconnect to server (when errors/disconnect). Set `disable_auto_reconnect=true` to disable */ esp_transport_keep_alive_t tcp_keep_alive_cfg; /*!< Transport keep-alive config*/ - esp_transport_handle_t transport; /*!< Custom transport handle to use, leave it NULL to allow MQTT client create or recreate its own. Warning: The transport should be valid during the client lifetime and is destroyed when esp_mqtt_client_destroy is called. */ - struct ifreq * if_name; /*!< The name of interface for data to go through. Use the default interface without setting */ + esp_transport_handle_t + transport; /*!< Custom transport handle to use, leave it NULL to allow MQTT client create or recreate its own. Warning: The transport should be valid during the client lifetime and is destroyed when esp_mqtt_client_destroy is called. */ + struct ifreq *if_name; /*!< The name of interface for data to go through. Use the default interface without setting */ } network; /*!< Network configuration */ /** * Client task configuration @@ -364,7 +372,7 @@ typedef struct esp_mqtt_client_config_t { /** * Client buffer size configuration * - * Client have two buffers for input and output respectivelly. + * Client have two buffers for input and output respectively. */ struct buffer_t { int size; /*!< size of *MQTT* send/receive buffer*/ @@ -614,7 +622,7 @@ esp_err_t esp_mqtt_client_destroy(esp_mqtt_client_handle_t client); * (i.e. on "before_connect" event * * Notes: - * - When calling this function make sure to have all the intendend configurations + * - When calling this function make sure to have all the intended configurations * set, otherwise default values are set. * @param client *MQTT* client handle * @@ -655,7 +663,8 @@ esp_err_t esp_mqtt_client_register_event(esp_mqtt_client_handle_t client, * ESP_ERR_INVALID_ARG on invalid event ID * ESP_OK on success */ -esp_err_t esp_mqtt_client_unregister_event(esp_mqtt_client_handle_t client, esp_mqtt_event_id_t event, esp_event_handler_t event_handler); +esp_err_t esp_mqtt_client_unregister_event(esp_mqtt_client_handle_t client, esp_mqtt_event_id_t event, + esp_event_handler_t event_handler); /** * @brief Get outbox size diff --git a/include/mqtt_supported_features.h b/include/mqtt_supported_features.h index 6036e5f..2b46145 100644 --- a/include/mqtt_supported_features.h +++ b/include/mqtt_supported_features.h @@ -1,16 +1,8 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #ifndef _MQTT_SUPPORTED_FEATURES_H_ #define _MQTT_SUPPORTED_FEATURES_H_ diff --git a/lib/include/mqtt5_client_priv.h b/lib/include/mqtt5_client_priv.h index 1b5f04b..b3d355b 100644 --- a/lib/include/mqtt5_client_priv.h +++ b/lib/include/mqtt5_client_priv.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -48,9 +48,10 @@ void esp_mqtt5_client_destory(esp_mqtt5_client_handle_t client); esp_err_t esp_mqtt5_client_publish_check(esp_mqtt5_client_handle_t client, int qos, int retain); esp_err_t esp_mqtt5_client_subscribe_check(esp_mqtt5_client_handle_t client, int qos); esp_err_t esp_mqtt5_create_default_config(esp_mqtt5_client_handle_t client); -esp_err_t esp_mqtt5_get_publish_data(esp_mqtt5_client_handle_t client, uint8_t *msg_buf, size_t msg_read_len, char **msg_topic, size_t *msg_topic_len, char **msg_data, size_t *msg_data_len); +esp_err_t esp_mqtt5_get_publish_data(esp_mqtt5_client_handle_t client, uint8_t *msg_buf, size_t msg_read_len, + char **msg_topic, size_t *msg_topic_len, char **msg_data, size_t *msg_data_len); #ifdef __cplusplus } #endif //__cplusplus -#endif \ No newline at end of file +#endif diff --git a/lib/include/mqtt5_msg.h b/lib/include/mqtt5_msg.h index 6c2a036..cd93ef8 100644 --- a/lib/include/mqtt5_msg.h +++ b/lib/include/mqtt5_msg.h @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #ifndef MQTT5_MSG_H #define MQTT5_MSG_H #include @@ -119,16 +124,26 @@ typedef struct { #define mqtt5_get_pubcomp_data mqtt5_get_puback_data uint16_t mqtt5_get_id(uint8_t *buffer, size_t length); -char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length, char **msg_topic, size_t *msg_topic_len, esp_mqtt5_publish_resp_property_t *resp_property, uint16_t *property_len, size_t *payload_len, mqtt5_user_property_handle_t *user_property); +char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length, char **msg_topic, size_t *msg_topic_len, + esp_mqtt5_publish_resp_property_t *resp_property, uint16_t *property_len, size_t *payload_len, + mqtt5_user_property_handle_t *user_property); char *mqtt5_get_suback_data(uint8_t *buffer, size_t *length, mqtt5_user_property_handle_t *user_property); char *mqtt5_get_puback_data(uint8_t *buffer, size_t *length, mqtt5_user_property_handle_t *user_property); -mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info, esp_mqtt5_connection_property_storage_t *property, esp_mqtt5_connection_will_property_storage_t *will_property); -mqtt_message_t *mqtt5_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, int qos, int retain, uint16_t *message_id, const esp_mqtt5_publish_property_config_t *property, const char *resp_info); -esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, mqtt_connect_info_t *connection_info, esp_mqtt5_connection_property_storage_t *connection_property, esp_mqtt5_connection_server_resp_property_t *resp_property, int *reason_code, uint8_t *ack_flag, mqtt5_user_property_handle_t *user_property); +mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info, + esp_mqtt5_connection_property_storage_t *property, esp_mqtt5_connection_will_property_storage_t *will_property); +mqtt_message_t *mqtt5_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, + int qos, int retain, uint16_t *message_id, const esp_mqtt5_publish_property_config_t *property, const char *resp_info); +esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, mqtt_connect_info_t *connection_info, + esp_mqtt5_connection_property_storage_t *connection_property, + esp_mqtt5_connection_server_resp_property_t *resp_property, int *reason_code, uint8_t *ack_flag, + mqtt5_user_property_handle_t *user_property); int mqtt5_msg_get_reason_code(uint8_t *buffer, size_t length); -mqtt_message_t *mqtt5_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t *topic, int size, uint16_t *message_id, const esp_mqtt5_subscribe_property_config_t *property); -mqtt_message_t *mqtt5_msg_unsubscribe(mqtt_connection_t *connection, const char *topic, uint16_t *message_id, const esp_mqtt5_unsubscribe_property_config_t *property); -mqtt_message_t *mqtt5_msg_disconnect(mqtt_connection_t *connection, esp_mqtt5_disconnect_property_config_t *disconnect_property_info); +mqtt_message_t *mqtt5_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t *topic, int size, + uint16_t *message_id, const esp_mqtt5_subscribe_property_config_t *property); +mqtt_message_t *mqtt5_msg_unsubscribe(mqtt_connection_t *connection, const char *topic, uint16_t *message_id, + const esp_mqtt5_unsubscribe_property_config_t *property); +mqtt_message_t *mqtt5_msg_disconnect(mqtt_connection_t *connection, + esp_mqtt5_disconnect_property_config_t *disconnect_property_info); mqtt_message_t *mqtt5_msg_pubcomp(mqtt_connection_t *connection, uint16_t message_id); mqtt_message_t *mqtt5_msg_pubrel(mqtt_connection_t *connection, uint16_t message_id); mqtt_message_t *mqtt5_msg_pubrec(mqtt_connection_t *connection, uint16_t message_id); @@ -139,4 +154,3 @@ mqtt_message_t *mqtt5_msg_puback(mqtt_connection_t *connection, uint16_t message #endif #endif /* MQTT5_MSG_H */ - diff --git a/lib/include/mqtt_client_priv.h b/lib/include/mqtt_client_priv.h index c1ed038..38c047e 100644 --- a/lib/include/mqtt_client_priv.h +++ b/lib/include/mqtt_client_priv.h @@ -102,7 +102,7 @@ typedef struct { int message_retransmit_timeout; uint64_t outbox_limit; esp_transport_handle_t transport; - struct ifreq * if_name; + struct ifreq *if_name; esp_transport_keep_alive_t tcp_keep_alive_cfg; } mqtt_config_storage_t; diff --git a/lib/include/mqtt_config.h b/lib/include/mqtt_config.h index d96e2ad..9224557 100644 --- a/lib/include/mqtt_config.h +++ b/lib/include/mqtt_config.h @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* * This file is subject to the terms and conditions defined in * file 'LICENSE', which is part of this source code package. diff --git a/lib/include/mqtt_msg.h b/lib/include/mqtt_msg.h index f09590d..4b23881 100644 --- a/lib/include/mqtt_msg.h +++ b/lib/include/mqtt_msg.h @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2014 Stephen Robinson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + #ifndef MQTT_MSG_H #define MQTT_MSG_H #include @@ -9,41 +15,10 @@ extern "C" { #endif -/* -* Copyright (c) 2014, Stephen Robinson -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* 3. Neither the name of the copyright holder nor the names of its -* contributors may be used to endorse or promote products derived -* from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -* -*/ /* 7 6 5 4 3 2 1 0 */ /*| --- Message Type---- | DUP Flag | QoS Level | Retain | */ /* Remaining Length */ - enum mqtt_message_type { MQTT_MSG_TYPE_CONNECT = 1, MQTT_MSG_TYPE_CONNACK = 2, @@ -134,12 +109,14 @@ esp_err_t mqtt_msg_buffer_init(mqtt_connection_t *connection, int buffer_size); void mqtt_msg_buffer_destroy(mqtt_connection_t *connection); mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info); -mqtt_message_t *mqtt_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, int qos, int retain, uint16_t *message_id); +mqtt_message_t *mqtt_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, + int qos, int retain, uint16_t *message_id); mqtt_message_t *mqtt_msg_puback(mqtt_connection_t *connection, uint16_t message_id); mqtt_message_t *mqtt_msg_pubrec(mqtt_connection_t *connection, uint16_t message_id); mqtt_message_t *mqtt_msg_pubrel(mqtt_connection_t *connection, uint16_t message_id); mqtt_message_t *mqtt_msg_pubcomp(mqtt_connection_t *connection, uint16_t message_id); -mqtt_message_t *mqtt_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t topic_list[], int size, uint16_t *message_id) __attribute__((nonnull)); +mqtt_message_t *mqtt_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t topic_list[], int size, + uint16_t *message_id) __attribute__((nonnull)); mqtt_message_t *mqtt_msg_unsubscribe(mqtt_connection_t *connection, const char *topic, uint16_t *message_id); mqtt_message_t *mqtt_msg_pingreq(mqtt_connection_t *connection); mqtt_message_t *mqtt_msg_pingresp(mqtt_connection_t *connection); @@ -149,4 +126,3 @@ mqtt_message_t *mqtt_msg_disconnect(mqtt_connection_t *connection); #endif #endif /* MQTT_MSG_H */ - diff --git a/lib/include/mqtt_outbox.h b/lib/include/mqtt_outbox.h index 241b335..a096f0a 100644 --- a/lib/include/mqtt_outbox.h +++ b/lib/include/mqtt_outbox.h @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* * This file is subject to the terms and conditions defined in * file 'LICENSE', which is part of this source code package. diff --git a/lib/include/platform.h b/lib/include/platform.h index b3358b9..c647a4b 100644 --- a/lib/include/platform.h +++ b/lib/include/platform.h @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* * This file is subject to the terms and conditions defined in * file 'LICENSE', which is part of this source code package. diff --git a/lib/include/platform_esp32_idf.h b/lib/include/platform_esp32_idf.h index 98f5fe1..9623afe 100644 --- a/lib/include/platform_esp32_idf.h +++ b/lib/include/platform_esp32_idf.h @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* * This file is subject to the terms and conditions defined in * file 'LICENSE', which is part of this source code package. diff --git a/lib/mqtt5_msg.c b/lib/mqtt5_msg.c index 32ff929..628a9f8 100644 --- a/lib/mqtt5_msg.c +++ b/lib/mqtt5_msg.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include "mqtt5_msg.h" #include "mqtt_client.h" @@ -36,14 +41,18 @@ enum mqtt5_connect_flag { static void generate_variable_len(size_t len, uint8_t *len_bytes, uint8_t *encoded_lens) { uint8_t bytes = 0; + do { uint8_t i = len % 128; len /= 128; + if (len > 0) { i |= 0x80; } + encoded_lens[bytes ++] = i; } while (len > 0); + *len_bytes = bytes; } @@ -51,13 +60,16 @@ static size_t get_variable_len(uint8_t *buffer, size_t offset, size_t buffer_len { *len_bytes = 0; size_t len = 0, i = 0; + for (i = offset; i < buffer_length; i ++) { len += (buffer[i] & 0x7f) << (7 * (i - offset)); + if ((buffer[i] & 0x80) == 0) { i ++; break; } } + *len_bytes = i - offset; return len; } @@ -68,8 +80,8 @@ static int update_property_len_value(mqtt_connection_t *connection, size_t prope size_t len = property_len, message_offset = property_offset + property_len; generate_variable_len(len, &len_bytes, encoded_lens); int offset = len_bytes - 1; - connection->outbound_message.length += offset; + if (connection->outbound_message.length > connection->buffer_length) { return -1; } @@ -84,16 +96,20 @@ static int update_property_len_value(mqtt_connection_t *connection, size_t prope for (int i = 0; i < len_bytes; i ++) { connection->buffer[property_offset ++] = encoded_lens[i]; } + return offset; } -static int append_property(mqtt_connection_t *connection, uint8_t property_type, uint8_t len_occupy, const char *data, size_t data_len) +static int append_property(mqtt_connection_t *connection, uint8_t property_type, uint8_t len_occupy, const char *data, + size_t data_len) { - if ((connection->outbound_message.length + len_occupy + (data ? data_len : 0) + (property_type ? 1 : 0)) > connection->buffer_length) { + if ((connection->outbound_message.length + len_occupy + (data ? data_len : 0) + (property_type ? 1 : 0)) > + connection->buffer_length) { return -1; } size_t origin_message_len = connection->outbound_message.length; + if (property_type) { connection->buffer[connection->outbound_message.length ++] = property_type; } @@ -101,6 +117,7 @@ static int append_property(mqtt_connection_t *connection, uint8_t property_type, if (len_occupy == 0) { uint8_t encoded_lens[4] = {0}, len_bytes = 0; generate_variable_len(data_len, &len_bytes, encoded_lens); + for (int j = 0; j < len_bytes; j ++) { connection->buffer[connection->outbound_message.length ++] = encoded_lens[j]; } @@ -135,7 +152,6 @@ static uint16_t append_message_id(mqtt_connection_t *connection, uint16_t messag } MQTT5_CONVERT_TWO_BYTE(connection->buffer[connection->outbound_message.length ++], message_id) - return message_id; } @@ -157,6 +173,7 @@ static mqtt_message_t *fini_message(mqtt_connection_t *connection, int type, int int message_length = connection->outbound_message.length - MQTT5_MAX_FIXED_HEADER_SIZE; int total_length = message_length; uint8_t encoded_lens[4] = {0}, len_bytes = 0; + // Check if we have fragmented message and update total_len if (connection->outbound_message.fragmented_msg_total_length) { total_length = connection->outbound_message.fragmented_msg_total_length - MQTT5_MAX_FIXED_HEADER_SIZE; @@ -176,7 +193,8 @@ static mqtt_message_t *fini_message(mqtt_connection_t *connection, int type, int connection->outbound_message.data = connection->buffer + offs; connection->outbound_message.fragmented_msg_data_offset -= offs; // type byte - connection->buffer[offs ++] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[offs ++] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + // length bytes for (int j = 0; j < len_bytes; j ++) { connection->buffer[offs ++] = encoded_lens[j]; @@ -185,7 +203,8 @@ static mqtt_message_t *fini_message(mqtt_connection_t *connection, int type, int return &connection->outbound_message; } -static esp_err_t mqtt5_msg_set_user_property(mqtt5_user_property_handle_t *user_property, char *key, size_t key_len, char *value, size_t value_len) +static esp_err_t mqtt5_msg_set_user_property(mqtt5_user_property_handle_t *user_property, char *key, size_t key_len, + char *value, size_t value_len) { if (!*user_property) { *user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t)); @@ -202,7 +221,6 @@ static esp_err_t mqtt5_msg_set_user_property(mqtt5_user_property_handle_t *user_ }); memcpy(user_property_item->key, key, key_len); user_property_item->key[key_len] = '\0'; - user_property_item->value = calloc(1, value_len + 1); ESP_MEM_CHECK(TAG, user_property_item->value, { free(user_property_item->key); @@ -211,7 +229,6 @@ static esp_err_t mqtt5_msg_set_user_property(mqtt5_user_property_handle_t *user_ }); memcpy(user_property_item->value, value, value_len); user_property_item->value[value_len] = '\0'; - STAILQ_INSERT_TAIL(*user_property, user_property_item, next); return ESP_OK; } @@ -221,14 +238,17 @@ static mqtt5_user_property_handle_t mqtt5_msg_get_user_property(uint8_t *buffer, mqtt5_user_property_handle_t user_porperty = NULL; uint8_t *property = buffer; uint16_t property_offset = 0, len = 0; + while (property_offset < buffer_length) { uint8_t property_id = property[property_offset ++]; + switch (property_id) { case MQTT5_PROPERTY_REASON_STRING: //only print now MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_REASON_STRING %.*s", len, &property[property_offset]); property_offset += len; continue; + case MQTT5_PROPERTY_USER_PROPERTY: { uint8_t *key = NULL, *value = NULL; size_t key_len = 0, value_len = 0; @@ -242,17 +262,21 @@ static mqtt5_user_property_handle_t mqtt5_msg_get_user_property(uint8_t *buffer, value_len = len; ESP_LOGD(TAG, "MQTT5_PROPERTY_USER_PROPERTY value: %.*s", value_len, (char *)value); property_offset += len; + if (mqtt5_msg_set_user_property(&user_porperty, (char *)key, key_len, (char *)value, value_len) != ESP_OK) { ESP_LOGE(TAG, "mqtt5_msg_set_user_property fail"); goto err; } + continue; } + default: - ESP_LOGW(TAG, "Unknow property id 0x%02x", property_id); + ESP_LOGW(TAG, "Unknown property id 0x%02x", property_id); goto err; } } + return user_porperty; err: esp_mqtt5_client_delete_user_property(user_porperty); @@ -276,14 +300,18 @@ uint16_t mqtt5_get_id(uint8_t *buffer, size_t length) case MQTT_MSG_TYPE_PUBLISH: { MQTT5_CONVERT_ONE_BYTE_TO_TWO(topiclen, buffer[offset++], buffer[offset++]) offset += topiclen; + if (offset + 2 > length) { return 0; } + if (mqtt_get_qos(buffer) == 0) { return 0; } + return (buffer[offset] << 8) | buffer[offset + 1]; } + case MQTT_MSG_TYPE_PUBACK: case MQTT_MSG_TYPE_PUBREC: case MQTT_MSG_TYPE_PUBREL: @@ -294,12 +322,15 @@ uint16_t mqtt5_get_id(uint8_t *buffer, size_t length) case MQTT_MSG_TYPE_UNSUBSCRIBE: { return (buffer[offset] << 8) | buffer[offset + 1]; } + default: return 0; } } -char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length, char **msg_topic, size_t *msg_topic_len, esp_mqtt5_publish_resp_property_t *resp_property, uint16_t *property_len, size_t *payload_len, mqtt5_user_property_handle_t *user_property) +char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length, char **msg_topic, size_t *msg_topic_len, + esp_mqtt5_publish_resp_property_t *resp_property, uint16_t *property_len, size_t *payload_len, + mqtt5_user_property_handle_t *user_property) { *user_property = NULL; uint8_t len_bytes = 0; @@ -307,7 +338,6 @@ char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length, size_t totlen = get_variable_len(buffer, offset, buffer_length, &len_bytes); offset += len_bytes; totlen += offset; - size_t topic_len = buffer[offset ++] << 8; topic_len |= buffer[offset ++] & 0xff; *msg_topic = (char *)(buffer + offset); @@ -322,52 +352,65 @@ char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length, if (offset + 2 >= buffer_length) { return NULL; } + offset += 2; // skip the message id } *property_len = get_variable_len(buffer, offset, buffer_length, &len_bytes); offset += len_bytes; - uint16_t len = 0, property_offset = 0; uint8_t *property = (buffer + offset); + while (property_offset < *property_len) { uint8_t property_id = property[property_offset ++]; + switch (property_id) { case MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR: resp_property->payload_format_indicator = property[property_offset ++]; ESP_LOGD(TAG, "MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR %d", resp_property->payload_format_indicator); continue; + case MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL: - MQTT5_CONVERT_ONE_BYTE_TO_FOUR(resp_property->message_expiry_interval, property[property_offset ++], property[property_offset ++], property[property_offset ++], property[property_offset ++]) + MQTT5_CONVERT_ONE_BYTE_TO_FOUR(resp_property->message_expiry_interval, property[property_offset ++], + property[property_offset ++], property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL %"PRIu32, resp_property->message_expiry_interval); continue; + case MQTT5_PROPERTY_TOPIC_ALIAS: MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->topic_alias, property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_TOPIC_ALIAS %d", resp_property->topic_alias); continue; + case MQTT5_PROPERTY_RESPONSE_TOPIC: - MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->response_topic_len, property[property_offset ++], property[property_offset ++]) + MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->response_topic_len, property[property_offset ++], + property[property_offset ++]) resp_property->response_topic = (char *)(property + property_offset); property_offset += resp_property->response_topic_len; ESP_LOGD(TAG, "MQTT5_PROPERTY_RESPONSE_TOPIC %.*s", resp_property->response_topic_len, resp_property->response_topic); continue; + case MQTT5_PROPERTY_CORRELATION_DATA: - MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->correlation_data_len, property[property_offset ++], property[property_offset ++]) + MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->correlation_data_len, property[property_offset ++], + property[property_offset ++]) resp_property->correlation_data = (char *)(property + property_offset); property_offset += resp_property->correlation_data_len; ESP_LOGD(TAG, "MQTT5_PROPERTY_CORRELATION_DATA length %d", resp_property->correlation_data_len); continue; + case MQTT5_PROPERTY_SUBSCRIBE_IDENTIFIER: resp_property->subscribe_id = get_variable_len(property, property_offset, buffer_length, &len_bytes); property_offset += len_bytes; ESP_LOGD(TAG, "MQTT5_PROPERTY_SUBSCRIBE_IDENTIFIER %d", resp_property->subscribe_id); continue; + case MQTT5_PROPERTY_CONTENT_TYPE: - MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->content_type_len, property[property_offset ++], property[property_offset ++]) + MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->content_type_len, property[property_offset ++], + property[property_offset ++]) resp_property->content_type = (char *)(property + property_offset); property_offset += resp_property->content_type_len; ESP_LOGD(TAG, "MQTT5_PROPERTY_CONTENT_TYPE %.*s", resp_property->content_type_len, resp_property->content_type); continue; + case MQTT5_PROPERTY_USER_PROPERTY: { uint8_t *key = NULL, *value = NULL; size_t key_len = 0, value_len = 0; @@ -381,31 +424,37 @@ char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length, value_len = len; ESP_LOGD(TAG, "MQTT5_PROPERTY_USER_PROPERTY value: %.*s", value_len, (char *)value); property_offset += len; + if (mqtt5_msg_set_user_property(user_property, (char *)key, key_len, (char *)value, value_len) != ESP_OK) { esp_mqtt5_client_delete_user_property(*user_property); *user_property = NULL; ESP_LOGE(TAG, "mqtt5_msg_set_user_property fail"); return NULL; } + continue; } + case MQTT5_PROPERTY_REASON_STRING: //only print now MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_REASON_STRING %.*s", len, &property[property_offset]); property_offset += len; continue; + default: - ESP_LOGW(TAG, "Unknow publish property id 0x%02x", property_id); + ESP_LOGW(TAG, "Unknown publish property id 0x%02x", property_id); return NULL; } } offset += property_offset; + if (totlen <= buffer_length) { *payload_len = totlen - offset; } else { *payload_len = buffer_length - offset; } + return (char *)(buffer + offset); } @@ -420,17 +469,21 @@ char *mqtt5_get_suback_data(uint8_t *buffer, size_t *length, mqtt5_user_property if (totlen > *length) { goto err; } + offset += 2; // skip the message id + if (offset < totlen) { size_t property_len = get_variable_len(buffer, offset, totlen, &len_bytes); offset += len_bytes; *user_property = mqtt5_msg_get_user_property(buffer + offset, property_len); offset += property_len; + if (offset < totlen) { *length = totlen - offset; return (char *)(buffer + offset); } } + err: *user_property = NULL; *length = 0; @@ -444,17 +497,19 @@ char *mqtt5_get_puback_data(uint8_t *buffer, size_t *length, mqtt5_user_property size_t totlen = get_variable_len(buffer, offset, *length, &len_bytes); offset += len_bytes; totlen += offset; - offset += 2; // skip the message id + if (offset < totlen) { *length = 1; char *data = (char *)(buffer + offset); offset ++; + if (offset < totlen) { size_t property_len = get_variable_len(buffer, offset, totlen, &len_bytes); offset += len_bytes; *user_property = mqtt5_msg_get_user_property(buffer + offset, property_len); } + return data; } else { *length = 0; @@ -462,7 +517,8 @@ char *mqtt5_get_puback_data(uint8_t *buffer, size_t *length, mqtt5_user_property } } -mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info, esp_mqtt5_connection_property_storage_t *property, esp_mqtt5_connection_will_property_storage_t *will_property) +mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info, + esp_mqtt5_connection_property_storage_t *property, esp_mqtt5_connection_will_property_storage_t *will_property) { init_message(connection); connection->buffer[connection->outbound_message.length ++] = 0; // Variable header length MSB @@ -471,7 +527,6 @@ mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_in memcpy(&connection->buffer[connection->outbound_message.length], "MQTT", 4); // Protocol name connection->outbound_message.length += 4; connection->buffer[connection->outbound_message.length ++] = 5; // Protocol version - int flags_offset = connection->outbound_message.length; connection->buffer[connection->outbound_message.length ++] = 0; // Flags MQTT5_CONVERT_TWO_BYTE(connection->buffer[connection->outbound_message.length ++], info->keepalive) // Keep-alive @@ -483,32 +538,46 @@ mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_in //Add properties int properties_offset = connection->outbound_message.length; connection->outbound_message.length ++; + if (property->session_expiry_interval) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL, 4, NULL, property->session_expiry_interval), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL, 4, NULL, + property->session_expiry_interval), fail_message(connection)); } + if (property->maximum_packet_size) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_MAXIMUM_PACKET_SIZE, 4, NULL, property->maximum_packet_size), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_MAXIMUM_PACKET_SIZE, 4, NULL, property->maximum_packet_size), + fail_message(connection)); } + if (property->receive_maximum) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_RECEIVE_MAXIMUM, 2, NULL, property->receive_maximum), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_RECEIVE_MAXIMUM, 2, NULL, property->receive_maximum), + fail_message(connection)); } + if (property->topic_alias_maximum) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_TOPIC_ALIAS_MAXIMIM, 2, NULL, property->topic_alias_maximum), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_TOPIC_ALIAS_MAXIMIM, 2, NULL, property->topic_alias_maximum), + fail_message(connection)); } + if (property->request_resp_info) { APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_REQUEST_RESP_INFO, 1, NULL, 1), fail_message(connection)); } + if (property->request_problem_info) { APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_REQUEST_PROBLEM_INFO, 1, NULL, 1), fail_message(connection)); } + if (property->user_property) { mqtt5_user_property_item_t item; STAILQ_FOREACH(item, property->user_property, next) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), + fail_message(connection)); APPEND_CHECK(append_property(connection, 0, 2, item->value, strlen(item->value)), fail_message(connection)); } } - APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, properties_offset), fail_message(connection)); + + APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, + properties_offset), fail_message(connection)); if (info->client_id != NULL && info->client_id[0] != '\0') { APPEND_CHECK(append_property(connection, 0, 2, info->client_id, strlen(info->client_id)), fail_message(connection)); @@ -520,40 +589,56 @@ mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_in if (info->will_topic != NULL && info->will_topic[0] != '\0') { properties_offset = connection->outbound_message.length; connection->outbound_message.length ++; + if (will_property->will_delay_interval) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_WILL_DELAY_INTERVAL, 4, NULL, will_property->will_delay_interval), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_WILL_DELAY_INTERVAL, 4, NULL, + will_property->will_delay_interval), fail_message(connection)); } + if (will_property->payload_format_indicator) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR, 1, NULL, 1), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR, 1, NULL, 1), + fail_message(connection)); } + if (will_property->message_expiry_interval) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL, 4, NULL, will_property->message_expiry_interval), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL, 4, NULL, + will_property->message_expiry_interval), fail_message(connection)); } + if (will_property->content_type) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_CONTENT_TYPE, 2, will_property->content_type, strlen(will_property->content_type)), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_CONTENT_TYPE, 2, will_property->content_type, + strlen(will_property->content_type)), fail_message(connection)); } + if (will_property->response_topic) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_RESPONSE_TOPIC, 2, will_property->response_topic, strlen(will_property->response_topic)), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_RESPONSE_TOPIC, 2, will_property->response_topic, + strlen(will_property->response_topic)), fail_message(connection)); } + if (will_property->correlation_data && will_property->correlation_data_len) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_CORRELATION_DATA, 2, will_property->correlation_data, will_property->correlation_data_len), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_CORRELATION_DATA, 2, will_property->correlation_data, + will_property->correlation_data_len), fail_message(connection)); } + if (will_property->user_property) { mqtt5_user_property_item_t item; STAILQ_FOREACH(item, will_property->user_property, next) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), + fail_message(connection)); APPEND_CHECK(append_property(connection, 0, 2, item->value, strlen(item->value)), fail_message(connection)); } } - APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, properties_offset), fail_message(connection)); + APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, + properties_offset), fail_message(connection)); APPEND_CHECK(append_property(connection, 0, 2, info->will_topic, strlen(info->will_topic)), fail_message(connection)); APPEND_CHECK(append_property(connection, 0, 2, info->will_message, info->will_length), fail_message(connection)); - connection->buffer[flags_offset] |= MQTT5_CONNECT_FLAG_WILL; + if (info->will_retain) { connection->buffer[flags_offset] |= MQTT5_CONNECT_FLAG_WILL_RETAIN; } + connection->buffer[flags_offset] |= (info->will_qos & 3) << 3; } @@ -570,6 +655,7 @@ mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_in APPEND_CHECK(append_property(connection, 0, 2, NULL, 0), fail_message(connection)); connection->buffer[flags_offset] |= MQTT5_CONNECT_FLAG_USERNAME; } + APPEND_CHECK(append_property(connection, 0, 2, info->password, strlen(info->password)), fail_message(connection)); connection->buffer[flags_offset] |= MQTT5_CONNECT_FLAG_PASSWORD; } @@ -577,7 +663,10 @@ mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_in return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); } -esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, mqtt_connect_info_t *connection_info, esp_mqtt5_connection_property_storage_t *connection_property, esp_mqtt5_connection_server_resp_property_t *resp_property, int *reason_code, uint8_t *ack_flag, mqtt5_user_property_handle_t *user_property) +esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, mqtt_connect_info_t *connection_info, + esp_mqtt5_connection_property_storage_t *connection_property, + esp_mqtt5_connection_server_resp_property_t *resp_property, int *reason_code, uint8_t *ack_flag, + mqtt5_user_property_handle_t *user_property) { *reason_code = 0; *user_property = NULL; @@ -598,53 +687,70 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m offset += len_bytes; uint16_t property_offset = 0, len = 0; uint8_t *property = (buffer + offset); + while (property_offset < property_len) { uint8_t property_id = property[property_offset ++]; + switch (property_id) { case MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL: - MQTT5_CONVERT_ONE_BYTE_TO_FOUR(connection_property->session_expiry_interval, property[property_offset ++], property[property_offset ++], property[property_offset ++], property[property_offset ++]) + MQTT5_CONVERT_ONE_BYTE_TO_FOUR(connection_property->session_expiry_interval, property[property_offset ++], + property[property_offset ++], property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL %"PRIu32, connection_property->session_expiry_interval); continue; + case MQTT5_PROPERTY_RECEIVE_MAXIMUM: - MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->receive_maximum, property[property_offset ++], property[property_offset ++]) + MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->receive_maximum, property[property_offset ++], + property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_RECEIVE_MAXIMUM %d", resp_property->receive_maximum); continue; + case MQTT5_PROPERTY_MAXIMUM_QOS: resp_property->max_qos = property[property_offset ++]; ESP_LOGD(TAG, "MQTT5_PROPERTY_MAXIMUM_QOS %d", resp_property->max_qos); continue; + case MQTT5_PROPERTY_RETAIN_AVAILABLE: resp_property->retain_available = property[property_offset ++]; ESP_LOGD(TAG, "MQTT5_PROPERTY_RETAIN_AVAILABLE %d", resp_property->retain_available); continue; + case MQTT5_PROPERTY_MAXIMUM_PACKET_SIZE: - MQTT5_CONVERT_ONE_BYTE_TO_FOUR(resp_property->maximum_packet_size, property[property_offset ++], property[property_offset ++], property[property_offset ++], property[property_offset ++]) + MQTT5_CONVERT_ONE_BYTE_TO_FOUR(resp_property->maximum_packet_size, property[property_offset ++], + property[property_offset ++], property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_MAXIMUM_PACKET_SIZE %"PRIu32, resp_property->maximum_packet_size); continue; + case MQTT5_PROPERTY_ASSIGNED_CLIENT_IDENTIFIER: MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++]) if (connection_info->client_id) { free(connection_info->client_id); } + connection_info->client_id = calloc(1, len + 1); + if (!connection_info->client_id) { ESP_LOGE(TAG, "Failed to calloc %d data", len); return ESP_FAIL; } + memcpy(connection_info->client_id, &property[property_offset], len); connection_info->client_id[len] = '\0'; property_offset += len; ESP_LOGD(TAG, "MQTT5_PROPERTY_ASSIGNED_CLIENT_IDENTIFIER %s", connection_info->client_id); continue; + case MQTT5_PROPERTY_TOPIC_ALIAS_MAXIMIM: - MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->topic_alias_maximum, property[property_offset ++], property[property_offset ++]) + MQTT5_CONVERT_ONE_BYTE_TO_TWO(resp_property->topic_alias_maximum, property[property_offset ++], + property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_TOPIC_ALIAS_MAXIMIM %d", resp_property->topic_alias_maximum); continue; + case MQTT5_PROPERTY_REASON_STRING: //only print now MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_REASON_STRING %.*s", len, &property[property_offset]); property_offset += len; continue; + case MQTT5_PROPERTY_USER_PROPERTY: { uint8_t *key = NULL, *value = NULL; size_t key_len = 0, value_len = 0; @@ -658,76 +764,93 @@ esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, m value_len = len; ESP_LOGD(TAG, "MQTT5_PROPERTY_USER_PROPERTY value: %.*s", value_len, (char *)value); property_offset += len; + if (mqtt5_msg_set_user_property(user_property, (char *)key, key_len, (char *)value, value_len) != ESP_OK) { esp_mqtt5_client_delete_user_property(*user_property); *user_property = NULL; ESP_LOGE(TAG, "mqtt5_msg_set_user_property fail"); return ESP_FAIL; } + continue; } + case MQTT5_PROPERTY_WILDCARD_SUBSCR_AVAILABLE: resp_property->wildcard_subscribe_available = property[property_offset++]; ESP_LOGD(TAG, "MQTT5_PROPERTY_WILDCARD_SUBSCR_AVAILABLE %d", resp_property->wildcard_subscribe_available); continue; + case MQTT5_PROPERTY_SUBSCR_IDENTIFIER_AVAILABLE: resp_property->subscribe_identifiers_available = property[property_offset++]; ESP_LOGD(TAG, "MQTT5_PROPERTY_SUBSCR_IDENTIFIER_AVAILABLE %d", resp_property->subscribe_identifiers_available); continue; + case MQTT5_PROPERTY_SHARED_SUBSCR_AVAILABLE: resp_property->shared_subscribe_available = property[property_offset++]; ESP_LOGD(TAG, "MQTT5_PROPERTY_SHARED_SUBSCR_AVAILABLE %d", resp_property->shared_subscribe_available); continue; + case MQTT5_PROPERTY_SERVER_KEEP_ALIVE: MQTT5_CONVERT_ONE_BYTE_TO_TWO(connection_info->keepalive, property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_SERVER_KEEP_ALIVE %lld", connection_info->keepalive); continue; + case MQTT5_PROPERTY_RESP_INFO: if (resp_property->response_info) { free(resp_property->response_info); } + MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++]) resp_property->response_info = calloc(1, len + 1); + if (!resp_property->response_info) { ESP_LOGE(TAG, "Failed to calloc %d data", len); return ESP_FAIL; } + memcpy(resp_property->response_info, &property[property_offset], len); resp_property->response_info[len] = '\0'; property_offset += len; ESP_LOGD(TAG, "MQTT5_PROPERTY_RESP_INFO %s", resp_property->response_info); continue; + case MQTT5_PROPERTY_SERVER_REFERENCE: //only print now MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_SERVER_REFERENCE %.*s", len, &property[property_offset]); property_offset += len; continue; + case MQTT5_PROPERTY_AUTHENTICATION_METHOD: //only print now MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_AUTHENTICATION_METHOD %.*s", len, &property[property_offset]); property_offset += len; continue; + case MQTT5_PROPERTY_AUTHENTICATION_DATA: //only print now MQTT5_CONVERT_ONE_BYTE_TO_TWO(len, property[property_offset ++], property[property_offset ++]) ESP_LOGD(TAG, "MQTT5_PROPERTY_AUTHENTICATION_DATA length %d", len); property_offset += len; continue; + default: - ESP_LOGW(TAG, "Unknow connack property id 0x%02x", property_id); + ESP_LOGW(TAG, "Unknown connack property id 0x%02x", property_id); return ESP_FAIL; } } + return ESP_OK; } -mqtt_message_t *mqtt5_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, int qos, int retain, uint16_t *message_id, const esp_mqtt5_publish_property_config_t *property, const char *resp_info) +mqtt_message_t *mqtt5_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, + int qos, int retain, uint16_t *message_id, const esp_mqtt5_publish_property_config_t *property, const char *resp_info) { init_message(connection); - if ((topic == NULL || topic[0] == '\0') && (!property || !property->topic_alias)){ + if ((topic == NULL || topic[0] == '\0') && (!property || !property->topic_alias)) { ESP_LOGE(TAG, "Message must have a topic filter or a topic alias set"); return fail_message(connection); } + int topic_len = (topic == NULL || topic[0] == '\0') ? 0 : strlen(topic); APPEND_CHECK(append_property(connection, 0, 2, topic, topic_len), fail_message(connection)); @@ -748,62 +871,85 @@ mqtt_message_t *mqtt5_msg_publish(mqtt_connection_t *connection, const char *top if (property) { if (property->payload_format_indicator) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR, 1, NULL, 1), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR, 1, NULL, 1), + fail_message(connection)); } + if (property->message_expiry_interval) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL, 4, NULL, property->message_expiry_interval), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL, 4, NULL, + property->message_expiry_interval), fail_message(connection)); } + if (property->topic_alias) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_TOPIC_ALIAS, 2, NULL, property->topic_alias), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_TOPIC_ALIAS, 2, NULL, property->topic_alias), + fail_message(connection)); } + if (property->response_topic) { if (resp_info && strlen(resp_info)) { uint16_t response_topic_size = strlen(property->response_topic) + strlen(resp_info) + 1; char *response_topic = calloc(1, response_topic_size); + if (!response_topic) { ESP_LOGE(TAG, "Failed to calloc %d memory", response_topic_size); return fail_message(connection); } + snprintf(response_topic, response_topic_size, "%s/%s", property->response_topic, resp_info); + if (append_property(connection, MQTT5_PROPERTY_RESPONSE_TOPIC, 2, response_topic, response_topic_size) == -1) { ESP_LOGE(TAG, "%s(%d) fail", __FUNCTION__, __LINE__); free(response_topic); return fail_message(connection); } + free(response_topic); } else { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_RESPONSE_TOPIC, 2, property->response_topic, strlen(property->response_topic)), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_RESPONSE_TOPIC, 2, property->response_topic, + strlen(property->response_topic)), fail_message(connection)); } } + if (property->correlation_data && property->correlation_data_len) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_CORRELATION_DATA, 2, property->correlation_data, property->correlation_data_len), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_CORRELATION_DATA, 2, property->correlation_data, + property->correlation_data_len), fail_message(connection)); } + if (property->user_property) { mqtt5_user_property_item_t item; STAILQ_FOREACH(item, property->user_property, next) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), + fail_message(connection)); APPEND_CHECK(append_property(connection, 0, 2, item->value, strlen(item->value)), fail_message(connection)); } } + if (property->content_type) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_CONTENT_TYPE, 2, property->content_type, strlen(property->content_type)), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_CONTENT_TYPE, 2, property->content_type, + strlen(property->content_type)), fail_message(connection)); } } - APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, properties_offset), fail_message(connection)); + + APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, + properties_offset), fail_message(connection)); if (connection->outbound_message.length + data_length > connection->buffer_length) { // Not enough size in buffer -> fragment this message connection->outbound_message.fragmented_msg_data_offset = connection->outbound_message.length; - memcpy(connection->buffer + connection->outbound_message.length, data, connection->buffer_length - connection->outbound_message.length); + memcpy(connection->buffer + connection->outbound_message.length, data, + connection->buffer_length - connection->outbound_message.length); connection->outbound_message.length = connection->buffer_length; - connection->outbound_message.fragmented_msg_total_length = data_length + connection->outbound_message.fragmented_msg_data_offset; + connection->outbound_message.fragmented_msg_total_length = data_length + + connection->outbound_message.fragmented_msg_data_offset; } else { if (data != NULL) { memcpy(connection->buffer + connection->outbound_message.length, data, data_length); connection->outbound_message.length += data_length; } + connection->outbound_message.fragmented_msg_total_length = 0; } + return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); } @@ -819,42 +965,54 @@ int mqtt5_msg_get_reason_code(uint8_t *buffer, size_t length) case MQTT_MSG_TYPE_PUBREC: case MQTT_MSG_TYPE_PUBREL: case MQTT_MSG_TYPE_PUBCOMP: - if(variable_len == 2) { - return 0; + if (variable_len == 2) { + return 0; } + offset += 2; //skip the message id + if (offset >= length) { ESP_LOGE(TAG, "Invalid control packet, reason code is absent"); return -1; } + return buffer[offset]; + case MQTT_MSG_TYPE_SUBACK: case MQTT_MSG_TYPE_UNSUBACK: { offset += 2; //skip the message id + if (offset >= length) { return -1; } + size_t property_len = get_variable_len(buffer, offset, length, &len_bytes); offset = offset + len_bytes + property_len; + if (offset >= length) { ESP_LOGE(TAG, "Invalid control packet, reason code is absent"); return -1; } + return buffer[offset]; } + case MQTT_MSG_TYPE_DISCONNECT: if (offset >= length) { return -1; } else { return buffer[offset]; } + default: break; } + return -1; } -mqtt_message_t *mqtt5_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t *topic_list, int size, uint16_t *message_id, const esp_mqtt5_subscribe_property_config_t *property) +mqtt_message_t *mqtt5_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t *topic_list, int size, + uint16_t *message_id, const esp_mqtt5_subscribe_property_config_t *property) { init_message(connection); @@ -867,88 +1025,115 @@ mqtt_message_t *mqtt5_msg_subscribe(mqtt_connection_t *connection, const esp_mqt if (property) { if (property->subscribe_id) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_SUBSCRIBE_IDENTIFIER, 0, NULL, property->subscribe_id), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_SUBSCRIBE_IDENTIFIER, 0, NULL, property->subscribe_id), + fail_message(connection)); } + if (property->user_property) { mqtt5_user_property_item_t item; STAILQ_FOREACH(item, property->user_property, next) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), + fail_message(connection)); APPEND_CHECK(append_property(connection, 0, 2, item->value, strlen(item->value)), fail_message(connection)); } } } - APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, properties_offset), fail_message(connection)); + + APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, + properties_offset), fail_message(connection)); for (int topic_number = 0; topic_number < size; ++topic_number) { if (topic_list[topic_number].filter[0] == '\0') { return fail_message(connection); } + if (property && property->is_share_subscribe) { - uint16_t shared_topic_size = strlen(topic_list[topic_number].filter) + strlen(MQTT5_SHARED_SUB) + strlen(property->share_name); + uint16_t shared_topic_size = strlen(topic_list[topic_number].filter) + strlen(MQTT5_SHARED_SUB) + strlen( + property->share_name); char *shared_topic = calloc(1, shared_topic_size); + if (!shared_topic) { ESP_LOGE(TAG, "Failed to calloc %d memory", shared_topic_size); fail_message(connection); } + snprintf(shared_topic, shared_topic_size, MQTT5_SHARED_SUB, property->share_name, topic_list[topic_number].filter); + if (append_property(connection, 0, 2, shared_topic, strlen(shared_topic)) == -1) { ESP_LOGE(TAG, "%s(%d) fail", __FUNCTION__, __LINE__); free(shared_topic); return fail_message(connection); } + free(shared_topic); } else { - APPEND_CHECK(append_property(connection, 0, 2, topic_list[topic_number].filter, strlen(topic_list[topic_number].filter)), fail_message(connection)); + APPEND_CHECK(append_property(connection, 0, 2, topic_list[topic_number].filter, + strlen(topic_list[topic_number].filter)), fail_message(connection)); } if (connection->outbound_message.length + 1 > connection->buffer_length) { return fail_message(connection); } + connection->buffer[connection->outbound_message.length] = 0; + if (property) { if (property->retain_handle > 0 && property->retain_handle < 3) { connection->buffer[connection->outbound_message.length] |= (property->retain_handle & 3) << 4; } + if (property->no_local_flag) { connection->buffer[connection->outbound_message.length] |= (property->no_local_flag << 2); } + if (property->retain_as_published_flag) { connection->buffer[connection->outbound_message.length] |= (property->retain_as_published_flag << 3); } } + connection->buffer[connection->outbound_message.length] |= (topic_list[topic_number].qos & 3); connection->outbound_message.length ++; } + return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); } -mqtt_message_t *mqtt5_msg_disconnect(mqtt_connection_t *connection, esp_mqtt5_disconnect_property_config_t *disconnect_property_info) +mqtt_message_t *mqtt5_msg_disconnect(mqtt_connection_t *connection, + esp_mqtt5_disconnect_property_config_t *disconnect_property_info) { init_message(connection); int reason_offset = connection->outbound_message.length; connection->buffer[connection->outbound_message.length ++] = 0; int properties_offset = connection->outbound_message.length; connection->outbound_message.length ++; + if (disconnect_property_info) { if (disconnect_property_info->session_expiry_interval) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL, 4, NULL, disconnect_property_info->session_expiry_interval), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL, 4, NULL, + disconnect_property_info->session_expiry_interval), fail_message(connection)); } + if (disconnect_property_info->user_property) { mqtt5_user_property_item_t item; STAILQ_FOREACH(item, disconnect_property_info->user_property, next) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), + fail_message(connection)); APPEND_CHECK(append_property(connection, 0, 2, item->value, strlen(item->value)), fail_message(connection)); } } + if (disconnect_property_info->disconnect_reason) { connection->buffer[reason_offset] = disconnect_property_info->disconnect_reason; } } - APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, properties_offset), fail_message(connection)); + + APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, + properties_offset), fail_message(connection)); return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); } -mqtt_message_t *mqtt5_msg_unsubscribe(mqtt_connection_t *connection, const char *topic, uint16_t *message_id, const esp_mqtt5_unsubscribe_property_config_t *property) +mqtt_message_t *mqtt5_msg_unsubscribe(mqtt_connection_t *connection, const char *topic, uint16_t *message_id, + const esp_mqtt5_unsubscribe_property_config_t *property) { init_message(connection); @@ -962,30 +1147,38 @@ mqtt_message_t *mqtt5_msg_unsubscribe(mqtt_connection_t *connection, const char int properties_offset = connection->outbound_message.length; connection->outbound_message.length ++; + if (property) { if (property->user_property) { mqtt5_user_property_item_t item; STAILQ_FOREACH(item, property->user_property, next) { - APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), fail_message(connection)); + APPEND_CHECK(append_property(connection, MQTT5_PROPERTY_USER_PROPERTY, 2, item->key, strlen(item->key)), + fail_message(connection)); APPEND_CHECK(append_property(connection, 0, 2, item->value, strlen(item->value)), fail_message(connection)); } } } - APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, properties_offset), fail_message(connection)); + APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, + properties_offset), fail_message(connection)); + if (property && property->is_share_subscribe) { uint16_t shared_topic_size = strlen(topic) + strlen(MQTT5_SHARED_SUB) + strlen(property->share_name); char *shared_topic = calloc(1, shared_topic_size); + if (!shared_topic) { ESP_LOGE(TAG, "Failed to calloc %d memory", shared_topic_size); fail_message(connection); } + snprintf(shared_topic, shared_topic_size, MQTT5_SHARED_SUB, property->share_name, topic); + if (append_property(connection, 0, 2, shared_topic, strlen(shared_topic)) == -1) { ESP_LOGE(TAG, "%s(%d) fail", __FUNCTION__, __LINE__); free(shared_topic); return fail_message(connection); } + free(shared_topic); } else { APPEND_CHECK(append_property(connection, 0, 2, topic, strlen(topic)), fail_message(connection)); @@ -997,51 +1190,63 @@ mqtt_message_t *mqtt5_msg_unsubscribe(mqtt_connection_t *connection, const char mqtt_message_t *mqtt5_msg_puback(mqtt_connection_t *connection, uint16_t message_id) { init_message(connection); + if (append_message_id(connection, message_id) == 0) { return fail_message(connection); } + connection->buffer[connection->outbound_message.length ++] = 0; // Regard it is success int properties_offset = connection->outbound_message.length; connection->outbound_message.length ++; - APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, properties_offset), fail_message(connection)); + APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, + properties_offset), fail_message(connection)); return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); } mqtt_message_t *mqtt5_msg_pubrec(mqtt_connection_t *connection, uint16_t message_id) { init_message(connection); + if (append_message_id(connection, message_id) == 0) { return fail_message(connection); } + connection->buffer[connection->outbound_message.length ++] = 0; // Regard it is success int properties_offset = connection->outbound_message.length; connection->outbound_message.length ++; - APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, properties_offset), fail_message(connection)); + APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, + properties_offset), fail_message(connection)); return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); } mqtt_message_t *mqtt5_msg_pubrel(mqtt_connection_t *connection, uint16_t message_id) { init_message(connection); + if (append_message_id(connection, message_id) == 0) { return fail_message(connection); } + connection->buffer[connection->outbound_message.length ++] = 0; // Regard it is success int properties_offset = connection->outbound_message.length; connection->outbound_message.length ++; - APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, properties_offset), fail_message(connection)); + APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, + properties_offset), fail_message(connection)); return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); } mqtt_message_t *mqtt5_msg_pubcomp(mqtt_connection_t *connection, uint16_t message_id) { init_message(connection); + if (append_message_id(connection, message_id) == 0) { return fail_message(connection); } + connection->buffer[connection->outbound_message.length ++] = 0; // Regard it is success int properties_offset = connection->outbound_message.length; connection->outbound_message.length ++; - APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, properties_offset), fail_message(connection)); + APPEND_CHECK(update_property_len_value(connection, connection->outbound_message.length - properties_offset - 1, + properties_offset), fail_message(connection)); return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); } diff --git a/lib/mqtt_msg.c b/lib/mqtt_msg.c index 582fc92..ccfcdc2 100644 --- a/lib/mqtt_msg.c +++ b/lib/mqtt_msg.c @@ -1,33 +1,8 @@ /* -* Copyright (c) 2014, Stephen Robinson -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* 3. Neither the name of the copyright holder nor the names of its -* contributors may be used to endorse or promote products derived -* from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -* -*/ + * SPDX-FileCopyrightText: 2014 Stephen Robinson + * + * SPDX-License-Identifier: BSD-3-Clause + */ #include #include "mqtt_client.h" #include "mqtt_msg.h" @@ -56,7 +31,6 @@ static int append_string(mqtt_connection_t *connection, const char *string, int connection->buffer[connection->outbound_message.length++] = len & 0xff; memcpy(connection->buffer + connection->outbound_message.length, string, len); connection->outbound_message.length += len; - return len + 2; } @@ -78,7 +52,6 @@ static uint16_t append_message_id(mqtt_connection_t *connection, uint16_t messag connection->buffer[connection->outbound_message.length++] = message_id >> 8; connection->buffer[connection->outbound_message.length++] = message_id & 0xff; - return message_id; } @@ -101,6 +74,7 @@ static mqtt_message_t *fini_message(mqtt_connection_t *connection, int type, int int total_length = message_length; int encoded_length = 0; uint8_t encoded_lens[4] = {0}; + // Check if we have fragmented message and update total_len if (connection->outbound_message.fragmented_msg_total_length) { total_length = connection->outbound_message.fragmented_msg_total_length - MQTT_MAX_FIXED_HEADER_SIZE; @@ -108,12 +82,15 @@ static mqtt_message_t *fini_message(mqtt_connection_t *connection, int type, int // Encode MQTT message length int len_bytes = 0; // size of encoded message length + do { encoded_length = total_length % 128; total_length /= 128; + if (total_length > 0) { encoded_length |= 0x80; } + encoded_lens[len_bytes] = encoded_length; len_bytes++; } while (total_length > 0); @@ -129,7 +106,8 @@ static mqtt_message_t *fini_message(mqtt_connection_t *connection, int type, int connection->outbound_message.data = connection->buffer + offs; connection->outbound_message.fragmented_msg_data_offset -= offs; // type byte - connection->buffer[offs++] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[offs++] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + // length bytes for (int j = 0; j < len_bytes; j++) { connection->buffer[offs++] = encoded_lens[j]; @@ -145,12 +123,15 @@ size_t mqtt_get_total_length(const uint8_t *buffer, size_t length, int *fixed_si for (i = 1; i < length; ++i) { totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) { ++i; break; } } + totlen += i; + if (fixed_size_len) { *fixed_size_len = i; } @@ -167,24 +148,27 @@ bool mqtt_header_complete(uint8_t *buffer, size_t buffer_length) if (i >= buffer_length) { return false; } + if ((buffer[i] & 0x80) == 0) { ++i; break; } } + // i is now the length of the fixed header if (i + 2 >= buffer_length) { return false; } + topiclen = buffer[i++] << 8; topiclen |= buffer[i++]; - i += topiclen; if (mqtt_get_qos(buffer) > 0) { i += 2; } + // i is now the length of the fixed + variable header return buffer_length >= i; } @@ -204,6 +188,7 @@ char *mqtt_get_publish_topic(uint8_t *buffer, size_t *length) if (i + 2 >= *length) { return NULL; } + topiclen = buffer[i++] << 8; topiclen |= buffer[i++]; @@ -225,16 +210,19 @@ char *mqtt_get_publish_data(uint8_t *buffer, size_t *length) for (i = 1; i < blength; ++i) { totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) { ++i; break; } } + totlen += i; if (i + 2 >= blength) { return NULL; } + topiclen = buffer[i++] << 8; topiclen |= buffer[i++]; @@ -248,6 +236,7 @@ char *mqtt_get_publish_data(uint8_t *buffer, size_t *length) if (i + 2 >= blength) { return NULL; } + i += 2; } @@ -260,6 +249,7 @@ char *mqtt_get_publish_data(uint8_t *buffer, size_t *length) } else { *length = blength - i; } + return (char *)(buffer + i); } @@ -271,6 +261,7 @@ char *mqtt_get_suback_data(uint8_t *buffer, size_t *length) *length -= 4; return (char *)(buffer + 4); } + *length = 0; return NULL; } @@ -296,18 +287,21 @@ uint16_t mqtt_get_id(uint8_t *buffer, size_t length) if (i + 2 >= length) { return 0; } + topiclen = buffer[i++] << 8; topiclen |= buffer[i++]; if (i + topiclen > length) { return 0; } + i += topiclen; if (mqtt_get_qos(buffer) > 0) { if (i + 2 > length) { return 0; } + //i += 2; } else { return 0; @@ -315,6 +309,7 @@ uint16_t mqtt_get_id(uint8_t *buffer, size_t length) return (buffer[i] << 8) | buffer[i + 1]; } + case MQTT_MSG_TYPE_PUBACK: case MQTT_MSG_TYPE_PUBREC: case MQTT_MSG_TYPE_PUBREL: @@ -339,10 +334,9 @@ uint16_t mqtt_get_id(uint8_t *buffer, size_t length) mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info) { - set_message_header_size(connection); - int header_len; + if (info->protocol_ver == MQTT_PROTOCOL_V_3_1) { header_len = MQTT_3_1_VARIABLE_HEADER_SIZE; } else { @@ -352,9 +346,9 @@ mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_inf if (connection->outbound_message.length + header_len > connection->buffer_length) { return fail_message(connection); } + char *variable_header = (char *)(connection->buffer + connection->outbound_message.length); connection->outbound_message.length += header_len; - int header_idx = 0; variable_header[header_idx++] = 0; // Variable header length MSB @@ -400,9 +394,11 @@ mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_inf } variable_header[flags_offset] |= MQTT_CONNECT_FLAG_WILL; + if (info->will_retain) { variable_header[flags_offset] |= MQTT_CONNECT_FLAG_WILL_RETAIN; } + variable_header[flags_offset] |= (info->will_qos & 3) << 3; } @@ -436,7 +432,8 @@ mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_inf return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); } -mqtt_message_t *mqtt_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, int qos, int retain, uint16_t *message_id) +mqtt_message_t *mqtt_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, + int qos, int retain, uint16_t *message_id) { set_message_header_size(connection); @@ -464,55 +461,67 @@ mqtt_message_t *mqtt_msg_publish(mqtt_connection_t *connection, const char *topi if (connection->outbound_message.length + data_length > connection->buffer_length) { // Not enough size in buffer -> fragment this message connection->outbound_message.fragmented_msg_data_offset = connection->outbound_message.length; - memcpy(connection->buffer + connection->outbound_message.length, data, connection->buffer_length - connection->outbound_message.length); + memcpy(connection->buffer + connection->outbound_message.length, data, + connection->buffer_length - connection->outbound_message.length); connection->outbound_message.length = connection->buffer_length; - connection->outbound_message.fragmented_msg_total_length = data_length + connection->outbound_message.fragmented_msg_data_offset; + connection->outbound_message.fragmented_msg_total_length = data_length + + connection->outbound_message.fragmented_msg_data_offset; } else { memcpy(connection->buffer + connection->outbound_message.length, data, data_length); connection->outbound_message.length += data_length; connection->outbound_message.fragmented_msg_total_length = 0; } } + return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); } mqtt_message_t *mqtt_msg_puback(mqtt_connection_t *connection, uint16_t message_id) { set_message_header_size(connection); + if (append_message_id(connection, message_id) == 0) { return fail_message(connection); } + return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); } mqtt_message_t *mqtt_msg_pubrec(mqtt_connection_t *connection, uint16_t message_id) { set_message_header_size(connection); + if (append_message_id(connection, message_id) == 0) { return fail_message(connection); } + return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); } mqtt_message_t *mqtt_msg_pubrel(mqtt_connection_t *connection, uint16_t message_id) { set_message_header_size(connection); + if (append_message_id(connection, message_id) == 0) { return fail_message(connection); } + return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); } mqtt_message_t *mqtt_msg_pubcomp(mqtt_connection_t *connection, uint16_t message_id) { set_message_header_size(connection); + if (append_message_id(connection, message_id) == 0) { return fail_message(connection); } + return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); } -mqtt_message_t *mqtt_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t topic_list[], int size, uint16_t *message_id) +mqtt_message_t *mqtt_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t topic_list[], int size, + uint16_t *message_id) { set_message_header_size(connection); @@ -532,6 +541,7 @@ mqtt_message_t *mqtt_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt if (connection->outbound_message.length + 1 > connection->buffer_length) { return fail_message(connection); } + connection->buffer[connection->outbound_message.length] = topic_list[topic_number].qos; connection->outbound_message.length ++; } @@ -587,6 +597,7 @@ int mqtt_has_valid_msg_hdr(uint8_t *buffer, size_t length) if (length < 1) { return 0; } + switch (mqtt_get_type(buffer)) { case MQTT_MSG_TYPE_CONNECT: case MQTT_MSG_TYPE_CONNACK: @@ -599,10 +610,12 @@ int mqtt_has_valid_msg_hdr(uint8_t *buffer, size_t length) case MQTT_MSG_TYPE_PINGRESP: case MQTT_MSG_TYPE_DISCONNECT: return (buffer[0] & 0x0f) == 0; /* all flag bits are 0 */ + case MQTT_MSG_TYPE_PUBREL: case MQTT_MSG_TYPE_SUBSCRIBE: case MQTT_MSG_TYPE_UNSUBSCRIBE: return (buffer[0] & 0x0f) == 0x02; /* only bit 1 is set */ + case MQTT_MSG_TYPE_PUBLISH: qos = mqtt_get_qos(buffer); dup = mqtt_get_dup(buffer); @@ -611,6 +624,7 @@ int mqtt_has_valid_msg_hdr(uint8_t *buffer, size_t length) * dup flag must be set to 0 for all qos=0 messages [MQTT-3.3.1-2] */ return (qos < 3) && ((qos > 0) || (dup == 0)); + default: return 0; } @@ -620,9 +634,11 @@ esp_err_t mqtt_msg_buffer_init(mqtt_connection_t *connection, int buffer_size) { memset(&connection->outbound_message, 0, sizeof(mqtt_message_t)); connection->buffer = (uint8_t *)calloc(buffer_size, sizeof(uint8_t)); + if (!connection->buffer) { return ESP_ERR_NO_MEM; } + connection->buffer_length = buffer_size; return ESP_OK; } @@ -633,5 +649,3 @@ void mqtt_msg_buffer_destroy(mqtt_connection_t *connection) free(connection->buffer); } } - - diff --git a/lib/mqtt_outbox.c b/lib/mqtt_outbox.c index 6e63b19..abf2f17 100644 --- a/lib/mqtt_outbox.c +++ b/lib/mqtt_outbox.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include "mqtt_outbox.h" #include #include @@ -55,12 +60,15 @@ outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handl return NULL; }); memcpy(item->buffer, message->data, message->len); + if (message->remaining_data) { memcpy(item->buffer + message->len, message->remaining_data, message->remaining_len); } + STAILQ_INSERT_TAIL(outbox->list, item, next); outbox->size += item->len; - ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%"PRIu64, message->msg_id, message->msg_type, message->len + message->remaining_len, outbox_get_size(outbox)); + ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%"PRIu64, message->msg_id, message->msg_type, + message->len + message->remaining_len, outbox_get_size(outbox)); return item; } @@ -83,6 +91,7 @@ outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pend if (tick) { *tick = item->tick; } + return item; } } @@ -96,7 +105,8 @@ esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item_t if (item == item_to_delete) { STAILQ_REMOVE(outbox->list, item, outbox_item, next); outbox->size -= item->len; - ESP_LOGD(TAG, "DELETE_ITEM msgid=%d, msg_type=%d, remain size=%"PRIu64, item_to_delete->msg_id, item_to_delete->msg_type, outbox_get_size(outbox)); + ESP_LOGD(TAG, "DELETE_ITEM msgid=%d, msg_type=%d, remain size=%"PRIu64, item_to_delete->msg_id, + item_to_delete->msg_type, outbox_get_size(outbox)); free(item->buffer); free(item); return ESP_OK; @@ -114,6 +124,7 @@ uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *qos = item->msg_qos; return (uint8_t *)item->buffer; } + return NULL; } @@ -129,7 +140,6 @@ esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type) free(item); return ESP_OK; } - } return ESP_FAIL; } @@ -137,10 +147,12 @@ esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type) esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending) { outbox_item_handle_t item = outbox_get(outbox, msg_id); + if (item) { item->pending = pending; return ESP_OK; } + return ESP_FAIL; } @@ -149,16 +161,19 @@ pending_state_t outbox_item_get_pending(outbox_item_handle_t item) if (item) { return item->pending; } + return QUEUED; } esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick) { outbox_item_handle_t item = outbox_get(outbox, msg_id); + if (item) { item->tick = tick; return ESP_OK; } + return ESP_FAIL; } @@ -176,7 +191,6 @@ int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_t ESP_LOGD(TAG, "DELETE_SINGLE_EXPIRED msgid=%d, remain size=%"PRIu64, msg_id, outbox_get_size(outbox)); return msg_id; } - } return msg_id; } @@ -194,7 +208,6 @@ int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, ou free(item); deleted_items ++; } - } return deleted_items; } @@ -210,7 +223,8 @@ void outbox_delete_all_items(outbox_handle_t outbox) STAILQ_FOREACH_SAFE(item, outbox->list, next, tmp) { STAILQ_REMOVE(outbox->list, item, outbox_item, next); outbox->size -= item->len; - ESP_LOGD(TAG, "DELETE_ALL_ITEMS msgid=%d, msg_type=%d, remain size=%"PRIu64, item->msg_id, item->msg_type, outbox_get_size(outbox)); + ESP_LOGD(TAG, "DELETE_ALL_ITEMS msgid=%d, msg_type=%d, remain size=%"PRIu64, item->msg_id, item->msg_type, + outbox_get_size(outbox)); free(item->buffer); free(item); } diff --git a/lib/platform_esp32_idf.c b/lib/platform_esp32_idf.c index a84e6fb..e7401f0 100644 --- a/lib/platform_esp32_idf.c +++ b/lib/platform_esp32_idf.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include "platform.h" #ifdef ESP_PLATFORM @@ -24,14 +29,14 @@ char *platform_create_id_string(void) { char *id_string = calloc(1, MAX_ID_STRING); ESP_MEM_CHECK(TAG, id_string, return NULL); - #ifndef MAC_TYPE +#ifndef MAC_TYPE ESP_LOGW(TAG, "Soc doesn't provide MAC, client could be disconnected in case of device with same name in the broker."); sprintf(id_string, "esp_mqtt_client_id"); - #else +#else uint8_t mac[6]; esp_read_mac(mac, MAC_TYPE); sprintf(id_string, "ESP32_%02x%02X%02X", mac[3], mac[4], mac[5]); - #endif +#endif return id_string; } @@ -42,7 +47,7 @@ int platform_random(int max) uint64_t platform_tick_get_ms(void) { - return esp_timer_get_time()/(int64_t)1000; + return esp_timer_get_time() / (int64_t)1000; } #endif diff --git a/mqtt5_client.c b/mqtt5_client.c index 060dfed..8b089f6 100644 --- a/mqtt5_client.c +++ b/mqtt5_client.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,14 +11,18 @@ static const char *TAG = "mqtt5_client"; static void esp_mqtt5_print_error_code(esp_mqtt5_client_handle_t client, int code); -static esp_err_t esp_mqtt5_client_update_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, char *topic, size_t topic_len); -static char *esp_mqtt5_client_get_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, size_t *topic_length); +static esp_err_t esp_mqtt5_client_update_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, + uint16_t topic_alias, char *topic, size_t topic_len); +static char *esp_mqtt5_client_get_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, + size_t *topic_length); static void esp_mqtt5_client_delete_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle); -static esp_err_t esp_mqtt5_user_property_copy(mqtt5_user_property_handle_t user_property_new, const mqtt5_user_property_handle_t user_property_old); +static esp_err_t esp_mqtt5_user_property_copy(mqtt5_user_property_handle_t user_property_new, + const mqtt5_user_property_handle_t user_property_old); void esp_mqtt5_increment_packet_counter(esp_mqtt5_client_handle_t client) { bool msg_dup = mqtt5_get_dup(client->mqtt_state.connection.outbound_message.data); + if (msg_dup == false) { client->send_publish_packet_count ++; ESP_LOGD(TAG, "Sent (%d) qos > 0 publish packet without ack", client->send_publish_packet_count); @@ -36,9 +40,11 @@ void esp_mqtt5_decrement_packet_counter(esp_mqtt5_client_handle_t client) void esp_mqtt5_parse_pubcomp(esp_mqtt5_client_handle_t client) { if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { - ESP_LOGD(TAG, "MQTT_MSG_TYPE_PUBCOMP return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len)); + ESP_LOGD(TAG, "MQTT_MSG_TYPE_PUBCOMP return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, + client->mqtt_state.in_buffer_read_len)); size_t msg_data_len = client->mqtt_state.in_buffer_read_len; - client->event.data = mqtt5_get_pubcomp_data(client->mqtt_state.in_buffer, &msg_data_len, &client->event.property->user_property); + client->event.data = mqtt5_get_pubcomp_data(client->mqtt_state.in_buffer, &msg_data_len, + &client->event.property->user_property); client->event.data_len = msg_data_len; client->event.total_data_len = msg_data_len; client->event.current_data_offset = 0; @@ -48,9 +54,11 @@ void esp_mqtt5_parse_pubcomp(esp_mqtt5_client_handle_t client) void esp_mqtt5_parse_puback(esp_mqtt5_client_handle_t client) { if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { - ESP_LOGD(TAG, "MQTT_MSG_TYPE_PUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len)); + ESP_LOGD(TAG, "MQTT_MSG_TYPE_PUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, + client->mqtt_state.in_buffer_read_len)); size_t msg_data_len = client->mqtt_state.in_buffer_read_len; - client->event.data = mqtt5_get_puback_data(client->mqtt_state.in_buffer, &msg_data_len, &client->event.property->user_property); + client->event.data = mqtt5_get_puback_data(client->mqtt_state.in_buffer, &msg_data_len, + &client->event.property->user_property); client->event.data_len = msg_data_len; client->event.total_data_len = msg_data_len; client->event.current_data_offset = 0; @@ -60,9 +68,11 @@ void esp_mqtt5_parse_puback(esp_mqtt5_client_handle_t client) void esp_mqtt5_parse_unsuback(esp_mqtt5_client_handle_t client) { if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { - ESP_LOGD(TAG, "MQTT_MSG_TYPE_UNSUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len)); + ESP_LOGD(TAG, "MQTT_MSG_TYPE_UNSUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, + client->mqtt_state.in_buffer_read_len)); size_t msg_data_len = client->mqtt_state.in_buffer_read_len; - client->event.data = mqtt5_get_unsuback_data(client->mqtt_state.in_buffer, &msg_data_len, &client->event.property->user_property); + client->event.data = mqtt5_get_unsuback_data(client->mqtt_state.in_buffer, &msg_data_len, + &client->event.property->user_property); client->event.data_len = msg_data_len; client->event.total_data_len = msg_data_len; client->event.current_data_offset = 0; @@ -72,7 +82,8 @@ void esp_mqtt5_parse_unsuback(esp_mqtt5_client_handle_t client) void esp_mqtt5_parse_suback(esp_mqtt5_client_handle_t client) { if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { - ESP_LOGD(TAG, "MQTT_MSG_TYPE_SUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len)); + ESP_LOGD(TAG, "MQTT_MSG_TYPE_SUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, + client->mqtt_state.in_buffer_read_len)); } } @@ -89,45 +100,56 @@ esp_err_t esp_mqtt5_parse_connack(esp_mqtt5_client_handle_t client, int *connect size_t len = client->mqtt_state.in_buffer_read_len; client->mqtt_state.in_buffer_read_len = 0; uint8_t ack_flag = 0; + if (mqtt5_msg_parse_connack_property(client->mqtt_state.in_buffer, len, &client->mqtt_state. - connection.information, &client->mqtt5_config->connect_property_info, &client->mqtt5_config->server_resp_property_info, connect_rsp_code, &ack_flag, &client->event.property->user_property) != ESP_OK) { + connection.information, &client->mqtt5_config->connect_property_info, &client->mqtt5_config->server_resp_property_info, + connect_rsp_code, &ack_flag, &client->event.property->user_property) != ESP_OK) { ESP_LOGE(TAG, "Failed to parse CONNACK packet"); return ESP_FAIL; } + if (*connect_rsp_code == MQTT_CONNECTION_ACCEPTED) { ESP_LOGD(TAG, "Connected"); client->event.session_present = ack_flag & 0x01; return ESP_OK; } + esp_mqtt5_print_error_code(client, *connect_rsp_code); return ESP_FAIL; } -esp_err_t esp_mqtt5_get_publish_data(esp_mqtt5_client_handle_t client, uint8_t *msg_buf, size_t msg_read_len, char **msg_topic, size_t *msg_topic_len, char **msg_data, size_t *msg_data_len) +esp_err_t esp_mqtt5_get_publish_data(esp_mqtt5_client_handle_t client, uint8_t *msg_buf, size_t msg_read_len, + char **msg_topic, size_t *msg_topic_len, char **msg_data, size_t *msg_data_len) { // get property uint16_t property_len = 0; esp_mqtt5_publish_resp_property_t property = {0}; - *msg_data = mqtt5_get_publish_property_payload(msg_buf, msg_read_len, msg_topic, msg_topic_len, &property, &property_len, msg_data_len, &client->event.property->user_property); - if (*msg_data == NULL) { + *msg_data = mqtt5_get_publish_property_payload(msg_buf, msg_read_len, msg_topic, msg_topic_len, &property, + &property_len, msg_data_len, &client->event.property->user_property); + + if (*msg_data == NULL) { ESP_LOGE(TAG, "%s: mqtt5_get_publish_property_payload() failed", __func__); return ESP_FAIL; } if (property.topic_alias > client->mqtt5_config->connect_property_info.topic_alias_maximum) { - ESP_LOGE(TAG, "%s: Broker response topic alias %d is over the max topic alias %d", __func__, property.topic_alias, client->mqtt5_config->connect_property_info.topic_alias_maximum); + ESP_LOGE(TAG, "%s: Broker response topic alias %d is over the max topic alias %d", __func__, property.topic_alias, + client->mqtt5_config->connect_property_info.topic_alias_maximum); return ESP_FAIL; } if (property.topic_alias) { if (*msg_topic_len == 0) { - *msg_topic = esp_mqtt5_client_get_topic_alias(client->mqtt5_config->peer_topic_alias, property.topic_alias, msg_topic_len); + *msg_topic = esp_mqtt5_client_get_topic_alias(client->mqtt5_config->peer_topic_alias, property.topic_alias, + msg_topic_len); + if (!*msg_topic) { ESP_LOGE(TAG, "%s: esp_mqtt5_client_get_topic_alias() failed", __func__); return ESP_FAIL; } } else { - if (esp_mqtt5_client_update_topic_alias(client->mqtt5_config->peer_topic_alias, property.topic_alias, *msg_topic, *msg_topic_len) != ESP_OK) { + if (esp_mqtt5_client_update_topic_alias(client->mqtt5_config->peer_topic_alias, property.topic_alias, *msg_topic, + *msg_topic_len) != ESP_OK) { ESP_LOGE(TAG, "%s: esp_mqtt5_client_update_topic_alias() failed", __func__); return ESP_FAIL; } @@ -159,6 +181,7 @@ esp_err_t esp_mqtt5_create_default_config(esp_mqtt5_client_handle_t client) client->mqtt5_config->server_resp_property_info.shared_subscribe_available = true; client->mqtt5_config->server_resp_property_info.receive_maximum = 65535; } + return ESP_OK; } @@ -168,110 +191,145 @@ static void esp_mqtt5_print_error_code(esp_mqtt5_client_handle_t client, int cod case MQTT5_UNSPECIFIED_ERROR: ESP_LOGW(TAG, "Unspecified error"); break; + case MQTT5_MALFORMED_PACKET: ESP_LOGW(TAG, "Malformed Packet"); break; + case MQTT5_PROTOCOL_ERROR: ESP_LOGW(TAG, "Protocol Error"); break; + case MQTT5_IMPLEMENT_SPECIFIC_ERROR: ESP_LOGW(TAG, "Implementation specific error"); break; + case MQTT5_UNSUPPORTED_PROTOCOL_VER: ESP_LOGW(TAG, "Unsupported Protocol Version"); break; + case MQTT5_INVALID_CLIENT_ID: ESP_LOGW(TAG, "Client Identifier not valid"); break; + case MQTT5_BAD_USERNAME_OR_PWD: ESP_LOGW(TAG, "Bad User Name or Password"); break; + case MQTT5_NOT_AUTHORIZED: ESP_LOGW(TAG, "Not authorized"); break; + case MQTT5_SERVER_UNAVAILABLE: ESP_LOGW(TAG, "Server unavailable"); break; + case MQTT5_SERVER_BUSY: ESP_LOGW(TAG, "Server busy"); break; + case MQTT5_BANNED: ESP_LOGW(TAG, "Banned"); break; + case MQTT5_SERVER_SHUTTING_DOWN: ESP_LOGW(TAG, "Server shutting down"); break; + case MQTT5_BAD_AUTH_METHOD: ESP_LOGW(TAG, "Bad authentication method"); break; + case MQTT5_KEEP_ALIVE_TIMEOUT: ESP_LOGW(TAG, "Keep Alive timeout"); break; + case MQTT5_SESSION_TAKEN_OVER: ESP_LOGW(TAG, "Session taken over"); break; + case MQTT5_TOPIC_FILTER_INVALID: ESP_LOGW(TAG, "Topic Filter invalid"); break; + case MQTT5_TOPIC_NAME_INVALID: ESP_LOGW(TAG, "Topic Name invalid"); break; + case MQTT5_PACKET_IDENTIFIER_IN_USE: ESP_LOGW(TAG, "Packet Identifier in use"); break; + case MQTT5_PACKET_IDENTIFIER_NOT_FOUND: ESP_LOGW(TAG, "Packet Identifier not found"); break; + case MQTT5_RECEIVE_MAXIMUM_EXCEEDED: ESP_LOGW(TAG, "Receive Maximum exceeded"); break; + case MQTT5_TOPIC_ALIAS_INVALID: ESP_LOGW(TAG, "Topic Alias invalid"); break; + case MQTT5_PACKET_TOO_LARGE: ESP_LOGW(TAG, "Packet too large"); break; + case MQTT5_MESSAGE_RATE_TOO_HIGH: ESP_LOGW(TAG, "Message rate too high"); break; + case MQTT5_QUOTA_EXCEEDED: ESP_LOGW(TAG, "Quota exceeded"); break; + case MQTT5_ADMINISTRATIVE_ACTION: ESP_LOGW(TAG, "Administrative action"); break; + case MQTT5_PAYLOAD_FORMAT_INVALID: ESP_LOGW(TAG, "Payload format invalid"); break; + case MQTT5_RETAIN_NOT_SUPPORT: ESP_LOGW(TAG, "Retain not supported"); break; + case MQTT5_QOS_NOT_SUPPORT: ESP_LOGW(TAG, "QoS not supported"); break; + case MQTT5_USE_ANOTHER_SERVER: ESP_LOGW(TAG, "Use another server"); break; + case MQTT5_SERVER_MOVED: ESP_LOGW(TAG, "Server moved"); break; + case MQTT5_SHARED_SUBSCR_NOT_SUPPORTED: ESP_LOGW(TAG, "Shared Subscriptions not supported"); break; + case MQTT5_CONNECTION_RATE_EXCEEDED: ESP_LOGW(TAG, "Connection rate exceeded"); break; + case MQTT5_MAXIMUM_CONNECT_TIME: ESP_LOGW(TAG, "Maximum connect time"); break; + case MQTT5_SUBSCRIBE_IDENTIFIER_NOT_SUPPORT: ESP_LOGW(TAG, "Subscription Identifiers not supported"); break; + case MQTT5_WILDCARD_SUBSCRIBE_NOT_SUPPORT: ESP_LOGW(TAG, "Wildcard Subscriptions not supported"); break; + default: - ESP_LOGW(TAG, "Connection refused, Unknow reason"); + ESP_LOGW(TAG, "Connection refused, Unknown reason"); break; } } @@ -303,7 +361,8 @@ esp_err_t esp_mqtt5_client_publish_check(esp_mqtt5_client_handle_t client, int q /* Flow control to check PUBLISH(No PUBACK or PUBCOMP received) packet sent count(Only record QoS1 and QoS2)*/ if (client->send_publish_packet_count > client->mqtt5_config->server_resp_property_info.receive_maximum) { - ESP_LOGE(TAG, "Client send more than %d QoS1 and QoS2 PUBLISH packet without no ack", client->mqtt5_config->server_resp_property_info.receive_maximum); + ESP_LOGE(TAG, "Client send more than %d QoS1 and QoS2 PUBLISH packet without no ack", + client->mqtt5_config->server_resp_property_info.receive_maximum); return ESP_FAIL; } @@ -324,6 +383,7 @@ void esp_mqtt5_client_destory(esp_mqtt5_client_handle_t client) esp_mqtt5_client_delete_user_property(client->mqtt5_config->disconnect_property_info.user_property); free(client->mqtt5_config); } + free(client->event.property); } } @@ -341,7 +401,8 @@ static void esp_mqtt5_client_delete_topic_alias(mqtt5_topic_alias_handle_t topic } } -static esp_err_t esp_mqtt5_client_update_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, char *topic, size_t topic_len) +static esp_err_t esp_mqtt5_client_update_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, + uint16_t topic_alias, char *topic, size_t topic_len) { mqtt5_topic_alias_item_t item; bool found = false; @@ -351,6 +412,7 @@ static esp_err_t esp_mqtt5_client_update_topic_alias(mqtt5_topic_alias_handle_t break; } } + if (found) { if ((item->topic_len != topic_len) || strncmp(topic, item->topic, topic_len)) { free(item->topic); @@ -372,10 +434,12 @@ static esp_err_t esp_mqtt5_client_update_topic_alias(mqtt5_topic_alias_handle_t memcpy(item->topic, topic, topic_len); STAILQ_INSERT_TAIL(topic_alias_handle, item, next); } + return ESP_OK; } -static char *esp_mqtt5_client_get_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, size_t *topic_length) +static char *esp_mqtt5_client_get_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, + size_t *topic_length) { mqtt5_topic_alias_item_t item; STAILQ_FOREACH(item, topic_alias_handle, next) { @@ -388,7 +452,8 @@ static char *esp_mqtt5_client_get_topic_alias(mqtt5_topic_alias_handle_t topic_a return NULL; } -static esp_err_t esp_mqtt5_user_property_copy(mqtt5_user_property_handle_t user_property_new, const mqtt5_user_property_handle_t user_property_old) +static esp_err_t esp_mqtt5_user_property_copy(mqtt5_user_property_handle_t user_property_new, + const mqtt5_user_property_handle_t user_property_old) { if (!user_property_new || !user_property_old) { ESP_LOGE(TAG, "Input is NULL"); @@ -415,12 +480,14 @@ static esp_err_t esp_mqtt5_user_property_copy(mqtt5_user_property_handle_t user_ return ESP_OK; } -esp_err_t esp_mqtt5_client_set_publish_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_publish_property_config_t *property) +esp_err_t esp_mqtt5_client_set_publish_property(esp_mqtt5_client_handle_t client, + const esp_mqtt5_publish_property_config_t *property) { if (!client) { ESP_LOGE(TAG, "Client was not initialized"); return ESP_ERR_INVALID_ARG; } + MQTT_API_LOCK(client); /* Check protocol version */ @@ -429,27 +496,33 @@ esp_err_t esp_mqtt5_client_set_publish_property(esp_mqtt5_client_handle_t client MQTT_API_UNLOCK(client); return ESP_FAIL; } + /* Check topic alias less than server maximum topic alias */ if (property->topic_alias > client->mqtt5_config->server_resp_property_info.topic_alias_maximum) { - ESP_LOGE(TAG, "Topic alias %d is bigger than server support %d", property->topic_alias, client->mqtt5_config->server_resp_property_info.topic_alias_maximum); + ESP_LOGE(TAG, "Topic alias %d is bigger than server support %d", property->topic_alias, + client->mqtt5_config->server_resp_property_info.topic_alias_maximum); MQTT_API_UNLOCK(client); return ESP_FAIL; } + client->mqtt5_config->publish_property_info = property; MQTT_API_UNLOCK(client); return ESP_OK; } -esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_subscribe_property_config_t *property) +esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t client, + const esp_mqtt5_subscribe_property_config_t *property) { if (!client) { ESP_LOGE(TAG, "Client was not initialized"); return ESP_ERR_INVALID_ARG; } + if (property->retain_handle > 2) { ESP_LOGE(TAG, "retain_handle only support 0, 1, 2"); return -1; } + MQTT_API_LOCK(client); /* Check protocol version */ @@ -458,6 +531,7 @@ esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t clie MQTT_API_UNLOCK(client); return ESP_FAIL; } + if (property->is_share_subscribe) { if (property->no_local_flag) { // MQTT-3.8.3-4 not allow that No Local bit to 1 on a Shared Subscription @@ -465,28 +539,33 @@ esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t clie MQTT_API_UNLOCK(client); return ESP_FAIL; } + if (!client->mqtt5_config->server_resp_property_info.shared_subscribe_available) { ESP_LOGE(TAG, "MQTT broker not support shared subscribe"); MQTT_API_UNLOCK(client); return ESP_FAIL; } + if (!property->share_name || !strlen(property->share_name)) { ESP_LOGE(TAG, "Share name can't be empty for shared subscribe"); MQTT_API_UNLOCK(client); return ESP_FAIL; } } + client->mqtt5_config->subscribe_property_info = property; MQTT_API_UNLOCK(client); return ESP_OK; } -esp_err_t esp_mqtt5_client_set_unsubscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_unsubscribe_property_config_t *property) +esp_err_t esp_mqtt5_client_set_unsubscribe_property(esp_mqtt5_client_handle_t client, + const esp_mqtt5_unsubscribe_property_config_t *property) { if (!client) { ESP_LOGE(TAG, "Client was not initialized"); return ESP_ERR_INVALID_ARG; } + MQTT_API_LOCK(client); /* Check protocol version */ @@ -495,29 +574,34 @@ esp_err_t esp_mqtt5_client_set_unsubscribe_property(esp_mqtt5_client_handle_t cl MQTT_API_UNLOCK(client); return ESP_FAIL; } + if (property->is_share_subscribe) { if (!client->mqtt5_config->server_resp_property_info.shared_subscribe_available) { ESP_LOGE(TAG, "MQTT broker not support shared subscribe"); MQTT_API_UNLOCK(client); return ESP_FAIL; } + if (!property->share_name || !strlen(property->share_name)) { ESP_LOGE(TAG, "Share name can't be empty for shared subscribe"); MQTT_API_UNLOCK(client); return ESP_FAIL; } } + client->mqtt5_config->unsubscribe_property_info = property; MQTT_API_UNLOCK(client); return ESP_OK; } -esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_disconnect_property_config_t *property) +esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t client, + const esp_mqtt5_disconnect_property_config_t *property) { if (!client) { ESP_LOGE(TAG, "Client was not initialized"); return ESP_ERR_INVALID_ARG; } + MQTT_API_LOCK(client); /* Check protocol version */ @@ -526,13 +610,16 @@ esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t cli MQTT_API_UNLOCK(client); return ESP_FAIL; } + if (property) { if (property->session_expiry_interval) { client->mqtt5_config->disconnect_property_info.session_expiry_interval = property->session_expiry_interval; } + if (property->disconnect_reason) { client->mqtt5_config->disconnect_property_info.disconnect_reason = property->disconnect_reason; } + if (property->user_property) { esp_mqtt5_client_delete_user_property(client->mqtt5_config->disconnect_property_info.user_property); client->mqtt5_config->disconnect_property_info.user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t)); @@ -541,7 +628,9 @@ esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t cli return ESP_ERR_NO_MEM; }); STAILQ_INIT(client->mqtt5_config->disconnect_property_info.user_property); - if (esp_mqtt5_user_property_copy(client->mqtt5_config->disconnect_property_info.user_property, property->user_property) != ESP_OK) { + + if (esp_mqtt5_user_property_copy(client->mqtt5_config->disconnect_property_info.user_property, + property->user_property) != ESP_OK) { ESP_LOGE(TAG, "esp_mqtt5_user_property_copy fail"); free(client->mqtt5_config->disconnect_property_info.user_property); client->mqtt5_config->disconnect_property_info.user_property = NULL; @@ -555,12 +644,14 @@ esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t cli return ESP_OK; } -esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_connection_property_config_t *connect_property) +esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client, + const esp_mqtt5_connection_property_config_t *connect_property) { if (!client) { ESP_LOGE(TAG, "Client was not initialized"); return ESP_ERR_INVALID_ARG; } + MQTT_API_LOCK(client); /* Check protocol version */ @@ -569,13 +660,16 @@ esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client MQTT_API_UNLOCK(client); return ESP_FAIL; } + if (connect_property) { if (connect_property->session_expiry_interval) { client->mqtt5_config->connect_property_info.session_expiry_interval = connect_property->session_expiry_interval; } + if (connect_property->maximum_packet_size) { if (connect_property->maximum_packet_size > client->mqtt_state.in_buffer_length) { - ESP_LOGW(TAG, "Connect maximum_packet_size property is over buffer_size(%d), Please first change it", client->mqtt_state.in_buffer_length); + ESP_LOGW(TAG, "Connect maximum_packet_size property is over buffer_size(%d), Please first change it", + client->mqtt_state.in_buffer_length); MQTT_API_UNLOCK(client); return ESP_FAIL; } else { @@ -584,62 +678,82 @@ esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client } else { client->mqtt5_config->connect_property_info.maximum_packet_size = client->mqtt_state.in_buffer_length; } + if (connect_property->receive_maximum) { client->mqtt5_config->connect_property_info.receive_maximum = connect_property->receive_maximum; } + if (connect_property->topic_alias_maximum) { client->mqtt5_config->connect_property_info.topic_alias_maximum = connect_property->topic_alias_maximum; + if (!client->mqtt5_config->peer_topic_alias) { client->mqtt5_config->peer_topic_alias = calloc(1, sizeof(struct mqtt5_topic_alias_list_t)); ESP_MEM_CHECK(TAG, client->mqtt5_config->peer_topic_alias, goto _mqtt_set_config_failed); STAILQ_INIT(client->mqtt5_config->peer_topic_alias); } } + if (connect_property->request_resp_info) { client->mqtt5_config->connect_property_info.request_resp_info = connect_property->request_resp_info; } + if (connect_property->request_problem_info) { client->mqtt5_config->connect_property_info.request_problem_info = connect_property->request_problem_info; } + if (connect_property->user_property) { esp_mqtt5_client_delete_user_property(client->mqtt5_config->connect_property_info.user_property); client->mqtt5_config->connect_property_info.user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t)); ESP_MEM_CHECK(TAG, client->mqtt5_config->connect_property_info.user_property, goto _mqtt_set_config_failed); STAILQ_INIT(client->mqtt5_config->connect_property_info.user_property); - if (esp_mqtt5_user_property_copy(client->mqtt5_config->connect_property_info.user_property, connect_property->user_property) != ESP_OK) { + + if (esp_mqtt5_user_property_copy(client->mqtt5_config->connect_property_info.user_property, + connect_property->user_property) != ESP_OK) { ESP_LOGE(TAG, "esp_mqtt5_user_property_copy fail"); goto _mqtt_set_config_failed; } } + if (connect_property->payload_format_indicator) { client->mqtt5_config->will_property_info.payload_format_indicator = connect_property->payload_format_indicator; } + if (connect_property->will_delay_interval) { client->mqtt5_config->will_property_info.will_delay_interval = connect_property->will_delay_interval; } + if (connect_property->message_expiry_interval) { client->mqtt5_config->will_property_info.message_expiry_interval = connect_property->message_expiry_interval; } - ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(connect_property->content_type, &client->mqtt5_config->will_property_info.content_type), goto _mqtt_set_config_failed); - ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(connect_property->response_topic, &client->mqtt5_config->will_property_info.response_topic), goto _mqtt_set_config_failed); + + ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(connect_property->content_type, + &client->mqtt5_config->will_property_info.content_type), goto _mqtt_set_config_failed); + ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(connect_property->response_topic, + &client->mqtt5_config->will_property_info.response_topic), goto _mqtt_set_config_failed); + if (connect_property->correlation_data && connect_property->correlation_data_len) { free(client->mqtt5_config->will_property_info.correlation_data); client->mqtt5_config->will_property_info.correlation_data = malloc(connect_property->correlation_data_len); ESP_MEM_CHECK(TAG, client->mqtt5_config->will_property_info.correlation_data, goto _mqtt_set_config_failed); - memcpy(client->mqtt5_config->will_property_info.correlation_data, connect_property->correlation_data, connect_property->correlation_data_len); + memcpy(client->mqtt5_config->will_property_info.correlation_data, connect_property->correlation_data, + connect_property->correlation_data_len); client->mqtt5_config->will_property_info.correlation_data_len = connect_property->correlation_data_len; } + if (connect_property->will_user_property) { esp_mqtt5_client_delete_user_property(client->mqtt5_config->will_property_info.user_property); client->mqtt5_config->will_property_info.user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t)); ESP_MEM_CHECK(TAG, client->mqtt5_config->will_property_info.user_property, goto _mqtt_set_config_failed); STAILQ_INIT(client->mqtt5_config->will_property_info.user_property); - if (esp_mqtt5_user_property_copy(client->mqtt5_config->will_property_info.user_property, connect_property->will_user_property) != ESP_OK) { + + if (esp_mqtt5_user_property_copy(client->mqtt5_config->will_property_info.user_property, + connect_property->will_user_property) != ESP_OK) { ESP_LOGE(TAG, "esp_mqtt5_user_property_copy fail"); goto _mqtt_set_config_failed; } } } + MQTT_API_UNLOCK(client); return ESP_OK; _mqtt_set_config_failed: @@ -648,7 +762,8 @@ _mqtt_set_config_failed: return ESP_ERR_NO_MEM; } -esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_property, esp_mqtt5_user_property_item_t item[], uint8_t item_num) +esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_property, + esp_mqtt5_user_property_item_t item[], uint8_t item_num) { if (!item_num || !item) { ESP_LOGE(TAG, "Input value is NULL"); @@ -667,7 +782,6 @@ esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_ ESP_MEM_CHECK(TAG, user_property_item, goto err); size_t key_len = strlen(item[i].key); size_t value_len = strlen(item[i].value); - user_property_item->key = calloc(1, key_len + 1); ESP_MEM_CHECK(TAG, user_property_item->key, { free(user_property_item); @@ -675,7 +789,6 @@ esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_ }); memcpy(user_property_item->key, item[i].key, key_len); user_property_item->key[key_len] = '\0'; - user_property_item->value = calloc(1, value_len + 1); ESP_MEM_CHECK(TAG, user_property_item->value, { free(user_property_item->key); @@ -684,10 +797,10 @@ esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_ }); memcpy(user_property_item->value, item[i].value, value_len); user_property_item->value[value_len] = '\0'; - STAILQ_INSERT_TAIL(*user_property, user_property_item, next); } } + return ESP_OK; err: esp_mqtt5_client_delete_user_property(*user_property); @@ -695,9 +808,11 @@ err: return ESP_ERR_NO_MEM; } -esp_err_t esp_mqtt5_client_get_user_property(mqtt5_user_property_handle_t user_property, esp_mqtt5_user_property_item_t *item, uint8_t *item_num) +esp_err_t esp_mqtt5_client_get_user_property(mqtt5_user_property_handle_t user_property, + esp_mqtt5_user_property_item_t *item, uint8_t *item_num) { int i = 0, j = 0; + if (user_property && item && *item_num) { mqtt5_user_property_item_t user_property_item; uint8_t num = *item_num; @@ -729,27 +844,33 @@ esp_err_t esp_mqtt5_client_get_user_property(mqtt5_user_property_handle_t user_p ESP_LOGE(TAG, "Input value is NULL or item_num is 0"); return ESP_FAIL; } + err: + for (j = 0; j < i; j ++) { if (item[j].key) { free((char *)item[j].key); } + if (item[j].value) { free((char *)item[j].value); } } + return ESP_ERR_NO_MEM; } uint8_t esp_mqtt5_client_get_user_property_count(mqtt5_user_property_handle_t user_property) { uint8_t count = 0; + if (user_property) { mqtt5_user_property_item_t item; STAILQ_FOREACH(item, user_property, next) { count ++; } } + return count; } @@ -764,5 +885,6 @@ void esp_mqtt5_client_delete_user_property(mqtt5_user_property_handle_t user_pro free(item); } } + free(user_property); } diff --git a/mqtt_client.c b/mqtt_client.c index dd0ca7f..bf8bbde 100644 --- a/mqtt_client.c +++ b/mqtt_client.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include #include @@ -44,7 +49,7 @@ static esp_err_t send_disconnect_msg(esp_mqtt_client_handle_t client); * * @param err: Error reported from TCP transport * @param client: MQTT client handle - * @param mid_message: True if the error occured when reading incomplete message + * @param mid_message: True if the error occurred when reading incomplete message * * @return - 0 on Timeout * - -1 on Timeout with incomplete message @@ -57,6 +62,7 @@ static int esp_mqtt_handle_transport_read_error(int err, esp_mqtt_client_handle_ // No error message, because we could've read with timeout 0 (caller will decide) return -1; } + // Not an error, continue ESP_LOGV(TAG, "%s: transport_read(): call timed out before data was ready!", __func__); return 0; @@ -79,7 +85,8 @@ enum esp_mqtt_ssl_cert_key_api { MQTT_SSL_DATA_API_MAX, }; -static esp_err_t esp_mqtt_set_cert_key_data(esp_transport_handle_t ssl, enum esp_mqtt_ssl_cert_key_api what, const char *cert_key_data, int cert_key_len) +static esp_err_t esp_mqtt_set_cert_key_data(esp_transport_handle_t ssl, enum esp_mqtt_ssl_cert_key_api what, + const char *cert_key_data, int cert_key_len) { char *data = (char *)cert_key_data; int ssl_transport_api_id = what; @@ -95,11 +102,13 @@ static esp_err_t esp_mqtt_set_cert_key_data(esp_transport_handle_t ssl, enum esp ssl_transport_api_id += MQTT_SSL_DATA_API_MAX; len = strlen(data); } + #ifndef MQTT_SUPPORTED_FEATURE_DER_CERTIFICATES else { ESP_LOGE(TAG, "Explicit cert-/key-len is not available in IDF version %s", IDF_VER); return ESP_ERR_NOT_SUPPORTED; } + #endif // option to force the cert/key config to null (i.e. skip validation) when existing config updates @@ -110,32 +119,41 @@ static esp_err_t esp_mqtt_set_cert_key_data(esp_transport_handle_t ssl, enum esp switch (ssl_transport_api_id) { #ifdef MQTT_SUPPORTED_FEATURE_DER_CERTIFICATES + case MQTT_SSL_DATA_API_CA_CERT: esp_transport_ssl_set_cert_data_der(ssl, data, len); break; + case MQTT_SSL_DATA_API_CLIENT_CERT: esp_transport_ssl_set_client_cert_data_der(ssl, data, len); break; + case MQTT_SSL_DATA_API_CLIENT_KEY: esp_transport_ssl_set_client_key_data_der(ssl, data, len); break; #endif + case MQTT_SSL_DATA_API_CA_CERT + MQTT_SSL_DATA_API_MAX: esp_transport_ssl_set_cert_data(ssl, data, len); break; + case MQTT_SSL_DATA_API_CLIENT_CERT + MQTT_SSL_DATA_API_MAX: esp_transport_ssl_set_client_cert_data(ssl, data, len); break; + case MQTT_SSL_DATA_API_CLIENT_KEY + MQTT_SSL_DATA_API_MAX: esp_transport_ssl_set_client_key_data(ssl, data, len); break; + default: return ESP_ERR_INVALID_ARG; } + return ESP_OK; } -static esp_err_t esp_mqtt_set_ssl_transport_properties(esp_transport_list_handle_t transport_list, mqtt_config_storage_t *cfg) +static esp_err_t esp_mqtt_set_ssl_transport_properties(esp_transport_list_handle_t transport_list, + mqtt_config_storage_t *cfg) { esp_transport_handle_t ssl = esp_transport_list_get_transport(transport_list, MQTT_OVER_SSL_SCHEME); @@ -156,12 +174,11 @@ static esp_err_t esp_mqtt_set_ssl_transport_properties(esp_transport_list_handle } else { ESP_OK_CHECK(TAG, esp_mqtt_set_cert_key_data(ssl, MQTT_SSL_DATA_API_CA_CERT, cfg->cacert_buf, cfg->cacert_bytes), goto esp_mqtt_set_transport_failed); - } - if(cfg->ciphersuites_list) - { + + if (cfg->ciphersuites_list) { #if defined(MQTT_SUPPORTED_FEATURE_CIPHERSUITES_LIST) - esp_transport_ssl_set_ciphersuites_list(ssl,cfg->ciphersuites_list); + esp_transport_ssl_set_ciphersuites_list(ssl, cfg->ciphersuites_list); #else ESP_LOGE(TAG, "Cipher suites list feature is not available in IDF version %s", IDF_VER); goto esp_mqtt_set_transport_failed; @@ -173,7 +190,8 @@ static esp_err_t esp_mqtt_set_ssl_transport_properties(esp_transport_list_handle #ifdef CONFIG_ESP_TLS_PSK_VERIFICATION esp_transport_ssl_set_psk_key_hint(ssl, cfg->psk_hint_key); #else - ESP_LOGE(TAG, "PSK authentication configured but not enabled in menuconfig: Please enable ESP_TLS_PSK_VERIFICATION option"); + ESP_LOGE(TAG, + "PSK authentication configured but not enabled in menuconfig: Please enable ESP_TLS_PSK_VERIFICATION option"); goto esp_mqtt_set_transport_failed; #endif #else @@ -187,7 +205,8 @@ static esp_err_t esp_mqtt_set_ssl_transport_properties(esp_transport_list_handle #if defined(CONFIG_MBEDTLS_SSL_ALPN) || defined(CONFIG_WOLFSSL_HAVE_ALPN) esp_transport_ssl_set_alpn_protocol(ssl, (const char **)cfg->alpn_protos); #else - ESP_LOGE(TAG, "APLN configured but not enabled in menuconfig: Please enable MBEDTLS_SSL_ALPN or WOLFSSL_HAVE_ALPN option"); + ESP_LOGE(TAG, + "APLN configured but not enabled in menuconfig: Please enable MBEDTLS_SSL_ALPN or WOLFSSL_HAVE_ALPN option"); goto esp_mqtt_set_transport_failed; #endif #else @@ -196,7 +215,6 @@ static esp_err_t esp_mqtt_set_ssl_transport_properties(esp_transport_list_handle #endif } - if (cfg->skip_cert_common_name_check) { #if defined(MQTT_SUPPORTED_FEATURE_SKIP_CRT_CMN_NAME_CHECK) && MQTT_ENABLE_SSL esp_transport_ssl_skip_common_name_check(ssl); @@ -257,25 +275,25 @@ static esp_err_t esp_mqtt_set_ssl_transport_properties(esp_transport_list_handle #endif /* MQTT_SUPPORTED_FEATURE_ECDSA_PERIPHERAL */ } - ESP_OK_CHECK(TAG, esp_mqtt_set_cert_key_data(ssl, MQTT_SSL_DATA_API_CLIENT_CERT, cfg->clientcert_buf, cfg->clientcert_bytes), + ESP_OK_CHECK(TAG, esp_mqtt_set_cert_key_data(ssl, MQTT_SSL_DATA_API_CLIENT_CERT, cfg->clientcert_buf, + cfg->clientcert_bytes), goto esp_mqtt_set_transport_failed); - ESP_OK_CHECK(TAG, esp_mqtt_set_cert_key_data(ssl, MQTT_SSL_DATA_API_CLIENT_KEY, cfg->clientkey_buf, cfg->clientkey_bytes), + ESP_OK_CHECK(TAG, esp_mqtt_set_cert_key_data(ssl, MQTT_SSL_DATA_API_CLIENT_KEY, cfg->clientkey_buf, + cfg->clientkey_bytes), goto esp_mqtt_set_transport_failed); if (cfg->clientkey_password && cfg->clientkey_password_len) { #if defined(MQTT_SUPPORTED_FEATURE_CLIENT_KEY_PASSWORD) && MQTT_ENABLE_SSL esp_transport_ssl_set_client_key_password(ssl, - cfg->clientkey_password, - cfg->clientkey_password_len); + cfg->clientkey_password, + cfg->clientkey_password_len); #else ESP_LOGE(TAG, "Password protected keys are not available in IDF version %s", IDF_VER); goto esp_mqtt_set_transport_failed; #endif } - return ESP_OK; - esp_mqtt_set_transport_failed: return ESP_FAIL; } @@ -288,25 +306,31 @@ static esp_err_t esp_mqtt_check_cfg_conflict(const mqtt_config_storage_t *cfg, c ESP_LOGE(TAG, "Invalid configuration"); return ESP_ERR_INVALID_ARG; } - esp_err_t ret = ESP_OK; - bool ssl_cfg_enabled = cfg->use_global_ca_store || cfg->cacert_buf || cfg->clientcert_buf || cfg->psk_hint_key || cfg->alpn_protos; + esp_err_t ret = ESP_OK; + bool ssl_cfg_enabled = cfg->use_global_ca_store || cfg->cacert_buf || cfg->clientcert_buf || cfg->psk_hint_key || + cfg->alpn_protos; bool is_ssl_scheme = false; + if (cfg->scheme) { - is_ssl_scheme = (strncasecmp(cfg->scheme, MQTT_OVER_SSL_SCHEME, sizeof(MQTT_OVER_SSL_SCHEME)) == 0) || (strncasecmp(cfg->scheme, MQTT_OVER_WSS_SCHEME, sizeof(MQTT_OVER_WSS_SCHEME)) == 0); + is_ssl_scheme = (strncasecmp(cfg->scheme, MQTT_OVER_SSL_SCHEME, sizeof(MQTT_OVER_SSL_SCHEME)) == 0) || + (strncasecmp(cfg->scheme, MQTT_OVER_WSS_SCHEME, sizeof(MQTT_OVER_WSS_SCHEME)) == 0); } if (!is_ssl_scheme && ssl_cfg_enabled) { if (cfg->uri) { ESP_LOGW(TAG, "SSL related configs set, but the URI scheme specifies a non-SSL scheme, scheme = %s", cfg->scheme); } else { - ESP_LOGW(TAG, "SSL related configs set, but the transport protocol is a non-SSL scheme, transport = %d", user_cfg->broker.address.transport); + ESP_LOGW(TAG, "SSL related configs set, but the transport protocol is a non-SSL scheme, transport = %d", + user_cfg->broker.address.transport); } + ret = ESP_ERR_INVALID_ARG; } if (cfg->uri && user_cfg->broker.address.transport) { - ESP_LOGW(TAG, "Transport config set, but overridden by scheme from URI: transport = %d, uri scheme = %s", user_cfg->broker.address.transport, cfg->scheme); + ESP_LOGW(TAG, "Transport config set, but overridden by scheme from URI: transport = %d, uri scheme = %s", + user_cfg->broker.address.transport, cfg->scheme); ret = ESP_ERR_INVALID_ARG; } @@ -318,40 +342,50 @@ bool esp_mqtt_set_if_config(char const *const new_config, char **old_config) if (new_config) { free(*old_config); *old_config = strdup(new_config); + if (*old_config == NULL) { return false; } } + return true; } static esp_err_t esp_mqtt_client_create_transport(esp_mqtt_client_handle_t client) { esp_err_t ret = ESP_OK; + if (client->transport_list) { esp_transport_list_destroy(client->transport_list); client->transport_list = NULL; } + if (client->config->scheme) { client->transport_list = esp_transport_list_init(); ESP_MEM_CHECK(TAG, client->transport_list, return ESP_ERR_NO_MEM); - if ((strncasecmp(client->config->scheme, MQTT_OVER_TCP_SCHEME, sizeof(MQTT_OVER_TCP_SCHEME)) == 0) || (strncasecmp(client->config->scheme, MQTT_OVER_WS_SCHEME, sizeof(MQTT_OVER_WS_SCHEME)) == 0)) { + if ((strncasecmp(client->config->scheme, MQTT_OVER_TCP_SCHEME, sizeof(MQTT_OVER_TCP_SCHEME)) == 0) || + (strncasecmp(client->config->scheme, MQTT_OVER_WS_SCHEME, sizeof(MQTT_OVER_WS_SCHEME)) == 0)) { esp_transport_handle_t tcp = esp_transport_tcp_init(); ESP_MEM_CHECK(TAG, tcp, return ESP_ERR_NO_MEM); esp_transport_set_default_port(tcp, MQTT_TCP_DEFAULT_PORT); + if (client->config->if_name) { esp_transport_tcp_set_interface_name(tcp, client->config->if_name); } + esp_transport_list_add(client->transport_list, tcp, MQTT_OVER_TCP_SCHEME); + if (strncasecmp(client->config->scheme, MQTT_OVER_WS_SCHEME, sizeof(MQTT_OVER_WS_SCHEME)) == 0) { #if MQTT_ENABLE_WS esp_transport_handle_t ws = esp_transport_ws_init(tcp); ESP_MEM_CHECK(TAG, ws, return ESP_ERR_NO_MEM); esp_transport_set_default_port(ws, MQTT_WS_DEFAULT_PORT); + if (client->config->path) { esp_transport_ws_set_path(ws, client->config->path); } + #ifdef MQTT_SUPPORTED_FEATURE_WS_SUBPROTOCOL esp_transport_ws_set_subprotocol(ws, MQTT_OVER_TCP_SCHEME); #endif @@ -361,23 +395,29 @@ static esp_err_t esp_mqtt_client_create_transport(esp_mqtt_client_handle_t clien ret = ESP_FAIL; #endif } - } else if ((strncasecmp(client->config->scheme, MQTT_OVER_SSL_SCHEME, sizeof(MQTT_OVER_SSL_SCHEME)) == 0) || (strncasecmp(client->config->scheme, MQTT_OVER_WSS_SCHEME, sizeof(MQTT_OVER_WSS_SCHEME)) == 0)) { + } else if ((strncasecmp(client->config->scheme, MQTT_OVER_SSL_SCHEME, sizeof(MQTT_OVER_SSL_SCHEME)) == 0) || + (strncasecmp(client->config->scheme, MQTT_OVER_WSS_SCHEME, sizeof(MQTT_OVER_WSS_SCHEME)) == 0)) { #if MQTT_ENABLE_SSL esp_transport_handle_t ssl = esp_transport_ssl_init(); ESP_MEM_CHECK(TAG, ssl, return ESP_ERR_NO_MEM); esp_transport_set_default_port(ssl, MQTT_SSL_DEFAULT_PORT); + if (client->config->if_name) { esp_transport_ssl_set_interface_name(ssl, client->config->if_name); } + esp_transport_list_add(client->transport_list, ssl, MQTT_OVER_SSL_SCHEME); + if (strncasecmp(client->config->scheme, MQTT_OVER_WSS_SCHEME, sizeof(MQTT_OVER_WSS_SCHEME)) == 0) { #if MQTT_ENABLE_WS esp_transport_handle_t wss = esp_transport_ws_init(ssl); ESP_MEM_CHECK(TAG, wss, return ESP_ERR_NO_MEM); esp_transport_set_default_port(wss, MQTT_WSS_DEFAULT_PORT); + if (client->config->path) { esp_transport_ws_set_path(wss, client->config->path); } + #ifdef MQTT_SUPPORTED_FEATURE_WS_SUBPROTOCOL esp_transport_ws_set_subprotocol(wss, MQTT_OVER_TCP_SCHEME); #endif @@ -387,6 +427,7 @@ static esp_err_t esp_mqtt_client_create_transport(esp_mqtt_client_handle_t clien ret = ESP_FAIL; #endif } + #else ESP_LOGE(TAG, "Please enable MQTT_ENABLE_SSL to use %s", client->config->scheme); ret = ESP_FAIL; @@ -399,6 +440,7 @@ static esp_err_t esp_mqtt_client_create_transport(esp_mqtt_client_handle_t clien ESP_LOGE(TAG, "No scheme found"); ret = ESP_FAIL; } + return ret; } @@ -408,9 +450,11 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl ESP_LOGE(TAG, "Client was not initialized"); return ESP_ERR_INVALID_ARG; } + MQTT_API_LOCK(client); //Copy user configurations to client context esp_err_t err = ESP_OK; + if (!client->config) { client->config = calloc(1, sizeof(mqtt_config_storage_t)); ESP_MEM_CHECK(TAG, client->config, { @@ -421,12 +465,14 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl mqtt_msg_buffer_destroy(&client->mqtt_state.connection); int buffer_size = config->buffer.size; + if (buffer_size <= 0) { buffer_size = MQTT_BUFFER_SIZE_BYTE; } // use separate value for output buffer size if configured int out_buffer_size = config->buffer.out_size > 0 ? config->buffer.out_size : buffer_size; + if (mqtt_msg_buffer_init(&client->mqtt_state.connection, out_buffer_size) != ESP_OK) { goto _mqtt_set_config_failed; } @@ -435,18 +481,20 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl client->mqtt_state.in_buffer = (uint8_t *)malloc(buffer_size); ESP_MEM_CHECK(TAG, client->mqtt_state.in_buffer, goto _mqtt_set_config_failed); client->mqtt_state.in_buffer_length = buffer_size; - client->config->message_retransmit_timeout = config->session.message_retransmit_timeout; + if (config->session.message_retransmit_timeout <= 0) { client->config->message_retransmit_timeout = MQTT_DEFAULT_RETRANSMIT_TIMEOUT_MS; } client->config->task_prio = config->task.priority; + if (client->config->task_prio <= 0) { client->config->task_prio = MQTT_TASK_PRIORITY; } client->config->task_stack = config->task.stack_size; + if (client->config->task_stack <= 0) { client->config->task_stack = MQTT_TASK_STACK; } @@ -460,29 +508,38 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl } err = ESP_ERR_NO_MEM; - ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->broker.address.hostname, &client->config->host), goto _mqtt_set_config_failed); - ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->broker.address.path, &client->config->path), goto _mqtt_set_config_failed); - ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->credentials.username, &client->mqtt_state.connection.information.username), goto _mqtt_set_config_failed); - ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->credentials.authentication.password, &client->mqtt_state.connection.information.password), goto _mqtt_set_config_failed); + ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->broker.address.hostname, &client->config->host), + goto _mqtt_set_config_failed); + ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->broker.address.path, &client->config->path), + goto _mqtt_set_config_failed); + ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->credentials.username, + &client->mqtt_state.connection.information.username), goto _mqtt_set_config_failed); + ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->credentials.authentication.password, + &client->mqtt_state.connection.information.password), goto _mqtt_set_config_failed); if (!config->credentials.set_null_client_id) { if (config->credentials.client_id) { - ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->credentials.client_id, &client->mqtt_state.connection.information.client_id), goto _mqtt_set_config_failed); + ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->credentials.client_id, + &client->mqtt_state.connection.information.client_id), goto _mqtt_set_config_failed); } else if (client->mqtt_state.connection.information.client_id == NULL) { client->mqtt_state.connection.information.client_id = platform_create_id_string(); } + ESP_MEM_CHECK(TAG, client->mqtt_state.connection.information.client_id, goto _mqtt_set_config_failed); ESP_LOGD(TAG, "MQTT client_id=%s", client->mqtt_state.connection.information.client_id); } - ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->broker.address.uri, &client->config->uri), goto _mqtt_set_config_failed); - ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->session.last_will.topic, &client->mqtt_state.connection.information.will_topic), goto _mqtt_set_config_failed); + ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->broker.address.uri, &client->config->uri), + goto _mqtt_set_config_failed); + ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->session.last_will.topic, + &client->mqtt_state.connection.information.will_topic), goto _mqtt_set_config_failed); if (config->session.last_will.msg_len && config->session.last_will.msg) { free(client->mqtt_state.connection.information.will_message); client->mqtt_state.connection.information.will_message = malloc(config->session.last_will.msg_len); ESP_MEM_CHECK(TAG, client->mqtt_state.connection.information.will_message, goto _mqtt_set_config_failed); - memcpy(client->mqtt_state.connection.information.will_message, config->session.last_will.msg, config->session.last_will.msg_len); + memcpy(client->mqtt_state.connection.information.will_message, config->session.last_will.msg, + config->session.last_will.msg_len); client->mqtt_state.connection.information.will_length = config->session.last_will.msg_len; } else if (config->session.last_will.msg) { free(client->mqtt_state.connection.information.will_message); @@ -490,25 +547,31 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl ESP_MEM_CHECK(TAG, client->mqtt_state.connection.information.will_message, goto _mqtt_set_config_failed); client->mqtt_state.connection.information.will_length = strlen(config->session.last_will.msg); } + if (config->session.last_will.qos) { client->mqtt_state.connection.information.will_qos = config->session.last_will.qos; } + if (config->session.last_will.retain) { client->mqtt_state.connection.information.will_retain = config->session.last_will.retain; } if (config->session.disable_clean_session == client->mqtt_state.connection.information.clean_session) { client->mqtt_state.connection.information.clean_session = !config->session.disable_clean_session; + if (!client->mqtt_state.connection.information.clean_session && config->credentials.set_null_client_id) { ESP_LOGE(TAG, "Clean Session flag must be true if client has a null id"); } } + if (config->session.keepalive) { client->mqtt_state.connection.information.keepalive = config->session.keepalive; } + if (client->mqtt_state.connection.information.keepalive == 0) { client->mqtt_state.connection.information.keepalive = MQTT_KEEPALIVE_TICK; } + if (config->session.disable_keepalive) { // internal `keepalive` value (in connect_info) is in line with 3.1.2.10 Keep Alive from mqtt spec: // * keepalive=0: Keep alive mechanism disabled (server not to disconnect the client on its inactivity) @@ -519,6 +582,7 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl if (config->session.protocol_ver) { client->mqtt_state.connection.information.protocol_ver = config->session.protocol_ver; } + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_UNDEFINED) { #ifdef MQTT_PROTOCOL_311 client->mqtt_state.connection.information.protocol_ver = MQTT_PROTOCOL_V_3_1_1; @@ -533,6 +597,7 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl } client->config->network_timeout_ms = config->network.timeout_ms; + if (client->config->network_timeout_ms <= 0) { client->config->network_timeout_ms = MQTT_NETWORK_TIMEOUT_MS; } @@ -542,6 +607,7 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl } client->config->auto_reconnect = true; + if (config->network.disable_auto_reconnect == client->config->auto_reconnect) { client->config->auto_reconnect = !config->network.disable_auto_reconnect; } @@ -564,16 +630,18 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl for (int i = 0; i < client->config->num_alpn_protos; i++) { free(client->config->alpn_protos[i]); } + free(client->config->alpn_protos); client->config->num_alpn_protos = 0; - const char **p; - for (p = config->broker.verification.alpn_protos; *p != NULL; p++ ) { + for (p = config->broker.verification.alpn_protos; *p != NULL; p++) { client->config->num_alpn_protos++; } + // mbedTLS expects the list to be null-terminated - client->config->alpn_protos = calloc(client->config->num_alpn_protos + 1, sizeof(*config->broker.verification.alpn_protos)); + client->config->alpn_protos = calloc(client->config->num_alpn_protos + 1, + sizeof(*config->broker.verification.alpn_protos)); ESP_MEM_CHECK(TAG, client->config->alpn_protos, goto _mqtt_set_config_failed); for (int i = 0; i < client->config->num_alpn_protos; i++) { @@ -604,33 +672,39 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl client->config->clientkey_password_len = config->credentials.authentication.key_password_len; client->config->clientkey_password = malloc(client->config->clientkey_password_len); ESP_MEM_CHECK(TAG, client->config->clientkey_password, goto _mqtt_set_config_failed); - memcpy(client->config->clientkey_password, config->credentials.authentication.key_password, client->config->clientkey_password_len); + memcpy(client->config->clientkey_password, config->credentials.authentication.key_password, + client->config->clientkey_password_len); } if (config->broker.address.transport) { free(client->config->scheme); client->config->scheme = NULL; + if (config->broker.address.transport == MQTT_TRANSPORT_OVER_TCP) { client->config->scheme = create_string(MQTT_OVER_TCP_SCHEME, strlen(MQTT_OVER_TCP_SCHEME)); ESP_MEM_CHECK(TAG, client->config->scheme, goto _mqtt_set_config_failed); } + #if MQTT_ENABLE_WS else if (config->broker.address.transport == MQTT_TRANSPORT_OVER_WS) { client->config->scheme = create_string(MQTT_OVER_WS_SCHEME, strlen(MQTT_OVER_WS_SCHEME)); ESP_MEM_CHECK(TAG, client->config->scheme, goto _mqtt_set_config_failed); } + #endif #if MQTT_ENABLE_SSL else if (config->broker.address.transport == MQTT_TRANSPORT_OVER_SSL) { client->config->scheme = create_string(MQTT_OVER_SSL_SCHEME, strlen(MQTT_OVER_SSL_SCHEME)); ESP_MEM_CHECK(TAG, client->config->scheme, goto _mqtt_set_config_failed); } + #endif #if MQTT_ENABLE_WSS else if (config->broker.address.transport == MQTT_TRANSPORT_OVER_WSS) { client->config->scheme = create_string(MQTT_OVER_WSS_SCHEME, strlen(MQTT_OVER_WSS_SCHEME)); ESP_MEM_CHECK(TAG, client->config->scheme, goto _mqtt_set_config_failed); } + #endif } @@ -641,11 +715,10 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl goto _mqtt_set_config_failed; } } + client->config->outbox_limit = config->outbox.limit; esp_err_t config_has_conflict = esp_mqtt_check_cfg_conflict(client->config, config); - MQTT_API_UNLOCK(client); - return config_has_conflict; _mqtt_set_config_failed: esp_mqtt_destroy_config(client); @@ -658,15 +731,18 @@ void esp_mqtt_destroy_config(esp_mqtt_client_handle_t client) if (client->config == NULL) { return; } + free(client->mqtt_state.in_buffer); mqtt_msg_buffer_destroy(&client->mqtt_state.connection); free(client->config->host); free(client->config->uri); free(client->config->path); free(client->config->scheme); + for (int i = 0; i < client->config->num_alpn_protos; i++) { free(client->config->alpn_protos[i]); } + free(client->config->alpn_protos); free(client->config->clientkey_password); free(client->config->if_name); @@ -680,9 +756,11 @@ void esp_mqtt_destroy_config(esp_mqtt_client_handle_t client) #endif memset(&client->mqtt_state.connection.information, 0, sizeof(mqtt_connect_info_t)); #ifdef MQTT_SUPPORTED_FEATURE_EVENT_LOOP + if (client->config->event_loop_handle) { esp_event_loop_delete(client->config->event_loop_handle); } + #endif esp_transport_destroy(client->config->transport); memset(client->config, 0, sizeof(mqtt_config_storage_t)); @@ -701,13 +779,14 @@ static esp_err_t process_keepalive(esp_mqtt_client_handle_t client) if (client->mqtt_state.connection.information.keepalive > 0) { const uint64_t keepalive_ms = client->mqtt_state.connection.information.keepalive * 1000; - if (client->wait_for_ping_resp == true ) { + if (client->wait_for_ping_resp == true) { if (has_timed_out(client->keepalive_tick, keepalive_ms)) { ESP_LOGE(TAG, "No PING_RESP, disconnected"); esp_mqtt_abort_connection(client); client->wait_for_ping_resp = false; return ESP_FAIL; } + return ESP_OK; } @@ -717,21 +796,25 @@ static esp_err_t process_keepalive(esp_mqtt_client_handle_t client) esp_mqtt_abort_connection(client); return ESP_FAIL; } + client->wait_for_ping_resp = true; return ESP_OK; } } + return ESP_OK; } static inline esp_err_t esp_mqtt_write(esp_mqtt_client_handle_t client) { int wlen = 0, widx = 0, len = client->mqtt_state.connection.outbound_message.length; + while (len > 0) { wlen = esp_transport_write(client->transport, (char *)client->mqtt_state.connection.outbound_message.data + widx, len, client->config->network_timeout_ms); + if (wlen < 0) { ESP_LOGE(TAG, "Writing failed: errno=%d", errno); esp_mqtt_client_dispatch_transport_error(client); @@ -742,9 +825,11 @@ static inline esp_err_t esp_mqtt_write(esp_mqtt_client_handle_t client) ESP_LOGE(TAG, "Writing didn't complete in specified timeout: errno=%d", errno); return ESP_ERR_TIMEOUT; } + widx += wlen; len -= wlen; } + return ESP_OK; } @@ -752,30 +837,35 @@ static esp_err_t esp_mqtt_connect(esp_mqtt_client_handle_t client, int timeout_m { int read_len, connect_rsp_code = 0; client->wait_for_ping_resp = false; + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 mqtt5_msg_connect(&client->mqtt_state.connection, - &client->mqtt_state.connection.information, &client->mqtt5_config->connect_property_info, &client->mqtt5_config->will_property_info); + &client->mqtt_state.connection.information, &client->mqtt5_config->connect_property_info, + &client->mqtt5_config->will_property_info); #endif } else { mqtt_msg_connect(&client->mqtt_state.connection, &client->mqtt_state.connection.information); } + if (client->mqtt_state.connection.outbound_message.length == 0) { ESP_LOGE(TAG, "Connect message cannot be created"); return ESP_FAIL; } client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.connection.outbound_message.data); + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 client->mqtt_state.pending_msg_id = mqtt5_get_id(client->mqtt_state.connection.outbound_message.data, - client->mqtt_state.connection.outbound_message.length); + client->mqtt_state.connection.outbound_message.length); #endif } else { client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.connection.outbound_message.data, - client->mqtt_state.connection.outbound_message.length); + client->mqtt_state.connection.outbound_message.length); } + ESP_LOGD(TAG, "Sending MQTT CONNECT message, type: %d, id: %04X", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); @@ -786,9 +876,9 @@ static esp_err_t esp_mqtt_connect(esp_mqtt_client_handle_t client, int timeout_m client->mqtt_state.in_buffer_read_len = 0; client->mqtt_state.message_length = 0; - /* wait configured network timeout for broker connection response */ uint64_t connack_recv_started = platform_tick_get_ms(); + do { read_len = mqtt_message_receive(client, client->config->network_timeout_ms); } while (read_len == 0 && platform_tick_get_ms() - connack_recv_started < client->config->network_timeout_ms); @@ -802,38 +892,48 @@ static esp_err_t esp_mqtt_connect(esp_mqtt_client_handle_t client, int timeout_m ESP_LOGE(TAG, "Invalid MSG_TYPE response: %d, read_len: %d", mqtt_get_type(client->mqtt_state.in_buffer), read_len); return ESP_FAIL; } + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 + if (esp_mqtt5_parse_connack(client, &connect_rsp_code) == ESP_OK) { client->send_publish_packet_count = 0; return ESP_OK; } + #endif } else { client->mqtt_state.in_buffer_read_len = 0; connect_rsp_code = mqtt_get_connect_return_code(client->mqtt_state.in_buffer); + if (connect_rsp_code == MQTT_CONNECTION_ACCEPTED) { ESP_LOGD(TAG, "Connected"); return ESP_OK; } + switch (connect_rsp_code) { case MQTT_CONNECTION_REFUSE_PROTOCOL: ESP_LOGW(TAG, "Connection refused, bad protocol"); break; + case MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE: ESP_LOGW(TAG, "Connection refused, server unavailable"); break; + case MQTT_CONNECTION_REFUSE_BAD_USERNAME: ESP_LOGW(TAG, "Connection refused, bad username or password"); break; + case MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED: ESP_LOGW(TAG, "Connection refused, not authorized"); break; + default: - ESP_LOGW(TAG, "Connection refused, Unknow reason"); + ESP_LOGW(TAG, "Connection refused, Unknown reason"); break; } } + /* propagate event with connection refused error */ client->event.event_id = MQTT_EVENT_ERROR; client->event.error_handle->error_type = MQTT_ERROR_TYPE_CONNECTION_REFUSED; @@ -842,7 +942,6 @@ static esp_err_t esp_mqtt_connect(esp_mqtt_client_handle_t client, int timeout_m client->event.error_handle->esp_tls_last_esp_err = 0; client->event.error_handle->esp_tls_cert_verify_flags = 0; esp_mqtt_dispatch_event_with_msgid(client); - return ESP_FAIL; } @@ -864,15 +963,12 @@ static bool create_client_data(esp_mqtt_client_handle_t client) { client->event.error_handle = calloc(1, sizeof(esp_mqtt_error_codes_t)); ESP_MEM_CHECK(TAG, client->event.error_handle, return false) - client->api_lock = xSemaphoreCreateRecursiveMutex(); ESP_MEM_CHECK(TAG, client->api_lock, return false); - client->outbox = outbox_init(); ESP_MEM_CHECK(TAG, client->outbox, return false); client->status_bits = xEventGroupCreate(); ESP_MEM_CHECK(TAG, client->status_bits, return false); - return true; } @@ -880,13 +976,14 @@ esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *co { esp_mqtt_client_handle_t client = heap_caps_calloc(1, sizeof(struct esp_mqtt_client), #if MQTT_EVENT_QUEUE_SIZE > 1 - // if supporting multiple queued events, we keep track of them - // using atomic variable, so need to make sure it won't get allocated in PSRAM - MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + // if supporting multiple queued events, we keep track of them + // using atomic variable, so need to make sure it won't get allocated in PSRAM + MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); #else - MALLOC_CAP_DEFAULT); + MALLOC_CAP_DEFAULT); #endif ESP_MEM_CHECK(TAG, client, return NULL); + if (!create_client_data(client)) { goto _mqtt_init_failed; } @@ -894,6 +991,7 @@ esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *co if (esp_mqtt_set_config(client, config) != ESP_OK) { goto _mqtt_init_failed; } + #ifdef MQTT_SUPPORTED_FEATURE_EVENT_LOOP esp_event_loop_args_t no_task_loop = { .queue_size = MQTT_EVENT_QUEUE_SIZE, @@ -904,15 +1002,16 @@ esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *co atomic_init(&client->queued_events, 0); #endif #endif - client->keepalive_tick = platform_tick_get_ms(); client->reconnect_tick = platform_tick_get_ms(); client->refresh_connection_tick = platform_tick_get_ms(); client->wait_for_ping_resp = false; #ifdef MQTT_PROTOCOL_5 + if (esp_mqtt5_create_default_config(client) != ESP_OK) { goto _mqtt_init_failed; } + #endif return client; _mqtt_init_failed: @@ -925,22 +1024,29 @@ esp_err_t esp_mqtt_client_destroy(esp_mqtt_client_handle_t client) if (client == NULL) { return ESP_ERR_INVALID_ARG; } + if (client->run) { esp_mqtt_client_stop(client); } + esp_mqtt_destroy_config(client); + if (client->transport_list) { esp_transport_list_destroy(client->transport_list); } + if (client->outbox) { outbox_destroy(client->outbox); } + if (client->status_bits) { vEventGroupDelete(client->status_bits); } + if (client->api_lock) { vSemaphoreDelete(client->api_lock); } + free(client->event.error_handle); free(client); return ESP_OK; @@ -949,9 +1055,11 @@ esp_err_t esp_mqtt_client_destroy(esp_mqtt_client_handle_t client) static char *create_string(const char *ptr, int len) { char *ret; + if (len <= 0) { return NULL; } + ret = calloc(1, len + 1); ESP_MEM_CHECK(TAG, ret, return NULL); memcpy(ret, ptr, len); @@ -963,6 +1071,7 @@ esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client, const char *u struct http_parser_url puri; http_parser_url_init(&puri); int parser_status = http_parser_parse_url(uri, strlen(uri), 0, &puri); + if (parser_status != 0) { ESP_LOGE(TAG, "Error parse uri = %s", uri); return ESP_FAIL; @@ -982,7 +1091,6 @@ esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client, const char *u free(client->config->scheme); free(client->config->host); free(client->config->path); - client->config->scheme = create_string(uri + puri.field_data[UF_SCHEMA].off, puri.field_data[UF_SCHEMA].len); client->config->host = create_string(uri + puri.field_data[UF_HOST].off, puri.field_data[UF_HOST].len); client->config->path = NULL; @@ -990,23 +1098,24 @@ esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client, const char *u if (puri.field_data[UF_PATH].len || puri.field_data[UF_QUERY].len) { int asprintf_ret_value; + if (puri.field_data[UF_QUERY].len == 0) { asprintf_ret_value = asprintf(&client->config->path, - "%.*s", - puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off); + "%.*s", + puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off); } else if (puri.field_data[UF_PATH].len == 0) { asprintf_ret_value = asprintf(&client->config->path, - "/?%.*s", - puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off); + "/?%.*s", + puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off); } else { asprintf_ret_value = asprintf(&client->config->path, - "%.*s?%.*s", - puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off, - puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off); + "%.*s?%.*s", + puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off, + puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off); } if (asprintf_ret_value == -1) { - ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, "Memory exhausted"); + ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, "Memory exhausted"); MQTT_API_UNLOCK(client); return ESP_ERR_NO_MEM; } @@ -1017,15 +1126,17 @@ esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client, const char *u } char *user_info = create_string(uri + puri.field_data[UF_USERINFO].off, puri.field_data[UF_USERINFO].len); + if (user_info) { char *pass = strchr(user_info, ':'); + if (pass) { pass[0] = 0; //terminal username pass ++; client->mqtt_state.connection.information.password = strdup(pass); } - client->mqtt_state.connection.information.username = strdup(user_info); + client->mqtt_state.connection.information.username = strdup(user_info); free(user_info); } @@ -1033,7 +1144,6 @@ esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client, const char *u return ESP_OK; } - static esp_err_t esp_mqtt_dispatch_event_with_msgid(esp_mqtt_client_handle_t client) { if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { @@ -1043,16 +1153,20 @@ static esp_err_t esp_mqtt_dispatch_event_with_msgid(esp_mqtt_client_handle_t cli } else { client->event.msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); } + return esp_mqtt_dispatch_event(client); } esp_err_t esp_mqtt_dispatch_custom_event(esp_mqtt_client_handle_t client, esp_mqtt_event_t *event) { - esp_err_t ret = esp_event_post_to(client->config->event_loop_handle, MQTT_EVENTS, MQTT_USER_EVENT, event, sizeof(*event), 0); + esp_err_t ret = esp_event_post_to(client->config->event_loop_handle, MQTT_EVENTS, MQTT_USER_EVENT, event, + sizeof(*event), 0); #if MQTT_EVENT_QUEUE_SIZE > 1 + if (ret == ESP_OK) { atomic_fetch_add(&client->queued_events, 1); } + #endif return ret; } @@ -1062,19 +1176,21 @@ static esp_err_t esp_mqtt_dispatch_event(esp_mqtt_client_handle_t client) client->event.client = client; client->event.protocol_ver = client->mqtt_state.connection.information.protocol_ver; esp_err_t ret = ESP_FAIL; - #ifdef MQTT_SUPPORTED_FEATURE_EVENT_LOOP - esp_event_post_to(client->config->event_loop_handle, MQTT_EVENTS, client->event.event_id, &client->event, sizeof(client->event), portMAX_DELAY); + esp_event_post_to(client->config->event_loop_handle, MQTT_EVENTS, client->event.event_id, &client->event, + sizeof(client->event), portMAX_DELAY); ret = esp_event_loop_run(client->config->event_loop_handle, 0); #else return ESP_FAIL; #endif + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 esp_mqtt5_client_delete_user_property(client->event.property->user_property); client->event.property->user_property = NULL; #endif } + return ret; } @@ -1093,21 +1209,26 @@ static esp_err_t deliver_publish(esp_mqtt_client_handle_t client) if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 - if (esp_mqtt5_get_publish_data(client, msg_buf, msg_read_len, &msg_topic, &msg_topic_len, &msg_data, &msg_data_len) != ESP_OK) { + + if (esp_mqtt5_get_publish_data(client, msg_buf, msg_read_len, &msg_topic, &msg_topic_len, &msg_data, + &msg_data_len) != ESP_OK) { ESP_LOGE(TAG, "%s: esp_mqtt5_get_publish_data() failed", __func__); return ESP_FAIL; } + #endif } else { msg_topic = mqtt_get_publish_topic(msg_buf, &msg_topic_len); + if (msg_topic == NULL) { ESP_LOGE(TAG, "%s: mqtt_get_publish_topic() failed", __func__); return ESP_FAIL; } - ESP_LOGD(TAG, "%s: msg_topic_len=%"NEWLIB_NANO_COMPAT_FORMAT, __func__, NEWLIB_NANO_COMPAT_CAST(msg_topic_len)); + ESP_LOGD(TAG, "%s: msg_topic_len=%"NEWLIB_NANO_COMPAT_FORMAT, __func__, NEWLIB_NANO_COMPAT_CAST(msg_topic_len)); // get payload msg_data = mqtt_get_publish_data(msg_buf, &msg_data_len); + if (msg_data_len > 0 && msg_data == NULL) { ESP_LOGE(TAG, "%s: mqtt_get_publish_data() failed", __func__); return ESP_FAIL; @@ -1115,6 +1236,7 @@ static esp_err_t deliver_publish(esp_mqtt_client_handle_t client) } client->event.retain = mqtt_get_retain(msg_buf); + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 client->event.msg_id = mqtt5_get_id(msg_buf, msg_read_len); @@ -1122,13 +1244,15 @@ static esp_err_t deliver_publish(esp_mqtt_client_handle_t client) } else { client->event.msg_id = mqtt_get_id(msg_buf, msg_read_len); } + client->event.qos = mqtt_get_qos(msg_buf); client->event.dup = mqtt_get_dup(msg_buf); client->event.total_data_len = msg_data_len + msg_total_len - msg_read_len; - bool send_event = true; + while (send_event) { - ESP_LOGD(TAG, "Get data len= %"NEWLIB_NANO_COMPAT_FORMAT", topic len=%"NEWLIB_NANO_COMPAT_FORMAT", total_data: %d offset: %"NEWLIB_NANO_COMPAT_FORMAT, + ESP_LOGD(TAG, + "Get data len= %"NEWLIB_NANO_COMPAT_FORMAT", topic len=%"NEWLIB_NANO_COMPAT_FORMAT", total_data: %d offset: %"NEWLIB_NANO_COMPAT_FORMAT, NEWLIB_NANO_COMPAT_CAST(msg_data_len), NEWLIB_NANO_COMPAT_CAST(msg_topic_len), client->event.total_data_len, NEWLIB_NANO_COMPAT_CAST(msg_data_offset)); client->event.event_id = MQTT_EVENT_DATA; @@ -1142,15 +1266,16 @@ static esp_err_t deliver_publish(esp_mqtt_client_handle_t client) if (msg_read_len < msg_total_len) { send_event = true; - #ifdef CONFIG_MQTT_TOPIC_PRESENT_ALL_DATA_EVENTS +#ifdef CONFIG_MQTT_TOPIC_PRESENT_ALL_DATA_EVENTS + if (!saved_msg_topic) { saved_msg_topic = strndup(msg_topic, msg_topic_len); ESP_MEM_CHECK(TAG, saved_msg_topic, return ESP_ERR_NO_MEM); saved_msg_topic_len = msg_topic_len; } - #endif - size_t buf_len = client->mqtt_state.in_buffer_length; +#endif + size_t buf_len = client->mqtt_state.in_buffer_length; msg_data = (char *)client->mqtt_state.in_buffer; msg_topic = saved_msg_topic; msg_topic_len = saved_msg_topic_len; @@ -1158,6 +1283,7 @@ static esp_err_t deliver_publish(esp_mqtt_client_handle_t client) int ret = esp_transport_read(client->transport, (char *)client->mqtt_state.in_buffer, msg_total_len - msg_read_len > buf_len ? buf_len : msg_total_len - msg_read_len, client->config->network_timeout_ms); + if (ret <= 0) { return esp_mqtt_handle_transport_read_error(ret, client, false) == 0 ? ESP_OK : ESP_FAIL; } @@ -1166,6 +1292,7 @@ static esp_err_t deliver_publish(esp_mqtt_client_handle_t client) msg_read_len += msg_data_len; } } + free(saved_msg_topic); return ESP_OK; } @@ -1186,15 +1313,18 @@ static esp_err_t deliver_suback(esp_mqtt_client_handle_t client) } else { msg_data = mqtt_get_suback_data(msg_buf, &msg_data_len); } + if (msg_data_len <= 0) { ESP_LOGE(TAG, "Failed to acquire suback data"); return ESP_FAIL; } + client->event.error_handle->esp_tls_stack_err = 0; client->event.error_handle->esp_tls_last_esp_err = 0; client->event.error_handle->esp_tls_cert_verify_flags = 0; client->event.error_handle->error_type = MQTT_ERROR_TYPE_NONE; client->event.error_handle->connect_return_code = MQTT_CONNECTION_ACCEPTED; + // post data event for (int topic = 0; topic < msg_data_len; ++topic) { if ((uint8_t)msg_data[topic] >= 0x80) { @@ -1202,13 +1332,13 @@ static esp_err_t deliver_suback(esp_mqtt_client_handle_t client) break; } } + client->event.data_len = msg_data_len; client->event.total_data_len = msg_data_len; client->event.event_id = MQTT_EVENT_SUBSCRIBED; client->event.data = msg_data; client->event.current_data_offset = 0; esp_mqtt_dispatch_event_with_msgid(client); - return ESP_OK; } @@ -1241,11 +1371,10 @@ static outbox_item_handle_t mqtt_enqueue(esp_mqtt_client_handle_t client, uint8_ return outbox_enqueue(client->outbox, &msg, platform_tick_get_ms()); } - /* * Returns: * -2 in case of failure or EOF (clean connection closure) - * -1 timeout while in-the-middle of the messge + * -1 timeout while in-the-middle of the message * 0 if no message has been received * 1 if a message has been received and placed to client->mqtt_state: * message length: client->mqtt_state.message_length @@ -1257,18 +1386,21 @@ static int mqtt_message_receive(esp_mqtt_client_handle_t client, int read_poll_t int read_len, total_len, fixed_header_len; uint8_t *buf = client->mqtt_state.in_buffer + client->mqtt_state.in_buffer_read_len; esp_transport_handle_t t = client->transport; - client->mqtt_state.message_length = 0; + if (client->mqtt_state.in_buffer_read_len == 0) { /* * Read first byte of the mqtt packet fixed header, it contains packet * type and flags. */ read_len = esp_transport_read(t, (char *)buf, 1, read_poll_timeout_ms); + if (read_len <= 0) { return esp_mqtt_handle_transport_read_error(read_len, client, false); } + ESP_LOGD(TAG, "%s: first byte: 0x%x", __func__, *buf); + /* * Verify the flags and act according to MQTT protocol: close connection * if the flags are set incorrectly. @@ -1277,9 +1409,11 @@ static int mqtt_message_receive(esp_mqtt_client_handle_t client, int read_poll_t ESP_LOGE(TAG, "%s: received a message with an invalid header=0x%x", __func__, *buf); goto err; } + buf++; client->mqtt_state.in_buffer_read_len++; } + if ((client->mqtt_state.in_buffer_read_len == 1) || ((client->mqtt_state.in_buffer_read_len < 6) && (*(buf - 1) & 0x80))) { do { @@ -1291,17 +1425,23 @@ static int mqtt_message_receive(esp_mqtt_client_handle_t client, int read_poll_t * size of 16386 bytes). */ read_len = esp_transport_read(t, (char *)buf, 1, read_poll_timeout_ms); + if (read_len <= 0) { return esp_mqtt_handle_transport_read_error(read_len, client, true); } + ESP_LOGD(TAG, "%s: read \"remaining length\" byte: 0x%x", __func__, *buf); buf++; client->mqtt_state.in_buffer_read_len++; } while ((client->mqtt_state.in_buffer_read_len < 6) && (*(buf - 1) & 0x80)); } - total_len = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len, &fixed_header_len); - ESP_LOGD(TAG, "%s: total message length: %d (already read: %"NEWLIB_NANO_COMPAT_FORMAT")", __func__, total_len, NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.in_buffer_read_len)); + + total_len = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len, + &fixed_header_len); + ESP_LOGD(TAG, "%s: total message length: %d (already read: %"NEWLIB_NANO_COMPAT_FORMAT")", __func__, total_len, + NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.in_buffer_read_len)); client->mqtt_state.message_length = total_len; + if (client->mqtt_state.in_buffer_length < total_len) { if (mqtt_get_type(client->mqtt_state.in_buffer) == MQTT_MSG_TYPE_PUBLISH) { /* @@ -1310,51 +1450,66 @@ static int mqtt_message_receive(esp_mqtt_client_handle_t client, int read_poll_t */ if (client->mqtt_state.in_buffer_read_len < fixed_header_len + 2) { /* read next 2 bytes - topic length to get minimum portion of publish packet */ - read_len = esp_transport_read(t, (char *)buf, client->mqtt_state.in_buffer_read_len - fixed_header_len + 2, read_poll_timeout_ms); + read_len = esp_transport_read(t, (char *)buf, client->mqtt_state.in_buffer_read_len - fixed_header_len + 2, + read_poll_timeout_ms); ESP_LOGD(TAG, "%s: read_len=%d", __func__, read_len); + if (read_len <= 0) { return esp_mqtt_handle_transport_read_error(read_len, client, true); } + client->mqtt_state.in_buffer_read_len += read_len; buf += read_len; + if (client->mqtt_state.in_buffer_read_len < fixed_header_len + 2) { - ESP_LOGD(TAG, "%s: transport_read(): message reading left in progress :: total message length: %d (already read: %"NEWLIB_NANO_COMPAT_FORMAT")", + ESP_LOGD(TAG, + "%s: transport_read(): message reading left in progress :: total message length: %d (already read: %"NEWLIB_NANO_COMPAT_FORMAT")", __func__, total_len, NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.in_buffer_read_len)); return 0; } } + int topic_len = client->mqtt_state.in_buffer[fixed_header_len] << 8; topic_len |= client->mqtt_state.in_buffer[fixed_header_len + 1]; total_len = fixed_header_len + topic_len + (mqtt_get_qos(client->mqtt_state.in_buffer) > 0 ? 2 : 0); ESP_LOGD(TAG, "%s: total len modified to %d as message longer than input buffer", __func__, total_len); + if (client->mqtt_state.in_buffer_length < total_len) { ESP_LOGE(TAG, "%s: message is too big, insufficient buffer size", __func__); goto err; } else { total_len = client->mqtt_state.in_buffer_length; } + /* free to continue with reading */ } else { ESP_LOGE(TAG, "%s: message is too big, insufficient buffer size", __func__); goto err; } } + if (client->mqtt_state.in_buffer_read_len < total_len) { /* read the rest of the mqtt message */ read_len = esp_transport_read(t, (char *)buf, total_len - client->mqtt_state.in_buffer_read_len, read_poll_timeout_ms); ESP_LOGD(TAG, "%s: read_len=%d", __func__, read_len); + if (read_len <= 0) { return esp_mqtt_handle_transport_read_error(read_len, client, true); } + client->mqtt_state.in_buffer_read_len += read_len; + if (client->mqtt_state.in_buffer_read_len < total_len) { - ESP_LOGD(TAG, "%s: transport_read(): message reading left in progress :: total message length: %d (already read: %"NEWLIB_NANO_COMPAT_FORMAT")", + ESP_LOGD(TAG, + "%s: transport_read(): message reading left in progress :: total message length: %d (already read: %"NEWLIB_NANO_COMPAT_FORMAT")", __func__, total_len, NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.in_buffer_read_len)); return 0; } } + ESP_LOGV(TAG, "%s: transport_read():%"NEWLIB_NANO_COMPAT_FORMAT" %"NEWLIB_NANO_COMPAT_FORMAT, __func__, - NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.in_buffer_read_len), NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.message_length)); + NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.in_buffer_read_len), + NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.message_length)); return 1; err: esp_mqtt_client_dispatch_transport_error(client); @@ -1366,29 +1521,33 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) uint8_t msg_type = 0, msg_qos = 0; uint16_t msg_id = 0; size_t previous_in_buffer_read_len = client->mqtt_state.in_buffer_read_len; - /* non-blocking receive in order not to block other tasks */ int recv = mqtt_message_receive(client, 0); + if (recv == 0) { // Timeout return ESP_OK; } + if (recv == -1) { // Mid-message timeout if (previous_in_buffer_read_len == client->mqtt_state.in_buffer_read_len) { // Report error only if didn't receive anything since previous iteration ESP_LOGE(TAG, "%s: Network timeout while reading MQTT message", __func__); return ESP_FAIL; } + return ESP_OK; // Treat as standard timeout (keep reading the message) } + if (recv < 0) { // Other error ESP_LOGE(TAG, "%s: mqtt_message_receive() returned %d", __func__, recv); return ESP_FAIL; } - int read_len = client->mqtt_state.message_length; + int read_len = client->mqtt_state.message_length; // If the message was valid, get the type, quality of service and id of the message msg_type = mqtt_get_type(client->mqtt_state.in_buffer); msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 msg_id = mqtt5_get_id(client->mqtt_state.in_buffer, read_len); @@ -1405,14 +1564,19 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) #ifdef MQTT_PROTOCOL_5 esp_mqtt5_parse_suback(client); #endif - ESP_LOGD(TAG, "deliver_suback, message_length_read=%"NEWLIB_NANO_COMPAT_FORMAT", message_length=%"NEWLIB_NANO_COMPAT_FORMAT, - NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.in_buffer_read_len), NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.message_length)); + ESP_LOGD(TAG, + "deliver_suback, message_length_read=%"NEWLIB_NANO_COMPAT_FORMAT", message_length=%"NEWLIB_NANO_COMPAT_FORMAT, + NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.in_buffer_read_len), + NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.message_length)); + if (deliver_suback(client) != ESP_OK) { ESP_LOGE(TAG, "Failed to deliver suback message id=%d", msg_id); return ESP_FAIL; } } + break; + case MQTT_MSG_TYPE_UNSUBACK: if (remove_initiator_message(client, MQTT_MSG_TYPE_UNSUBSCRIBE, msg_id)) { #ifdef MQTT_PROTOCOL_5 @@ -1422,13 +1586,20 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) client->event.event_id = MQTT_EVENT_UNSUBSCRIBED; esp_mqtt_dispatch_event_with_msgid(client); } + break; + case MQTT_MSG_TYPE_PUBLISH: - ESP_LOGD(TAG, "deliver_publish, message_length_read=%"NEWLIB_NANO_COMPAT_FORMAT", message_length=%"NEWLIB_NANO_COMPAT_FORMAT, NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.in_buffer_read_len), NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.message_length)); + ESP_LOGD(TAG, + "deliver_publish, message_length_read=%"NEWLIB_NANO_COMPAT_FORMAT", message_length=%"NEWLIB_NANO_COMPAT_FORMAT, + NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.in_buffer_read_len), + NEWLIB_NANO_COMPAT_CAST(client->mqtt_state.message_length)); + if (deliver_publish(client) != ESP_OK) { ESP_LOGE(TAG, "Failed to deliver publish message id=%d", msg_id); return ESP_FAIL; } + if (msg_qos == 1 || msg_qos == 2) { if (msg_qos == 1) { if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { @@ -1447,6 +1618,7 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) mqtt_msg_pubrec(&client->mqtt_state.connection, msg_id); } } + if (client->mqtt_state.connection.outbound_message.length == 0) { ESP_LOGE(TAG, "Publish response message PUBACK or PUBREC cannot be created"); return ESP_FAIL; @@ -1455,17 +1627,21 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) ESP_LOGD(TAG, "Queue response QoS: %d", msg_qos); if (esp_mqtt_write(client) != ESP_OK) { - ESP_LOGE(TAG, "Error write qos msg repsonse, qos = %d", msg_qos); + ESP_LOGE(TAG, "Error write qos msg response, qos = %d", msg_qos); return ESP_FAIL; } } + break; + case MQTT_MSG_TYPE_PUBACK: #ifdef MQTT_PROTOCOL_5 if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { esp_mqtt5_decrement_packet_counter(client); } + #endif + if (remove_initiator_message(client, MQTT_MSG_TYPE_PUBLISH, msg_id)) { ESP_LOGD(TAG, "received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish"); #ifdef MQTT_PROTOCOL_5 @@ -1474,17 +1650,22 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) client->event.event_id = MQTT_EVENT_PUBLISHED; esp_mqtt_dispatch_event_with_msgid(client); } + break; + case MQTT_MSG_TYPE_PUBREC: ESP_LOGD(TAG, "received MQTT_MSG_TYPE_PUBREC"); + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 - ESP_LOGI(TAG, "MQTT_MSG_TYPE_PUBREC return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len)); + ESP_LOGI(TAG, "MQTT_MSG_TYPE_PUBREC return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, + client->mqtt_state.in_buffer_read_len)); mqtt5_msg_pubrel(&client->mqtt_state.connection, msg_id); #endif } else { mqtt_msg_pubrel(&client->mqtt_state.connection, msg_id); } + if (client->mqtt_state.connection.outbound_message.length == 0) { ESP_LOGE(TAG, "Publish response message PUBREL cannot be created"); return ESP_FAIL; @@ -1493,16 +1674,20 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) outbox_set_pending(client->outbox, msg_id, ACKNOWLEDGED); esp_mqtt_write(client); break; + case MQTT_MSG_TYPE_PUBREL: ESP_LOGD(TAG, "received MQTT_MSG_TYPE_PUBREL"); + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 - ESP_LOGI(TAG, "MQTT_MSG_TYPE_PUBREL return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len)); + ESP_LOGI(TAG, "MQTT_MSG_TYPE_PUBREL return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, + client->mqtt_state.in_buffer_read_len)); mqtt5_msg_pubcomp(&client->mqtt_state.connection, msg_id); #endif } else { mqtt_msg_pubcomp(&client->mqtt_state.connection, msg_id); } + if (client->mqtt_state.connection.outbound_message.length == 0) { ESP_LOGE(TAG, "Publish response message PUBCOMP cannot be created"); return ESP_FAIL; @@ -1510,13 +1695,17 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) esp_mqtt_write(client); break; + case MQTT_MSG_TYPE_PUBCOMP: ESP_LOGD(TAG, "received MQTT_MSG_TYPE_PUBCOMP"); #ifdef MQTT_PROTOCOL_5 + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { esp_mqtt5_decrement_packet_counter(client); } + #endif + if (remove_initiator_message(client, MQTT_MSG_TYPE_PUBLISH, msg_id)) { ESP_LOGD(TAG, "Receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish"); #ifdef MQTT_PROTOCOL_5 @@ -1525,7 +1714,9 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) client->event.event_id = MQTT_EVENT_PUBLISHED; esp_mqtt_dispatch_event_with_msgid(client); } + break; + case MQTT_MSG_TYPE_PINGRESP: ESP_LOGD(TAG, "MQTT_MSG_TYPE_PINGRESP"); client->wait_for_ping_resp = false; @@ -1536,9 +1727,11 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) */ client->keepalive_tick = platform_tick_get_ms(); break; + case MQTT_MSG_TYPE_DISCONNECT: ESP_LOGD(TAG, "MQTT_MSG_TYPE_DISCONNECT"); #ifdef MQTT_PROTOCOL_5 + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { int disconnect_rsp_code; esp_mqtt5_parse_disconnect(client, &disconnect_rsp_code); @@ -1546,6 +1739,7 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) client->event.error_handle->disconnect_return_code = disconnect_rsp_code; esp_mqtt_dispatch_event_with_msgid(client); } + #endif break; } @@ -1557,12 +1751,16 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client) static esp_err_t mqtt_resend_queued(esp_mqtt_client_handle_t client, outbox_item_handle_t item) { // decode queued data - client->mqtt_state.connection.outbound_message.data = outbox_item_get_data(item, &client->mqtt_state.connection.outbound_message.length, &client->mqtt_state.pending_msg_id, - &client->mqtt_state.pending_msg_type, &client->mqtt_state.pending_publish_qos); + client->mqtt_state.connection.outbound_message.data = outbox_item_get_data(item, + &client->mqtt_state.connection.outbound_message.length, &client->mqtt_state.pending_msg_id, + &client->mqtt_state.pending_msg_type, &client->mqtt_state.pending_publish_qos); + // set duplicate flag for QoS-1 and QoS-2 messages - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_publish_qos > 0 && (outbox_item_get_pending(item) == TRANSMITTED)) { + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_publish_qos > 0 && + (outbox_item_get_pending(item) == TRANSMITTED)) { mqtt_set_dup(client->mqtt_state.connection.outbound_message.data); - ESP_LOGD(TAG, "Sending Duplicated QoS%d message with id=%d", client->mqtt_state.pending_publish_qos, client->mqtt_state.pending_msg_id); + ESP_LOGD(TAG, "Sending Duplicated QoS%d message with id=%d", client->mqtt_state.pending_publish_qos, + client->mqtt_state.pending_msg_id); } // try to resend the data @@ -1577,20 +1775,24 @@ static esp_err_t mqtt_resend_queued(esp_mqtt_client_handle_t client, outbox_item static esp_err_t mqtt_resend_pubrel(esp_mqtt_client_handle_t client, outbox_item_handle_t item) { - client->mqtt_state.connection.outbound_message.data = outbox_item_get_data(item, &client->mqtt_state.connection.outbound_message.length, &client->mqtt_state.pending_msg_id, - &client->mqtt_state.pending_msg_type, &client->mqtt_state.pending_publish_qos); - if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { + client->mqtt_state.connection.outbound_message.data = outbox_item_get_data(item, + &client->mqtt_state.connection.outbound_message.length, &client->mqtt_state.pending_msg_id, + &client->mqtt_state.pending_msg_type, &client->mqtt_state.pending_publish_qos); + + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 - ESP_LOGI(TAG, "MQTT_MSG_TYPE_PUBREC return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len)); - mqtt5_msg_pubrel(&client->mqtt_state.connection, client->mqtt_state.pending_msg_id); + ESP_LOGI(TAG, "MQTT_MSG_TYPE_PUBREC return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, + client->mqtt_state.in_buffer_read_len)); + mqtt5_msg_pubrel(&client->mqtt_state.connection, client->mqtt_state.pending_msg_id); #endif - } else { - mqtt_msg_pubrel(&client->mqtt_state.connection, client->mqtt_state.pending_msg_id); - } - if (client->mqtt_state.connection.outbound_message.length == 0) { - ESP_LOGE(TAG, "Publish response message PUBREL cannot be created"); - return ESP_FAIL; - } + } else { + mqtt_msg_pubrel(&client->mqtt_state.connection, client->mqtt_state.pending_msg_id); + } + + if (client->mqtt_state.connection.outbound_message.length == 0) { + ESP_LOGE(TAG, "Publish response message PUBREL cannot be created"); + return ESP_FAIL; + } if (esp_mqtt_write(client) != ESP_OK) { ESP_LOGE(TAG, "Error to resend data "); @@ -1607,20 +1809,24 @@ static void mqtt_delete_expired_messages(esp_mqtt_client_handle_t client) #if MQTT_REPORT_DELETED_MESSAGES // also report the deleted items as MQTT_EVENT_DELETED events if enabled int msg_id = 0; - while ((msg_id = outbox_delete_single_expired(client->outbox, platform_tick_get_ms(), OUTBOX_EXPIRED_TIMEOUT_MS)) >= 0) { + + while ((msg_id = outbox_delete_single_expired(client->outbox, platform_tick_get_ms(), + OUTBOX_EXPIRED_TIMEOUT_MS)) >= 0) { client->event.event_id = MQTT_EVENT_DELETED; client->event.msg_id = msg_id; + if (esp_mqtt_dispatch_event(client) != ESP_OK) { ESP_LOGE(TAG, "Failed to post event on deleting message id=%d", msg_id); } } + #else outbox_delete_expired(client->outbox, platform_tick_get_ms(), OUTBOX_EXPIRED_TIMEOUT_MS); #endif } /** - * @brief When using multiple queued item, we'd like to reduce the poll timeout to proceed with event loop exacution + * @brief When using multiple queued item, we'd like to reduce the poll timeout to proceed with event loop execution */ static inline int max_poll_timeout(esp_mqtt_client_handle_t client, int max_timeout) { @@ -1635,12 +1841,14 @@ static inline int max_poll_timeout(esp_mqtt_client_handle_t client, int max_time static inline void run_event_loop(esp_mqtt_client_handle_t client) { #if MQTT_EVENT_QUEUE_SIZE > 1 + if (atomic_load(&client->queued_events) > 0) { atomic_fetch_sub(&client->queued_events, 1); #else { #endif esp_err_t ret = esp_event_loop_run(client->config->event_loop_handle, 0); + if (ret != ESP_OK) { ESP_LOGE(TAG, "Error in running event_loop %d", ret); } @@ -1653,29 +1861,31 @@ static void esp_mqtt_task(void *pv) uint64_t last_retransmit = 0; outbox_tick_t msg_tick = 0; client->run = true; - client->state = MQTT_STATE_INIT; xEventGroupClearBits(client->status_bits, STOPPED_BIT); + while (client->run) { MQTT_API_LOCK(client); run_event_loop(client); // delete long pending messages mqtt_delete_expired_messages(client); mqtt_client_state_t state = client->state; + switch (state) { case MQTT_STATE_DISCONNECTED: break; + case MQTT_STATE_INIT: xEventGroupClearBits(client->status_bits, RECONNECT_BIT | DISCONNECT_BIT); - client->transport = client->config->transport; - if (!client->transport) { + if (!client->transport) { if (esp_mqtt_client_create_transport(client) != ESP_OK) { ESP_LOGE(TAG, "Failed to create transport list"); client->run = false; break; } + //get transport by scheme client->transport = esp_transport_list_get_transport(client->transport_list, client->config->scheme); @@ -1685,6 +1895,7 @@ static void esp_mqtt_task(void *pv) break; } } + //default port if (client->config->port == 0) { client->config->port = esp_transport_get_default_port(client->transport); @@ -1694,7 +1905,7 @@ static void esp_mqtt_task(void *pv) esp_mqtt_set_ssl_transport_properties(client->transport_list, client->config); #endif - if(client->config->tcp_keep_alive_cfg.keep_alive_enable) { + if (client->config->tcp_keep_alive_cfg.keep_alive_enable) { esp_transport_tcp_set_keep_alive(client->transport, &client->config->tcp_keep_alive_cfg); } @@ -1710,29 +1921,36 @@ static void esp_mqtt_task(void *pv) esp_mqtt_abort_connection(client); break; } + ESP_LOGD(TAG, "Transport connected to %s://%s:%d", client->config->scheme, client->config->host, client->config->port); + if (esp_mqtt_connect(client, client->config->network_timeout_ms) != ESP_OK) { ESP_LOGE(TAG, "MQTT connect failed"); esp_mqtt_abort_connection(client); break; } + client->event.event_id = MQTT_EVENT_CONNECTED; + if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) { client->event.session_present = mqtt_get_connect_session_present(client->mqtt_state.in_buffer); } + client->state = MQTT_STATE_CONNECTED; esp_mqtt_dispatch_event_with_msgid(client); client->refresh_connection_tick = platform_tick_get_ms(); client->keepalive_tick = platform_tick_get_ms(); - break; + case MQTT_STATE_CONNECTED: + // check for disconnection request if (xEventGroupWaitBits(client->status_bits, DISCONNECT_BIT, true, true, 0) & DISCONNECT_BIT) { send_disconnect_msg(client); // ignore error, if clean disconnect fails, just abort the connection esp_mqtt_abort_connection(client); break; } + // receive and process data if (mqtt_process_receive(client) == ESP_FAIL) { esp_mqtt_abort_connection(client); @@ -1744,9 +1962,9 @@ static void esp_mqtt_task(void *pv) last_retransmit = platform_tick_get_ms(); } - // resend all non-transmitted messages first outbox_item_handle_t item = outbox_dequeue(client->outbox, QUEUED, NULL); + if (item) { if (mqtt_resend_queued(client, item) == ESP_OK) { if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_publish_qos == 0) { @@ -1755,35 +1973,46 @@ static void esp_mqtt_task(void *pv) ESP_LOGE(TAG, "Failed to remove queued qos0 message from the outbox"); } } + if (client->mqtt_state.pending_publish_qos > 0) { outbox_set_pending(client->outbox, client->mqtt_state.pending_msg_id, TRANSMITTED); #ifdef MQTT_PROTOCOL_5 + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { esp_mqtt5_increment_packet_counter(client); } + #endif } } + // resend other "transmitted" messages after 1s } else if (has_timed_out(last_retransmit, client->config->message_retransmit_timeout)) { last_retransmit = platform_tick_get_ms(); item = outbox_dequeue(client->outbox, TRANSMITTED, &msg_tick); + if (item && (last_retransmit - msg_tick > client->config->message_retransmit_timeout)) { if (mqtt_resend_queued(client, item) == ESP_OK) { #ifdef MQTT_PROTOCOL_5 + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { esp_mqtt5_increment_packet_counter(client); } + #endif } } + item = outbox_dequeue(client->outbox, ACKNOWLEDGED, &msg_tick); + if (item && (last_retransmit - msg_tick > client->config->message_retransmit_timeout)) { if (mqtt_resend_pubrel(client, item) == ESP_OK) { #ifdef MQTT_PROTOCOL_5 + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { esp_mqtt5_increment_packet_counter(client); } + #endif } } @@ -1801,8 +2030,8 @@ static void esp_mqtt_task(void *pv) } break; - case MQTT_STATE_WAIT_RECONNECT: + case MQTT_STATE_WAIT_RECONNECT: if (!client->config->auto_reconnect && xEventGroupGetBits(client->status_bits)&RECONNECT_BIT) { xEventGroupClearBits(client->status_bits, RECONNECT_BIT); client->state = MQTT_STATE_INIT; @@ -1816,29 +2045,32 @@ static void esp_mqtt_task(void *pv) ESP_LOGD(TAG, "Reconnecting..."); break; } + MQTT_API_UNLOCK(client); xEventGroupWaitBits(client->status_bits, RECONNECT_BIT, false, true, max_poll_timeout(client, client->wait_timeout_ms / 2 / portTICK_PERIOD_MS)); // continue the while loop instead of break, as the mutex is unlocked continue; + default: ESP_LOGE(TAG, "MQTT client error, client is in an unrecoverable state."); break; } + MQTT_API_UNLOCK(client); + if (MQTT_STATE_CONNECTED == client->state) { if (esp_transport_poll_read(client->transport, max_poll_timeout(client, MQTT_POLL_READ_TIMEOUT_MS)) < 0) { ESP_LOGE(TAG, "Poll read error: %d, aborting connection", errno); esp_mqtt_abort_connection(client); } } - } + esp_transport_close(client->transport); outbox_delete_all_items(client->outbox); client->state = MQTT_STATE_DISCONNECTED; xEventGroupSetBits(client->status_bits, STOPPED_BIT); - vTaskDelete(NULL); } @@ -1848,25 +2080,34 @@ esp_err_t esp_mqtt_client_start(esp_mqtt_client_handle_t client) ESP_LOGE(TAG, "Client was not initialized"); return ESP_ERR_INVALID_ARG; } + MQTT_API_LOCK(client); + if (client->state != MQTT_STATE_INIT && client->state != MQTT_STATE_DISCONNECTED) { ESP_LOGE(TAG, "Client has started"); MQTT_API_UNLOCK(client); return ESP_FAIL; } + esp_err_t err = ESP_OK; #if MQTT_CORE_SELECTION_ENABLED ESP_LOGD(TAG, "Core selection enabled on %u", MQTT_TASK_CORE); - if (xTaskCreatePinnedToCore(esp_mqtt_task, "mqtt_task", client->config->task_stack, client, client->config->task_prio, &client->task_handle, MQTT_TASK_CORE) != pdTRUE) { + + if (xTaskCreatePinnedToCore(esp_mqtt_task, "mqtt_task", client->config->task_stack, client, client->config->task_prio, + &client->task_handle, MQTT_TASK_CORE) != pdTRUE) { ESP_LOGE(TAG, "Error create mqtt task"); err = ESP_FAIL; } + #else ESP_LOGD(TAG, "Core selection disabled"); - if (xTaskCreate(esp_mqtt_task, "mqtt_task", client->config->task_stack, client, client->config->task_prio, &client->task_handle) != pdTRUE) { + + if (xTaskCreate(esp_mqtt_task, "mqtt_task", client->config->task_stack, client, client->config->task_prio, + &client->task_handle) != pdTRUE) { ESP_LOGE(TAG, "Error create mqtt task"); err = ESP_FAIL; } + #endif MQTT_API_UNLOCK(client); return err; @@ -1878,6 +2119,7 @@ esp_err_t esp_mqtt_client_disconnect(esp_mqtt_client_handle_t client) ESP_LOGE(TAG, "Client was not initialized"); return ESP_ERR_INVALID_ARG; } + ESP_LOGI(TAG, "Client asked to disconnect"); xEventGroupSetBits(client->status_bits, DISCONNECT_BIT); return ESP_OK; @@ -1889,11 +2131,14 @@ esp_err_t esp_mqtt_client_reconnect(esp_mqtt_client_handle_t client) ESP_LOGE(TAG, "Client was not initialized"); return ESP_ERR_INVALID_ARG; } + ESP_LOGI(TAG, "Client force reconnect requested"); + if (client->state != MQTT_STATE_WAIT_RECONNECT) { ESP_LOGD(TAG, "The client is not waiting for reconnection. Ignore the request"); return ESP_FAIL; } + client->wait_timeout_ms = 0; xEventGroupSetBits(client->status_bits, RECONNECT_BIT); return ESP_OK; @@ -1905,22 +2150,27 @@ static esp_err_t send_disconnect_msg(esp_mqtt_client_handle_t client) if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 mqtt5_msg_disconnect(&client->mqtt_state.connection, &client->mqtt5_config->disconnect_property_info); + if (client->mqtt_state.connection.outbound_message.length) { esp_mqtt5_client_delete_user_property(client->mqtt5_config->disconnect_property_info.user_property); client->mqtt5_config->disconnect_property_info.user_property = NULL; memset(&client->mqtt5_config->disconnect_property_info, 0, sizeof(esp_mqtt5_disconnect_property_config_t)); } + #endif } else { mqtt_msg_disconnect(&client->mqtt_state.connection); } + if (client->mqtt_state.connection.outbound_message.length == 0) { ESP_LOGE(TAG, "Disconnect message cannot be created"); return ESP_FAIL; } + if (esp_mqtt_write(client) != ESP_OK) { ESP_LOGE(TAG, "Error sending disconnect message"); } + return ESP_OK; } @@ -1930,10 +2180,13 @@ esp_err_t esp_mqtt_client_stop(esp_mqtt_client_handle_t client) ESP_LOGE(TAG, "Client was not initialized"); return ESP_ERR_INVALID_ARG; } + MQTT_API_LOCK(client); + if (client->run) { /* A running client cannot be stopped from the MQTT task/event handler */ TaskHandle_t running_task = xTaskGetCurrentTaskHandle(); + if (running_task == client->task_handle) { MQTT_API_UNLOCK(client); ESP_LOGE(TAG, "Client cannot be stopped from MQTT task"); @@ -1960,6 +2213,7 @@ esp_err_t esp_mqtt_client_stop(esp_mqtt_client_handle_t client) static esp_err_t esp_mqtt_client_ping(esp_mqtt_client_handle_t client) { mqtt_msg_pingreq(&client->mqtt_state.connection); + if (client->mqtt_state.connection.outbound_message.length == 0) { ESP_LOGE(TAG, "Ping message cannot be created"); return ESP_FAIL; @@ -1969,6 +2223,7 @@ static esp_err_t esp_mqtt_client_ping(esp_mqtt_client_handle_t client) ESP_LOGE(TAG, "Error sending ping"); return ESP_FAIL; } + ESP_LOGD(TAG, "Sent PING successful"); return ESP_OK; } @@ -1991,31 +2246,38 @@ int esp_mqtt_client_subscribe_multiple(esp_mqtt_client_handle_t client, } MQTT_API_LOCK(client); + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 int max_qos = topic_list[0].qos; + for (int topic_number = 0; topic_number < size; ++topic_number) { if (topic_list[topic_number].qos > max_qos) { max_qos = topic_list[topic_number].qos; } } + if (esp_mqtt5_client_subscribe_check(client, max_qos) != ESP_OK) { ESP_LOGI(TAG, "MQTT5 subscribe check fail: QoS %d not accepted by broker ", max_qos); MQTT_API_UNLOCK(client); return -1; } + mqtt5_msg_subscribe(&client->mqtt_state.connection, topic_list, size, &client->mqtt_state.pending_msg_id, client->mqtt5_config->subscribe_property_info); + if (client->mqtt_state.connection.outbound_message.length) { client->mqtt5_config->subscribe_property_info = NULL; } + #endif } else { mqtt_msg_subscribe(&client->mqtt_state.connection, topic_list, size, &client->mqtt_state.pending_msg_id); } + if (client->mqtt_state.connection.outbound_message.length == 0) { ESP_LOGE(TAG, "Subscribe message cannot be created"); MQTT_API_UNLOCK(client); @@ -2023,11 +2285,13 @@ int esp_mqtt_client_subscribe_multiple(esp_mqtt_client_handle_t client, } client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.connection.outbound_message.data); + //move pending msg to outbox (if have) if (!mqtt_enqueue(client, NULL, 0)) { MQTT_API_UNLOCK(client); return -1; } + outbox_set_pending(client->outbox, client->mqtt_state.pending_msg_id, TRANSMITTED);// handle error if (esp_mqtt_write(client) != ESP_OK) { @@ -2037,11 +2301,9 @@ int esp_mqtt_client_subscribe_multiple(esp_mqtt_client_handle_t client, } ESP_LOGD(TAG, "Sent subscribe, first topic=%s, id: %d", topic_list[0].filter, client->mqtt_state.pending_msg_id); - int pending_msg_id = client->mqtt_state.pending_msg_id; MQTT_API_UNLOCK(client); return pending_msg_id; - } int esp_mqtt_client_subscribe_single(esp_mqtt_client_handle_t client, const char *topic, int qos) { @@ -2055,37 +2317,45 @@ int esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client, const char *top ESP_LOGE(TAG, "Client was not initialized"); return -1; } + if (client->state != MQTT_STATE_CONNECTED) { ESP_LOGE(TAG, "Client has not connected"); return -1; } + MQTT_API_LOCK(client); + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 mqtt5_msg_unsubscribe(&client->mqtt_state.connection, topic, &client->mqtt_state.pending_msg_id, client->mqtt5_config->unsubscribe_property_info); + if (client->mqtt_state.connection.outbound_message.length) { client->mqtt5_config->unsubscribe_property_info = NULL; } + #endif } else { mqtt_msg_unsubscribe(&client->mqtt_state.connection, topic, &client->mqtt_state.pending_msg_id); } + if (client->mqtt_state.connection.outbound_message.length == 0) { MQTT_API_UNLOCK(client); ESP_LOGE(TAG, "Unubscribe message cannot be created"); return -1; } - ESP_LOGD(TAG, "unsubscribe, topic\"%s\", id: %d", topic, client->mqtt_state.pending_msg_id); + ESP_LOGD(TAG, "unsubscribe, topic\"%s\", id: %d", topic, client->mqtt_state.pending_msg_id); client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.connection.outbound_message.data); + if (!mqtt_enqueue(client, NULL, 0)) { MQTT_API_UNLOCK(client); return -1; } + outbox_set_pending(client->outbox, client->mqtt_state.pending_msg_id, TRANSMITTED); //handle error if (esp_mqtt_write(client) != ESP_OK) { @@ -2095,7 +2365,6 @@ int esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client, const char *top } ESP_LOGD(TAG, "Sent Unsubscribe topic=%s, id: %d, successful", topic, client->mqtt_state.pending_msg_id); - int pending_msg_id = client->mqtt_state.pending_msg_id; MQTT_API_UNLOCK(client); return pending_msg_id; @@ -2105,15 +2374,19 @@ static int make_publish(esp_mqtt_client_handle_t client, const char *topic, cons int len, int qos, int retain) { uint16_t pending_msg_id = 0; + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { #ifdef MQTT_PROTOCOL_5 mqtt5_msg_publish(&client->mqtt_state.connection, topic, data, len, qos, retain, - &pending_msg_id, client->mqtt5_config->publish_property_info, client->mqtt5_config->server_resp_property_info.response_info); + &pending_msg_id, client->mqtt5_config->publish_property_info, + client->mqtt5_config->server_resp_property_info.response_info); + if (client->mqtt_state.connection.outbound_message.length) { client->mqtt5_config->publish_property_info = NULL; } + #endif } else { mqtt_msg_publish(&client->mqtt_state.connection, @@ -2126,52 +2399,64 @@ static int make_publish(esp_mqtt_client_handle_t client, const char *topic, cons ESP_LOGE(TAG, "Publish message cannot be created"); return -1; } + return pending_msg_id; } static inline int mqtt_client_enqueue_publish(esp_mqtt_client_handle_t client, const char *topic, const char *data, - int len, int qos, int retain, bool store) + int len, int qos, int retain, bool store) { int pending_msg_id = make_publish(client, topic, data, len, qos, retain); + if (pending_msg_id < 0) { return -1; } + /* We have to set as pending all the qos>0 messages */ //TODO: client->mqtt_state.outbound_message = publish_msg; if (qos > 0 || store) { client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.connection.outbound_message.data); client->mqtt_state.pending_msg_id = pending_msg_id; client->mqtt_state.pending_publish_qos = qos; + // by default store as QUEUED (not transmitted yet) only for messages which would fit outbound buffer if (client->mqtt_state.connection.outbound_message.fragmented_msg_total_length == 0) { if (!mqtt_enqueue(client, NULL, 0)) { return -1; } } else { - int first_fragment = client->mqtt_state.connection.outbound_message.length - client->mqtt_state.connection.outbound_message.fragmented_msg_data_offset; + int first_fragment = client->mqtt_state.connection.outbound_message.length - + client->mqtt_state.connection.outbound_message.fragmented_msg_data_offset; + if (!mqtt_enqueue(client, ((uint8_t *)data) + first_fragment, len - first_fragment)) { return -1; } + client->mqtt_state.connection.outbound_message.fragmented_msg_total_length = 0; } } + return pending_msg_id; } -int esp_mqtt_client_publish(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, int retain) +int esp_mqtt_client_publish(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, + int retain) { if (!client) { ESP_LOGE(TAG, "Client was not initialized"); return -1; } + #if MQTT_SKIP_PUBLISH_IF_DISCONNECTED + if (client->state != MQTT_STATE_CONNECTED) { ESP_LOGI(TAG, "Publishing skipped: client is not connected"); return -1; } -#endif -MQTT_API_LOCK(client); +#endif + MQTT_API_LOCK(client); #ifdef MQTT_PROTOCOL_5 + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { if (esp_mqtt5_client_publish_check(client, qos, retain) != ESP_OK) { ESP_LOGI(TAG, "MQTT5 publish check fail"); @@ -2179,6 +2464,7 @@ MQTT_API_LOCK(client); return -1; } } + #endif /* Acceptable publish messages: @@ -2198,15 +2484,18 @@ MQTT_API_LOCK(client); } int pending_msg_id = mqtt_client_enqueue_publish(client, topic, data, len, qos, retain, false); + if (pending_msg_id < 0) { MQTT_API_UNLOCK(client); return -1; } + int ret = 0; /* Skip sending if not connected (rely on resending) */ if (client->state != MQTT_STATE_CONNECTED) { ESP_LOGD(TAG, "Publish: client is not connected"); + if (qos > 0) { ret = pending_msg_id; } else { @@ -2222,14 +2511,14 @@ MQTT_API_LOCK(client); bool sending = true; while (sending) { - if (esp_mqtt_write(client) != ESP_OK) { esp_mqtt_abort_connection(client); ret = -1; goto cannot_publish; } - int data_sent = client->mqtt_state.connection.outbound_message.length - client->mqtt_state.connection.outbound_message.fragmented_msg_data_offset; + int data_sent = client->mqtt_state.connection.outbound_message.length - + client->mqtt_state.connection.outbound_message.fragmented_msg_data_offset; client->mqtt_state.connection.outbound_message.fragmented_msg_data_offset = 0; client->mqtt_state.connection.outbound_message.fragmented_msg_total_length = 0; remaining_len -= data_sent; @@ -2251,29 +2540,33 @@ MQTT_API_LOCK(client); if (qos > 0) { #ifdef MQTT_PROTOCOL_5 + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { esp_mqtt5_increment_packet_counter(client); } + #endif //Tick is set after transmit to avoid retransmitting too early due slow network speed / big messages outbox_set_tick(client->outbox, pending_msg_id, platform_tick_get_ms()); outbox_set_pending(client->outbox, pending_msg_id, TRANSMITTED); } + MQTT_API_UNLOCK(client); return pending_msg_id; - cannot_publish: // clear out possible fragmented publish if failed or skipped client->mqtt_state.connection.outbound_message.fragmented_msg_total_length = 0; + if (qos == 0) { ESP_LOGW(TAG, "Publish: Losing qos0 data when client not connected"); } - MQTT_API_UNLOCK(client); + MQTT_API_UNLOCK(client); return ret; } -int esp_mqtt_client_enqueue(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, int retain, bool store) +int esp_mqtt_client_enqueue(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, + int retain, bool store) { if (!client) { ESP_LOGE(TAG, "Client was not initialized"); @@ -2293,11 +2586,11 @@ int esp_mqtt_client_enqueue(esp_mqtt_client_handle_t client, const char *topic, if (len + outbox_get_size(client->outbox) > client->config->outbox_limit) { return -2; } - } MQTT_API_LOCK(client); #ifdef MQTT_PROTOCOL_5 + if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) { if (esp_mqtt5_client_publish_check(client, qos, retain) != ESP_OK) { ESP_LOGI(TAG, "esp_mqtt_client_enqueue check fail"); @@ -2305,37 +2598,43 @@ int esp_mqtt_client_enqueue(esp_mqtt_client_handle_t client, const char *topic, return -1; } } + #endif int ret = mqtt_client_enqueue_publish(client, topic, data, len, qos, retain, store); MQTT_API_UNLOCK(client); + if (ret == 0 && store == false) { // messages with qos=0 are not enqueued if not overridden by store_in_outobx -> indicate as error return -1; } + return ret; } -esp_err_t esp_mqtt_client_register_event(esp_mqtt_client_handle_t client, esp_mqtt_event_id_t event, esp_event_handler_t event_handler, void *event_handler_arg) +esp_err_t esp_mqtt_client_register_event(esp_mqtt_client_handle_t client, esp_mqtt_event_id_t event, + esp_event_handler_t event_handler, void *event_handler_arg) { if (client == NULL) { return ESP_ERR_INVALID_ARG; } -#ifdef MQTT_SUPPORTED_FEATURE_EVENT_LOOP - return esp_event_handler_register_with(client->config->event_loop_handle, MQTT_EVENTS, event, event_handler, event_handler_arg); +#ifdef MQTT_SUPPORTED_FEATURE_EVENT_LOOP + return esp_event_handler_register_with(client->config->event_loop_handle, MQTT_EVENTS, event, event_handler, + event_handler_arg); #else ESP_LOGE(TAG, "Registering event handler while event loop not available in IDF version %s", IDF_VER); return ESP_FAIL; #endif } -esp_err_t esp_mqtt_client_unregister_event(esp_mqtt_client_handle_t client, esp_mqtt_event_id_t event, esp_event_handler_t event_handler) +esp_err_t esp_mqtt_client_unregister_event(esp_mqtt_client_handle_t client, esp_mqtt_event_id_t event, + esp_event_handler_t event_handler) { if (client == NULL) { return ESP_ERR_INVALID_ARG; } -#ifdef MQTT_SUPPORTED_FEATURE_EVENT_LOOP +#ifdef MQTT_SUPPORTED_FEATURE_EVENT_LOOP return esp_event_handler_unregister_with(client->config->event_loop_handle, MQTT_EVENTS, event, event_handler); #else ESP_LOGE(TAG, "Unregistering event handler while event loop not available in IDF version %s", IDF_VER); @@ -2352,9 +2651,10 @@ static void esp_mqtt_client_dispatch_transport_error(esp_mqtt_client_handle_t cl client->event.error_handle->disconnect_return_code = 0; #endif #ifdef MQTT_SUPPORTED_FEATURE_TRANSPORT_ERR_REPORTING - client->event.error_handle->esp_tls_last_esp_err = esp_tls_get_and_clear_last_error(esp_transport_get_error_handle(client->transport), - &client->event.error_handle->esp_tls_stack_err, - &client->event.error_handle->esp_tls_cert_verify_flags); + client->event.error_handle->esp_tls_last_esp_err = esp_tls_get_and_clear_last_error(esp_transport_get_error_handle( + client->transport), + &client->event.error_handle->esp_tls_stack_err, + &client->event.error_handle->esp_tls_cert_verify_flags); #ifdef MQTT_SUPPORTED_FEATURE_TRANSPORT_SOCK_ERRNO_REPORTING client->event.error_handle->esp_transport_sock_errno = esp_transport_get_errno(client->transport); #endif @@ -2377,16 +2677,18 @@ int esp_mqtt_client_get_outbox_size(esp_mqtt_client_handle_t client) } MQTT_API_UNLOCK(client); - return outbox_size; } -esp_transport_handle_t esp_mqtt_client_get_transport(esp_mqtt_client_handle_t client, char *transport_scheme){ +esp_transport_handle_t esp_mqtt_client_get_transport(esp_mqtt_client_handle_t client, char *transport_scheme) +{ if (client == NULL || (transport_scheme == NULL && client->config->transport == NULL)) { return NULL; } + if (transport_scheme == NULL && client->config->transport != NULL) { return client->config->transport; } + return esp_transport_list_get_transport(client->transport_list, transport_scheme); } diff --git a/pytest.ini b/pytest.ini index 3bbea53..3502acb 100644 --- a/pytest.ini +++ b/pytest.ini @@ -25,4 +25,3 @@ 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 - diff --git a/test/apps/build_test/main/idf_component.yml b/test/apps/build_test/main/idf_component.yml index 1b39baa..aad217d 100644 --- a/test/apps/build_test/main/idf_component.yml +++ b/test/apps/build_test/main/idf_component.yml @@ -1,4 +1,4 @@ dependencies: espressif/mqtt: version: "*" - override_path: "../../../.." \ No newline at end of file + override_path: "../../../.." diff --git a/test/apps/build_test/main/mqtt_app.cpp b/test/apps/build_test/main/mqtt_app.cpp index 2960f41..cf5f43b 100644 --- a/test/apps/build_test/main/mqtt_app.cpp +++ b/test/apps/build_test/main/mqtt_app.cpp @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ /* Build only example to check mqtt client API from C++ This example code is in the Public Domain (or CC0 licensed, at your option.) diff --git a/test/apps/common/test_mqtt_connection.c b/test/apps/common/test_mqtt_connection.c index a6cb702..451df74 100644 --- a/test/apps/common/test_mqtt_connection.c +++ b/test/apps/common/test_mqtt_connection.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,7 +11,6 @@ #include "esp_eth.h" #include "esp_log.h" - #if SOC_EMAC_SUPPORTED #define ETH_START_BIT BIT(0) #define ETH_STOP_BIT BIT(1) @@ -20,7 +19,6 @@ #define ETH_STOP_TIMEOUT_MS (10000) #define ETH_GET_IP_TIMEOUT_MS (60000) - static const char *TAG = "esp32_eth_test_fixture"; static EventGroupHandle_t s_eth_event_group = NULL; static esp_netif_t *s_eth_netif = NULL; @@ -29,30 +27,34 @@ static esp_eth_phy_t *s_phy = NULL; static esp_eth_handle_t s_eth_handle = NULL; static esp_eth_netif_glue_handle_t s_eth_glue = NULL; - /** Event handler for Ethernet events */ static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { EventGroupHandle_t eth_event_group = (EventGroupHandle_t)arg; + switch (event_id) { - case ETHERNET_EVENT_CONNECTED: - xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT); - ESP_LOGI(TAG, "Ethernet Link Up"); - break; - case ETHERNET_EVENT_DISCONNECTED: - ESP_LOGI(TAG, "Ethernet Link Down"); - break; - case ETHERNET_EVENT_START: - xEventGroupSetBits(eth_event_group, ETH_START_BIT); - ESP_LOGI(TAG, "Ethernet Started"); - break; - case ETHERNET_EVENT_STOP: - xEventGroupSetBits(eth_event_group, ETH_STOP_BIT); - ESP_LOGI(TAG, "Ethernet Stopped"); - break; - default: - break; + case ETHERNET_EVENT_CONNECTED: + xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT); + ESP_LOGI(TAG, "Ethernet Link Up"); + break; + + case ETHERNET_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "Ethernet Link Down"); + break; + + case ETHERNET_EVENT_START: + xEventGroupSetBits(eth_event_group, ETH_START_BIT); + ESP_LOGI(TAG, "Ethernet Started"); + break; + + case ETHERNET_EVENT_STOP: + xEventGroupSetBits(eth_event_group, ETH_STOP_BIT); + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + + default: + break; } } @@ -76,12 +78,15 @@ static esp_err_t test_uninstall_driver(esp_eth_handle_t eth_hdl, uint32_t ms_to_ { int i = 0; ms_to_wait += 100; + for (i = 0; i < ms_to_wait / 100; i++) { vTaskDelay(pdMS_TO_TICKS(100)); + if (esp_eth_driver_uninstall(eth_hdl) == ESP_OK) { break; } } + if (i < ms_to_wait / 10) { return ESP_OK; } else { @@ -89,7 +94,6 @@ static esp_err_t test_uninstall_driver(esp_eth_handle_t eth_hdl, uint32_t ms_to_ } } - void connect_test_fixture_setup(void) { EventBits_t bits; @@ -99,14 +103,12 @@ void connect_test_fixture_setup(void) // create TCP/IP netif esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); s_eth_netif = esp_netif_new(&netif_cfg); - eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); s_mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); s_phy = esp_eth_phy_new_ip101(&phy_config); esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(s_mac, s_phy); - // install Ethernet driver TEST_ESP_OK(esp_eth_driver_install(ð_config, &s_eth_handle)); // combine driver with netif diff --git a/test/apps/mqtt/main/idf_component.yml b/test/apps/mqtt/main/idf_component.yml index 30ca4ee..8cd077e 100644 --- a/test/apps/mqtt/main/idf_component.yml +++ b/test/apps/mqtt/main/idf_component.yml @@ -2,4 +2,3 @@ dependencies: espressif/mqtt: version: "*" override_path: "../../../.." - diff --git a/test/apps/mqtt/main/test_mqtt.c b/test/apps/mqtt/main/test_mqtt.c index cb56a5e..c1bf478 100644 --- a/test/apps/mqtt/main/test_mqtt.c +++ b/test/apps/mqtt/main/test_mqtt.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 * @@ -23,12 +23,14 @@ TEST_GROUP(mqtt); -TEST_SETUP(mqtt){ +TEST_SETUP(mqtt) +{ test_utils_record_free_mem(); TEST_ESP_OK(test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL)); } -TEST_TEAR_DOWN(mqtt){ +TEST_TEAR_DOWN(mqtt) +{ test_utils_finish_and_evaluate_leaks(test_utils_get_leak_level(ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_ALL), test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_ALL)); } @@ -36,87 +38,85 @@ TEST_TEAR_DOWN(mqtt){ TEST(mqtt, init_with_invalid_url) { const esp_mqtt_client_config_t mqtt_cfg = { - .broker.address.uri = "INVALID", + .broker.address.uri = "INVALID", }; esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); - TEST_ASSERT_EQUAL(NULL, client ); + TEST_ASSERT_EQUAL(NULL, client); } TEST(mqtt, init_and_deinit) { const esp_mqtt_client_config_t mqtt_cfg = { - // no connection takes place, but the uri has to be valid for init() to succeed - .broker.address.uri = "mqtts://localhost:8883", + // no connection takes place, but the uri has to be valid for init() to succeed + .broker.address.uri = "mqtts://localhost:8883", }; esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); - TEST_ASSERT_NOT_EQUAL(NULL, client ); + TEST_ASSERT_NOT_EQUAL(NULL, client); esp_mqtt_client_destroy(client); } -static const char* this_bin_addr(void) +static const char *this_bin_addr(void) { esp_partition_mmap_handle_t out_handle; const void *binary_address; - const esp_partition_t* partition = esp_ota_get_running_partition(); + const esp_partition_t *partition = esp_ota_get_running_partition(); esp_partition_mmap(partition, 0, partition->size, ESP_PARTITION_MMAP_DATA, &binary_address, &out_handle); return binary_address; } TEST(mqtt, enqueue_and_destroy_outbox) { - const char * bin_addr = this_bin_addr(); - - // Reseting leak detection since this_bin_addr adds to allocated memory. + const char *bin_addr = this_bin_addr(); + // Resetting leak detection since this_bin_addr adds to allocated memory. test_utils_record_free_mem(); TEST_ESP_OK(test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL)); const int messages = 20; const int size = 2000; const esp_mqtt_client_config_t mqtt_cfg = { - // no connection takes place, but the uri has to be valid for init() to succeed - .broker.address.uri = "mqtts://localhost:8883", + // no connection takes place, but the uri has to be valid for init() to succeed + .broker.address.uri = "mqtts://localhost:8883", }; esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); - TEST_ASSERT_NOT_EQUAL(NULL, client ); + TEST_ASSERT_NOT_EQUAL(NULL, client); int bytes_before = esp_get_free_heap_size(); - for (int i=0; iqos; xEventGroupSetBits(s_event_group, DATA_BIT); } @@ -56,12 +55,13 @@ static void mqtt_data_handler_lwt(void *handler_args, esp_event_base_t base, int ESP_LOGI("mqtt-lwt", "MQTT_EVENT_DATA"); ESP_LOGI("mqtt-lwt", "TOPIC=%.*s", event->topic_len, event->topic); ESP_LOGI("mqtt-lwt", "DATA=%.*s", event->data_len, event->data); + if (strncmp(event->data, "no-lwt", event->data_len) == 0) { // no lwt, just to indicate the test has finished xEventGroupSetBits(s_event_group, DATA_BIT); } else { // count up any potential lwt message - int * count = handler_args; + int *count = handler_args; *count = *count + 1; ESP_LOGE("mqtt-lwt", "count=%d", *count); } @@ -73,40 +73,42 @@ static void mqtt_data_handler_subscribe(void *handler_args, esp_event_base_t bas if (event_id == MQTT_EVENT_SUBSCRIBED) { esp_mqtt_event_handle_t event = event_data; ESP_LOGI("mqtt-subscribe", "MQTT_EVENT_SUBSCRIBED, data size=%d", event->data_len); - int * sub_payload = handler_args; + int *sub_payload = handler_args; + if (event->data_len == 1) { - ESP_LOGI("mqtt-subscribe", "DATA=%d", *(uint8_t*)event->data); - *sub_payload = *(uint8_t*)event->data; + ESP_LOGI("mqtt-subscribe", "DATA=%d", *(uint8_t *)event->data); + *sub_payload = *(uint8_t *)event->data; } + xEventGroupSetBits(s_event_group, DATA_BIT); } } - static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { switch ((esp_mqtt_event_id_t)event_id) { - case MQTT_EVENT_CONNECTED: - xEventGroupSetBits(s_event_group, CONNECT_BIT); - break; + case MQTT_EVENT_CONNECTED: + xEventGroupSetBits(s_event_group, CONNECT_BIT); + break; - case MQTT_EVENT_DISCONNECTED: - xEventGroupSetBits(s_event_group, DISCONNECT_BIT); - break; - default: - break; + case MQTT_EVENT_DISCONNECTED: + xEventGroupSetBits(s_event_group, DISCONNECT_BIT); + break; + + default: + break; } } bool mqtt_connect_disconnect(void) { const esp_mqtt_client_config_t mqtt_cfg = { - .broker.address.uri = CONFIG_MQTT_TEST_BROKER_URI, - .network.disable_auto_reconnect = true, + .broker.address.uri = CONFIG_MQTT_TEST_BROKER_URI, + .network.disable_auto_reconnect = true, }; s_event_group = xEventGroupCreate(); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); - TEST_ASSERT_TRUE(NULL != client ); + TEST_ASSERT_TRUE(NULL != client); esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); TEST_ASSERT_TRUE(ESP_OK == esp_mqtt_client_start(client)); WAIT_FOR_EVENT(CONNECT_BIT); @@ -122,13 +124,13 @@ bool mqtt_connect_disconnect(void) bool mqtt_subscribe_publish(void) { const esp_mqtt_client_config_t mqtt_cfg = { - .broker.address.uri = CONFIG_MQTT_TEST_BROKER_URI, + .broker.address.uri = CONFIG_MQTT_TEST_BROKER_URI, }; - char* topic = append_mac("topic"); + char *topic = append_mac("topic"); TEST_ASSERT_TRUE(NULL != topic); s_event_group = xEventGroupCreate(); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); - TEST_ASSERT_TRUE(NULL != client ); + TEST_ASSERT_TRUE(NULL != client); esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); TEST_ASSERT_TRUE(ESP_OK == esp_mqtt_client_start(client)); WAIT_FOR_EVENT(CONNECT_BIT); @@ -149,25 +151,24 @@ bool mqtt_subscribe_publish(void) bool mqtt_lwt_clean_disconnect(void) { - char* lwt = append_mac("lwt"); + char *lwt = append_mac("lwt"); TEST_ASSERT_TRUE(lwt); const esp_mqtt_client_config_t mqtt_cfg1 = { - .broker.address.uri = CONFIG_MQTT_TEST_BROKER_URI, - .credentials.set_null_client_id = true, - .session.last_will.topic = lwt, - .session.last_will.msg = "lwt_msg" + .broker.address.uri = CONFIG_MQTT_TEST_BROKER_URI, + .credentials.set_null_client_id = true, + .session.last_will.topic = lwt, + .session.last_will.msg = "lwt_msg" }; const esp_mqtt_client_config_t mqtt_cfg2 = { - .broker.address.uri = CONFIG_MQTT_TEST_BROKER_URI, - .credentials.set_null_client_id = true, - .session.last_will.topic = lwt, - .session.last_will.msg = "lwt_msg" + .broker.address.uri = CONFIG_MQTT_TEST_BROKER_URI, + .credentials.set_null_client_id = true, + .session.last_will.topic = lwt, + .session.last_will.msg = "lwt_msg" }; s_event_group = xEventGroupCreate(); - esp_mqtt_client_handle_t client1 = esp_mqtt_client_init(&mqtt_cfg1); esp_mqtt_client_handle_t client2 = esp_mqtt_client_init(&mqtt_cfg2); - TEST_ASSERT_TRUE(NULL != client1 && NULL != client2 ); + TEST_ASSERT_TRUE(NULL != client1 && NULL != client2); esp_mqtt_client_register_event(client1, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); esp_mqtt_client_register_event(client2, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); TEST_ASSERT_TRUE(esp_mqtt_client_start(client1) == ESP_OK); @@ -201,14 +202,14 @@ bool mqtt_lwt_clean_disconnect(void) bool mqtt_subscribe_payload(void) { const esp_mqtt_client_config_t mqtt_cfg = { - .broker.address.uri = CONFIG_MQTT_TEST_BROKER_URI, - .network.disable_auto_reconnect = true, + .broker.address.uri = CONFIG_MQTT_TEST_BROKER_URI, + .network.disable_auto_reconnect = true, }; - char* topic = append_mac("topic"); + char *topic = append_mac("topic"); TEST_ASSERT_TRUE(NULL != topic); s_event_group = xEventGroupCreate(); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); - TEST_ASSERT_TRUE(NULL != client ); + TEST_ASSERT_TRUE(NULL != client); esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); TEST_ASSERT_TRUE(ESP_OK == esp_mqtt_client_start(client)); WAIT_FOR_EVENT(CONNECT_BIT); diff --git a/test/apps/mqtt/main/test_mqtt_client_broker.h b/test/apps/mqtt/main/test_mqtt_client_broker.h index 63e7856..6063cd1 100644 --- a/test/apps/mqtt/main/test_mqtt_client_broker.h +++ b/test/apps/mqtt/main/test_mqtt_client_broker.h @@ -19,7 +19,6 @@ ESP_LOGI("mqtt_test", "Test:" #test_name "() passed "); \ } while(0) - /** * @brief This module contains mqtt test cases interacting the client with a (real) broker */ diff --git a/test/apps/mqtt5/main/idf_component.yml b/test/apps/mqtt5/main/idf_component.yml index 1b39baa..aad217d 100644 --- a/test/apps/mqtt5/main/idf_component.yml +++ b/test/apps/mqtt5/main/idf_component.yml @@ -1,4 +1,4 @@ dependencies: espressif/mqtt: version: "*" - override_path: "../../../.." \ No newline at end of file + override_path: "../../../.." diff --git a/test/apps/mqtt5/main/test_mqtt5.c b/test/apps/mqtt5/main/test_mqtt5.c index 9d8705d..2f3041a 100644 --- a/test/apps/mqtt5/main/test_mqtt5.c +++ b/test/apps/mqtt5/main/test_mqtt5.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,7 +15,6 @@ #include "test_mqtt_connection.h" #include "esp_partition.h" - TEST_GROUP(mqtt5); TEST_SETUP(mqtt5) @@ -43,7 +42,7 @@ TEST(mqtt5, init_with_invalid_url) .session.protocol_ver = MQTT_PROTOCOL_V_5, }; esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt5_cfg); - TEST_ASSERT_EQUAL(NULL, client ); + TEST_ASSERT_EQUAL(NULL, client); } TEST(mqtt5, init_and_deinit) @@ -75,14 +74,13 @@ TEST(mqtt5, init_and_deinit) .correlation_data = "123456", .correlation_data_len = 6, }; - esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt5_cfg); esp_mqtt5_client_set_user_property(&connect_property.user_property, user_property_arr, 3); esp_mqtt5_client_set_user_property(&connect_property.will_user_property, user_property_arr, 3); esp_mqtt5_client_set_connect_property(client, &connect_property); esp_mqtt5_client_delete_user_property(connect_property.user_property); esp_mqtt5_client_delete_user_property(connect_property.will_user_property); - TEST_ASSERT_NOT_EQUAL(NULL, client ); + TEST_ASSERT_NOT_EQUAL(NULL, client); esp_mqtt_client_destroy(client); } @@ -98,7 +96,7 @@ static const char *this_bin_addr(void) TEST(mqtt5, enqueue_and_destroy_outbox) { const char *bin_addr = this_bin_addr(); - // Reseting leak detection since this_bin_addr adds to allocated memory. + // Resetting leak detection since this_bin_addr adds to allocated memory. test_utils_record_free_mem(); TEST_ESP_OK(test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL)); const int messages = 20; @@ -118,8 +116,9 @@ TEST(mqtt5, enqueue_and_destroy_outbox) .content_type = "json", }; esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt5_cfg); - TEST_ASSERT_NOT_EQUAL(NULL, client ); + TEST_ASSERT_NOT_EQUAL(NULL, client); int bytes_before = esp_get_free_heap_size(); + for (int i = 0; i < messages; i ++) { esp_mqtt5_client_set_user_property(&publish_property.user_property, user_property_arr, 3); esp_mqtt5_client_set_publish_property(client, &publish_property); @@ -127,27 +126,25 @@ TEST(mqtt5, enqueue_and_destroy_outbox) esp_mqtt5_client_delete_user_property(publish_property.user_property); publish_property.user_property = NULL; } + int bytes_after = esp_get_free_heap_size(); // check that outbox allocated all messages on heap TEST_ASSERT_GREATER_OR_EQUAL(messages * size, bytes_before - bytes_after); - esp_mqtt_client_destroy(client); } #if SOC_EMAC_SUPPORTED /** - * This test cases uses ethernet kit, so build and use it only if EMAC supported + * This test cases uses ethernet kit, so build and use it only if EMACS supported */ TEST(mqtt5, broker_tests) { test_case_uses_tcpip(); connect_test_fixture_setup(); - RUN_MQTT5_BROKER_TEST(mqtt5_connect_disconnect); RUN_MQTT5_BROKER_TEST(mqtt5_subscribe_publish); RUN_MQTT5_BROKER_TEST(mqtt5_lwt_clean_disconnect); RUN_MQTT5_BROKER_TEST(mqtt5_subscribe_payload); - connect_test_fixture_teardown(); } #endif // SOC_EMAC_SUPPORTED @@ -158,14 +155,12 @@ TEST_GROUP_RUNNER(mqtt5) RUN_TEST_CASE(mqtt5, init_with_invalid_url); RUN_TEST_CASE(mqtt5, init_and_deinit); RUN_TEST_CASE(mqtt5, enqueue_and_destroy_outbox); - #if SOC_EMAC_SUPPORTED RUN_TEST_CASE(mqtt5, broker_tests); #endif // SOC_EMAC_SUPPORTED #endif //!DISABLED_FOR_TARGETS(ESP32H2) } - void app_main(void) { UNITY_MAIN(mqtt5); diff --git a/test/apps/mqtt5/main/test_mqtt5_client_broker.c b/test/apps/mqtt5/main/test_mqtt5_client_broker.c index 0ac4c3d..b7b6e25 100644 --- a/test/apps/mqtt5/main/test_mqtt5_client_broker.c +++ b/test/apps/mqtt5/main/test_mqtt5_client_broker.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,7 +23,6 @@ } \ } while(0) - static const int COMMON_OPERATION_TIMEOUT = 10000; static const int CONNECT_BIT = BIT0; static const int DISCONNECT_BIT = BIT1; @@ -37,7 +36,7 @@ static esp_mqtt5_user_property_item_t user_property_arr[3] = { {"p", "password"} }; -static char* append_mac(const char* string) +static char *append_mac(const char *string) { uint8_t mac[6]; char *id_string = NULL; @@ -50,7 +49,7 @@ static void mqtt5_data_handler_qos(void *handler_args, esp_event_base_t base, in { if (event_id == MQTT_EVENT_DATA) { esp_mqtt_event_handle_t event = event_data; - int * qos = handler_args; + int *qos = handler_args; *qos = event->qos; xEventGroupSetBits(s_event_group, DATA_BIT); } @@ -63,12 +62,13 @@ static void mqtt5_data_handler_lwt(void *handler_args, esp_event_base_t base, in ESP_LOGI("mqtt-lwt", "MQTT_EVENT_DATA"); ESP_LOGI("mqtt-lwt", "TOPIC=%.*s", event->topic_len, event->topic); ESP_LOGI("mqtt-lwt", "DATA=%.*s", event->data_len, event->data); + if (strncmp(event->data, "no-lwt", event->data_len) == 0) { // no lwt, just to indicate the test has finished xEventGroupSetBits(s_event_group, DATA_BIT); } else { // count up any potential lwt message - int * count = handler_args; + int *count = handler_args; *count = *count + 1; ESP_LOGE("mqtt5-lwt", "count=%d", *count); } @@ -80,37 +80,39 @@ static void mqtt5_data_handler_subscribe(void *handler_args, esp_event_base_t ba if (event_id == MQTT_EVENT_SUBSCRIBED) { esp_mqtt_event_handle_t event = event_data; ESP_LOGI("mqtt5-subscribe", "MQTT_EVENT_SUBSCRIBED, data size=%d", event->data_len); - int * sub_payload = handler_args; + int *sub_payload = handler_args; + if (event->data_len == 1) { - ESP_LOGI("mqtt5-subscribe", "DATA=%d", *(uint8_t*)event->data); - *sub_payload = *(uint8_t*)event->data; + ESP_LOGI("mqtt5-subscribe", "DATA=%d", *(uint8_t *)event->data); + *sub_payload = *(uint8_t *)event->data; } + xEventGroupSetBits(s_event_group, DATA_BIT); } } - static void mqtt5_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { switch ((esp_mqtt_event_id_t)event_id) { - case MQTT_EVENT_CONNECTED: - xEventGroupSetBits(s_event_group, CONNECT_BIT); - break; + case MQTT_EVENT_CONNECTED: + xEventGroupSetBits(s_event_group, CONNECT_BIT); + break; - case MQTT_EVENT_DISCONNECTED: - xEventGroupSetBits(s_event_group, DISCONNECT_BIT); - break; - default: - break; + case MQTT_EVENT_DISCONNECTED: + xEventGroupSetBits(s_event_group, DISCONNECT_BIT); + break; + + default: + break; } } bool mqtt5_connect_disconnect(void) { const esp_mqtt_client_config_t mqtt5_cfg = { - .broker.address.uri = CONFIG_MQTT5_TEST_BROKER_URI, - .network.disable_auto_reconnect = true, - .session.protocol_ver = MQTT_PROTOCOL_V_5, + .broker.address.uri = CONFIG_MQTT5_TEST_BROKER_URI, + .network.disable_auto_reconnect = true, + .session.protocol_ver = MQTT_PROTOCOL_V_5, }; esp_mqtt5_connection_property_config_t connect_property = { .session_expiry_interval = 10, @@ -126,14 +128,15 @@ bool mqtt5_connect_disconnect(void) }; s_event_group = xEventGroupCreate(); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt5_cfg); - TEST_ASSERT_TRUE(NULL != client ); + TEST_ASSERT_TRUE(NULL != client); esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt5_event_handler, NULL); TEST_ASSERT_TRUE(ESP_OK == esp_mqtt5_client_set_user_property(&connect_property.user_property, user_property_arr, 3)); TEST_ASSERT_TRUE(ESP_OK == esp_mqtt5_client_set_connect_property(client, &connect_property)); esp_mqtt5_client_delete_user_property(connect_property.user_property); TEST_ASSERT_TRUE(ESP_OK == esp_mqtt_client_start(client)); WAIT_FOR_EVENT(CONNECT_BIT); - TEST_ASSERT_TRUE(ESP_OK == esp_mqtt5_client_set_user_property(&disconnect_property.user_property, user_property_arr, 3)); + TEST_ASSERT_TRUE(ESP_OK == esp_mqtt5_client_set_user_property(&disconnect_property.user_property, user_property_arr, + 3)); TEST_ASSERT_TRUE(ESP_OK == esp_mqtt5_client_set_disconnect_property(client, &disconnect_property)); esp_mqtt5_client_delete_user_property(disconnect_property.user_property); esp_mqtt_client_disconnect(client); @@ -148,8 +151,8 @@ bool mqtt5_connect_disconnect(void) bool mqtt5_subscribe_publish(void) { const esp_mqtt_client_config_t mqtt5_cfg = { - .broker.address.uri = CONFIG_MQTT5_TEST_BROKER_URI, - .session.protocol_ver = MQTT_PROTOCOL_V_5, + .broker.address.uri = CONFIG_MQTT5_TEST_BROKER_URI, + .session.protocol_ver = MQTT_PROTOCOL_V_5, }; esp_mqtt5_publish_property_config_t publish_property = { .payload_format_indicator = 1, @@ -166,11 +169,11 @@ bool mqtt5_subscribe_publish(void) .retain_as_published_flag = true, .retain_handle = 0, }; - char* topic = append_mac("topic"); + char *topic = append_mac("topic"); TEST_ASSERT_TRUE(NULL != topic); s_event_group = xEventGroupCreate(); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt5_cfg); - TEST_ASSERT_TRUE(NULL != client ); + TEST_ASSERT_TRUE(NULL != client); esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt5_event_handler, NULL); TEST_ASSERT_TRUE(ESP_OK == esp_mqtt_client_start(client)); WAIT_FOR_EVENT(CONNECT_BIT); @@ -193,21 +196,21 @@ bool mqtt5_subscribe_publish(void) bool mqtt5_lwt_clean_disconnect(void) { - char* lwt = append_mac("lwt"); + char *lwt = append_mac("lwt"); TEST_ASSERT_TRUE(lwt); const esp_mqtt_client_config_t mqtt5_cfg1 = { - .broker.address.uri = CONFIG_MQTT5_TEST_BROKER_URI, - .credentials.set_null_client_id = true, - .session.last_will.topic = lwt, - .session.last_will.msg = "lwt_msg", - .session.protocol_ver = MQTT_PROTOCOL_V_5, + .broker.address.uri = CONFIG_MQTT5_TEST_BROKER_URI, + .credentials.set_null_client_id = true, + .session.last_will.topic = lwt, + .session.last_will.msg = "lwt_msg", + .session.protocol_ver = MQTT_PROTOCOL_V_5, }; const esp_mqtt_client_config_t mqtt5_cfg2 = { - .broker.address.uri = CONFIG_MQTT5_TEST_BROKER_URI, - .credentials.set_null_client_id = true, - .session.last_will.topic = lwt, - .session.last_will.msg = "lwt_msg", - .session.protocol_ver = MQTT_PROTOCOL_V_5, + .broker.address.uri = CONFIG_MQTT5_TEST_BROKER_URI, + .credentials.set_null_client_id = true, + .session.last_will.topic = lwt, + .session.last_will.msg = "lwt_msg", + .session.protocol_ver = MQTT_PROTOCOL_V_5, }; esp_mqtt5_connection_property_config_t connect_property = { .will_delay_interval = 10, @@ -219,10 +222,9 @@ bool mqtt5_lwt_clean_disconnect(void) .correlation_data_len = 6, }; s_event_group = xEventGroupCreate(); - esp_mqtt_client_handle_t client1 = esp_mqtt_client_init(&mqtt5_cfg1); esp_mqtt_client_handle_t client2 = esp_mqtt_client_init(&mqtt5_cfg2); - TEST_ASSERT_TRUE(NULL != client1 && NULL != client2 ); + TEST_ASSERT_TRUE(NULL != client1 && NULL != client2); esp_mqtt_client_register_event(client1, ESP_EVENT_ANY_ID, mqtt5_event_handler, NULL); esp_mqtt_client_register_event(client2, ESP_EVENT_ANY_ID, mqtt5_event_handler, NULL); TEST_ASSERT_TRUE(ESP_OK == esp_mqtt5_client_set_connect_property(client1, &connect_property)); @@ -258,15 +260,15 @@ bool mqtt5_lwt_clean_disconnect(void) bool mqtt5_subscribe_payload(void) { const esp_mqtt_client_config_t mqtt5_cfg = { - .broker.address.uri = CONFIG_MQTT5_TEST_BROKER_URI, - .network.disable_auto_reconnect = true, - .session.protocol_ver = MQTT_PROTOCOL_V_5, + .broker.address.uri = CONFIG_MQTT5_TEST_BROKER_URI, + .network.disable_auto_reconnect = true, + .session.protocol_ver = MQTT_PROTOCOL_V_5, }; - char* topic = append_mac("topic"); + char *topic = append_mac("topic"); TEST_ASSERT_TRUE(NULL != topic); s_event_group = xEventGroupCreate(); esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt5_cfg); - TEST_ASSERT_TRUE(NULL != client ); + TEST_ASSERT_TRUE(NULL != client); esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt5_event_handler, NULL); TEST_ASSERT_TRUE(ESP_OK == esp_mqtt_client_start(client)); WAIT_FOR_EVENT(CONNECT_BIT); diff --git a/test/apps/mqtt5/main/test_mqtt5_client_broker.h b/test/apps/mqtt5/main/test_mqtt5_client_broker.h index a181dd8..429e860 100644 --- a/test/apps/mqtt5/main/test_mqtt5_client_broker.h +++ b/test/apps/mqtt5/main/test_mqtt5_client_broker.h @@ -20,7 +20,6 @@ ESP_LOGI("mqtt5_test", "Test:" #test_name "() passed "); \ } while(0) - /** * @brief This module contains mqtt5 test cases interacting the client with a (real) broker */ diff --git a/test/apps/publish_connect_test/main/connect_test.c b/test/apps/publish_connect_test/main/connect_test.c index 88db721..098ec19 100644 --- a/test/apps/publish_connect_test/main/connect_test.c +++ b/test/apps/publish_connect_test/main/connect_test.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* MQTT connect test This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -43,16 +48,21 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ (void)event_id; esp_mqtt_event_handle_t event = event_data; ESP_LOGD(TAG, "Event: %d, Test case: %d", event->event_id, running_test_case); + switch (event->event_id) { case MQTT_EVENT_BEFORE_CONNECT: break; + case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED: Test=%d", running_test_case); break; + case MQTT_EVENT_DISCONNECTED: break; + case MQTT_EVENT_ERROR: ESP_LOGI(TAG, "MQTT_EVENT_ERROR: Test=%d", running_test_case); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_ESP_TLS) { ESP_LOGI(TAG, "ESP-TLS ERROR: %s", esp_err_to_name(event->error_handle->esp_tls_last_esp_err)); } else if (event->error_handle->error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) { @@ -60,7 +70,9 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ } else { ESP_LOGW(TAG, "Unknown error type: 0x%x", event->error_handle->error_type); } + break; + default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; @@ -69,7 +81,7 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ static void connect_no_certs(esp_mqtt_client_handle_t client, const char *uri) { - ESP_LOGI(TAG, "Runnning :CONFIG_EXAMPLE_CONNECT_CASE_NO_CERT"); + ESP_LOGI(TAG, "Running :CONFIG_EXAMPLE_CONNECT_CASE_NO_CERT"); const esp_mqtt_client_config_t mqtt_cfg = { .broker.address.uri = uri }; @@ -81,10 +93,10 @@ static void connect_with_client_key_password(esp_mqtt_client_handle_t client, co const esp_mqtt_client_config_t mqtt_cfg = { .broker.address.uri = uri, .broker.verification.certificate = (const char *)ca_local_crt, - .credentials.authentication.certificate = (const char *)client_pwd_crt, - .credentials.authentication.key = (const char *)client_pwd_key, - .credentials.authentication.key_password = "esp32", - .credentials.authentication.key_password_len = 5 + .credentials.authentication.certificate = (const char *)client_pwd_crt, + .credentials.authentication.key = (const char *)client_pwd_key, + .credentials.authentication.key_password = "esp32", + .credentials.authentication.key_password_len = 5 }; esp_mqtt_set_config(client, &mqtt_cfg); } @@ -94,9 +106,9 @@ static void connect_with_server_der_cert(esp_mqtt_client_handle_t client, const const esp_mqtt_client_config_t mqtt_cfg = { .broker.address.uri = uri, .broker.verification.certificate = (const char *)ca_der_start, - .broker.verification.certificate_len = ca_der_end - ca_der_start, - .credentials.authentication.certificate = "NULL", - .credentials.authentication.key = "NULL" + .broker.verification.certificate_len = ca_der_end - ca_der_start, + .credentials.authentication.certificate = "NULL", + .credentials.authentication.key = "NULL" }; esp_mqtt_set_config(client, &mqtt_cfg); } @@ -106,8 +118,8 @@ static void connect_with_wrong_server_cert(esp_mqtt_client_handle_t client, cons const esp_mqtt_client_config_t mqtt_cfg = { .broker.address.uri = uri, .broker.verification.certificate = (const char *)client_pwd_crt, - .credentials.authentication.certificate = "NULL", - .credentials.authentication.key = "NULL" + .credentials.authentication.certificate = "NULL", + .credentials.authentication.key = "NULL" }; esp_mqtt_set_config(client, &mqtt_cfg); } @@ -126,8 +138,8 @@ static void connect_with_server_client_certs(esp_mqtt_client_handle_t client, co const esp_mqtt_client_config_t mqtt_cfg = { .broker.address.uri = uri, .broker.verification.certificate = (const char *)ca_local_crt, - .credentials.authentication.certificate = (const char *)client_pwd_crt, - .credentials.authentication.key = (const char *)client_no_pwd_key + .credentials.authentication.certificate = (const char *)client_pwd_crt, + .credentials.authentication.key = (const char *)client_no_pwd_key }; esp_mqtt_set_config(client, &mqtt_cfg); } @@ -137,8 +149,8 @@ static void connect_with_invalid_client_certs(esp_mqtt_client_handle_t client, c const esp_mqtt_client_config_t mqtt_cfg = { .broker.address.uri = uri, .broker.verification.certificate = (const char *)ca_local_crt, - .credentials.authentication.certificate = (const char *)client_inv_crt, - .credentials.authentication.key = (const char *)client_no_pwd_key + .credentials.authentication.certificate = (const char *)client_inv_crt, + .credentials.authentication.key = (const char *)client_no_pwd_key }; esp_mqtt_set_config(client, &mqtt_cfg); } @@ -153,45 +165,57 @@ static void connect_with_alpn(esp_mqtt_client_handle_t client, const char *uri) esp_mqtt_set_config(client, &mqtt_cfg); } -void connect_setup(command_context_t * ctx) { +void connect_setup(command_context_t *ctx) +{ esp_mqtt_client_register_event(ctx->mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, ctx->data); } -void connect_teardown(command_context_t * ctx) { +void connect_teardown(command_context_t *ctx) +{ esp_mqtt_client_unregister_event(ctx->mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler); } -void connection_test(command_context_t * ctx, const char *uri, int test_case) +void connection_test(command_context_t *ctx, const char *uri, int test_case) { ESP_LOGI(TAG, "CASE:%d, connecting to %s", test_case, uri); running_test_case = test_case; + switch (test_case) { case CONFIG_EXAMPLE_CONNECT_CASE_NO_CERT: connect_no_certs(ctx->mqtt_client, uri); break; + case CONFIG_EXAMPLE_CONNECT_CASE_SERVER_CERT: connect_with_server_cert(ctx->mqtt_client, uri); break; + case CONFIG_EXAMPLE_CONNECT_CASE_MUTUAL_AUTH: connect_with_server_client_certs(ctx->mqtt_client, uri); break; + case CONFIG_EXAMPLE_CONNECT_CASE_INVALID_SERVER_CERT: connect_with_wrong_server_cert(ctx->mqtt_client, uri); break; + case CONFIG_EXAMPLE_CONNECT_CASE_SERVER_DER_CERT: connect_with_server_der_cert(ctx->mqtt_client, uri); break; + case CONFIG_EXAMPLE_CONNECT_CASE_MUTUAL_AUTH_KEY_PWD: connect_with_client_key_password(ctx->mqtt_client, uri); break; + case CONFIG_EXAMPLE_CONNECT_CASE_MUTUAL_AUTH_BAD_CRT: connect_with_invalid_client_certs(ctx->mqtt_client, uri); break; + case CONFIG_EXAMPLE_CONNECT_CASE_NO_CERT_ALPN: connect_with_alpn(ctx->mqtt_client, uri); break; + default: ESP_LOGE(TAG, "Unknown test case %d ", test_case); break; } + ESP_LOGI(TAG, "Test case:%d started", test_case); } diff --git a/test/apps/publish_connect_test/main/publish_connect_test.c b/test/apps/publish_connect_test/main/publish_connect_test.c index f316439..be7ebb4 100644 --- a/test/apps/publish_connect_test/main/publish_connect_test.c +++ b/test/apps/publish_connect_test/main/publish_connect_test.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* MQTT publish-connect test This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -36,15 +41,16 @@ publish_args_t publish_args; return 1; \ }} while(0) - -static int do_free_heap(int argc, char **argv) { +static int do_free_heap(int argc, char **argv) +{ (void)argc; (void)argv; ESP_LOGI(TAG, "Note free memory: %d bytes", esp_get_free_heap_size()); return 0; } -static int do_init(int argc, char **argv) { +static int do_init(int argc, char **argv) +{ (void)argc; (void)argv; const esp_mqtt_client_config_t mqtt_cfg = { @@ -52,75 +58,92 @@ static int do_init(int argc, char **argv) { .network.disable_auto_reconnect = true }; command_context.mqtt_client = esp_mqtt_client_init(&mqtt_cfg); - if(!command_context.mqtt_client) { + + if (!command_context.mqtt_client) { ESP_LOGE(TAG, "Failed to initialize client"); return 1; } + publish_init_flags(); ESP_LOGI(TAG, "Mqtt client initialized"); return 0; } -static int do_start(int argc, char **argv) { +static int do_start(int argc, char **argv) +{ (void)argc; (void)argv; - if(esp_mqtt_client_start(command_context.mqtt_client) != ESP_OK) { + + if (esp_mqtt_client_start(command_context.mqtt_client) != ESP_OK) { ESP_LOGE(TAG, "Failed to start mqtt client task"); return 1; } + ESP_LOGI(TAG, "Mqtt client started"); return 0; } -static int do_stop(int argc, char **argv) { +static int do_stop(int argc, char **argv) +{ (void)argc; (void)argv; - if(esp_mqtt_client_stop(command_context.mqtt_client) != ESP_OK) { + + if (esp_mqtt_client_stop(command_context.mqtt_client) != ESP_OK) { ESP_LOGE(TAG, "Failed to stop mqtt client task"); return 1; } + ESP_LOGI(TAG, "Mqtt client stopped"); return 0; } -static int do_disconnect(int argc, char **argv) { +static int do_disconnect(int argc, char **argv) +{ (void)argc; (void)argv; - if(esp_mqtt_client_disconnect(command_context.mqtt_client) != ESP_OK) { + + if (esp_mqtt_client_disconnect(command_context.mqtt_client) != ESP_OK) { ESP_LOGE(TAG, "Failed to request disconnection"); return 1; } + ESP_LOGI(TAG, "Mqtt client disconnected"); return 0; } -static int do_connect_setup(int argc, char **argv) { +static int do_connect_setup(int argc, char **argv) +{ (void)argc; (void)argv; connect_setup(&command_context); return 0; } -static int do_connect_teardown(int argc, char **argv) { +static int do_connect_teardown(int argc, char **argv) +{ (void)argc; (void)argv; connect_teardown(&command_context); return 0; } -static int do_reconnect(int argc, char **argv) { +static int do_reconnect(int argc, char **argv) +{ (void)argc; (void)argv; - if(esp_mqtt_client_reconnect(command_context.mqtt_client) != ESP_OK) { + + if (esp_mqtt_client_reconnect(command_context.mqtt_client) != ESP_OK) { ESP_LOGE(TAG, "Failed to request reconnection"); return 1; } + ESP_LOGI(TAG, "Mqtt client will reconnect"); return 0; ; } -static int do_destroy(int argc, char **argv) { +static int do_destroy(int argc, char **argv) +{ (void)argc; (void)argv; esp_mqtt_client_destroy(command_context.mqtt_client); @@ -132,54 +155,62 @@ static int do_destroy(int argc, char **argv) { static int do_connect(int argc, char **argv) { int nerrors = arg_parse(argc, argv, (void **) &connection_args); + if (nerrors != 0) { arg_print_errors(stderr, connection_args.end, argv[0]); return 1; } - if(!command_context.mqtt_client) { + + if (!command_context.mqtt_client) { ESP_LOGE(TAG, "MQTT client not initialized, call init first"); return 1; } + connection_test(&command_context, *connection_args.uri->sval, *connection_args.test_case->ival); return 0; } -static int do_publish_setup(int argc, char **argv) { +static int do_publish_setup(int argc, char **argv) +{ RETURN_ON_PARSE_ERROR(publish_setup_args); - if(command_context.data) { + + if (command_context.data) { free(command_context.data); } + command_context.data = calloc(1, sizeof(publish_context_t)); - ((publish_context_t*)command_context.data)->pattern = strdup(*publish_setup_args.pattern->sval); - ((publish_context_t*)command_context.data)->pattern_repetitions = *publish_setup_args.pattern_repetitions->ival; - ((publish_context_t*)command_context.data)->subscribe_to = strdup(*publish_setup_args.subscribe_to->sval); - ((publish_context_t*)command_context.data)->publish_to = strdup(*publish_setup_args.publish_to->sval); + ((publish_context_t *)command_context.data)->pattern = strdup(*publish_setup_args.pattern->sval); + ((publish_context_t *)command_context.data)->pattern_repetitions = *publish_setup_args.pattern_repetitions->ival; + ((publish_context_t *)command_context.data)->subscribe_to = strdup(*publish_setup_args.subscribe_to->sval); + ((publish_context_t *)command_context.data)->publish_to = strdup(*publish_setup_args.publish_to->sval); publish_setup(&command_context, *publish_setup_args.transport->sval); return 0; } -static int do_publish(int argc, char **argv) { +static int do_publish(int argc, char **argv) +{ RETURN_ON_PARSE_ERROR(publish_args); - publish_test(&command_context, publish_args.expected_to_publish->ival[0], publish_args.qos->ival[0], publish_args.enqueue->ival[0]); + publish_test(&command_context, publish_args.expected_to_publish->ival[0], publish_args.qos->ival[0], + publish_args.enqueue->ival[0]); return 0; } -static int do_publish_report(int argc, char **argv) { +static int do_publish_report(int argc, char **argv) +{ (void)argc; (void)argv; - publish_context_t * ctx = command_context.data; - ESP_LOGI(TAG,"Test Report : Messages received %d, %d expected", ctx->nr_of_msg_received, ctx->nr_of_msg_expected); - + publish_context_t *ctx = command_context.data; + ESP_LOGI(TAG, "Test Report : Messages received %d, %d expected", ctx->nr_of_msg_received, ctx->nr_of_msg_expected); return 0; } -void register_common_commands(void) { +void register_common_commands(void) +{ const esp_console_cmd_t init = { .command = "init", .help = "Run inition test\n", .hint = NULL, .func = &do_init, }; - const esp_console_cmd_t start = { .command = "start", .help = "Run startion test\n", @@ -210,17 +241,19 @@ void register_common_commands(void) { ESP_ERROR_CHECK(esp_console_cmd_register(&destroy)); ESP_ERROR_CHECK(esp_console_cmd_register(&free_heap)); } -void register_publish_commands(void) { - publish_setup_args.transport = arg_str1(NULL,NULL,"", "Selected transport to test"); - publish_setup_args.publish_to = arg_str1(NULL,NULL,"", "Selected publish_to to publish"); - publish_setup_args.subscribe_to = arg_str1(NULL,NULL,"", "Selected subscribe_to to publish"); - publish_setup_args.pattern = arg_str1(NULL,NULL,"", "Message pattern repeated to build big messages"); - publish_setup_args.pattern_repetitions = arg_int1(NULL,NULL,"", "How many times the pattern is repeated"); +void register_publish_commands(void) +{ + publish_setup_args.transport = arg_str1(NULL, NULL, "", "Selected transport to test"); + publish_setup_args.publish_to = arg_str1(NULL, NULL, "", "Selected publish_to to publish"); + publish_setup_args.subscribe_to = arg_str1(NULL, NULL, "", "Selected subscribe_to to publish"); + publish_setup_args.pattern = arg_str1(NULL, NULL, "", "Message pattern repeated to build big messages"); + publish_setup_args.pattern_repetitions = arg_int1(NULL, NULL, "", + "How many times the pattern is repeated"); publish_setup_args.end = arg_end(1); - - publish_args.expected_to_publish = arg_int1(NULL,NULL,"", "How many times the pattern is repeated"); - publish_args.qos = arg_int1(NULL,NULL,"", "How many times the pattern is repeated"); - publish_args.enqueue = arg_int1(NULL,NULL,"", "How many times the pattern is repeated"); + publish_args.expected_to_publish = arg_int1(NULL, NULL, "", + "How many times the pattern is repeated"); + publish_args.qos = arg_int1(NULL, NULL, "", "How many times the pattern is repeated"); + publish_args.enqueue = arg_int1(NULL, NULL, "", "How many times the pattern is repeated"); publish_args.end = arg_end(1); const esp_console_cmd_t publish_setup = { .command = "publish_setup", @@ -229,7 +262,6 @@ void register_publish_commands(void) { .func = &do_publish_setup, .argtable = &publish_setup_args }; - const esp_console_cmd_t publish = { .command = "publish", .help = "Run publish test\n", @@ -247,11 +279,11 @@ void register_publish_commands(void) { ESP_ERROR_CHECK(esp_console_cmd_register(&publish)); ESP_ERROR_CHECK(esp_console_cmd_register(&publish_report)); } -void register_connect_commands(void){ - connection_args.uri = arg_str1(NULL,NULL,"", "Broker address"); - connection_args.test_case = arg_int1(NULL, NULL, "","Selected test case"); +void register_connect_commands(void) +{ + connection_args.uri = arg_str1(NULL, NULL, "", "Broker address"); + connection_args.test_case = arg_int1(NULL, NULL, "", "Selected test case"); connection_args.end = arg_end(1); - const esp_console_cmd_t connect = { .command = "connect", .help = "Run connection test\n", @@ -259,7 +291,6 @@ void register_connect_commands(void){ .func = &do_connect, .argtable = &connection_args }; - const esp_console_cmd_t reconnect = { .command = "reconnect", .help = "Run reconnection test\n", @@ -292,7 +323,7 @@ void register_connect_commands(void){ } #ifdef CONFIG_EXAMPLE_RUN_LOCAL_BROKER -static void broker_task(void* ctx) +static void broker_task(void *ctx) { // broker continues to run in this task struct mosq_broker_config config = { .host = CONFIG_EXAMPLE_BROKER_HOST, .port = CONFIG_EXAMPLE_BROKER_PORT }; @@ -303,15 +334,12 @@ static void broker_task(void* ctx) void app_main(void) { static const size_t max_line = 256; - ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); - esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("wifi", ESP_LOG_ERROR); esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE); esp_log_level_set("outbox", ESP_LOG_VERBOSE); - ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); @@ -327,7 +355,6 @@ void app_main(void) register_common_commands(); register_connect_commands(); register_publish_commands(); - esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl)); ESP_ERROR_CHECK(esp_console_start_repl(repl)); diff --git a/test/apps/publish_connect_test/main/publish_connect_test.h b/test/apps/publish_connect_test/main/publish_connect_test.h index c5311ec..0bf3053 100644 --- a/test/apps/publish_connect_test/main/publish_connect_test.h +++ b/test/apps/publish_connect_test/main/publish_connect_test.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -11,7 +11,7 @@ typedef enum {NONE, TCP, SSL, WS, WSS} transport_t; typedef struct { esp_mqtt_client_handle_t mqtt_client; - void * data; + void *data; } command_context_t; typedef struct { @@ -25,7 +25,7 @@ typedef struct { size_t expected_size; size_t nr_of_msg_received; size_t nr_of_msg_expected; - char * received_data; + char *received_data; } publish_context_t ; typedef struct { @@ -51,9 +51,9 @@ typedef struct { } publish_setup_args_t; void publish_init_flags(void); -void publish_setup(command_context_t * ctx, char const * transport); -void publish_teardown(command_context_t * ctx); -void publish_test(command_context_t * ctx, int expect_to_publish, int qos, bool enqueue); -void connection_test(command_context_t * ctx, const char *uri, int test_case); -void connect_setup(command_context_t * ctx); -void connect_teardown(command_context_t * ctx); +void publish_setup(command_context_t *ctx, char const *transport); +void publish_teardown(command_context_t *ctx); +void publish_test(command_context_t *ctx, int expect_to_publish, int qos, bool enqueue); +void connection_test(command_context_t *ctx, const char *uri, int test_case); +void connect_setup(command_context_t *ctx); +void connect_teardown(command_context_t *ctx); diff --git a/test/apps/publish_connect_test/main/publish_test.c b/test/apps/publish_connect_test/main/publish_test.c index fe373b6..c8e9692 100644 --- a/test/apps/publish_connect_test/main/publish_test.c +++ b/test/apps/publish_connect_test/main/publish_test.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ /* MQTT publish test This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -27,7 +32,8 @@ static EventGroupHandle_t mqtt_event_group; const static int CONNECTED_BIT = BIT0; #define CLIENT_ID_SUFFIX_SIZE 12 #if CONFIG_EXAMPLE_BROKER_CERTIFICATE_OVERRIDDEN == 1 -static const uint8_t mqtt_eclipseprojects_io_pem_start[] = "-----BEGIN CERTIFICATE-----\n" CONFIG_EXAMPLE_BROKER_CERTIFICATE_OVERRIDE "\n-----END CERTIFICATE-----"; +static const uint8_t mqtt_eclipseprojects_io_pem_start[] = "-----BEGIN CERTIFICATE-----\n" + CONFIG_EXAMPLE_BROKER_CERTIFICATE_OVERRIDE "\n-----END CERTIFICATE-----"; #else extern const uint8_t mqtt_eclipseprojects_io_pem_start[] asm("_binary_mqtt_eclipseprojects_io_pem_start"); #endif @@ -35,21 +41,23 @@ extern const uint8_t mqtt_eclipseprojects_io_pem_end[] asm("_binary_mqtt_eclip static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { - publish_context_t * test_data = handler_args; + publish_context_t *test_data = handler_args; esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; static int msg_id = 0; static int actual_len = 0; + switch (event->event_id) { case MQTT_EVENT_BEFORE_CONNECT: break; + case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); xEventGroupSetBits(mqtt_event_group, CONNECTED_BIT); msg_id = esp_mqtt_client_subscribe(client, test_data->subscribe_to, test_data->qos); ESP_LOGI(TAG, "sent subscribe successful %s , msg_id=%d", test_data->subscribe_to, msg_id); - break; + case MQTT_EVENT_DISCONNECTED: ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); break; @@ -57,58 +65,74 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_ case MQTT_EVENT_SUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_UNSUBSCRIBED: ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_PUBLISHED: ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; + case MQTT_EVENT_DATA: ESP_LOGI(TAG, "MQTT_EVENT_DATA"); ESP_LOGI(TAG, "TOPIC=%.*s", event->topic_len, event->topic); - ESP_LOGI(TAG, "ID=%d, total_len=%d, data_len=%d, current_data_offset=%d", event->msg_id, event->total_data_len, event->data_len, event->current_data_offset); - if (event->current_data_offset == 0) { - actual_len = event->data_len; - msg_id = event->msg_id; - if (event->total_data_len != test_data->expected_size) { - ESP_LOGE(TAG, "Incorrect message size: %d != %d", event->total_data_len, test_data->expected_size); - abort(); - } - } else { - actual_len += event->data_len; - // check consistency with msg_id across multiple data events for single msg - if (msg_id != event->msg_id) { - ESP_LOGE(TAG, "Wrong msg_id in chunked message %d != %d", msg_id, event->msg_id); - abort(); - } - } - if (event->current_data_offset + event->data_len > test_data->expected_size) { - ESP_LOGE(TAG, "Buffer overflow detected: offset %d + data_len %d > buffer size %d", event->current_data_offset, event->data_len, test_data->expected_size); - abort(); - } - if (memcmp(test_data->expected + event->current_data_offset, event->data, event->data_len) != 0) { - ESP_LOGE(TAG, "Data mismatch at offset %d: \n expected %.*s, \n got %.*s", event->current_data_offset, event->data_len, test_data->expected + event->current_data_offset, event->data_len, event->data); - abort(); - } + ESP_LOGI(TAG, "ID=%d, total_len=%d, data_len=%d, current_data_offset=%d", event->msg_id, event->total_data_len, + event->data_len, event->current_data_offset); + + if (event->current_data_offset == 0) { + actual_len = event->data_len; + msg_id = event->msg_id; + + if (event->total_data_len != test_data->expected_size) { + ESP_LOGE(TAG, "Incorrect message size: %d != %d", event->total_data_len, test_data->expected_size); + abort(); + } + } else { + actual_len += event->data_len; + + // check consistency with msg_id across multiple data events for single msg + if (msg_id != event->msg_id) { + ESP_LOGE(TAG, "Wrong msg_id in chunked message %d != %d", msg_id, event->msg_id); + abort(); + } + } + + if (event->current_data_offset + event->data_len > test_data->expected_size) { + ESP_LOGE(TAG, "Buffer overflow detected: offset %d + data_len %d > buffer size %d", event->current_data_offset, + event->data_len, test_data->expected_size); + abort(); + } + + if (memcmp(test_data->expected + event->current_data_offset, event->data, event->data_len) != 0) { + ESP_LOGE(TAG, "Data mismatch at offset %d: \n expected %.*s, \n got %.*s", event->current_data_offset, event->data_len, + test_data->expected + event->current_data_offset, event->data_len, event->data); + abort(); + } + + memcpy(test_data->received_data + event->current_data_offset, event->data, event->data_len); + + if (actual_len == event->total_data_len) { + if (0 == memcmp(test_data->received_data, test_data->expected, test_data->expected_size)) { + memset(test_data->received_data, 0, test_data->expected_size); + test_data->nr_of_msg_received++; + + if (test_data->nr_of_msg_received == test_data->nr_of_msg_expected) { + ESP_LOGI(TAG, "Correct pattern received exactly x times"); + ESP_LOGI(TAG, "Test finished correctly!"); + } + } else { + ESP_LOGE(TAG, "FAILED!"); + abort(); + } + } - memcpy(test_data->received_data + event->current_data_offset, event->data, event->data_len); - if (actual_len == event->total_data_len) { - if (0 == memcmp(test_data->received_data, test_data->expected, test_data->expected_size)) { - memset(test_data->received_data, 0, test_data->expected_size); - test_data->nr_of_msg_received++; - if (test_data->nr_of_msg_received == test_data->nr_of_msg_expected) { - ESP_LOGI(TAG, "Correct pattern received exactly x times"); - ESP_LOGI(TAG, "Test finished correctly!"); - } - } else { - ESP_LOGE(TAG, "FAILED!"); - abort(); - } - } break; + case MQTT_EVENT_ERROR: ESP_LOGE(TAG, "MQTT_EVENT_ERROR"); break; + default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; @@ -120,7 +144,7 @@ void test_init(void) ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size()); } -void pattern_setup(publish_context_t * test_data) +void pattern_setup(publish_context_t *test_data) { int pattern_size = strlen(test_data->pattern); free(test_data->expected); @@ -129,17 +153,20 @@ void pattern_setup(publish_context_t * test_data) test_data->expected_size = (size_t)(pattern_size) * test_data->pattern_repetitions; test_data->expected = malloc(test_data->expected_size); test_data->received_data = malloc(test_data->expected_size); + for (int i = 0; i < test_data->pattern_repetitions; i++) { memcpy(test_data->expected + (ptrdiff_t)(i * pattern_size), test_data->pattern, pattern_size); } + ESP_LOGI(TAG, "EXPECTED STRING %.*s, SIZE:%d", test_data->expected_size, test_data->expected, test_data->expected_size); } -static void configure_client(command_context_t * ctx, const char *transport) +static void configure_client(command_context_t *ctx, const char *transport) { - publish_context_t * test_data = ctx->data; + publish_context_t *test_data = ctx->data; ESP_LOGI(TAG, "Configuration"); transport_t selected_transport; + if (0 == strcmp(transport, "tcp")) { selected_transport = TCP; } else if (0 == strcmp(transport, "ssl")) { @@ -153,34 +180,40 @@ static void configure_client(command_context_t * ctx, const char *transport) abort(); } - if (selected_transport != test_data->selected_transport) { test_data->selected_transport = selected_transport; esp_mqtt_client_config_t config = {0}; + switch (selected_transport) { case NONE: break; + case TCP: ESP_LOGI(TAG, "[TCP transport] Startup.."); config.broker.address.uri = CONFIG_EXAMPLE_BROKER_TCP_URI; break; + case SSL: ESP_LOGI(TAG, "[SSL transport] Startup.."); config.broker.address.uri = CONFIG_EXAMPLE_BROKER_SSL_URI; break; + case WS: ESP_LOGI(TAG, "[WS transport] Startup.."); config.broker.address.uri = CONFIG_EXAMPLE_BROKER_WS_URI; break; + case WSS: ESP_LOGI(TAG, "[WSS transport] Startup.."); config.broker.address.uri = CONFIG_EXAMPLE_BROKER_WSS_URI; break; } + if (selected_transport == SSL || selected_transport == WSS) { ESP_LOGI(TAG, "Set certificate"); config.broker.verification.certificate = (const char *)mqtt_eclipseprojects_io_pem_start; } + // Generate a random client id for each iteration char client_id[CLIENT_ID_SUFFIX_SIZE] = {0}; snprintf(client_id, sizeof(client_id), "esp32-%08X", esp_random()); @@ -189,41 +222,47 @@ static void configure_client(command_context_t * ctx, const char *transport) } } -void publish_init_flags(void) { +void publish_init_flags(void) +{ mqtt_event_group = xEventGroupCreate(); } -void publish_setup(command_context_t * ctx, char const * const transport) { +void publish_setup(command_context_t *ctx, char const *const transport) +{ xEventGroupClearBits(mqtt_event_group, CONNECTED_BIT); - publish_context_t * data = (publish_context_t*)ctx->data; + publish_context_t *data = (publish_context_t *)ctx->data; pattern_setup(data); configure_client(ctx, transport); esp_mqtt_client_register_event(ctx->mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, data); } -void publish_teardown(command_context_t * ctx) +void publish_teardown(command_context_t *ctx) { esp_mqtt_client_unregister_event(ctx->mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler); } -void publish_test(command_context_t * ctx, int expect_to_publish, int qos, bool enqueue) +void publish_test(command_context_t *ctx, int expect_to_publish, int qos, bool enqueue) { - publish_context_t * data = (publish_context_t*)ctx->data; + publish_context_t *data = (publish_context_t *)ctx->data; data->nr_of_msg_expected = expect_to_publish; - ESP_LOGI(TAG, "PATTERN:%s REPEATED:%d PUBLISHED:%d", data->pattern, data->pattern_repetitions, data->nr_of_msg_expected); - + ESP_LOGI(TAG, "PATTERN:%s REPEATED:%d PUBLISHED:%d", data->pattern, data->pattern_repetitions, + data->nr_of_msg_expected); xEventGroupWaitBits(mqtt_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); + for (int i = 0; i < data->nr_of_msg_expected; i++) { int msg_id; + if (enqueue) { msg_id = esp_mqtt_client_enqueue(ctx->mqtt_client, data->publish_to, data->expected, data->expected_size, qos, 0, true); } else { msg_id = esp_mqtt_client_publish(ctx->mqtt_client, data->publish_to, data->expected, data->expected_size, qos, 0); - if(msg_id < 0) { + + if (msg_id < 0) { ESP_LOGE(TAG, "Failed to publish"); break; } } + ESP_LOGD(TAG, "Publishing msg_id=%d", msg_id); } } diff --git a/test/apps/publish_connect_test/pytest_mqtt_app.py b/test/apps/publish_connect_test/pytest_mqtt_app.py index 70da7eb..d2d1dd9 100644 --- a/test/apps/publish_connect_test/pytest_mqtt_app.py +++ b/test/apps/publish_connect_test/pytest_mqtt_app.py @@ -181,8 +181,8 @@ def connect_dut(dut: Dut, uri: str, case_id: int) -> Any: def run_cases(dut: Dut, uri: str, cases: Dict[str, int]) -> None: try: dut.write('init') - dut.write(f'start') - dut.write(f'disconnect') + dut.write('start') + dut.write('disconnect') for case in [ 'EXAMPLE_CONNECT_CASE_NO_CERT', 'EXAMPLE_CONNECT_CASE_SERVER_CERT', diff --git a/test/host/main/test_mqtt_client.cpp b/test/host/main/test_mqtt_client.cpp index a5dc05c..8333174 100644 --- a/test/host/main/test_mqtt_client.cpp +++ b/test/host/main/test_mqtt_client.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -25,7 +25,7 @@ extern "C" { #include "Mockqueue.h" #include "Mocktask.h" #if __has_include ("Mockidf_additions.h") -/* Some functions were moved from "task.h" to "idf_additions.h" */ + /* Some functions were moved from "task.h" to "idf_additions.h" */ #include "Mockidf_additions.h" #endif #include "Mockesp_timer.h" @@ -49,7 +49,8 @@ auto random_string(std::size_t n) return str; } -using unique_mqtt_client = std::unique_ptr < std::remove_pointer_t, decltype([](esp_mqtt_client_handle_t client) +using unique_mqtt_client = std::unique_ptr < std::remove_pointer_t, +decltype([](esp_mqtt_client_handle_t client) { esp_mqtt_client_destroy(client); }) >; @@ -118,15 +119,15 @@ SCENARIO("MQTT Client Operation") REQUIRE(res == ESP_FAIL); } } - SECTION("User set interface to use"){ + SECTION("User set interface to use") { http_parser_parse_url_ExpectAnyArgsAndReturn(0); http_parser_parse_url_ReturnThruPtr_u(&ret_uri); struct ifreq if_name = {}; strncpy(if_name.ifr_name, "custom", IFNAMSIZ - 1); if_name.ifr_name[IFNAMSIZ - 1] = '\0';; config.network.if_name = &if_name; - SECTION("Client is not started"){ - REQUIRE(esp_mqtt_set_config(client.get(), &config)== ESP_OK); + SECTION("Client is not started") { + REQUIRE(esp_mqtt_set_config(client.get(), &config) == ESP_OK); } } SECTION("After Start Client Is Cleanly destroyed") { @@ -143,15 +144,14 @@ SCENARIO("MQTT Client Operation") auto password = random_string(10); auto lw_topic = random_string(10); auto lw_msg = random_string(10); - config.broker = {.address = { .hostname = host.data(), - .path = path.data() + .path = path.data() } }; config.credentials = { .username = username.data(), - .client_id = client_id.data(), + .client_id = client_id.data(), .authentication = { .password = password.data() } @@ -159,13 +159,11 @@ SCENARIO("MQTT Client Operation") config.session = { .last_will { .topic = lw_topic.data(), - .msg = lw_msg.data() + .msg = lw_msg.data() } }; auto client = unique_mqtt_client{esp_mqtt_client_init(&config)}; REQUIRE(client != nullptr); - } } } -