Compare commits

..

38 Commits

Author SHA1 Message Date
08f1f0175d Merge pull request #228 from gabsuren/CI/update_websocket_version
websocket: Updated version to 1.0.0
2023-03-06 12:36:30 +04:00
a2050ac041 Merge pull request #233 from tom-borcin/feature/add_issue_templates
Add issue templates
2023-03-02 17:19:59 +01:00
6015c19c31 Merge pull request #239 from kumekay/feature/add_url_license
Add homepage URL and License to all components (IDFGH-9521)
2023-03-02 17:17:00 +01:00
5df8fdb713 Add input for component version 2023-03-02 15:38:10 +01:00
ef3f0ee688 Add homepage URL and License to all components 2023-03-02 12:35:17 +01:00
996fef7fcf esp_websocket_client: Updated version to 1.0.0
Updated tests to run agains release-v5.0
2023-02-28 19:00:46 +04:00
9f87a8ca77 Add issue templates 2023-02-24 15:53:31 +01:00
3bf0511938 Merge pull request #216 from gabsuren/feature/websocket-errorhandling
esp_websocket_client: Improve error handling (IDFGH-8444)
2023-02-24 07:35:41 +01:00
319fce018e Merge pull request #231 from tore-espressif/fix/esp_modem/on_data_cb
esp_modem: Return true from on_data callback in data mode
2023-02-24 07:34:20 +01:00
44bae24c78 esp_modem: Return true from on_data callback in data mode
The callback should return false only if there is a problem with the received data.
The updated UART Terminal implementation cannot longer clear is callback.
It is DTE's responsibility to clear the callback.
2023-02-23 20:32:13 +01:00
d047ff569c esp_websocket_client:
* Error handling improved to show status code from server
* Added new API `esp_websocket_client_set_headers`
* Dispatches 'WEBSOCKET_EVENT_BEFORE_CONNECT' event before tcp connection
2023-02-22 11:41:33 +04:00
02be2b76f8 Merge pull request #227 from gabsuren/docs/mdns_document_link_fix
docs: fix of mdns link translation
2023-02-20 16:19:30 +04:00
1c850ddacf docs: fix of mdns link translation 2023-02-20 16:11:24 +04:00
9ae88aab48 Merge pull request #223 from AndriiFilippov/master
Unite all tags under common structure (IDFGH-9399)
2023-02-17 15:08:18 +01:00
c6db3ea84c unite all tags under common structure
py test: update tags under common structure
2023-02-17 14:27:34 +01:00
b23eedac3a Merge pull request #189 from euripedesrocha/master
Adds esp_mqtt_cxx component
2023-02-16 06:17:08 -03:00
1407dfc2e7 Adds esp_mqtt_cxx component
- Component moved from esp-idf
- Files moved to adjust to esp-protocol structure
2023-02-16 09:59:46 +01:00
34c6f52f70 build system: re-add -Wno-format as private flag for some example components 2023-02-15 10:26:19 +01:00
c674b8855d [MQTT] - Updates esp_mqtt configuration struct
- Layered config struct
- Fix examples.
2023-02-15 10:26:19 +01:00
256389613f docs: changes docs supported targets tables 2023-02-15 10:26:19 +01:00
2865255ec0 examples, components: remove C/C++ standard overrides
...where they are not necessary after switching to C17 and C++20.
2023-02-15 10:26:19 +01:00
7b2470ddd6 ci: partially enable example build for esp32c2 2023-02-15 10:26:19 +01:00
dda2722563 tools: Increase the minimal supported CMake version to 3.16
This updates the minimal supported version of CMake to 3.16, which in turn enables us to use more CMake features and have a cleaner build system.
This is the version that provides most new features and also the one we use in our latest docker image for CI.
2023-02-15 10:26:19 +01:00
36db37ea2b examples/cxx: add missing header 2023-02-15 10:26:19 +01:00
4f013e3522 Build & config: Remove leftover files from the unsupported "make" build system 2023-02-15 10:26:19 +01:00
231392d0c6 experimental/mqtt_cxx: Adds a C++ Mqtt client wrapper
- Base class with separated event handlers for each Mqtt client event.
- Topic matcher added to support data events.
- Filter class to allow only mqtt valid topic filters.
- Initial code for unit test on the host.
2023-02-15 10:26:19 +01:00
9fb01ca534 Merge pull request #219 from david-cermak/feat/mdns_reverse_query_ip6
feat(mdns): Add support for IPv6 reverse query
2023-02-15 08:07:17 +01:00
d464ed7d30 feat(mdns): Add reverse lookup to the example and test 2023-02-15 07:48:04 +01:00
b94a211e4b Merge pull request #222 from boborjan2/mdns_task_notification
components/mdns: use semaphore instead of task notification bits (IDFGH-9380) (IDFGH-9388)
2023-02-15 07:12:26 +01:00
73f2800b59 components/mdns: use semaphore instead of task notification bits (IDFGH-9380)
Component should not alter the task notification bits of the calling task.
2023-02-14 11:43:02 +01:00
e00e056162 Merge pull request #215 from ESP-YJM/feat/add_auth_for_websocket
websocket: Support HTTP basic authorization
2023-02-10 17:29:31 +04:00
1b134486db websocket: Support HTTP basic authorization 2023-02-09 15:09:40 +08:00
7ba1085e9f Merge pull request #32 from 0xFEEDC0DE64/websocketclient-errormsg
Add websocket client error message string (IDFGH-7591)
2023-02-08 16:11:38 +01:00
3bdb2067e0 Merge pull request #218 from 0xFEEDC0DE64/add_task_name
Add task_name config option (for websocket client) (IDFGH-9346)
2023-02-08 16:10:05 +01:00
1d6888445d Add task_name config option 2023-02-08 15:52:03 +01:00
d68624ec4f Add websocket error messages 2023-02-08 15:35:01 +01:00
d4825f5c53 feat(mdns): Add support for IPv6 reverse query
Implements reverse querries together with
b87bef52e5 (IPv4)

Closes https://github.com/espressif/esp-protocols/issues/101
2023-02-07 20:16:26 +01:00
de1480a072 Merge pull request #217 from david-cermak/mdns/bump_component_version
mdns: bump the component version to 1.0.8
2023-02-06 20:13:17 +01:00
59 changed files with 2337 additions and 73 deletions

