feat(mqtt): add MQTT v5 client5 module with per-message properties - #678
Open
danielmeza wants to merge 14 commits into
Open
feat(mqtt): add MQTT v5 client5 module with per-message properties#678danielmeza wants to merge 14 commits into
danielmeza wants to merge 14 commits into
Conversation
Introduces a new `client5` module for MQTT 5.0 protocol support, including an `ErrorReasonCode` enum for MQTT5 error reason codes, conversion implementations, and utility methods. The new module is conditionally compiled with the `esp_idf_mqtt_protocol_5` feature.
Simplifies the conversion from mqtt5_error_reason_code to ErrorReasonCode by switching from TryFrom to From and returning UnspecifiedError for unknown codes. Updates EventProperty construction to include payload_format_indicator and removes unused ErrorType import.
This commit introduces support for MQTT v5 property configuration in subscribe, unsubscribe, and publish operations for both sync and async clients. It adds new methods to handle property configs, user properties, and message metadata, and refactors the client5 module to focus on user property handling. Deprecated or unused error reason code logic is removed, and trait implementations are updated to support the new property features.
Reworks the MQTT v5 client implementation to accept property configuration options as Option types for publish, subscribe, unsubscribe, and disconnect operations. Removes legacy publish_with_config and subscribe_with_config methods from the v3 client, and updates property handling to use new wrapper structs. Improves FFI safety and code clarity by encapsulating property conversions and updating event field extraction.
Replaces direct struct construction and From implementations for MQTT5 property configs with TryFrom<Option<T>> implementations, improving memory safety and lifetime management. Introduces internal ownership of C string buffers and user property lists to prevent premature drops, and ensures proper cleanup via Drop implementations. Adds logging for publish calls and updates method usage to leverage the new property config handling.
The RawCstrs instance in the TryFrom implementation for EspUnsubscribePropertyConfig no longer needs to be mutable, as it is not mutated after creation.
…APIs Replace the set-then-call pattern (esp_mqtt5_client_set_*_property + esp_mqtt_client_*) with the new single-call per-message APIs that bake the property config directly into the packet under the API lock, eliminating the cross-task race.
The `std` feature unconditionally enabled `embedded-svc/mqtt_protocol_v5`, which does not exist in the released embedded-svc 0.29.0, so every default build failed to resolve. Move it to a dedicated opt-in `mqtt_protocol_v5` feature (which implies `std`) and retarget the v5 cfg gates in `mqtt::client` and `mqtt::client5` at it, so default builds resolve against the released embedded-svc.
Cargo validates `dep/feature` references in the manifest even when the feature is not activated, so naming `embedded-svc/mqtt_protocol_v5` fails resolution against the published embedded-svc 0.29.0 regardless of gating. Add embedded-svc to the existing sibling-crate patch table, the same way esp-idf-sys and esp-idf-hal are already handled, so the crate resolves once esp-rs/embedded-svc#87 lands on master.
This was referenced Aug 16, 2026
SubscribePropertyConfig::share_name and UnsubscribePropertyConfig::share_name are `Option<&str>`, and a Rust &str is not NUL-terminated. Three sites passed `s.as_ptr()` straight into the C property structs, where mqtt5_msg_subscribe() and mqtt5_msg_unsubscribe() call strlen() on it and format it with %s, reading past the end of the string. esp_mqtt5_client_set_subscribe_property also stores the pointer rather than copying it, so the bytes have to outlive the call too. Route all three through the RawCstrs arena, as the subscribe property config already did, and keep the arena alive across the setter and the following subscribe/unsubscribe.
Collaborator
|
@danielmeza Sorry I was AFK the last couple of weeks. Will try to review in the next couple of days. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thank you for your contribution!
Replaces the closed #590 (
add-mqtt-v5-protocol), rebased onto currentmaster(0.52.1, which now includesMqtt5ConnectionPropertyConfigvia #659) and extended with atomic per-message publish/subscribe/unsubscribe property support.Submission Checklist 📝
cargo fmtcommand to ensure that all changed code is formatted correctly. —cargo fmt --checkpassescargo clippycommand to ensure that all changed code passes latest Clippy nightly lints. — cleanCHANGELOG.mdin the proper section. —### AddedPull Request Details 📖
Description
Adds
src/mqtt/client5.rs, implementing theembedded-svcMQTT v5 client traits (EspMqtt5Client,EspMqtt5Connection) from esp-rs/embedded-svc#87.The module is behind a new opt-in
mqtt_protocol_v5cargo feature (which impliesstd) and is additionally gated onesp_idf_mqtt_protocol_5, i.e.CONFIG_MQTT_PROTOCOL_5=yin sdkconfig. It is deliberately not part ofstd:embedded-svc0.29.0 as published does not carry themqtt_protocol_v5feature yet, so enabling it by default would break every build that resolvesembedded-svcfrom crates.io.Beyond the original #590, the last commits use the new atomic per-message IDF APIs (
esp_mqtt_client_publish5/subscribe5/unsubscribe5, espressif/esp-mqtt#343) The property config for a message is passed with that message and applied under the client's API lock, in place of anything staged viaesp_mqtt5_client_set_*_property— so there is no window in which another task can overwrite the properties between asetcall and thepublish, and no reliance on client-scoped state at all. That upstream PR also moved the property validation (share_name,topic_aliasagainst the server maximum,retain_handlerange) so it runs on the per-message path too, which backstops the cases this crate does not check itself.Why per-message properties, per the MQTT 5.0 specification
References are to MQTT Version 5.0, OASIS Standard, 07 March 2019; each link is a section anchor in that published HTML.
MQTT 5.0 carries these properties in the Variable Header of the individual packet:
Only §3.1.2.11 CONNECT Properties are connection-scoped — those remain in
MqttClientConfiguration::mqtt5_connection_propertyfrom #659.Concretely: §3.3.2.3.4 Topic Alias says "It is a Protocol Error to include the Topic Alias value more than once"; §3.3.2.3.3 Message Expiry Interval says "the Four Byte value is the lifetime of the Application Message in seconds"; and §3.3.2.3.7 User Property says "The Server MUST send all User Properties unaltered in a PUBLISH packet when forwarding the Application Message to a Client [MQTT-3.3.2-17]". All three describe one Application Message, so the API takes them per call.
Dependencies, and the current CI state
client5trait definitions and themqtt_protocol_v5feature.publish5/enqueue5/subscribe5/unsubscribe5entry points.CI is red until (1) lands on
embedded-svcmaster, and will go green with no further change here. Cargo validatesdep/featurereferences in a manifest even when the feature is not activated, so simply namingembedded-svc/mqtt_protocol_v5fails resolution against the publishedembedded-svc0.29.0 — making the feature opt-in is necessary but not sufficient. Following the convention already used in this repo foresp-idf-sysandesp-idf-hal,embedded-svcis added to[patch.crates-io]pointing atesp-rs/embedded-svcmaster; that entry should be dropped once anembedded-svcrelease carries the feature.Verified locally that the patch is the whole of the remaining gap: with the same patch entry pointed at the #87 branch, resolution succeeds.
Testing
cargo fmt --checkclean.cargo clippyclean.--features mqtt_protocol_v5andembedded-svcfrom Mqtt protocol v5 definition (rebased onto 0.29 + per-message properties) embedded-svc#87,cargo checkpasses forxtensa-esp32s3-espidfagainst ESP-IDF v5.5 withCONFIG_MQTT_PROTOCOL_5=yand the feat(mqtt5): add per-message publish/subscribe/unsubscribe property APIs (IDFGH-18158) espressif/esp-mqtt#343 changes applied.mqtt::client5and the v5 paths inmqtt::clientarecfg-ed out.