[PubSubClient3] Update to latest MQTT Client PubSubClient3

This commit is contained in:
TD-er
2026-05-11 21:32:09 +02:00
parent 76729ae905
commit 2f7f42b460
35 changed files with 1613 additions and 537 deletions
+105
View File
@@ -0,0 +1,105 @@
name: Bug Report
description: Create a report to help improve PubSubClient3
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
placeholder: Tell us what happened...
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Steps to reproduce the behavior
placeholder: |
1. Connect to '...'
2. Publish message to '...'
3. See error
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: input
id: version
attributes:
label: Library version
description: What version of PubSubClient3 are you running?
placeholder: v3.3.0
validations:
required: true
- type: dropdown
id: platform
attributes:
label: Platform
description: What platform are you using?
options:
- Arduino AVR
- ESP32
- ESP8266
- Other (specify in environment)
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: |
Provide details about your hardware and software environment, including:
- Board: (e.g., Arduino Uno, ESP12E, ESP32, NodeMCU, etc.)
- Framework Version: (e.g., Arduino AVR 5.2.0, ESP32 Arduino 3.3.2, ESP8266 Core 3.1.2 / 3.2.0-git)
- Development Environment: (e.g., Arduino IDE 2.3.6, PlatformIO Core 6.1.8)
- Platform: (if Other was selected above)
placeholder: |
- Board:
- Framework Version:
- Development Environment:
render: markdown
validations:
required: true
- type: textarea
id: example_code
attributes:
label: Minimal reproducible example (MVP)
description: Provide the smallest code/config and exact steps that reproduce the bug. Paste complete minimal sketch or commands.
placeholder: |
```cpp
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// minimal setup showing the bug
void setup() {
// ...
}
void loop() {
// ...
}
```
render: markdown
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output
render: shell
@@ -0,0 +1,32 @@
name: Feature Request
description: Suggest a new feature for PubSubClient3
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to suggest a new feature!
- type: textarea
id: reason
attributes:
label: Describe the reason of your feature request
description: A clear and concise description of what the problem is.
placeholder: I want to do [...]
validations:
required: true
- type: textarea
id: solution
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional context
description: Add any other context or code examples about the feature request here.
render: markdown
Vendored Regular → Executable
+1 -1
View File
@@ -29,7 +29,7 @@ ln -s $GITHUB_WORKSPACE $HOME/Arduino/libraries/CI_Test_Library
# Compile all *.ino files for the Arduino
for f in **/*.ino ; do
if [[ "$f" != *mqtt_esp8266.ino && "$f" != *mqtt_large_message.ino ]]; then
if [[ "$f" != *"mqtt_esp"* ]]; then
echo "################################################################"
echo "Arduino Uno compiling file ${f}"
arduino-cli compile -b arduino:avr:uno $f
Vendored Regular → Executable
+1 -1
View File
@@ -9,7 +9,7 @@ cd $GITHUB_WORKSPACE
#command -v clang-format >/dev/null 2>&1 || sudo apt-get -y install clang-format
# need Ubuntu clang-format version 19.1.1 (1ubuntu1~24.04.2)
# default Ubuntu clang-format version 18.1.3 (1ubuntu1) is not working
sudo apt-get -y install clang-format-19
#sudo apt-get -y install clang-format-19
# Check clang-format output
for f in **/*.{h,c,hpp,cpp,ino} ; do
#if [ -f "$f" ] && [[ "$f" != "tests/"* ]]; then
+7
View File
@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: weekly
time: "06:00"
View File
+103
View File
@@ -0,0 +1,103 @@
name: Compile examples
# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows
on:
push:
paths:
- ".github/workflows/compile-examples.yml"
- 'examples/**'
- 'src/**'
pull_request:
paths:
- ".github/workflows/compile-examples.yml"
- 'examples/**'
- 'src/**'
jobs:
build:
name: ${{ matrix.board.fqbn }}
runs-on: ubuntu-latest
env:
SKETCHES_REPORTS_PATH: sketches-reports
strategy:
fail-fast: false
matrix:
board:
- fqbn: arduino:avr:uno
platforms: |
- name: arduino:avr
libraries: ""
artifact-name-suffix: arduino-avr-uno
- fqbn: arduino:megaavr:uno2018
platforms: |
- name: arduino:megaavr
libraries: |
- name: WiFiNINA
artifact-name-suffix: arduino-megaavr-uno2018
- fqbn: arduino:samd:mkr1000
platforms: |
- name: arduino:samd
libraries: |
- name: WiFi101
artifact-name-suffix: arduino-samd-mkr1000
- fqbn: esp8266:esp8266:generic
platforms: |
- name: esp8266:esp8266
source-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json
libraries: ""
artifact-name-suffix: esp8266-esp8266-generic
- fqbn: "esp32:esp32:esp32"
platforms: |
- name: esp32:esp32
libraries: ""
artifact-name-suffix: esp32-esp32-esp32
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Download SRAM library
run: |
mkdir -p ./arduino-libraries
git -C ./arduino-libraries clone https://github.com/ennui2342/arduino-sram.git
- name: Filter examples
if: success()
run: |
if [[ "${{ matrix.board.fqbn }}" == esp32:esp32:* ]]; then
echo "Removing non-ESP32 specific examples"
rm -rf examples/mqtt_stream
fi
if [[ ! "${{ matrix.board.fqbn }}" == *:esp*:* ]]; then
echo "Removing ESP specific examples"
rm -rf examples/mqtt_esp*
fi
- name: Compile examples
uses: arduino/compile-sketches@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
fqbn: ${{ matrix.board.fqbn }}
platforms: ${{ matrix.board.platforms }}
libraries: |
# Install the library from the local path
- source-path: ./
- source-path: ./arduino-libraries/arduino-sram
# Install libraries from GitHub
#- source-url: https://github.com/ennui2342/arduino-sram
- name: Ethernet
${{ matrix.board.libraries }}
sketch-paths: |
- examples
enable-deltas-report: true
sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }}
- name: Save sketches report as workflow artifact
uses: actions/upload-artifact@v7
with:
if-no-files-found: error
path: ${{ env.SKETCHES_REPORTS_PATH }}
name: sketches-report-${{ matrix.board.artifact-name-suffix }}
+6 -7
View File
@@ -7,7 +7,7 @@ name: Generate Doxygen API and deploy content to GitHub Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["master"]
branches: ["release"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
@@ -34,12 +34,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Update Doxyfile PROJECT_NUMBER
run: bash .github/doxygen-update-version.sh
- name: Doxygenize
#uses: langroodi/doxygenize@v1.7.1 # waiting for update ...
uses: langroodi/doxygenize@6e920681c5d838e9a1b5cd273b814d1e4023b63d
uses: langroodi/doxygenize@v1.7.1
#uses: hmueller01/doxygenize@update-alpine-linux+patches
with:
# Doxygen configuration file path
@@ -54,12 +53,12 @@ jobs:
# run: |
# find ./site
- name: Setup Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@v6
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v5
with:
#path: '.' # Upload entire repository
path: './site'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
+28
View File
@@ -0,0 +1,28 @@
name: Lint
on:
push:
branches: [ "master" ]
paths:
- 'examples/**'
- 'src/**'
- 'tests/**'
pull_request:
branches: [ "master" ]
jobs:
run-checks:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install deps
# need Ubuntu clang-format version 19.1.1 (1ubuntu1~24.04.2)
# default Ubuntu clang-format version 18.1.3 (1ubuntu1) is not working
run: |
sudo apt-get update
sudo apt-get install -y clang-format-19
- name: Check clang-format conformity
run: bash .github/clang-lint.sh
@@ -0,0 +1,24 @@
name: Report Size Deltas
# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows
on:
push:
paths:
- ".github/workflows/report-size-deltas.yml"
schedule:
# Run at the minimum interval allowed by GitHub Actions.
# Note: GitHub Actions periodically has outages which result in workflow failures.
# In this event, the workflows will start passing again once the service recovers.
- cron: "*/5 * * * *"
workflow_dispatch:
repository_dispatch:
jobs:
report:
runs-on: ubuntu-latest
steps:
- name: Comment size deltas reports to PRs
uses: arduino/report-size-deltas@v1
with:
# Regex matching the names of the workflow artifacts created by the "Compile Examples" workflow
sketches-reports-source: ^sketches-report-.+
@@ -1,4 +1,4 @@
name: build
name: Unit tests
on:
push:
@@ -11,14 +11,11 @@ on:
branches: [ "master" ]
jobs:
build:
run-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Check clang-format conformity
run: bash .github/clang-lint.sh
uses: actions/checkout@v6
- name: Build tests
# Build / Execute tests defined in tests folder
@@ -31,5 +28,5 @@ jobs:
#run: make test
run: bin/connect_spec && bin/publish_spec && bin/receive_spec && bin/subscribe_spec
- name: Build on Arduino CLI
run: bash .github/build-arduino.sh
# - name: Build on Arduino CLI
# run: bash .github/build-arduino.sh
+5 -3
View File
@@ -1,6 +1,8 @@
tests/bin
.pioenvs
.piolibdeps
.clang_complete
.gcc-flags.json
.pio/
.pioenvs
.piolibdeps
.vscode/
site/api
tests/bin
+192
View File
@@ -0,0 +1,192 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to
[Semantic Versioning](https://semver.org/).
## Unreleased
### Changed
* Improved test coverage for all QoS levels (0, 1, 2)
* Increased robustness and correctness in client-side handling of QoS 1 and QoS 2 messages (proper handling of PUBACK, PUBREC, PUBREL, PUBCOMP sequences)
* Merged and cleaned up tests to ensure MQTT protocol compliance for all QoS scenarios
## [3.3.0] - 2025-12-14
### Added
* Speedup publish large messages by using a buffer by @TD-er in [#59](https://github.com/hmueller01/pubsubclient3/pull/59)
* Support `PROGMEM` and `__FlashStringHelper` topics by @hmueller01 in [#72](https://github.com/hmueller01/pubsubclient3/pull/72)
* Added issue template by @hmueller01 in [#76](https://github.com/hmueller01/pubsubclient3/pull/76)
* Report delta sizes in pull requests by @hmueller01 in [#77](https://github.com/hmueller01/pubsubclient3/pull/77)
### Changed
* Refactored private members by @hmueller01 in [#68](https://github.com/hmueller01/pubsubclient3/pull/68)
* Refactored private functions by @hmueller01 in [#70](https://github.com/hmueller01/pubsubclient3/pull/70)
* Do not crash if `_client` not set by @hmueller01 in [#69](https://github.com/hmueller01/pubsubclient3/pull/69)
* Minor updates that come across by @hmueller01 in [#73](https://github.com/hmueller01/pubsubclient3/pull/73)
* Inline optimisation by @hmueller01 in [#75](https://github.com/hmueller01/pubsubclient3/pull/75)
* Bump actions/upload-pages-artifact from 3 to 4 by @dependabot[bot] in [#78](https://github.com/hmueller01/pubsubclient3/pull/78)
* Bump actions/checkout from 2 to 5 by @dependabot[bot] in [#79](https://github.com/hmueller01/pubsubclient3/pull/79)
* Tag unused parameter to avoid warnings by @hmueller01 in [#82](https://github.com/hmueller01/pubsubclient3/pull/82)
* Fix compile warning at strnlen bound by @hmueller01 in [#83](https://github.com/hmueller01/pubsubclient3/pull/83)
* Fix deprecated `BUILTIN_LED` by @hmueller01 in [#84](https://github.com/hmueller01/pubsubclient3/pull/84)
* Bump actions/checkout from 5 to 6 by @dependabot[bot] in [#85](https://github.com/hmueller01/pubsubclient3/pull/85)
* Update workflows by @hmueller01 in [#86](https://github.com/hmueller01/pubsubclient3/pull/86)
## [3.2.1] - 2025-09-11
### Fixed
- Fixed bug introduced with QoS &gt; 0 support (#65)
## [3.2.0] - 2025-07-20
### Added
- Support publish at QoS 1 and 2 (by @hmueller01) (#49)
- Doxygen API documentation on gh-pages (by @hmueller01) (#51)
### Changed
- Add check for nullptr in `CHECK_STRING_LENGTH` (by @TD-er) (#48)
- Move `CHECK_STRING_LENGTH` to `PubSubClient.cpp` (by @hmueller01) (#50)
- Get rid of lazy calling parameter (by @hmueller01) (#53)
- Implemented `writeNextMsgId()` (by @hmueller01) (#54)
- Cleanup `writeString()` (by @hmueller01) (#55)
## [3.1.0] - 2025-03-20
### Added
- Added `ERROR_PSC_PRINTF` and `ERROR_PSC_PRINTF_P` outputs (#25)
### Changed
- Fix `setServer(domain, ...)` by copying domain string to an internal buffer (prevent potential dangling pointer) (#10)
- Connect to broker only if port != 0 (protect against incorrect initialization) (#10)
- Refactored buffer length types to `size_t` (#11)
- Added warnings and `-Werror` to tests Makefile (#11)
- Reformatted tests code using Google style (see `.clang-format`) (#11)
- Fix keepalive handling (thanks to @uschnindler) (#14)
- Use initializer lists for constructors (#15)
- Fix `DEBUG_PSC_PRINTF` outputs (#18)
- Introduce `MQTTRETAINED` flag (#21)
- Added documentation on `readByte()` and `readPacket()` (#23)
- Refactored `buildHeader()`, `readPacket()`, `readByte()`, `beginPublish()`/`endPublish()`, `publish()`/`publish_P()`, `connect()`, `connected()`, `subscribe()`/`unsubscribe()` and internal timing handling (#22, #23, #28, #30, #31, #33, #34, #37, #38, #39, #40, #44)
- Fixed potential memory corruption in `MQTTPUBLISH` callback preparation (#25)
- Fixed a missing failure test on `_client->read()` in `readByte()` (#32)
- Switch from constructor init-lists to class member initialization (C++ core guideline C.45) (#42)
## [3.0.2] - 2025-02-27
### Added
- GitHub workflow to execute tests
- GitHub workflow to compile examples using Arduino CLI
- Examples: Use a better source of randomness (#9)
- Optionally deactivate `std::function` usage (#7)
## [3.0.1] - 2025-02-20
### Added
- Added `.editorconfig`
- Added GitHub workflow to check code style on push/PR
### Changed
- Reformatted code using Google style
- Fixes for tests: missing def for `strlen_P` & Python libs
## [3.0] - 2025-02-17
Maintenance taken over by Holger Mueller
### Added
- In-source documentation added to `PubSubClient` header
- Add flag for enabling debugging of library
### Changed
- Always use `PubSubClient()` constructor
- Use `bool` instead of `boolean`
- Add `yield()` calls in `connect()` and `write()` to avoid watchdog resets
- Fix bug in `publish_P` handling of PROGMEM payload length
- Fix increase `bytesToWrite` to `uint16_t` to prevent overflow
- Remove compiler warning about unsigned `expectedLength`
- Extend usage of `std::function` where available
- Fix for keep alive zero
## [2.8] - 2020-05-20
### Added
- `setBufferSize()` to override `MQTT_MAX_PACKET_SIZE`
- `setKeepAlive()` to override `MQTT_KEEPALIVE`
- `setSocketTimeout()` to override `MQTT_SOCKET_TIMEOUT`
- Check to prevent subscribe/unsubscribe to empty topics
- Declare WiFi mode prior to connect in ESP example
- Use `strnlen` to avoid overruns
- Support pre-connected `Client` objects
## [2.7] - 2018-11-02
### Added
- Added large-payload API: `beginPublish`/`write`/`publish`/`endPublish`
- Added `yield` calls to improve reliability on ESP
- Added Clean Session flag to connect options
- Added ESP32 support for functional callback signature
### Fixed
- Fixed remaining-length handling to prevent buffer overrun
## [2.4] - 2015-11-21
### Added
- `MQTT_SOCKET_TIMEOUT` to avoid blocking indefinitely while waiting for inbound data
### Fixed
- Fixed return code when publishing &gt;256 bytes
## [2.3] - 2015-09-11
### Added
- `publish(topic,payload,retained)` convenience overload
## [2.2] - 2015-09-07
### Changed
- Changed code layout to match Arduino Library requirements
## [2.1] - 2015-09-07
### Added
- `MAX_TRANSFER_SIZE` to chunk messages if needed
- Reject topic/payloads that exceed `MQTT_MAX_PACKET_SIZE`
## [2.0] - 2015-08-28
### Added
- MQTT 3.1.1 support
- Fix PROGMEM handling for Intel Galileo/ESP8266
- Overloaded constructors for convenience
- Chainable setters for server/callback/client/stream
- `state()` function to return CONNACK code
## [1.0] - 2009-02-02
- Initial release notes: QoS 0 only, 128 byte max message size, keepalive 30s, no Will messages
-136
View File
@@ -1,136 +0,0 @@
3.1.0
* Fix setServer(domain, ...) by copy domain string to own buffer (prevent potential dangling string pointer) #10
* Connect to broker only if port != 0 (e.g. in case of incorrect PubSubClient initialization) #10
* Refactored different buffer length types to size_t #11
* Added a bunch of warnings and -Werror to tests Makefile #11
* Reformatted tests code using Google sytle (see .clang-format) #11
* Fix keepalive handling, thanks to @uschnindler providing this to thingsboard #14
* Use initializer lists instead of assigning global members in constructor #15
* Fix DEBUG_PSC_PRINTF outputs #18
* Added ERROR_PSC_PRINTF and ERROR_PSC_PRINTF_P outputs #25
* Introduce MQTTRETAINED flag instead of hard coding #21
* Added documentation on readByte() and readPacket() functions #23
* Refactored buildHeader() (change return type, use MQTT_MAX_HEADER_SIZE, locals) #22, #34, #37
* Refactored readPacket() (changed return type to size_t, used MQTT_MAX_HEADER_SIZE, locals) #23
* Refactored readByte() #23
* Refactored beginPublish() and endPublish() #28
* Refactored publish() #31
* Refactored publish_P() #30
* Refactored connect() #40
* Refactored connected() (mostly rewritten and simplified, added setting pingOutstanding, don't need flush()) #44
* Refactored subscribe() and unsubscribe() #44
* Refactored internal socketTimeout and keepAlive to millis to improve performance #33, #38, #39
* Fix potential memory corruption in MQTTPUBLISH callback preparation #25
* Fix potential error in readByte() (missing failure test on _client->read()) #32
* Switch from constructor init-lists to class member initialization, see C++ core guideline C.45 #42
3.0.2
* Added github workflow to execute tests
* Added github workflow to compile examples using arduino-cli
* Examples: Use a better source of random #9
* Deactivate use of std::function on demand #7
3.0.1
* Reformatted code using Google sytle (see .clang-format)
* Added .editorconfig
* Added github workflow to check code sytle on push and pull request
* Fix-tests: missing def for strlen_P & Python libs
3.0
* Maintenance taken over by Holger Mueller
* Adds in-source documentation to PubSubClient header.
* Add flag for enabling debugging of library
* Always use PubSubClient() constructor
* Use bool instead of boolean
* Add yield() calls in connect() and write() to avoid wdt resets if either blocks for too long
* Fix bug in publish_P which will always treat the payload length as 0 when used with PROGMEM
* Fix increase `bytesToWrite` to uint16_t to prevent overflow
* Remove compiler warning `expectedLength` should be unsigned
* Extend usage of std::function on all the platforms where it's available
* Fix for keep alive zero
2.8
* Add setBufferSize() to override MQTT_MAX_PACKET_SIZE
* Add setKeepAlive() to override MQTT_KEEPALIVE
* Add setSocketTimeout() to overide MQTT_SOCKET_TIMEOUT
* Added check to prevent subscribe/unsubscribe to empty topics
* Declare wifi mode prior to connect in ESP example
* Use `strnlen` to avoid overruns
* Support pre-connected Client objects
2.7
* Fix remaining-length handling to prevent buffer overrun
* Add large-payload API - beginPublish/write/publish/endPublish
* Add yield call to improve reliability on ESP
* Add Clean Session flag to connect options
* Add ESP32 support for functional callback signature
* Various other fixes
2.4
* Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely
whilst waiting for inbound data
* Fixed return code when publishing >256 bytes
2.3
* Add publish(topic,payload,retained) function
2.2
* Change code layout to match Arduino Library reqs
2.1
* Add MAX_TRANSFER_SIZE def to chunk messages if needed
* Reject topic/payloads that exceed MQTT_MAX_PACKET_SIZE
2.0
* Add (and default to) MQTT 3.1.1 support
* Fix PROGMEM handling for Intel Galileo/ESP8266
* Add overloaded constructors for convenience
* Add chainable setters for server/callback/client/stream
* Add state function to return connack return code
1.9
* Do not split MQTT packets over multiple calls to _client->write()
* API change: All constructors now require an instance of Client
to be passed in.
* Fixed example to match 1.8 api changes - dpslwk
* Added username/password support - WilHall
* Added publish_P - publishes messages from PROGMEM - jobytaffey
1.8
* KeepAlive interval is configurable in PubSubClient.h
* Maximum packet size is configurable in PubSubClient.h
* API change: Return boolean rather than int from various functions
* API change: Length parameter in message callback changed
from int to unsigned int
* Various internal tidy-ups around types
1.7
* Improved keepalive handling
* Updated to the Arduino-1.0 API
1.6
* Added the ability to publish a retained message
1.5
* Added default constructor
* Fixed compile error when used with arduino-0021 or later
1.4
* Fixed connection lost handling
1.3
* Fixed packet reading bug in PubSubClient.readPacket
1.2
* Fixed compile error when used with arduino-0016 or later
1.1
* Reduced size of library
* Added support for Will messages
* Clarified licensing - see LICENSE.txt
1.0
* Only Quality of Service (QOS) 0 messaging is supported
* The maximum message size, including header, is 128 bytes
* The keepalive interval is set to 30 seconds
* No support for Will messages
Regular → Executable
View File
+17 -3
View File
@@ -29,7 +29,7 @@ Full API documentation is available here: https://hmueller01.github.io/pubsubcli
## Limitations
- The client is based on the [MQTT Version 3.1.1 specification](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html) with some limitations.
- It can publish at QoS 0, 1 or 2.
- It can publish at QoS 0 and since [v3.2.0](https://github.com/hmueller01/pubsubclient3/releases/tag/v3.2.0) also at QoS 1 or 2.
**WARNING:** No retransmission is supported to keep the library as much memory friendly as possible.
@@ -45,7 +45,17 @@ Full API documentation is available here: https://hmueller01.github.io/pubsubcli
`PubSubClient::setKeepAlive(keepAlive)`.
- The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by
changing value of `MQTT_VERSION` in `PubSubClient.h`.
- Since [v3.3.0](https://github.com/hmueller01/pubsubclient3/releases/tag/v3.3.0) it can publish and subscribe to `PROGMEM` or `__FlashStringHelper` topics.
Details see [mqtt_progmem](https://github.com/hmueller01/pubsubclient3/blob/aae84e4d1aa65e752e19e30239b5796b4fe2705b/examples/mqtt_progmem/src/mqtt_progmem.cpp#L39-L48) example.
But if you like to publish `PROGMEM` topics you have to use
```c
const char TOPIC[] PROGMEM = "test";
const char HELLO_WORLD[] PROGMEM = "hello world";
client.beginPublish_P(TOPIC, strlen_P(HELLO_WORLD), MQTT_QOS0, false);
client.write_P(HELLO_WORLD);
client.endPublish();
```
as `client.publish_P(...)` is already used for `PROGMEM` payloads.
## Compatible Hardware
@@ -69,6 +79,10 @@ The library cannot currently be used with hardware based on the ENC28J60 chip
such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an
[alternative library](https://github.com/njh/NanodeMQTT) available.
## Changelog
See [CHANGELOG.md](CHANGELOG.md)
## License
This code is released under the MIT License.
This code is released under the [MIT License](LICENSE.txt).
+3 -1
View File
@@ -11,12 +11,14 @@
#include <PubSubClient.h>
#include <SPI.h>
#define _UNUSED_ __attribute__((unused))
// Update these with values suitable for your network.
byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED};
IPAddress ip(172, 16, 0, 100);
IPAddress server(172, 16, 0, 2);
void callback(char* topic, uint8_t* payload, size_t plength) {
void callback(_UNUSED_ char* topic, _UNUSED_ uint8_t* payload, _UNUSED_ size_t plength) {
// handle message arrived
}
View File
@@ -23,7 +23,7 @@
#elif defined(ESP32)
#include <WiFi.h>
#include <esp_random.h>
#define BUILTIN_LED A0
#define LED_BUILTIN A0
#define RANDOM_REG32 esp_random()
#else
#error Platform not supported.
@@ -77,11 +77,11 @@ void callback(char* topic, uint8_t* payload, size_t plength) {
// Switch on the LED if an 1 was received as first character
if ((char)payload[0] == '1') {
digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level
digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level
// but actually the LED is on; this is because
// it is active low on the ESP-01)
} else {
digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH
digitalWrite(LED_BUILTIN, HIGH); // Turn the LED off by making the voltage HIGH
}
}
@@ -110,7 +110,7 @@ void reconnect() {
}
void setup() {
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
@@ -127,7 +127,7 @@ void loop() {
if (now - lastMsg > 2000) {
lastMsg = now;
++value;
snprintf(msg, MSG_BUFFER_SIZE, "hello world #%ld", value);
snprintf(msg, MSG_BUFFER_SIZE, "hello world #%d", value);
Serial.print("Publish message: ");
Serial.println(msg);
client.publish("outTopic", msg);
@@ -28,7 +28,6 @@
#elif defined(ESP32)
#include <WiFi.h>
#include <esp_random.h>
#define BUILTIN_LED A0
#define RANDOM_REG32 esp_random()
#else
#error Platform not supported.
@@ -170,7 +169,6 @@ void reconnect() {
}
void setup() {
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
@@ -0,0 +1,3 @@
// Example sketch showing how to use PubSubClient with strings stored in PROGMEM.
// This needs to be an empty file to satisfy the arduino-cli build system.
// See src/mqtt_progmem.cpp for the actual example code.
@@ -0,0 +1,40 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
; https://docs.platformio.org/en/latest/boards/atmelavr/uno.html
[platformio]
description = Basic MQTT example with Authentication and Progmem strings
[env]
framework = arduino
lib_deps =
arduino-libraries/Ethernet @ ^2.0.2
hmueller01/PubSubClient3 @ ^3.3.0
build_flags =
; -D DEBUG_ESP_PORT=Serial
; -D DEBUG_PUBSUBCLIENT
; -D MQTT_MAX_PACKET_SIZE=512
; -D MQTT_KEEPALIVE=120
[env:uno]
platform = atmelavr
board = uno
[env:esp8266]
platform = espressif8266
platform_packages = platformio/framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git
board = esp12e
build_flags =
${env.build_flags}
-D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
[env:esp32]
platform = espressif32
board = esp32dev
@@ -0,0 +1,54 @@
/*
Basic MQTT example with Authentication
- connects to an MQTT server, providing username
and password
- publishes "hello world" to the topic "outTopic"
- subscribes to the topic "inTopic"
*/
#include <Ethernet.h>
#include <PubSubClient.h>
#include <SPI.h>
#define _UNUSED_ __attribute__((unused))
// Update these with values suitable for your network.
byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED};
IPAddress ip(172, 16, 0, 100);
IPAddress server(172, 16, 0, 2);
const char HELLO_WORLD_3[] PROGMEM = "hello world 3";
const char HELLO_WORLD_4[] PROGMEM = "hello world 4";
const char SUBSCRIBE_TOPIC[] PROGMEM = "inTopic1";
void callback(_UNUSED_ char* topic, _UNUSED_ uint8_t* payload, _UNUSED_ size_t plength) {
// handle message arrived
}
EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);
void setup() {
Ethernet.begin(mac, ip);
// Note - the default maximum packet size is 128 bytes. If the
// combined length of clientId, username and password exceed this use the
// following to increase the buffer size:
// client.setBufferSize(255);
if (client.connect("arduinoClient", "testuser", "testpass")) {
client.publish(F("outTopic"), "hello world 1", MQTT_QOS0, false);
client.publish(F("outTopic"), F("hello world 2"), MQTT_QOS1, false);
client.publish_P(F("outTopic"), HELLO_WORLD_3, MQTT_QOS2, false);
client.beginPublish(F("outTopic"), strlen_P(HELLO_WORLD_4), MQTT_QOS1, false);
client.write_P(HELLO_WORLD_4);
client.endPublish();
client.subscribe(F("inTopic"));
client.subscribe_P(SUBSCRIBE_TOPIC, MQTT_QOS1);
}
}
void loop() {
client.loop();
}
@@ -17,6 +17,8 @@
#include <PubSubClient.h>
#include <SPI.h>
#define _UNUSED_ __attribute__((unused))
// Update these with values suitable for your network.
byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED};
IPAddress ip(172, 16, 0, 100);
@@ -29,7 +31,7 @@ EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);
// Callback function
void callback(char* topic, uint8_t* payload, size_t plength) {
void callback(_UNUSED_ char* topic, uint8_t* payload, size_t plength) {
// In order to republish this payload, a copy must be made
// as the orignal payload buffer will be overwritten whilst
// constructing the PUBLISH packet.
@@ -11,12 +11,14 @@
#include <PubSubClient.h>
#include <SPI.h>
#define _UNUSED_ __attribute__((unused))
// Update these with values suitable for your hardware/network.
byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED};
IPAddress ip(172, 16, 0, 100);
IPAddress server(172, 16, 0, 2);
void callback(char* topic, uint8_t* payload, size_t plength) {
void callback(_UNUSED_ char* topic, _UNUSED_ uint8_t* payload, _UNUSED_ size_t plength) {
// handle message arrived
}
@@ -14,6 +14,8 @@
#include <SPI.h>
#include <SRAM.h>
#define _UNUSED_ __attribute__((unused))
// Update these with values suitable for your network.
byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED};
IPAddress ip(172, 16, 0, 100);
@@ -21,7 +23,7 @@ IPAddress server(172, 16, 0, 2);
SRAM sram(4, SRAM_1024);
void callback(char* topic, uint8_t* payload, size_t plength) {
void callback(_UNUSED_ char* topic, _UNUSED_ uint8_t* payload, size_t plength) {
sram.seek(1);
// do something with the message
Regular → Executable
+3
View File
@@ -17,9 +17,12 @@ disconnect KEYWORD2
publish KEYWORD2
publish_P KEYWORD2
beginPublish KEYWORD2
beginPublish_P KEYWORD2
endPublish KEYWORD2
write KEYWORD2
write_P KEYWORD2
subscribe KEYWORD2
subscribe_P KEYWORD2
unsubscribe KEYWORD2
loop KEYWORD2
connected KEYWORD2
+3 -1
View File
@@ -6,13 +6,15 @@
"type": "git",
"url": "https://github.com/hmueller01/pubsubclient3.git"
},
"version": "3.1.0",
"version": "3.3.0",
"license": "MIT",
"exclude": "tests",
"examples": "examples/*/*.ino",
"frameworks": "arduino",
"platforms": [
"atmelavr",
"atmelsam",
"atmelmegaavr",
"espressif8266",
"espressif32"
]
+1 -1
View File
@@ -1,5 +1,5 @@
name=PubSubClient3
version=3.1.0
version=3.3.0
author=Nick O'Leary <nick.oleary@gmail.com>
maintainer=Holger Mueller <github@euhm.de>
license=MIT
+359 -312
View File
File diff suppressed because it is too large Load Diff
+272 -45
View File
@@ -34,7 +34,7 @@
* @brief Maximum packet size defined by MQTT protocol.
*/
#ifndef MQTT_MAX_POSSIBLE_PACKET_SIZE
#define MQTT_MAX_POSSIBLE_PACKET_SIZE 268435455
#define MQTT_MAX_POSSIBLE_PACKET_SIZE ((size_t)268435455) // might be limited to 65535 if size_t is 16-bit (unsigned int)
#endif
/**
@@ -176,36 +176,39 @@
class PubSubClient : public Print {
private:
Client* _client{};
uint8_t* buffer{};
size_t bufferSize{};
unsigned long keepAliveMillis{};
unsigned long socketTimeoutMillis{};
uint16_t nextMsgId{};
unsigned long lastOutActivity{};
unsigned long lastInActivity{};
bool pingOutstanding{};
MQTT_CALLBACK_SIGNATURE{};
IPAddress ip{};
char* domain{};
uint16_t port{};
Stream* stream{};
int _state{MQTT_DISCONNECTED};
uint8_t* _buffer{};
size_t _bufferSize{};
size_t _bufferWritePos{};
uint8_t _qos{MQTT_QOS0};
unsigned long _keepAliveMillis{};
unsigned long _socketTimeoutMillis{};
uint16_t _nextMsgId{};
unsigned long _lastOutActivity{};
unsigned long _lastInActivity{};
bool _pingOutstanding{};
MQTT_CALLBACK_SIGNATURE{};
IPAddress _ip{};
char* _domain{};
uint16_t _port{};
Stream* _stream{};
int _state{MQTT_DISCONNECTED};
size_t readPacket(uint8_t* hdrLen);
bool handlePacket(uint8_t hdrLen, size_t len);
bool readByte(uint8_t* result);
bool readByte(uint8_t* result, size_t* pos);
uint8_t buildHeader(uint8_t header, uint8_t* buf, size_t length);
bool write(uint8_t header, uint8_t* buf, size_t length);
size_t writeString(const char* string, uint8_t* buf, size_t pos, size_t size);
size_t writeNextMsgId(uint8_t* buf, size_t pos, size_t size);
uint8_t buildHeader(uint8_t header, size_t length);
bool writeControlPacket(uint8_t header, size_t length);
size_t writeBuffer(size_t pos, size_t size);
size_t writeStringImpl(bool progmem, const char* string, size_t pos);
size_t writeString(const char* string, size_t pos);
size_t writeNextMsgId(size_t pos);
bool beginPublishImpl(bool progmem, const char* topic, size_t plength, uint8_t qos, bool retained);
bool subscribeImpl(bool progmem, const char* topic, uint8_t qos);
bool unsubscribeImpl(bool progmem, const char* topic);
// Add to buffer and flush if full (only to be used with beginPublish/endPublish)Add commentMore actions
// Add to buffer and flush if full (only to be used with beginPublish/endPublish)
size_t appendBuffer(uint8_t data);
size_t appendBuffer(const uint8_t *data, size_t size);
size_t flushBuffer();
public:
@@ -428,7 +431,9 @@ class PubSubClient : public Print {
* @return true If client succeeded in establishing a connection to the broker.
* false If client failed to establish a connection to the broker.
*/
bool connect(const char* id);
inline bool connect(const char* id) {
return connect(id, nullptr, nullptr, nullptr, MQTT_QOS0, false, nullptr, true);
}
/**
* @brief Connects the client using a clean session with username and password.
@@ -440,7 +445,9 @@ class PubSubClient : public Print {
* @return true If client succeeded in establishing a connection to the broker.
* false If client failed to establish a connection to the broker.
*/
bool connect(const char* id, const char* user, const char* pass);
inline bool connect(const char* id, const char* user, const char* pass) {
return connect(id, user, pass, nullptr, MQTT_QOS0, false, nullptr, true);
}
/**
* @brief Connects the client using a clean session and will.
@@ -453,7 +460,9 @@ class PubSubClient : public Print {
* @return true If client succeeded in establishing a connection to the broker.
* false If client failed to establish a connection to the broker.
*/
bool connect(const char* id, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage);
inline bool connect(const char* id, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage) {
return connect(id, nullptr, nullptr, willTopic, willQos, willRetain, willMessage, true);
}
/**
* @brief Connects the client using a clean session with username, password and will.
@@ -470,7 +479,10 @@ class PubSubClient : public Print {
* @return true If client succeeded in establishing a connection to the broker.
* false If client failed to establish a connection to the broker.
*/
bool connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage);
inline bool connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, bool willRetain,
const char* willMessage) {
return connect(id, user, pass, willTopic, willQos, willRetain, willMessage, true);
}
/**
* @brief Connects the client with all possible parameters (user, password, will and session).
@@ -503,7 +515,9 @@ class PubSubClient : public Print {
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish(const char* topic, const char* payload);
inline bool publish(const char* topic, const char* payload) {
return publish(topic, payload, MQTT_QOS0, false);
}
/**
* @brief Publishes a message to the specified topic using QoS 0.
@@ -513,7 +527,9 @@ class PubSubClient : public Print {
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish(const char* topic, const char* payload, bool retained);
inline bool publish(const char* topic, const char* payload, bool retained) {
return publish(topic, payload, MQTT_QOS0, retained);
}
/**
* @brief Publishes a message to the specified topic.
@@ -524,7 +540,35 @@ class PubSubClient : public Print {
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish(const char* topic, const char* payload, uint8_t qos, bool retained);
inline bool publish(const char* topic, const char* payload, uint8_t qos, bool retained) {
return publish(topic, reinterpret_cast<const uint8_t*>(payload), payload ? strlen(payload) : 0, qos, retained);
}
/**
* @brief Publishes a message to the specified topic.
* @param topic The topic from __FlashStringHelper to publish to.
* @param payload The message to publish.
* @param qos The quality of service (\ref group_qos) to publish at. [0, 1, 2].
* @param retained Publish the message with the retain flag.
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
inline bool publish(const __FlashStringHelper* topic, const char* payload, uint8_t qos, bool retained) {
return publish(topic, reinterpret_cast<const uint8_t*>(payload), payload ? strlen(payload) : 0, qos, retained);
}
/**
* @brief Publishes a message from __FlashStringHelper to the specified topic from __FlashStringHelper.
* @param topic The topic to publish to.
* @param payload The message to publish.
* @param qos The quality of service (\ref group_qos) to publish at. [0, 1, 2].
* @param retained Publish the message with the retain flag.
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
inline bool publish(const __FlashStringHelper* topic, const __FlashStringHelper* payload, uint8_t qos, bool retained) {
return publish_P(topic, reinterpret_cast<const uint8_t*>(payload), payload ? strlen_P(reinterpret_cast<const char*>(payload)) : 0, qos, retained);
}
/**
* @brief Publishes a non retained message to the specified topic using QoS 0.
@@ -534,7 +578,9 @@ class PubSubClient : public Print {
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish(const char* topic, const uint8_t* payload, size_t plength);
inline bool publish(const char* topic, const uint8_t* payload, size_t plength) {
return publish(topic, payload, plength, MQTT_QOS0, false);
}
/**
* @brief Publishes a message to the specified topic using QoS 0.
@@ -545,7 +591,9 @@ class PubSubClient : public Print {
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish(const char* topic, const uint8_t* payload, size_t plength, bool retained);
inline bool publish(const char* topic, const uint8_t* payload, size_t plength, bool retained) {
return publish(topic, payload, plength, MQTT_QOS0, retained);
}
/**
* @brief Publishes a message to the specified topic.
@@ -559,6 +607,18 @@ class PubSubClient : public Print {
*/
bool publish(const char* topic, const uint8_t* payload, size_t plength, uint8_t qos, bool retained);
/**
* @brief Publishes a message to the specified topic.
* @param topic The topic from __FlashStringHelper to publish to.
* @param payload The message to publish.
* @param plength The length of the payload.
* @param qos The quality of service (\ref group_qos) to publish at. [0, 1, 2].
* @param retained Publish the message with the retain flag.
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish(const __FlashStringHelper* topic, const uint8_t* payload, size_t plength, uint8_t qos, bool retained);
/**
* @brief Publishes a message stored in PROGMEM to the specified topic using QoS 0.
* @param topic The topic to publish to.
@@ -567,7 +627,9 @@ class PubSubClient : public Print {
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish_P(const char* topic, const char* payload, bool retained);
inline bool publish_P(const char* topic, PGM_P payload, bool retained) {
return publish_P(topic, payload, MQTT_QOS0, retained);
}
/**
* @brief Publishes a message stored in PROGMEM to the specified topic.
@@ -578,7 +640,22 @@ class PubSubClient : public Print {
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish_P(const char* topic, const char* payload, uint8_t qos, bool retained);
inline bool publish_P(const char* topic, PGM_P payload, uint8_t qos, bool retained) {
return publish_P(topic, reinterpret_cast<const uint8_t*>(payload), payload ? strlen_P(payload) : 0, qos, retained);
}
/**
* @brief Publishes a message stored in PROGMEM to the specified topic.
* @param topic The topic from __FlashStringHelper to publish to.
* @param payload The message to publish.
* @param qos The quality of service (\ref group_qos) to publish at. [0, 1, 2].
* @param retained Publish the message with the retain flag.
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish_P(const __FlashStringHelper* topic, PGM_P payload, uint8_t qos, bool retained) {
return publish_P(topic, reinterpret_cast<const uint8_t*>(payload), payload ? strlen_P(payload) : 0, qos, retained);
}
/**
* @brief Publishes a message stored in PROGMEM to the specified topic using QoS 0.
@@ -589,7 +666,9 @@ class PubSubClient : public Print {
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish_P(const char* topic, const uint8_t* payload, size_t plength, bool retained);
inline bool publish_P(const char* topic, const uint8_t* payload, size_t plength, bool retained) {
return publish_P(topic, payload, plength, MQTT_QOS0, retained);
}
/**
* @brief Publishes a message stored in PROGMEM to the specified topic.
@@ -603,6 +682,18 @@ class PubSubClient : public Print {
*/
bool publish_P(const char* topic, const uint8_t* payload, size_t plength, uint8_t qos, bool retained);
/**
* @brief Publishes a message stored in PROGMEM to the specified topic.
* @param topic The topic from __FlashStringHelper to publish to.
* @param payload The message from PROGMEM to publish.
* @param plength The length of the payload.
* @param qos The quality of service (\ref group_qos) to publish at. [0, 1, 2].
* @param retained Publish the message with the retain flag.
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool publish_P(const __FlashStringHelper* topic, const uint8_t* payload, size_t plength, uint8_t qos, bool retained);
/**
* @brief Start to publish a message using QoS 0.
* This API:
@@ -617,7 +708,9 @@ class PubSubClient : public Print {
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool beginPublish(const char* topic, size_t plength, bool retained);
inline bool beginPublish(const char* topic, size_t plength, bool retained) {
return beginPublishImpl(false, topic, plength, MQTT_QOS0, retained);
}
/**
* @brief Start to publish a message.
@@ -634,7 +727,48 @@ class PubSubClient : public Print {
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
bool beginPublish(const char* topic, size_t plength, uint8_t qos, bool retained);
inline bool beginPublish(const char* topic, size_t plength, uint8_t qos, bool retained) {
return beginPublishImpl(false, topic, plength, qos, retained);
}
/**
* @brief Start to publish a message using a topic from __FlashStringHelper F().
* This API:
* beginPublish(...)
* one or more calls to write(...)
* endPublish()
* Allows for arbitrarily large payloads to be sent without them having to be copied into
* a new buffer and held in memory at one time.
* @param topic The topic from __FlashStringHelper to publish to.
* @param plength The length of the payload.
* @param qos The quality of service (\ref group_qos) to publish at. [0, 1, 2].
* @param retained Publish the message with the retain flag.
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
inline bool beginPublish(const __FlashStringHelper* topic, size_t plength, uint8_t qos, bool retained) {
// convert FlashStringHelper in PROGMEM-pointer
return beginPublishImpl(true, reinterpret_cast<const char*>(topic), plength, qos, retained);
}
/**
* @brief Start to publish a message using a topic in PROGMEM.
* This API:
* beginPublish_P(...)
* one or more calls to write(...)
* endPublish()
* Allows for arbitrarily large payloads to be sent without them having to be copied into
* a new buffer and held in memory at one time.
* @param topic The topic in PROGMEM to publish to.
* @param plength The length of the payload.
* @param qos The quality of service (\ref group_qos) to publish at. [0, 1, 2].
* @param retained Publish the message with the retain flag.
* @return true If the publish succeeded.
* false If the publish failed, either connection lost or message too large.
*/
inline bool beginPublish_P(PGM_P topic, size_t plength, uint8_t qos, bool retained) {
return beginPublishImpl(true, reinterpret_cast<const char*>(topic), plength, qos, retained);
}
/**
* @brief Finish sending a message that was started with a call to beginPublish.
@@ -645,21 +779,43 @@ class PubSubClient : public Print {
/**
* @brief Writes a single byte as a component of a publish started with a call to beginPublish.
* For performance reasons, this will be appended to the internal buffer,
* For performance reasons, this will be appended to the internal buffer,
* which will be flushed when full or on a call to endPublish().
* @param data A byte to write to the publish payload.
* @return The number of bytes written.
* @return The number of bytes written (0 or 1). If 0 is returned a write error occurred.
*/
virtual size_t write(uint8_t data);
/**
* @brief Writes an array of bytes as a component of a publish started with a call to beginPublish.
* For performance reasons, this will be appended to the internal buffer,
* which will be flushed when full or on a call to endPublish(). * @param buffer The bytes to write.
* For performance reasons, this will be appended to the internal buffer,
* which will be flushed when full or on a call to endPublish().
* @param buf The bytes to write.
* @param size The length of the payload to be sent.
* @return The number of bytes written.
* @return The number of bytes written. If return value is != size a write error occurred.
*/
virtual size_t write(const uint8_t* buffer, size_t size);
virtual size_t write(const uint8_t* buf, size_t size);
/**
* @brief Writes a string in PROGMEM as a component of a publish started with a call to beginPublish.
* For performance reasons, this will be appended to the internal buffer,
* which will be flushed when full or on a call to endPublish().
* @param string The message to write.
* @return The number of bytes written. If return value is != string length a write error occurred.
*/
inline size_t write_P(PGM_P string) {
return write_P(reinterpret_cast<const uint8_t*>(string), strlen_P(string));
}
/**
* @brief Writes an array of progmem bytes as a component of a publish started with a call to beginPublish.
* For performance reasons, this will be appended to the internal buffer,
* which will be flushed when full or on a call to endPublish().
* @param buf The bytes to write.
* @param size The length of the payload to be sent.
* @return The number of bytes written. If return value is != size a write error occurred.
*/
size_t write_P(const uint8_t* buf, size_t size);
/**
* @brief Subscribes to messages published to the specified topic using QoS 0.
@@ -667,7 +823,30 @@ class PubSubClient : public Print {
* @return true If sending the subscribe succeeded.
* false If sending the subscribe failed, either connection lost or message too large.
*/
bool subscribe(const char* topic);
inline bool subscribe(const char* topic) {
return subscribeImpl(false, topic, MQTT_QOS0);
}
/**
* @brief Subscribes to messages published to the specified topic from __FlashStringHelper using QoS 0.
* @param topic The topic from __FlashStringHelper to subscribe to.
* @return true If sending the subscribe succeeded.
* false If sending the subscribe failed, either connection lost or message too large.
*/
inline bool subscribe(const __FlashStringHelper* topic) {
// convert FlashStringHelper in PROGMEM-pointer
return subscribeImpl(true, reinterpret_cast<const char*>(topic), MQTT_QOS0);
}
/**
* @brief Subscribes to messages published to the specified topic in PROGMEM using QoS 0.
* @param topic The topic in PROGMEM to subscribe to.
* @return true If sending the subscribe succeeded.
* false If sending the subscribe failed, either connection lost or message too large.
*/
inline bool subscribe_P(PGM_P topic) {
return subscribeImpl(true, reinterpret_cast<const char*>(topic), MQTT_QOS0);
}
/**
* @brief Subscribes to messages published to the specified topic.
@@ -676,7 +855,32 @@ class PubSubClient : public Print {
* @return true If sending the subscribe succeeded.
* false If sending the subscribe failed, either connection lost or message too large.
*/
bool subscribe(const char* topic, uint8_t qos);
inline bool subscribe(const char* topic, uint8_t qos) {
return subscribeImpl(false, topic, qos);
}
/**
* @brief Subscribes to messages published to the specified topic from __FlashStringHelper.
* @param topic The topic from __FlashStringHelper to subscribe to.
* @param qos The qos to subscribe at. [0, 1].
* @return true If sending the subscribe succeeded.
* false If sending the subscribe failed, either connection lost or message too large.
*/
inline bool subscribe(const __FlashStringHelper* topic, uint8_t qos) {
// convert FlashStringHelper in PROGMEM-pointer
return subscribeImpl(true, reinterpret_cast<const char*>(topic), qos);
}
/**
* @brief Subscribes to messages published to the specified topic in PROGMEM.
* @param topic The topic in PROGMEM to subscribe to.
* @param qos The qos to subscribe at. [0, 1].
* @return true If sending the subscribe succeeded.
* false If sending the subscribe failed, either connection lost or message too large.
*/
inline bool subscribe_P(PGM_P topic, uint8_t qos) {
return subscribeImpl(true, reinterpret_cast<const char*>(topic), qos);
}
/**
* @brief Unsubscribes from the specified topic.
@@ -684,7 +888,30 @@ class PubSubClient : public Print {
* @return true If sending the unsubscribe succeeded.
* false If sending the unsubscribe failed, either connection lost or message too large.
*/
bool unsubscribe(const char* topic);
inline bool unsubscribe(const char* topic) {
return unsubscribeImpl(false, topic);
}
/**
* @brief Unsubscribes from the specified topic from __FlashStringHelper.
* @param topic The topic from __FlashStringHelper to unsubscribe from.
* @return true If sending the unsubscribe succeeded.
* false If sending the unsubscribe failed, either connection lost or message too large.
*/
inline bool unsubscribe(const __FlashStringHelper* topic) {
// convert FlashStringHelper in PROGMEM-pointer
return unsubscribeImpl(true, reinterpret_cast<const char*>(topic));
}
/**
* @brief Unsubscribes from the specified topic in PROGMEM.
* @param topic The topic in PROGMEM to unsubscribe from.
* @return true If sending the unsubscribe succeeded.
* false If sending the unsubscribe failed, either connection lost or message too large.
*/
inline bool unsubscribe_P(PGM_P topic) {
return unsubscribeImpl(true, reinterpret_cast<const char*>(topic));
}
/**
* @brief This should be called regularly to allow the client to process incoming messages and maintain its connection to the server.
@@ -20,9 +20,14 @@ extern void loop(void);
unsigned long millis(void);
}
class __FlashStringHelper;
#define PROGMEM
#define PGM_P const char*
#define memcpy_P memcpy
#define strlen_P strlen
#define strnlen_P strnlen
#define pgm_read_byte_near(x) *(x)
#define F(x) (reinterpret_cast<const __FlashStringHelper*>(x))
#define yield(x) {}
+167 -8
View File
@@ -13,6 +13,7 @@ int test_publish_bytes();
int test_publish_retained();
int test_publish_retained_2();
int test_publish_not_connected();
int test_publish_long();
int test_publish_too_long();
int test_publish_P();
int test_publish_P_too_long();
@@ -22,6 +23,10 @@ int test_publish_qos1();
int test_publish_qos2();
int test_publish_P_qos1();
int test_publish_P_qos2();
int test_publish_FlashStringHelper();
int test_publish_FlashStringHelper2();
int test_publish_P_FlashStringHelper();
int test_publish_P_P();
void callback(_UNUSED_ char* topic, _UNUSED_ uint8_t* payload, _UNUSED_ size_t plength) {
// handle message arrived
@@ -139,6 +144,49 @@ int test_publish_not_connected() {
END_IT
}
int test_publish_long() {
IT("publishes with long payload message (> buffer size)");
ShimClient shimClient;
shimClient.setAllowConnect(true);
// buffer size 64 bytes - 5 bytes header - 2 bytes topic length = max. 57 bytes topic
// 0 1 2 3 4 5 6 7 8 9 0 1 2
char topic[] = "123456789012345678901234567890123456789012345678901234567";
// 0 1 2 3 4 5 6 7 8 9 0 1 2
char payload[] = "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
size_t plength = strlen(payload);
byte connack[] = {0x20, 0x02, 0x00, 0x00};
shimClient.respond(connack, 4);
PubSubClient client(server, 1883, callback, shimClient);
client.setBufferSize(64);
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte publish[256];
publish[0] = 0x30; // PUBLISH, QoS 0, no retain
publish[1] = 0xb3; // Remaining length byte 1: 2 + 57 + 120 bytes (topic length bytes + topic length + payload length = 179)
publish[2] = 0x01; // Remaining length byte 2
publish[3] = 0x00; // Topic length MSB
publish[4] = 0x39; // Topic length LSB (57 bytes)
memcpy(&publish[5], topic, sizeof(topic) - 1);
memcpy(&publish[5 + sizeof(topic) - 1], payload, sizeof(payload) - 1);
shimClient.expect(publish, 5 + sizeof(topic) - 1 + sizeof(payload) - 1);
rc = client.beginPublish(topic, plength, 0, false);
IS_TRUE(rc);
plength = client.write((uint8_t*)payload, plength);
IS_EQUAL(plength, strlen(payload));
rc = client.endPublish();
IS_TRUE(rc);
IS_FALSE(shimClient.error());
END_IT
}
int test_publish_too_long() {
IT("publish fails when topic/payload are too long");
ShimClient shimClient;
@@ -167,7 +215,7 @@ int test_publish_too_long() {
}
int test_publish_P() {
IT("publishes using PROGMEM");
IT("publishes using PROGMEM payload");
ShimClient shimClient;
shimClient.setAllowConnect(true);
@@ -193,7 +241,7 @@ int test_publish_P() {
}
int test_publish_P_too_long() {
IT("publish using PROGMEM fails when topic is too long");
IT("publish using PROGMEM payload fails when topic is too long");
ShimClient shimClient;
shimClient.setAllowConnect(true);
@@ -219,6 +267,112 @@ int test_publish_P_too_long() {
END_IT
}
int test_publish_FlashStringHelper() {
IT("publishes using FlashStringHelper topic");
ShimClient shimClient;
shimClient.setAllowConnect(true);
char payload[] = "12345";
byte connack[] = {0x20, 0x02, 0x00, 0x00};
shimClient.respond(connack, sizeof(connack));
PubSubClient client(server, 1883, callback, shimClient);
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte publish[] = {0x31, 0x0c, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', '1', '2', '3', '4', '5'};
shimClient.expect(publish, sizeof(publish));
rc = client.publish(F("topic"), payload, MQTT_QOS0, true);
IS_TRUE(rc);
IS_FALSE(shimClient.error());
END_IT
}
int test_publish_FlashStringHelper2() {
IT("publishes using FlashStringHelper topic and payload");
ShimClient shimClient;
shimClient.setAllowConnect(true);
char payload[] = "12345";
byte connack[] = {0x20, 0x02, 0x00, 0x00};
shimClient.respond(connack, sizeof(connack));
PubSubClient client(server, 1883, callback, shimClient);
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte publish[] = {0x31, 0x0c, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', '1', '2', '3', '4', '5'};
shimClient.expect(publish, sizeof(publish));
rc = client.publish(F("topic"), F(payload), MQTT_QOS0, true);
IS_TRUE(rc);
IS_FALSE(shimClient.error());
END_IT
}
int test_publish_P_FlashStringHelper() {
IT("publishes using FlashStringHelper topic and PROGMEM payload");
ShimClient shimClient;
shimClient.setAllowConnect(true);
char payload[] PROGMEM = "12345";
byte connack[] = {0x20, 0x02, 0x00, 0x00};
shimClient.respond(connack, sizeof(connack));
PubSubClient client(server, 1883, callback, shimClient);
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte publish[] = {0x31, 0x0c, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', '1', '2', '3', '4', '5'};
shimClient.expect(publish, sizeof(publish));
rc = client.publish_P(F("topic"), payload, MQTT_QOS0, true);
IS_TRUE(rc);
IS_FALSE(shimClient.error());
END_IT
}
int test_publish_P_P() {
IT("publishes using PROGMEM topic and PROGMEM payload");
ShimClient shimClient;
shimClient.setAllowConnect(true);
char topic[] PROGMEM = "topic";
char payload[] PROGMEM = "12345";
size_t length = strlen_P(payload);
byte connack[] = {0x20, 0x02, 0x00, 0x00};
shimClient.respond(connack, sizeof(connack));
PubSubClient client(server, 1883, callback, shimClient);
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte publish[] = {0x31, 0x0c, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', '1', '2', '3', '4', '5'};
shimClient.expect(publish, sizeof(publish));
rc = client.beginPublish_P(topic, length, MQTT_QOS0, true);
IS_TRUE(rc);
length = client.write_P(payload);
IS_EQUAL(length, strlen_P(payload));
rc = client.endPublish();
IS_TRUE(rc);
IS_FALSE(shimClient.error());
END_IT
}
int test_publish_empty_topic() {
IT("publish fails when topic is empty");
ShimClient shimClient;
@@ -275,7 +429,7 @@ int test_publish_qos1() {
IS_TRUE(rc);
// Example publish packet for QoS 1 (0x32)
byte publish[] = {0x32, 0x10, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', 'p', 'a', 'y', 'l', 'o', 'a', 'd', 0x00, 0x02};
byte publish[] = {0x32, 0x10, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', 0x00, 0x02, 'p', 'a', 'y', 'l', 'o', 'a', 'd'};
shimClient.expect(publish, sizeof(publish));
rc = client.publish("topic", "payload", MQTT_QOS1, false);
@@ -299,7 +453,7 @@ int test_publish_qos2() {
IS_TRUE(rc);
// Example publish packet for QoS 2 (0x34)
byte publish[] = {0x34, 0x10, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', 'p', 'a', 'y', 'l', 'o', 'a', 'd', 0x00, 0x02};
byte publish[] = {0x34, 0x10, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', 0x00, 0x02, 'p', 'a', 'y', 'l', 'o', 'a', 'd'};
shimClient.expect(publish, sizeof(publish));
rc = client.publish("topic", "payload", MQTT_QOS2, false);
@@ -311,7 +465,7 @@ int test_publish_qos2() {
}
int test_publish_P_qos1() {
IT("publishes using PROGMEM with QoS 1 retained");
IT("publishes using PROGMEM payload with QoS 1 retained");
ShimClient shimClient;
shimClient.setAllowConnect(true);
@@ -325,7 +479,7 @@ int test_publish_P_qos1() {
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte publish[] = {0x33, 0x0e, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x02};
byte publish[] = {0x33, 0x0e, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', 0x00, 0x02, 0x01, 0x02, 0x03, 0x04, 0x05};
shimClient.expect(publish, sizeof(publish));
rc = client.publish_P("topic", payload, length, MQTT_QOS1, true);
@@ -337,7 +491,7 @@ int test_publish_P_qos1() {
}
int test_publish_P_qos2() {
IT("publishes using PROGMEM with QoS 2 retained");
IT("publishes using PROGMEM payload with QoS 2 retained");
ShimClient shimClient;
shimClient.setAllowConnect(true);
@@ -351,7 +505,7 @@ int test_publish_P_qos2() {
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte publish[] = {0x35, 0x0e, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x02};
byte publish[] = {0x35, 0x0e, 0x00, 0x05, 't', 'o', 'p', 'i', 'c', 0x00, 0x02, 0x01, 0x02, 0x03, 0x04, 0x05};
shimClient.expect(publish, sizeof(publish));
rc = client.publish_P("topic", payload, length, MQTT_QOS2, true);
@@ -371,6 +525,7 @@ int main() {
test_publish_qos1();
test_publish_qos2();
test_publish_null_payload();
test_publish_long();
test_publish_not_connected();
test_publish_empty_topic();
test_publish_too_long();
@@ -378,6 +533,10 @@ int main() {
test_publish_P_qos1();
test_publish_P_qos2();
test_publish_P_too_long();
test_publish_FlashStringHelper();
test_publish_FlashStringHelper2();
test_publish_P_FlashStringHelper();
test_publish_P_P();
FINISH
}
@@ -28,6 +28,7 @@ int test_receive_oversized_message();
int test_resize_buffer();
int test_receive_oversized_stream_message();
int test_receive_qos1();
int test_receive_qos2();
void reset_callback() {
callback_called = false;
@@ -330,6 +331,57 @@ int test_receive_qos1() {
END_IT
}
int test_receive_qos2() {
IT("receives a qos2 message - responds PUBREC then PUBCOMP");
reset_callback();
ShimClient shimClient;
shimClient.setAllowConnect(true);
byte connack[] = {0x20, 0x02, 0x00, 0x00};
shimClient.respond(connack, 4);
PubSubClient client(server, 1883, callback, shimClient);
bool rc = client.connect("client_test1");
IS_TRUE(rc);
// QoS 2 PUBLISH from broker (0x34 = MQTTPUBLISH | QoS2 bits)
// Fixed header 0x34, remaining length 0x10 (16), topic len 0x0005, topic "topic",
// msgId 0x1234, payload "payload"
byte publish[] = {0x34, 0x10, 0x0, 0x5, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x34, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64};
shimClient.respond(publish, 18);
// Client must respond with PUBREC (0x50), remaining length 2, msgId 0x1234
byte pubrec[] = {0x50, 0x02, 0x12, 0x34};
shimClient.expect(pubrec, 4);
rc = client.loop();
IS_TRUE(rc);
IS_TRUE(callback_called);
IS_TRUE(strcmp(lastTopic, "topic") == 0);
IS_TRUE(memcmp(lastPayload, "payload", 7) == 0);
IS_TRUE(lastLength == 7);
IS_FALSE(shimClient.error());
reset_callback();
// Broker sends PUBREL (0x62 = MQTTPUBREL | bit1), remaining length 2, msgId 0x1234
byte pubrel[] = {0x62, 0x02, 0x12, 0x34};
shimClient.respond(pubrel, 4);
// Client must respond with PUBCOMP (0x70), remaining length 2, msgId 0x1234
byte pubcomp[] = {0x70, 0x02, 0x12, 0x34};
shimClient.expect(pubcomp, 4);
rc = client.loop();
IS_TRUE(rc);
IS_FALSE(callback_called); // callback must NOT fire again on PUBREL
IS_FALSE(shimClient.error());
END_IT
}
int main() {
SUITE("Receive");
test_receive_callback();
@@ -340,6 +392,7 @@ int main() {
test_resize_buffer();
test_receive_oversized_stream_message();
test_receive_qos1();
test_receive_qos2();
FINISH
}
@@ -10,6 +10,10 @@ byte server[] = {172, 16, 0, 2};
void callback(char* topic, uint8_t* payload, size_t plength);
int test_subscribe_no_qos();
int test_subscribe_qos_1();
int test_subscribe_P();
int test_subscribe_P_qos_1();
int test_subscribe_FlashStringHelper();
int test_subscribe_FlashStringHelper_qos_1();
int test_subscribe_not_connected();
int test_subscribe_invalid_qos();
int test_subscribe_too_long();
@@ -70,6 +74,108 @@ int test_subscribe_qos_1() {
END_IT
}
int test_subscribe_P() {
IT("subscribe using PROGMEM");
ShimClient shimClient;
shimClient.setAllowConnect(true);
byte connack[] = {0x20, 0x02, 0x00, 0x00};
shimClient.respond(connack, 4);
PubSubClient client(server, 1883, callback, shimClient);
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte subscribe[] = {0x82, 0xa, 0x0, 0x2, 0x0, 0x5, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x0};
shimClient.expect(subscribe, 12);
byte suback[] = {0x90, 0x3, 0x0, 0x2, 0x0};
shimClient.respond(suback, 5);
char topic[] PROGMEM = "topic";
rc = client.subscribe_P(topic);
IS_TRUE(rc);
IS_FALSE(shimClient.error());
END_IT
}
int test_subscribe_P_qos_1() {
IT("subscribe using PROGMEM with QoS 1");
ShimClient shimClient;
shimClient.setAllowConnect(true);
byte connack[] = {0x20, 0x02, 0x00, 0x00};
shimClient.respond(connack, 4);
PubSubClient client(server, 1883, callback, shimClient);
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte subscribe[] = {0x82, 0xa, 0x0, 0x2, 0x0, 0x5, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x1};
shimClient.expect(subscribe, 12);
byte suback[] = {0x90, 0x3, 0x0, 0x2, 0x1};
shimClient.respond(suback, 5);
char topic[] PROGMEM = "topic";
rc = client.subscribe_P(topic, MQTT_QOS1);
IS_TRUE(rc);
IS_FALSE(shimClient.error());
END_IT
}
int test_subscribe_FlashStringHelper() {
IT("subscribe using FlashStringHelper");
ShimClient shimClient;
shimClient.setAllowConnect(true);
byte connack[] = {0x20, 0x02, 0x00, 0x00};
shimClient.respond(connack, 4);
PubSubClient client(server, 1883, callback, shimClient);
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte subscribe[] = {0x82, 0xa, 0x0, 0x2, 0x0, 0x5, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x0};
shimClient.expect(subscribe, 12);
byte suback[] = {0x90, 0x3, 0x0, 0x2, 0x0};
shimClient.respond(suback, 5);
rc = client.subscribe(F("topic"));
IS_TRUE(rc);
IS_FALSE(shimClient.error());
END_IT
}
int test_subscribe_FlashStringHelper_qos_1() {
IT("subscribe using FlashStringHelper with QoS 1");
ShimClient shimClient;
shimClient.setAllowConnect(true);
byte connack[] = {0x20, 0x02, 0x00, 0x00};
shimClient.respond(connack, 4);
PubSubClient client(server, 1883, callback, shimClient);
bool rc = client.connect("client_test1");
IS_TRUE(rc);
byte subscribe[] = {0x82, 0xa, 0x0, 0x2, 0x0, 0x5, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x1};
shimClient.expect(subscribe, 12);
byte suback[] = {0x90, 0x3, 0x0, 0x2, 0x1};
shimClient.respond(suback, 5);
rc = client.subscribe(F("topic"), MQTT_QOS1);
IS_TRUE(rc);
IS_FALSE(shimClient.error());
END_IT
}
int test_subscribe_not_connected() {
IT("subscribe fails when not connected");
ShimClient shimClient;
@@ -176,6 +282,10 @@ int main() {
SUITE("Subscribe");
test_subscribe_no_qos();
test_subscribe_qos_1();
test_subscribe_P();
test_subscribe_P_qos_1();
test_subscribe_FlashStringHelper();
test_subscribe_FlashStringHelper_qos_1();
test_subscribe_not_connected();
test_subscribe_invalid_qos();
test_subscribe_too_long();