51
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@ -0,0 +1,51 @@
name: Installation or build bug report
description: Report installation or build bugs
labels: ['Type: Bug']
body:
- type: checkboxes
id: checklist
attributes:
label: Answers checklist.
description: Before submitting a new issue, please follow the checklist and try to find the answer.
options:
- label: I have read the documentation for [esp-protocols components](https://espressif.github.io/esp-protocols/) and the issue is not addressed there.
required: true
- label: I have updated my esp-protocols branch (master or release) to the latest version and checked that the issue is present there.
required: true
- label: I have searched the issue tracker for a similar issue and not found a similar issue.
required: true
- type: dropdown
id: component
attributes:
label: What component are you using? If you choose Other, provide details in More Information.
multiple: false
options:
- ASIO port
- esp_modem
- esp_websocket_client
- mDNS
- Other
validations:
required: true
- type: input
id: component_id
attributes:
label: component version
description: Which component version does this issue occur on? (see your `idf_component.yml`)
placeholder: ex. 1.0.0
- type: input
id: idf_version
attributes:
label: IDF version.
description: On which IDF version does this issue occur on? Run `git describe --tags` to find it.
placeholder: ex. v3.2-dev-1148-g96cd3b75c
validations:
required: true
- type: textarea
id: more-info
attributes:
label: More Information.
description: Do you have any other information from investigating this?
placeholder: ex. I tried on my friend's Windows 10 PC and the command works there.
validations:
required: false

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: esp-protocols documentation
url: https://espressif.github.io/esp-protocols/
about: Documenation for esp-protocols components.
- name: ESP-IDF Programming Guide
url: https://docs.espressif.com/projects/esp-idf/en/latest/
about: Documentation for configuring and using ESP-IDF.
- name: Espressif documentation page
url: https://www.espressif.com/en/support/download/documents
about: Hardware documentation (datasheets, Technical Reference Manual, etc).
- name: Forum
url: https://esp32.com
about: For questions about using ESP-IDF and/or ESP32 series chips. Please submit all questions starting "How do I..." here.

View File

@ -0,0 +1,33 @@
name: Feature request
description: Suggest an idea or new component for this project.
labels: ['Type: Feature Request']
body:
- type: markdown
attributes:
value: |
* We welcome any ideas or feature requests! Its helpful if you can explain exactly why the feature would be useful.
* There are usually some outstanding feature requests in the [existing issues list](https://github.com/espressif/esp-protocols/labels/Type%3A%20Feature%20Request), feel free to add comments to them.
- type: textarea
id: problem-related
attributes:
label: Is your feature request related to a problem?
description: Please provide a clear and concise description of what the problem is.
placeholder: ex. I'm always frustrated when ...
- type: textarea
id: solution
attributes:
label: Describe the solution you'd like.
description: Please provide a clear and concise description of what you want to happen.
placeholder: ex. When building my application ...
- type: textarea
id: alternatives
attributes:
label: Describe alternatives you've considered.
description: Please provide a clear and concise description of any alternative solutions or features you've considered.
placeholder: ex. Choosing other approach wouldn't work, because ...
- type: textarea
id: context
attributes:
label: Additional context.
description: Please add any other context or screenshots about the feature request here.
placeholder: ex. This would work only when ...

23
.github/ISSUE_TEMPLATE/other-issue.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: General issue report
description: File an issue report
body:
- type: checkboxes
id: checklist
attributes:
label: Answers checklist.
description: Before submitting a new issue, please follow the checklist and try to find the answer.
options:
- label: I have read the documentation for [esp-protocols components](https://espressif.github.io/esp-protocols/) and the issue is not addressed there.
required: true
- label: I have updated my esp-protocols branch (master or release) to the latest version and checked that the issue is present there.
required: true
- label: I have searched the issue tracker for a similar issue and not found a similar issue.
required: true
- type: textarea
id: issue
attributes:
label: General issue report
description: Your issue report goes here.
placeholder: ex. How do I...
validations:
required: true

View File

@ -138,7 +138,7 @@ jobs:
build_websocket:
strategy:
matrix:
idf_ver: ["latest"]
idf_ver: ["release-v5.0", "latest"]
idf_target: ["esp32"]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
@ -179,13 +179,34 @@ jobs:
${{ env.TEST_DIR }}/build/config/sdkconfig.json
if-no-files-found: error
build_esp_mqtt_cxx:
strategy:
matrix:
idf_ver: ["latest", "release-v5.0"]
idf_target: ["esp32"]
test: [ { app: example, path: "components/esp_mqtt_cxx/examples" }]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
shell: bash
working-directory: ${{matrix.test.path}}
run: |
${IDF_PATH}/install.sh --enable-pytest
. ${IDF_PATH}/export.sh
python $IDF_PATH/tools/ci/ci_build_apps.py . --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
run-target-websocket:
name: Run Websocket Example Test on target
needs: build_websocket
strategy:
fail-fast: false
matrix:
idf_ver: ["latest"]
idf_ver: ["release-v5.0", "latest"]
idf_target: ["esp32"]
runs-on:
- self-hosted

23
components/asio/LICENSE Normal file
View File

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,5 +1,6 @@
version: "1.14.1~3"
description: ASIO
url: https://github.com/espressif/esp-protocols/tree/master/components/asio
dependencies:
idf:
version: ">=5.0"

View File

@ -1,4 +1,4 @@
version: "0.1.26"
version: "0.1.27"
description: esp modem
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_modem
dependencies:

View File

@ -87,7 +87,7 @@ void Netif::start()
{
ppp_dte->set_read_cb([this](uint8_t *data, size_t len) -> bool {
receive(data, len);
return false;
return true;
});
esp_netif_action_start(driver.base.netif, nullptr, 0, nullptr);
signal.set(PPP_STARTED);

View File

@ -39,7 +39,7 @@ void Netif::start()
{
ppp_dte->set_read_cb([this](uint8_t *data, size_t len) -> bool {
receive(data, len);
return false;
return true;
});
netif->transmit = esp_modem_dte_transmit;
netif->ctx = (void *)this;

View File

@ -125,9 +125,7 @@ void UartTerminal::task()
case UART_DATA:
uart_get_buffered_data_len(uart.port, &len);
if (len && on_read) {
if (on_read(nullptr, len)) {
on_read = nullptr;
}
on_read(nullptr, len);
}
break;
case UART_FIFO_OVF:

View File

@ -0,0 +1,8 @@
idf_build_get_property(target IDF_TARGET)
idf_component_register(SRCS "esp_mqtt_cxx.cpp"
INCLUDE_DIRS "include"
REQUIRES mqtt
)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,73 @@
# This is Doxygen configuration file
#
# Doxygen provides over 260 configuration statements
# To make this file easier to follow,
# it contains only statements that are non-default
#
# NOTE:
# It is recommended not to change defaults unless specifically required
# Test any changes how they affect generated documentation
# Make sure that correct warnings are generated to flag issues with documented code
#
# For the complete list of configuration statements see:
# http://doxygen.nl/manual/config.html
PROJECT_NAME = "MQTT C++ client"
## The 'INPUT' statement below is used as input by script 'gen-df-input.py'
## to automatically generate API reference list files heder_file.inc
## These files are placed in '_inc' directory
## and used to include in API reference documentation
INPUT = \
$(PROJECT_PATH)/include/esp_mqtt.hpp \
$(PROJECT_PATH)/include/esp_mqtt_client_config.hpp
## Get warnings for functions that have no documentation for their parameters or return value
##
WARN_NO_PARAMDOC = YES
## Enable preprocessing and remove __attribute__(...) expressions from the INPUT files
##
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = \
$(ENV_DOXYGEN_DEFINES) \
__DOXYGEN__=1 \
__attribute__(x)= \
_Static_assert()= \
IDF_DEPRECATED(X)= \
IRAM_ATTR= \
configSUPPORT_DYNAMIC_ALLOCATION=1 \
configSUPPORT_STATIC_ALLOCATION=1 \
configQUEUE_REGISTRY_SIZE=1 \
configUSE_RECURSIVE_MUTEXES=1 \
configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS=1 \
configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \
configUSE_APPLICATION_TASK_TAG=1 \
configTASKLIST_INCLUDE_COREID=1 \
"ESP_EVENT_DECLARE_BASE(x)=extern esp_event_base_t x"
## Do not complain about not having dot
##
HAVE_DOT = NO
## Generate XML that is required for Breathe
##
GENERATE_XML = YES
XML_OUTPUT = xml
GENERATE_HTML = NO
HAVE_DOT = NO
GENERATE_LATEX = NO
GENERATE_MAN = YES
## Skip distracting progress messages
##
QUIET = YES
## Enable Section Tags for conditional documentation
##
ENABLED_SECTIONS += \
DOC_EXCLUDE_HEADER_SECTION ## To conditionally remove doc sections from IDF source files without affecting documentation in upstream files.

View File

@ -0,0 +1,23 @@
# flake8: noqa
from esp_docs.conf_docs import *
extensions += [
'sphinx_copybutton',
# Needed as a trigger for running doxygen
'esp_docs.esp_extensions.dummy_build_system',
'esp_docs.esp_extensions.run_doxygen',
]
# link roles config
github_repo = 'espressif/esp-protocols'
# context used by sphinx_idf_theme
html_context['github_user'] = 'espressif'
html_context['github_repo'] = 'esp-docs'
# Extra options required by sphinx_idf_theme
project_slug = 'esp-idf' # >=5.0
versions_url = 'https://github.com/espressif/esp-protocols/docs/docs_versions.js'
idf_targets = ['esp32']
languages = ['en']

View File

@ -0,0 +1,25 @@
esp_mqtt_client_config.hpp:line: warning: Compound idf::mqtt::BrokerConfiguration is not documented.
esp_mqtt_client_config.hpp:line: warning: Compound idf::mqtt::ClientCredentials is not documented.
esp_mqtt_client_config.hpp:line: warning: Compound idf::mqtt::Configuration is not documented.
esp_mqtt_client_config.hpp:line: warning: Compound idf::mqtt::Connection is not documented.
esp_mqtt_client_config.hpp:line: warning: Compound idf::mqtt::Event is not documented.
esp_mqtt_client_config.hpp:line: warning: Compound idf::mqtt::LastWill is not documented.
esp_mqtt_client_config.hpp:line: warning: Compound idf::mqtt::Session is not documented.
esp_mqtt_client_config.hpp:line: warning: Compound idf::mqtt::Task is not documented.
esp_mqtt_client_config.hpp:line: warning: Member address (variable) of struct idf::mqtt::BrokerConfiguration is not documented.
esp_mqtt_client_config.hpp:line: warning: Member security (variable) of struct idf::mqtt::BrokerConfiguration is not documented.
esp_mqtt.hpp:line: warning: Member operator()(esp_mqtt_client *client_handler) (function) of struct idf::mqtt::Client::MqttClientDeleter is not documented.
esp_mqtt_client_config.hpp:line: warning: Member username (variable) of struct idf::mqtt::ClientCredentials is not documented.
esp_mqtt_client_config.hpp:line: warning: Member authentication (variable) of struct idf::mqtt::ClientCredentials is not documented.
esp_mqtt_client_config.hpp:line: warning: Member client_id (variable) of struct idf::mqtt::ClientCredentials is not documented.
esp_mqtt_client_config.hpp:line: warning: Member event (variable) of struct idf::mqtt::Configuration is not documented.
esp_mqtt_client_config.hpp:line: warning: Member task (variable) of struct idf::mqtt::Configuration is not documented.
esp_mqtt_client_config.hpp:line: warning: Member session (variable) of struct idf::mqtt::Configuration is not documented.
esp_mqtt_client_config.hpp:line: warning: Member connection (variable) of struct idf::mqtt::Configuration is not documented.
esp_mqtt_client_config.hpp:line: warning: Member data (variable) of struct idf::mqtt::DER is not documented.
esp_mqtt_client_config.hpp:line: warning: Member len (variable) of struct idf::mqtt::DER is not documented.
esp_mqtt_client_config.hpp:line: warning: Member ds_data (variable) of struct idf::mqtt::DigitalSignatureData is not documented.
esp_mqtt_client_config.hpp:line: warning: Member data (variable) of struct idf::mqtt::Password is not documented.
esp_mqtt_client_config.hpp:line: warning: Member data (variable) of struct idf::mqtt::PEM is not documented.
esp_mqtt_client_config.hpp:line: warning: Member hint_key (variable) of struct idf::mqtt::PSK is not documented.
esp_mqtt_client_config.hpp:line: warning: Member last_will (variable) of struct idf::mqtt::Session is not documented.

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
#
# English Language RTD & Sphinx config file
#
# Uses ../conf_common.py for most non-language-specific settings.
# Importing conf_common adds all the non-language-specific
# parts to this conf module
try:
from conf_common import * # noqa: F403,F401
except ImportError:
import os
import sys
sys.path.insert(0, os.path.abspath('../'))
from conf_common import * # noqa: F403,F401
# General information about the project.
project = u'ESP-Protocols'
copyright = u'2016 - 2023, Espressif Systems (Shanghai) Co., Ltd'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = 'en'

View File

@ -0,0 +1,132 @@
ESP MQTT C++ client
====================
Overview
--------
The ESP MQTT client is a wrapper over the `esp_mqtt` client with the goal of providing a higher level API.
Features
--------
* Supports MQTT version 3.11
* Adds a Filter validation class for topic filters
* Split the event handlers to member functions
Configuration
-------------
The current design uses exception as an error handling mechanism, therefore exceptions need to be enabled in menuconfig.
Usage
-----
User code needs to inherit fromm :cpp:class:`idf::mqtt::Client` and provide overloads for the event handlers.
.. note:: The handler is available to allow user code to interact directly with it in case of need. This member will likely be made private in the future once the class API stabilizes.
.. doxygenclass:: idf::mqtt::Client
:members:
:protected-members:
Event Handling
--------------
Events are dispatched throug calls to member functions each one dedicated to a type of event.
Application Example
-------------------
* :example:`tcp <../examples/tcp>`
* :example:`ssl <../examples/ssl>`
API Reference
-------------
Header File
^^^^^^^^^^^
* :project_file:`include/esp_mqtt.hpp`
Structures
^^^^^^^^^^
.. doxygenstruct:: idf::mqtt::MQTTException
:members:
.. doxygenstruct:: idf::mqtt::Message
:members:
Classes
^^^^^^^
.. doxygenclass:: idf::mqtt::Filter
:members:
Header File
^^^^^^^^^^^
* :project_file:`include/esp_mqtt_client_config.hpp`
Structures
^^^^^^^^^^
.. doxygenstruct:: idf::mqtt::Host
:members:
.. doxygenstruct:: idf::mqtt::URI
:members:
.. doxygenstruct:: idf::mqtt::BrokerAddress
:members:
.. doxygenstruct:: idf::mqtt::PEM
:members:
.. doxygenstruct:: idf::mqtt::DER
:members:
.. doxygenstruct:: idf::mqtt::Insecure
:members:
.. doxygenstruct:: idf::mqtt::GlobalCAStore
:members:
.. doxygenstruct:: idf::mqtt::PSK
:members:
.. doxygenstruct:: idf::mqtt::Password
:members:
.. doxygenstruct:: idf::mqtt::ClientCertificate
:members:
.. doxygenstruct:: idf::mqtt::SecureElement
:members:
.. doxygenstruct:: idf::mqtt::DigitalSignatureData
:members:
.. doxygenstruct:: idf::mqtt::BrokerConfiguration
:members:
.. doxygenstruct:: idf::mqtt::ClientCredentials
:members:
.. doxygenstruct:: idf::mqtt::Event
:members:
.. doxygenstruct:: idf::mqtt::LastWill
:members:
.. doxygenstruct:: idf::mqtt::Session
:members:
.. doxygenstruct:: idf::mqtt::Task
:members:
.. doxygenstruct:: idf::mqtt::Connection
:members:
.. doxygenstruct:: idf::mqtt::Configuration
:members:

View File

@ -0,0 +1,26 @@
build-docs --target esp32 --language en
cp -rf _build/en/esp32/html .
rm -rf _build __pycache__
# Modifes some version and target fields of index.html
echo "<script type="text/javascript">
window.onload =(function() {
var myAnchor = document.getElementById('version-select');
var mySpan = document.createElement('input');
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'latest';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
var myAnchor = document.getElementById('target-select');
var mySpan = document.createElement('input');
mySpan.setAttribute('type', 'text');
mySpan.setAttribute('maxLength', '10');
mySpan.value = 'all targets';
mySpan.setAttribute('disabled', true);
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
})();
</script>" >> html/index.html

View File

@ -0,0 +1,292 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string>
#include <algorithm>
#include <stdexcept>
#include "mqtt_client.h"
#include "esp_log.h"
#include "esp_mqtt.hpp"
#include "esp_mqtt_client_config.hpp"
namespace {
// Helper for static assert.
template<class T>
constexpr bool always_false = false;
template<class... Ts> struct overloaded : Ts... {
using Ts::operator()...;
};
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
using namespace idf::mqtt;
/*
* This function is responsible for fill in the configurations for the broker related data
* of mqtt_client_config_t
*/
void config_broker(esp_mqtt_client_config_t &mqtt_client_cfg, BrokerConfiguration const &broker)
{
std::visit(overloaded{
[&mqtt_client_cfg](Host const & host)
{
mqtt_client_cfg.broker.address.hostname = host.address.c_str();
mqtt_client_cfg.broker.address.path = host.path.c_str();
mqtt_client_cfg.broker.address.transport = host.transport;
},
[&mqtt_client_cfg](URI const & uri)
{
mqtt_client_cfg.broker.address.uri = uri.address.c_str();
},
[]([[maybe_unused ]]auto & unknown)
{
static_assert(always_false<decltype(unknown)>, "Missing type handler for variant handler");
}
},
broker.address.address);
std::visit(overloaded{
[]([[maybe_unused]]Insecure const & insecure) {},
[&mqtt_client_cfg](GlobalCAStore const & use_global_store)
{
mqtt_client_cfg.broker.verification.use_global_ca_store = true;
},
[&mqtt_client_cfg](CryptographicInformation const & certificates)
{
std::visit(overloaded{
[&mqtt_client_cfg](PEM const & pem)
{
mqtt_client_cfg.broker.verification.certificate = pem.data;
}, [&mqtt_client_cfg](DER const & der)
{
mqtt_client_cfg.broker.verification.certificate = der.data;
mqtt_client_cfg.broker.verification.certificate_len = der.len;
}}, certificates);
},
[]([[maybe_unused]] PSK const & psk) {},
[]([[maybe_unused]] auto & unknown)
{
static_assert(always_false<decltype(unknown)>, "Missing type handler for variant handler");
}
},
broker.security);
mqtt_client_cfg.broker.address.port = broker.address.port;
}
/*
* This function is responsible for fill in the configurations for the client credentials related data
* of mqtt_client_config_t
*/
void config_client_credentials(esp_mqtt_client_config_t &mqtt_client_cfg, ClientCredentials const &credentials)
{
mqtt_client_cfg.credentials.client_id = credentials.client_id.has_value() ? credentials.client_id.value().c_str() : nullptr ;
mqtt_client_cfg.credentials.username = credentials.username.has_value() ? credentials.username.value().c_str() : nullptr ;
std::visit(overloaded{
[&mqtt_client_cfg](Password const & password)
{
mqtt_client_cfg.credentials.authentication.password = password.data.c_str();
},
[](ClientCertificate const & certificate) {},
[](SecureElement const & enable_secure_element) {},
[]([[maybe_unused ]]auto & unknown)
{
static_assert(always_false<decltype(unknown)>, "Missing type handler for variant handler");
}
}, credentials.authentication);
}
esp_mqtt_client_config_t make_config(BrokerConfiguration const &broker, ClientCredentials const &credentials, Configuration const &config)
{
esp_mqtt_client_config_t mqtt_client_cfg{};
config_broker(mqtt_client_cfg, broker);
config_client_credentials(mqtt_client_cfg, credentials);
return mqtt_client_cfg;
}
}
namespace idf::mqtt {
Client::Client(BrokerConfiguration const &broker, ClientCredentials const &credentials, Configuration const &config): Client(make_config(broker, credentials, config)) {}
Client::Client(esp_mqtt_client_config_t const &config) : handler(esp_mqtt_client_init(&config))
{
if (handler == nullptr) {
throw MQTTException(ESP_FAIL);
};
CHECK_THROW_SPECIFIC(esp_mqtt_client_register_event(handler.get(), MQTT_EVENT_ANY, mqtt_event_handler, this), mqtt::MQTTException);
CHECK_THROW_SPECIFIC(esp_mqtt_client_start(handler.get()), mqtt::MQTTException);
}
void Client::mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) noexcept
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
auto *event = static_cast<esp_mqtt_event_t *>(event_data);
auto &client = *static_cast<Client *>(handler_args);
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
client.on_connected(event);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
client.on_disconnected(event);
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
client.on_subscribed(event);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
client.on_unsubscribed(event);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
client.on_published(event);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
client.on_data(event);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
client.on_error(event);
break;
case MQTT_EVENT_BEFORE_CONNECT:
ESP_LOGI(TAG, "MQTT_EVENT_BEFORE_CONNECT");
client.on_before_connect(event);
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}
void Client::on_error(esp_mqtt_event_handle_t const event)
{
auto log_error_if_nonzero = [](const char *message, int error_code) {
if (error_code != 0) {
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
}
};
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
}
}
void Client::on_disconnected(esp_mqtt_event_handle_t const event)
{
}
void Client::on_subscribed(esp_mqtt_event_handle_t const event)
{
printf("Subscribed to %.*s\r\n", event->topic_len, event->topic);
}
void Client::on_unsubscribed(esp_mqtt_event_handle_t const event)
{
}
void Client::on_published(esp_mqtt_event_handle_t const event)
{
}
void Client::on_before_connect(esp_mqtt_event_handle_t const event)
{
}
void Client::on_connected(esp_mqtt_event_handle_t const event)
{
}
void Client::on_data(esp_mqtt_event_handle_t const event)
{
}
std::optional<MessageID> Client::subscribe(std::string const &topic, QoS qos)
{
auto res = esp_mqtt_client_subscribe(handler.get(), topic.c_str(),
static_cast<int>(qos));
if (res < 0) {
return std::nullopt;
}
return MessageID{res};
}
bool is_valid(std::string::const_iterator first, std::string::const_iterator last)
{
if (first == last) {
return false;
}
auto number = std::find(first, last, '#');
if (number != last) {
if (std::next(number) != last) {
return false;
}
if (*std::prev(number) != '/' && number != first) {
return false;
}
}
auto plus = std::find(first, last, '+');
if (plus != last) {
if (*(std::prev(plus)) != '/' && plus != first) {
return false;
}
if (std::next(plus) != last && *(std::next(plus)) != '/') {
return false;
}
}
return true;
}
Filter::Filter(std::string user_filter) : filter(std::move(user_filter))
{
if (!is_valid(filter.begin(), filter.end())) {
throw std::domain_error("Forbidden Filter string");
}
}
[[nodiscard]] bool Filter::match(std::string::const_iterator topic_begin, std::string::const_iterator topic_end) const noexcept
{
auto filter_begin = filter.begin();
auto filter_end = filter.end();
for (auto mismatch = std::mismatch(filter_begin, filter_end, topic_begin);
mismatch.first != filter.end() and mismatch.second != topic_end;
mismatch = std::mismatch(filter_begin, filter_end, topic_begin)) {
if (*mismatch.first != '#' and * mismatch.first != '+') {
return false;
}
if (*mismatch.first == '#') {
return true;
}
if (*mismatch.first == '+') {
filter_begin = advance(mismatch.first, filter_end);
topic_begin = advance(mismatch.second, topic_end);
if (filter_begin == filter_end and topic_begin != topic_end) {
return false;
}
}
}
return true;
}
const std::string &Filter::get()
{
return filter;
}
[[nodiscard]] bool Filter::match(char *const begin, int size) const noexcept
{
auto it = static_cast<std::string::const_iterator>(begin);
return match(it, it + size);
}
std::string::const_iterator Filter::advance(std::string::const_iterator begin, std::string::const_iterator end) const
{
constexpr auto separator = '/';
return std::find(begin, end, separator);
}
}

View File

@ -0,0 +1,14 @@
# The following four lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
# (Not part of the boilerplate) This example uses an extra component for common
# functions such as Wi-Fi and Ethernet connection.
# The path to esp_mqtt_cxx is also added.
set(EXTRA_COMPONENT_DIRS
"../../../../common_components/protocol_examples_common" "../../")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mqtt_ssl_cxx)
target_add_binary_data(mqtt_ssl_cxx.elf "main/mqtt_eclipseprojects_io.pem" TEXT)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "mqtt_ssl_example.cpp"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -0,0 +1,9 @@
menu "Example Configuration"
config BROKER_URI
string "Broker URL"
default "mqtts://mqtt.eclipseprojects.io:8883"
help
URL of the broker to connect to
endmenu

View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
nLRbwHOoq7hHwg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* C++ MQTT (over TCP) Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <cstdint>
#include <string>
#include "esp_mqtt_client_config.hpp"
#include "nvs_flash.h"
#include "protocol_examples_common.h"
#include "esp_log.h"
#include "esp_mqtt.hpp"
namespace {
constexpr auto *TAG = "MQTT_EXAMPLE";
extern const char mqtt_eclipse_org_pem_start[] asm("_binary_mqtt_eclipseprojects_io_pem_start");
extern const char mqtt_eclipse_org_pem_end[] asm("_binary_mqtt_eclipseprojects_io_pem_end");
class MyClient final : public idf::mqtt::Client {
public:
using idf::mqtt::Client::Client;
private:
void on_connected(esp_mqtt_event_handle_t const event) override
{
using idf::mqtt::QoS;
subscribe(messages.get());
subscribe(sent_load.get(), QoS::AtMostOnce);
}
void on_data(esp_mqtt_event_handle_t const event) override
{
if (messages.match(event->topic, event->topic_len)) {
ESP_LOGI(TAG, "Received in the messages topic");
}
}
idf::mqtt::Filter messages{std::string{"$SYS/broker/messages/received"}};
idf::mqtt::Filter sent_load{std::string{"$SYS/broker/load/+/sent"}};
};
}
namespace mqtt = idf::mqtt;
extern "C" void app_main(void)
{
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("MQTT_EXAMPLE", 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);
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
mqtt::BrokerConfiguration broker{
.address = {mqtt::URI{std::string{CONFIG_BROKER_URI}}},
.security = mqtt::CryptographicInformation{mqtt::PEM{mqtt_eclipse_org_pem_start}}
};
idf::mqtt::ClientCredentials credentials{};
idf::mqtt::Configuration config{};
MyClient client{broker, credentials, config};
while (true) {
constexpr TickType_t xDelay = 500 / portTICK_PERIOD_MS;
vTaskDelay( xDelay );
}
}

View File

@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
@pytest.mark.esp32
def test_dummy() -> None:
pass

View File

@ -0,0 +1,21 @@
# Enable C++ exceptions and set emergency pool size for exception objects
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y

View File

@ -0,0 +1,12 @@
# The following four lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
# The path to esp_mqtt_cxx is also added.
set(EXTRA_COMPONENT_DIRS
"../../../../common_components/protocol_examples_common" "../../")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mqtt_tcp_cxx)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "mqtt_tcp_example.cpp"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -0,0 +1,9 @@
menu "Example Configuration"
config BROKER_URL
string "Broker URL"
default "mqtt://mqtt.eclipseprojects.io"
help
URL of the broker to connect to
endmenu

