forked from espressif/esp-mqtt
Compare commits
472 Commits
idf
...
fix_host_t
Author | SHA1 | Date | |
---|---|---|---|
b43d93c82d | |||
aa6f889fb4 | |||
1edd167dcb | |||
c06f1540fe | |||
37478a9c00 | |||
fd21b27a83 | |||
e7b9aa5e6a | |||
726e5f2fce | |||
14f5fa079f | |||
5e3abd4b8c | |||
37cb056c5f | |||
74481cbbf4 | |||
c33a0c8e44 | |||
5c17fc4a1a | |||
657a2aea77 | |||
891380bdf5 | |||
fa88da5282 | |||
5a35782272 | |||
acdb66d5c6 | |||
d3e3c4e7ad | |||
371f594cce | |||
ddde502782 | |||
7894dd0ace | |||
ea0df31e00 | |||
abd8b6cadc | |||
67800569de | |||
e6afdb4025 | |||
c0b40b1293 | |||
dc93367bd5 | |||
00ee059bf8 | |||
05b347643f | |||
f35aaa1577 | |||
4b28192d00 | |||
c355e0b5ae | |||
301bd9e028 | |||
cc41d1b852 | |||
1ca73479cb | |||
90b4a4538e | |||
dc775bb52e | |||
fe32d8f224 | |||
cb1e6cf218 | |||
cd81773bd1 | |||
395aa141c8 | |||
8d98103013 | |||
a3b04f2d0a | |||
585e3ba2e0 | |||
36eec6f625 | |||
effd1e6705 | |||
6c849c62ef | |||
ee3ea29d52 | |||
4050df4caf | |||
aee82c7ba8 | |||
5896e259ad | |||
363fbf7dab | |||
5d491a45ce | |||
63cfec799c | |||
372ab7b374 | |||
21a5491d53 | |||
122875bf8a | |||
ed628098a1 | |||
6438676b66 | |||
a492935951 | |||
a89af4bf8d | |||
6195762d28 | |||
a5c1b441dc | |||
ffd7d4df6c | |||
4c2ed1676f | |||
5b8a541939 | |||
6c3cbdd195 | |||
63ebf8b1a2 | |||
89981b7277 | |||
5bd9724c69 | |||
70cbaca728 | |||
b2bd8e5f49 | |||
77372e81f9 | |||
fe622fc77a | |||
490bde044f | |||
0e4cec9497 | |||
94defb867e | |||
14b11ad07e | |||
da6d38a17e | |||
5729048683 | |||
72833c7f8a | |||
0b315a01b1 | |||
2c71f9e69b | |||
6bcd906b8a | |||
d71dcf372a | |||
9c1826d152 | |||
5cce2c4f35 | |||
9f2db7b4b6 | |||
36f0faa80d | |||
7089fac9c0 | |||
65a4fdaff5 | |||
1011e63cbe | |||
5a156f56b4 | |||
32102558d3 | |||
5adbe11aaf | |||
2fa945d0b8 | |||
86d21e4902 | |||
e9b865eb9d | |||
acab02f2c5 | |||
ed76036744 | |||
c96f6f804c | |||
dffabb067f | |||
159b1638b2 | |||
f27cb47c0f | |||
ee9b52b7d2 | |||
01d0f34654 | |||
0b98c63247 | |||
94ecca2073 | |||
1e598b071b | |||
d4ee31f627 | |||
411ea65ec7 | |||
bc30b9a8e3 | |||
d372bbf09e | |||
610db7b934 | |||
62c5545f2d | |||
b80d4d468c | |||
5539c83c0a | |||
0a507f1205 | |||
77e41479d7 | |||
a88eaebad9 | |||
8dc8e5ae0b | |||
933da9efed | |||
a840bcf274 | |||
fcd548daab | |||
23e5c5a02c | |||
71d9236275 | |||
f8cb9f7286 | |||
47ac29b1e2 | |||
174c6781de | |||
1766fd4d5b | |||
4afe71b132 | |||
74882b18f0 | |||
ff8a93585f | |||
b4474ac16d | |||
3c764f0fc1 | |||
c03353333b | |||
382caedbbb | |||
93158d5d18 | |||
a8b0f474ec | |||
0fda7d7062 | |||
128be3f402 | |||
f2f30baec0 | |||
f072c9f753 | |||
3883bde0b0 | |||
40fdf32793 | |||
2c3363e23a | |||
6756e62387 | |||
10bba73d21 | |||
b342444746 | |||
5facf78673 | |||
6c55757f70 | |||
87d698cbc7 | |||
25b661c8ea | |||
8b3d3db10b | |||
691e7bca88 | |||
9b79fe3d00 | |||
cb782cae1e | |||
9db10b5b7b | |||
29ce3c5869 | |||
605352c1c5 | |||
5ba36d511f | |||
650328b021 | |||
5be3f7b5d4 | |||
0178bafba8 | |||
8a24bebbc0 | |||
62f3dc4705 | |||
6cadfe519b | |||
37e511bef1 | |||
b4ba09ce38 | |||
25f423d69b | |||
5a50fc0812 | |||
bef4fce9c1 | |||
49296d851d | |||
a903e5e3a9 | |||
63b3f29ffc | |||
332fadfea6 | |||
3a2e82ec0f | |||
7f77ad063d | |||
6809fcb2df | |||
6b70e14236 | |||
e19b9aa2df | |||
9726859fa3 | |||
7437dce403 | |||
60e39865e6 | |||
14e9df14ff | |||
a31ede754f | |||
229167f48a | |||
71d6861d6a | |||
78233fe7de | |||
1d49ac3249 | |||
20dda12b88 | |||
24b656ca4b | |||
c5069977a4 | |||
f80772b8d7 | |||
d5a2c0a7c6 | |||
d1f6ad23b7 | |||
fde00340f1 | |||
fb42588a03 | |||
3e0a118e0b | |||
8a6005781d | |||
e80f3e6a87 | |||
3738fcd196 | |||
5f297c0d62 | |||
9af5c26045 | |||
2b52b50a6b | |||
47b3f9b899 | |||
a87fd9c738 | |||
68e8c4f829 | |||
5688a84cbc | |||
9db9ee7648 | |||
5f3cadb44d | |||
97503cceb3 | |||
9186e5fa00 | |||
a9a9fe76bf | |||
f14eeb9157 | |||
ae53d799da | |||
9a56b2f086 | |||
89e5c6014f | |||
493822dc9d | |||
fdf2aeb36f | |||
3d275f42b9 | |||
0aa0a342a1 | |||
7253211a45 | |||
a21c387d62 | |||
7a31a555e9 | |||
16952ea66f | |||
e05d873aa2 | |||
9299f5400f | |||
69b6493423 | |||
7f765cac2c | |||
647e0eff32 | |||
684843a309 | |||
f6caaff254 | |||
64f88b4412 | |||
e31834c00f | |||
59b48a4d49 | |||
2cdcdcb4fa | |||
6b794e4708 | |||
2c2e6f38b5 | |||
5878e2e8a6 | |||
fb3184cc14 | |||
4874bab356 | |||
f83d2a9111 | |||
d3d816ed24 | |||
2e508fa6ef | |||
08ce9ed83f | |||
b66b9745f6 | |||
3a5334de6b | |||
0eda78e9b7 | |||
b7b858cd7f | |||
1a96ea3efa | |||
985078affa | |||
4c5a65c79e | |||
b021fc8e30 | |||
c28a56ddb0 | |||
6ef98d6510 | |||
026253d802 | |||
372b323535 | |||
383e7e5f0f | |||
df8dc92870 | |||
b86d42c130 | |||
08c4d62bf4 | |||
09287a1156 | |||
1fd50dd2cb | |||
36de30e46d | |||
320b058e28 | |||
3f05b1aedc | |||
86e40f8615 | |||
5ec3702881 | |||
de47f1c341 | |||
e1d5a9402f | |||
89894bd0c6 | |||
1d10608409 | |||
d7b00da517 | |||
1b009c840b | |||
88f4b8ed50 | |||
5b3c81ee48 | |||
1b71980575 | |||
4a89bff610 | |||
026ea95338 | |||
c11c314f44 | |||
ebef896b00 | |||
7471177fe7 | |||
e24852a4dc | |||
231b274962 | |||
f10321a53b | |||
db13533904 | |||
831d25cb56 | |||
a7e5accc7e | |||
90f0bf8dd4 | |||
1a94efe8b9 | |||
c4b7324bd6 | |||
a4d5ee0f12 | |||
67553aba45 | |||
36a3990404 | |||
2e15c9a5fe | |||
626dc3645b | |||
c7ae67b7f0 | |||
afba070093 | |||
eec6f0e17d | |||
78fcf23947 | |||
048f12f55f | |||
ff8226a464 | |||
7db36f9177 | |||
8f3cac8c36 | |||
d8c9c7a9e7 | |||
a00a3134c6 | |||
06157be118 | |||
e1a90d0161 | |||
5b27d1896e | |||
ff8e64839a | |||
1db731f985 | |||
87fcce72c9 | |||
2b20b5117c | |||
2f57985c0b | |||
9fdf7b6138 | |||
169b6986eb | |||
fd2d04e687 | |||
001dc1a95e | |||
a5a60752c0 | |||
369f4207f8 | |||
1a64f3176c | |||
0213382593 | |||
a74adfa9eb | |||
7a38867ce2 | |||
2530edbdce | |||
e05ed2ce18 | |||
3efac7b7fa | |||
b834be80a5 | |||
9ea804e0ab | |||
e2de0f3e3e | |||
2e35d4d4d5 | |||
f44dcb1c26 | |||
dc7fd5c0b1 | |||
8bb4a26f46 | |||
f65d5d05db | |||
cef3e16c5d | |||
b7158a4aea | |||
da850b0add | |||
8562437c8a | |||
e8be6c3aa7 | |||
65d8a813ea | |||
36d721e60b | |||
0a1d9d0300 | |||
673086e13a | |||
259baaec96 | |||
845c2a3a1e | |||
23c8e1ecf5 | |||
d6613e995b | |||
b9db8d9020 | |||
fcd7f0d083 | |||
a03228ac46 | |||
01594bf118 | |||
7d8e59de00 | |||
ae408d9a69 | |||
a8ef2a434b | |||
867773e423 | |||
ac7209049f | |||
bdadd77c6e | |||
f7325bfa10 | |||
d4aaec08ff | |||
5e17dcaeb2 | |||
db4bce01ab | |||
702da6d528 | |||
0bafcc5188 | |||
651c2fb4a2 | |||
d754293d25 | |||
8a412c147d | |||
5b8c04f348 | |||
40b06deb10 | |||
109985d3ad | |||
a7ff9afa3f | |||
2f9b337071 | |||
5e8950e681 | |||
6bc94add89 | |||
20bf9928d4 | |||
fb41520206 | |||
615aeae0c2 | |||
0c3d306589 | |||
b02746492a | |||
50dc010b97 | |||
dc1a635a97 | |||
0fc904c5d8 | |||
d8e2081332 | |||
9b750651fc | |||
61a8f4b63c | |||
f243225521 | |||
8a1e1a5a9f | |||
62d1509961 | |||
e2aa29d2ea | |||
d801f03f46 | |||
a9036b82a4 | |||
3e4f91ae50 | |||
7983357489 | |||
d5e915296e | |||
9e20c7ae3d | |||
38eab46f14 | |||
b963a5cd86 | |||
2994c3f273 | |||
420441b677 | |||
bbcf078a2b | |||
ed90a64551 | |||
f7941e29be | |||
3a47e3abae | |||
9eca3f6db9 | |||
7087193093 | |||
89a0c1fc95 | |||
6e08f6a04f | |||
8ab095b5bb | |||
616fa257fb | |||
7ac0a42831 | |||
ab1e8d7969 | |||
86fc8b7584 | |||
2684ed413d | |||
f74fe3d887 | |||
acc91bacf3 | |||
057f140228 | |||
97f91eda5c | |||
716b8625ba | |||
b25077a338 | |||
99586c2768 | |||
17e2f68e43 | |||
9a5187771a | |||
e8bb0bcf6f | |||
9ca997d2d4 | |||
9655845fdc | |||
1cd0885611 | |||
e1ab64f0d8 | |||
c1766c0f56 | |||
3da472fd37 | |||
566b034984 | |||
e3b013e2db | |||
67042a1315 | |||
430676cadb | |||
52cdfa9087 | |||
40302a3d43 | |||
3e35fc8323 | |||
78e2a7050e | |||
d01c77f70b | |||
59b228e0ad | |||
9fbd7d9244 | |||
2e0e93a2d3 | |||
2b04d177c7 | |||
176be08f75 | |||
04253b2b07 | |||
0234f6e538 | |||
1134cb234c | |||
9a56cc7e14 | |||
54c0161481 | |||
fb3d2107cd | |||
92aa01deb8 | |||
cbae6343e9 | |||
2fef1a07c5 | |||
117eef2dad | |||
dc37d3a065 | |||
0cc4077bd3 | |||
65bf2255d7 | |||
e85430def7 | |||
d9faeb47a3 | |||
82fa03a508 | |||
7223302deb | |||
11f884623b | |||
27780e3c45 | |||
78a55fac68 | |||
51a5ca026f | |||
5fd5523b9b | |||
99004f2a48 | |||
4e4b5dd06f | |||
8e8cf28bca | |||
b930438e3d |
37
.github/workflows/build-and-target-test.yml
vendored
Normal file
37
.github/workflows/build-and-target-test.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: Build app an run on target
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
idf_version:
|
||||
required: true
|
||||
type: string
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
app_name:
|
||||
type: string
|
||||
required: true
|
||||
app_path:
|
||||
type: string
|
||||
required: true
|
||||
|
||||
|
||||
jobs:
|
||||
build-app:
|
||||
uses: "./.github/workflows/build-app.yml"
|
||||
with:
|
||||
idf_version: ${{inputs.idf_version}}
|
||||
target: ${{inputs.target}}
|
||||
app_name: ${{inputs.app_name}}
|
||||
app_path: ${{inputs.app_path}}
|
||||
|
||||
# run-on-target:
|
||||
# needs: build-app
|
||||
# uses: "./.github/workflows/run-on-target.yml"
|
||||
# with:
|
||||
# idf_version: ${{inputs.idf_version}}
|
||||
# target: ${{inputs.target}}
|
||||
# app_name: ${{inputs.app_name}}
|
||||
# app_path: ${{inputs.app_path}}
|
||||
|
59
.github/workflows/build-app.yml
vendored
Normal file
59
.github/workflows/build-app.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
name: Build app
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
idf_version:
|
||||
required: true
|
||||
type: string
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
app_name:
|
||||
type: string
|
||||
required: true
|
||||
app_path:
|
||||
type: string
|
||||
required: true
|
||||
upload_artifacts:
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build App
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{inputs.idf_version}}
|
||||
steps:
|
||||
- if: ${{ env.ACT }}
|
||||
name: Add node for local tests
|
||||
run: |
|
||||
curl -fsSL https://deb.nodesource.com/setup_14.x | bash -
|
||||
apt-get install -y nodejs
|
||||
- name: Checkout esp-mqtt
|
||||
uses: actions/checkout@v3
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ${{inputs.idf_version}}-${{inputs.target}}
|
||||
- name: Build ${{ inputs.app_name }} with IDF-${{ inputs.idf_version }}
|
||||
shell: bash
|
||||
run: |
|
||||
${IDF_PATH}/install.sh --enable-pytest
|
||||
. ${IDF_PATH}/export.sh
|
||||
python -m pip install idf-build-apps
|
||||
rm -rf $IDF_PATH/components/mqtt/esp-mqtt
|
||||
cp -r . $IDF_PATH/components/mqtt/esp-mqtt
|
||||
IDF_CCACHE_ENABLE=1 idf-build-apps build --config-file ci/idf_build_apps.toml -p ${{inputs.app_path}} -t ${{inputs.target}}
|
||||
- name: Upload files to artifacts for run-target job
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{inputs.upload_artifacts}}
|
||||
with:
|
||||
name: mqtt_bin_${{inputs.target}}_${{ inputs.idf_version }}_${{ inputs.app_name }}
|
||||
path: |
|
||||
build_${{inputs.target}}_${{inputs.app_name}}/bootloader/bootloader.bin
|
||||
build_${{inputs.target}}_${{inputs.app_name}}/partition_table/partition-table.bin
|
||||
build_${{inputs.target}}_${{inputs.app_name}}/*.bin
|
||||
build_${{inputs.target}}_${{inputs.app_name}}/*.elf
|
||||
build_${{inputs.target}}_${{inputs.app_name}}/flasher_args.json
|
||||
if-no-files-found: error
|
20
.github/workflows/issue_comment.yml
vendored
Normal file
20
.github/workflows/issue_comment.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: Sync issue comments to JIRA
|
||||
|
||||
# This workflow will be triggered when new issue comment is created (including PR comments)
|
||||
on: issue_comment
|
||||
|
||||
jobs:
|
||||
sync_issue_comments_to_jira:
|
||||
name: Sync Issue Comments to Jira
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Sync issue comments to JIRA
|
||||
uses: espressif/github-actions/sync_issues_to_jira@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
JIRA_PASS: ${{ secrets.JIRA_PASS }}
|
||||
JIRA_PROJECT: IDFGH
|
||||
JIRA_COMPONENT: esp-mqtt
|
||||
JIRA_URL: ${{ secrets.JIRA_URL }}
|
||||
JIRA_USER: ${{ secrets.JIRA_USER }}
|
80
.github/workflows/mqtt__host-tests.yml
vendored
Normal file
80
.github/workflows/mqtt__host-tests.yml
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
name: "esp-mqtt: host-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
host_test_esp_mqtt:
|
||||
name: Host Tests
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
container: espressif/idf:latest
|
||||
env:
|
||||
COMP_DIR: components/mqtt/esp-mqtt
|
||||
steps:
|
||||
- name: Checkout esp-mqtt
|
||||
uses: actions/checkout@v3
|
||||
- name: Build and Test
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update && apt-get install -y gcc g++ python3-pip rsync
|
||||
${IDF_PATH}/install.sh
|
||||
. ${IDF_PATH}/export.sh
|
||||
echo "IDF_PATH=${IDF_PATH}" >> $GITHUB_ENV
|
||||
rm -rf $IDF_PATH/${{ env.COMP_DIR }}
|
||||
cp -r . $IDF_PATH/${{ env.COMP_DIR }}
|
||||
cd $IDF_PATH/${{ env.COMP_DIR }}/host_test
|
||||
idf.py build
|
||||
./build/host_mqtt_client_test.elf -r junit -o junit.xml
|
||||
- name: Build with Coverage Enabled
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $IDF_PATH/${{ env.COMP_DIR }}/host_test
|
||||
cat sdkconfig.ci.coverage >> sdkconfig.defaults
|
||||
rm -rf build sdkconfig
|
||||
idf.py build
|
||||
./build/host_mqtt_client_test.elf
|
||||
- name: Run gcovr
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install gcovr
|
||||
cd $IDF_PATH/${{ env.COMP_DIR }}
|
||||
gcov -b host_test/main/mqtt_client.c. -o `find . -name "mqtt_client*gcda" -exec dirname {} \;`
|
||||
gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x esp_mqtt_coverage.xml
|
||||
mkdir docs_gcovr
|
||||
mv index.html docs_gcovr
|
||||
touch docs_gcovr/.nojekyll
|
||||
cp -r docs_gcovr esp_mqtt_coverage.xml $GITHUB_WORKSPACE
|
||||
- name: Code Coverage Summary Report
|
||||
uses: irongut/CodeCoverageSummary@v1.3.0
|
||||
with:
|
||||
filename: ${{ env.GITHUB_WORKSPACE }}/**/esp_mqtt_coverage.xml
|
||||
badge: true
|
||||
fail_below_min: false
|
||||
format: markdown
|
||||
hide_branch_rate: false
|
||||
hide_complexity: false
|
||||
indicators: true
|
||||
output: both
|
||||
thresholds: '60 80'
|
||||
- name: Write to Job Summary
|
||||
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: docs_gcovr
|
||||
path: ${{ env.IDF_PATH }}/${{ env.COMP_DIR }}/docs_gcovr
|
||||
if-no-files-found: error
|
||||
- name: Deploy coverage summary
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: JamesIves/github-pages-deploy-action@v4.4.1
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: ${{ env.IDF_PATH }}/${{ env.COMP_DIR }}/docs_gcovr
|
20
.github/workflows/new_issues.yml
vendored
Normal file
20
.github/workflows/new_issues.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: Sync issues to Jira
|
||||
|
||||
# This workflow will be triggered when a new issue is opened
|
||||
on: issues
|
||||
|
||||
jobs:
|
||||
sync_issues_to_jira:
|
||||
name: Sync issues to Jira
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Sync GitHub issues to Jira project
|
||||
uses: espressif/github-actions/sync_issues_to_jira@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
JIRA_PASS: ${{ secrets.JIRA_PASS }}
|
||||
JIRA_PROJECT: IDFGH
|
||||
JIRA_COMPONENT: esp-mqtt
|
||||
JIRA_URL: ${{ secrets.JIRA_URL }}
|
||||
JIRA_USER: ${{ secrets.JIRA_USER }}
|
29
.github/workflows/new_prs.yml
vendored
Normal file
29
.github/workflows/new_prs.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Sync remain PRs to Jira
|
||||
|
||||
# This workflow will be triggered every hour, to sync remaining PRs (i.e. PRs with zero comment) to Jira project
|
||||
# Note that, PRs can also get synced when new PR comment is created
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 * * * *"
|
||||
|
||||
# Limit to single concurrent run for workflows which can create Jira issues.
|
||||
# Same concurrency group is used in issue_comment.yml
|
||||
concurrency: jira_issues
|
||||
|
||||
jobs:
|
||||
sync_prs_to_jira:
|
||||
name: Sync PRs to Jira
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Sync PRs to Jira project
|
||||
uses: espressif/github-actions/sync_issues_to_jira@master
|
||||
with:
|
||||
cron_job: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
JIRA_PASS: ${{ secrets.JIRA_PASS }}
|
||||
JIRA_PROJECT: IDFGH
|
||||
JIRA_COMPONENT: esp-mqtt
|
||||
JIRA_URL: ${{ secrets.JIRA_URL }}
|
||||
JIRA_USER: ${{ secrets.JIRA_USER }}
|
64
.github/workflows/run-on-target.yml
vendored
Normal file
64
.github/workflows/run-on-target.yml
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
name: Run on target
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
idf_version:
|
||||
required: true
|
||||
type: string
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
app_name:
|
||||
type: string
|
||||
required: true
|
||||
app_path:
|
||||
type: string
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
target-test:
|
||||
if: github.repository == 'espressif/esp-mqtt'
|
||||
name: Run App on target
|
||||
env:
|
||||
IDF_PATH: idf
|
||||
runs-on: [self-hosted, ESP32-ETHERNET-KIT]
|
||||
steps:
|
||||
- name: Select idf ref
|
||||
id: detect_version
|
||||
run: |
|
||||
if echo "${{inputs.idf_version}}" | grep "latest" -> /dev/null ; then
|
||||
echo ref="master" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo ref="`echo ${{matrix.idf_version}} | sed -s "s/-/\//"`" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: IP discovery
|
||||
id: detect_ip
|
||||
run: |
|
||||
apt-get update && apt-get install -y iproute2
|
||||
ip route
|
||||
echo runner_ip ="`ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'`" >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout IDF ${{inputs.idf_version}}
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: espressif/esp-idf
|
||||
path: ${{env.IDF_PATH}}
|
||||
ref: ${{steps.detect_version.outputs.ref}}
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://dl.espressif.com/pypi/"
|
||||
run: |
|
||||
pip install --only-binary cryptography -r ${{env.IDF_PATH}}/tools/requirements/requirements.pytest.txt
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: mqtt_bin_${{inputs.target}}_${{ inputs.idf_version }}_${{ inputs.app_name }}
|
||||
path: build
|
||||
|
||||
- name: Run ${{inputs.app_name}} application on ${{inputs.target}}
|
||||
run: |
|
||||
python -m pytest ${{inputs.app_path}} --log-cli-level DEBUG --app-path . --junit-xml=./results_${{inputs.app_name}}_${{inputs.idf_version}}.xml --target=${{inputs.target}}
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: results_${{inputs.app_name}}_${{inputs.idf_version}}.xml
|
||||
path: build/*.xml
|
66
.github/workflows/test-examples.yml
vendored
Normal file
66
.github/workflows/test-examples.yml
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
name: "Build example apps"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
cpp-compatibility:
|
||||
name: Cpp compatibility
|
||||
strategy:
|
||||
matrix:
|
||||
idf_version: ["release-v5.0", "release-v5.1", "latest"]
|
||||
target: ["esp32"]
|
||||
example: [{name: cpp_compatibility, path: "build_test"}]
|
||||
uses: "./.github/workflows/build-app.yml"
|
||||
with:
|
||||
idf_version: ${{matrix.idf_version}}
|
||||
target: ${{matrix.target}}
|
||||
app_name: ${{matrix.example.name}}
|
||||
app_path: $IDF_PATH/tools/test_apps/protocols/mqtt/${{matrix.example.path}}
|
||||
upload_artifacts: false
|
||||
build-only-example:
|
||||
name: Build Only Apps
|
||||
strategy:
|
||||
matrix:
|
||||
idf_version: ["release-v5.0", "release-v5.1", "latest"]
|
||||
target: ["esp32s2", "esp32c3", "esp32s3"]
|
||||
example: [{name: ssl_psk, path: "mqtt/ssl_psk"}]
|
||||
uses: "./.github/workflows/build-app.yml"
|
||||
with:
|
||||
idf_version: ${{matrix.idf_version}}
|
||||
target: ${{matrix.target}}
|
||||
app_name: ${{matrix.example.name}}
|
||||
app_path: $IDF_PATH/examples/protocols/${{matrix.example.path}}
|
||||
|
||||
build-only-ds-example:
|
||||
name: Build Only Apps
|
||||
strategy:
|
||||
matrix:
|
||||
idf_version: ["release-v5.0", "release-v5.1", "latest"]
|
||||
target: ["esp32s2", "esp32c3", "esp32s3"]
|
||||
example: [{name: ssl_ds, path: "mqtt/ssl_ds"}, {name: ssl_psk, path: "mqtt/ssl_psk"}]
|
||||
uses: "./.github/workflows/build-app.yml"
|
||||
with:
|
||||
idf_version: ${{matrix.idf_version}}
|
||||
target: ${{matrix.target}}
|
||||
app_name: ${{matrix.example.name}}
|
||||
app_path: $IDF_PATH/examples/protocols/${{matrix.example.path}}
|
||||
|
||||
build-examples:
|
||||
name: Build and Run on target
|
||||
strategy:
|
||||
matrix:
|
||||
idf_version: ["release-v5.0", "release-v5.1", "latest"]
|
||||
target: ["esp32"]
|
||||
example: [{name: tcp, path: "mqtt/tcp"}, {name: ssl, path: "mqtt/ssl"},{name: ssl_mutual_auth, path: "mqtt/ssl_mutual_auth"},{name: ws, path: "mqtt/ws"},{name: wss, path: "mqtt/wss"}]
|
||||
uses: "./.github/workflows/build-and-target-test.yml"
|
||||
with:
|
||||
idf_version: ${{matrix.idf_version}}
|
||||
target: ${{matrix.target}}
|
||||
app_name: ${{matrix.example.name}}
|
||||
app_path: $IDF_PATH/examples/protocols/${{matrix.example.path}}
|
||||
|
164
.gitlab-ci.yml
164
.gitlab-ci.yml
@ -1,14 +1,11 @@
|
||||
stages:
|
||||
- build
|
||||
- static_analysis
|
||||
- deploy_report
|
||||
- test
|
||||
- deploy
|
||||
|
||||
|
||||
variables:
|
||||
IDF_REPO: ${GITLAB_SSH_SERVER}/idf/esp-idf.git
|
||||
OLDER_IDF: release/v3.1
|
||||
RECENT_IDF: release/v3.3
|
||||
|
||||
.add_gh_key_remote: &add_gh_key_remote |
|
||||
cit_add_ssh_key "${GH_PUSH_KEY}"
|
||||
@ -19,110 +16,91 @@ before_script:
|
||||
# Use CI Tools
|
||||
- curl -sSL ${CIT_LOADER_URL} | sh
|
||||
- source citools/import_functions
|
||||
# Add gitlab ssh key
|
||||
- mkdir -p ~/.ssh
|
||||
- chmod 700 ~/.ssh
|
||||
- echo -n $GITLAB_KEY > ~/.ssh/id_rsa_base64
|
||||
- base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa
|
||||
- chmod 600 ~/.ssh/id_rsa
|
||||
- echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
|
||||
- PATH=$CI_PROJECT_DIR/esp-idf/tools:$PATH
|
||||
- export MQTT_PATH=$CI_PROJECT_DIR
|
||||
|
||||
build_with_older_idf:
|
||||
.build_template:
|
||||
stage: build
|
||||
image: ${CI_DOCKER_REGISTRY}/esp32-ci-env
|
||||
tags:
|
||||
- build
|
||||
dependencies: []
|
||||
- internet
|
||||
script:
|
||||
- cit_add_ssh_key "${GITLAB_KEY}"
|
||||
- git clone "${IDF_REPO}" && cd esp-idf && git checkout ${OLDER_IDF}
|
||||
- ./tools/ci/mirror-submodule-update.sh
|
||||
- export IDF_PATH=$(pwd)
|
||||
- cd $CI_PROJECT_DIR
|
||||
- ./modify_for_legacy_idf.sh ${RECENT_IDF} || true
|
||||
- cd $CI_PROJECT_DIR/examples/tcp
|
||||
- make defconfig
|
||||
- make
|
||||
# Replace the IDF's default esp-mqtt with this version
|
||||
- rm -rf $IDF_PATH/components/mqtt/esp-mqtt && cp -r $MQTT_PATH $IDF_PATH/components/mqtt/
|
||||
# Build the examples
|
||||
- $MQTT_PATH/ci/build_examples.sh
|
||||
|
||||
build_with_idf:
|
||||
build_idf_v5.0:
|
||||
extends: .build_template
|
||||
image: espressif/idf:release-v5.0
|
||||
|
||||
build_idf_v5.1:
|
||||
extends: .build_template
|
||||
image: espressif/idf:release-v5.1
|
||||
|
||||
build_idf_latest:
|
||||
extends: .build_template
|
||||
image: espressif/idf:latest
|
||||
|
||||
build_and_host_test:
|
||||
stage: build
|
||||
image: ${CI_DOCKER_REGISTRY}/esp32-ci-env
|
||||
image: espressif/idf:latest
|
||||
script:
|
||||
# Replace the IDF's default esp-mqtt with this version
|
||||
- rm -rf $IDF_PATH/components/mqtt/esp-mqtt && cp -r $MQTT_PATH $IDF_PATH/components/mqtt/
|
||||
- cd $IDF_PATH/components/mqtt/esp-mqtt/host_test
|
||||
- idf.py build
|
||||
- build/host_mqtt_client_test.elf
|
||||
|
||||
build_and_test_qemu:
|
||||
stage: build
|
||||
image: ${CI_DOCKER_REGISTRY}/qemu-v5.1:1-20220802
|
||||
tags:
|
||||
- build
|
||||
- shiny
|
||||
dependencies: []
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- tidybuild/*
|
||||
expire_in: 1 day
|
||||
script:
|
||||
- cit_add_ssh_key "${GITLAB_KEY}"
|
||||
- export IDF_PATH=$CI_PROJECT_DIR/esp-idf
|
||||
- git clone "${IDF_REPO}"
|
||||
- cd esp-idf
|
||||
- ./tools/ci/mirror-submodule-update.sh
|
||||
- export IDF_PATH=$(pwd)
|
||||
- cd $IDF_PATH/components/mqtt/esp-mqtt
|
||||
- rm -rf .git
|
||||
- cp -r $CI_PROJECT_DIR/.git .
|
||||
- git reset --hard $CI_COMMIT_SHA
|
||||
# capture compile commands/flags for static analysis
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/tcp
|
||||
- mkdir -p tidybuild && cd tidybuild
|
||||
- cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
|
||||
- mv $IDF_PATH/examples/protocols/mqtt/tcp/tidybuild $CI_PROJECT_DIR/tidybuild
|
||||
# enable pedantic flags for compilation
|
||||
- export PEDANTIC_CFLAGS="-Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function"
|
||||
- export EXTRA_CFLAGS=${PEDANTIC_CFLAGS} && export EXTRA_CXXFLAGS=${EXTRA_CFLAGS}
|
||||
# build other examples
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/tcp && idf.py build
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/ssl && idf.py build
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/ssl_mutual_auth && idf.py build
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/ws && idf.py build
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/wss && idf.py build
|
||||
# switch to IDF and setup the tools
|
||||
- $MQTT_PATH/ci/set_idf.sh release/v5.1
|
||||
- $IDF_PATH/tools/idf_tools.py install-python-env
|
||||
- cd $IDF_PATH && tools/idf_tools.py --non-interactive install && eval "$(tools/idf_tools.py --non-interactive export)"
|
||||
# Remove `debug_backend` and Add `paho-mqtt` to the required packages
|
||||
- sed '/debug_backend/d;/pygobject/d' $IDF_PATH/tools/requirements/requirements.ttfw.txt > requirements.txt
|
||||
- python -m pip install -r requirements.txt
|
||||
- python -m pip install paho-mqtt
|
||||
- $MQTT_PATH/ci/set_mqtt.sh $CI_COMMIT_SHA
|
||||
# build publish-connect stress test, setup test parameters
|
||||
- cd tools/test_apps/protocols/mqtt/publish_connect_test && cat sdkconfig.qemu | $IDF_PATH/tools/ci/envsubst.py > sdkconfig.defaults && idf.py build
|
||||
- export TEST_PATH=`pwd` && export MQTT_PUBLISH_TEST=1
|
||||
- export PYTHONPATH="$IDF_PATH/tools:$IDF_PATH/tools/ci/python_packages"
|
||||
# run test (with environment->qemu)
|
||||
- cd $IDF_PATH/tools/ci/python_packages/tiny_test_fw/bin
|
||||
# use more relaxed criteria with QEMU tests
|
||||
- export MQTT_PUBLISH_MSG_len_0=0 MQTT_PUBLISH_MSG_repeat_0=5
|
||||
- export MQTT_PUBLISH_MSG_len_1=2 MQTT_PUBLISH_MSG_repeat_1=50
|
||||
- export MQTT_PUBLISH_MSG_len_2=128 MQTT_PUBLISH_MSG_repeat_2=2
|
||||
- export MQTT_PUBLISH_MSG_len_3=20 MQTT_PUBLISH_MSG_repeat_3=20
|
||||
- python Runner.py $TEST_PATH -c $MQTT_PATH/ci/publish_connect_mqtt_qemu.yml -e $TEST_PATH/env.yml
|
||||
|
||||
clang_tidy_check:
|
||||
stage: static_analysis
|
||||
image: ${CI_DOCKER_REGISTRY}/clang-static-analysis
|
||||
tags:
|
||||
- host_test
|
||||
dependencies:
|
||||
- build_with_idf
|
||||
artifacts:
|
||||
reports:
|
||||
junit: esp-idf/examples/protocols/mqtt/tcp/tidybuild/output.xml
|
||||
when: always
|
||||
paths:
|
||||
- esp-idf/examples/protocols/mqtt/tcp/tidybuild/report/*
|
||||
expire_in: 1 day
|
||||
check_remotes_sync:
|
||||
stage: test
|
||||
except:
|
||||
- master
|
||||
- idf
|
||||
script:
|
||||
- cit_add_ssh_key "${GITLAB_KEY}"
|
||||
- git clone "${IDF_REPO}"
|
||||
- cd esp-idf
|
||||
- ./tools/ci/mirror-submodule-update.sh
|
||||
- export IDF_PATH=$(pwd)
|
||||
- cd $IDF_PATH/components/mqtt/esp-mqtt
|
||||
- rm -rf .git
|
||||
- cp -r $CI_PROJECT_DIR/.git .
|
||||
- git reset --hard $CI_COMMIT_SHA
|
||||
- mv $CI_PROJECT_DIR/tidybuild $IDF_PATH/examples/protocols/mqtt/tcp/tidybuild
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/tcp/tidybuild
|
||||
- git clone $IDF_ANALYSIS_UTILS static_analysis_utils && cd static_analysis_utils
|
||||
- ./generate_report.sh $CI_PROJECT_DIR/static-analysis-rules.yml $IDF_PATH/examples/protocols/mqtt/tcp/tidybuild/report $IDF_PATH/examples/protocols/mqtt/tcp/tidybuild/output.xml
|
||||
|
||||
deploy_report:
|
||||
stage: deploy_report
|
||||
image: $CI_DOCKER_REGISTRY/esp32-ci-env
|
||||
tags:
|
||||
- deploy
|
||||
dependencies:
|
||||
- clang_tidy_check
|
||||
script:
|
||||
- cit_add_ssh_key "${DOCS_DEPLOY_KEY}"
|
||||
- echo -e "Host $DOCS_SERVER\n\tStrictHostKeyChecking no\n\tUser $DOCS_SERVER_USER\n" >> ~/.ssh/config
|
||||
- export GIT_VER=$(git describe --always)
|
||||
- cd esp-idf/examples/protocols/mqtt/tcp/tidybuild
|
||||
- mv report $GIT_VER
|
||||
- tar czvf $GIT_VER.tar.gz $GIT_VER
|
||||
- ssh $DOCS_SERVER -x "mkdir -p $DOCS_PATH/clang-tidy"
|
||||
- scp $GIT_VER.tar.gz $DOCS_SERVER:$DOCS_PATH/clang-tidy
|
||||
- ssh $DOCS_SERVER -x "cd $DOCS_PATH/clang-tidy && tar xzvf $GIT_VER.tar.gz && rm -f latest && ln -s $GIT_VER latest"
|
||||
# add link to view the report
|
||||
- echo "[static analysis][clang tidy] $CI_DOCKER_REGISTRY/static_analysis/esp-idf/clang-tidy/${GIT_VER}/index.html"
|
||||
- test ! -e ${GIT_VER}/FAILED_RULES || { echo 'Failed static analysis rules!'; cat ${GIT_VER}/FAILED_RULES; exit 1; }
|
||||
- *add_gh_key_remote
|
||||
- git fetch --depth=1 origin master
|
||||
- git fetch --depth=1 github master
|
||||
- test "$(git rev-parse origin/master)" == "$(git rev-parse github/master)"
|
||||
|
||||
push_master_to_github:
|
||||
stage: deploy
|
||||
|
@ -17,7 +17,7 @@ before_install:
|
||||
- git checkout -b temporary_ref_branch
|
||||
- CI_COMMIT_SHA=$(git rev-parse HEAD)
|
||||
# Test building with latest (stable == v3.3 for now) IDF
|
||||
- LATEST_IDF=release/v3.3
|
||||
- LTS_IDF=release/v3.3
|
||||
|
||||
install:
|
||||
# Install ESP32 toochain following steps as desribed
|
||||
@ -44,13 +44,13 @@ script:
|
||||
- cd $IDF_PATH
|
||||
- git checkout v3.1 && git submodule update --init --recursive
|
||||
- cd $PROJECT_PATH
|
||||
- ./modify_for_legacy_idf.sh ${LATEST_IDF} || true
|
||||
- ./ci/modify_for_legacy_idf.sh ${LTS_IDF} || true
|
||||
- cd $PROJECT_PATH/examples/tcp
|
||||
- make defconfig
|
||||
- make -j4
|
||||
# Build with latest IDF
|
||||
# Build with v3.3 (LTS) IDF
|
||||
- cd $IDF_PATH
|
||||
- git checkout ${LATEST_IDF} && git submodule update --init --recursive
|
||||
- git checkout ${LTS_IDF} && git submodule update --init --recursive
|
||||
- cd $IDF_PATH/components/mqtt/esp-mqtt
|
||||
- git remote add local $PROJECT_PATH/.git
|
||||
- git fetch local
|
||||
|
14
CMakeLists.txt
Normal file
14
CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
set(srcs mqtt_client.c lib/mqtt_msg.c lib/mqtt_outbox.c lib/platform_esp32_idf.c)
|
||||
|
||||
if(CONFIG_MQTT_PROTOCOL_5)
|
||||
list(APPEND srcs lib/mqtt5_msg.c mqtt5_client.c)
|
||||
endif()
|
||||
|
||||
list(TRANSFORM srcs PREPEND ${CMAKE_CURRENT_LIST_DIR}/)
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/include
|
||||
PRIV_INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/lib/include
|
||||
REQUIRES esp_event tcp_transport
|
||||
PRIV_REQUIRES esp_timer http_parser esp_hw_support heap
|
||||
KCONFIG ${CMAKE_CURRENT_LIST_DIR}/Kconfig
|
||||
)
|
180
Kconfig
Normal file
180
Kconfig
Normal file
@ -0,0 +1,180 @@
|
||||
menu "ESP-MQTT Configurations"
|
||||
|
||||
config MQTT_PROTOCOL_311
|
||||
bool "Enable MQTT protocol 3.1.1"
|
||||
default y
|
||||
help
|
||||
If not, this library will use MQTT protocol 3.1
|
||||
|
||||
config MQTT_PROTOCOL_5
|
||||
bool "Enable MQTT protocol 5.0"
|
||||
default n
|
||||
help
|
||||
If not, this library will not support MQTT 5.0
|
||||
|
||||
config MQTT_TRANSPORT_SSL
|
||||
bool "Enable MQTT over SSL"
|
||||
default y
|
||||
help
|
||||
Enable MQTT transport over SSL with mbedtls
|
||||
|
||||
config MQTT_TRANSPORT_WEBSOCKET
|
||||
bool "Enable MQTT over Websocket"
|
||||
default y
|
||||
depends on WS_TRANSPORT
|
||||
help
|
||||
Enable MQTT transport over Websocket.
|
||||
|
||||
config MQTT_TRANSPORT_WEBSOCKET_SECURE
|
||||
bool "Enable MQTT over Websocket Secure"
|
||||
default y
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET
|
||||
depends on MQTT_TRANSPORT_SSL
|
||||
help
|
||||
Enable MQTT transport over Websocket Secure.
|
||||
|
||||
config MQTT_MSG_ID_INCREMENTAL
|
||||
bool "Use Incremental Message Id"
|
||||
default n
|
||||
help
|
||||
Set this to true for the message id (2.3.1 Packet Identifier) to be generated
|
||||
as an incremental number rather then a random value (used by default)
|
||||
|
||||
config MQTT_SKIP_PUBLISH_IF_DISCONNECTED
|
||||
bool "Skip publish if disconnected"
|
||||
default n
|
||||
help
|
||||
Set this to true to avoid publishing (enqueueing messages) if the client is disconnected.
|
||||
The MQTT client tries to publish all messages by default, even in the disconnected state
|
||||
(where the qos1 and qos2 packets are stored in the internal outbox to be published later)
|
||||
The MQTT_SKIP_PUBLISH_IF_DISCONNECTED option allows applications to override this behaviour
|
||||
and not enqueue publish packets in the disconnected state.
|
||||
|
||||
config MQTT_REPORT_DELETED_MESSAGES
|
||||
bool "Report deleted messages"
|
||||
default n
|
||||
help
|
||||
Set this to true to post events for all messages which were deleted from the outbox
|
||||
before being correctly sent and confirmed.
|
||||
|
||||
config MQTT_USE_CUSTOM_CONFIG
|
||||
bool "MQTT Using custom configurations"
|
||||
default n
|
||||
help
|
||||
Custom MQTT configurations.
|
||||
|
||||
config MQTT_TCP_DEFAULT_PORT
|
||||
int "Default MQTT over TCP port"
|
||||
default 1883
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Default MQTT over TCP port
|
||||
|
||||
config MQTT_SSL_DEFAULT_PORT
|
||||
int "Default MQTT over SSL port"
|
||||
default 8883
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
depends on MQTT_TRANSPORT_SSL
|
||||
help
|
||||
Default MQTT over SSL port
|
||||
|
||||
config MQTT_WS_DEFAULT_PORT
|
||||
int "Default MQTT over Websocket port"
|
||||
default 80
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET
|
||||
help
|
||||
Default MQTT over Websocket port
|
||||
|
||||
config MQTT_WSS_DEFAULT_PORT
|
||||
int "Default MQTT over Websocket Secure port"
|
||||
default 443
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET_SECURE
|
||||
help
|
||||
Default MQTT over Websocket Secure port
|
||||
|
||||
config MQTT_BUFFER_SIZE
|
||||
int "Default MQTT Buffer Size"
|
||||
default 1024
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
This buffer size using for both transmit and receive
|
||||
|
||||
config MQTT_TASK_STACK_SIZE
|
||||
int "MQTT task stack size"
|
||||
default 6144
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
MQTT task stack size
|
||||
|
||||
config MQTT_DISABLE_API_LOCKS
|
||||
bool "Disable API locks"
|
||||
default n
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Default config employs API locks to protect internal structures. It is possible to disable
|
||||
these locks if the user code doesn't access MQTT API from multiple concurrent tasks
|
||||
|
||||
config MQTT_TASK_PRIORITY
|
||||
int "MQTT task priority"
|
||||
default 5
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
MQTT task priority. Higher number denotes higher priority.
|
||||
|
||||
config MQTT_POLL_READ_TIMEOUT_MS
|
||||
int "MQTT transport poll read timeut"
|
||||
default 1000
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Timeout when polling underlying transport for read.
|
||||
|
||||
config MQTT_EVENT_QUEUE_SIZE
|
||||
int "Number of queued events."
|
||||
default 1
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
A value higher than 1 enables multiple queued events.
|
||||
|
||||
config MQTT_TASK_CORE_SELECTION_ENABLED
|
||||
bool "Enable MQTT task core selection"
|
||||
help
|
||||
This will enable core selection
|
||||
|
||||
choice MQTT_TASK_CORE_SELECTION
|
||||
depends on MQTT_TASK_CORE_SELECTION_ENABLED
|
||||
prompt "Core to use ?"
|
||||
config MQTT_USE_CORE_0
|
||||
bool "Core 0"
|
||||
config MQTT_USE_CORE_1
|
||||
bool "Core 1"
|
||||
endchoice
|
||||
|
||||
config MQTT_OUTBOX_DATA_ON_EXTERNAL_MEMORY
|
||||
bool "Use external memory for outbox data"
|
||||
default n
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Set to true to use external memory for outbox data.
|
||||
|
||||
config MQTT_CUSTOM_OUTBOX
|
||||
bool "Enable custom outbox implementation"
|
||||
default n
|
||||
help
|
||||
Set to true if a specific implementation of message outbox is needed (e.g. persistent outbox in NVM or
|
||||
similar).
|
||||
Note: Implementation of the custom outbox must be added to the mqtt component. These CMake commands
|
||||
could be used to append the custom implementation to lib-mqtt sources:
|
||||
idf_component_get_property(mqtt mqtt COMPONENT_LIB)
|
||||
set_property(TARGET ${mqtt} PROPERTY SOURCES ${PROJECT_DIR}/custom_outbox.c APPEND)
|
||||
|
||||
config MQTT_OUTBOX_EXPIRED_TIMEOUT_MS
|
||||
int "Outbox message expired timeout[ms]"
|
||||
default 30000
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Messages which stays in the outbox longer than this value before being published will be discarded.
|
||||
|
||||
endmenu
|
11
ci/build-test-rules.yml
Normal file
11
ci/build-test-rules.yml
Normal file
@ -0,0 +1,11 @@
|
||||
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||
|
||||
examples/protocols:
|
||||
enable:
|
||||
- if: IDF_TARGET in ["esp32"]
|
||||
|
||||
examples/protocols/mqtt/ssl_ds:
|
||||
disable:
|
||||
- if: SOC_DIG_SIGN_SUPPORTED != 1
|
||||
temporary: false
|
||||
reason: DS not present
|
22
ci/build_examples.sh
Executable file
22
ci/build_examples.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# build mqtt examples with make if $1=="make", with cmake otherwise
|
||||
set -o errexit # Exit if command failed.
|
||||
|
||||
if [ -z $IDF_PATH ] ; then
|
||||
echo "Mandatory variables undefined"
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
examples="tcp ssl ssl_mutual_auth ws wss"
|
||||
for i in $examples; do
|
||||
echo "Building MQTT example $i"
|
||||
cd $IDF_PATH/examples/protocols/mqtt/$i
|
||||
if [[ "$1" = "make" ]]; then
|
||||
make defconfig
|
||||
make -j 4
|
||||
else
|
||||
rm -rf build sdkconfig
|
||||
idf.py build
|
||||
fi;
|
||||
done
|
2
ci/idf_build_apps.toml
Normal file
2
ci/idf_build_apps.toml
Normal file
@ -0,0 +1,2 @@
|
||||
build_dir = "$GITHUB_WORKSPACE/build_@t_@n"
|
||||
config="sdkconfig.ci"
|
7
ci/publish_connect_mqtt_qemu.yml
Normal file
7
ci/publish_connect_mqtt_qemu.yml
Normal file
@ -0,0 +1,7 @@
|
||||
CaseConfig:
|
||||
- name: test_app_protocol_mqtt_publish_connect
|
||||
overwrite:
|
||||
dut:
|
||||
class: ESP32QEMUDUT
|
||||
package: ttfw_idf
|
||||
|
19
ci/set_idf.sh
Executable file
19
ci/set_idf.sh
Executable file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# sets up the IDF repo incl submodules with specified version as $1
|
||||
set -o errexit # Exit if command failed.
|
||||
|
||||
if [ -z $IDF_PATH ] || [ -z $MQTT_PATH ] || [ -z $1 ] ; then
|
||||
echo "Mandatory variables undefined"
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
echo "Checking out IDF version $1"
|
||||
cd $IDF_PATH
|
||||
# Cleans out the untracked files in the repo, so the next "git checkout" doesn't fail
|
||||
git clean -f
|
||||
git checkout $1
|
||||
# Removes the mqtt submodule, so the next submodule update doesn't fail
|
||||
rm -rf $IDF_PATH/components/mqtt/esp-mqtt
|
||||
git submodule update --init --recursive
|
||||
|
16
ci/set_mqtt.sh
Executable file
16
ci/set_mqtt.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# sets the mqtt in IDF tree as a submodule with the version specified as $1
|
||||
set -o errexit # Exit if command failed.
|
||||
|
||||
if [ -z $IDF_PATH ] || [ -z $MQTT_PATH ] || [ -z $1 ] ; then
|
||||
echo "Mandatory variables undefined"
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
echo "Checking out MQTT version to $1"
|
||||
# exchange remotes of mqtt submodules with plain copy
|
||||
cd $IDF_PATH/components/mqtt/esp-mqtt
|
||||
rm -rf .git # removes the actual IDF referenced version
|
||||
cp -r $MQTT_PATH/.git . # replaces with the MQTT_PATH (CI checked tree)
|
||||
git reset --hard $1 # sets the requested version
|
@ -1,19 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
get_filename_component(DEV_ROOT "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE)
|
||||
|
||||
set(PROJECT_ROOT "${DEV_ROOT}/")
|
||||
|
||||
set(SUBMODULE_ROOT "${DEV_ROOT}/../../../")
|
||||
|
||||
set(PROJECT_NAME "mqtt_ssl")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
set(MAIN_SRCS ${PROJECT_ROOT}/main/app_main.c)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "${EXTRA_COMPONENT_DIRS} ${SUBMODULE_ROOT}")
|
||||
set(BUILD_COMPONENTS "${BUILD_COMPONENTS} espmqtt")
|
||||
|
||||
project(${PROJECT_NAME})
|
||||
|
@ -1,13 +0,0 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
PROJECT_NAME := emitter_client
|
||||
EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/../../../
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
@ -1,10 +0,0 @@
|
||||
# ESPMQTT Emitter client
|
||||
|
||||
## Before you run this Example
|
||||
- Register an account from https://emitter.io/
|
||||
- Login and create channel key, grant access for the channel `/topic/` as the images bellow
|
||||
- `make menuconfig` provide Wi-Fi information and CHANNEL_KEY to `MQTT Application example`
|
||||
- `make flash monitor`
|
||||
|
||||

|
||||

|
Binary file not shown.
Before Width: | Height: | Size: 234 KiB |
Binary file not shown.
Before Width: | Height: | Size: 61 KiB |
@ -1,22 +0,0 @@
|
||||
menu "MQTT Application example"
|
||||
|
||||
config WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "mypassword"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use.
|
||||
|
||||
config EMITTER_CHANNEL_KEY
|
||||
string "Emitter channel key"
|
||||
default ""
|
||||
help
|
||||
The Emitter channel key using to pub/sub
|
||||
|
||||
|
||||
endmenu
|
@ -1,143 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_event_loop.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/netdb.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "mqtt_client.h"
|
||||
|
||||
static const char *TAG = "MQTTS_SAMPLE";
|
||||
|
||||
static EventGroupHandle_t wifi_event_group;
|
||||
const static int CONNECTED_BIT = BIT0;
|
||||
|
||||
|
||||
|
||||
static esp_err_t wifi_event_handler(void *ctx, system_event_t *event)
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case SYSTEM_EVENT_STA_START:
|
||||
esp_wifi_connect();
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
||||
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
esp_wifi_connect();
|
||||
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void wifi_init(void)
|
||||
{
|
||||
tcpip_adapter_init();
|
||||
wifi_event_group = xEventGroupCreate();
|
||||
ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_handler, NULL));
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.ssid = CONFIG_WIFI_SSID,
|
||||
.password = CONFIG_WIFI_PASSWORD,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
|
||||
ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]", CONFIG_WIFI_SSID, "******");
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
ESP_LOGI(TAG, "Waiting for wifi");
|
||||
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static esp_err_t mqtt_event_handler(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, CONFIG_EMITTER_CHANNEL_KEY"/topic/", 0);
|
||||
ESP_LOGI(TAG, "sent subscribe 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", event->msg_id);
|
||||
msg_id = esp_mqtt_client_publish(client, CONFIG_EMITTER_CHANNEL_KEY"/topic/", "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;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mqtt_app_start(void)
|
||||
{
|
||||
const esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.uri = "mqtts://api.emitter.io:443", // for mqtt over ssl
|
||||
// .uri = "mqtt://api.emitter.io:8080", //for mqtt over tcp
|
||||
// .uri = "ws://api.emitter.io:8080", //for mqtt over websocket
|
||||
// .uri = "wss://api.emitter.io:443", //for mqtt over websocket secure
|
||||
.event_handle = mqtt_event_handler,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
|
||||
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
esp_mqtt_client_start(client);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
ESP_LOGI(TAG, "[APP] Startup..");
|
||||
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("MQTT_CLIENT", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE);
|
||||
|
||||
nvs_flash_init();
|
||||
wifi_init();
|
||||
mqtt_app_start();
|
||||
|
||||
}
|
16
host_test/CMakeLists.txt
Normal file
16
host_test/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(COMPONENTS mqtt main)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS
|
||||
"mocks/heap/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/esp_hw_support/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/freertos/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/esp_timer/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/esp_event/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/lwip/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/esp-tls/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/http_parser/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/tcp_transport/")
|
||||
|
||||
project(host_mqtt_client_test)
|
30
host_test/README.md
Normal file
30
host_test/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
| Supported Targets | Linux |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# Description
|
||||
|
||||
This directory contains test code for the mqtt client that runs on host.
|
||||
|
||||
Tests are written using [Catch2](https://github.com/catchorg/Catch2) test framework
|
||||
|
||||
# Build
|
||||
|
||||
Tests build regularly like an idf project.
|
||||
|
||||
```
|
||||
idf.py build
|
||||
```
|
||||
|
||||
# Run
|
||||
|
||||
The build produces an executable in the build folder.
|
||||
|
||||
Just run:
|
||||
|
||||
```
|
||||
./build/host_mqtt_client_test.elf
|
||||
```
|
||||
|
||||
The test executable have some options provided by the test framework.
|
||||
|
||||
|
20
host_test/main/CMakeLists.txt
Normal file
20
host_test/main/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
idf_component_register(SRCS "test_mqtt_client.cpp"
|
||||
INCLUDE_DIRS "$ENV{IDF_PATH}/tools/catch"
|
||||
REQUIRES cmock mqtt esp_timer esp_hw_support http_parser log)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC -fsanitize=address -fconcepts)
|
||||
target_link_options(${COMPONENT_LIB} PUBLIC -fsanitize=address)
|
||||
|
||||
idf_component_get_property(mqtt mqtt COMPONENT_LIB)
|
||||
target_compile_definitions(${mqtt} PRIVATE SOC_WIFI_SUPPORTED=1)
|
||||
target_compile_options(${mqtt} PUBLIC -fsanitize=address -fconcepts)
|
||||
target_link_options(${mqtt} PUBLIC -fsanitize=address)
|
||||
|
||||
if(CONFIG_GCOV_ENABLED)
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
target_link_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
|
||||
idf_component_get_property(mqtt mqtt COMPONENT_LIB)
|
||||
target_compile_options(${mqtt} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
target_link_options(${mqtt} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
endif()
|
9
host_test/main/Kconfig
Normal file
9
host_test/main/Kconfig
Normal file
@ -0,0 +1,9 @@
|
||||
menu "Host-test config"
|
||||
|
||||
config GCOV_ENABLED
|
||||
bool "Coverage analyzer"
|
||||
default n
|
||||
help
|
||||
Enables coverage analyzing for host tests.
|
||||
|
||||
endmenu
|
169
host_test/main/test_mqtt_client.cpp
Normal file
169
host_test/main/test_mqtt_client.cpp
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <memory>
|
||||
#include <net/if.h>
|
||||
#include <random>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include "esp_transport.h"
|
||||
#define CATCH_CONFIG_MAIN // This tells the catch header to generate a main
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "mqtt_client.h"
|
||||
extern "C" {
|
||||
#include "Mockesp_event.h"
|
||||
#include "Mockesp_mac.h"
|
||||
#include "Mockesp_transport.h"
|
||||
#include "Mockesp_transport_ssl.h"
|
||||
#include "Mockesp_transport_tcp.h"
|
||||
#include "Mockesp_transport_ws.h"
|
||||
#include "Mockevent_groups.h"
|
||||
#include "Mockhttp_parser.h"
|
||||
#include "Mockqueue.h"
|
||||
#include "Mocktask.h"
|
||||
#if __has_include ("Mockidf_additions.h")
|
||||
/* Some functions were moved from "task.h" to "idf_additions.h" */
|
||||
#include "Mockidf_additions.h"
|
||||
#endif
|
||||
#include "Mockesp_timer.h"
|
||||
|
||||
/*
|
||||
* The following functions are not directly called but the generation of them
|
||||
* from cmock is broken, so we need to define them here.
|
||||
*/
|
||||
esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *esp_tls_code, int *esp_tls_flags)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
auto random_string(std::size_t n)
|
||||
{
|
||||
static constexpr std::string_view char_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456790";
|
||||
std::string str;
|
||||
std::sample(char_set.begin(), char_set.end(), std::back_inserter(str), n,
|
||||
std::mt19937 {std::random_device{}()});
|
||||
return str;
|
||||
}
|
||||
|
||||
using unique_mqtt_client = std::unique_ptr < std::remove_pointer_t<esp_mqtt_client_handle_t>, decltype([](esp_mqtt_client_handle_t client)
|
||||
{
|
||||
esp_mqtt_client_destroy(client);
|
||||
}) >;
|
||||
|
||||
SCENARIO("MQTT Client Operation")
|
||||
{
|
||||
// Set expectations for the mocked calls.
|
||||
int mtx = 0;
|
||||
int transport_list = 0;
|
||||
int transport = 0;
|
||||
int event_group = 0;
|
||||
uint8_t mac[] = {0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55};
|
||||
esp_timer_get_time_IgnoreAndReturn(0);
|
||||
xQueueTakeMutexRecursive_IgnoreAndReturn(true);
|
||||
xQueueGiveMutexRecursive_IgnoreAndReturn(true);
|
||||
xQueueCreateMutex_ExpectAnyArgsAndReturn(
|
||||
reinterpret_cast<QueueHandle_t>(&mtx));
|
||||
xEventGroupCreate_IgnoreAndReturn(reinterpret_cast<EventGroupHandle_t>(&event_group));
|
||||
esp_transport_list_init_IgnoreAndReturn(reinterpret_cast<esp_transport_list_handle_t>(&transport_list));
|
||||
esp_transport_tcp_init_IgnoreAndReturn(reinterpret_cast<esp_transport_handle_t>(&transport));
|
||||
esp_transport_ssl_init_IgnoreAndReturn(reinterpret_cast<esp_transport_handle_t>(&transport));
|
||||
esp_transport_ws_init_IgnoreAndReturn(reinterpret_cast<esp_transport_handle_t>(&transport));
|
||||
esp_transport_ws_set_subprotocol_IgnoreAndReturn(ESP_OK);
|
||||
esp_transport_list_add_IgnoreAndReturn(ESP_OK);
|
||||
esp_transport_set_default_port_IgnoreAndReturn(ESP_OK);
|
||||
http_parser_url_init_Ignore();
|
||||
esp_event_loop_create_IgnoreAndReturn(ESP_OK);
|
||||
esp_read_mac_IgnoreAndReturn(ESP_OK);
|
||||
esp_read_mac_ReturnThruPtr_mac(mac);
|
||||
esp_transport_list_destroy_IgnoreAndReturn(ESP_OK);
|
||||
esp_transport_destroy_IgnoreAndReturn(ESP_OK);
|
||||
vEventGroupDelete_Ignore();
|
||||
vQueueDelete_Ignore();
|
||||
GIVEN("An a minimal config") {
|
||||
esp_mqtt_client_config_t config{};
|
||||
config.broker.address.uri = "mqtt://1.1.1.1";
|
||||
struct http_parser_url ret_uri = {
|
||||
.field_set = 1 | (1 << 1),
|
||||
.port = 0,
|
||||
.field_data = { { 0, 4 } /*mqtt*/, { 7, 1 } } // at least *scheme* and *host*
|
||||
};
|
||||
http_parser_parse_url_ExpectAnyArgsAndReturn(0);
|
||||
http_parser_parse_url_ReturnThruPtr_u(&ret_uri);
|
||||
xTaskCreatePinnedToCore_ExpectAnyArgsAndReturn(pdTRUE);
|
||||
SECTION("Client with minimal config") {
|
||||
auto client = unique_mqtt_client{esp_mqtt_client_init(&config)};
|
||||
REQUIRE(client != nullptr);
|
||||
SECTION("User will set a new uri") {
|
||||
struct http_parser_url ret_uri = {
|
||||
.field_set = 1,
|
||||
.port = 0,
|
||||
.field_data = { { 0, 1} }
|
||||
};
|
||||
SECTION("User set a correct URI") {
|
||||
http_parser_parse_url_StopIgnore();
|
||||
http_parser_parse_url_ExpectAnyArgsAndReturn(0);
|
||||
http_parser_parse_url_ReturnThruPtr_u(&ret_uri);
|
||||
auto res = esp_mqtt_client_set_uri(client.get(), " ");
|
||||
REQUIRE(res == ESP_OK);
|
||||
}
|
||||
SECTION("Incorrect URI from user") {
|
||||
http_parser_parse_url_StopIgnore();
|
||||
http_parser_parse_url_ExpectAnyArgsAndReturn(1);
|
||||
http_parser_parse_url_ReturnThruPtr_u(&ret_uri);
|
||||
auto res = esp_mqtt_client_set_uri(client.get(), " ");
|
||||
REQUIRE(res == ESP_FAIL);
|
||||
}
|
||||
}
|
||||
SECTION("User set interface to use"){
|
||||
http_parser_parse_url_ExpectAnyArgsAndReturn(0);
|
||||
http_parser_parse_url_ReturnThruPtr_u(&ret_uri);
|
||||
struct ifreq if_name = {.ifr_ifrn = {"custom"}};
|
||||
config.network.if_name = &if_name;
|
||||
SECTION("Client is not started"){
|
||||
REQUIRE(esp_mqtt_set_config(client.get(), &config)== ESP_OK);
|
||||
}
|
||||
}
|
||||
SECTION("After Start Client Is Cleanly destroyed") {
|
||||
REQUIRE(esp_mqtt_client_start(client.get()) == ESP_OK);
|
||||
// Only need to start the client, destroy is called automatically at the end of
|
||||
// scope
|
||||
}
|
||||
}
|
||||
SECTION("Client with all allocating configuration set") {
|
||||
auto host = random_string(20);
|
||||
auto path = random_string(10);
|
||||
auto username = random_string(10);
|
||||
auto client_id = random_string(10);
|
||||
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()
|
||||
}
|
||||
};
|
||||
config.credentials = {
|
||||
.username = username.data(),
|
||||
.client_id = client_id.data(),
|
||||
.authentication = {
|
||||
.password = password.data()
|
||||
}
|
||||
};
|
||||
config.session = {
|
||||
.last_will {
|
||||
.topic = lw_topic.data(),
|
||||
.msg = lw_msg.data()
|
||||
}
|
||||
};
|
||||
auto client = unique_mqtt_client{esp_mqtt_client_init(&config)};
|
||||
REQUIRE(client != nullptr);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
4
host_test/mocks/heap/CMakeLists.txt
Normal file
4
host_test/mocks/heap/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
idf_component_get_property(original_heap_dir heap COMPONENT_OVERRIDEN_DIR)
|
||||
|
||||
idf_component_register(SRCS heap_mock.c
|
||||
INCLUDE_DIRS "${original_heap_dir}/include")
|
11
host_test/mocks/heap/heap_mock.c
Normal file
11
host_test/mocks/heap/heap_mock.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include "esp_heap_caps.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
|
||||
void *heap_caps_calloc(size_t n, size_t size, uint32_t caps) {
|
||||
(void)caps;
|
||||
return calloc(n, size);
|
||||
|
||||
}
|
71
host_test/mocks/include/sys/queue.h
Normal file
71
host_test/mocks/include/sys/queue.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 1991-1993 The Regents of the University of California
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/* Implementation from BSD headers*/
|
||||
#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
|
||||
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
|
||||
#define STAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *stqh_first;/* first element */ \
|
||||
struct type **stqh_last;/* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
STAILQ_NEXT((elm), field) = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INIT(head) do { \
|
||||
STAILQ_FIRST((head)) = NULL; \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for((var) = STAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = STAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT(elm, field) = \
|
||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = \
|
||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
|
||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = STAILQ_FIRST((head)); \
|
||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
||||
curelm = STAILQ_NEXT(curelm, field); \
|
||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
||||
} \
|
||||
TRASHIT(*oldnext); \
|
||||
} while (0)
|
1
host_test/sdkconfig.ci.coverage
Normal file
1
host_test/sdkconfig.ci.coverage
Normal file
@ -0,0 +1 @@
|
||||
CONFIG_GCOV_ENABLED=y
|
6
host_test/sdkconfig.defaults
Normal file
6
host_test/sdkconfig.defaults
Normal file
@ -0,0 +1,6 @@
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_COMPILER_CXX_RTTI=y
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
|
||||
CONFIG_COMPILER_STACK_CHECK_NONE=y
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
5
idf_component.yml
Normal file
5
idf_component.yml
Normal file
@ -0,0 +1,5 @@
|
||||
version: "1.0.0"
|
||||
description: esp-mqtt
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.0"
|
286
include/mqtt5_client.h
Normal file
286
include/mqtt5_client.h
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _MQTT5_CLIENT_H_
|
||||
#define _MQTT5_CLIENT_H_
|
||||
|
||||
#include "mqtt_client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct esp_mqtt_client *esp_mqtt5_client_handle_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol error reason code, more details refer to MQTT5 protocol document section 2.4
|
||||
*/
|
||||
enum mqtt5_error_reason_code {
|
||||
MQTT5_UNSPECIFIED_ERROR = 0x80,
|
||||
MQTT5_MALFORMED_PACKET = 0x81,
|
||||
MQTT5_PROTOCOL_ERROR = 0x82,
|
||||
MQTT5_IMPLEMENT_SPECIFIC_ERROR = 0x83,
|
||||
MQTT5_UNSUPPORTED_PROTOCOL_VER = 0x84,
|
||||
MQTT5_INVAILD_CLIENT_ID __attribute__((deprecated)) = 0x85,
|
||||
MQTT5_INVALID_CLIENT_ID = 0x85,
|
||||
MQTT5_BAD_USERNAME_OR_PWD = 0x86,
|
||||
MQTT5_NOT_AUTHORIZED = 0x87,
|
||||
MQTT5_SERVER_UNAVAILABLE = 0x88,
|
||||
MQTT5_SERVER_BUSY = 0x89,
|
||||
MQTT5_BANNED = 0x8A,
|
||||
MQTT5_SERVER_SHUTTING_DOWN = 0x8B,
|
||||
MQTT5_BAD_AUTH_METHOD = 0x8C,
|
||||
MQTT5_KEEP_ALIVE_TIMEOUT = 0x8D,
|
||||
MQTT5_SESSION_TAKEN_OVER = 0x8E,
|
||||
MQTT5_TOPIC_FILTER_INVAILD __attribute__((deprecated)) = 0x8F,
|
||||
MQTT5_TOPIC_FILTER_INVALID = 0x8F,
|
||||
MQTT5_TOPIC_NAME_INVAILD __attribute__((deprecated)) = 0x90,
|
||||
MQTT5_TOPIC_NAME_INVALID = 0x90,
|
||||
MQTT5_PACKET_IDENTIFIER_IN_USE = 0x91,
|
||||
MQTT5_PACKET_IDENTIFIER_NOT_FOUND = 0x92,
|
||||
MQTT5_RECEIVE_MAXIMUM_EXCEEDED = 0x93,
|
||||
MQTT5_TOPIC_ALIAS_INVAILD __attribute__((deprecated)) = 0x94,
|
||||
MQTT5_TOPIC_ALIAS_INVALID = 0x94,
|
||||
MQTT5_PACKET_TOO_LARGE = 0x95,
|
||||
MQTT5_MESSAGE_RATE_TOO_HIGH = 0x96,
|
||||
MQTT5_QUOTA_EXCEEDED = 0x97,
|
||||
MQTT5_ADMINISTRATIVE_ACTION = 0x98,
|
||||
MQTT5_PAYLOAD_FORMAT_INVAILD __attribute__((deprecated)) = 0x99,
|
||||
MQTT5_PAYLOAD_FORMAT_INVALID = 0x99,
|
||||
MQTT5_RETAIN_NOT_SUPPORT = 0x9A,
|
||||
MQTT5_QOS_NOT_SUPPORT = 0x9B,
|
||||
MQTT5_USE_ANOTHER_SERVER = 0x9C,
|
||||
MQTT5_SERVER_MOVED = 0x9D,
|
||||
MQTT5_SHARED_SUBSCR_NOT_SUPPORTED = 0x9E,
|
||||
MQTT5_CONNECTION_RATE_EXCEEDED = 0x9F,
|
||||
MQTT5_MAXIMUM_CONNECT_TIME = 0xA0,
|
||||
MQTT5_SUBSCRIBE_IDENTIFIER_NOT_SUPPORT = 0xA1,
|
||||
MQTT5_WILDCARD_SUBSCRIBE_NOT_SUPPORT = 0xA2,
|
||||
};
|
||||
|
||||
/**
|
||||
* MQTT5 user property handle
|
||||
*/
|
||||
typedef struct mqtt5_user_property_list_t *mqtt5_user_property_handle_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol connect properties and will properties configuration, more details refer to MQTT5 protocol document section 3.1.2.11 and 3.3.2.3
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t session_expiry_interval; /*!< The interval time of session expiry */
|
||||
uint32_t maximum_packet_size; /*!< The maximum packet size that we can receive */
|
||||
uint16_t receive_maximum; /*!< The maximum pakcket count that we process concurrently */
|
||||
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 */
|
||||
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 *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 */
|
||||
} esp_mqtt5_connection_property_config_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol publish properties configuration, more details refer to MQTT5 protocol document section 3.3.2.3
|
||||
*/
|
||||
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 */
|
||||
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 */
|
||||
} esp_mqtt5_publish_property_config_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol subscribe properties configuration, more details refer to MQTT5 protocol document section 3.8.2.1
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t subscribe_id; /*!< A variable byte represents the identifier of the subscription */
|
||||
bool no_local_flag; /*!< Subscription Option to allow that server publish message that client sent */
|
||||
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 */
|
||||
} esp_mqtt5_subscribe_property_config_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol unsubscribe properties configuration, more details refer to MQTT5 protocol document section 3.10.2.1
|
||||
*/
|
||||
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 */
|
||||
} esp_mqtt5_unsubscribe_property_config_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol disconnect properties configuration, more details refer to MQTT5 protocol document section 3.14.2.2
|
||||
*/
|
||||
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 */
|
||||
} esp_mqtt5_disconnect_property_config_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol for event properties
|
||||
*/
|
||||
typedef struct {
|
||||
bool payload_format_indicator; /*!< Payload format of the message */
|
||||
char *response_topic; /*!< Response topic of the message */
|
||||
int response_topic_len; /*!< Response topic length of the message */
|
||||
char *correlation_data; /*!< Correlation data of the message */
|
||||
uint16_t correlation_data_len; /*!< Correlation data length of the message */
|
||||
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 */
|
||||
} esp_mqtt5_event_property_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol for user property
|
||||
*/
|
||||
typedef struct {
|
||||
const char *key; /*!< Item key name */
|
||||
const char *value; /*!< Item value string */
|
||||
} esp_mqtt5_user_property_item_t;
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client connect property configuration
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param connect_property connect property
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client publish property configuration
|
||||
*
|
||||
* This API will not store the publish property, it is one-time configuration.
|
||||
* Before call `esp_mqtt_client_publish` to publish data, call this API to set publish property if have
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param property publish property
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client subscribe property configuration
|
||||
*
|
||||
* This API will not store the subscribe property, it is one-time configuration.
|
||||
* Before call `esp_mqtt_client_subscribe` to subscribe topic, call this API to set subscribe property if have
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param property subscribe property
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client unsubscribe property configuration
|
||||
*
|
||||
* This API will not store the unsubscribe property, it is one-time configuration.
|
||||
* Before call `esp_mqtt_client_unsubscribe` to unsubscribe topic, call this API to set unsubscribe property if have
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param property unsubscribe property
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client disconnect property configuration
|
||||
*
|
||||
* This API will not store the disconnect property, it is one-time configuration.
|
||||
* Before call `esp_mqtt_client_disconnect` to disconnect connection, call this API to set disconnect property if have
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param property disconnect property
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client user property configuration
|
||||
*
|
||||
* This API will allocate memory for user_property, please DO NOT forget `call esp_mqtt5_client_delete_user_property`
|
||||
* after you use it.
|
||||
* Before publish data, subscribe topic, unsubscribe, etc, call this API to set user property if have
|
||||
*
|
||||
* @param user_property user_property handle
|
||||
* @param item array of user property data (eg. {{"var","val"},{"other","2"}})
|
||||
* @param item_num number of items in user property data
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Get MQTT5 client user property
|
||||
*
|
||||
* @param user_property user_property handle
|
||||
* @param item point that store user property data
|
||||
* @param item_num number of items in user property data
|
||||
*
|
||||
* This API can use with `esp_mqtt5_client_get_user_property_count` to get list count of user property.
|
||||
* And malloc number of count item array memory to store the user property data.
|
||||
* Please DO NOT forget the item memory, key and value point in item memory when get user property data successfully.
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Get MQTT5 client user property list count
|
||||
*
|
||||
* @param user_property user_property handle
|
||||
* @return user property list count
|
||||
*/
|
||||
uint8_t esp_mqtt5_client_get_user_property_count(mqtt5_user_property_handle_t user_property);
|
||||
|
||||
/**
|
||||
* @brief Free the user property list
|
||||
*
|
||||
* @param user_property user_property handle
|
||||
*
|
||||
* This API will free the memory in user property list and free user_property itself
|
||||
*/
|
||||
void esp_mqtt5_client_delete_user_property(mqtt5_user_property_handle_t user_property);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
@ -11,9 +11,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "mqtt_config.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_transport.h"
|
||||
#ifdef CONFIG_MQTT_PROTOCOL_5
|
||||
#include "mqtt5_client.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -21,128 +23,382 @@ extern "C" {
|
||||
|
||||
#ifndef ESP_EVENT_DECLARE_BASE
|
||||
// Define event loop types if macros not available
|
||||
typedef void * esp_event_loop_handle_t;
|
||||
typedef void * esp_event_handler_t;
|
||||
typedef void *esp_event_loop_handle_t;
|
||||
typedef void *esp_event_handler_t;
|
||||
#endif
|
||||
|
||||
typedef struct esp_mqtt_client *esp_mqtt_client_handle_t;
|
||||
|
||||
/**
|
||||
* @brief MQTT event types.
|
||||
* @brief *MQTT* event types.
|
||||
*
|
||||
* User event handler receives context data in `esp_mqtt_event_t` structure with
|
||||
* - `user_context` - user data from `esp_mqtt_client_config_t`
|
||||
* - `client` - mqtt client handle
|
||||
* - client - *MQTT* client handle
|
||||
* - various other data depending on event type
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
MQTT_EVENT_ERROR = 0,
|
||||
MQTT_EVENT_CONNECTED, /*!< connected event, additional context: session_present flag */
|
||||
MQTT_EVENT_DISCONNECTED, /*!< disconnected event */
|
||||
MQTT_EVENT_SUBSCRIBED, /*!< subscribed event, additional context: msg_id */
|
||||
MQTT_EVENT_UNSUBSCRIBED, /*!< unsubscribed event */
|
||||
MQTT_EVENT_PUBLISHED, /*!< published event, additional context: msg_id */
|
||||
MQTT_EVENT_DATA, /*!< data event, additional context:
|
||||
- msg_id message id
|
||||
- topic pointer to the received topic
|
||||
- topic_len length of the topic
|
||||
- data pointer to the received data
|
||||
- data_len length of the data for this event
|
||||
- current_data_offset offset of the current data for this event
|
||||
- total_data_len total length of the data received
|
||||
Note: Multiple MQTT_EVENT_DATA could be fired for one message, if it is
|
||||
longer than internal buffer. In that case only first event contains topic
|
||||
pointer and length, other contain data only with current data length
|
||||
and current data offset updating.
|
||||
*/
|
||||
MQTT_EVENT_BEFORE_CONNECT, /*!< The event occurs before connecting */
|
||||
typedef enum esp_mqtt_event_id_t {
|
||||
MQTT_EVENT_ANY = -1,
|
||||
MQTT_EVENT_ERROR =
|
||||
0, /*!< on error event, additional context: connection return code, error
|
||||
handle from esp_tls (if supported) */
|
||||
MQTT_EVENT_CONNECTED, /*!< connected event, additional context:
|
||||
session_present flag */
|
||||
MQTT_EVENT_DISCONNECTED, /*!< disconnected event */
|
||||
MQTT_EVENT_SUBSCRIBED, /*!< subscribed event, additional context:
|
||||
- msg_id message id
|
||||
- error_handle `error_type` in case subscribing failed
|
||||
- data pointer to broker response, check for errors.
|
||||
- data_len length of the data for this
|
||||
event
|
||||
*/
|
||||
MQTT_EVENT_UNSUBSCRIBED, /*!< unsubscribed event, additional context: msg_id */
|
||||
MQTT_EVENT_PUBLISHED, /*!< published event, additional context: msg_id */
|
||||
MQTT_EVENT_DATA, /*!< data event, additional context:
|
||||
- msg_id message id
|
||||
- topic pointer to the received topic
|
||||
- topic_len length of the topic
|
||||
- data pointer to the received data
|
||||
- data_len length of the data for this event
|
||||
- current_data_offset offset of the current data for
|
||||
this event
|
||||
- total_data_len total length of the data received
|
||||
- retain retain flag of the message
|
||||
- qos QoS level of the message
|
||||
- dup dup flag of the message
|
||||
Note: Multiple MQTT_EVENT_DATA could be fired for one
|
||||
message, if it is longer than internal buffer. In that
|
||||
case only first event contains topic pointer and length,
|
||||
other contain data only with current data length and
|
||||
current data offset updating.
|
||||
*/
|
||||
MQTT_EVENT_BEFORE_CONNECT, /*!< The event occurs before connecting */
|
||||
MQTT_EVENT_DELETED, /*!< Notification on delete of one message from the
|
||||
internal outbox, if the message couldn't have been sent
|
||||
and acknowledged before expiring defined in
|
||||
OUTBOX_EXPIRED_TIMEOUT_MS. (events are not posted upon
|
||||
deletion of successfully acknowledged messages)
|
||||
- This event id is posted only if
|
||||
MQTT_REPORT_DELETED_MESSAGES==1
|
||||
- Additional context: msg_id (id of the deleted
|
||||
message).
|
||||
*/
|
||||
MQTT_USER_EVENT, /*!< Custom event used to queue tasks into mqtt event handler
|
||||
All fields from the esp_mqtt_event_t type could be used to pass
|
||||
an additional context data to the handler.
|
||||
*/
|
||||
} esp_mqtt_event_id_t;
|
||||
|
||||
typedef enum {
|
||||
/**
|
||||
* *MQTT* connection error codes propagated via ERROR event
|
||||
*/
|
||||
typedef enum esp_mqtt_connect_return_code_t {
|
||||
MQTT_CONNECTION_ACCEPTED = 0, /*!< Connection accepted */
|
||||
MQTT_CONNECTION_REFUSE_PROTOCOL, /*!< *MQTT* connection refused reason: Wrong
|
||||
protocol */
|
||||
MQTT_CONNECTION_REFUSE_ID_REJECTED, /*!< *MQTT* connection refused reason: ID
|
||||
rejected */
|
||||
MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE, /*!< *MQTT* connection refused
|
||||
reason: Server unavailable */
|
||||
MQTT_CONNECTION_REFUSE_BAD_USERNAME, /*!< *MQTT* connection refused reason:
|
||||
Wrong user */
|
||||
MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED /*!< *MQTT* connection refused reason:
|
||||
Wrong username or password */
|
||||
} esp_mqtt_connect_return_code_t;
|
||||
|
||||
/**
|
||||
* *MQTT* connection error codes propagated via ERROR event
|
||||
*/
|
||||
typedef enum esp_mqtt_error_type_t {
|
||||
MQTT_ERROR_TYPE_NONE = 0,
|
||||
MQTT_ERROR_TYPE_TCP_TRANSPORT,
|
||||
MQTT_ERROR_TYPE_CONNECTION_REFUSED,
|
||||
MQTT_ERROR_TYPE_SUBSCRIBE_FAILED
|
||||
} esp_mqtt_error_type_t;
|
||||
|
||||
/**
|
||||
* MQTT_ERROR_TYPE_TCP_TRANSPORT error type hold all sorts of transport layer
|
||||
* errors, including ESP-TLS error, but in the past only the errors from
|
||||
* MQTT_ERROR_TYPE_ESP_TLS layer were reported, so the ESP-TLS error type is
|
||||
* re-defined here for backward compatibility
|
||||
*/
|
||||
#define MQTT_ERROR_TYPE_ESP_TLS MQTT_ERROR_TYPE_TCP_TRANSPORT
|
||||
|
||||
typedef enum esp_mqtt_transport_t {
|
||||
MQTT_TRANSPORT_UNKNOWN = 0x0,
|
||||
MQTT_TRANSPORT_OVER_TCP, /*!< MQTT over TCP, using scheme: ``mqtt`` */
|
||||
MQTT_TRANSPORT_OVER_SSL, /*!< MQTT over SSL, using scheme: ``mqtts`` */
|
||||
MQTT_TRANSPORT_OVER_WS, /*!< MQTT over Websocket, using scheme:: ``ws`` */
|
||||
MQTT_TRANSPORT_OVER_WSS /*!< MQTT over Websocket Secure, using scheme: ``wss`` */
|
||||
MQTT_TRANSPORT_OVER_TCP, /*!< *MQTT* over TCP, using scheme: ``MQTT`` */
|
||||
MQTT_TRANSPORT_OVER_SSL, /*!< *MQTT* over SSL, using scheme: ``MQTTS`` */
|
||||
MQTT_TRANSPORT_OVER_WS, /*!< *MQTT* over Websocket, using scheme:: ``ws`` */
|
||||
MQTT_TRANSPORT_OVER_WSS /*!< *MQTT* over Websocket Secure, using scheme:
|
||||
``wss`` */
|
||||
} esp_mqtt_transport_t;
|
||||
|
||||
/**
|
||||
* MQTT event configuration structure
|
||||
* *MQTT* protocol version used for connection
|
||||
*/
|
||||
typedef struct {
|
||||
esp_mqtt_event_id_t event_id; /*!< MQTT event type */
|
||||
esp_mqtt_client_handle_t client; /*!< MQTT client handle for this event */
|
||||
void *user_context; /*!< User context passed from MQTT client config */
|
||||
char *data; /*!< Data asociated with this event */
|
||||
int data_len; /*!< Lenght of the data for this event */
|
||||
int total_data_len; /*!< Total length of the data (longer data are supplied with multiple events) */
|
||||
int current_data_offset; /*!< Actual offset for the data asociated with this event */
|
||||
char *topic; /*!< Topic asociated with this event */
|
||||
int topic_len; /*!< Length of the topic for this event asociated with this event */
|
||||
int msg_id; /*!< MQTT messaged id of message */
|
||||
int session_present; /*!< MQTT session_present flag for connection event */
|
||||
typedef enum esp_mqtt_protocol_ver_t {
|
||||
MQTT_PROTOCOL_UNDEFINED = 0,
|
||||
MQTT_PROTOCOL_V_3_1,
|
||||
MQTT_PROTOCOL_V_3_1_1,
|
||||
MQTT_PROTOCOL_V_5,
|
||||
} esp_mqtt_protocol_ver_t;
|
||||
|
||||
/**
|
||||
* @brief *MQTT* error code structure to be passed as a contextual information
|
||||
* into ERROR event
|
||||
*
|
||||
* Important: This structure extends `esp_tls_last_error` error structure and is
|
||||
* backward compatible with it (so might be down-casted and treated as
|
||||
* `esp_tls_last_error` error, but recommended to update applications if used
|
||||
* this way previously)
|
||||
*
|
||||
* Use this structure directly checking error_type first and then appropriate
|
||||
* error code depending on the source of the error:
|
||||
*
|
||||
* | error_type | related member variables | note |
|
||||
* | MQTT_ERROR_TYPE_TCP_TRANSPORT | esp_tls_last_esp_err, esp_tls_stack_err,
|
||||
* esp_tls_cert_verify_flags, sock_errno | Error reported from
|
||||
* tcp_transport/esp-tls | | MQTT_ERROR_TYPE_CONNECTION_REFUSED |
|
||||
* connect_return_code | Internal error reported from *MQTT* broker on
|
||||
* connection |
|
||||
*/
|
||||
typedef struct esp_mqtt_error_codes {
|
||||
/* compatible portion of the struct corresponding to struct esp_tls_last_error
|
||||
*/
|
||||
esp_err_t esp_tls_last_esp_err; /*!< last esp_err code reported from esp-tls
|
||||
component */
|
||||
int esp_tls_stack_err; /*!< tls specific error code reported from underlying
|
||||
tls stack */
|
||||
int esp_tls_cert_verify_flags; /*!< tls flags reported from underlying tls
|
||||
stack during certificate verification */
|
||||
/* esp-mqtt specific structure extension */
|
||||
esp_mqtt_error_type_t
|
||||
error_type; /*!< error type referring to the source of the error */
|
||||
esp_mqtt_connect_return_code_t
|
||||
connect_return_code; /*!< connection refused error code reported from
|
||||
*MQTT* broker on connection */
|
||||
/* tcp_transport extension */
|
||||
int esp_transport_sock_errno; /*!< errno from the underlying socket */
|
||||
|
||||
} esp_mqtt_error_codes_t;
|
||||
|
||||
/**
|
||||
* *MQTT* event configuration structure
|
||||
*/
|
||||
typedef struct esp_mqtt_event_t {
|
||||
esp_mqtt_event_id_t event_id; /*!< *MQTT* event type */
|
||||
esp_mqtt_client_handle_t client; /*!< *MQTT* client handle for this event */
|
||||
char *data; /*!< Data associated with this event */
|
||||
int data_len; /*!< Length of the data for this event */
|
||||
int total_data_len; /*!< Total length of the data (longer data are supplied
|
||||
with multiple events) */
|
||||
int current_data_offset; /*!< Actual offset for the data associated with this
|
||||
event */
|
||||
char *topic; /*!< Topic associated with this event */
|
||||
int topic_len; /*!< Length of the topic for this event associated with this
|
||||
event */
|
||||
int msg_id; /*!< *MQTT* messaged id of message */
|
||||
int session_present; /*!< *MQTT* session_present flag for connection event */
|
||||
esp_mqtt_error_codes_t
|
||||
*error_handle; /*!< esp-mqtt error handle including esp-tls errors as well
|
||||
as internal *MQTT* errors */
|
||||
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*/
|
||||
#ifdef CONFIG_MQTT_PROTOCOL_5
|
||||
esp_mqtt5_event_property_t *property; /*!< MQTT 5 property associated with this event */
|
||||
#endif
|
||||
|
||||
} esp_mqtt_event_t;
|
||||
|
||||
typedef esp_mqtt_event_t *esp_mqtt_event_handle_t;
|
||||
|
||||
typedef esp_err_t (* mqtt_event_callback_t)(esp_mqtt_event_handle_t event);
|
||||
|
||||
/**
|
||||
* MQTT client configuration structure
|
||||
* *MQTT* client configuration structure
|
||||
*
|
||||
* - Default values can be set via menuconfig
|
||||
* - All certificates and key data could be passed in PEM or DER format. PEM format must have a terminating NULL
|
||||
* character and the related len field set to 0. DER format requires a related len field set to the correct length.
|
||||
*/
|
||||
typedef struct {
|
||||
mqtt_event_callback_t event_handle; /*!< handle for MQTT events as a callback in legacy mode */
|
||||
esp_event_loop_handle_t event_loop_handle; /*!< handle for MQTT event loop library */
|
||||
const char *host; /*!< MQTT server domain (ipv4 as string) */
|
||||
const char *uri; /*!< Complete MQTT broker URI */
|
||||
uint32_t port; /*!< MQTT server port */
|
||||
const char *client_id; /*!< default client id is ``ESP32_%CHIPID%`` where %CHIPID% are last 3 bytes of MAC address in hex format */
|
||||
const char *username; /*!< MQTT username */
|
||||
const char *password; /*!< MQTT password */
|
||||
const char *lwt_topic; /*!< LWT (Last Will and Testament) message topic (NULL by default) */
|
||||
const char *lwt_msg; /*!< LWT message (NULL by default) */
|
||||
int lwt_qos; /*!< LWT message qos */
|
||||
int lwt_retain; /*!< LWT retained message flag */
|
||||
int lwt_msg_len; /*!< LWT message length */
|
||||
int disable_clean_session; /*!< mqtt clean session, default clean_session is true */
|
||||
int keepalive; /*!< mqtt keepalive, default is 120 seconds */
|
||||
bool disable_auto_reconnect; /*!< this mqtt client will reconnect to server (when errors/disconnect). Set disable_auto_reconnect=true to disable */
|
||||
void *user_context; /*!< pass user context to this option, then can receive that context in ``event->user_context`` */
|
||||
int task_prio; /*!< MQTT task priority, default is 5, can be changed in ``make menuconfig`` */
|
||||
int task_stack; /*!< MQTT task stack size, default is 6144 bytes, can be changed in ``make menuconfig`` */
|
||||
int buffer_size; /*!< size of MQTT send/receive buffer, default is 1024 */
|
||||
const char *cert_pem; /*!< Pointer to certificate data in PEM format for server verify (with SSL), default is NULL, not required to verify the server */
|
||||
const char *client_cert_pem; /*!< Pointer to certificate data in PEM format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_key_pem` has to be provided. */
|
||||
const char *client_key_pem; /*!< Pointer to private key data in PEM format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_cert_pem` has to be provided. */
|
||||
esp_mqtt_transport_t transport; /*!< overrides URI transport */
|
||||
int refresh_connection_after_ms; /*!< Refresh connection after this value (in milliseconds) */
|
||||
typedef struct esp_mqtt_client_config_t {
|
||||
/**
|
||||
* Broker related configuration
|
||||
*/
|
||||
struct broker_t {
|
||||
/**
|
||||
* Broker address
|
||||
*
|
||||
* - uri have precedence over other fields
|
||||
* - If uri isn't set at least hostname, transport and port should.
|
||||
*/
|
||||
struct address_t {
|
||||
const char *uri; /*!< Complete *MQTT* broker URI */
|
||||
const char *hostname; /*!< Hostname, to set ipv4 pass it as string) */
|
||||
esp_mqtt_transport_t transport; /*!< Selects transport*/
|
||||
const char *path; /*!< Path in the URI*/
|
||||
uint32_t port; /*!< *MQTT* server port */
|
||||
} address; /*!< Broker address configuration */
|
||||
/**
|
||||
* Broker identity verification
|
||||
*
|
||||
* If fields are not set broker's identity isn't verified. it's recommended
|
||||
* to set the options in this struct for security reasons.
|
||||
*/
|
||||
struct verification_t {
|
||||
bool use_global_ca_store; /*!< Use a global ca_store, look esp-tls
|
||||
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.*/
|
||||
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).
|
||||
PSK is enabled only if there are no other ways to
|
||||
verify broker. It's not copied nor freed by the client, user needs to clean up.*/
|
||||
bool skip_cert_common_name_check; /*!< Skip any validation of server certificate CN field, this reduces the
|
||||
security of TLS and makes the *MQTT* client susceptible to MITM attacks */
|
||||
const char **alpn_protos; /*!< NULL-terminated list of supported application protocols to be used for ALPN.*/
|
||||
const char *common_name; /*!< Pointer to the string containing server certificate common name.
|
||||
If non-NULL, server certificate CN must match this name,
|
||||
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.*/
|
||||
} verification; /*!< Security verification of the broker */
|
||||
} broker; /*!< Broker address and security verification */
|
||||
/**
|
||||
* Client related credentials for authentication.
|
||||
*/
|
||||
struct credentials_t {
|
||||
const char *username; /*!< *MQTT* username */
|
||||
const char *client_id; /*!< Set *MQTT* client identifier. Ignored if set_null_client_id == true If NULL set
|
||||
the default client id. Default client id is ``ESP32_%CHIPID%`` where `%CHIPID%` are
|
||||
last 3 bytes of MAC address in hex format */
|
||||
bool set_null_client_id; /*!< Selects a NULL client id */
|
||||
/**
|
||||
* Client authentication
|
||||
*
|
||||
* Fields related to client authentication by broker
|
||||
*
|
||||
* For mutual authentication using TLS, user could select certificate and key,
|
||||
* secure element or digital signature peripheral if available.
|
||||
*
|
||||
*/
|
||||
struct authentication_t {
|
||||
const char *password; /*!< *MQTT* password */
|
||||
const char *certificate; /*!< Certificate for ssl mutual authentication, not required if mutual
|
||||
authentication is not needed. Must be provided with `key`. 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 char *key; /*!< Private key for SSL mutual authentication, not required if mutual authentication
|
||||
is not needed. If it is not NULL, also `certificate` has to be provided. It's not copied nor freed by the client, user needs to clean up.*/
|
||||
size_t key_len; /*!< Length of the buffer pointed to by key.*/
|
||||
const char *key_password; /*!< Client key decryption password, not PEM nor DER, if provided
|
||||
`key_password_len` must be correctly set.*/
|
||||
int key_password_len; /*!< Length of the password pointed to by `key_password` */
|
||||
bool use_secure_element; /*!< Enable secure element, available in ESP32-ROOM-32SE, for SSL connection */
|
||||
void *ds_data; /*!< Carrier of handle for digital signature parameters, digital signature peripheral is
|
||||
available in some Espressif devices. It's not copied nor freed by the client, user needs to clean up.*/
|
||||
} authentication; /*!< Client authentication */
|
||||
} credentials; /*!< User credentials for broker */
|
||||
/**
|
||||
* *MQTT* Session related configuration
|
||||
*/
|
||||
struct session_t {
|
||||
/**
|
||||
* Last Will and Testament message configuration.
|
||||
*/
|
||||
struct last_will_t {
|
||||
const char *topic; /*!< LWT (Last Will and Testament) message topic */
|
||||
const char *msg; /*!< LWT message, may be NULL terminated*/
|
||||
int msg_len; /*!< LWT message length, if msg isn't NULL terminated must have the correct length */
|
||||
int qos; /*!< LWT message QoS */
|
||||
int retain; /*!< LWT retained message flag */
|
||||
} last_will; /*!< Last will configuration */
|
||||
bool disable_clean_session; /*!< *MQTT* clean session, default clean_session is true */
|
||||
int keepalive; /*!< *MQTT* keepalive, default is 120 seconds
|
||||
When configuring this value, keep in mind that the client attempts
|
||||
to communicate with the broker at half the interval that is actually set.
|
||||
This conservative approach allows for more attempts before the broker's timeout occurs */
|
||||
bool disable_keepalive; /*!< Set `disable_keepalive=true` to turn off keep-alive mechanism, keepalive is active
|
||||
by default. Note: setting the config value `keepalive` to `0` doesn't disable
|
||||
keepalive feature, but uses a default keepalive period */
|
||||
esp_mqtt_protocol_ver_t protocol_ver; /*!< *MQTT* protocol version used for connection.*/
|
||||
int message_retransmit_timeout; /*!< timeout for retransmitting of failed packet */
|
||||
} session; /*!< *MQTT* session configuration. */
|
||||
/**
|
||||
* Network related configuration
|
||||
*/
|
||||
struct network_t {
|
||||
int reconnect_timeout_ms; /*!< Reconnect to the broker after this value in miliseconds 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). */
|
||||
int refresh_connection_after_ms; /*!< Refresh connection after this value (in milliseconds) */
|
||||
bool disable_auto_reconnect; /*!< Client will reconnect to server (when errors/disconnect). Set
|
||||
`disable_auto_reconnect=true` to disable */
|
||||
esp_transport_handle_t transport; /*!< Custom transport handle to use. 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
|
||||
*/
|
||||
struct task_t {
|
||||
int priority; /*!< *MQTT* task priority*/
|
||||
int stack_size; /*!< *MQTT* task stack size*/
|
||||
} task; /*!< FreeRTOS task configuration.*/
|
||||
/**
|
||||
* Client buffer size configuration
|
||||
*
|
||||
* Client have two buffers for input and output respectivelly.
|
||||
*/
|
||||
struct buffer_t {
|
||||
int size; /*!< size of *MQTT* send/receive buffer*/
|
||||
int out_size; /*!< size of *MQTT* output buffer. If not defined, defaults to the size defined by
|
||||
``buffer_size`` */
|
||||
} buffer; /*!< Buffer size configuration.*/
|
||||
|
||||
/**
|
||||
* Client outbox configuration options.
|
||||
*/
|
||||
struct outbox_config_t {
|
||||
uint64_t limit; /*!< Size limit for the outbox in bytes.*/
|
||||
} outbox; /*!< Outbox configuration. */
|
||||
} esp_mqtt_client_config_t;
|
||||
|
||||
/**
|
||||
* @brief Creates mqtt client handle based on the configuration
|
||||
* Topic definition struct
|
||||
*/
|
||||
typedef struct topic_t {
|
||||
const char *filter; /*!< Topic filter to subscribe */
|
||||
int qos; /*!< Max QoS level of the subscription */
|
||||
} esp_mqtt_topic_t;
|
||||
|
||||
/**
|
||||
* @brief Creates *MQTT* client handle based on the configuration
|
||||
*
|
||||
* @param config mqtt configuration structure
|
||||
* @param config *MQTT* configuration structure
|
||||
*
|
||||
* @return mqtt_client_handle if successfully created, NULL on error
|
||||
*/
|
||||
esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *config);
|
||||
esp_mqtt_client_handle_t
|
||||
esp_mqtt_client_init(const esp_mqtt_client_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Sets mqtt connection URI. This API is usually used to overrides the URI
|
||||
* configured in esp_mqtt_client_init
|
||||
* @brief Sets *MQTT* connection URI. This API is usually used to overrides the
|
||||
* URI configured in esp_mqtt_client_init
|
||||
*
|
||||
* @param client mqtt client hanlde
|
||||
* @param client *MQTT* client handle
|
||||
* @param uri
|
||||
*
|
||||
* @return ESP_FAIL if URI parse error, ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client, const char *uri);
|
||||
esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client,
|
||||
const char *uri);
|
||||
|
||||
/**
|
||||
* @brief Starts mqtt client with already created client handle
|
||||
* @brief Starts *MQTT* client with already created client handle
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
@ -153,110 +409,257 @@ esp_err_t esp_mqtt_client_start(esp_mqtt_client_handle_t client);
|
||||
/**
|
||||
* @brief This api is typically used to force reconnection upon a specific event
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_FAIL if client is in invalid state
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_reconnect(esp_mqtt_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief Stops mqtt client tasks
|
||||
* @brief This api is typically used to force disconnection from the broker
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_disconnect(esp_mqtt_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief Stops *MQTT* client tasks
|
||||
*
|
||||
* * Notes:
|
||||
* - Cannot be called from the *MQTT* event handler
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_FAIL if client is in invalid state
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_stop(esp_mqtt_client_handle_t client);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#define esp_mqtt_client_subscribe esp_mqtt_client_subscribe_single
|
||||
|
||||
#else
|
||||
/**
|
||||
* @brief Convenience macro to select subscribe function to use.
|
||||
*
|
||||
* Notes:
|
||||
* - Usage of `esp_mqtt_client_subscribe_single` is the same as previous
|
||||
* esp_mqtt_client_subscribe, refer to it for details.
|
||||
*
|
||||
* @param client_handle *MQTT* client handle
|
||||
* @param topic_type Needs to be char* for single subscription or `esp_mqtt_topic_t` for multiple topics
|
||||
* @param qos_or_size It's either a qos when subscribing to a single topic or the size of the subscription array when subscribing to multiple topics.
|
||||
*
|
||||
* @return message_id of the subscribe message on success
|
||||
* -1 on failure
|
||||
* -2 in case of full outbox.
|
||||
*/
|
||||
#define esp_mqtt_client_subscribe(client_handle, topic_type, qos_or_size) _Generic((topic_type), \
|
||||
char *: esp_mqtt_client_subscribe_single, \
|
||||
const char *: esp_mqtt_client_subscribe_single, \
|
||||
esp_mqtt_topic_t*: esp_mqtt_client_subscribe_multiple \
|
||||
)(client_handle, topic_type, qos_or_size)
|
||||
|
||||
#endif /* __cplusplus*/
|
||||
/**
|
||||
* @brief Subscribe the client to defined topic with defined qos
|
||||
*
|
||||
* Notes:
|
||||
* - Client must be connected to send subscribe message
|
||||
* - This API is could be executed from a user task or
|
||||
* from a mqtt event callback i.e. internal mqtt task
|
||||
* from a *MQTT* event callback i.e. internal *MQTT* task
|
||||
* (API is protected by internal mutex, so it might block
|
||||
* if a longer data receive operation is in progress.
|
||||
* - `esp_mqtt_client_subscribe` could be used to call this function.
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param topic
|
||||
* @param qos
|
||||
* @param client *MQTT* client handle
|
||||
* @param topic topic filter to subscribe
|
||||
* @param qos Max qos level of the subscription
|
||||
*
|
||||
* @return message_id of the subscribe message on success
|
||||
* -1 on failure
|
||||
* -2 in case of full outbox.
|
||||
*/
|
||||
int esp_mqtt_client_subscribe(esp_mqtt_client_handle_t client, const char *topic, int qos);
|
||||
int esp_mqtt_client_subscribe_single(esp_mqtt_client_handle_t client,
|
||||
const char *topic, int qos);
|
||||
/**
|
||||
* @brief Subscribe the client to a list of defined topics with defined qos
|
||||
*
|
||||
* Notes:
|
||||
* - Client must be connected to send subscribe message
|
||||
* - This API is could be executed from a user task or
|
||||
* from a *MQTT* event callback i.e. internal *MQTT* task
|
||||
* (API is protected by internal mutex, so it might block
|
||||
* if a longer data receive operation is in progress.
|
||||
* - `esp_mqtt_client_subscribe` could be used to call this function.
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param topic_list List of topics to subscribe
|
||||
* @param size size of topic_list
|
||||
*
|
||||
* @return message_id of the subscribe message on success
|
||||
* -1 on failure
|
||||
* -2 in case of full outbox.
|
||||
*/
|
||||
int esp_mqtt_client_subscribe_multiple(esp_mqtt_client_handle_t client,
|
||||
const esp_mqtt_topic_t *topic_list, int size);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe the client from defined topic
|
||||
*
|
||||
* Notes:
|
||||
* - Client must be connected to send unsubscribe message
|
||||
* - It is thread safe, please refer to `esp_mqtt_client_subscribe` for details
|
||||
* - It is thread safe, please refer to `esp_mqtt_client_subscribe_single` for details
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param client *MQTT* client handle
|
||||
* @param topic
|
||||
*
|
||||
* @return message_id of the subscribe message on success
|
||||
* -1 on failure
|
||||
*/
|
||||
int esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client, const char *topic);
|
||||
int esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client,
|
||||
const char *topic);
|
||||
|
||||
/**
|
||||
* @brief Client to send a publish message to the broker
|
||||
*
|
||||
* Notes:
|
||||
* - Client doesn't have to be connected to send publish message
|
||||
* (although it would drop all qos=0 messages, qos>1 messages would be enqueued)
|
||||
* - This API might block for several seconds, either due to network timeout
|
||||
* (10s) or if publishing payloads longer than internal buffer (due to message
|
||||
* fragmentation)
|
||||
* - Client doesn't have to be connected for this API to work, enqueueing the
|
||||
* messages with qos>1 (returning -1 for all the qos=0 messages if
|
||||
* disconnected). If MQTT_SKIP_PUBLISH_IF_DISCONNECTED is enabled, this API will
|
||||
* not attempt to publish when the client is not connected and will always
|
||||
* return -1.
|
||||
* - It is thread safe, please refer to `esp_mqtt_client_subscribe` for details
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param client *MQTT* client handle
|
||||
* @param topic topic string
|
||||
* @param data payload string (set to NULL, sending empty payload message)
|
||||
* @param len data length, if set to 0, length is calculated from payload string
|
||||
* @param qos qos of publish message
|
||||
* @param retain ratain flag
|
||||
* @param len data length, if set to 0, length is calculated from payload
|
||||
* string
|
||||
* @param qos QoS of publish message
|
||||
* @param retain retain flag
|
||||
*
|
||||
* @return message_id of the subscribe message on success
|
||||
* 0 if cannot publish
|
||||
* @return message_id of the publish message (for QoS 0 message_id will always
|
||||
* be zero) on success. -1 on failure, -2 in case of full outbox.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Enqueue a message to the outbox, to be sent later. Typically used for
|
||||
* messages with qos>0, but could be also used for qos=0 messages if store=true.
|
||||
*
|
||||
* This API generates and stores the publish message into the internal outbox
|
||||
* and the actual sending to the network is performed in the mqtt-task context
|
||||
* (in contrast to the esp_mqtt_client_publish() which sends the publish message
|
||||
* immediately in the user task's context). Thus, it could be used as a non
|
||||
* blocking version of esp_mqtt_client_publish().
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param topic topic string
|
||||
* @param data payload string (set to NULL, sending empty payload message)
|
||||
* @param len data length, if set to 0, length is calculated from payload
|
||||
* string
|
||||
* @param qos QoS of publish message
|
||||
* @param retain retain flag
|
||||
* @param store if true, all messages are enqueued; otherwise only QoS 1 and
|
||||
* QoS 2 are enqueued
|
||||
*
|
||||
* @return message_id if queued successfully, -1 on failure, -2 in case of full outbox.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Destroys the client handle
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* Notes:
|
||||
* - Cannot be called from the *MQTT* event handler
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @return ESP_OK
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_destroy(esp_mqtt_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief Set configuration structure, typically used when updating the config (i.e. on "before_connect" event
|
||||
* @brief Set configuration structure, typically used when updating the config
|
||||
* (i.e. on "before_connect" event
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* Notes:
|
||||
* - When calling this function make sure to have all the intendend configurations
|
||||
* set, otherwise default values are set.
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @param config mqtt configuration structure
|
||||
* @param config *MQTT* configuration structure
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_ERR_INVALID_ARG if conflicts on transport configuration.
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_client_config_t *config);
|
||||
esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client,
|
||||
const esp_mqtt_client_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Registers mqtt event
|
||||
* @brief Registers *MQTT* event
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param client *MQTT* client handle
|
||||
* @param event event type
|
||||
* @param event_handler hanlder callback
|
||||
* @param event_handler handler callback
|
||||
* @param event_handler_arg handlers context
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_OK on success
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Unregisters mqtt event
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param event event ID
|
||||
* @param event_handler handler to unregister
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Get outbox size
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @return outbox size
|
||||
* 0 on wrong initialization
|
||||
*/
|
||||
int esp_mqtt_client_get_outbox_size(esp_mqtt_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief Dispatch user event to the mqtt internal event loop
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param event *MQTT* event handle structure
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_TIMEOUT if the event couldn't be queued (ref also CONFIG_MQTT_EVENT_QUEUE_SIZE)
|
||||
*/
|
||||
esp_err_t esp_mqtt_dispatch_custom_event(esp_mqtt_client_handle_t client, esp_mqtt_event_t *event);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -31,13 +31,44 @@
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0)
|
||||
// Features supported from 3.3
|
||||
#define MQTT_SUPPORTED_FEATURE_EVENT_LOOP
|
||||
#define MQTT_SUPPORTED_FEATURE_SKIP_CRT_CMN_NAME_CHECK
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
|
||||
// Features supported in 4.0
|
||||
#define MQTT_SUPPORTED_FEATURE_WS_SUBPROTOCOL
|
||||
#define MQTT_SUPPORTED_FEATURE_TRANSPORT_ERR_REPORTING
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0)
|
||||
// Features supported in 4.1
|
||||
#define MQTT_SUPPORTED_FEATURE_PSK_AUTHENTICATION
|
||||
#define MQTT_SUPPORTED_FEATURE_DER_CERTIFICATES
|
||||
#define MQTT_SUPPORTED_FEATURE_ALPN
|
||||
#define MQTT_SUPPORTED_FEATURE_CLIENT_KEY_PASSWORD
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
// Features supported in 4.2
|
||||
#define MQTT_SUPPORTED_FEATURE_SECURE_ELEMENT
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
// Features supported in 4.3
|
||||
#define MQTT_SUPPORTED_FEATURE_DIGITAL_SIGNATURE
|
||||
#define MQTT_SUPPORTED_FEATURE_TRANSPORT_SOCK_ERRNO_REPORTING
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
// Features supported in 4.4
|
||||
#define MQTT_SUPPORTED_FEATURE_CERTIFICATE_BUNDLE
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
// Features supported in 5.1.0
|
||||
#define MQTT_SUPPORTED_FEATURE_CRT_CMN_NAME
|
||||
#endif
|
||||
|
||||
|
||||
#endif // _MQTT_SUPPORTED_FEATURES_H_
|
||||
#endif /* ESP_IDF_VERSION */
|
||||
#endif // _MQTT_SUPPORTED_FEATURES_H_
|
||||
|
55
lib/include/mqtt5_client_priv.h
Normal file
55
lib/include/mqtt5_client_priv.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _MQTT5_CLIENT_PRIV_H_
|
||||
#define _MQTT5_CLIENT_PRIV_H_
|
||||
|
||||
#include "mqtt5_client.h"
|
||||
#include "mqtt_client_priv.h"
|
||||
#include "mqtt5_msg.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct mqtt5_topic_alias {
|
||||
char *topic;
|
||||
uint16_t topic_len;
|
||||
uint16_t topic_alias;
|
||||
STAILQ_ENTRY(mqtt5_topic_alias) next;
|
||||
} mqtt5_topic_alias_t;
|
||||
STAILQ_HEAD(mqtt5_topic_alias_list_t, mqtt5_topic_alias);
|
||||
typedef struct mqtt5_topic_alias_list_t *mqtt5_topic_alias_handle_t;
|
||||
typedef struct mqtt5_topic_alias *mqtt5_topic_alias_item_t;
|
||||
|
||||
typedef struct {
|
||||
esp_mqtt5_connection_property_storage_t connect_property_info;
|
||||
esp_mqtt5_connection_will_property_storage_t will_property_info;
|
||||
esp_mqtt5_connection_server_resp_property_t server_resp_property_info;
|
||||
esp_mqtt5_disconnect_property_config_t disconnect_property_info;
|
||||
const esp_mqtt5_publish_property_config_t *publish_property_info;
|
||||
const esp_mqtt5_subscribe_property_config_t *subscribe_property_info;
|
||||
const esp_mqtt5_unsubscribe_property_config_t *unsubscribe_property_info;
|
||||
mqtt5_topic_alias_handle_t peer_topic_alias;
|
||||
} mqtt5_config_storage_t;
|
||||
|
||||
void esp_mqtt5_increment_packet_counter(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_decrement_packet_counter(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_parse_pubcomp(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_parse_puback(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_parse_unsuback(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_parse_suback(esp_mqtt5_client_handle_t client);
|
||||
esp_err_t esp_mqtt5_parse_connack(esp_mqtt5_client_handle_t client, int *connect_rsp_code);
|
||||
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);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
142
lib/include/mqtt5_msg.h
Normal file
142
lib/include/mqtt5_msg.h
Normal file
@ -0,0 +1,142 @@
|
||||
#ifndef MQTT5_MSG_H
|
||||
#define MQTT5_MSG_H
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "sys/queue.h"
|
||||
#include "mqtt_config.h"
|
||||
#include "mqtt_msg.h"
|
||||
#include "mqtt_client.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum mqtt_properties_type {
|
||||
MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR = 0x01,
|
||||
MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL = 0x02,
|
||||
MQTT5_PROPERTY_CONTENT_TYPE = 0x03,
|
||||
MQTT5_PROPERTY_RESPONSE_TOPIC = 0x08,
|
||||
MQTT5_PROPERTY_CORRELATION_DATA = 0x09,
|
||||
MQTT5_PROPERTY_SUBSCRIBE_IDENTIFIER = 0x0B,
|
||||
MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL = 0x11,
|
||||
MQTT5_PROPERTY_ASSIGNED_CLIENT_IDENTIFIER = 0x12,
|
||||
MQTT5_PROPERTY_SERVER_KEEP_ALIVE = 0x13,
|
||||
MQTT5_PROPERTY_AUTHENTICATION_METHOD = 0x15,
|
||||
MQTT5_PROPERTY_AUTHENTICATION_DATA = 0x16,
|
||||
MQTT5_PROPERTY_REQUEST_PROBLEM_INFO = 0x17,
|
||||
MQTT5_PROPERTY_WILL_DELAY_INTERVAL = 0x18,
|
||||
MQTT5_PROPERTY_REQUEST_RESP_INFO = 0x19,
|
||||
MQTT5_PROPERTY_RESP_INFO = 0x1A,
|
||||
MQTT5_PROPERTY_SERVER_REFERENCE = 0x1C,
|
||||
MQTT5_PROPERTY_REASON_STRING = 0x1F,
|
||||
MQTT5_PROPERTY_RECEIVE_MAXIMUM = 0x21,
|
||||
MQTT5_PROPERTY_TOPIC_ALIAS_MAXIMIM = 0x22,
|
||||
MQTT5_PROPERTY_TOPIC_ALIAS = 0x23,
|
||||
MQTT5_PROPERTY_MAXIMUM_QOS = 0x24,
|
||||
MQTT5_PROPERTY_RETAIN_AVAILABLE = 0x25,
|
||||
MQTT5_PROPERTY_USER_PROPERTY = 0x26,
|
||||
MQTT5_PROPERTY_MAXIMUM_PACKET_SIZE = 0x27,
|
||||
MQTT5_PROPERTY_WILDCARD_SUBSCR_AVAILABLE = 0x28,
|
||||
MQTT5_PROPERTY_SUBSCR_IDENTIFIER_AVAILABLE = 0x29,
|
||||
MQTT5_PROPERTY_SHARED_SUBSCR_AVAILABLE = 0x2A,
|
||||
};
|
||||
|
||||
typedef struct mqtt5_user_property {
|
||||
char *key;
|
||||
char *value;
|
||||
STAILQ_ENTRY(mqtt5_user_property) next;
|
||||
} mqtt5_user_property_t;
|
||||
STAILQ_HEAD(mqtt5_user_property_list_t, mqtt5_user_property);
|
||||
typedef struct mqtt5_user_property *mqtt5_user_property_item_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t maximum_packet_size;
|
||||
uint16_t receive_maximum;
|
||||
uint16_t topic_alias_maximum;
|
||||
uint8_t max_qos;
|
||||
bool retain_available;
|
||||
bool wildcard_subscribe_available;
|
||||
bool subscribe_identifiers_available;
|
||||
bool shared_subscribe_available;
|
||||
char *response_info;
|
||||
} esp_mqtt5_connection_server_resp_property_t;
|
||||
|
||||
typedef struct {
|
||||
bool payload_format_indicator;
|
||||
uint32_t message_expiry_interval;
|
||||
uint16_t topic_alias;
|
||||
char *response_topic;
|
||||
int response_topic_len;
|
||||
char *correlation_data;
|
||||
uint16_t correlation_data_len;
|
||||
char *content_type;
|
||||
int content_type_len;
|
||||
uint16_t subscribe_id;
|
||||
} esp_mqtt5_publish_resp_property_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t session_expiry_interval;
|
||||
uint32_t maximum_packet_size;
|
||||
uint16_t receive_maximum;
|
||||
uint16_t topic_alias_maximum;
|
||||
bool request_resp_info;
|
||||
bool request_problem_info;
|
||||
mqtt5_user_property_handle_t user_property;
|
||||
} esp_mqtt5_connection_property_storage_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t will_delay_interval;
|
||||
uint32_t message_expiry_interval;
|
||||
bool payload_format_indicator;
|
||||
char *content_type;
|
||||
char *response_topic;
|
||||
char *correlation_data;
|
||||
uint16_t correlation_data_len;
|
||||
mqtt5_user_property_handle_t user_property;
|
||||
} esp_mqtt5_connection_will_property_storage_t;
|
||||
|
||||
#define mqtt5_get_type mqtt_get_type
|
||||
|
||||
#define mqtt5_get_dup mqtt_get_dup
|
||||
|
||||
#define mqtt5_set_dup mqtt_set_dup
|
||||
|
||||
#define mqtt5_get_qos mqtt_get_qos
|
||||
|
||||
#define mqtt5_get_retain mqtt_get_retain
|
||||
|
||||
#define mqtt5_msg_init mqtt_msg_init
|
||||
|
||||
#define mqtt5_get_total_length mqtt_get_total_length
|
||||
|
||||
#define mqtt5_has_valid_msg_hdr mqtt_has_valid_msg_hdr
|
||||
|
||||
#define mqtt5_msg_pingreq mqtt_msg_pingreq
|
||||
|
||||
#define mqtt5_msg_pingresp mqtt_msg_pingresp
|
||||
|
||||
#define mqtt5_get_unsuback_data mqtt5_get_suback_data
|
||||
|
||||
#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_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);
|
||||
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_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);
|
||||
mqtt_message_t *mqtt5_msg_puback(mqtt_connection_t *connection, uint16_t message_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MQTT5_MSG_H */
|
||||
|
146
lib/include/mqtt_client_priv.h
Normal file
146
lib/include/mqtt_client_priv.h
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _MQTT_CLIENT_PRIV_H_
|
||||
#define _MQTT_CLIENT_PRIV_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdatomic.h>
|
||||
#include "esp_err.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "mqtt_msg.h"
|
||||
#ifdef MQTT_PROTOCOL_5
|
||||
#include "mqtt5_client_priv.h"
|
||||
#endif
|
||||
#include "esp_transport.h"
|
||||
#include "esp_transport_tcp.h"
|
||||
#include "esp_transport_ssl.h"
|
||||
#include "esp_transport_ws.h"
|
||||
#include "esp_log.h"
|
||||
#include "mqtt_outbox.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mqtt_supported_features.h"
|
||||
|
||||
/* using uri parser */
|
||||
#include "http_parser.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_NEWLIB_NANO_FORMAT
|
||||
#define NEWLIB_NANO_COMPAT_FORMAT PRIu32
|
||||
#define NEWLIB_NANO_COMPAT_CAST(size_t_var) (uint32_t)size_t_var
|
||||
#else
|
||||
#define NEWLIB_NANO_COMPAT_FORMAT "zu"
|
||||
#define NEWLIB_NANO_COMPAT_CAST(size_t_var) size_t_var
|
||||
#endif
|
||||
|
||||
#ifdef MQTT_DISABLE_API_LOCKS
|
||||
# define MQTT_API_LOCK(c)
|
||||
# define MQTT_API_UNLOCK(c)
|
||||
#else
|
||||
# define MQTT_API_LOCK(c) xSemaphoreTakeRecursive(c->api_lock, portMAX_DELAY)
|
||||
# define MQTT_API_UNLOCK(c) xSemaphoreGiveRecursive(c->api_lock)
|
||||
#endif /* MQTT_USE_API_LOCKS */
|
||||
|
||||
typedef struct mqtt_state {
|
||||
uint8_t *in_buffer;
|
||||
int in_buffer_length;
|
||||
size_t message_length;
|
||||
size_t in_buffer_read_len;
|
||||
mqtt_connection_t connection;
|
||||
uint16_t pending_msg_id;
|
||||
int pending_msg_type;
|
||||
int pending_publish_qos;
|
||||
} mqtt_state_t;
|
||||
|
||||
typedef struct {
|
||||
esp_event_loop_handle_t event_loop_handle;
|
||||
int task_stack;
|
||||
int task_prio;
|
||||
char *uri;
|
||||
char *host;
|
||||
char *path;
|
||||
char *scheme;
|
||||
int port;
|
||||
bool auto_reconnect;
|
||||
int network_timeout_ms;
|
||||
int refresh_connection_after_ms;
|
||||
int reconnect_timeout_ms;
|
||||
char **alpn_protos;
|
||||
int num_alpn_protos;
|
||||
char *clientkey_password;
|
||||
int clientkey_password_len;
|
||||
bool use_global_ca_store;
|
||||
esp_err_t ((*crt_bundle_attach)(void *conf));
|
||||
const char *cacert_buf;
|
||||
size_t cacert_bytes;
|
||||
const char *clientcert_buf;
|
||||
size_t clientcert_bytes;
|
||||
const char *clientkey_buf;
|
||||
size_t clientkey_bytes;
|
||||
const struct psk_key_hint *psk_hint_key;
|
||||
bool skip_cert_common_name_check;
|
||||
const char *common_name;
|
||||
bool use_secure_element;
|
||||
void *ds_data;
|
||||
int message_retransmit_timeout;
|
||||
uint64_t outbox_limit;
|
||||
esp_transport_handle_t transport;
|
||||
struct ifreq * if_name;
|
||||
} mqtt_config_storage_t;
|
||||
|
||||
typedef enum {
|
||||
MQTT_STATE_INIT = 0,
|
||||
MQTT_STATE_DISCONNECTED,
|
||||
MQTT_STATE_CONNECTED,
|
||||
MQTT_STATE_WAIT_RECONNECT,
|
||||
} mqtt_client_state_t;
|
||||
|
||||
struct esp_mqtt_client {
|
||||
esp_transport_list_handle_t transport_list;
|
||||
esp_transport_handle_t transport;
|
||||
mqtt_config_storage_t *config;
|
||||
mqtt_state_t mqtt_state;
|
||||
_Atomic mqtt_client_state_t state;
|
||||
uint64_t refresh_connection_tick;
|
||||
int64_t keepalive_tick;
|
||||
uint64_t reconnect_tick;
|
||||
#ifdef MQTT_PROTOCOL_5
|
||||
mqtt5_config_storage_t *mqtt5_config;
|
||||
uint16_t send_publish_packet_count; // This is for MQTT v5.0 flow control
|
||||
#endif
|
||||
int wait_timeout_ms;
|
||||
int auto_reconnect;
|
||||
esp_mqtt_event_t event;
|
||||
bool run;
|
||||
bool wait_for_ping_resp;
|
||||
outbox_handle_t outbox;
|
||||
EventGroupHandle_t status_bits;
|
||||
SemaphoreHandle_t api_lock;
|
||||
TaskHandle_t task_handle;
|
||||
#if MQTT_EVENT_QUEUE_SIZE > 1
|
||||
atomic_int queued_events;
|
||||
#endif
|
||||
};
|
||||
|
||||
bool esp_mqtt_set_if_config(char const *const new_config, char **old_config);
|
||||
void esp_mqtt_destroy_config(esp_mqtt_client_handle_t client);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
@ -8,9 +8,27 @@
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define MQTT_PROTOCOL_311 CONFIG_MQTT_PROTOCOL_311
|
||||
#define MQTT_RECONNECT_TIMEOUT_MS (10*1000)
|
||||
#ifdef CONFIG_MQTT_PROTOCOL_311
|
||||
#define MQTT_PROTOCOL_311
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MQTT_PROTOCOL_5
|
||||
#define MQTT_PROTOCOL_5
|
||||
#endif
|
||||
|
||||
#define MQTT_RECON_DEFAULT_MS (10*1000)
|
||||
|
||||
#ifdef CONFIG_MQTT_POLL_READ_TIMEOUT_MS
|
||||
#define MQTT_POLL_READ_TIMEOUT_MS CONFIG_MQTT_POLL_READ_TIMEOUT_MS
|
||||
#else
|
||||
#define MQTT_POLL_READ_TIMEOUT_MS (1000)
|
||||
#endif
|
||||
|
||||
#define MQTT_MSG_ID_INCREMENTAL CONFIG_MQTT_MSG_ID_INCREMENTAL
|
||||
|
||||
#define MQTT_SKIP_PUBLISH_IF_DISCONNECTED CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED
|
||||
|
||||
#define MQTT_REPORT_DELETED_MESSAGES CONFIG_MQTT_REPORT_DELETED_MESSAGES
|
||||
|
||||
#if CONFIG_MQTT_BUFFER_SIZE
|
||||
#define MQTT_BUFFER_SIZE_BYTE CONFIG_MQTT_BUFFER_SIZE
|
||||
@ -18,13 +36,11 @@
|
||||
#define MQTT_BUFFER_SIZE_BYTE 1024
|
||||
#endif
|
||||
|
||||
#define MQTT_MAX_HOST_LEN 64
|
||||
#define MQTT_MAX_CLIENT_LEN 32
|
||||
#define MQTT_MAX_USERNAME_LEN 32
|
||||
#define MQTT_MAX_PASSWORD_LEN 65
|
||||
#define MQTT_MAX_LWT_TOPIC 32
|
||||
#define MQTT_MAX_LWT_MSG 128
|
||||
#if CONFIG_MQTT_TASK_PRIORITY
|
||||
#define MQTT_TASK_PRIORITY CONFIG_MQTT_TASK_PRIORITY
|
||||
#else
|
||||
#define MQTT_TASK_PRIORITY 5
|
||||
#endif
|
||||
|
||||
#if CONFIG_MQTT_TASK_STACK_SIZE
|
||||
#define MQTT_TASK_STACK CONFIG_MQTT_TASK_STACK_SIZE
|
||||
@ -33,7 +49,6 @@
|
||||
#endif
|
||||
|
||||
#define MQTT_KEEPALIVE_TICK (120)
|
||||
#define MQTT_CMD_QUEUE_SIZE (10)
|
||||
#define MQTT_NETWORK_TIMEOUT_MS (10000)
|
||||
|
||||
#ifdef CONFIG_MQTT_TCP_DEFAULT_PORT
|
||||
@ -54,7 +69,7 @@
|
||||
#define MQTT_WS_DEFAULT_PORT 80
|
||||
#endif
|
||||
|
||||
#ifdef MQTT_WSS_DEFAULT_PORT
|
||||
#ifdef CONFIG_MQTT_WSS_DEFAULT_PORT
|
||||
#define MQTT_WSS_DEFAULT_PORT CONFIG_MQTT_WSS_DEFAULT_PORT
|
||||
#else
|
||||
#define MQTT_WSS_DEFAULT_PORT 443
|
||||
@ -76,11 +91,28 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MQTT_OUTBOX_EXPIRED_TIMEOUT_MS
|
||||
#define OUTBOX_EXPIRED_TIMEOUT_MS CONFIG_MQTT_OUTBOX_EXPIRED_TIMEOUT_MS
|
||||
#else
|
||||
#define OUTBOX_EXPIRED_TIMEOUT_MS (30*1000)
|
||||
#endif
|
||||
|
||||
#define MQTT_ENABLE_SSL CONFIG_MQTT_TRANSPORT_SSL
|
||||
#define MQTT_ENABLE_WS CONFIG_MQTT_TRANSPORT_WEBSOCKET
|
||||
#define MQTT_ENABLE_WSS CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE
|
||||
#define MQTT_DEFAULT_RETRANSMIT_TIMEOUT_MS 1000
|
||||
|
||||
#ifdef CONFIG_MQTT_EVENT_QUEUE_SIZE
|
||||
#define MQTT_EVENT_QUEUE_SIZE CONFIG_MQTT_EVENT_QUEUE_SIZE
|
||||
#else
|
||||
#define MQTT_EVENT_QUEUE_SIZE 1
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MQTT_OUTBOX_DATA_ON_EXTERNAL_MEMORY
|
||||
#define MQTT_OUTBOX_MEMORY MALLOC_CAP_SPIRAM
|
||||
#else
|
||||
#define MQTT_OUTBOX_MEMORY MALLOC_CAP_DEFAULT
|
||||
#endif
|
||||
|
||||
#define OUTBOX_EXPIRED_TIMEOUT_MS (30*1000)
|
||||
#define OUTBOX_MAX_SIZE (4*1024)
|
||||
#endif
|
@ -1,6 +1,10 @@
|
||||
#ifndef MQTT_MSG_H
|
||||
#define MQTT_MSG_H
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "mqtt_config.h"
|
||||
#include "mqtt_client.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -57,59 +61,51 @@ enum mqtt_message_type {
|
||||
MQTT_MSG_TYPE_DISCONNECT = 14
|
||||
};
|
||||
|
||||
enum mqtt_connect_return_code {
|
||||
CONNECTION_ACCEPTED = 0,
|
||||
CONNECTION_REFUSE_PROTOCOL,
|
||||
CONNECTION_REFUSE_ID_REJECTED,
|
||||
CONNECTION_REFUSE_SERVER_UNAVAILABLE,
|
||||
CONNECTION_REFUSE_BAD_USERNAME,
|
||||
CONNECTION_REFUSE_NOT_AUTHORIZED
|
||||
};
|
||||
|
||||
typedef struct mqtt_message {
|
||||
uint8_t *data;
|
||||
uint32_t length;
|
||||
uint32_t fragmented_msg_total_length; /*!< total len of fragmented messages (zero for all other messages) */
|
||||
uint32_t fragmented_msg_data_offset; /*!< data offset of fragmented messages (zero for all other messages) */
|
||||
size_t length;
|
||||
size_t fragmented_msg_total_length; /*!< total len of fragmented messages (zero for all other messages) */
|
||||
size_t fragmented_msg_data_offset; /*!< data offset of fragmented messages (zero for all other messages) */
|
||||
} mqtt_message_t;
|
||||
|
||||
typedef struct mqtt_connection {
|
||||
mqtt_message_t message;
|
||||
|
||||
uint16_t message_id;
|
||||
uint8_t *buffer;
|
||||
uint16_t buffer_length;
|
||||
|
||||
} mqtt_connection_t;
|
||||
|
||||
typedef struct mqtt_connect_info {
|
||||
char *client_id;
|
||||
char *username;
|
||||
char *password;
|
||||
char *will_topic;
|
||||
char *will_message;
|
||||
int keepalive;
|
||||
int64_t keepalive; /*!< keepalive=0 -> keepalive is disabled */
|
||||
int will_length;
|
||||
int will_qos;
|
||||
int will_retain;
|
||||
int clean_session;
|
||||
|
||||
esp_mqtt_protocol_ver_t protocol_ver;
|
||||
} mqtt_connect_info_t;
|
||||
|
||||
typedef struct mqtt_connection {
|
||||
mqtt_message_t outbound_message;
|
||||
#if MQTT_MSG_ID_INCREMENTAL
|
||||
uint16_t last_message_id; /*!< last used id if incremental message id configured */
|
||||
#endif
|
||||
uint8_t *buffer;
|
||||
size_t buffer_length;
|
||||
mqtt_connect_info_t information;
|
||||
|
||||
static inline int mqtt_get_type(uint8_t *buffer)
|
||||
} mqtt_connection_t;
|
||||
|
||||
static inline int mqtt_get_type(const uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] & 0xf0) >> 4;
|
||||
}
|
||||
static inline int mqtt_get_connect_session_present(uint8_t *buffer)
|
||||
static inline int mqtt_get_connect_session_present(const uint8_t *buffer)
|
||||
{
|
||||
return buffer[2] & 0x01;
|
||||
}
|
||||
static inline int mqtt_get_connect_return_code(uint8_t *buffer)
|
||||
static inline int mqtt_get_connect_return_code(const uint8_t *buffer)
|
||||
{
|
||||
return buffer[3];
|
||||
}
|
||||
static inline int mqtt_get_dup(uint8_t *buffer)
|
||||
static inline int mqtt_get_dup(const uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] & 0x08) >> 3;
|
||||
}
|
||||
@ -117,22 +113,25 @@ static inline void mqtt_set_dup(uint8_t *buffer)
|
||||
{
|
||||
buffer[0] |= 0x08;
|
||||
}
|
||||
static inline int mqtt_get_qos(uint8_t *buffer)
|
||||
static inline int mqtt_get_qos(const uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] & 0x06) >> 1;
|
||||
}
|
||||
static inline int mqtt_get_retain(uint8_t *buffer)
|
||||
static inline int mqtt_get_retain(const uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] & 0x01);
|
||||
}
|
||||
|
||||
void mqtt_msg_init(mqtt_connection_t *connection, uint8_t *buffer, uint16_t buffer_length);
|
||||
bool mqtt_header_complete(uint8_t *buffer, uint16_t buffer_length);
|
||||
uint32_t mqtt_get_total_length(uint8_t *buffer, uint16_t length, int *fixed_size_len);
|
||||
char *mqtt_get_publish_topic(uint8_t *buffer, uint32_t *length);
|
||||
char *mqtt_get_publish_data(uint8_t *buffer, uint32_t *length);
|
||||
uint16_t mqtt_get_id(uint8_t *buffer, uint16_t length);
|
||||
int mqtt_has_valid_msg_hdr(uint8_t *buffer, uint16_t length);
|
||||
bool mqtt_header_complete(uint8_t *buffer, size_t buffer_length);
|
||||
size_t mqtt_get_total_length(const uint8_t *buffer, size_t length, int *fixed_size_len);
|
||||
char *mqtt_get_publish_topic(uint8_t *buffer, size_t *length);
|
||||
char *mqtt_get_publish_data(uint8_t *buffer, size_t *length);
|
||||
char *mqtt_get_suback_data(uint8_t *buffer, size_t *length);
|
||||
uint16_t mqtt_get_id(uint8_t *buffer, size_t length);
|
||||
int mqtt_has_valid_msg_hdr(uint8_t *buffer, size_t length);
|
||||
|
||||
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);
|
||||
@ -140,13 +139,11 @@ mqtt_message_t *mqtt_msg_puback(mqtt_connection_t *connection, uint16_t message_
|
||||
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 char *topic, int qos, 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_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);
|
||||
mqtt_message_t *mqtt_msg_disconnect(mqtt_connection_t *connection);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -6,6 +6,7 @@
|
||||
#ifndef _MQTT_OUTOBX_H_
|
||||
#define _MQTT_OUTOBX_H_
|
||||
#include "platform.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -13,9 +14,10 @@ extern "C" {
|
||||
|
||||
struct outbox_item;
|
||||
|
||||
typedef struct outbox_list_t *outbox_handle_t;
|
||||
typedef struct outbox_t *outbox_handle_t;
|
||||
typedef struct outbox_item *outbox_item_handle_t;
|
||||
typedef struct outbox_message *outbox_message_handle_t;
|
||||
typedef long long outbox_tick_t;
|
||||
|
||||
typedef struct outbox_message {
|
||||
uint8_t *data;
|
||||
@ -30,23 +32,31 @@ typedef struct outbox_message {
|
||||
typedef enum pending_state {
|
||||
QUEUED,
|
||||
TRANSMITTED,
|
||||
ACKNOWLEDGED,
|
||||
CONFIRMED
|
||||
} pending_state_t;
|
||||
|
||||
outbox_handle_t outbox_init(void);
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, int tick);
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, int *tick);
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick);
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick);
|
||||
outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id);
|
||||
uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos);
|
||||
esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type);
|
||||
esp_err_t outbox_delete_msgid(outbox_handle_t outbox, int msg_id);
|
||||
esp_err_t outbox_delete_msgtype(outbox_handle_t outbox, int msg_type);
|
||||
int outbox_delete_expired(outbox_handle_t outbox, int current_tick, int timeout);
|
||||
esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item);
|
||||
int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout);
|
||||
/**
|
||||
* @brief Deletes single expired message returning it's message id
|
||||
*
|
||||
* @return msg id of the deleted message, -1 if no expired message in the outbox
|
||||
*/
|
||||
int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout);
|
||||
|
||||
esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending);
|
||||
int outbox_get_size(outbox_handle_t outbox);
|
||||
esp_err_t outbox_cleanup(outbox_handle_t outbox, int max_size);
|
||||
pending_state_t outbox_item_get_pending(outbox_item_handle_t item);
|
||||
esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick);
|
||||
uint64_t outbox_get_size(outbox_handle_t outbox);
|
||||
void outbox_destroy(outbox_handle_t outbox);
|
||||
void outbox_delete_all_items(outbox_handle_t outbox);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -7,29 +7,23 @@
|
||||
#define _ESP_PLATFORM_H__
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
#include "sys/queue.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
char *platform_create_id_string(void);
|
||||
int platform_random(int max);
|
||||
long long platform_tick_get_ms(void);
|
||||
void ms_to_timeval(int timeout_ms, struct timeval *tv);
|
||||
uint64_t platform_tick_get_ms(void);
|
||||
|
||||
#define ESP_MEM_CHECK(TAG, a, action) if (!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s): %s", __FILE__, __LINE__, __FUNCTION__, "Memory exhausted"); \
|
||||
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, "Memory exhausted"); \
|
||||
action; \
|
||||
}
|
||||
|
||||
#define ESP_OK_CHECK(TAG, a, action) if ((a) != ESP_OK) { \
|
||||
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, "Failed with non ESP_OK err code"); \
|
||||
action; \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
1043
lib/mqtt5_msg.c
Normal file
1043
lib/mqtt5_msg.c
Normal file
File diff suppressed because it is too large
Load Diff
267
lib/mqtt_msg.c
267
lib/mqtt_msg.c
@ -28,14 +28,15 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "mqtt_client.h"
|
||||
#include "mqtt_msg.h"
|
||||
#include "mqtt_config.h"
|
||||
#include "platform.h"
|
||||
|
||||
#define MQTT_MAX_FIXED_HEADER_SIZE 5
|
||||
#define MQTT_3_1_VARIABLE_HEADER_SIZE 12
|
||||
#define MQTT_3_1_1_VARIABLE_HEADER_SIZE 10
|
||||
|
||||
enum mqtt_connect_flag {
|
||||
MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
|
||||
@ -45,30 +46,16 @@ enum mqtt_connect_flag {
|
||||
MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
|
||||
};
|
||||
|
||||
struct __attribute((__packed__)) mqtt_connect_variable_header {
|
||||
uint8_t lengthMsb;
|
||||
uint8_t lengthLsb;
|
||||
#if defined(MQTT_PROTOCOL_311)
|
||||
uint8_t magic[4];
|
||||
#else
|
||||
uint8_t magic[6];
|
||||
#endif
|
||||
uint8_t version;
|
||||
uint8_t flags;
|
||||
uint8_t keepaliveMsb;
|
||||
uint8_t keepaliveLsb;
|
||||
};
|
||||
|
||||
static int append_string(mqtt_connection_t *connection, const char *string, int len)
|
||||
{
|
||||
if (connection->message.length + len + 2 > connection->buffer_length) {
|
||||
if (connection->outbound_message.length + len + 2 > connection->buffer_length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
connection->buffer[connection->message.length++] = len >> 8;
|
||||
connection->buffer[connection->message.length++] = len & 0xff;
|
||||
memcpy(connection->buffer + connection->message.length, string, len);
|
||||
connection->message.length += len;
|
||||
connection->buffer[connection->outbound_message.length++] = len >> 8;
|
||||
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,41 +65,45 @@ static uint16_t append_message_id(mqtt_connection_t *connection, uint16_t messag
|
||||
// If message_id is zero then we should assign one, otherwise
|
||||
// we'll use the one supplied by the caller
|
||||
while (message_id == 0) {
|
||||
#if MQTT_MSG_ID_INCREMENTAL
|
||||
message_id = ++connection->last_message_id;
|
||||
#else
|
||||
message_id = platform_random(65535);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (connection->message.length + 2 > connection->buffer_length) {
|
||||
if (connection->outbound_message.length + 2 > connection->buffer_length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
connection->buffer[connection->message.length++] = message_id >> 8;
|
||||
connection->buffer[connection->message.length++] = message_id & 0xff;
|
||||
connection->buffer[connection->outbound_message.length++] = message_id >> 8;
|
||||
connection->buffer[connection->outbound_message.length++] = message_id & 0xff;
|
||||
|
||||
return message_id;
|
||||
}
|
||||
|
||||
static int init_message(mqtt_connection_t *connection)
|
||||
static int set_message_header_size(mqtt_connection_t *connection)
|
||||
{
|
||||
connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
connection->outbound_message.length = MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
return MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
}
|
||||
|
||||
static mqtt_message_t *fail_message(mqtt_connection_t *connection)
|
||||
{
|
||||
connection->message.data = connection->buffer;
|
||||
connection->message.length = 0;
|
||||
return &connection->message;
|
||||
connection->outbound_message.data = connection->buffer;
|
||||
connection->outbound_message.length = 0;
|
||||
return &connection->outbound_message;
|
||||
}
|
||||
|
||||
static mqtt_message_t *fini_message(mqtt_connection_t *connection, int type, int dup, int qos, int retain)
|
||||
{
|
||||
int message_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
int message_length = connection->outbound_message.length - MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
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->message.fragmented_msg_total_length) {
|
||||
total_length = connection->message.fragmented_msg_total_length - MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
if (connection->outbound_message.fragmented_msg_total_length) {
|
||||
total_length = connection->outbound_message.fragmented_msg_total_length - MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
}
|
||||
|
||||
// Encode MQTT message length
|
||||
@ -133,10 +124,10 @@ static mqtt_message_t *fini_message(mqtt_connection_t *connection, int type, int
|
||||
}
|
||||
|
||||
// Save the header bytes
|
||||
connection->message.length = message_length + len_bytes + 1; // msg len + encoded_size len + type (1 byte)
|
||||
connection->outbound_message.length = message_length + len_bytes + 1; // msg len + encoded_size len + type (1 byte)
|
||||
int offs = MQTT_MAX_FIXED_HEADER_SIZE - 1 - len_bytes;
|
||||
connection->message.data = connection->buffer + offs;
|
||||
connection->message.fragmented_msg_data_offset -= offs;
|
||||
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);
|
||||
// length bytes
|
||||
@ -144,20 +135,13 @@ static mqtt_message_t *fini_message(mqtt_connection_t *connection, int type, int
|
||||
connection->buffer[offs++] = encoded_lens[j];
|
||||
}
|
||||
|
||||
return &connection->message;
|
||||
return &connection->outbound_message;
|
||||
}
|
||||
|
||||
void mqtt_msg_init(mqtt_connection_t *connection, uint8_t *buffer, uint16_t buffer_length)
|
||||
{
|
||||
memset(connection, 0, sizeof(mqtt_connection_t));
|
||||
connection->buffer = buffer;
|
||||
connection->buffer_length = buffer_length;
|
||||
}
|
||||
|
||||
uint32_t mqtt_get_total_length(uint8_t *buffer, uint16_t length, int *fixed_size_len)
|
||||
size_t mqtt_get_total_length(const uint8_t *buffer, size_t length, int *fixed_size_len)
|
||||
{
|
||||
int i;
|
||||
uint32_t totlen = 0;
|
||||
size_t totlen = 0;
|
||||
|
||||
for (i = 1; i < length; ++i) {
|
||||
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
|
||||
@ -174,7 +158,7 @@ uint32_t mqtt_get_total_length(uint8_t *buffer, uint16_t length, int *fixed_size
|
||||
return totlen;
|
||||
}
|
||||
|
||||
bool mqtt_header_complete(uint8_t *buffer, uint16_t buffer_length)
|
||||
bool mqtt_header_complete(uint8_t *buffer, size_t buffer_length)
|
||||
{
|
||||
uint16_t i;
|
||||
uint16_t topiclen;
|
||||
@ -205,20 +189,17 @@ bool mqtt_header_complete(uint8_t *buffer, uint16_t buffer_length)
|
||||
return buffer_length >= i;
|
||||
}
|
||||
|
||||
char *mqtt_get_publish_topic(uint8_t *buffer, uint32_t *length)
|
||||
char *mqtt_get_publish_topic(uint8_t *buffer, size_t *length)
|
||||
{
|
||||
int i;
|
||||
int totlen = 0;
|
||||
int topiclen;
|
||||
|
||||
for (i = 1; i < *length; ++i) {
|
||||
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
|
||||
if ((buffer[i] & 0x80) == 0) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
totlen += i;
|
||||
|
||||
if (i + 2 >= *length) {
|
||||
return NULL;
|
||||
@ -234,7 +215,7 @@ char *mqtt_get_publish_topic(uint8_t *buffer, uint32_t *length)
|
||||
return (char *)(buffer + i);
|
||||
}
|
||||
|
||||
char *mqtt_get_publish_data(uint8_t *buffer, uint32_t *length)
|
||||
char *mqtt_get_publish_data(uint8_t *buffer, size_t *length)
|
||||
{
|
||||
int i;
|
||||
int totlen = 0;
|
||||
@ -282,7 +263,19 @@ char *mqtt_get_publish_data(uint8_t *buffer, uint32_t *length)
|
||||
return (char *)(buffer + i);
|
||||
}
|
||||
|
||||
uint16_t mqtt_get_id(uint8_t *buffer, uint16_t length)
|
||||
char *mqtt_get_suback_data(uint8_t *buffer, size_t *length)
|
||||
{
|
||||
// SUBACK payload length = total length - (fixed header (2 bytes) + variable header (2 bytes))
|
||||
// This requires the remaining length to be encoded in 1 byte.
|
||||
if (*length > 4) {
|
||||
*length -= 4;
|
||||
return (char *)(buffer + 4);
|
||||
}
|
||||
*length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t mqtt_get_id(uint8_t *buffer, size_t length)
|
||||
{
|
||||
if (length < 1) {
|
||||
return 0;
|
||||
@ -346,33 +339,45 @@ uint16_t mqtt_get_id(uint8_t *buffer, uint16_t length)
|
||||
|
||||
mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info)
|
||||
{
|
||||
struct mqtt_connect_variable_header *variable_header;
|
||||
|
||||
init_message(connection);
|
||||
set_message_header_size(connection);
|
||||
|
||||
if (connection->message.length + sizeof(*variable_header) > connection->buffer_length) {
|
||||
int header_len;
|
||||
if (info->protocol_ver == MQTT_PROTOCOL_V_3_1) {
|
||||
header_len = MQTT_3_1_VARIABLE_HEADER_SIZE;
|
||||
} else {
|
||||
header_len = MQTT_3_1_1_VARIABLE_HEADER_SIZE;
|
||||
}
|
||||
|
||||
if (connection->outbound_message.length + header_len > connection->buffer_length) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
variable_header = (void *)(connection->buffer + connection->message.length);
|
||||
connection->message.length += sizeof(*variable_header);
|
||||
char *variable_header = (char *)(connection->buffer + connection->outbound_message.length);
|
||||
connection->outbound_message.length += header_len;
|
||||
|
||||
variable_header->lengthMsb = 0;
|
||||
#if defined(CONFIG_MQTT_PROTOCOL_311)
|
||||
variable_header->lengthLsb = 4;
|
||||
memcpy(variable_header->magic, "MQTT", 4);
|
||||
variable_header->version = 4;
|
||||
#else
|
||||
variable_header->lengthLsb = 6;
|
||||
memcpy(variable_header->magic, "MQIsdp", 6);
|
||||
variable_header->version = 3;
|
||||
#endif
|
||||
int header_idx = 0;
|
||||
variable_header[header_idx++] = 0; // Variable header length MSB
|
||||
|
||||
variable_header->flags = 0;
|
||||
variable_header->keepaliveMsb = info->keepalive >> 8;
|
||||
variable_header->keepaliveLsb = info->keepalive & 0xff;
|
||||
if (info->protocol_ver == MQTT_PROTOCOL_V_3_1) {
|
||||
variable_header[header_idx++] = 6; // Variable header length LSB
|
||||
memcpy(&variable_header[header_idx], "MQIsdp", 6); // Protocol name
|
||||
header_idx = header_idx + 6;
|
||||
variable_header[header_idx++] = 3; // Protocol version
|
||||
} else {
|
||||
/* Defaults to protocol version 3.1.1 values */
|
||||
variable_header[header_idx++] = 4; // Variable header length LSB
|
||||
memcpy(&variable_header[header_idx], "MQTT", 4); // Protocol name
|
||||
header_idx = header_idx + 4;
|
||||
variable_header[header_idx++] = 4; // Protocol version
|
||||
}
|
||||
|
||||
int flags_offset = header_idx;
|
||||
variable_header[header_idx++] = 0; // Flags
|
||||
variable_header[header_idx++] = info->keepalive >> 8; // Keep-alive MSB
|
||||
variable_header[header_idx] = info->keepalive & 0xff; // Keep-alive LSB
|
||||
|
||||
if (info->clean_session) {
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
|
||||
}
|
||||
|
||||
if (info->client_id != NULL && info->client_id[0] != '\0') {
|
||||
@ -380,7 +385,9 @@ mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_inf
|
||||
return fail_message(connection);
|
||||
}
|
||||
} else {
|
||||
return fail_message(connection);
|
||||
if (append_string(connection, "", 0) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
}
|
||||
|
||||
if (info->will_topic != NULL && info->will_topic[0] != '\0') {
|
||||
@ -392,11 +399,11 @@ mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_inf
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_WILL;
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_WILL;
|
||||
if (info->will_retain) {
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_WILL_RETAIN;
|
||||
}
|
||||
variable_header->flags |= (info->will_qos & 3) << 3;
|
||||
variable_header[flags_offset] |= (info->will_qos & 3) << 3;
|
||||
}
|
||||
|
||||
if (info->username != NULL && info->username[0] != '\0') {
|
||||
@ -404,15 +411,26 @@ mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_inf
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME;
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_USERNAME;
|
||||
}
|
||||
|
||||
if (info->password != NULL && info->password[0] != '\0') {
|
||||
if (info->username == NULL || info->username[0] == '\0') {
|
||||
/* In case if password is set without username, we need to set a zero length username.
|
||||
* (otherwise we violate: MQTT-3.1.2-22: If the User Name Flag is set to 0 then the Password Flag MUST be set to 0.)
|
||||
*/
|
||||
if (append_string(connection, "", 0) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_USERNAME;
|
||||
}
|
||||
|
||||
if (append_string(connection, info->password, strlen(info->password)) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD;
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_PASSWORD;
|
||||
}
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0);
|
||||
@ -420,7 +438,7 @@ mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_inf
|
||||
|
||||
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)
|
||||
{
|
||||
init_message(connection);
|
||||
set_message_header_size(connection);
|
||||
|
||||
if (topic == NULL || topic[0] == '\0') {
|
||||
return fail_message(connection);
|
||||
@ -430,6 +448,10 @@ mqtt_message_t *mqtt_msg_publish(mqtt_connection_t *connection, const char *topi
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (data == NULL && data_length > 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (qos > 0) {
|
||||
if ((*message_id = append_message_id(connection, 0)) == 0) {
|
||||
return fail_message(connection);
|
||||
@ -438,23 +460,25 @@ mqtt_message_t *mqtt_msg_publish(mqtt_connection_t *connection, const char *topi
|
||||
*message_id = 0;
|
||||
}
|
||||
|
||||
if (connection->message.length + data_length > connection->buffer_length) {
|
||||
// Not enough size in buffer -> fragment this message
|
||||
connection->message.fragmented_msg_data_offset = connection->message.length;
|
||||
memcpy(connection->buffer + connection->message.length, data, connection->buffer_length - connection->message.length);
|
||||
connection->message.length = connection->buffer_length;
|
||||
connection->message.fragmented_msg_total_length = data_length + connection->message.fragmented_msg_data_offset;
|
||||
} else {
|
||||
memcpy(connection->buffer + connection->message.length, data, data_length);
|
||||
connection->message.length += data_length;
|
||||
connection->message.fragmented_msg_total_length = 0;
|
||||
if (data != NULL) {
|
||||
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);
|
||||
connection->outbound_message.length = connection->buffer_length;
|
||||
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)
|
||||
{
|
||||
init_message(connection);
|
||||
set_message_header_size(connection);
|
||||
if (append_message_id(connection, message_id) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
@ -463,7 +487,7 @@ mqtt_message_t *mqtt_msg_puback(mqtt_connection_t *connection, uint16_t message_
|
||||
|
||||
mqtt_message_t *mqtt_msg_pubrec(mqtt_connection_t *connection, uint16_t message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
set_message_header_size(connection);
|
||||
if (append_message_id(connection, message_id) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
@ -472,7 +496,7 @@ mqtt_message_t *mqtt_msg_pubrec(mqtt_connection_t *connection, uint16_t message_
|
||||
|
||||
mqtt_message_t *mqtt_msg_pubrel(mqtt_connection_t *connection, uint16_t message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
set_message_header_size(connection);
|
||||
if (append_message_id(connection, message_id) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
@ -481,40 +505,43 @@ mqtt_message_t *mqtt_msg_pubrel(mqtt_connection_t *connection, uint16_t message_
|
||||
|
||||
mqtt_message_t *mqtt_msg_pubcomp(mqtt_connection_t *connection, uint16_t message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
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 char *topic, int qos, 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)
|
||||
{
|
||||
init_message(connection);
|
||||
|
||||
if (topic == NULL || topic[0] == '\0') {
|
||||
return fail_message(connection);
|
||||
}
|
||||
set_message_header_size(connection);
|
||||
|
||||
if ((*message_id = append_message_id(connection, 0)) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (append_string(connection, topic, strlen(topic)) < 0) {
|
||||
return 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 (connection->message.length + 1 > connection->buffer_length) {
|
||||
return fail_message(connection);
|
||||
if (append_string(connection, topic_list[topic_number].filter, strlen(topic_list[topic_number].filter)) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
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 ++;
|
||||
}
|
||||
connection->buffer[connection->message.length++] = qos;
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_unsubscribe(mqtt_connection_t *connection, const char *topic, uint16_t *message_id)
|
||||
{
|
||||
init_message(connection);
|
||||
set_message_header_size(connection);
|
||||
|
||||
if (topic == NULL || topic[0] == '\0') {
|
||||
return fail_message(connection);
|
||||
@ -533,19 +560,19 @@ mqtt_message_t *mqtt_msg_unsubscribe(mqtt_connection_t *connection, const char *
|
||||
|
||||
mqtt_message_t *mqtt_msg_pingreq(mqtt_connection_t *connection)
|
||||
{
|
||||
init_message(connection);
|
||||
set_message_header_size(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_pingresp(mqtt_connection_t *connection)
|
||||
{
|
||||
init_message(connection);
|
||||
set_message_header_size(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_disconnect(mqtt_connection_t *connection)
|
||||
{
|
||||
init_message(connection);
|
||||
set_message_header_size(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0);
|
||||
}
|
||||
|
||||
@ -553,7 +580,7 @@ mqtt_message_t *mqtt_msg_disconnect(mqtt_connection_t *connection)
|
||||
* check flags: [MQTT-2.2.2-1], [MQTT-2.2.2-2]
|
||||
* returns 0 if flags are invalid, otherwise returns 1
|
||||
*/
|
||||
int mqtt_has_valid_msg_hdr(uint8_t *buffer, uint16_t length)
|
||||
int mqtt_has_valid_msg_hdr(uint8_t *buffer, size_t length)
|
||||
{
|
||||
int qos, dup;
|
||||
|
||||
@ -587,4 +614,24 @@ int mqtt_has_valid_msg_hdr(uint8_t *buffer, uint16_t length)
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void mqtt_msg_buffer_destroy(mqtt_connection_t *connection)
|
||||
{
|
||||
if (connection) {
|
||||
free(connection->buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
#include "mqtt_outbox.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "mqtt_config.h"
|
||||
#include "sys/queue.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifndef CONFIG_MQTT_CUSTOM_OUTBOX
|
||||
|
||||
|
||||
static const char *TAG = "OUTBOX";
|
||||
static const char *TAG = "outbox";
|
||||
|
||||
typedef struct outbox_item {
|
||||
char *buffer;
|
||||
@ -15,24 +16,30 @@ typedef struct outbox_item {
|
||||
int msg_id;
|
||||
int msg_type;
|
||||
int msg_qos;
|
||||
int tick;
|
||||
int retry_count;
|
||||
outbox_tick_t tick;
|
||||
pending_state_t pending;
|
||||
STAILQ_ENTRY(outbox_item) next;
|
||||
} outbox_item_t;
|
||||
|
||||
STAILQ_HEAD(outbox_list_t, outbox_item);
|
||||
|
||||
struct outbox_t {
|
||||
_Atomic uint64_t size;
|
||||
struct outbox_list_t *list;
|
||||
};
|
||||
|
||||
outbox_handle_t outbox_init(void)
|
||||
{
|
||||
outbox_handle_t outbox = calloc(1, sizeof(struct outbox_list_t));
|
||||
outbox_handle_t outbox = calloc(1, sizeof(struct outbox_t));
|
||||
ESP_MEM_CHECK(TAG, outbox, return NULL);
|
||||
STAILQ_INIT(outbox);
|
||||
outbox->list = calloc(1, sizeof(struct outbox_list_t));
|
||||
ESP_MEM_CHECK(TAG, outbox->list, {free(outbox); return NULL;});
|
||||
outbox->size = 0;
|
||||
STAILQ_INIT(outbox->list);
|
||||
return outbox;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, int tick)
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick)
|
||||
{
|
||||
outbox_item_handle_t item = calloc(1, sizeof(outbox_item_t));
|
||||
ESP_MEM_CHECK(TAG, item, return NULL);
|
||||
@ -42,7 +49,7 @@ outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handl
|
||||
item->tick = tick;
|
||||
item->len = message->len + message->remaining_len;
|
||||
item->pending = QUEUED;
|
||||
item->buffer = malloc(message->len + message->remaining_len);
|
||||
item->buffer = heap_caps_malloc(message->len + message->remaining_len, MQTT_OUTBOX_MEMORY);
|
||||
ESP_MEM_CHECK(TAG, item->buffer, {
|
||||
free(item);
|
||||
return NULL;
|
||||
@ -51,15 +58,16 @@ outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handl
|
||||
if (message->remaining_data) {
|
||||
memcpy(item->buffer + message->len, message->remaining_data, message->remaining_len);
|
||||
}
|
||||
STAILQ_INSERT_TAIL(outbox, item, next);
|
||||
ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%d", message->msg_id, message->msg_type, message->len + message->remaining_len, outbox_get_size(outbox));
|
||||
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));
|
||||
return item;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
STAILQ_FOREACH(item, outbox->list, next) {
|
||||
if (item->msg_id == msg_id) {
|
||||
return item;
|
||||
}
|
||||
@ -67,10 +75,10 @@ outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, int *tick)
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
STAILQ_FOREACH(item, outbox->list, next) {
|
||||
if (item->pending == pending) {
|
||||
if (tick) {
|
||||
*tick = item->tick;
|
||||
@ -81,6 +89,21 @@ outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pend
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item_to_delete)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox->list, next) {
|
||||
if (item == item_to_delete) {
|
||||
STAILQ_REMOVE(outbox->list, item, outbox_item, next);
|
||||
outbox->size -= item->len;
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos)
|
||||
{
|
||||
if (item) {
|
||||
@ -96,31 +119,20 @@ uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t
|
||||
esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
STAILQ_FOREACH_SAFE(item, outbox->list, next, tmp) {
|
||||
if (item->msg_id == msg_id && (0xFF & (item->msg_type)) == msg_type) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
STAILQ_REMOVE(outbox->list, item, outbox_item, next);
|
||||
outbox->size -= item->len;
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
ESP_LOGD(TAG, "DELETED msgid=%d, msg_type=%d, remain size=%d", msg_id, msg_type, outbox_get_size(outbox));
|
||||
ESP_LOGD(TAG, "DELETED msgid=%d, msg_type=%d, remain size=%"PRIu64, msg_id, msg_type, outbox_get_size(outbox));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_err_t outbox_delete_msgid(outbox_handle_t outbox, int msg_id)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
if (item->msg_id == msg_id) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
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);
|
||||
@ -131,28 +143,51 @@ esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete_msgtype(outbox_handle_t outbox, int msg_type)
|
||||
pending_state_t outbox_item_get_pending(outbox_item_handle_t item)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
if (item->msg_type == msg_type) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
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;
|
||||
}
|
||||
|
||||
int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
int msg_id = -1;
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox->list, next) {
|
||||
if (current_tick - item->tick > timeout) {
|
||||
STAILQ_REMOVE(outbox->list, item, outbox_item, next);
|
||||
free(item->buffer);
|
||||
outbox->size -= item->len;
|
||||
msg_id = item->msg_id;
|
||||
free(item);
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_OK;
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
int outbox_delete_expired(outbox_handle_t outbox, int current_tick, int timeout)
|
||||
int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
int deleted_items = 0;
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
STAILQ_FOREACH_SAFE(item, outbox->list, next, tmp) {
|
||||
if (current_tick - item->tick > timeout) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
STAILQ_REMOVE(outbox->list, item, outbox_item, next);
|
||||
free(item->buffer);
|
||||
outbox->size -= item->len;
|
||||
free(item);
|
||||
deleted_items ++;
|
||||
}
|
||||
@ -161,35 +196,25 @@ int outbox_delete_expired(outbox_handle_t outbox, int current_tick, int timeout)
|
||||
return deleted_items;
|
||||
}
|
||||
|
||||
int outbox_get_size(outbox_handle_t outbox)
|
||||
uint64_t outbox_get_size(outbox_handle_t outbox)
|
||||
{
|
||||
int siz = 0;
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
// Suppressing "use after free" warning as this could happen only if queue is in inconsistent state
|
||||
// which never happens if STAILQ interface used
|
||||
siz += item->len; // NOLINT(clang-analyzer-unix.Malloc)
|
||||
}
|
||||
return siz;
|
||||
return outbox->size;
|
||||
}
|
||||
|
||||
esp_err_t outbox_cleanup(outbox_handle_t outbox, int max_size)
|
||||
void outbox_delete_all_items(outbox_handle_t outbox)
|
||||
{
|
||||
while (outbox_get_size(outbox) > max_size) {
|
||||
outbox_item_handle_t item = outbox_dequeue(outbox, CONFIRMED, NULL);
|
||||
if (item == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox->list, next, tmp) {
|
||||
STAILQ_REMOVE(outbox->list, item, outbox_item, next);
|
||||
outbox->size -= item->len;
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void outbox_destroy(outbox_handle_t outbox)
|
||||
{
|
||||
outbox_cleanup(outbox, 0);
|
||||
outbox_delete_all_items(outbox);
|
||||
free(outbox->list);
|
||||
free(outbox);
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,37 @@
|
||||
#include "platform.h"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include <sys/time.h>
|
||||
#include "esp_mac.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_random.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const char *TAG = "PLATFORM";
|
||||
static const char *TAG = "platform";
|
||||
|
||||
#define MAX_ID_STRING (32)
|
||||
|
||||
#if defined SOC_WIFI_SUPPORTED
|
||||
#define MAC_TYPE ESP_MAC_WIFI_STA
|
||||
#elif defined SOC_EMAC_SUPPORTED
|
||||
#define MAC_TYPE ESP_MAC_ETH
|
||||
#elif defined SOC_IEEE802154_SUPPORTED
|
||||
#define MAC_TYPE ESP_MAC_IEEE802154
|
||||
#endif
|
||||
char *platform_create_id_string(void)
|
||||
{
|
||||
uint8_t mac[6];
|
||||
char *id_string = calloc(1, MAX_ID_STRING);
|
||||
ESP_MEM_CHECK(TAG, id_string, return NULL);
|
||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||
#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
|
||||
uint8_t mac[6];
|
||||
esp_read_mac(mac, MAC_TYPE);
|
||||
sprintf(id_string, "ESP32_%02x%02X%02X", mac[3], mac[4], mac[5]);
|
||||
#endif
|
||||
return id_string;
|
||||
}
|
||||
|
||||
@ -24,18 +40,9 @@ int platform_random(int max)
|
||||
return esp_random() % max;
|
||||
}
|
||||
|
||||
long long platform_tick_get_ms(void)
|
||||
uint64_t platform_tick_get_ms(void)
|
||||
{
|
||||
struct timeval te;
|
||||
gettimeofday(&te, NULL); // get current time
|
||||
long long milliseconds = te.tv_sec * 1000LL + te.tv_usec / 1000; // calculate milliseconds
|
||||
// printf("milliseconds: %lld\n", milliseconds);
|
||||
return milliseconds;
|
||||
return esp_timer_get_time()/(int64_t)1000;
|
||||
}
|
||||
|
||||
void ms_to_timeval(int timeout_ms, struct timeval *tv)
|
||||
{
|
||||
tv->tv_sec = timeout_ms / 1000;
|
||||
tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000;
|
||||
}
|
||||
#endif
|
||||
|
761
mqtt5_client.c
Normal file
761
mqtt5_client.c
Normal file
@ -0,0 +1,761 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "mqtt_client_priv.h"
|
||||
#include "esp_log.h"
|
||||
#include <string.h>
|
||||
|
||||
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 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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_decrement_packet_counter(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->send_publish_packet_count > 0) {
|
||||
client->send_publish_packet_count --;
|
||||
ESP_LOGD(TAG, "Receive (%d) qos > 0 publish packet with ack", client->send_publish_packet_count);
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_parse_pubcomp(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGI(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_len = msg_data_len;
|
||||
client->event.total_data_len = msg_data_len;
|
||||
client->event.current_data_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_parse_puback(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGI(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_len = msg_data_len;
|
||||
client->event.total_data_len = msg_data_len;
|
||||
client->event.current_data_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_parse_unsuback(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGI(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_len = msg_data_len;
|
||||
client->event.total_data_len = msg_data_len;
|
||||
client->event.current_data_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_parse_suback(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGI(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_err_t esp_mqtt5_parse_connack(esp_mqtt5_client_handle_t client, int *connect_rsp_code)
|
||||
{
|
||||
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) {
|
||||
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)
|
||||
{
|
||||
// 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) {
|
||||
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);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (property.topic_alias) {
|
||||
if (*msg_topic_len == 0) {
|
||||
ESP_LOGI(TAG, "Publish topic is empty, use topic alias");
|
||||
*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) {
|
||||
ESP_LOGE(TAG, "%s: esp_mqtt5_client_update_topic_alias() failed", __func__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client->event.property->payload_format_indicator = property.payload_format_indicator;
|
||||
client->event.property->response_topic = property.response_topic;
|
||||
client->event.property->response_topic_len = property.response_topic_len;
|
||||
client->event.property->correlation_data = property.correlation_data;
|
||||
client->event.property->correlation_data_len = property.correlation_data_len;
|
||||
client->event.property->content_type = property.content_type;
|
||||
client->event.property->content_type_len = property.content_type_len;
|
||||
client->event.property->subscribe_id = property.subscribe_id;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_create_default_config(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
client->event.property = calloc(1, sizeof(esp_mqtt5_event_property_t));
|
||||
ESP_MEM_CHECK(TAG, client->event.property, return ESP_FAIL)
|
||||
client->mqtt5_config = calloc(1, sizeof(mqtt5_config_storage_t));
|
||||
ESP_MEM_CHECK(TAG, client->mqtt5_config, return ESP_FAIL)
|
||||
client->mqtt5_config->server_resp_property_info.max_qos = 2;
|
||||
client->mqtt5_config->server_resp_property_info.retain_available = true;
|
||||
client->mqtt5_config->server_resp_property_info.wildcard_subscribe_available = true;
|
||||
client->mqtt5_config->server_resp_property_info.subscribe_identifiers_available = true;
|
||||
client->mqtt5_config->server_resp_property_info.shared_subscribe_available = true;
|
||||
client->mqtt5_config->server_resp_property_info.receive_maximum = 65535;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void esp_mqtt5_print_error_code(esp_mqtt5_client_handle_t client, int code)
|
||||
{
|
||||
switch (code) {
|
||||
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");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_subscribe_check(esp_mqtt5_client_handle_t client, int qos)
|
||||
{
|
||||
/* Check Server support QoS level */
|
||||
if (client->mqtt5_config->server_resp_property_info.max_qos < qos) {
|
||||
ESP_LOGE(TAG, "Server only support max QoS level %d", client->mqtt5_config->server_resp_property_info.max_qos);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_publish_check(esp_mqtt5_client_handle_t client, int qos, int retain)
|
||||
{
|
||||
/* Check Server support QoS level */
|
||||
if (client->mqtt5_config->server_resp_property_info.max_qos < qos) {
|
||||
ESP_LOGE(TAG, "Server only support max QoS level %d", client->mqtt5_config->server_resp_property_info.max_qos);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Check Server support RETAIN */
|
||||
if (!client->mqtt5_config->server_resp_property_info.retain_available && retain) {
|
||||
ESP_LOGE(TAG, "Server not support retain");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_mqtt5_client_destory(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
if (client->mqtt5_config) {
|
||||
free(client->mqtt5_config->will_property_info.content_type);
|
||||
free(client->mqtt5_config->will_property_info.response_topic);
|
||||
free(client->mqtt5_config->will_property_info.correlation_data);
|
||||
free(client->mqtt5_config->server_resp_property_info.response_info);
|
||||
esp_mqtt5_client_delete_topic_alias(client->mqtt5_config->peer_topic_alias);
|
||||
esp_mqtt5_client_delete_user_property(client->mqtt5_config->connect_property_info.user_property);
|
||||
esp_mqtt5_client_delete_user_property(client->mqtt5_config->will_property_info.user_property);
|
||||
esp_mqtt5_client_delete_user_property(client->mqtt5_config->disconnect_property_info.user_property);
|
||||
free(client->mqtt5_config);
|
||||
}
|
||||
free(client->event.property);
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_mqtt5_client_delete_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle)
|
||||
{
|
||||
if (topic_alias_handle) {
|
||||
mqtt5_topic_alias_item_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, topic_alias_handle, next, tmp) {
|
||||
STAILQ_REMOVE(topic_alias_handle, item, mqtt5_topic_alias, next);
|
||||
free(item->topic);
|
||||
free(item);
|
||||
}
|
||||
free(topic_alias_handle);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
STAILQ_FOREACH(item, topic_alias_handle, next) {
|
||||
if (item->topic_alias == topic_alias) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if ((item->topic_len != topic_len) || strncmp(topic, item->topic, topic_len)) {
|
||||
free(item->topic);
|
||||
item->topic = calloc(1, topic_len);
|
||||
ESP_MEM_CHECK(TAG, item->topic, return ESP_FAIL);
|
||||
memcpy(item->topic, topic, topic_len);
|
||||
item->topic_len = topic_len;
|
||||
}
|
||||
} else {
|
||||
item = calloc(1, sizeof(mqtt5_topic_alias_t));
|
||||
ESP_MEM_CHECK(TAG, item, return ESP_FAIL);
|
||||
item->topic_alias = topic_alias;
|
||||
item->topic_len = topic_len;
|
||||
item->topic = calloc(1, topic_len);
|
||||
ESP_MEM_CHECK(TAG, item->topic, {
|
||||
free(item);
|
||||
return ESP_FAIL;
|
||||
});
|
||||
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)
|
||||
{
|
||||
mqtt5_topic_alias_item_t item;
|
||||
STAILQ_FOREACH(item, topic_alias_handle, next) {
|
||||
if (item->topic_alias == topic_alias) {
|
||||
*topic_length = item->topic_len;
|
||||
return item->topic;
|
||||
}
|
||||
}
|
||||
*topic_length = 0;
|
||||
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)
|
||||
{
|
||||
if (!user_property_new || !user_property_old) {
|
||||
ESP_LOGE(TAG, "Input is NULL");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
mqtt5_user_property_item_t old_item, new_item;
|
||||
STAILQ_FOREACH(old_item, user_property_old, next) {
|
||||
new_item = calloc(1, sizeof(mqtt5_user_property_t));
|
||||
ESP_MEM_CHECK(TAG, new_item, return ESP_FAIL);
|
||||
new_item->key = strdup(old_item->key);
|
||||
ESP_MEM_CHECK(TAG, new_item->key, {
|
||||
free(new_item);
|
||||
return ESP_FAIL;
|
||||
});
|
||||
new_item->value = strdup(old_item->value);
|
||||
ESP_MEM_CHECK(TAG, new_item->value, {
|
||||
free(new_item->key);
|
||||
free(new_item);
|
||||
return ESP_FAIL;
|
||||
});
|
||||
STAILQ_INSERT_TAIL(user_property_new, new_item, next);
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (!client) {
|
||||
ESP_LOGE(TAG, "Client was not initialized");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
MQTT_API_LOCK(client);
|
||||
|
||||
/* Check protocol version */
|
||||
if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGE(TAG, "MQTT protocol version is not v5");
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGE(TAG, "MQTT protocol version is not v5");
|
||||
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
|
||||
ESP_LOGE(TAG, "Protocol error that no local flag set on shared subscription");
|
||||
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)
|
||||
{
|
||||
if (!client) {
|
||||
ESP_LOGE(TAG, "Client was not initialized");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
MQTT_API_LOCK(client);
|
||||
|
||||
/* Check protocol version */
|
||||
if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGE(TAG, "MQTT protocol version is not v5");
|
||||
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)
|
||||
{
|
||||
if (!client) {
|
||||
ESP_LOGE(TAG, "Client was not initialized");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
MQTT_API_LOCK(client);
|
||||
|
||||
/* Check protocol version */
|
||||
if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGE(TAG, "MQTT protocol version is not v5");
|
||||
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));
|
||||
ESP_MEM_CHECK(TAG, client->mqtt5_config->disconnect_property_info.user_property, {
|
||||
MQTT_API_UNLOCK(client);
|
||||
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) {
|
||||
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;
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MQTT_API_UNLOCK(client);
|
||||
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)
|
||||
{
|
||||
if (!client) {
|
||||
ESP_LOGE(TAG, "Client was not initialized");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
MQTT_API_LOCK(client);
|
||||
|
||||
/* Check protocol version */
|
||||
if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGE(TAG, "MQTT protocol version is not v5");
|
||||
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);
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
} else {
|
||||
client->mqtt5_config->connect_property_info.maximum_packet_size = connect_property->maximum_packet_size;
|
||||
}
|
||||
} 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) {
|
||||
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);
|
||||
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);
|
||||
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) {
|
||||
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:
|
||||
esp_mqtt_destroy_config(client);
|
||||
MQTT_API_UNLOCK(client);
|
||||
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)
|
||||
{
|
||||
if (!item_num || !item) {
|
||||
ESP_LOGE(TAG, "Input value is NULL");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (!*user_property) {
|
||||
*user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t));
|
||||
ESP_MEM_CHECK(TAG, *user_property, return ESP_ERR_NO_MEM);
|
||||
STAILQ_INIT(*user_property);
|
||||
}
|
||||
|
||||
for (int i = 0; i < item_num; i ++) {
|
||||
if (item[i].key && item[i].value) {
|
||||
mqtt5_user_property_item_t user_property_item = calloc(1, sizeof(mqtt5_user_property_t));
|
||||
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);
|
||||
goto err;
|
||||
});
|
||||
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);
|
||||
free(user_property_item);
|
||||
goto err;
|
||||
});
|
||||
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);
|
||||
*user_property = NULL;
|
||||
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)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
if (user_property && item && *item_num) {
|
||||
mqtt5_user_property_item_t user_property_item;
|
||||
uint8_t num = *item_num;
|
||||
STAILQ_FOREACH(user_property_item, user_property, next) {
|
||||
if (i < num) {
|
||||
size_t item_key_len = strlen(user_property_item->key);
|
||||
size_t item_value_len = strlen(user_property_item->value);
|
||||
char *key = calloc(1, item_key_len + 1);
|
||||
ESP_MEM_CHECK(TAG, key, goto err);
|
||||
memcpy(key, user_property_item->key, item_key_len);
|
||||
key[item_key_len] = '\0';
|
||||
char *value = calloc(1, item_value_len + 1);
|
||||
ESP_MEM_CHECK(TAG, value, {
|
||||
free(key);
|
||||
goto err;
|
||||
});
|
||||
memcpy(value, user_property_item->value, item_value_len);
|
||||
value[item_value_len] = '\0';
|
||||
item[i].key = key;
|
||||
item[i].value = value;
|
||||
i ++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*item_num = i;
|
||||
return ESP_OK;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
void esp_mqtt5_client_delete_user_property(mqtt5_user_property_handle_t user_property)
|
||||
{
|
||||
if (user_property) {
|
||||
mqtt5_user_property_item_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, user_property, next, tmp) {
|
||||
STAILQ_REMOVE(user_property, item, mqtt5_user_property, next);
|
||||
free(item->key);
|
||||
free(item->value);
|
||||
free(item);
|
||||
}
|
||||
}
|
||||
free(user_property);
|
||||
}
|
2293
mqtt_client.c
2293
mqtt_client.c
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user