View File

@ -0,0 +1,86 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* C++ MQTT (over TCP) Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "nvs_flash.h"
#include "protocol_examples_common.h"
#include "esp_log.h"
#include "esp_mqtt.hpp"
#include "esp_mqtt_client_config.hpp"
namespace mqtt = idf::mqtt;
namespace {
constexpr auto *TAG = "MQTT_EXAMPLE";
class MyClient final : public mqtt::Client {
public:
using mqtt::Client::Client;
private:
void on_connected(esp_mqtt_event_handle_t const event) override
{
using mqtt::QoS;
subscribe(messages.get());
subscribe(sent_load.get(), QoS::AtMostOnce);
}
void on_data(esp_mqtt_event_handle_t const event) override
{
if (messages.match(event->topic, event->topic_len)) {
ESP_LOGI(TAG, "Received in the messages topic");
}
}
mqtt::Filter messages{"$SYS/broker/messages/received"};
mqtt::Filter sent_load{"$SYS/broker/load/+/sent"};
};
}
extern "C" void app_main(void)
{
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("MQTT_EXAMPLE", 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);
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
mqtt::BrokerConfiguration broker{
.address = {mqtt::URI{std::string{CONFIG_BROKER_URL}}},
.security = mqtt::Insecure{}
};
mqtt::ClientCredentials credentials{};
mqtt::Configuration config{};
MyClient client{broker, credentials, config};
while (true) {
constexpr TickType_t xDelay = 500 / portTICK_PERIOD_MS;
vTaskDelay(xDelay);
}
}

View File

@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
@pytest.mark.esp32
def test_dummy() -> None:
pass

View File

@ -0,0 +1,3 @@
# Enable C++ exceptions and set emergency pool size for exception objects
CONFIG_COMPILER_CXX_EXCEPTIONS=y
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024

View File

@ -0,0 +1,8 @@
version: "0.1.0"
description: esp mqtt cxx
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_mqtt_cxx
dependencies:
espressif/esp-idf-cxx: "^1.0.0-beta"
# Required IDF version
idf:
version: ">=5.0"

View File

@ -0,0 +1,296 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <string_view>
#ifndef __cpp_exceptions
#error MQTT class can only be used when __cpp_exceptions is enabled. Enable CONFIG_COMPILER_CXX_EXCEPTIONS in Kconfig
#endif
#include <optional>
#include <variant>
#include <utility>
#include <memory>
#include <string>
#include "esp_exception.hpp"
#include "esp_mqtt_client_config.hpp"
#include "mqtt_client.h"
namespace idf::mqtt {
constexpr auto *TAG = "mqtt_client_cpp";
struct MQTTException : ESPException {
using ESPException::ESPException;
};
/**
* @brief QoS for publish and subscribe
*
* Sets the QoS as:
* AtMostOnce : Best effort delivery of messages. Message loss can occur.
* AtLeastOnce : Guaranteed delivery of messages. Duplicates can occur.
* ExactlyOnce : Guaranteed delivery of messages exactly once.
*
* @note
* When subscribing to a topic the QoS means the maximum QoS that should be sent to
* client on this topic
*/
enum class QoS { AtMostOnce = 0, AtLeastOnce = 1, ExactlyOnce = 2 };
/**
* @brief Sets if a message must be retained.
*
* Retained messages are delivered to future subscribers that match the topic name.
*
*/
enum class Retain : bool { NotRetained = false, Retained = true };
/**
* @brief Message class template to publish.
*
*/
template <typename T> struct Message {
T data; /*!< Data for publish. Should be a contiguous type*/
QoS qos = QoS::AtLeastOnce; /*!< QoS for the message*/
Retain retain = Retain::NotRetained; /*!< Retention mark for the message.*/
};
/**
* @brief Message type that holds std::string for data
*
*/
using StringMessage = Message<std::string>;
[[nodiscard]] bool filter_is_valid(std::string::const_iterator first, std::string::const_iterator last);
/**
* @brief Filter for mqtt topic subscription.
*
* Topic filter.
*
*/
class Filter {
public:
/**
* @brief Constructs the topic filter from the user filter
* @throws std::domain_error if the filter is invalid.
*
* @param user_filter Filter to be used.
*/
explicit Filter(std::string user_filter);
/**
* @brief Get the filter string used.
*
* @return Reference to the topic filter.
*/
const std::string &get();
/**
* @brief Checks the filter string against a topic name.
*
* @param topic_begin Iterator to the beginning of the sequence.
* @param topic_end Iterator to the end of the sequence.
*
* @return true if the topic name match the filter
*/
[[nodiscard]] bool match(std::string::const_iterator topic_begin, std::string::const_iterator topic_end) const noexcept;
/**
* @brief Checks the filter string against a topic name.
*
* @param topic topic name
*
* @return true if the topic name match the filter
*/
[[nodiscard]] bool match(const std::string &topic) const noexcept;
/**
* @brief Checks the filter string against a topic name.
*
* @param begin Char array with topic name.
* @param size Size of given topic name.
*
* @return true if the topic name match the filter
*/
[[nodiscard]] bool match(char *begin, int size) const noexcept;
private:
/**
* @brief Advance the topic to the next level.
*
* An mqtt topic ends with a /. This function is used to iterate in topic levels.
*
* @return Iterator to the start of the topic.
*/
[[nodiscard]] std::string::const_iterator advance(std::string::const_iterator begin, std::string::const_iterator end) const;
std::string filter;
};
/**
* @brief Message identifier to track delivery.
*
*/
enum class MessageID : int {};
/**
* @brief Base class for MQTT client
*
* Should be inherited to provide event handlers.
*/
class Client {
public:
/**
* @brief Constructor of the client
*
* @param broker Configuration for broker connection
* @param credentials client credentials to be presented to the broker
* @param config Mqtt client configuration
*/
Client(const BrokerConfiguration &broker, const ClientCredentials &credentials, const Configuration &config);
/**
* @brief Constructs Client using the same configuration used for
* `esp_mqtt_client`
* @param config config struct to `esp_mqtt_client`
*/
Client(const esp_mqtt_client_config_t &config);
/**
* @brief Subscribe to topic
*
* @param topic_filter MQTT topic filter
* @param qos QoS subscription, defaulted as QoS::AtLeastOnce
*
* @return Optional MessageID. In case of failure std::nullopt is returned.
*/
std::optional<MessageID> subscribe(const std::string &topic_filter, QoS qos = QoS::AtLeastOnce);
/**
* @brief publish message to topic
*
* @tparam Container Type for data container. Must be a contiguous memory.
* @param topic Topic name
* @param message Message struct containing data, qos and retain
* configuration.
*
* @return Optional MessageID. In case of failure std::nullopt is returned.
*/
template <class Container>
std::optional<MessageID> publish(const std::string &topic, const Message<Container> &message)
{
return publish(topic, std::begin(message.data), std::end(message.data), message.qos, message.retain);
}
/**
* @brief publish message to topic
*
* @tparam InputIt Input data iterator type.
* @param topic Topic name
* @param first, last Iterator pair of data to publish
* @param qos Set qos message
* @param retain Set if message should be retained
*
* @return Optional MessageID. In case of failure std::nullopt is returned.
*/
template <class InputIt>
std::optional<MessageID> publish(const std::string &topic, InputIt first, InputIt last, QoS qos = QoS::AtLeastOnce, Retain retain = Retain::NotRetained)
{
auto size = std::distance(first, last);
auto res = esp_mqtt_client_publish(handler.get(), topic.c_str(), &(*first), size, static_cast<int>(qos),
static_cast<int>(retain));
if (res < 0) {
return std::nullopt;
}
return MessageID{res};
}
virtual ~Client() = default;
protected:
/**
* @brief Helper type to be used as custom deleter for std::unique_ptr.
*/
struct MqttClientDeleter {
void operator()(esp_mqtt_client *client_handler)
{
esp_mqtt_client_destroy(client_handler);
}
};
/**
* @brief Type of the handler for the underlying mqtt_client handler.
* It uses std::unique_ptr for lifetime management
*/
using ClientHandler = std::unique_ptr<esp_mqtt_client, MqttClientDeleter>;
/**
* @brief esp_mqtt_client handler
*
*/
ClientHandler handler;
/**
* @brief Called if there is an error event
*
* @param event mqtt event data
*/
virtual void on_error(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an disconnection event
*
* @param event mqtt event data
*/
virtual void on_disconnected(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an subscribed event
*
* @param event mqtt event data
*/
virtual void on_subscribed(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an unsubscribed event
*
* @param event mqtt event data
*/
virtual void on_unsubscribed(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an published event
*
* @param event mqtt event data
*/
virtual void on_published(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an before connect event
*
* @param event mqtt event data
*/
virtual void on_before_connect(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an connected event
*
* @param event mqtt event data
*
*/
virtual void on_connected(const esp_mqtt_event_handle_t event) = 0;
/**
* @brief Called if there is an data event
*
* @param event mqtt event data
*
*/
virtual void on_data(const esp_mqtt_event_handle_t event) = 0;
private:
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id,
void *event_data) noexcept;
void init(const esp_mqtt_client_config_t &config);
};
} // namespace idf::mqtt

View File

@ -0,0 +1,213 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <cstddef>
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include "mqtt_client.h"
namespace idf::mqtt {
/**
* @brief Broker addresss
*
* Use this to set the broker without parsing the URI string.
*
*/
struct Host {
std::string address; /*!< Host name*/
std::string path; /*!< Route path of the broker in host*/
esp_mqtt_transport_t transport; /*!< Transport scheme to use. */
};
/**
* @brief Broker addresss URI
*
* Use this to set the broker address using the URI.
*
*/
struct URI {
std::string address; /*!< Broker adddress URI*/
};
/**
* @brief Broker addresss.
*
*/
struct BrokerAddress {
std::variant<Host, URI> address; /*!< Address, defined by URI or Host struct */
uint32_t port = 0; /*!< Port used, defaults to 0 to select common port for the scheme used */
};
/**
* @brief PEM formated data
*
* Store certificates, keys and cryptographic data.
*
*/
struct PEM {
const char *data;
};
/**
* @brief DER formated data
*
* Store certificates, keys and cryptographic data.
*
*/
struct DER {
const char *data;
size_t len;
};
/**
* @brief Holds cryptography related information
*
* Hold PEM or DER formated cryptographic data.
*
*/
using CryptographicInformation = std::variant<PEM, DER>;
/**
* @brief Do not verify broker certificate.
*
* To be used when doing MQTT over TLS connection but not verify broker's certificates.
*
*/
struct Insecure {};
/**
* @brief Use global CA store
*
* To be used when client should use the Global CA Store to get trusted certificates for the broker.
*
*/
struct GlobalCAStore {};
/**
* @brief Use a pre shared key for broker authentication.
*
* To be used when client should use a PSK to authenticate the broker.
*
*/
struct PSK {
const struct psk_key_hint *hint_key;/* Pointer to PSK struct defined in esp_tls.h to enable PSK authentication */
};
/**
* @brief Authentication method for Broker
*
* Selects the method for authentication based on the type it holds.
*
*/
using BrokerAuthentication = std::variant<Insecure, GlobalCAStore, CryptographicInformation, PSK>;
/**
* @brief Password related data.
*
*/
struct Password {
std::string data;
};
/**
* @brief Data to authenticate client with certificates.
*
*/
struct ClientCertificate {
CryptographicInformation certificate; /*!< Certificate in PEM or DER format.*/
CryptographicInformation key; /*!< Key data in PEM or DER format.*/
std::optional<Password> key_password = std::nullopt; /*!< Optional password for key */
};
/**
* @brief Used to select usage of Secure Element
*
* Enables the usage of the secure element present in ESP32-WROOM-32SE.
*
*/
struct SecureElement {};
/**
* @brief Used to select usage of Digital Signature Peripheral.
*
* Enables the usage of the Digital Signature hardware accelerator.
*
*/
struct DigitalSignatureData {
void *ds_data; /* carrier of handle for digital signature parameters */
};
using AuthenticationFactor = std::variant<Password, ClientCertificate, SecureElement>;
struct BrokerConfiguration {
BrokerAddress address;
BrokerAuthentication security;
};
struct ClientCredentials {
std::optional<std::string> username; // MQTT username
AuthenticationFactor authentication;
std::vector<std::string> alpn_protos; /*!< List of supported application protocols to be used for ALPN */
/* default is ``ESP32_%CHIPID%`` where %CHIPID% are last 3 bytes of MAC address in hex format */
std::optional<std::string > client_id = std::nullopt;
};
struct Event {
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 */
};
struct LastWill {
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 */
};
struct Session {
LastWill last_will;
int disable_clean_session; /*!< mqtt clean session, default clean_session is true */
int keepalive; /*!< mqtt keepalive, default is 120 seconds */
bool disable_keepalive; /*!< Set disable_keepalive=true to turn off keep-alive mechanism, false by default (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, defaults to value from menuconfig*/
};
struct Task {
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`` */
};
struct Connection {
esp_mqtt_transport_t transport; /*!< overrides URI transport */
int reconnect_timeout_ms; /*!< Reconnect to the broker after this value in miliseconds if auto reconnect is not disabled (defaults to 10s) */
int network_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; /*!< this mqtt client will reconnect to server (when errors/disconnect). Set disable_auto_reconnect=true to disable */
};
struct Configuration {
Event event;
Task task;
Session session;
Connection connection;
void *user_context; /*!< pass user context to this option, then can receive that context in ``event->user_context`` */
int buffer_size; /*!< size of MQTT send/receive buffer, default is 1024 (only receive buffer size if ``out_buffer_size`` defined) */
int out_buffer_size; /*!< size of MQTT output buffer. If not defined, both output and input buffers have the same size defined as ``buffer_size`` */
};
} // idf::mqtt

View File

@ -18,8 +18,9 @@
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_tls_crypto.h"
static const char *TAG = "WEBSOCKET_CLIENT";
static const char *TAG = "websocket_client";
#define WEBSOCKET_TCP_DEFAULT_PORT (80)
#define WEBSOCKET_SSL_DEFAULT_PORT (443)
@ -55,6 +56,7 @@ static const char *TAG = "WEBSOCKET_CLIENT";
#define WS_OVER_TCP_SCHEME "ws"
#define WS_OVER_TLS_SCHEME "wss"
#define WS_HTTP_BASIC_AUTH "Basic "
const static int STOPPED_BIT = BIT0;
const static int CLOSE_FRAME_SENT_BIT = BIT1; // Indicates that a close frame was sent by the client
@ -63,6 +65,7 @@ const static int CLOSE_FRAME_SENT_BIT = BIT1; // Indicates that a close frame
ESP_EVENT_DEFINE_BASE(WEBSOCKET_EVENTS);
typedef struct {
const char *task_name;
int task_stack;
int task_prio;
char *uri;
@ -71,6 +74,7 @@ typedef struct {
char *scheme;
char *username;
char *password;
char *auth;
int port;
bool auto_reconnect;
void *user_context;
@ -103,6 +107,7 @@ typedef enum {
struct esp_websocket_client {
esp_event_loop_handle_t event_handle;
TaskHandle_t task_handle;
esp_websocket_error_codes_t error_handle;
esp_transport_list_handle_t transport_list;
esp_transport_handle_t transport;
websocket_config_storage_t *config;
@ -118,6 +123,8 @@ struct esp_websocket_client {
bool selected_for_destroying;
EventGroupHandle_t status_bits;
SemaphoreHandle_t lock;
size_t errormsg_size;
char *errormsg_buffer;
char *rx_buffer;
char *tx_buffer;
int buffer_size;
@ -190,6 +197,16 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
event_data.payload_len = client->payload_len;
event_data.payload_offset = client->payload_offset;
if (client->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
event_data.error_handle.esp_tls_last_esp_err = esp_tls_get_and_clear_last_error(esp_transport_get_error_handle(client->transport),
&client->error_handle.esp_tls_stack_err,
&client->error_handle.esp_tls_cert_verify_flags);
event_data.error_handle.esp_transport_sock_errno = esp_transport_get_errno(client->transport);
}
event_data.error_handle.error_type = client->error_handle.error_type;
event_data.error_handle.esp_ws_handshake_status_code = client->error_handle.esp_ws_handshake_status_code;
if ((err = esp_event_post_to(client->event_handle,
WEBSOCKET_EVENTS, event,
&event_data,
@ -200,7 +217,7 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
return esp_event_loop_run(client->event_handle, 0);
}
static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client)
static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client, esp_websocket_error_type_t error_type)
{
ESP_WS_CLIENT_STATE_CHECK(TAG, client, return ESP_FAIL);
esp_transport_close(client->transport);
@ -209,25 +226,71 @@ static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_hand
client->reconnect_tick_ms = _tick_get_ms();
ESP_LOGI(TAG, "Reconnect after %d ms", client->wait_timeout_ms);
}
client->error_handle.error_type = error_type;
client->state = WEBSOCKET_STATE_WAIT_TIMEOUT;
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DISCONNECTED, NULL, 0);
return ESP_OK;
}
static esp_err_t esp_websocket_client_error_connection(esp_websocket_client_handle_t client)
static esp_err_t esp_websocket_client_error(esp_websocket_client_handle_t client, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
static esp_err_t esp_websocket_client_error(esp_websocket_client_handle_t client, const char *format, ...)
{
ESP_WS_CLIENT_STATE_CHECK(TAG, client, return ESP_FAIL);
esp_transport_close(client->transport);
va_list myargs;
va_start(myargs, format);
if (client->config->auto_reconnect) {
client->reconnect_tick_ms = _tick_get_ms();
ESP_LOGI(TAG, "Reconnect after %d ms", client->wait_timeout_ms);
size_t needed_size = vsnprintf(NULL, 0, format, myargs);
needed_size++; // null terminator
if (needed_size > client->errormsg_size) {
if (client->errormsg_buffer) {
free(client->errormsg_buffer);
}
client->errormsg_buffer = malloc(needed_size);
if (client->errormsg_buffer == NULL) {
client->errormsg_size = 0;
ESP_LOGE(TAG, "Failed to allocate...");
return ESP_ERR_NO_MEM;
}
client->errormsg_size = needed_size;
}
client->state = WEBSOCKET_STATE_WAIT_TIMEOUT;
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_ERROR, NULL, 0);
needed_size = vsnprintf(client->errormsg_buffer, client->errormsg_size, format, myargs);
va_end(myargs);
ESP_LOGE(TAG, "%s", client->errormsg_buffer);
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_ERROR, client->errormsg_buffer, needed_size);
return ESP_OK;
}
static char *http_auth_basic(const char *username, const char *password)
{
int out;
char *user_info = NULL;
char *digest = NULL;
size_t n = 0;
if (asprintf(&user_info, "%s:%s", username, password) < 0) {
return NULL;
}
if (!user_info) {
ESP_LOGE(TAG, "No enough memory for user information");
return NULL;
}
esp_crypto_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info));
digest = calloc(1, strlen(WS_HTTP_BASIC_AUTH) + n + 1);
if (digest) {
strcpy(digest, WS_HTTP_BASIC_AUTH);
esp_crypto_base64_encode((unsigned char *)digest + 6, n, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info));
}
free(user_info);
return digest;
}
static esp_err_t esp_websocket_client_set_config(esp_websocket_client_handle_t client, const esp_websocket_client_config_t *config)
{
websocket_config_storage_t *cfg = client->config;
@ -236,6 +299,8 @@ static esp_err_t esp_websocket_client_set_config(esp_websocket_client_handle_t c
cfg->task_prio = WEBSOCKET_TASK_PRIORITY;
}
cfg->task_name = config->task_name;
cfg->task_stack = config->task_stack;
if (cfg->task_stack == 0) {
cfg->task_stack = WEBSOCKET_TASK_STACK;
@ -262,6 +327,12 @@ static esp_err_t esp_websocket_client_set_config(esp_websocket_client_handle_t c
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->password, return ESP_ERR_NO_MEM);
}
if (cfg->username && cfg->password) {
free(cfg->auth);
cfg->auth = http_auth_basic(cfg->username, cfg->password);
ESP_WS_CLIENT_MEM_CHECK(TAG, cfg->auth, return ESP_ERR_NO_MEM);
}
if (config->uri) {
free(cfg->uri);
cfg->uri = strdup(config->uri);
@ -334,6 +405,7 @@ static esp_err_t esp_websocket_client_destroy_config(esp_websocket_client_handle
free(cfg->scheme);
free(cfg->username);
free(cfg->password);
free(cfg->auth);
free(cfg->subprotocol);
free(cfg->user_agent);
free(cfg->headers);
@ -356,6 +428,7 @@ static void destroy_and_free_resources(esp_websocket_client_handle_t client)
vQueueDelete(client->lock);
free(client->tx_buffer);
free(client->rx_buffer);
free(client->errormsg_buffer);
if (client->status_bits) {
vEventGroupDelete(client->status_bits);
}
@ -372,6 +445,7 @@ static esp_err_t set_websocket_transport_optional_settings(esp_websocket_client_
.sub_protocol = client->config->subprotocol,
.user_agent = client->config->user_agent,
.headers = client->config->headers,
.auth = client->config->auth,
.propagate_control_frames = true
};
return esp_transport_ws_set_config(trans, &config);
@ -554,6 +628,8 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
if (buffer_size <= 0) {
buffer_size = WEBSOCKET_BUFFER_SIZE_BYTE;
}
client->errormsg_buffer = NULL;
client->errormsg_size = 0;
#ifndef CONFIG_ESP_WS_CLIENT_ENABLE_DYNAMIC_BUFFER
client->rx_buffer = malloc(buffer_size);
ESP_WS_CLIENT_MEM_CHECK(TAG, client->rx_buffer, {
@ -662,6 +738,19 @@ esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, con
return ESP_OK;
}
esp_err_t esp_websocket_client_set_headers(esp_websocket_client_handle_t client, const char *headers)
{
if (client == NULL || client->state != WEBSOCKET_STATE_CONNECTED || headers == NULL) {
return ESP_ERR_INVALID_ARG;
}
xSemaphoreTakeRecursive(client->lock, portMAX_DELAY);
esp_err_t ret = esp_transport_ws_set_headers(client->transport, headers);
xSemaphoreGiveRecursive(client->lock);
return ret;
}
static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
{
int rlen;
@ -673,8 +762,15 @@ static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
do {
rlen = esp_transport_read(client->transport, client->rx_buffer, client->buffer_size, client->config->network_timeout_ms);
if (rlen < 0) {
ESP_LOGE(TAG, "Error read data");
esp_websocket_free_buf(client, false);
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
if (error_handle) {
esp_websocket_client_error(client, "esp_transport_read() failed with %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
rlen, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
} else {
esp_websocket_client_error(client, "esp_transport_read() failed with %d, errno=%d", rlen, errno);
}
return ESP_FAIL;
}
client->payload_len = esp_transport_ws_get_read_payload_len(client->transport);
@ -743,21 +839,32 @@ static void esp_websocket_client_task(void *pv)
client->run = false;
break;
}
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_BEFORE_CONNECT, NULL, 0);
int result = esp_transport_connect(client->transport,
client->config->host,
client->config->port,
client->config->network_timeout_ms);
if (result < 0) {
ESP_LOGE(TAG, "Error transport connect %i", result);
esp_websocket_client_error_connection(client);
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
client->error_handle.esp_ws_handshake_status_code = esp_transport_ws_get_upgrade_request_status(client->transport);
if (error_handle) {
esp_websocket_client_error(client, "esp_transport_connect() failed with %d,\
transport_error=%s, tls_error_code=%i, tls_flags=%i, esp_ws_handshake_status_code=%d, errno=%d",
result, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, client->error_handle.esp_ws_handshake_status_code, errno);
} else {
esp_websocket_client_error(client, "esp_transport_connect() failed with %d, esp_ws_handshake_status_code=%d, errno=%d",
result, client->error_handle.esp_ws_handshake_status_code, errno);
}
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
break;
}
ESP_LOGD(TAG, "Transport connected to %s://%s:%d", client->config->scheme, client->config->host, client->config->port);
client->state = WEBSOCKET_STATE_CONNECTED;
client->wait_for_pong_resp = false;
client->error_handle.error_type = WEBSOCKET_ERROR_TYPE_NONE;
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_CONNECTED, NULL, 0);
break;
case WEBSOCKET_STATE_CONNECTED:
if ((CLOSE_FRAME_SENT_BIT & xEventGroupGetBits(client->status_bits)) == 0) { // only send and check for PING
@ -775,13 +882,14 @@ static void esp_websocket_client_task(void *pv)
if ( _tick_get_ms() - client->pingpong_tick_ms > client->config->pingpong_timeout_sec * 1000 ) {
if (client->wait_for_pong_resp) {
ESP_LOGE(TAG, "Error, no PONG received for more than %d seconds after PING", client->config->pingpong_timeout_sec);
esp_websocket_client_abort_connection(client);
esp_websocket_client_error(client, "Error, no PONG received for more than %d seconds after PING", client->config->pingpong_timeout_sec);
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_PONG_TIMEOUT);
break;
}
}
}
if (read_select == 0) {
ESP_LOGV(TAG, "Read poll timeout: skipping esp_transport_read()...");
break;
@ -790,7 +898,7 @@ static void esp_websocket_client_task(void *pv)
if (esp_websocket_client_recv(client) == ESP_FAIL) {
ESP_LOGE(TAG, "Error receive data");
esp_websocket_client_abort_connection(client);
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
break;
}
break;
@ -822,8 +930,15 @@ static void esp_websocket_client_task(void *pv)
if (WEBSOCKET_STATE_CONNECTED == client->state) {
read_select = esp_transport_poll_read(client->transport, 1000); //Poll every 1000ms
if (read_select < 0) {
ESP_LOGE(TAG, "Network error: esp_transport_poll_read() returned %d, errno=%d", read_select, errno);
esp_websocket_client_abort_connection(client);
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
if (error_handle) {
esp_websocket_client_error(client, "esp_transport_poll_read() returned %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
read_select, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
} else {
esp_websocket_client_error(client, "esp_transport_poll_read() returned %d, errno=%d", read_select, errno);
}
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
}
} else if (WEBSOCKET_STATE_WAIT_TIMEOUT == client->state) {
// waiting for reconnecting...
@ -869,7 +984,8 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client)
return ESP_FAIL;
}
if (xTaskCreate(esp_websocket_client_task, "websocket_task", client->config->task_stack, client, client->config->task_prio, &client->task_handle) != pdTRUE) {
if (xTaskCreate(esp_websocket_client_task, client->config->task_name ? client->config->task_name : "websocket_task",
client->config->task_stack, client, client->config->task_prio, &client->task_handle) != pdTRUE) {
ESP_LOGE(TAG, "Error create websocket task");
return ESP_FAIL;
}
@ -1019,9 +1135,16 @@ int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client,
(timeout == portMAX_DELAY) ? -1 : timeout * portTICK_PERIOD_MS);
if (wlen < 0 || (wlen == 0 && need_write != 0)) {
ret = wlen;
ESP_LOGE(TAG, "Network error: esp_transport_write() returned %d, errno=%d", ret, errno);
esp_websocket_free_buf(client, true);
esp_websocket_client_abort_connection(client);
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
if (error_handle) {
esp_websocket_client_error(client, "esp_transport_write() returned %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
ret, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
error_handle->esp_tls_flags, errno);
} else {
esp_websocket_client_error(client, "esp_transport_write() returned %d, errno=%d", ret, errno);
}
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
goto unlock_and_return;
}
current_opcode = 0;

View File

@ -2,7 +2,7 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../..")
set(EXTRA_COMPONENT_DIRS "..")
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
list(APPEND EXTRA_COMPONENT_DIRS "../../../common_components/protocol_examples_common")

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -32,11 +32,18 @@
#define NO_DATA_TIMEOUT_SEC 5
static const char *TAG = "WEBSOCKET";
static const char *TAG = "websocket";
static TimerHandle_t shutdown_signal_timer;
static SemaphoreHandle_t shutdown_sema;
static void log_error_if_nonzero(const char *message, int error_code)
{
if (error_code != 0) {
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
}
}
static void shutdown_signaler(TimerHandle_t xTimer)
{
ESP_LOGI(TAG, "No data received for %d seconds, signaling shutdown", NO_DATA_TIMEOUT_SEC);
@ -71,6 +78,12 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED");
log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code);
if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno);
}
break;
case WEBSOCKET_EVENT_DATA:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA");
@ -99,6 +112,12 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
break;
case WEBSOCKET_EVENT_ERROR:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR");
log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code);
if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno);
}
break;
}
}
@ -154,9 +173,9 @@ void app_main(void)
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("WEBSOCKET_CLIENT", ESP_LOG_DEBUG);
esp_log_level_set("TRANSPORT_WS", ESP_LOG_DEBUG);
esp_log_level_set("TRANS_TCP", ESP_LOG_DEBUG);
esp_log_level_set("websocket_client", ESP_LOG_DEBUG);
esp_log_level_set("transport_ws", ESP_LOG_DEBUG);
esp_log_level_set("trans_tcp", ESP_LOG_DEBUG);
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import json
import random
@ -81,7 +81,7 @@ def test_examples_protocol_websocket(dut):
def test_close(dut):
code = dut.expect(
re.compile(
b'WEBSOCKET: Received closed message with code=(\\d*)'))[0]
b'websocket: Received closed message with code=(\\d*)'))[0]
print('Received close frame with code {}'.format(code))
def test_json(dut, websocket):

View File

@ -1,5 +1,6 @@
version: "0.0.4"
version: "1.0.0"
description: esp websocket client
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_websocket_client
dependencies:
idf:
version: ">=5.0"

View File

@ -35,9 +35,35 @@ typedef enum {
WEBSOCKET_EVENT_DISCONNECTED, /*!< The connection has been disconnected */
WEBSOCKET_EVENT_DATA, /*!< When receiving data from the server, possibly multiple portions of the packet */
WEBSOCKET_EVENT_CLOSED, /*!< The connection has been closed cleanly */
WEBSOCKET_EVENT_BEFORE_CONNECT, /*!< The event occurs before connecting */
WEBSOCKET_EVENT_MAX
} esp_websocket_event_id_t;
/**
* @brief Websocket connection error codes propagated via ERROR event
*/
typedef enum {
WEBSOCKET_ERROR_TYPE_NONE = 0,
WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT,
WEBSOCKET_ERROR_TYPE_PONG_TIMEOUT,
WEBSOCKET_ERROR_TYPE_HANDSHAKE
} esp_websocket_error_type_t;
/**
* @brief Websocket error code structure to be passed as a contextual information into ERROR event
*/
typedef struct {
/* 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-websocket specific structure extension */
esp_websocket_error_type_t error_type;
int esp_ws_handshake_status_code; /*!< http status code of the websocket upgrade handshake */
/* tcp_transport extension */
int esp_transport_sock_errno; /*!< errno from the underlying socket */
} esp_websocket_error_codes_t;
/**
* @brief Websocket event data
*/
@ -50,6 +76,7 @@ typedef struct {
void *user_context; /*!< user_data context, from esp_websocket_client_config_t user_data */
int payload_len; /*!< Total payload length, payloads exceeding buffer will be posted through multiple events */
int payload_offset; /*!< Actual offset for the data associated with this event */
esp_websocket_error_codes_t error_handle; /*!< esp-websocket error handle including esp-tls errors as well as internal websocket errors */
} esp_websocket_event_data_t;
/**
@ -68,12 +95,13 @@ typedef struct {
const char *uri; /*!< Websocket URI, the information on the URI can be overrides the other fields below, if any */
const char *host; /*!< Domain or IP as string */
int port; /*!< Port to connect, default depend on esp_websocket_transport_t (80 or 443) */
const char *username; /*!< Using for Http authentication - Not supported for now */
const char *password; /*!< Using for Http authentication - Not supported for now */
const char *username; /*!< Using for Http authentication, only support basic auth now */
const char *password; /*!< Using for Http authentication */
const char *path; /*!< HTTP Path, if not set, default is `/` */
bool disable_auto_reconnect; /*!< Disable the automatic reconnect function when disconnected */
void *user_context; /*!< HTTP user data context */
int task_prio; /*!< Websocket task priority */
const char *task_name; /*!< Websocket task name */
int task_stack; /*!< Websocket task stack */
int buffer_size; /*!< Websocket buffer size */
const char *cert_pem; /*!< Pointer to certificate data in PEM or DER format for server verify (with SSL), default is NULL, not required to verify the server. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in cert_len. */
@ -126,6 +154,17 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
*/
esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, const char *uri);
/**
* @brief Set additional websocket headers for the client, when performing this behavior, the headers will replace the old ones
* @pre Must stop the WebSocket client before set headers if the client has been connected
*
* @param[in] client The client
* @param headers additional header strings each terminated with \r\n
*
* @return esp_err_t
*/
esp_err_t esp_websocket_client_set_headers(esp_websocket_client_handle_t client, const char *headers);
/**
* @brief Open the WebSocket connection
*

202
components/mdns/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,6 +1,6 @@
mDNS Service
============
:link_to_translation:`zh_CN:[中文]`
`zh_CN:[中文] <https://espressif.github.io/esp-protocols/mdns/zh_CN/index.html>`_
Overview
--------

View File

@ -1,6 +1,6 @@
mDNS 服务
=========
:link_to_translation:`en:[English]`
`en:[English] <https://espressif.github.io/esp-protocols/mdns/en/index.html>`_
概述
----

View File

@ -276,16 +276,15 @@ void app_main(void)
* This is typically performed in "GOT_IP" event handler, but we call it here directly
* since the `EXAMPLE_INTERFACE` netif is connected already, to keep the example simple.
*/
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ENABLE_IP4));
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ANNOUNCE_IP4));
#endif
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ENABLE_IP4 | MDNS_EVENT_ENABLE_IP6));
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ANNOUNCE_IP4 | MDNS_EVENT_ANNOUNCE_IP6));
#if defined(CONFIG_MDNS_RESPOND_REVERSE_QUERIES)
while (mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_IP4_REVERSE_LOOKUP) != ESP_OK) {
vTaskDelay(50 / portTICK_PERIOD_MS);
}
ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_IP4_REVERSE_LOOKUP | MDNS_EVENT_IP6_REVERSE_LOOKUP));
#endif
#endif // CONFIG_MDNS_ADD_CUSTOM_NETIF
initialise_button();
xTaskCreate(&mdns_example_task, "mdns_example_task", 2048, NULL, 5, NULL);
}

View File

@ -120,10 +120,11 @@ def test_examples_protocol_mdns(dut):
2. get the dut host name (and IP address)
3. check the mdns name is accessible
4. check DUT output if mdns advertized host is resolved
5. check if DUT responds to dig
6. check the DUT is searchable via reverse IP lookup
"""
specific_host = dut.expect(re.compile(
b'mdns hostname set to: \[(.*?)\]')).group(1).decode() # noqa: W605
specific_host = dut.expect(r'mdns hostname set to: \[(.*?)\]')[1].decode()
mdns_server_events = {
'stop': Event(),
@ -132,9 +133,14 @@ def test_examples_protocol_mdns(dut):
}
mdns_responder = Thread(target=mdns_server,
args=(str(specific_host), mdns_server_events))
ip_address = dut.expect(
re.compile(b'IPv4 address:([a-zA-Z0-9]*).*')).group(1).decode()
print('Connected to AP with IP: {}'.format(ip_address))
ipv4 = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]',
timeout=30)[1].decode()
ip_addresses = [ipv4]
if dut.app.sdkconfig.get('LWIP_IPV6') is True:
ipv6_r = r':'.join((r'[0-9a-fA-F]{4}', ) * 8)
ipv6 = dut.expect(ipv6_r, timeout=30)[0].decode()
ip_addresses.append(ipv6)
print('Connected with IP addresses: {}'.format(','.join(ip_addresses)))
try:
# 3. check the mdns name is accessible.
mdns_responder.start()
@ -165,11 +171,25 @@ def test_examples_protocol_mdns(dut):
])
print('Resolving {} using "dig" succeeded with:\n{}'.format(
specific_host, dig_output))
if not ip_address.encode('utf-8') in dig_output:
if not ipv4.encode('utf-8') in dig_output:
raise ValueError(
'Test has failed: Incorrectly resolved DUT hostname using dig'
"Output should've contained DUT's IP address:{}".format(
ip_address))
"Output should've contained DUT's IP address:{}".format(ipv4))
# 6. check the DUT reverse lookup
if dut.app.sdkconfig.get('MDNS_RESPOND_REVERSE_QUERIES') is True:
for ip_address in ip_addresses:
dig_output = subprocess.check_output([
'dig', '+short', '-p', '5353', '@224.0.0.251', '-x',
'{}'.format(ip_address)
])
print('Reverse lookup for {} using "dig" succeeded with:\n{}'.
format(ip_address, dig_output))
if specific_host not in dig_output.decode():
raise ValueError(
'Test has failed: Incorrectly resolved DUT IP address using dig'
"Output should've contained DUT's name:{}".format(
specific_host))
finally:
mdns_server_events['stop'].set()
mdns_responder.join()

View File

@ -6,6 +6,7 @@ CONFIG_MDNS_PREDEF_NETIF_STA=n
CONFIG_MDNS_PREDEF_NETIF_AP=n
CONFIG_MDNS_PREDEF_NETIF_ETH=n
CONFIG_MDNS_ADD_CUSTOM_NETIF=y
CONFIG_MDNS_RESPOND_REVERSE_QUERIES=y
CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n

View File

@ -1,5 +1,6 @@
version: "1.0.8"
description: mDNS
url: https://github.com/espressif/esp-protocols/tree/master/components/mdns
dependencies:
idf:
version: ">=5.0"

View File

@ -45,7 +45,7 @@ mdns_server_t *_mdns_server = NULL;
static mdns_host_item_t *_mdns_host_list = NULL;
static mdns_host_item_t _mdns_self_host;
static const char *TAG = "MDNS";
static const char *TAG = "mdns";
static volatile TaskHandle_t _mdns_service_task_handle = NULL;
static SemaphoreHandle_t _mdns_service_semaphore = NULL;
@ -437,8 +437,8 @@ static const uint8_t *_mdns_read_fqdn(const uint8_t *packet, const uint8_t *star
if (name->parts == 1 && buf[0] != '_'
&& (strcasecmp(buf, MDNS_DEFAULT_DOMAIN) != 0)
&& (strcasecmp(buf, "arpa") != 0)
&& (strcasecmp(buf, "ip6") != 0)
#ifndef CONFIG_MDNS_RESPOND_REVERSE_QUERIES
&& (strcasecmp(buf, "ip6") != 0)
&& (strcasecmp(buf, "in-addr") != 0)
#endif
) {
@ -1162,7 +1162,7 @@ static uint16_t _mdns_append_question(uint8_t *packet, uint16_t *index, mdns_out
{
uint8_t part_length;
#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES
if (q->host && strstr(q->host, "in-addr")) {
if (q->host && (strstr(q->host, "in-addr") || strstr(q->host, "ip6"))) {
part_length = append_fqdn_dots(packet, index, q->host, false);
if (!part_length) {
return 0;
@ -1275,7 +1275,7 @@ static uint8_t _mdns_append_host_answer(uint8_t *packet, uint16_t *index, mdns_h
*/
static uint8_t _mdns_append_reverse_ptr_record(uint8_t *packet, uint16_t *index, const char *name)
{
if (strstr(name, "in-addr") == NULL) {
if (strstr(name, "in-addr") == NULL && strstr(name, "ip6") == NULL) {
return 0;
}
@ -1339,7 +1339,8 @@ static uint8_t _mdns_append_answer(uint8_t *packet, uint16_t *index, mdns_out_an
if (answer->service) {
return _mdns_append_service_ptr_answers(packet, index, answer->service, answer->flush, answer->bye);
#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES
} else if (answer->host && answer->host->hostname && strstr(answer->host->hostname, "in-addr")) {
} else if (answer->host && answer->host->hostname &&
(strstr(answer->host->hostname, "in-addr") || strstr(answer->host->hostname, "ip6"))) {
return _mdns_append_reverse_ptr_record(packet, index, answer->host->hostname) > 0;
#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */
} else {
@ -3966,6 +3967,13 @@ void _mdns_disable_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].state = PCB_OFF;
}
#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES
static inline char nibble_to_hex(int var)
{
return var > 9 ? var - 10 + 'a' : var + '0';
}
#endif
/**
* @brief Performs interface changes based on system events or custom commands
*/
@ -4007,6 +4015,32 @@ static void perform_event_action(mdns_if_t mdns_if, mdns_event_actions_t action)
}
}
}
#ifdef CONFIG_LWIP_IPV6
if (action & MDNS_EVENT_IP6_REVERSE_LOOKUP) {
esp_ip6_addr_t addr6;
if (!esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(mdns_if), &addr6) && !_ipv6_address_is_zero(addr6)) {
uint8_t *paddr = (uint8_t *)&addr6.addr;
const char sub[] = "ip6";
const size_t query_name_size = 4 * sizeof(addr6.addr) /* (2 nibbles + 2 dots)/per byte of IP address */ + sizeof(sub);
char *reverse_query_name = malloc(query_name_size);
if (reverse_query_name) {
char *ptr = &reverse_query_name[query_name_size]; // point to the end
memcpy(ptr - sizeof(sub), sub, sizeof(sub)); // copy the IP sub-domain
ptr -= sizeof(sub) + 1; // move before the sub-domain
while (reverse_query_name < ptr) { // continue populating reverse query from the end
*ptr-- = '.'; // nibble by nibble, until we reach the beginning
*ptr-- = nibble_to_hex(((*paddr) >> 4) & 0x0F);
*ptr-- = '.';
*ptr-- = nibble_to_hex((*paddr) & 0x0F);
paddr++;
}
ESP_LOGD(TAG, "Registered reverse query: %s.arpa", reverse_query_name);
_mdns_delegate_hostname_add(reverse_query_name, NULL);
}
}
}
#endif /* CONFIG_LWIP_IPV6 */
#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */
}
@ -4783,7 +4817,7 @@ static void _mdns_execute_action(mdns_action_t *action)
_mdns_server->hostname = action->data.hostname_set.hostname;
_mdns_self_host.hostname = action->data.hostname_set.hostname;
_mdns_restart_all_pcbs();
xTaskNotifyGive(action->data.hostname_set.calling_task);
xSemaphoreGive(_mdns_server->action_sema);
break;
case ACTION_INSTANCE_SET:
_mdns_send_bye_all_pcbs_no_instance(false);
@ -5326,6 +5360,12 @@ esp_err_t mdns_init(void)
goto free_lock;
}
_mdns_server->action_sema = xSemaphoreCreateBinary();
if (!_mdns_server->action_sema) {
err = ESP_ERR_NO_MEM;
goto free_queue;
}
#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP
if ((err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, mdns_preset_if_handle_system_event, NULL)) != ESP_OK) {
goto free_event_handlers;
@ -5381,6 +5421,8 @@ free_all_and_disable_pcbs:
free_event_handlers:
unregister_predefined_handlers();
#endif
vSemaphoreDelete(_mdns_server->action_sema);
free_queue:
vQueueDelete(_mdns_server->action_queue);
free_lock:
vSemaphoreDelete(_mdns_server->lock);
@ -5430,6 +5472,7 @@ void mdns_free(void)
}
free(h);
}
vSemaphoreDelete(_mdns_server->action_sema);
vSemaphoreDelete(_mdns_server->lock);
free(_mdns_server);
_mdns_server = NULL;
@ -5456,13 +5499,12 @@ esp_err_t mdns_hostname_set(const char *hostname)
}
action->type = ACTION_HOSTNAME_SET;
action->data.hostname_set.hostname = new_hostname;
action->data.hostname_set.calling_task = xTaskGetCurrentTaskHandle();
if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) {
free(new_hostname);
free(action);
return ESP_ERR_NO_MEM;
}
xTaskNotifyWait(0, 0x01, NULL, portMAX_DELAY);
xSemaphoreTake(_mdns_server->action_sema, portMAX_DELAY);
return ESP_OK;
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -27,7 +27,7 @@ extern mdns_server_t *_mdns_server;
* MDNS Server Networking
*
*/
static const char *TAG = "MDNS_Networking";
static const char *TAG = "mdns_networking";
static struct udp_pcb *_pcb_main = NULL;

View File

@ -29,7 +29,7 @@
extern mdns_server_t *_mdns_server;
static const char *TAG = "MDNS_Networking";
static const char *TAG = "mdns_networking";
static bool s_run_sock_recv_task = false;
static int create_socket(esp_netif_t *netif);
static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol);

View File

@ -89,7 +89,11 @@
#define MDNS_PACKET_QUEUE_LEN 16 // Maximum packets that can be queued for parsing
#define MDNS_ACTION_QUEUE_LEN 16 // Maximum actions pending to the server
#define MDNS_TXT_MAX_LEN 1024 // Maximum string length of text data in TXT record
#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_MDNS_RESPOND_REVERSE_QUERIES)
#define MDNS_NAME_MAX_LEN (64+4) // Need to account for IPv6 reverse queries (64 char address + ".ip6" )
#else
#define MDNS_NAME_MAX_LEN 64 // Maximum string length of hostname, instance, service and proto
#endif
#define MDNS_NAME_BUF_LEN (MDNS_NAME_MAX_LEN+1) // Maximum char buffer size to hold hostname, instance, service or proto
#define MDNS_MAX_PACKET_SIZE 1460 // Maximum size of mDNS outgoing packet
@ -380,6 +384,7 @@ typedef struct mdns_server_s {
mdns_srv_item_t *services;
SemaphoreHandle_t lock;
QueueHandle_t action_queue;
SemaphoreHandle_t action_sema;
mdns_tx_packet_t *tx_queue_head;
mdns_search_once_t *search_once;
esp_timer_handle_t timer_handle;
@ -390,7 +395,6 @@ typedef struct {
union {
struct {
char *hostname;
TaskHandle_t calling_task;
} hostname_set;
char *instance;
struct {

View File

@ -42,7 +42,7 @@ int main(int argc, char *argv[])
ESP_ERROR_CHECK(mdns_hostname_set(CONFIG_TEST_HOSTNAME));
ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_TEST_HOSTNAME);
ESP_ERROR_CHECK(mdns_register_netif(sta));
ESP_ERROR_CHECK(mdns_netif_action(sta, MDNS_EVENT_ENABLE_IP4 | MDNS_EVENT_IP4_REVERSE_LOOKUP));
ESP_ERROR_CHECK(mdns_netif_action(sta, MDNS_EVENT_ENABLE_IP4 | MDNS_EVENT_IP4_REVERSE_LOOKUP | MDNS_EVENT_IP6_REVERSE_LOOKUP));
#ifdef REGISTER_SERVICE
//set default mDNS instance name

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -13,7 +13,7 @@
#include "protocol_examples_common.h"
#include "mdns.h"
static const char *TAG = "MDNS_TEST";
static const char *TAG = "mdns_test";
void mdns_test(char *line);
static void get_string(char *line, size_t size)

View File

@ -10,7 +10,7 @@
#include "esp_log.h"
#include "esp_netif.h"
static const char *TAG = "MDNS_TEST_APP";
static const char *TAG = "mdns_test_app";
static const int RETRY_COUNT = 10;
static void mdns_print_results(mdns_result_t *results)