initial commmit
This commit is contained in:
1
managed_components/esp_mqtt/.component_hash
Normal file
1
managed_components/esp_mqtt/.component_hash
Normal file
@@ -0,0 +1 @@
|
||||
2fb8e302a94e05fa40af4788fa45414369db53b2a8ccfaa7d07797192894c708
|
||||
34
managed_components/esp_mqtt/.editorconfig
Normal file
34
managed_components/esp_mqtt/.editorconfig
Normal file
@@ -0,0 +1,34 @@
|
||||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[{*.md,*.rst}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{Makefile,*.mk,*.bat}]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
|
||||
[*/freertos/**]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[{*/freertos/**.S,**/FreeRTOSConfig.h}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.pem]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.py]
|
||||
max_line_length = 119
|
||||
36
managed_components/esp_mqtt/.gitignore
vendored
Normal file
36
managed_components/esp_mqtt/.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
build
|
||||
examples/**/build
|
||||
examples/**/sdkconfig*
|
||||
65
managed_components/esp_mqtt/.travis.yml
Normal file
65
managed_components/esp_mqtt/.travis.yml
Normal file
@@ -0,0 +1,65 @@
|
||||
sudo: false
|
||||
language: bash
|
||||
os:
|
||||
- linux
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gperf
|
||||
- python
|
||||
- python-serial
|
||||
|
||||
before_install:
|
||||
# Save path to the git respository
|
||||
- PROJECT_PATH=$(pwd)
|
||||
# Have to checkout a temp branch for later in tree reference
|
||||
- git checkout -b temporary_ref_branch
|
||||
- CI_COMMIT_SHA=$(git rev-parse HEAD)
|
||||
# Test building with latest (stable == v3.3 for now) IDF
|
||||
- LTS_IDF=release/v3.3
|
||||
|
||||
install:
|
||||
# Install ESP32 toochain following steps as desribed
|
||||
# in http://esp-idf.readthedocs.io/en/latest/linux-setup.html
|
||||
#
|
||||
# Get required packages - already done above, see addons: apt: packages:
|
||||
# - sudo apt-get install git wget make libncurses-dev flex bison gperf python python-serial
|
||||
# Prepare directory for the toolchain
|
||||
- mkdir -p ~/esp
|
||||
- cd ~/esp
|
||||
# Download binary toolchain for the ESP32
|
||||
- wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
|
||||
- tar -xzf xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
|
||||
# Get ESP-IDF from github (non-recursive to save time, later we update submodules for different versions)
|
||||
- git clone https://github.com/espressif/esp-idf.git
|
||||
# Set the path to ESP-IDF directory
|
||||
- export IDF_PATH=~/esp/esp-idf
|
||||
- python -m pip install --user -r $IDF_PATH/requirements.txt
|
||||
# Setup build tool: xtensa-esp32-elf and idf.py
|
||||
- export PATH=$PATH:$HOME/esp/xtensa-esp32-elf/bin:$IDF_PATH/tools
|
||||
|
||||
script:
|
||||
# Legacy build with IDF < 3.2
|
||||
- cd $IDF_PATH
|
||||
- git checkout v3.1 && git submodule update --init --recursive
|
||||
- cd $PROJECT_PATH
|
||||
- ./ci/modify_for_legacy_idf.sh ${LTS_IDF} || true
|
||||
- cd $PROJECT_PATH/examples/tcp
|
||||
- make defconfig
|
||||
- make -j4
|
||||
# Build with v3.3 (LTS) IDF
|
||||
- cd $IDF_PATH
|
||||
- git checkout ${LTS_IDF} && git submodule update --init --recursive
|
||||
- cd $IDF_PATH/components/mqtt/esp-mqtt
|
||||
- git remote add local $PROJECT_PATH/.git
|
||||
- git fetch local
|
||||
- git reset --hard $CI_COMMIT_SHA
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/tcp
|
||||
- idf.py build
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/ssl
|
||||
- idf.py build
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/ws
|
||||
- idf.py build
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/wss
|
||||
- idf.py build
|
||||
14
managed_components/esp_mqtt/CMakeLists.txt
Normal file
14
managed_components/esp_mqtt/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
set(srcs mqtt_client.c lib/mqtt_msg.c lib/mqtt_outbox.c lib/platform_esp32_idf.c)
|
||||
|
||||
if(CONFIG_MQTT_PROTOCOL_5)
|
||||
list(APPEND srcs lib/mqtt5_msg.c mqtt5_client.c)
|
||||
endif()
|
||||
|
||||
list(TRANSFORM srcs PREPEND ${CMAKE_CURRENT_LIST_DIR}/)
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/include
|
||||
PRIV_INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/lib/include
|
||||
REQUIRES esp_event tcp_transport
|
||||
PRIV_REQUIRES esp_timer http_parser esp_hw_support heap
|
||||
KCONFIG ${CMAKE_CURRENT_LIST_DIR}/Kconfig
|
||||
)
|
||||
190
managed_components/esp_mqtt/Kconfig
Normal file
190
managed_components/esp_mqtt/Kconfig
Normal file
@@ -0,0 +1,190 @@
|
||||
menu "ESP-MQTT Configurations"
|
||||
|
||||
config MQTT_PROTOCOL_311
|
||||
bool "Enable MQTT protocol 3.1.1"
|
||||
default y
|
||||
help
|
||||
If not, this library will use MQTT protocol 3.1
|
||||
|
||||
config MQTT_PROTOCOL_5
|
||||
bool "Enable MQTT protocol 5.0"
|
||||
default n
|
||||
help
|
||||
If not, this library will not support MQTT 5.0
|
||||
|
||||
config MQTT_TRANSPORT_SSL
|
||||
bool "Enable MQTT over SSL"
|
||||
default y
|
||||
help
|
||||
Enable MQTT transport over SSL with mbedtls
|
||||
|
||||
config MQTT_TRANSPORT_WEBSOCKET
|
||||
bool "Enable MQTT over Websocket"
|
||||
default y
|
||||
depends on WS_TRANSPORT
|
||||
help
|
||||
Enable MQTT transport over Websocket.
|
||||
|
||||
config MQTT_TRANSPORT_WEBSOCKET_SECURE
|
||||
bool "Enable MQTT over Websocket Secure"
|
||||
default y
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET
|
||||
depends on MQTT_TRANSPORT_SSL
|
||||
help
|
||||
Enable MQTT transport over Websocket Secure.
|
||||
|
||||
config MQTT_MSG_ID_INCREMENTAL
|
||||
bool "Use Incremental Message Id"
|
||||
default n
|
||||
help
|
||||
Set this to true for the message id (2.3.1 Packet Identifier) to be generated
|
||||
as an incremental number rather then a random value (used by default)
|
||||
|
||||
config MQTT_SKIP_PUBLISH_IF_DISCONNECTED
|
||||
bool "Skip publish if disconnected"
|
||||
default n
|
||||
help
|
||||
Set this to true to avoid publishing (enqueueing messages) if the client is disconnected.
|
||||
The MQTT client tries to publish all messages by default, even in the disconnected state
|
||||
(where the qos1 and qos2 packets are stored in the internal outbox to be published later)
|
||||
The MQTT_SKIP_PUBLISH_IF_DISCONNECTED option allows applications to override this behaviour
|
||||
and not enqueue publish packets in the disconnected state.
|
||||
|
||||
config MQTT_REPORT_DELETED_MESSAGES
|
||||
bool "Report deleted messages"
|
||||
default n
|
||||
help
|
||||
Set this to true to post events for all messages which were deleted from the outbox
|
||||
before being correctly sent and confirmed.
|
||||
|
||||
config MQTT_USE_CUSTOM_CONFIG
|
||||
bool "MQTT Using custom configurations"
|
||||
default n
|
||||
help
|
||||
Custom MQTT configurations.
|
||||
|
||||
config MQTT_TCP_DEFAULT_PORT
|
||||
int "Default MQTT over TCP port"
|
||||
default 1883
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Default MQTT over TCP port
|
||||
|
||||
config MQTT_SSL_DEFAULT_PORT
|
||||
int "Default MQTT over SSL port"
|
||||
default 8883
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
depends on MQTT_TRANSPORT_SSL
|
||||
help
|
||||
Default MQTT over SSL port
|
||||
|
||||
config MQTT_WS_DEFAULT_PORT
|
||||
int "Default MQTT over Websocket port"
|
||||
default 80
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET
|
||||
help
|
||||
Default MQTT over Websocket port
|
||||
|
||||
config MQTT_WSS_DEFAULT_PORT
|
||||
int "Default MQTT over Websocket Secure port"
|
||||
default 443
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET_SECURE
|
||||
help
|
||||
Default MQTT over Websocket Secure port
|
||||
|
||||
config MQTT_BUFFER_SIZE
|
||||
int "Default MQTT Buffer Size"
|
||||
default 1024
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
This buffer size using for both transmit and receive
|
||||
|
||||
config MQTT_TASK_STACK_SIZE
|
||||
int "MQTT task stack size"
|
||||
default 6144
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
MQTT task stack size
|
||||
|
||||
config MQTT_DISABLE_API_LOCKS
|
||||
bool "Disable API locks"
|
||||
default n
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Default config employs API locks to protect internal structures. It is possible to disable
|
||||
these locks if the user code doesn't access MQTT API from multiple concurrent tasks
|
||||
|
||||
config MQTT_TASK_PRIORITY
|
||||
int "MQTT task priority"
|
||||
default 5
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
MQTT task priority. Higher number denotes higher priority.
|
||||
|
||||
config MQTT_POLL_READ_TIMEOUT_MS
|
||||
int "MQTT transport poll read timeut"
|
||||
default 1000
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Timeout when polling underlying transport for read.
|
||||
|
||||
config MQTT_EVENT_QUEUE_SIZE
|
||||
int "Number of queued events."
|
||||
default 1
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
A value higher than 1 enables multiple queued events.
|
||||
|
||||
config MQTT_TASK_CORE_SELECTION_ENABLED
|
||||
bool "Enable MQTT task core selection"
|
||||
help
|
||||
This will enable core selection
|
||||
|
||||
choice MQTT_TASK_CORE_SELECTION
|
||||
depends on MQTT_TASK_CORE_SELECTION_ENABLED
|
||||
prompt "Core to use ?"
|
||||
config MQTT_USE_CORE_0
|
||||
bool "Core 0"
|
||||
config MQTT_USE_CORE_1
|
||||
bool "Core 1"
|
||||
endchoice
|
||||
|
||||
config MQTT_OUTBOX_DATA_ON_EXTERNAL_MEMORY
|
||||
bool "Use external memory for outbox data"
|
||||
default n
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Set to true to use external memory for outbox data.
|
||||
|
||||
config MQTT_CUSTOM_OUTBOX
|
||||
bool "Enable custom outbox implementation"
|
||||
default n
|
||||
help
|
||||
Set to true if a specific implementation of message outbox is needed (e.g. persistent outbox in NVM or
|
||||
similar).
|
||||
Note: Implementation of the custom outbox must be added to the mqtt component. These CMake commands
|
||||
could be used to append the custom implementation to lib-mqtt sources:
|
||||
idf_component_get_property(mqtt mqtt COMPONENT_LIB)
|
||||
set_property(TARGET ${mqtt} PROPERTY SOURCES ${PROJECT_DIR}/custom_outbox.c APPEND)
|
||||
|
||||
config MQTT_OUTBOX_EXPIRED_TIMEOUT_MS
|
||||
int "Outbox message expired timeout[ms]"
|
||||
default 30000
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Messages which stays in the outbox longer than this value before being published will be discarded.
|
||||
|
||||
config MQTT_TOPIC_PRESENT_ALL_DATA_EVENTS
|
||||
bool "Enable publish topic in all data events"
|
||||
default n
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Set to true to have publish topic in all data events. This changes the behaviour
|
||||
when the message is bigger than the receive buffer size. The first event of the sequence
|
||||
always have the topic.
|
||||
Note: This will allocate memory to store the topic only in case of messge bigger than the buffer size.
|
||||
|
||||
endmenu
|
||||
202
managed_components/esp_mqtt/LICENSE
Normal file
202
managed_components/esp_mqtt/LICENSE
Normal 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 2016 Tuan PM
|
||||
|
||||
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.
|
||||
41
managed_components/esp_mqtt/README.md
Normal file
41
managed_components/esp_mqtt/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# ESP32 MQTT Library
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- Based on: <https://github.com/tuanpmt/esp_mqtt>
|
||||
- Support MQTT over TCP, SSL with mbedtls, MQTT over Websocket, MQTT over Websocket Secure
|
||||
- Easy to setup with URI
|
||||
- Multiple instances (Multiple clients in one application)
|
||||
- Support subscribing, publishing, authentication, will messages, keep alive pings and all 3 QoS levels (it should be a fully functional client).
|
||||
- Support for MQTT 3.1.1 and 5.0
|
||||
|
||||
## How to use
|
||||
|
||||
[ESP-MQTT](https://github.com/espressif/esp-mqtt) is a standard [ESP-IDF](https://github.com/espressif/esp-idf) component.
|
||||
Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
|
||||
|
||||
## Documentation
|
||||
|
||||
- Please refer to the standard [ESP-IDF](https://github.com/espressif/esp-idf), documentation for the latest version: <https://docs.espressif.com/projects/esp-idf/>
|
||||
|
||||
- Documentation of ESP-MQTT API: <https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/protocols/mqtt.html>
|
||||
|
||||
## License
|
||||
|
||||
- MQTT Package - [Stephen Robinson - contiki-mqtt](https://github.com/esar/contiki-mqtt)
|
||||
- Others [@tuanpmt](https://twitter.com/tuanpmt)
|
||||
Apache License
|
||||
|
||||
## Older IDF verisons
|
||||
|
||||
For [ESP-IDF](https://github.com/espressif/esp-idf) versions prior to IDFv3.2, please clone as a component of [ESP-IDF](https://github.com/espressif/esp-idf):
|
||||
|
||||
```
|
||||
git submodule add https://github.com/espressif/esp-mqtt.git components/espmqtt
|
||||
```
|
||||
|
||||
and checkout the [ESP-MQTT_FOR_IDF_3.1](https://github.com/espressif/esp-mqtt/tree/ESP-MQTT_FOR_IDF_3.1) tag
|
||||
11
managed_components/esp_mqtt/ci/build-test-rules.yml
Normal file
11
managed_components/esp_mqtt/ci/build-test-rules.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||
|
||||
examples/protocols:
|
||||
enable:
|
||||
- if: IDF_TARGET in ["esp32"]
|
||||
|
||||
examples/protocols/mqtt/ssl_ds:
|
||||
disable:
|
||||
- if: SOC_DIG_SIGN_SUPPORTED != 1
|
||||
temporary: false
|
||||
reason: DS not present
|
||||
22
managed_components/esp_mqtt/ci/build_examples.sh
Normal file
22
managed_components/esp_mqtt/ci/build_examples.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# build mqtt examples with make if $1=="make", with cmake otherwise
|
||||
set -o errexit # Exit if command failed.
|
||||
|
||||
if [ -z $IDF_PATH ] ; then
|
||||
echo "Mandatory variables undefined"
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
examples="tcp ssl ssl_mutual_auth ws wss"
|
||||
for i in $examples; do
|
||||
echo "Building MQTT example $i"
|
||||
cd $IDF_PATH/examples/protocols/mqtt/$i
|
||||
if [[ "$1" = "make" ]]; then
|
||||
make defconfig
|
||||
make -j 4
|
||||
else
|
||||
rm -rf build sdkconfig
|
||||
idf.py build
|
||||
fi;
|
||||
done
|
||||
2
managed_components/esp_mqtt/ci/idf_build_apps.toml
Normal file
2
managed_components/esp_mqtt/ci/idf_build_apps.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
build_dir = "$GITHUB_WORKSPACE/build_@t_@n"
|
||||
config="sdkconfig.ci"
|
||||
33
managed_components/esp_mqtt/ci/modify_for_legacy_idf.sh
Normal file
33
managed_components/esp_mqtt/ci/modify_for_legacy_idf.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
LATEST_IDF=master
|
||||
else
|
||||
LATEST_IDF=$1
|
||||
fi
|
||||
|
||||
# This snipped prepares environment for using esp-mqtt repository separately from idf -- legacy use before IDFv3.2
|
||||
#
|
||||
esp_mqtt_path=`pwd`
|
||||
mkdir -p ${esp_mqtt_path}/examples
|
||||
pushd
|
||||
cd $IDF_PATH
|
||||
former_commit_id=`git rev-parse HEAD`
|
||||
git checkout ${LATEST_IDF}
|
||||
|
||||
for example in tcp; do
|
||||
cp -r $IDF_PATH/examples/protocols/mqtt/${example} ${esp_mqtt_path}/examples
|
||||
echo 'EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/../../../' > ${esp_mqtt_path}/examples/${example}/Makefile
|
||||
cat $IDF_PATH/examples/protocols/mqtt/${example}/Makefile >> ${esp_mqtt_path}/examples/${example}/Makefile
|
||||
echo "CONFIG_MQTT_TRANSPORT_SSL=" >> ${esp_mqtt_path}/examples/${example}/sdkconfig.defaults
|
||||
echo "CONFIG_MQTT_TRANSPORT_WEBSOCKET=" >> ${esp_mqtt_path}/examples/${example}/sdkconfig.defaults
|
||||
done
|
||||
|
||||
cp -r $IDF_PATH/components/tcp_transport ${esp_mqtt_path}/..
|
||||
rm ${esp_mqtt_path}/../tcp_transport/transport_ssl.c
|
||||
echo -e "#include \"esp_transport.h\"\nvoid esp_transport_ws_set_path(esp_transport_handle_t t, const char *path) {}" > ${esp_mqtt_path}/../tcp_transport/transport_ws.c
|
||||
|
||||
cp $IDF_PATH/components/mqtt/Kconfig ${esp_mqtt_path}
|
||||
sed 's/esp-mqtt/\./g' $IDF_PATH/components/mqtt/component.mk > ${esp_mqtt_path}/component.mk
|
||||
git checkout $former_commit_id
|
||||
popd
|
||||
@@ -0,0 +1,7 @@
|
||||
CaseConfig:
|
||||
- name: test_app_protocol_mqtt_publish_connect
|
||||
overwrite:
|
||||
dut:
|
||||
class: ESP32QEMUDUT
|
||||
package: ttfw_idf
|
||||
|
||||
19
managed_components/esp_mqtt/ci/set_idf.sh
Normal file
19
managed_components/esp_mqtt/ci/set_idf.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# sets up the IDF repo incl submodules with specified version as $1
|
||||
set -o errexit # Exit if command failed.
|
||||
|
||||
if [ -z $IDF_PATH ] || [ -z $MQTT_PATH ] || [ -z $1 ] ; then
|
||||
echo "Mandatory variables undefined"
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
echo "Checking out IDF version $1"
|
||||
cd $IDF_PATH
|
||||
# Cleans out the untracked files in the repo, so the next "git checkout" doesn't fail
|
||||
git clean -f
|
||||
git checkout $1
|
||||
# Removes the mqtt submodule, so the next submodule update doesn't fail
|
||||
rm -rf $IDF_PATH/components/mqtt/esp-mqtt
|
||||
git submodule update --init --recursive
|
||||
|
||||
16
managed_components/esp_mqtt/ci/set_mqtt.sh
Normal file
16
managed_components/esp_mqtt/ci/set_mqtt.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# sets the mqtt in IDF tree as a submodule with the version specified as $1
|
||||
set -o errexit # Exit if command failed.
|
||||
|
||||
if [ -z $IDF_PATH ] || [ -z $MQTT_PATH ] || [ -z $1 ] ; then
|
||||
echo "Mandatory variables undefined"
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
echo "Checking out MQTT version to $1"
|
||||
# exchange remotes of mqtt submodules with plain copy
|
||||
cd $IDF_PATH/components/mqtt/esp-mqtt
|
||||
rm -rf .git # removes the actual IDF referenced version
|
||||
cp -r $MQTT_PATH/.git . # replaces with the MQTT_PATH (CI checked tree)
|
||||
git reset --hard $1 # sets the requested version
|
||||
15
managed_components/esp_mqtt/host_test/CMakeLists.txt
Normal file
15
managed_components/esp_mqtt/host_test/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(COMPONENTS mqtt main)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS
|
||||
"$ENV{IDF_PATH}/tools/mocks/esp_hw_support/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/freertos/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/esp_timer/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/esp_event/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/lwip/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/esp-tls/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/http_parser/"
|
||||
"$ENV{IDF_PATH}/tools/mocks/tcp_transport/")
|
||||
|
||||
project(host_mqtt_client_test)
|
||||
30
managed_components/esp_mqtt/host_test/README.md
Normal file
30
managed_components/esp_mqtt/host_test/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
| Supported Targets | Linux |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# Description
|
||||
|
||||
This directory contains test code for the mqtt client that runs on host.
|
||||
|
||||
Tests are written using [Catch2](https://github.com/catchorg/Catch2) test framework
|
||||
|
||||
# Build
|
||||
|
||||
Tests build regularly like an idf project.
|
||||
|
||||
```
|
||||
idf.py build
|
||||
```
|
||||
|
||||
# Run
|
||||
|
||||
The build produces an executable in the build folder.
|
||||
|
||||
Just run:
|
||||
|
||||
```
|
||||
./build/host_mqtt_client_test.elf
|
||||
```
|
||||
|
||||
The test executable have some options provided by the test framework.
|
||||
|
||||
|
||||
21
managed_components/esp_mqtt/host_test/main/CMakeLists.txt
Normal file
21
managed_components/esp_mqtt/host_test/main/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
idf_component_register(SRCS "test_mqtt_client.cpp"
|
||||
REQUIRES cmock mqtt esp_timer esp_hw_support http_parser log
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC -fsanitize=address -fconcepts)
|
||||
target_link_options(${COMPONENT_LIB} PUBLIC -fsanitize=address)
|
||||
target_link_libraries(${COMPONENT_LIB} PUBLIC Catch2::Catch2WithMain)
|
||||
|
||||
idf_component_get_property(mqtt mqtt COMPONENT_LIB)
|
||||
target_compile_definitions(${mqtt} PRIVATE SOC_WIFI_SUPPORTED=1)
|
||||
target_compile_options(${mqtt} PUBLIC -fsanitize=address -fconcepts)
|
||||
target_link_options(${mqtt} PUBLIC -fsanitize=address)
|
||||
|
||||
if(CONFIG_GCOV_ENABLED)
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
target_link_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
|
||||
idf_component_get_property(mqtt mqtt COMPONENT_LIB)
|
||||
target_compile_options(${mqtt} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
target_link_options(${mqtt} PUBLIC --coverage -fprofile-arcs -ftest-coverage)
|
||||
endif()
|
||||
9
managed_components/esp_mqtt/host_test/main/Kconfig
Normal file
9
managed_components/esp_mqtt/host_test/main/Kconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
menu "Host-test config"
|
||||
|
||||
config GCOV_ENABLED
|
||||
bool "Coverage analyzer"
|
||||
default n
|
||||
help
|
||||
Enables coverage analyzing for host tests.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,6 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/catch2: "^3.5.2"
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: ">=5.0.0"
|
||||
168
managed_components/esp_mqtt/host_test/main/test_mqtt_client.cpp
Normal file
168
managed_components/esp_mqtt/host_test/main/test_mqtt_client.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <memory>
|
||||
#include <net/if.h>
|
||||
#include <random>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include "esp_transport.h"
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "mqtt_client.h"
|
||||
extern "C" {
|
||||
#include "Mockesp_event.h"
|
||||
#include "Mockesp_mac.h"
|
||||
#include "Mockesp_transport.h"
|
||||
#include "Mockesp_transport_ssl.h"
|
||||
#include "Mockesp_transport_tcp.h"
|
||||
#include "Mockesp_transport_ws.h"
|
||||
#include "Mockevent_groups.h"
|
||||
#include "Mockhttp_parser.h"
|
||||
#include "Mockqueue.h"
|
||||
#include "Mocktask.h"
|
||||
#if __has_include ("Mockidf_additions.h")
|
||||
/* Some functions were moved from "task.h" to "idf_additions.h" */
|
||||
#include "Mockidf_additions.h"
|
||||
#endif
|
||||
#include "Mockesp_timer.h"
|
||||
|
||||
/*
|
||||
* The following functions are not directly called but the generation of them
|
||||
* from cmock is broken, so we need to define them here.
|
||||
*/
|
||||
esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *esp_tls_code, int *esp_tls_flags)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
auto random_string(std::size_t n)
|
||||
{
|
||||
static constexpr std::string_view char_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456790";
|
||||
std::string str;
|
||||
std::sample(char_set.begin(), char_set.end(), std::back_inserter(str), n,
|
||||
std::mt19937 {std::random_device{}()});
|
||||
return str;
|
||||
}
|
||||
|
||||
using unique_mqtt_client = std::unique_ptr < std::remove_pointer_t<esp_mqtt_client_handle_t>, decltype([](esp_mqtt_client_handle_t client)
|
||||
{
|
||||
esp_mqtt_client_destroy(client);
|
||||
}) >;
|
||||
|
||||
SCENARIO("MQTT Client Operation")
|
||||
{
|
||||
// Set expectations for the mocked calls.
|
||||
int mtx = 0;
|
||||
int transport_list = 0;
|
||||
int transport = 0;
|
||||
int event_group = 0;
|
||||
uint8_t mac[] = {0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55};
|
||||
esp_timer_get_time_IgnoreAndReturn(0);
|
||||
xQueueTakeMutexRecursive_IgnoreAndReturn(true);
|
||||
xQueueGiveMutexRecursive_IgnoreAndReturn(true);
|
||||
xQueueCreateMutex_ExpectAnyArgsAndReturn(
|
||||
reinterpret_cast<QueueHandle_t>(&mtx));
|
||||
xEventGroupCreate_IgnoreAndReturn(reinterpret_cast<EventGroupHandle_t>(&event_group));
|
||||
esp_transport_list_init_IgnoreAndReturn(reinterpret_cast<esp_transport_list_handle_t>(&transport_list));
|
||||
esp_transport_tcp_init_IgnoreAndReturn(reinterpret_cast<esp_transport_handle_t>(&transport));
|
||||
esp_transport_ssl_init_IgnoreAndReturn(reinterpret_cast<esp_transport_handle_t>(&transport));
|
||||
esp_transport_ws_init_IgnoreAndReturn(reinterpret_cast<esp_transport_handle_t>(&transport));
|
||||
esp_transport_ws_set_subprotocol_IgnoreAndReturn(ESP_OK);
|
||||
esp_transport_list_add_IgnoreAndReturn(ESP_OK);
|
||||
esp_transport_set_default_port_IgnoreAndReturn(ESP_OK);
|
||||
http_parser_url_init_Ignore();
|
||||
esp_event_loop_create_IgnoreAndReturn(ESP_OK);
|
||||
esp_read_mac_IgnoreAndReturn(ESP_OK);
|
||||
esp_read_mac_ReturnThruPtr_mac(mac);
|
||||
esp_transport_list_destroy_IgnoreAndReturn(ESP_OK);
|
||||
esp_transport_destroy_IgnoreAndReturn(ESP_OK);
|
||||
vEventGroupDelete_Ignore();
|
||||
vQueueDelete_Ignore();
|
||||
GIVEN("An a minimal config") {
|
||||
esp_mqtt_client_config_t config{};
|
||||
config.broker.address.uri = "mqtt://1.1.1.1";
|
||||
struct http_parser_url ret_uri = {
|
||||
.field_set = 1 | (1 << 1),
|
||||
.port = 0,
|
||||
.field_data = { { 0, 4 } /*mqtt*/, { 7, 1 } } // at least *scheme* and *host*
|
||||
};
|
||||
http_parser_parse_url_ExpectAnyArgsAndReturn(0);
|
||||
http_parser_parse_url_ReturnThruPtr_u(&ret_uri);
|
||||
xTaskCreatePinnedToCore_ExpectAnyArgsAndReturn(pdTRUE);
|
||||
SECTION("Client with minimal config") {
|
||||
auto client = unique_mqtt_client{esp_mqtt_client_init(&config)};
|
||||
REQUIRE(client != nullptr);
|
||||
SECTION("User will set a new uri") {
|
||||
struct http_parser_url ret_uri = {
|
||||
.field_set = 1,
|
||||
.port = 0,
|
||||
.field_data = { { 0, 1} }
|
||||
};
|
||||
SECTION("User set a correct URI") {
|
||||
http_parser_parse_url_StopIgnore();
|
||||
http_parser_parse_url_ExpectAnyArgsAndReturn(0);
|
||||
http_parser_parse_url_ReturnThruPtr_u(&ret_uri);
|
||||
auto res = esp_mqtt_client_set_uri(client.get(), " ");
|
||||
REQUIRE(res == ESP_OK);
|
||||
}
|
||||
SECTION("Incorrect URI from user") {
|
||||
http_parser_parse_url_StopIgnore();
|
||||
http_parser_parse_url_ExpectAnyArgsAndReturn(1);
|
||||
http_parser_parse_url_ReturnThruPtr_u(&ret_uri);
|
||||
auto res = esp_mqtt_client_set_uri(client.get(), " ");
|
||||
REQUIRE(res == ESP_FAIL);
|
||||
}
|
||||
}
|
||||
SECTION("User set interface to use"){
|
||||
http_parser_parse_url_ExpectAnyArgsAndReturn(0);
|
||||
http_parser_parse_url_ReturnThruPtr_u(&ret_uri);
|
||||
struct ifreq if_name = {.ifr_ifrn = {"custom"}};
|
||||
config.network.if_name = &if_name;
|
||||
SECTION("Client is not started"){
|
||||
REQUIRE(esp_mqtt_set_config(client.get(), &config)== ESP_OK);
|
||||
}
|
||||
}
|
||||
SECTION("After Start Client Is Cleanly destroyed") {
|
||||
REQUIRE(esp_mqtt_client_start(client.get()) == ESP_OK);
|
||||
// Only need to start the client, destroy is called automatically at the end of
|
||||
// scope
|
||||
}
|
||||
}
|
||||
SECTION("Client with all allocating configuration set") {
|
||||
auto host = random_string(20);
|
||||
auto path = random_string(10);
|
||||
auto username = random_string(10);
|
||||
auto client_id = random_string(10);
|
||||
auto password = random_string(10);
|
||||
auto lw_topic = random_string(10);
|
||||
auto lw_msg = random_string(10);
|
||||
|
||||
config.broker = {.address = {
|
||||
.hostname = host.data(),
|
||||
.path = path.data()
|
||||
}
|
||||
};
|
||||
config.credentials = {
|
||||
.username = username.data(),
|
||||
.client_id = client_id.data(),
|
||||
.authentication = {
|
||||
.password = password.data()
|
||||
}
|
||||
};
|
||||
config.session = {
|
||||
.last_will {
|
||||
.topic = lw_topic.data(),
|
||||
.msg = lw_msg.data()
|
||||
}
|
||||
};
|
||||
auto client = unique_mqtt_client{esp_mqtt_client_init(&config)};
|
||||
REQUIRE(client != nullptr);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 1991-1993 The Regents of the University of California
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/* Implementation from BSD headers*/
|
||||
#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
|
||||
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
|
||||
#define STAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *stqh_first;/* first element */ \
|
||||
struct type **stqh_last;/* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
STAILQ_NEXT((elm), field) = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INIT(head) do { \
|
||||
STAILQ_FIRST((head)) = NULL; \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for((var) = STAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = STAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT(elm, field) = \
|
||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = \
|
||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
|
||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = STAILQ_FIRST((head)); \
|
||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
||||
curelm = STAILQ_NEXT(curelm, field); \
|
||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
||||
} \
|
||||
TRASHIT(*oldnext); \
|
||||
} while (0)
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_GCOV_ENABLED=y
|
||||
6
managed_components/esp_mqtt/host_test/sdkconfig.defaults
Normal file
6
managed_components/esp_mqtt/host_test/sdkconfig.defaults
Normal file
@@ -0,0 +1,6 @@
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_COMPILER_CXX_RTTI=y
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
|
||||
CONFIG_COMPILER_STACK_CHECK_NONE=y
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
||||
5
managed_components/esp_mqtt/idf_component.yml
Normal file
5
managed_components/esp_mqtt/idf_component.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
version: "1.0.0"
|
||||
description: esp-mqtt
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.0"
|
||||
286
managed_components/esp_mqtt/include/mqtt5_client.h
Normal file
286
managed_components/esp_mqtt/include/mqtt5_client.h
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _MQTT5_CLIENT_H_
|
||||
#define _MQTT5_CLIENT_H_
|
||||
|
||||
#include "mqtt_client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct esp_mqtt_client *esp_mqtt5_client_handle_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol error reason code, more details refer to MQTT5 protocol document section 2.4
|
||||
*/
|
||||
typedef enum mqtt5_error_reason_code_t {
|
||||
MQTT5_UNSPECIFIED_ERROR = 0x80,
|
||||
MQTT5_MALFORMED_PACKET = 0x81,
|
||||
MQTT5_PROTOCOL_ERROR = 0x82,
|
||||
MQTT5_IMPLEMENT_SPECIFIC_ERROR = 0x83,
|
||||
MQTT5_UNSUPPORTED_PROTOCOL_VER = 0x84,
|
||||
MQTT5_INVAILD_CLIENT_ID __attribute__((deprecated)) = 0x85,
|
||||
MQTT5_INVALID_CLIENT_ID = 0x85,
|
||||
MQTT5_BAD_USERNAME_OR_PWD = 0x86,
|
||||
MQTT5_NOT_AUTHORIZED = 0x87,
|
||||
MQTT5_SERVER_UNAVAILABLE = 0x88,
|
||||
MQTT5_SERVER_BUSY = 0x89,
|
||||
MQTT5_BANNED = 0x8A,
|
||||
MQTT5_SERVER_SHUTTING_DOWN = 0x8B,
|
||||
MQTT5_BAD_AUTH_METHOD = 0x8C,
|
||||
MQTT5_KEEP_ALIVE_TIMEOUT = 0x8D,
|
||||
MQTT5_SESSION_TAKEN_OVER = 0x8E,
|
||||
MQTT5_TOPIC_FILTER_INVAILD __attribute__((deprecated)) = 0x8F,
|
||||
MQTT5_TOPIC_FILTER_INVALID = 0x8F,
|
||||
MQTT5_TOPIC_NAME_INVAILD __attribute__((deprecated)) = 0x90,
|
||||
MQTT5_TOPIC_NAME_INVALID = 0x90,
|
||||
MQTT5_PACKET_IDENTIFIER_IN_USE = 0x91,
|
||||
MQTT5_PACKET_IDENTIFIER_NOT_FOUND = 0x92,
|
||||
MQTT5_RECEIVE_MAXIMUM_EXCEEDED = 0x93,
|
||||
MQTT5_TOPIC_ALIAS_INVAILD __attribute__((deprecated)) = 0x94,
|
||||
MQTT5_TOPIC_ALIAS_INVALID = 0x94,
|
||||
MQTT5_PACKET_TOO_LARGE = 0x95,
|
||||
MQTT5_MESSAGE_RATE_TOO_HIGH = 0x96,
|
||||
MQTT5_QUOTA_EXCEEDED = 0x97,
|
||||
MQTT5_ADMINISTRATIVE_ACTION = 0x98,
|
||||
MQTT5_PAYLOAD_FORMAT_INVAILD __attribute__((deprecated)) = 0x99,
|
||||
MQTT5_PAYLOAD_FORMAT_INVALID = 0x99,
|
||||
MQTT5_RETAIN_NOT_SUPPORT = 0x9A,
|
||||
MQTT5_QOS_NOT_SUPPORT = 0x9B,
|
||||
MQTT5_USE_ANOTHER_SERVER = 0x9C,
|
||||
MQTT5_SERVER_MOVED = 0x9D,
|
||||
MQTT5_SHARED_SUBSCR_NOT_SUPPORTED = 0x9E,
|
||||
MQTT5_CONNECTION_RATE_EXCEEDED = 0x9F,
|
||||
MQTT5_MAXIMUM_CONNECT_TIME = 0xA0,
|
||||
MQTT5_SUBSCRIBE_IDENTIFIER_NOT_SUPPORT = 0xA1,
|
||||
MQTT5_WILDCARD_SUBSCRIBE_NOT_SUPPORT = 0xA2,
|
||||
} esp_mqtt5_error_reason_code_t;
|
||||
|
||||
/**
|
||||
* MQTT5 user property handle
|
||||
*/
|
||||
typedef struct mqtt5_user_property_list_t *mqtt5_user_property_handle_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol connect properties and will properties configuration, more details refer to MQTT5 protocol document section 3.1.2.11 and 3.3.2.3
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t session_expiry_interval; /*!< The interval time of session expiry */
|
||||
uint32_t maximum_packet_size; /*!< The maximum packet size that we can receive */
|
||||
uint16_t receive_maximum; /*!< The maximum pakcket count that we process concurrently */
|
||||
uint16_t topic_alias_maximum; /*!< The maximum topic alias that we support */
|
||||
bool request_resp_info; /*!< This value to request Server to return Response information */
|
||||
bool request_problem_info; /*!< This value to indicate whether the reason string or user properties are sent in case of failures */
|
||||
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */
|
||||
uint32_t will_delay_interval; /*!< The time interval that server delays publishing will message */
|
||||
uint32_t message_expiry_interval; /*!< The time interval that message expiry */
|
||||
bool payload_format_indicator; /*!< This value is to indicator will message payload format */
|
||||
const char *content_type; /*!< This value is to indicator will message content type, use a MIME content type string */
|
||||
const char *response_topic; /*!< Topic name for a response message */
|
||||
const char *correlation_data; /*!< Binary data for receiver to match the response message */
|
||||
uint16_t correlation_data_len; /*!< The length of correlation data */
|
||||
mqtt5_user_property_handle_t will_user_property; /*!< The handle for will message user property, call function esp_mqtt5_client_set_user_property to set it */
|
||||
} esp_mqtt5_connection_property_config_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol publish properties configuration, more details refer to MQTT5 protocol document section 3.3.2.3
|
||||
*/
|
||||
typedef struct {
|
||||
bool payload_format_indicator; /*!< This value is to indicator publish message payload format */
|
||||
uint32_t message_expiry_interval; /*!< The time interval that message expiry */
|
||||
uint16_t topic_alias; /*!< An interger value to identify the topic instead of using topic name string */
|
||||
const char *response_topic; /*!< Topic name for a response message */
|
||||
const char *correlation_data; /*!< Binary data for receiver to match the response message */
|
||||
uint16_t correlation_data_len; /*!< The length of correlation data */
|
||||
const char *content_type; /*!< This value is to indicator publish message content type, use a MIME content type string */
|
||||
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */
|
||||
} esp_mqtt5_publish_property_config_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol subscribe properties configuration, more details refer to MQTT5 protocol document section 3.8.2.1
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t subscribe_id; /*!< A variable byte represents the identifier of the subscription */
|
||||
bool no_local_flag; /*!< Subscription Option to allow that server publish message that client sent */
|
||||
bool retain_as_published_flag; /*!< Subscription Option to keep the retain flag as published option */
|
||||
uint8_t retain_handle; /*!< Subscription Option to handle retain option */
|
||||
bool is_share_subscribe; /*!< Whether subscribe is a shared subscription */
|
||||
const char *share_name; /*!< The name of shared subscription which is a part of $share/{share_name}/{topic} */
|
||||
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */
|
||||
} esp_mqtt5_subscribe_property_config_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol unsubscribe properties configuration, more details refer to MQTT5 protocol document section 3.10.2.1
|
||||
*/
|
||||
typedef struct {
|
||||
bool is_share_subscribe; /*!< Whether subscribe is a shared subscription */
|
||||
const char *share_name; /*!< The name of shared subscription which is a part of $share/{share_name}/{topic} */
|
||||
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */
|
||||
} esp_mqtt5_unsubscribe_property_config_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol disconnect properties configuration, more details refer to MQTT5 protocol document section 3.14.2.2
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t session_expiry_interval; /*!< The interval time of session expiry */
|
||||
uint8_t disconnect_reason; /*!< The reason that connection disconnet, refer to mqtt5_error_reason_code */
|
||||
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */
|
||||
} esp_mqtt5_disconnect_property_config_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol for event properties
|
||||
*/
|
||||
typedef struct {
|
||||
bool payload_format_indicator; /*!< Payload format of the message */
|
||||
char *response_topic; /*!< Response topic of the message */
|
||||
int response_topic_len; /*!< Response topic length of the message */
|
||||
char *correlation_data; /*!< Correlation data of the message */
|
||||
uint16_t correlation_data_len; /*!< Correlation data length of the message */
|
||||
char *content_type; /*!< Content type of the message */
|
||||
int content_type_len; /*!< Content type length of the message */
|
||||
uint16_t subscribe_id; /*!< Subscription identifier of the message */
|
||||
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_delete_user_property to free the memory */
|
||||
} esp_mqtt5_event_property_t;
|
||||
|
||||
/**
|
||||
* MQTT5 protocol for user property
|
||||
*/
|
||||
typedef struct {
|
||||
const char *key; /*!< Item key name */
|
||||
const char *value; /*!< Item value string */
|
||||
} esp_mqtt5_user_property_item_t;
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client connect property configuration
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param connect_property connect property
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_FAIL on fail
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_connection_property_config_t *connect_property);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client publish property configuration
|
||||
*
|
||||
* This API will not store the publish property, it is one-time configuration.
|
||||
* Before call `esp_mqtt_client_publish` to publish data, call this API to set publish property if have
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param property publish property
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_FAIL on fail
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt5_client_set_publish_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_publish_property_config_t *property);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client subscribe property configuration
|
||||
*
|
||||
* This API will not store the subscribe property, it is one-time configuration.
|
||||
* Before call `esp_mqtt_client_subscribe` to subscribe topic, call this API to set subscribe property if have
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param property subscribe property
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_FAIL on fail
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_subscribe_property_config_t *property);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client unsubscribe property configuration
|
||||
*
|
||||
* This API will not store the unsubscribe property, it is one-time configuration.
|
||||
* Before call `esp_mqtt_client_unsubscribe` to unsubscribe topic, call this API to set unsubscribe property if have
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param property unsubscribe property
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_FAIL on fail
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt5_client_set_unsubscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_unsubscribe_property_config_t *property);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client disconnect property configuration
|
||||
*
|
||||
* This API will not store the disconnect property, it is one-time configuration.
|
||||
* Before call `esp_mqtt_client_disconnect` to disconnect connection, call this API to set disconnect property if have
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param property disconnect property
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_FAIL on fail
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_disconnect_property_config_t *property);
|
||||
|
||||
/**
|
||||
* @brief Set MQTT5 client user property configuration
|
||||
*
|
||||
* This API will allocate memory for user_property, please DO NOT forget `call esp_mqtt5_client_delete_user_property`
|
||||
* after you use it.
|
||||
* Before publish data, subscribe topic, unsubscribe, etc, call this API to set user property if have
|
||||
*
|
||||
* @param user_property user_property handle
|
||||
* @param item array of user property data (eg. {{"var","val"},{"other","2"}})
|
||||
* @param item_num number of items in user property data
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_FAIL on fail
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_property, esp_mqtt5_user_property_item_t item[], uint8_t item_num);
|
||||
|
||||
/**
|
||||
* @brief Get MQTT5 client user property
|
||||
*
|
||||
* @param user_property user_property handle
|
||||
* @param item point that store user property data
|
||||
* @param item_num number of items in user property data
|
||||
*
|
||||
* This API can use with `esp_mqtt5_client_get_user_property_count` to get list count of user property.
|
||||
* And malloc number of count item array memory to store the user property data.
|
||||
* Please DO NOT forget the item memory, key and value point in item memory when get user property data successfully.
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_FAIL on fail
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt5_client_get_user_property(mqtt5_user_property_handle_t user_property, esp_mqtt5_user_property_item_t *item, uint8_t *item_num);
|
||||
|
||||
/**
|
||||
* @brief Get MQTT5 client user property list count
|
||||
*
|
||||
* @param user_property user_property handle
|
||||
* @return user property list count
|
||||
*/
|
||||
uint8_t esp_mqtt5_client_get_user_property_count(mqtt5_user_property_handle_t user_property);
|
||||
|
||||
/**
|
||||
* @brief Free the user property list
|
||||
*
|
||||
* @param user_property user_property handle
|
||||
*
|
||||
* This API will free the memory in user property list and free user_property itself
|
||||
*/
|
||||
void esp_mqtt5_client_delete_user_property(mqtt5_user_property_handle_t user_property);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
||||
702
managed_components/esp_mqtt/include/mqtt_client.h
Normal file
702
managed_components/esp_mqtt/include/mqtt_client.h
Normal file
@@ -0,0 +1,702 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE', which is part of this source code package.
|
||||
* Tuan PM <tuanpm at live dot com>
|
||||
*/
|
||||
|
||||
#ifndef _MQTT_CLIENT_H_
|
||||
#define _MQTT_CLIENT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_transport.h"
|
||||
#ifdef CONFIG_MQTT_PROTOCOL_5
|
||||
#include "mqtt5_client.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef ESP_EVENT_DECLARE_BASE
|
||||
// Define event loop types if macros not available
|
||||
typedef void *esp_event_loop_handle_t;
|
||||
typedef void *esp_event_handler_t;
|
||||
#endif
|
||||
|
||||
typedef struct esp_mqtt_client *esp_mqtt_client_handle_t;
|
||||
|
||||
#define MQTT_OVER_TCP_SCHEME "mqtt"
|
||||
#define MQTT_OVER_SSL_SCHEME "mqtts"
|
||||
#define MQTT_OVER_WS_SCHEME "ws"
|
||||
#define MQTT_OVER_WSS_SCHEME "wss"
|
||||
|
||||
/**
|
||||
* @brief *MQTT* event types.
|
||||
*
|
||||
* User event handler receives context data in `esp_mqtt_event_t` structure with
|
||||
* - client - *MQTT* client handle
|
||||
* - various other data depending on event type
|
||||
*
|
||||
*/
|
||||
typedef enum esp_mqtt_event_id_t {
|
||||
MQTT_EVENT_ANY = -1,
|
||||
MQTT_EVENT_ERROR =
|
||||
0, /*!< on error event, additional context: connection return code, error
|
||||
handle from esp_tls (if supported) */
|
||||
MQTT_EVENT_CONNECTED, /*!< connected event, additional context:
|
||||
session_present flag */
|
||||
MQTT_EVENT_DISCONNECTED, /*!< disconnected event */
|
||||
MQTT_EVENT_SUBSCRIBED, /*!< subscribed event, additional context:
|
||||
- msg_id message id
|
||||
- error_handle `error_type` in case subscribing failed
|
||||
- data pointer to broker response, check for errors.
|
||||
- data_len length of the data for this
|
||||
event
|
||||
*/
|
||||
MQTT_EVENT_UNSUBSCRIBED, /*!< unsubscribed event, additional context: msg_id */
|
||||
MQTT_EVENT_PUBLISHED, /*!< published event, additional context: msg_id */
|
||||
MQTT_EVENT_DATA, /*!< data event, additional context:
|
||||
- msg_id message id
|
||||
- topic pointer to the received topic
|
||||
- topic_len length of the topic
|
||||
- data pointer to the received data
|
||||
- data_len length of the data for this event
|
||||
- current_data_offset offset of the current data for
|
||||
this event
|
||||
- total_data_len total length of the data received
|
||||
- retain retain flag of the message
|
||||
- qos QoS level of the message
|
||||
- dup dup flag of the message
|
||||
Note: Multiple MQTT_EVENT_DATA could be fired for one
|
||||
message, if it is longer than internal buffer. In that
|
||||
case only first event contains topic pointer and length,
|
||||
other contain data only with current data length and
|
||||
current data offset updating.
|
||||
*/
|
||||
MQTT_EVENT_BEFORE_CONNECT, /*!< The event occurs before connecting */
|
||||
MQTT_EVENT_DELETED, /*!< Notification on delete of one message from the
|
||||
internal outbox, if the message couldn't have been sent
|
||||
or acknowledged before expiring defined in
|
||||
OUTBOX_EXPIRED_TIMEOUT_MS. (events are not posted upon
|
||||
deletion of successfully acknowledged messages)
|
||||
- This event id is posted only if
|
||||
MQTT_REPORT_DELETED_MESSAGES==1
|
||||
- Additional context: msg_id (id of the deleted
|
||||
message, always 0 for QoS = 0 messages).
|
||||
*/
|
||||
MQTT_USER_EVENT, /*!< Custom event used to queue tasks into mqtt event handler
|
||||
All fields from the esp_mqtt_event_t type could be used to pass
|
||||
an additional context data to the handler.
|
||||
*/
|
||||
} esp_mqtt_event_id_t;
|
||||
|
||||
/**
|
||||
* *MQTT* connection error codes propagated via ERROR event
|
||||
*/
|
||||
typedef enum esp_mqtt_connect_return_code_t {
|
||||
MQTT_CONNECTION_ACCEPTED = 0, /*!< Connection accepted */
|
||||
MQTT_CONNECTION_REFUSE_PROTOCOL, /*!< *MQTT* connection refused reason: Wrong
|
||||
protocol */
|
||||
MQTT_CONNECTION_REFUSE_ID_REJECTED, /*!< *MQTT* connection refused reason: ID
|
||||
rejected */
|
||||
MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE, /*!< *MQTT* connection refused
|
||||
reason: Server unavailable */
|
||||
MQTT_CONNECTION_REFUSE_BAD_USERNAME, /*!< *MQTT* connection refused reason:
|
||||
Wrong user */
|
||||
MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED /*!< *MQTT* connection refused reason:
|
||||
Wrong username or password */
|
||||
} esp_mqtt_connect_return_code_t;
|
||||
|
||||
/**
|
||||
* *MQTT* connection error codes propagated via ERROR event
|
||||
*/
|
||||
typedef enum esp_mqtt_error_type_t {
|
||||
MQTT_ERROR_TYPE_NONE = 0,
|
||||
MQTT_ERROR_TYPE_TCP_TRANSPORT,
|
||||
MQTT_ERROR_TYPE_CONNECTION_REFUSED,
|
||||
MQTT_ERROR_TYPE_SUBSCRIBE_FAILED
|
||||
} esp_mqtt_error_type_t;
|
||||
|
||||
/**
|
||||
* MQTT_ERROR_TYPE_TCP_TRANSPORT error type hold all sorts of transport layer
|
||||
* errors, including ESP-TLS error, but in the past only the errors from
|
||||
* MQTT_ERROR_TYPE_ESP_TLS layer were reported, so the ESP-TLS error type is
|
||||
* re-defined here for backward compatibility
|
||||
*/
|
||||
#define MQTT_ERROR_TYPE_ESP_TLS MQTT_ERROR_TYPE_TCP_TRANSPORT
|
||||
|
||||
typedef enum esp_mqtt_transport_t {
|
||||
MQTT_TRANSPORT_UNKNOWN = 0x0,
|
||||
MQTT_TRANSPORT_OVER_TCP, /*!< *MQTT* over TCP, using scheme: ``MQTT`` */
|
||||
MQTT_TRANSPORT_OVER_SSL, /*!< *MQTT* over SSL, using scheme: ``MQTTS`` */
|
||||
MQTT_TRANSPORT_OVER_WS, /*!< *MQTT* over Websocket, using scheme:: ``ws`` */
|
||||
MQTT_TRANSPORT_OVER_WSS /*!< *MQTT* over Websocket Secure, using scheme:
|
||||
``wss`` */
|
||||
} esp_mqtt_transport_t;
|
||||
|
||||
/**
|
||||
* *MQTT* protocol version used for connection
|
||||
*/
|
||||
typedef enum esp_mqtt_protocol_ver_t {
|
||||
MQTT_PROTOCOL_UNDEFINED = 0,
|
||||
MQTT_PROTOCOL_V_3_1,
|
||||
MQTT_PROTOCOL_V_3_1_1,
|
||||
MQTT_PROTOCOL_V_5,
|
||||
} esp_mqtt_protocol_ver_t;
|
||||
|
||||
/**
|
||||
* @brief *MQTT* error code structure to be passed as a contextual information
|
||||
* into ERROR event
|
||||
*
|
||||
* Important: This structure extends `esp_tls_last_error` error structure and is
|
||||
* backward compatible with it (so might be down-casted and treated as
|
||||
* `esp_tls_last_error` error, but recommended to update applications if used
|
||||
* this way previously)
|
||||
*
|
||||
* Use this structure directly checking error_type first and then appropriate
|
||||
* error code depending on the source of the error:
|
||||
*
|
||||
* | error_type | related member variables | note |
|
||||
* | MQTT_ERROR_TYPE_TCP_TRANSPORT | esp_tls_last_esp_err, esp_tls_stack_err,
|
||||
* esp_tls_cert_verify_flags, sock_errno | Error reported from
|
||||
* tcp_transport/esp-tls | | MQTT_ERROR_TYPE_CONNECTION_REFUSED |
|
||||
* connect_return_code | Internal error reported from *MQTT* broker on
|
||||
* connection |
|
||||
*/
|
||||
typedef struct esp_mqtt_error_codes {
|
||||
/* compatible portion of the struct corresponding to struct esp_tls_last_error
|
||||
*/
|
||||
esp_err_t esp_tls_last_esp_err; /*!< last esp_err code reported from esp-tls
|
||||
component */
|
||||
int esp_tls_stack_err; /*!< tls specific error code reported from underlying
|
||||
tls stack */
|
||||
int esp_tls_cert_verify_flags; /*!< tls flags reported from underlying tls
|
||||
stack during certificate verification */
|
||||
/* esp-mqtt specific structure extension */
|
||||
esp_mqtt_error_type_t
|
||||
error_type; /*!< error type referring to the source of the error */
|
||||
esp_mqtt_connect_return_code_t
|
||||
connect_return_code; /*!< connection refused error code reported from
|
||||
*MQTT* broker on connection */
|
||||
#ifdef CONFIG_MQTT_PROTOCOL_5
|
||||
esp_mqtt5_error_reason_code_t
|
||||
disconnect_return_code; /*!< disconnection reason code reported from
|
||||
*MQTT* broker on disconnection */
|
||||
#endif
|
||||
/* tcp_transport extension */
|
||||
int esp_transport_sock_errno; /*!< errno from the underlying socket */
|
||||
|
||||
} esp_mqtt_error_codes_t;
|
||||
|
||||
/**
|
||||
* *MQTT* event configuration structure
|
||||
*/
|
||||
typedef struct esp_mqtt_event_t {
|
||||
esp_mqtt_event_id_t event_id; /*!< *MQTT* event type */
|
||||
esp_mqtt_client_handle_t client; /*!< *MQTT* client handle for this event */
|
||||
char *data; /*!< Data associated with this event */
|
||||
int data_len; /*!< Length of the data for this event */
|
||||
int total_data_len; /*!< Total length of the data (longer data are supplied
|
||||
with multiple events) */
|
||||
int current_data_offset; /*!< Actual offset for the data associated with this
|
||||
event */
|
||||
char *topic; /*!< Topic associated with this event */
|
||||
int topic_len; /*!< Length of the topic for this event associated with this
|
||||
event */
|
||||
int msg_id; /*!< *MQTT* messaged id of message */
|
||||
int session_present; /*!< *MQTT* session_present flag for connection event */
|
||||
esp_mqtt_error_codes_t
|
||||
*error_handle; /*!< esp-mqtt error handle including esp-tls errors as well
|
||||
as internal *MQTT* errors */
|
||||
bool retain; /*!< Retained flag of the message associated with this event */
|
||||
int qos; /*!< QoS of the messages associated with this event */
|
||||
bool dup; /*!< dup flag of the message associated with this event */
|
||||
esp_mqtt_protocol_ver_t protocol_ver; /*!< MQTT protocol version used for connection, defaults to value from menuconfig*/
|
||||
#ifdef CONFIG_MQTT_PROTOCOL_5
|
||||
esp_mqtt5_event_property_t *property; /*!< MQTT 5 property associated with this event */
|
||||
#endif
|
||||
|
||||
} esp_mqtt_event_t;
|
||||
|
||||
typedef esp_mqtt_event_t *esp_mqtt_event_handle_t;
|
||||
|
||||
/**
|
||||
* *MQTT* client configuration structure
|
||||
*
|
||||
* - Default values can be set via menuconfig
|
||||
* - All certificates and key data could be passed in PEM or DER format. PEM format must have a terminating NULL
|
||||
* character and the related len field set to 0. DER format requires a related len field set to the correct length.
|
||||
*/
|
||||
typedef struct esp_mqtt_client_config_t {
|
||||
/**
|
||||
* Broker related configuration
|
||||
*/
|
||||
struct broker_t {
|
||||
/**
|
||||
* Broker address
|
||||
*
|
||||
* - uri have precedence over other fields
|
||||
* - If uri isn't set at least hostname, transport and port should.
|
||||
*/
|
||||
struct address_t {
|
||||
const char *uri; /*!< Complete *MQTT* broker URI */
|
||||
const char *hostname; /*!< Hostname, to set ipv4 pass it as string) */
|
||||
esp_mqtt_transport_t transport; /*!< Selects transport*/
|
||||
const char *path; /*!< Path in the URI*/
|
||||
uint32_t port; /*!< *MQTT* server port */
|
||||
} address; /*!< Broker address configuration */
|
||||
/**
|
||||
* Broker identity verification
|
||||
*
|
||||
* If fields are not set broker's identity isn't verified. it's recommended
|
||||
* to set the options in this struct for security reasons.
|
||||
*/
|
||||
struct verification_t {
|
||||
bool use_global_ca_store; /*!< Use a global ca_store, look esp-tls
|
||||
documentation for details. */
|
||||
esp_err_t (*crt_bundle_attach)(void *conf); /*!< Pointer to ESP x509 Certificate Bundle attach function for
|
||||
the usage of certificate bundles. Client only attach the bundle, the clean up must be done by the user. */
|
||||
const char *certificate; /*!< Certificate data, default is NULL. It's not copied nor freed by the client, user needs to clean up.*/
|
||||
size_t certificate_len; /*!< Length of the buffer pointed to by certificate. */
|
||||
const struct psk_key_hint *psk_hint_key; /*!< Pointer to PSK struct defined in esp_tls.h to enable PSK
|
||||
authentication (as alternative to certificate verification).
|
||||
PSK is enabled only if there are no other ways to
|
||||
verify broker. It's not copied nor freed by the client, user needs to clean up.*/
|
||||
bool skip_cert_common_name_check; /*!< Skip any validation of server certificate CN field, this reduces the
|
||||
security of TLS and makes the *MQTT* client susceptible to MITM attacks */
|
||||
const char **alpn_protos; /*!< NULL-terminated list of supported application protocols to be used for ALPN.*/
|
||||
const char *common_name; /*!< Pointer to the string containing server certificate common name.
|
||||
If non-NULL, server certificate CN must match this name,
|
||||
If NULL, server certificate CN must match hostname.
|
||||
This is ignored if skip_cert_common_name_check=true.
|
||||
It's not copied nor freed by the client, user needs to clean up.*/
|
||||
} verification; /*!< Security verification of the broker */
|
||||
} broker; /*!< Broker address and security verification */
|
||||
/**
|
||||
* Client related credentials for authentication.
|
||||
*/
|
||||
struct credentials_t {
|
||||
const char *username; /*!< *MQTT* username */
|
||||
const char *client_id; /*!< Set *MQTT* client identifier. Ignored if set_null_client_id == true If NULL set
|
||||
the default client id. Default client id is ``ESP32_%CHIPID%`` where `%CHIPID%` are
|
||||
last 3 bytes of MAC address in hex format */
|
||||
bool set_null_client_id; /*!< Selects a NULL client id */
|
||||
/**
|
||||
* Client authentication
|
||||
*
|
||||
* Fields related to client authentication by broker
|
||||
*
|
||||
* For mutual authentication using TLS, user could select certificate and key,
|
||||
* secure element or digital signature peripheral if available.
|
||||
*
|
||||
*/
|
||||
struct authentication_t {
|
||||
const char *password; /*!< *MQTT* password */
|
||||
const char *certificate; /*!< Certificate for ssl mutual authentication, not required if mutual
|
||||
authentication is not needed. Must be provided with `key`. It's not copied nor freed by the client, user needs to clean up.*/
|
||||
size_t certificate_len; /*!< Length of the buffer pointed to by certificate.*/
|
||||
const char *key; /*!< Private key for SSL mutual authentication, not required if mutual authentication
|
||||
is not needed. If it is not NULL, also `certificate` has to be provided. It's not copied nor freed by the client, user needs to clean up.*/
|
||||
size_t key_len; /*!< Length of the buffer pointed to by key.*/
|
||||
const char *key_password; /*!< Client key decryption password, not PEM nor DER, if provided
|
||||
`key_password_len` must be correctly set.*/
|
||||
int key_password_len; /*!< Length of the password pointed to by `key_password` */
|
||||
bool use_secure_element; /*!< Enable secure element, available in ESP32-ROOM-32SE, for SSL connection */
|
||||
void *ds_data; /*!< Carrier of handle for digital signature parameters, digital signature peripheral is
|
||||
available in some Espressif devices. It's not copied nor freed by the client, user needs to clean up.*/
|
||||
bool use_ecdsa_peripheral; /*!< Enable ECDSA peripheral, available in some Espressif devices. */
|
||||
uint8_t ecdsa_key_efuse_blk; /*!< ECDSA key block number from efuse, available in some Espressif devices. */
|
||||
} authentication; /*!< Client authentication */
|
||||
} credentials; /*!< User credentials for broker */
|
||||
/**
|
||||
* *MQTT* Session related configuration
|
||||
*/
|
||||
struct session_t {
|
||||
/**
|
||||
* Last Will and Testament message configuration.
|
||||
*/
|
||||
struct last_will_t {
|
||||
const char *topic; /*!< LWT (Last Will and Testament) message topic */
|
||||
const char *msg; /*!< LWT message, may be NULL terminated*/
|
||||
int msg_len; /*!< LWT message length, if msg isn't NULL terminated must have the correct length */
|
||||
int qos; /*!< LWT message QoS */
|
||||
int retain; /*!< LWT retained message flag */
|
||||
} last_will; /*!< Last will configuration */
|
||||
bool disable_clean_session; /*!< *MQTT* clean session, default clean_session is true */
|
||||
int keepalive; /*!< *MQTT* keepalive, default is 120 seconds
|
||||
When configuring this value, keep in mind that the client attempts
|
||||
to communicate with the broker at half the interval that is actually set.
|
||||
This conservative approach allows for more attempts before the broker's timeout occurs */
|
||||
bool disable_keepalive; /*!< Set `disable_keepalive=true` to turn off keep-alive mechanism, keepalive is active
|
||||
by default. Note: setting the config value `keepalive` to `0` doesn't disable
|
||||
keepalive feature, but uses a default keepalive period */
|
||||
esp_mqtt_protocol_ver_t protocol_ver; /*!< *MQTT* protocol version used for connection.*/
|
||||
int message_retransmit_timeout; /*!< timeout for retransmitting of failed packet */
|
||||
} session; /*!< *MQTT* session configuration. */
|
||||
/**
|
||||
* Network related configuration
|
||||
*/
|
||||
struct network_t {
|
||||
int reconnect_timeout_ms; /*!< Reconnect to the broker after this value in miliseconds if auto reconnect is not
|
||||
disabled (defaults to 10s) */
|
||||
int timeout_ms; /*!< Abort network operation if it is not completed after this value, in milliseconds
|
||||
(defaults to 10s). */
|
||||
int refresh_connection_after_ms; /*!< Refresh connection after this value (in milliseconds) */
|
||||
bool disable_auto_reconnect; /*!< Client will reconnect to server (when errors/disconnect). Set
|
||||
`disable_auto_reconnect=true` to disable */
|
||||
esp_transport_keep_alive_t tcp_keep_alive_cfg; /*!< Transport keep-alive config*/
|
||||
esp_transport_handle_t transport; /*!< Custom transport handle to use, leave it NULL to allow MQTT client create or recreate its own. Warning: The transport should be valid during the client lifetime and is destroyed when esp_mqtt_client_destroy is called. */
|
||||
struct ifreq * if_name; /*!< The name of interface for data to go through. Use the default interface without setting */
|
||||
} network; /*!< Network configuration */
|
||||
/**
|
||||
* Client task configuration
|
||||
*/
|
||||
struct task_t {
|
||||
int priority; /*!< *MQTT* task priority*/
|
||||
int stack_size; /*!< *MQTT* task stack size*/
|
||||
} task; /*!< FreeRTOS task configuration.*/
|
||||
/**
|
||||
* Client buffer size configuration
|
||||
*
|
||||
* Client have two buffers for input and output respectivelly.
|
||||
*/
|
||||
struct buffer_t {
|
||||
int size; /*!< size of *MQTT* send/receive buffer*/
|
||||
int out_size; /*!< size of *MQTT* output buffer. If not defined, defaults to the size defined by
|
||||
``buffer_size`` */
|
||||
} buffer; /*!< Buffer size configuration.*/
|
||||
|
||||
/**
|
||||
* Client outbox configuration options.
|
||||
*/
|
||||
struct outbox_config_t {
|
||||
uint64_t limit; /*!< Size limit for the outbox in bytes.*/
|
||||
} outbox; /*!< Outbox configuration. */
|
||||
} esp_mqtt_client_config_t;
|
||||
|
||||
/**
|
||||
* Topic definition struct
|
||||
*/
|
||||
typedef struct topic_t {
|
||||
const char *filter; /*!< Topic filter to subscribe */
|
||||
int qos; /*!< Max QoS level of the subscription */
|
||||
} esp_mqtt_topic_t;
|
||||
|
||||
/**
|
||||
* @brief Creates *MQTT* client handle based on the configuration
|
||||
*
|
||||
* @param config *MQTT* configuration structure
|
||||
*
|
||||
* @return mqtt_client_handle if successfully created, NULL on error
|
||||
*/
|
||||
esp_mqtt_client_handle_t
|
||||
esp_mqtt_client_init(const esp_mqtt_client_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Sets *MQTT* connection URI. This API is usually used to overrides the
|
||||
* URI configured in esp_mqtt_client_init
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param uri
|
||||
*
|
||||
* @return ESP_FAIL if URI parse error, ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client,
|
||||
const char *uri);
|
||||
|
||||
/**
|
||||
* @brief Starts *MQTT* client with already created client handle
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_FAIL on other error
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_start(esp_mqtt_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief This api is typically used to force reconnection upon a specific event
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_FAIL if client is in invalid state
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_reconnect(esp_mqtt_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief This api is typically used to force disconnection from the broker
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_disconnect(esp_mqtt_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief Stops *MQTT* client tasks
|
||||
*
|
||||
* * Notes:
|
||||
* - Cannot be called from the *MQTT* event handler
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_FAIL if client is in invalid state
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_stop(esp_mqtt_client_handle_t client);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#define esp_mqtt_client_subscribe esp_mqtt_client_subscribe_single
|
||||
|
||||
#else
|
||||
/**
|
||||
* @brief Convenience macro to select subscribe function to use.
|
||||
*
|
||||
* Notes:
|
||||
* - Usage of `esp_mqtt_client_subscribe_single` is the same as previous
|
||||
* esp_mqtt_client_subscribe, refer to it for details.
|
||||
*
|
||||
* @param client_handle *MQTT* client handle
|
||||
* @param topic_type Needs to be char* for single subscription or `esp_mqtt_topic_t` for multiple topics
|
||||
* @param qos_or_size It's either a qos when subscribing to a single topic or the size of the subscription array when subscribing to multiple topics.
|
||||
*
|
||||
* @return message_id of the subscribe message on success
|
||||
* -1 on failure
|
||||
* -2 in case of full outbox.
|
||||
*/
|
||||
#define esp_mqtt_client_subscribe(client_handle, topic_type, qos_or_size) _Generic((topic_type), \
|
||||
char *: esp_mqtt_client_subscribe_single, \
|
||||
const char *: esp_mqtt_client_subscribe_single, \
|
||||
esp_mqtt_topic_t*: esp_mqtt_client_subscribe_multiple \
|
||||
)(client_handle, topic_type, qos_or_size)
|
||||
|
||||
#endif /* __cplusplus*/
|
||||
/**
|
||||
* @brief Subscribe the client to defined topic with defined qos
|
||||
*
|
||||
* Notes:
|
||||
* - Client must be connected to send subscribe message
|
||||
* - This API is could be executed from a user task or
|
||||
* from a *MQTT* event callback i.e. internal *MQTT* task
|
||||
* (API is protected by internal mutex, so it might block
|
||||
* if a longer data receive operation is in progress.
|
||||
* - `esp_mqtt_client_subscribe` could be used to call this function.
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param topic topic filter to subscribe
|
||||
* @param qos Max qos level of the subscription
|
||||
*
|
||||
* @return message_id of the subscribe message on success
|
||||
* -1 on failure
|
||||
* -2 in case of full outbox.
|
||||
*/
|
||||
int esp_mqtt_client_subscribe_single(esp_mqtt_client_handle_t client,
|
||||
const char *topic, int qos);
|
||||
/**
|
||||
* @brief Subscribe the client to a list of defined topics with defined qos
|
||||
*
|
||||
* Notes:
|
||||
* - Client must be connected to send subscribe message
|
||||
* - This API is could be executed from a user task or
|
||||
* from a *MQTT* event callback i.e. internal *MQTT* task
|
||||
* (API is protected by internal mutex, so it might block
|
||||
* if a longer data receive operation is in progress.
|
||||
* - `esp_mqtt_client_subscribe` could be used to call this function.
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param topic_list List of topics to subscribe
|
||||
* @param size size of topic_list
|
||||
*
|
||||
* @return message_id of the subscribe message on success
|
||||
* -1 on failure
|
||||
* -2 in case of full outbox.
|
||||
*/
|
||||
int esp_mqtt_client_subscribe_multiple(esp_mqtt_client_handle_t client,
|
||||
const esp_mqtt_topic_t *topic_list, int size);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe the client from defined topic
|
||||
*
|
||||
* Notes:
|
||||
* - Client must be connected to send unsubscribe message
|
||||
* - It is thread safe, please refer to `esp_mqtt_client_subscribe_single` for details
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param topic
|
||||
*
|
||||
* @return message_id of the subscribe message on success
|
||||
* -1 on failure
|
||||
*/
|
||||
int esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client,
|
||||
const char *topic);
|
||||
|
||||
/**
|
||||
* @brief Client to send a publish message to the broker
|
||||
*
|
||||
* Notes:
|
||||
* - This API might block for several seconds, either due to network timeout
|
||||
* (10s) or if publishing payloads longer than internal buffer (due to message
|
||||
* fragmentation)
|
||||
* - Client doesn't have to be connected for this API to work, enqueueing the
|
||||
* messages with qos>1 (returning -1 for all the qos=0 messages if
|
||||
* disconnected). If MQTT_SKIP_PUBLISH_IF_DISCONNECTED is enabled, this API will
|
||||
* not attempt to publish when the client is not connected and will always
|
||||
* return -1.
|
||||
* - It is thread safe, please refer to `esp_mqtt_client_subscribe` for details
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param topic topic string
|
||||
* @param data payload string (set to NULL, sending empty payload message)
|
||||
* @param len data length, if set to 0, length is calculated from payload
|
||||
* string
|
||||
* @param qos QoS of publish message
|
||||
* @param retain retain flag
|
||||
*
|
||||
* @return message_id of the publish message (for QoS 0 message_id will always
|
||||
* be zero) on success. -1 on failure, -2 in case of full outbox.
|
||||
*/
|
||||
int esp_mqtt_client_publish(esp_mqtt_client_handle_t client, const char *topic,
|
||||
const char *data, int len, int qos, int retain);
|
||||
|
||||
/**
|
||||
* @brief Enqueue a message to the outbox, to be sent later. Typically used for
|
||||
* messages with qos>0, but could be also used for qos=0 messages if store=true.
|
||||
*
|
||||
* This API generates and stores the publish message into the internal outbox
|
||||
* and the actual sending to the network is performed in the mqtt-task context
|
||||
* (in contrast to the esp_mqtt_client_publish() which sends the publish message
|
||||
* immediately in the user task's context). Thus, it could be used as a non
|
||||
* blocking version of esp_mqtt_client_publish().
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param topic topic string
|
||||
* @param data payload string (set to NULL, sending empty payload message)
|
||||
* @param len data length, if set to 0, length is calculated from payload
|
||||
* string
|
||||
* @param qos QoS of publish message
|
||||
* @param retain retain flag
|
||||
* @param store if true, all messages are enqueued; otherwise only QoS 1 and
|
||||
* QoS 2 are enqueued
|
||||
*
|
||||
* @return message_id if queued successfully, -1 on failure, -2 in case of full outbox.
|
||||
*/
|
||||
int esp_mqtt_client_enqueue(esp_mqtt_client_handle_t client, const char *topic,
|
||||
const char *data, int len, int qos, int retain,
|
||||
bool store);
|
||||
|
||||
/**
|
||||
* @brief Destroys the client handle
|
||||
*
|
||||
* Notes:
|
||||
* - Cannot be called from the *MQTT* event handler
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @return ESP_OK
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_destroy(esp_mqtt_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief Set configuration structure, typically used when updating the config
|
||||
* (i.e. on "before_connect" event
|
||||
*
|
||||
* Notes:
|
||||
* - When calling this function make sure to have all the intendend configurations
|
||||
* set, otherwise default values are set.
|
||||
* @param client *MQTT* client handle
|
||||
*
|
||||
* @param config *MQTT* configuration structure
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_ERR_INVALID_ARG if conflicts on transport configuration.
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client,
|
||||
const esp_mqtt_client_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Registers *MQTT* event
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param event event type
|
||||
* @param event_handler handler callback
|
||||
* @param event_handler_arg handlers context
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_ERR_INVALID_ARG on wrong initialization
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_register_event(esp_mqtt_client_handle_t client,
|
||||
esp_mqtt_event_id_t event,
|
||||
esp_event_handler_t event_handler,
|
||||
void *event_handler_arg);
|
||||
|
||||
/**
|
||||
* @brief Unregisters mqtt event
|
||||
*
|
||||
* @param client mqtt client handle
|
||||
* @param event event ID
|
||||
* @param event_handler handler to unregister
|
||||
*
|
||||
* @return ESP_ERR_NO_MEM if failed to allocate
|
||||
* ESP_ERR_INVALID_ARG on invalid event ID
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_mqtt_client_unregister_event(esp_mqtt_client_handle_t client, esp_mqtt_event_id_t event, esp_event_handler_t event_handler);
|
||||
|
||||
/**
|
||||
* @brief Get outbox size
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @return outbox size
|
||||
* 0 on wrong initialization
|
||||
*/
|
||||
int esp_mqtt_client_get_outbox_size(esp_mqtt_client_handle_t client);
|
||||
|
||||
/**
|
||||
* @brief Dispatch user event to the mqtt internal event loop
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param event *MQTT* event handle structure
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_TIMEOUT if the event couldn't be queued (ref also CONFIG_MQTT_EVENT_QUEUE_SIZE)
|
||||
*/
|
||||
esp_err_t esp_mqtt_dispatch_custom_event(esp_mqtt_client_handle_t client, esp_mqtt_event_t *event);
|
||||
|
||||
/**
|
||||
* @brief Get a transport from the scheme
|
||||
*
|
||||
* Allows extra settings to be made on the selected transport,
|
||||
* for convenience the scheme used by the mqtt client are defined as
|
||||
* MQTT_OVER_TCP_SCHEME, MQTT_OVER_SSL_SCHEME, MQTT_OVER_WS_SCHEME and MQTT_OVER_WSS_SCHEME
|
||||
* If the transport_scheme is NULL and the client was set with a custom transport the custom transport will be returned.
|
||||
*
|
||||
* Notes:
|
||||
* - This function should be called only on MQTT_EVENT_BEFORE_CONNECT.
|
||||
* - The intetion is to provide a way to set different configurations than the ones available from client config.
|
||||
* - If esp_mqtt_client_destroy is called the returned pointer will be invalidated.
|
||||
* - All the required settings should be made in the MQTT_EVENT_BEFORE_CONNECT event handler
|
||||
*
|
||||
* @param client *MQTT* client handle
|
||||
* @param transport_scheme Transport handle to search for.
|
||||
* @return the transport handle
|
||||
* NULL in case of error
|
||||
*
|
||||
*/
|
||||
esp_transport_handle_t esp_mqtt_client_get_transport(esp_mqtt_client_handle_t client, char *transport_scheme);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _MQTT_SUPPORTED_FEATURES_H_
|
||||
#define _MQTT_SUPPORTED_FEATURES_H_
|
||||
|
||||
#if __has_include("esp_idf_version.h")
|
||||
#include "esp_idf_version.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief This header defines supported features of IDF which mqtt module
|
||||
* could use depending on specific version of ESP-IDF.
|
||||
* In case "esp_idf_version.h" were not found, all additional
|
||||
* features would be disabled
|
||||
*/
|
||||
|
||||
#ifdef ESP_IDF_VERSION
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0)
|
||||
// Features supported from 3.3
|
||||
#define MQTT_SUPPORTED_FEATURE_EVENT_LOOP
|
||||
#define MQTT_SUPPORTED_FEATURE_SKIP_CRT_CMN_NAME_CHECK
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
|
||||
// Features supported in 4.0
|
||||
#define MQTT_SUPPORTED_FEATURE_WS_SUBPROTOCOL
|
||||
#define MQTT_SUPPORTED_FEATURE_TRANSPORT_ERR_REPORTING
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0)
|
||||
// Features supported in 4.1
|
||||
#define MQTT_SUPPORTED_FEATURE_PSK_AUTHENTICATION
|
||||
#define MQTT_SUPPORTED_FEATURE_DER_CERTIFICATES
|
||||
#define MQTT_SUPPORTED_FEATURE_ALPN
|
||||
#define MQTT_SUPPORTED_FEATURE_CLIENT_KEY_PASSWORD
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
|
||||
// Features supported in 4.2
|
||||
#define MQTT_SUPPORTED_FEATURE_SECURE_ELEMENT
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
// Features supported in 4.3
|
||||
#define MQTT_SUPPORTED_FEATURE_DIGITAL_SIGNATURE
|
||||
#define MQTT_SUPPORTED_FEATURE_TRANSPORT_SOCK_ERRNO_REPORTING
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
// Features supported in 4.4
|
||||
#define MQTT_SUPPORTED_FEATURE_CERTIFICATE_BUNDLE
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
// Features supported in 5.1.0
|
||||
#define MQTT_SUPPORTED_FEATURE_CRT_CMN_NAME
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
|
||||
// Features supported in 5.2.0
|
||||
#define MQTT_SUPPORTED_FEATURE_ECDSA_PERIPHERAL
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* ESP_IDF_VERSION */
|
||||
#endif // _MQTT_SUPPORTED_FEATURES_H_
|
||||
56
managed_components/esp_mqtt/lib/include/mqtt5_client_priv.h
Normal file
56
managed_components/esp_mqtt/lib/include/mqtt5_client_priv.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _MQTT5_CLIENT_PRIV_H_
|
||||
#define _MQTT5_CLIENT_PRIV_H_
|
||||
|
||||
#include "mqtt5_client.h"
|
||||
#include "mqtt_client_priv.h"
|
||||
#include "mqtt5_msg.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct mqtt5_topic_alias {
|
||||
char *topic;
|
||||
uint16_t topic_len;
|
||||
uint16_t topic_alias;
|
||||
STAILQ_ENTRY(mqtt5_topic_alias) next;
|
||||
} mqtt5_topic_alias_t;
|
||||
STAILQ_HEAD(mqtt5_topic_alias_list_t, mqtt5_topic_alias);
|
||||
typedef struct mqtt5_topic_alias_list_t *mqtt5_topic_alias_handle_t;
|
||||
typedef struct mqtt5_topic_alias *mqtt5_topic_alias_item_t;
|
||||
|
||||
typedef struct {
|
||||
esp_mqtt5_connection_property_storage_t connect_property_info;
|
||||
esp_mqtt5_connection_will_property_storage_t will_property_info;
|
||||
esp_mqtt5_connection_server_resp_property_t server_resp_property_info;
|
||||
esp_mqtt5_disconnect_property_config_t disconnect_property_info;
|
||||
const esp_mqtt5_publish_property_config_t *publish_property_info;
|
||||
const esp_mqtt5_subscribe_property_config_t *subscribe_property_info;
|
||||
const esp_mqtt5_unsubscribe_property_config_t *unsubscribe_property_info;
|
||||
mqtt5_topic_alias_handle_t peer_topic_alias;
|
||||
} mqtt5_config_storage_t;
|
||||
|
||||
void esp_mqtt5_increment_packet_counter(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_decrement_packet_counter(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_parse_pubcomp(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_parse_puback(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_parse_unsuback(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_parse_suback(esp_mqtt5_client_handle_t client);
|
||||
void esp_mqtt5_parse_disconnect(esp_mqtt5_client_handle_t client, int *disconnect_rsp_code);
|
||||
esp_err_t esp_mqtt5_parse_connack(esp_mqtt5_client_handle_t client, int *connect_rsp_code);
|
||||
void esp_mqtt5_client_destory(esp_mqtt5_client_handle_t client);
|
||||
esp_err_t esp_mqtt5_client_publish_check(esp_mqtt5_client_handle_t client, int qos, int retain);
|
||||
esp_err_t esp_mqtt5_client_subscribe_check(esp_mqtt5_client_handle_t client, int qos);
|
||||
esp_err_t esp_mqtt5_create_default_config(esp_mqtt5_client_handle_t client);
|
||||
esp_err_t esp_mqtt5_get_publish_data(esp_mqtt5_client_handle_t client, uint8_t *msg_buf, size_t msg_read_len, char **msg_topic, size_t *msg_topic_len, char **msg_data, size_t *msg_data_len);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
||||
142
managed_components/esp_mqtt/lib/include/mqtt5_msg.h
Normal file
142
managed_components/esp_mqtt/lib/include/mqtt5_msg.h
Normal file
@@ -0,0 +1,142 @@
|
||||
#ifndef MQTT5_MSG_H
|
||||
#define MQTT5_MSG_H
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "sys/queue.h"
|
||||
#include "mqtt_config.h"
|
||||
#include "mqtt_msg.h"
|
||||
#include "mqtt_client.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum mqtt_properties_type {
|
||||
MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR = 0x01,
|
||||
MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL = 0x02,
|
||||
MQTT5_PROPERTY_CONTENT_TYPE = 0x03,
|
||||
MQTT5_PROPERTY_RESPONSE_TOPIC = 0x08,
|
||||
MQTT5_PROPERTY_CORRELATION_DATA = 0x09,
|
||||
MQTT5_PROPERTY_SUBSCRIBE_IDENTIFIER = 0x0B,
|
||||
MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL = 0x11,
|
||||
MQTT5_PROPERTY_ASSIGNED_CLIENT_IDENTIFIER = 0x12,
|
||||
MQTT5_PROPERTY_SERVER_KEEP_ALIVE = 0x13,
|
||||
MQTT5_PROPERTY_AUTHENTICATION_METHOD = 0x15,
|
||||
MQTT5_PROPERTY_AUTHENTICATION_DATA = 0x16,
|
||||
MQTT5_PROPERTY_REQUEST_PROBLEM_INFO = 0x17,
|
||||
MQTT5_PROPERTY_WILL_DELAY_INTERVAL = 0x18,
|
||||
MQTT5_PROPERTY_REQUEST_RESP_INFO = 0x19,
|
||||
MQTT5_PROPERTY_RESP_INFO = 0x1A,
|
||||
MQTT5_PROPERTY_SERVER_REFERENCE = 0x1C,
|
||||
MQTT5_PROPERTY_REASON_STRING = 0x1F,
|
||||
MQTT5_PROPERTY_RECEIVE_MAXIMUM = 0x21,
|
||||
MQTT5_PROPERTY_TOPIC_ALIAS_MAXIMIM = 0x22,
|
||||
MQTT5_PROPERTY_TOPIC_ALIAS = 0x23,
|
||||
MQTT5_PROPERTY_MAXIMUM_QOS = 0x24,
|
||||
MQTT5_PROPERTY_RETAIN_AVAILABLE = 0x25,
|
||||
MQTT5_PROPERTY_USER_PROPERTY = 0x26,
|
||||
MQTT5_PROPERTY_MAXIMUM_PACKET_SIZE = 0x27,
|
||||
MQTT5_PROPERTY_WILDCARD_SUBSCR_AVAILABLE = 0x28,
|
||||
MQTT5_PROPERTY_SUBSCR_IDENTIFIER_AVAILABLE = 0x29,
|
||||
MQTT5_PROPERTY_SHARED_SUBSCR_AVAILABLE = 0x2A,
|
||||
};
|
||||
|
||||
typedef struct mqtt5_user_property {
|
||||
char *key;
|
||||
char *value;
|
||||
STAILQ_ENTRY(mqtt5_user_property) next;
|
||||
} mqtt5_user_property_t;
|
||||
STAILQ_HEAD(mqtt5_user_property_list_t, mqtt5_user_property);
|
||||
typedef struct mqtt5_user_property *mqtt5_user_property_item_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t maximum_packet_size;
|
||||
uint16_t receive_maximum;
|
||||
uint16_t topic_alias_maximum;
|
||||
uint8_t max_qos;
|
||||
bool retain_available;
|
||||
bool wildcard_subscribe_available;
|
||||
bool subscribe_identifiers_available;
|
||||
bool shared_subscribe_available;
|
||||
char *response_info;
|
||||
} esp_mqtt5_connection_server_resp_property_t;
|
||||
|
||||
typedef struct {
|
||||
bool payload_format_indicator;
|
||||
uint32_t message_expiry_interval;
|
||||
uint16_t topic_alias;
|
||||
char *response_topic;
|
||||
int response_topic_len;
|
||||
char *correlation_data;
|
||||
uint16_t correlation_data_len;
|
||||
char *content_type;
|
||||
int content_type_len;
|
||||
uint16_t subscribe_id;
|
||||
} esp_mqtt5_publish_resp_property_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t session_expiry_interval;
|
||||
uint32_t maximum_packet_size;
|
||||
uint16_t receive_maximum;
|
||||
uint16_t topic_alias_maximum;
|
||||
bool request_resp_info;
|
||||
bool request_problem_info;
|
||||
mqtt5_user_property_handle_t user_property;
|
||||
} esp_mqtt5_connection_property_storage_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t will_delay_interval;
|
||||
uint32_t message_expiry_interval;
|
||||
bool payload_format_indicator;
|
||||
char *content_type;
|
||||
char *response_topic;
|
||||
char *correlation_data;
|
||||
uint16_t correlation_data_len;
|
||||
mqtt5_user_property_handle_t user_property;
|
||||
} esp_mqtt5_connection_will_property_storage_t;
|
||||
|
||||
#define mqtt5_get_type mqtt_get_type
|
||||
|
||||
#define mqtt5_get_dup mqtt_get_dup
|
||||
|
||||
#define mqtt5_set_dup mqtt_set_dup
|
||||
|
||||
#define mqtt5_get_qos mqtt_get_qos
|
||||
|
||||
#define mqtt5_get_retain mqtt_get_retain
|
||||
|
||||
#define mqtt5_msg_init mqtt_msg_init
|
||||
|
||||
#define mqtt5_get_total_length mqtt_get_total_length
|
||||
|
||||
#define mqtt5_has_valid_msg_hdr mqtt_has_valid_msg_hdr
|
||||
|
||||
#define mqtt5_msg_pingreq mqtt_msg_pingreq
|
||||
|
||||
#define mqtt5_msg_pingresp mqtt_msg_pingresp
|
||||
|
||||
#define mqtt5_get_unsuback_data mqtt5_get_suback_data
|
||||
|
||||
#define mqtt5_get_pubcomp_data mqtt5_get_puback_data
|
||||
|
||||
uint16_t mqtt5_get_id(uint8_t *buffer, size_t length);
|
||||
char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length, char **msg_topic, size_t *msg_topic_len, esp_mqtt5_publish_resp_property_t *resp_property, uint16_t *property_len, size_t *payload_len, mqtt5_user_property_handle_t *user_property);
|
||||
char *mqtt5_get_suback_data(uint8_t *buffer, size_t *length, mqtt5_user_property_handle_t *user_property);
|
||||
char *mqtt5_get_puback_data(uint8_t *buffer, size_t *length, mqtt5_user_property_handle_t *user_property);
|
||||
mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info, esp_mqtt5_connection_property_storage_t *property, esp_mqtt5_connection_will_property_storage_t *will_property);
|
||||
mqtt_message_t *mqtt5_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, int qos, int retain, uint16_t *message_id, const esp_mqtt5_publish_property_config_t *property, const char *resp_info);
|
||||
esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, mqtt_connect_info_t *connection_info, esp_mqtt5_connection_property_storage_t *connection_property, esp_mqtt5_connection_server_resp_property_t *resp_property, int *reason_code, uint8_t *ack_flag, mqtt5_user_property_handle_t *user_property);
|
||||
int mqtt5_msg_get_reason_code(uint8_t *buffer, size_t length);
|
||||
mqtt_message_t *mqtt5_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t *topic, int size, uint16_t *message_id, const esp_mqtt5_subscribe_property_config_t *property);
|
||||
mqtt_message_t *mqtt5_msg_unsubscribe(mqtt_connection_t *connection, const char *topic, uint16_t *message_id, const esp_mqtt5_unsubscribe_property_config_t *property);
|
||||
mqtt_message_t *mqtt5_msg_disconnect(mqtt_connection_t *connection, esp_mqtt5_disconnect_property_config_t *disconnect_property_info);
|
||||
mqtt_message_t *mqtt5_msg_pubcomp(mqtt_connection_t *connection, uint16_t message_id);
|
||||
mqtt_message_t *mqtt5_msg_pubrel(mqtt_connection_t *connection, uint16_t message_id);
|
||||
mqtt_message_t *mqtt5_msg_pubrec(mqtt_connection_t *connection, uint16_t message_id);
|
||||
mqtt_message_t *mqtt5_msg_puback(mqtt_connection_t *connection, uint16_t message_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MQTT5_MSG_H */
|
||||
|
||||
149
managed_components/esp_mqtt/lib/include/mqtt_client_priv.h
Normal file
149
managed_components/esp_mqtt/lib/include/mqtt_client_priv.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _MQTT_CLIENT_PRIV_H_
|
||||
#define _MQTT_CLIENT_PRIV_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdatomic.h>
|
||||
#include "esp_err.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "mqtt_msg.h"
|
||||
#ifdef MQTT_PROTOCOL_5
|
||||
#include "mqtt5_client_priv.h"
|
||||
#endif
|
||||
#include "esp_transport.h"
|
||||
#include "esp_transport_tcp.h"
|
||||
#include "esp_transport_ssl.h"
|
||||
#include "esp_transport_ws.h"
|
||||
#include "esp_log.h"
|
||||
#include "mqtt_outbox.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mqtt_supported_features.h"
|
||||
|
||||
/* using uri parser */
|
||||
#include "http_parser.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_NEWLIB_NANO_FORMAT
|
||||
#define NEWLIB_NANO_COMPAT_FORMAT PRIu32
|
||||
#define NEWLIB_NANO_COMPAT_CAST(size_t_var) (uint32_t)size_t_var
|
||||
#else
|
||||
#define NEWLIB_NANO_COMPAT_FORMAT "zu"
|
||||
#define NEWLIB_NANO_COMPAT_CAST(size_t_var) size_t_var
|
||||
#endif
|
||||
|
||||
#ifdef MQTT_DISABLE_API_LOCKS
|
||||
# define MQTT_API_LOCK(c)
|
||||
# define MQTT_API_UNLOCK(c)
|
||||
#else
|
||||
# define MQTT_API_LOCK(c) xSemaphoreTakeRecursive(c->api_lock, portMAX_DELAY)
|
||||
# define MQTT_API_UNLOCK(c) xSemaphoreGiveRecursive(c->api_lock)
|
||||
#endif /* MQTT_USE_API_LOCKS */
|
||||
|
||||
typedef struct mqtt_state {
|
||||
uint8_t *in_buffer;
|
||||
int in_buffer_length;
|
||||
size_t message_length;
|
||||
size_t in_buffer_read_len;
|
||||
mqtt_connection_t connection;
|
||||
uint16_t pending_msg_id;
|
||||
int pending_msg_type;
|
||||
int pending_publish_qos;
|
||||
} mqtt_state_t;
|
||||
|
||||
typedef struct {
|
||||
esp_event_loop_handle_t event_loop_handle;
|
||||
int task_stack;
|
||||
int task_prio;
|
||||
char *uri;
|
||||
char *host;
|
||||
char *path;
|
||||
char *scheme;
|
||||
int port;
|
||||
bool auto_reconnect;
|
||||
int network_timeout_ms;
|
||||
int refresh_connection_after_ms;
|
||||
int reconnect_timeout_ms;
|
||||
char **alpn_protos;
|
||||
int num_alpn_protos;
|
||||
char *clientkey_password;
|
||||
int clientkey_password_len;
|
||||
bool use_global_ca_store;
|
||||
esp_err_t ((*crt_bundle_attach)(void *conf));
|
||||
const char *cacert_buf;
|
||||
size_t cacert_bytes;
|
||||
const char *clientcert_buf;
|
||||
size_t clientcert_bytes;
|
||||
const char *clientkey_buf;
|
||||
size_t clientkey_bytes;
|
||||
const struct psk_key_hint *psk_hint_key;
|
||||
bool skip_cert_common_name_check;
|
||||
const char *common_name;
|
||||
bool use_secure_element;
|
||||
void *ds_data;
|
||||
bool use_ecdsa_peripheral;
|
||||
uint8_t ecdsa_key_efuse_blk;
|
||||
int message_retransmit_timeout;
|
||||
uint64_t outbox_limit;
|
||||
esp_transport_handle_t transport;
|
||||
struct ifreq * if_name;
|
||||
esp_transport_keep_alive_t tcp_keep_alive_cfg;
|
||||
} mqtt_config_storage_t;
|
||||
|
||||
typedef enum {
|
||||
MQTT_STATE_INIT = 0,
|
||||
MQTT_STATE_DISCONNECTED,
|
||||
MQTT_STATE_CONNECTED,
|
||||
MQTT_STATE_WAIT_RECONNECT,
|
||||
} mqtt_client_state_t;
|
||||
|
||||
struct esp_mqtt_client {
|
||||
esp_transport_list_handle_t transport_list;
|
||||
esp_transport_handle_t transport;
|
||||
mqtt_config_storage_t *config;
|
||||
mqtt_state_t mqtt_state;
|
||||
_Atomic mqtt_client_state_t state;
|
||||
uint64_t refresh_connection_tick;
|
||||
int64_t keepalive_tick;
|
||||
uint64_t reconnect_tick;
|
||||
#ifdef MQTT_PROTOCOL_5
|
||||
mqtt5_config_storage_t *mqtt5_config;
|
||||
uint16_t send_publish_packet_count; // This is for MQTT v5.0 flow control
|
||||
#endif
|
||||
int wait_timeout_ms;
|
||||
int auto_reconnect;
|
||||
esp_mqtt_event_t event;
|
||||
bool run;
|
||||
bool wait_for_ping_resp;
|
||||
outbox_handle_t outbox;
|
||||
EventGroupHandle_t status_bits;
|
||||
SemaphoreHandle_t api_lock;
|
||||
TaskHandle_t task_handle;
|
||||
#if MQTT_EVENT_QUEUE_SIZE > 1
|
||||
atomic_int queued_events;
|
||||
#endif
|
||||
};
|
||||
|
||||
bool esp_mqtt_set_if_config(char const *const new_config, char **old_config);
|
||||
void esp_mqtt_destroy_config(esp_mqtt_client_handle_t client);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
||||
118
managed_components/esp_mqtt/lib/include/mqtt_config.h
Normal file
118
managed_components/esp_mqtt/lib/include/mqtt_config.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE', which is part of this source code package.
|
||||
* Tuan PM <tuanpm at live dot com>
|
||||
*/
|
||||
#ifndef _MQTT_CONFIG_H_
|
||||
#define _MQTT_CONFIG_H_
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef CONFIG_MQTT_PROTOCOL_311
|
||||
#define MQTT_PROTOCOL_311
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MQTT_PROTOCOL_5
|
||||
#define MQTT_PROTOCOL_5
|
||||
#endif
|
||||
|
||||
#define MQTT_RECON_DEFAULT_MS (10*1000)
|
||||
|
||||
#ifdef CONFIG_MQTT_POLL_READ_TIMEOUT_MS
|
||||
#define MQTT_POLL_READ_TIMEOUT_MS CONFIG_MQTT_POLL_READ_TIMEOUT_MS
|
||||
#else
|
||||
#define MQTT_POLL_READ_TIMEOUT_MS (1000)
|
||||
#endif
|
||||
|
||||
#define MQTT_MSG_ID_INCREMENTAL CONFIG_MQTT_MSG_ID_INCREMENTAL
|
||||
|
||||
#define MQTT_SKIP_PUBLISH_IF_DISCONNECTED CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED
|
||||
|
||||
#define MQTT_REPORT_DELETED_MESSAGES CONFIG_MQTT_REPORT_DELETED_MESSAGES
|
||||
|
||||
#if CONFIG_MQTT_BUFFER_SIZE
|
||||
#define MQTT_BUFFER_SIZE_BYTE CONFIG_MQTT_BUFFER_SIZE
|
||||
#else
|
||||
#define MQTT_BUFFER_SIZE_BYTE 1024
|
||||
#endif
|
||||
|
||||
#if CONFIG_MQTT_TASK_PRIORITY
|
||||
#define MQTT_TASK_PRIORITY CONFIG_MQTT_TASK_PRIORITY
|
||||
#else
|
||||
#define MQTT_TASK_PRIORITY 5
|
||||
#endif
|
||||
|
||||
#if CONFIG_MQTT_TASK_STACK_SIZE
|
||||
#define MQTT_TASK_STACK CONFIG_MQTT_TASK_STACK_SIZE
|
||||
#else
|
||||
#define MQTT_TASK_STACK (6*1024)
|
||||
#endif
|
||||
|
||||
#define MQTT_KEEPALIVE_TICK (120)
|
||||
#define MQTT_NETWORK_TIMEOUT_MS (10000)
|
||||
|
||||
#ifdef CONFIG_MQTT_TCP_DEFAULT_PORT
|
||||
#define MQTT_TCP_DEFAULT_PORT CONFIG_MQTT_TCP_DEFAULT_PORT
|
||||
#else
|
||||
#define MQTT_TCP_DEFAULT_PORT 1883
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MQTT_SSL_DEFAULT_PORT
|
||||
#define MQTT_SSL_DEFAULT_PORT CONFIG_MQTT_SSL_DEFAULT_PORT
|
||||
#else
|
||||
#define MQTT_SSL_DEFAULT_PORT 8883
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MQTT_WS_DEFAULT_PORT
|
||||
#define MQTT_WS_DEFAULT_PORT CONFIG_MQTT_WS_DEFAULT_PORT
|
||||
#else
|
||||
#define MQTT_WS_DEFAULT_PORT 80
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MQTT_WSS_DEFAULT_PORT
|
||||
#define MQTT_WSS_DEFAULT_PORT CONFIG_MQTT_WSS_DEFAULT_PORT
|
||||
#else
|
||||
#define MQTT_WSS_DEFAULT_PORT 443
|
||||
#endif
|
||||
|
||||
#define MQTT_CORE_SELECTION_ENABLED CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED
|
||||
|
||||
#ifdef CONFIG_MQTT_DISABLE_API_LOCKS
|
||||
#define MQTT_DISABLE_API_LOCKS CONFIG_MQTT_DISABLE_API_LOCKS
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MQTT_USE_CORE_0
|
||||
#define MQTT_TASK_CORE 0
|
||||
#else
|
||||
#ifdef CONFIG_MQTT_USE_CORE_1
|
||||
#define MQTT_TASK_CORE 1
|
||||
#else
|
||||
#define MQTT_TASK_CORE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MQTT_OUTBOX_EXPIRED_TIMEOUT_MS
|
||||
#define OUTBOX_EXPIRED_TIMEOUT_MS CONFIG_MQTT_OUTBOX_EXPIRED_TIMEOUT_MS
|
||||
#else
|
||||
#define OUTBOX_EXPIRED_TIMEOUT_MS (30*1000)
|
||||
#endif
|
||||
|
||||
#define MQTT_ENABLE_SSL CONFIG_MQTT_TRANSPORT_SSL
|
||||
#define MQTT_ENABLE_WS CONFIG_MQTT_TRANSPORT_WEBSOCKET
|
||||
#define MQTT_ENABLE_WSS CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE
|
||||
#define MQTT_DEFAULT_RETRANSMIT_TIMEOUT_MS 1000
|
||||
|
||||
#ifdef CONFIG_MQTT_EVENT_QUEUE_SIZE
|
||||
#define MQTT_EVENT_QUEUE_SIZE CONFIG_MQTT_EVENT_QUEUE_SIZE
|
||||
#else
|
||||
#define MQTT_EVENT_QUEUE_SIZE 1
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MQTT_OUTBOX_DATA_ON_EXTERNAL_MEMORY
|
||||
#define MQTT_OUTBOX_MEMORY MALLOC_CAP_SPIRAM
|
||||
#else
|
||||
#define MQTT_OUTBOX_MEMORY MALLOC_CAP_DEFAULT
|
||||
#endif
|
||||
|
||||
#define OUTBOX_MAX_SIZE (4*1024)
|
||||
#endif
|
||||
152
managed_components/esp_mqtt/lib/include/mqtt_msg.h
Normal file
152
managed_components/esp_mqtt/lib/include/mqtt_msg.h
Normal file
@@ -0,0 +1,152 @@
|
||||
#ifndef MQTT_MSG_H
|
||||
#define MQTT_MSG_H
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "mqtt_config.h"
|
||||
#include "mqtt_client.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014, Stephen Robinson
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/* 7 6 5 4 3 2 1 0 */
|
||||
/*| --- Message Type---- | DUP Flag | QoS Level | Retain | */
|
||||
/* Remaining Length */
|
||||
|
||||
|
||||
enum mqtt_message_type {
|
||||
MQTT_MSG_TYPE_CONNECT = 1,
|
||||
MQTT_MSG_TYPE_CONNACK = 2,
|
||||
MQTT_MSG_TYPE_PUBLISH = 3,
|
||||
MQTT_MSG_TYPE_PUBACK = 4,
|
||||
MQTT_MSG_TYPE_PUBREC = 5,
|
||||
MQTT_MSG_TYPE_PUBREL = 6,
|
||||
MQTT_MSG_TYPE_PUBCOMP = 7,
|
||||
MQTT_MSG_TYPE_SUBSCRIBE = 8,
|
||||
MQTT_MSG_TYPE_SUBACK = 9,
|
||||
MQTT_MSG_TYPE_UNSUBSCRIBE = 10,
|
||||
MQTT_MSG_TYPE_UNSUBACK = 11,
|
||||
MQTT_MSG_TYPE_PINGREQ = 12,
|
||||
MQTT_MSG_TYPE_PINGRESP = 13,
|
||||
MQTT_MSG_TYPE_DISCONNECT = 14
|
||||
};
|
||||
|
||||
typedef struct mqtt_message {
|
||||
uint8_t *data;
|
||||
size_t length;
|
||||
size_t fragmented_msg_total_length; /*!< total len of fragmented messages (zero for all other messages) */
|
||||
size_t fragmented_msg_data_offset; /*!< data offset of fragmented messages (zero for all other messages) */
|
||||
} mqtt_message_t;
|
||||
|
||||
typedef struct mqtt_connect_info {
|
||||
char *client_id;
|
||||
char *username;
|
||||
char *password;
|
||||
char *will_topic;
|
||||
char *will_message;
|
||||
int64_t keepalive; /*!< keepalive=0 -> keepalive is disabled */
|
||||
int will_length;
|
||||
int will_qos;
|
||||
int will_retain;
|
||||
int clean_session;
|
||||
esp_mqtt_protocol_ver_t protocol_ver;
|
||||
} mqtt_connect_info_t;
|
||||
|
||||
typedef struct mqtt_connection {
|
||||
mqtt_message_t outbound_message;
|
||||
#if MQTT_MSG_ID_INCREMENTAL
|
||||
uint16_t last_message_id; /*!< last used id if incremental message id configured */
|
||||
#endif
|
||||
uint8_t *buffer;
|
||||
size_t buffer_length;
|
||||
mqtt_connect_info_t information;
|
||||
|
||||
} mqtt_connection_t;
|
||||
|
||||
static inline int mqtt_get_type(const uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] & 0xf0) >> 4;
|
||||
}
|
||||
static inline int mqtt_get_connect_session_present(const uint8_t *buffer)
|
||||
{
|
||||
return buffer[2] & 0x01;
|
||||
}
|
||||
static inline int mqtt_get_connect_return_code(const uint8_t *buffer)
|
||||
{
|
||||
return buffer[3];
|
||||
}
|
||||
static inline int mqtt_get_dup(const uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] & 0x08) >> 3;
|
||||
}
|
||||
static inline void mqtt_set_dup(uint8_t *buffer)
|
||||
{
|
||||
buffer[0] |= 0x08;
|
||||
}
|
||||
static inline int mqtt_get_qos(const uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] & 0x06) >> 1;
|
||||
}
|
||||
static inline int mqtt_get_retain(const uint8_t *buffer)
|
||||
{
|
||||
return (buffer[0] & 0x01);
|
||||
}
|
||||
|
||||
bool mqtt_header_complete(uint8_t *buffer, size_t buffer_length);
|
||||
size_t mqtt_get_total_length(const uint8_t *buffer, size_t length, int *fixed_size_len);
|
||||
char *mqtt_get_publish_topic(uint8_t *buffer, size_t *length);
|
||||
char *mqtt_get_publish_data(uint8_t *buffer, size_t *length);
|
||||
char *mqtt_get_suback_data(uint8_t *buffer, size_t *length);
|
||||
uint16_t mqtt_get_id(uint8_t *buffer, size_t length);
|
||||
int mqtt_has_valid_msg_hdr(uint8_t *buffer, size_t length);
|
||||
|
||||
esp_err_t mqtt_msg_buffer_init(mqtt_connection_t *connection, int buffer_size);
|
||||
void mqtt_msg_buffer_destroy(mqtt_connection_t *connection);
|
||||
|
||||
mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info);
|
||||
mqtt_message_t *mqtt_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, int qos, int retain, uint16_t *message_id);
|
||||
mqtt_message_t *mqtt_msg_puback(mqtt_connection_t *connection, uint16_t message_id);
|
||||
mqtt_message_t *mqtt_msg_pubrec(mqtt_connection_t *connection, uint16_t message_id);
|
||||
mqtt_message_t *mqtt_msg_pubrel(mqtt_connection_t *connection, uint16_t message_id);
|
||||
mqtt_message_t *mqtt_msg_pubcomp(mqtt_connection_t *connection, uint16_t message_id);
|
||||
mqtt_message_t *mqtt_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t topic_list[], int size, uint16_t *message_id) __attribute__((nonnull));
|
||||
mqtt_message_t *mqtt_msg_unsubscribe(mqtt_connection_t *connection, const char *topic, uint16_t *message_id);
|
||||
mqtt_message_t *mqtt_msg_pingreq(mqtt_connection_t *connection);
|
||||
mqtt_message_t *mqtt_msg_pingresp(mqtt_connection_t *connection);
|
||||
mqtt_message_t *mqtt_msg_disconnect(mqtt_connection_t *connection);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MQTT_MSG_H */
|
||||
|
||||
64
managed_components/esp_mqtt/lib/include/mqtt_outbox.h
Normal file
64
managed_components/esp_mqtt/lib/include/mqtt_outbox.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE', which is part of this source code package.
|
||||
* Tuan PM <tuanpm at live dot com>
|
||||
*/
|
||||
#ifndef _MQTT_OUTOBX_H_
|
||||
#define _MQTT_OUTOBX_H_
|
||||
#include "platform.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct outbox_item;
|
||||
|
||||
typedef struct outbox_t *outbox_handle_t;
|
||||
typedef struct outbox_item *outbox_item_handle_t;
|
||||
typedef struct outbox_message *outbox_message_handle_t;
|
||||
typedef long long outbox_tick_t;
|
||||
|
||||
typedef struct outbox_message {
|
||||
uint8_t *data;
|
||||
int len;
|
||||
int msg_id;
|
||||
int msg_qos;
|
||||
int msg_type;
|
||||
uint8_t *remaining_data;
|
||||
int remaining_len;
|
||||
} outbox_message_t;
|
||||
|
||||
typedef enum pending_state {
|
||||
QUEUED,
|
||||
TRANSMITTED,
|
||||
ACKNOWLEDGED,
|
||||
CONFIRMED
|
||||
} pending_state_t;
|
||||
|
||||
outbox_handle_t outbox_init(void);
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick);
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick);
|
||||
outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id);
|
||||
uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos);
|
||||
esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type);
|
||||
esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item);
|
||||
int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout);
|
||||
/**
|
||||
* @brief Deletes single expired message returning it's message id
|
||||
*
|
||||
* @return msg id of the deleted message, -1 if no expired message in the outbox
|
||||
*/
|
||||
int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout);
|
||||
|
||||
esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending);
|
||||
pending_state_t outbox_item_get_pending(outbox_item_handle_t item);
|
||||
esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick);
|
||||
uint64_t outbox_get_size(outbox_handle_t outbox);
|
||||
void outbox_destroy(outbox_handle_t outbox);
|
||||
void outbox_delete_all_items(outbox_handle_t outbox);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
14
managed_components/esp_mqtt/lib/include/platform.h
Normal file
14
managed_components/esp_mqtt/lib/include/platform.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE', which is part of this source code package.
|
||||
* Tuan PM <tuanpm at live dot com>
|
||||
*/
|
||||
#ifndef _PLATFORM_H__
|
||||
#define _PLATFORM_H__
|
||||
|
||||
//Support ESP32
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "platform_esp32_idf.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
29
managed_components/esp_mqtt/lib/include/platform_esp32_idf.h
Normal file
29
managed_components/esp_mqtt/lib/include/platform_esp32_idf.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE', which is part of this source code package.
|
||||
* Tuan PM <tuanpm at live dot com>
|
||||
*/
|
||||
#ifndef _ESP_PLATFORM_H__
|
||||
#define _ESP_PLATFORM_H__
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
char *platform_create_id_string(void);
|
||||
int platform_random(int max);
|
||||
uint64_t platform_tick_get_ms(void);
|
||||
|
||||
#define ESP_MEM_CHECK(TAG, a, action) if (!(a)) { \
|
||||
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, "Memory exhausted"); \
|
||||
action; \
|
||||
}
|
||||
|
||||
#define ESP_OK_CHECK(TAG, a, action) if ((a) != ESP_OK) { \
|
||||
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, "Failed"); \
|
||||
action; \
|
||||
}
|
||||
|
||||
#endif
|
||||
1047
managed_components/esp_mqtt/lib/mqtt5_msg.c
Normal file
1047
managed_components/esp_mqtt/lib/mqtt5_msg.c
Normal file
File diff suppressed because it is too large
Load Diff
637
managed_components/esp_mqtt/lib/mqtt_msg.c
Normal file
637
managed_components/esp_mqtt/lib/mqtt_msg.c
Normal file
@@ -0,0 +1,637 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Stephen Robinson
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "mqtt_client.h"
|
||||
#include "mqtt_msg.h"
|
||||
#include "mqtt_config.h"
|
||||
#include "platform.h"
|
||||
|
||||
#define MQTT_MAX_FIXED_HEADER_SIZE 5
|
||||
#define MQTT_3_1_VARIABLE_HEADER_SIZE 12
|
||||
#define MQTT_3_1_1_VARIABLE_HEADER_SIZE 10
|
||||
|
||||
enum mqtt_connect_flag {
|
||||
MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
|
||||
MQTT_CONNECT_FLAG_PASSWORD = 1 << 6,
|
||||
MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5,
|
||||
MQTT_CONNECT_FLAG_WILL = 1 << 2,
|
||||
MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
|
||||
};
|
||||
|
||||
static int append_string(mqtt_connection_t *connection, const char *string, int len)
|
||||
{
|
||||
if (connection->outbound_message.length + len + 2 > connection->buffer_length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
connection->buffer[connection->outbound_message.length++] = len >> 8;
|
||||
connection->buffer[connection->outbound_message.length++] = len & 0xff;
|
||||
memcpy(connection->buffer + connection->outbound_message.length, string, len);
|
||||
connection->outbound_message.length += len;
|
||||
|
||||
return len + 2;
|
||||
}
|
||||
|
||||
static uint16_t append_message_id(mqtt_connection_t *connection, uint16_t message_id)
|
||||
{
|
||||
// If message_id is zero then we should assign one, otherwise
|
||||
// we'll use the one supplied by the caller
|
||||
while (message_id == 0) {
|
||||
#if MQTT_MSG_ID_INCREMENTAL
|
||||
message_id = ++connection->last_message_id;
|
||||
#else
|
||||
message_id = platform_random(65535);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (connection->outbound_message.length + 2 > connection->buffer_length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
connection->buffer[connection->outbound_message.length++] = message_id >> 8;
|
||||
connection->buffer[connection->outbound_message.length++] = message_id & 0xff;
|
||||
|
||||
return message_id;
|
||||
}
|
||||
|
||||
static int set_message_header_size(mqtt_connection_t *connection)
|
||||
{
|
||||
connection->outbound_message.length = MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
return MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
}
|
||||
|
||||
static mqtt_message_t *fail_message(mqtt_connection_t *connection)
|
||||
{
|
||||
connection->outbound_message.data = connection->buffer;
|
||||
connection->outbound_message.length = 0;
|
||||
return &connection->outbound_message;
|
||||
}
|
||||
|
||||
static mqtt_message_t *fini_message(mqtt_connection_t *connection, int type, int dup, int qos, int retain)
|
||||
{
|
||||
int message_length = connection->outbound_message.length - MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
int total_length = message_length;
|
||||
int encoded_length = 0;
|
||||
uint8_t encoded_lens[4] = {0};
|
||||
// Check if we have fragmented message and update total_len
|
||||
if (connection->outbound_message.fragmented_msg_total_length) {
|
||||
total_length = connection->outbound_message.fragmented_msg_total_length - MQTT_MAX_FIXED_HEADER_SIZE;
|
||||
}
|
||||
|
||||
// Encode MQTT message length
|
||||
int len_bytes = 0; // size of encoded message length
|
||||
do {
|
||||
encoded_length = total_length % 128;
|
||||
total_length /= 128;
|
||||
if (total_length > 0) {
|
||||
encoded_length |= 0x80;
|
||||
}
|
||||
encoded_lens[len_bytes] = encoded_length;
|
||||
len_bytes++;
|
||||
} while (total_length > 0);
|
||||
|
||||
// Sanity check for MQTT header
|
||||
if (len_bytes + 1 > MQTT_MAX_FIXED_HEADER_SIZE) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
// Save the header bytes
|
||||
connection->outbound_message.length = message_length + len_bytes + 1; // msg len + encoded_size len + type (1 byte)
|
||||
int offs = MQTT_MAX_FIXED_HEADER_SIZE - 1 - len_bytes;
|
||||
connection->outbound_message.data = connection->buffer + offs;
|
||||
connection->outbound_message.fragmented_msg_data_offset -= offs;
|
||||
// type byte
|
||||
connection->buffer[offs++] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1);
|
||||
// length bytes
|
||||
for (int j = 0; j < len_bytes; j++) {
|
||||
connection->buffer[offs++] = encoded_lens[j];
|
||||
}
|
||||
|
||||
return &connection->outbound_message;
|
||||
}
|
||||
|
||||
size_t mqtt_get_total_length(const uint8_t *buffer, size_t length, int *fixed_size_len)
|
||||
{
|
||||
int i;
|
||||
size_t totlen = 0;
|
||||
|
||||
for (i = 1; i < length; ++i) {
|
||||
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
|
||||
if ((buffer[i] & 0x80) == 0) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
totlen += i;
|
||||
if (fixed_size_len) {
|
||||
*fixed_size_len = i;
|
||||
}
|
||||
|
||||
return totlen;
|
||||
}
|
||||
|
||||
bool mqtt_header_complete(uint8_t *buffer, size_t buffer_length)
|
||||
{
|
||||
uint16_t i;
|
||||
uint16_t topiclen;
|
||||
|
||||
for (i = 1; i < MQTT_MAX_FIXED_HEADER_SIZE; ++i) {
|
||||
if (i >= buffer_length) {
|
||||
return false;
|
||||
}
|
||||
if ((buffer[i] & 0x80) == 0) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// i is now the length of the fixed header
|
||||
|
||||
if (i + 2 >= buffer_length) {
|
||||
return false;
|
||||
}
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
i += topiclen;
|
||||
|
||||
if (mqtt_get_qos(buffer) > 0) {
|
||||
i += 2;
|
||||
}
|
||||
// i is now the length of the fixed + variable header
|
||||
return buffer_length >= i;
|
||||
}
|
||||
|
||||
char *mqtt_get_publish_topic(uint8_t *buffer, size_t *length)
|
||||
{
|
||||
int i;
|
||||
int topiclen;
|
||||
|
||||
for (i = 1; i < *length; ++i) {
|
||||
if ((buffer[i] & 0x80) == 0) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i + 2 >= *length) {
|
||||
return NULL;
|
||||
}
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if (i + topiclen > *length) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*length = topiclen;
|
||||
return (char *)(buffer + i);
|
||||
}
|
||||
|
||||
char *mqtt_get_publish_data(uint8_t *buffer, size_t *length)
|
||||
{
|
||||
int i;
|
||||
int totlen = 0;
|
||||
int topiclen;
|
||||
int blength = *length;
|
||||
*length = 0;
|
||||
|
||||
for (i = 1; i < blength; ++i) {
|
||||
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
|
||||
if ((buffer[i] & 0x80) == 0) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
totlen += i;
|
||||
|
||||
if (i + 2 >= blength) {
|
||||
return NULL;
|
||||
}
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if (i + topiclen >= blength) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i += topiclen;
|
||||
|
||||
if (mqtt_get_qos(buffer) > 0) {
|
||||
if (i + 2 >= blength) {
|
||||
return NULL;
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
|
||||
if (totlen < i) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (totlen <= blength) {
|
||||
*length = totlen - i;
|
||||
} else {
|
||||
*length = blength - i;
|
||||
}
|
||||
return (char *)(buffer + i);
|
||||
}
|
||||
|
||||
char *mqtt_get_suback_data(uint8_t *buffer, size_t *length)
|
||||
{
|
||||
// SUBACK payload length = total length - (fixed header (2 bytes) + variable header (2 bytes))
|
||||
// This requires the remaining length to be encoded in 1 byte.
|
||||
if (*length > 4) {
|
||||
*length -= 4;
|
||||
return (char *)(buffer + 4);
|
||||
}
|
||||
*length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t mqtt_get_id(uint8_t *buffer, size_t length)
|
||||
{
|
||||
if (length < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (mqtt_get_type(buffer)) {
|
||||
case MQTT_MSG_TYPE_PUBLISH: {
|
||||
int i;
|
||||
int topiclen;
|
||||
|
||||
for (i = 1; i < length; ++i) {
|
||||
if ((buffer[i] & 0x80) == 0) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i + 2 >= length) {
|
||||
return 0;
|
||||
}
|
||||
topiclen = buffer[i++] << 8;
|
||||
topiclen |= buffer[i++];
|
||||
|
||||
if (i + topiclen > length) {
|
||||
return 0;
|
||||
}
|
||||
i += topiclen;
|
||||
|
||||
if (mqtt_get_qos(buffer) > 0) {
|
||||
if (i + 2 > length) {
|
||||
return 0;
|
||||
}
|
||||
//i += 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (buffer[i] << 8) | buffer[i + 1];
|
||||
}
|
||||
case MQTT_MSG_TYPE_PUBACK:
|
||||
case MQTT_MSG_TYPE_PUBREC:
|
||||
case MQTT_MSG_TYPE_PUBREL:
|
||||
case MQTT_MSG_TYPE_PUBCOMP:
|
||||
case MQTT_MSG_TYPE_SUBACK:
|
||||
case MQTT_MSG_TYPE_UNSUBACK:
|
||||
case MQTT_MSG_TYPE_SUBSCRIBE:
|
||||
case MQTT_MSG_TYPE_UNSUBSCRIBE: {
|
||||
// This requires the remaining length to be encoded in 1 byte,
|
||||
// which it should be.
|
||||
if (length >= 4 && (buffer[1] & 0x80) == 0) {
|
||||
return (buffer[2] << 8) | buffer[3];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info)
|
||||
{
|
||||
|
||||
set_message_header_size(connection);
|
||||
|
||||
int header_len;
|
||||
if (info->protocol_ver == MQTT_PROTOCOL_V_3_1) {
|
||||
header_len = MQTT_3_1_VARIABLE_HEADER_SIZE;
|
||||
} else {
|
||||
header_len = MQTT_3_1_1_VARIABLE_HEADER_SIZE;
|
||||
}
|
||||
|
||||
if (connection->outbound_message.length + header_len > connection->buffer_length) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
char *variable_header = (char *)(connection->buffer + connection->outbound_message.length);
|
||||
connection->outbound_message.length += header_len;
|
||||
|
||||
int header_idx = 0;
|
||||
variable_header[header_idx++] = 0; // Variable header length MSB
|
||||
|
||||
if (info->protocol_ver == MQTT_PROTOCOL_V_3_1) {
|
||||
variable_header[header_idx++] = 6; // Variable header length LSB
|
||||
memcpy(&variable_header[header_idx], "MQIsdp", 6); // Protocol name
|
||||
header_idx = header_idx + 6;
|
||||
variable_header[header_idx++] = 3; // Protocol version
|
||||
} else {
|
||||
/* Defaults to protocol version 3.1.1 values */
|
||||
variable_header[header_idx++] = 4; // Variable header length LSB
|
||||
memcpy(&variable_header[header_idx], "MQTT", 4); // Protocol name
|
||||
header_idx = header_idx + 4;
|
||||
variable_header[header_idx++] = 4; // Protocol version
|
||||
}
|
||||
|
||||
int flags_offset = header_idx;
|
||||
variable_header[header_idx++] = 0; // Flags
|
||||
variable_header[header_idx++] = info->keepalive >> 8; // Keep-alive MSB
|
||||
variable_header[header_idx] = info->keepalive & 0xff; // Keep-alive LSB
|
||||
|
||||
if (info->clean_session) {
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
|
||||
}
|
||||
|
||||
if (info->client_id != NULL && info->client_id[0] != '\0') {
|
||||
if (append_string(connection, info->client_id, strlen(info->client_id)) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
} else {
|
||||
if (append_string(connection, "", 0) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
}
|
||||
|
||||
if (info->will_topic != NULL && info->will_topic[0] != '\0') {
|
||||
if (append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (append_string(connection, info->will_message, info->will_length) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_WILL;
|
||||
if (info->will_retain) {
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_WILL_RETAIN;
|
||||
}
|
||||
variable_header[flags_offset] |= (info->will_qos & 3) << 3;
|
||||
}
|
||||
|
||||
if (info->username != NULL && info->username[0] != '\0') {
|
||||
if (append_string(connection, info->username, strlen(info->username)) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_USERNAME;
|
||||
}
|
||||
|
||||
if (info->password != NULL && info->password[0] != '\0') {
|
||||
if (info->username == NULL || info->username[0] == '\0') {
|
||||
/* In case if password is set without username, we need to set a zero length username.
|
||||
* (otherwise we violate: MQTT-3.1.2-22: If the User Name Flag is set to 0 then the Password Flag MUST be set to 0.)
|
||||
*/
|
||||
if (append_string(connection, "", 0) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_USERNAME;
|
||||
}
|
||||
|
||||
if (append_string(connection, info->password, strlen(info->password)) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
variable_header[flags_offset] |= MQTT_CONNECT_FLAG_PASSWORD;
|
||||
}
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, int qos, int retain, uint16_t *message_id)
|
||||
{
|
||||
set_message_header_size(connection);
|
||||
|
||||
if (topic == NULL || topic[0] == '\0') {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (append_string(connection, topic, strlen(topic)) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (data == NULL && data_length > 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (qos > 0) {
|
||||
if ((*message_id = append_message_id(connection, 0)) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
} else {
|
||||
*message_id = 0;
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
if (connection->outbound_message.length + data_length > connection->buffer_length) {
|
||||
// Not enough size in buffer -> fragment this message
|
||||
connection->outbound_message.fragmented_msg_data_offset = connection->outbound_message.length;
|
||||
memcpy(connection->buffer + connection->outbound_message.length, data, connection->buffer_length - connection->outbound_message.length);
|
||||
connection->outbound_message.length = connection->buffer_length;
|
||||
connection->outbound_message.fragmented_msg_total_length = data_length + connection->outbound_message.fragmented_msg_data_offset;
|
||||
} else {
|
||||
memcpy(connection->buffer + connection->outbound_message.length, data, data_length);
|
||||
connection->outbound_message.length += data_length;
|
||||
connection->outbound_message.fragmented_msg_total_length = 0;
|
||||
}
|
||||
}
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_puback(mqtt_connection_t *connection, uint16_t message_id)
|
||||
{
|
||||
set_message_header_size(connection);
|
||||
if (append_message_id(connection, message_id) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_pubrec(mqtt_connection_t *connection, uint16_t message_id)
|
||||
{
|
||||
set_message_header_size(connection);
|
||||
if (append_message_id(connection, message_id) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_pubrel(mqtt_connection_t *connection, uint16_t message_id)
|
||||
{
|
||||
set_message_header_size(connection);
|
||||
if (append_message_id(connection, message_id) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_pubcomp(mqtt_connection_t *connection, uint16_t message_id)
|
||||
{
|
||||
set_message_header_size(connection);
|
||||
if (append_message_id(connection, message_id) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_subscribe(mqtt_connection_t *connection, const esp_mqtt_topic_t topic_list[], int size, uint16_t *message_id)
|
||||
{
|
||||
set_message_header_size(connection);
|
||||
|
||||
if ((*message_id = append_message_id(connection, 0)) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
for (int topic_number = 0; topic_number < size; ++topic_number) {
|
||||
if (topic_list[topic_number].filter[0] == '\0') {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (append_string(connection, topic_list[topic_number].filter, strlen(topic_list[topic_number].filter)) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (connection->outbound_message.length + 1 > connection->buffer_length) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
connection->buffer[connection->outbound_message.length] = topic_list[topic_number].qos;
|
||||
connection->outbound_message.length ++;
|
||||
}
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_unsubscribe(mqtt_connection_t *connection, const char *topic, uint16_t *message_id)
|
||||
{
|
||||
set_message_header_size(connection);
|
||||
|
||||
if (topic == NULL || topic[0] == '\0') {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if ((*message_id = append_message_id(connection, 0)) == 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
if (append_string(connection, topic, strlen(topic)) < 0) {
|
||||
return fail_message(connection);
|
||||
}
|
||||
|
||||
return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_pingreq(mqtt_connection_t *connection)
|
||||
{
|
||||
set_message_header_size(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_pingresp(mqtt_connection_t *connection)
|
||||
{
|
||||
set_message_header_size(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0);
|
||||
}
|
||||
|
||||
mqtt_message_t *mqtt_msg_disconnect(mqtt_connection_t *connection)
|
||||
{
|
||||
set_message_header_size(connection);
|
||||
return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* check flags: [MQTT-2.2.2-1], [MQTT-2.2.2-2]
|
||||
* returns 0 if flags are invalid, otherwise returns 1
|
||||
*/
|
||||
int mqtt_has_valid_msg_hdr(uint8_t *buffer, size_t length)
|
||||
{
|
||||
int qos, dup;
|
||||
|
||||
if (length < 1) {
|
||||
return 0;
|
||||
}
|
||||
switch (mqtt_get_type(buffer)) {
|
||||
case MQTT_MSG_TYPE_CONNECT:
|
||||
case MQTT_MSG_TYPE_CONNACK:
|
||||
case MQTT_MSG_TYPE_PUBACK:
|
||||
case MQTT_MSG_TYPE_PUBREC:
|
||||
case MQTT_MSG_TYPE_PUBCOMP:
|
||||
case MQTT_MSG_TYPE_SUBACK:
|
||||
case MQTT_MSG_TYPE_UNSUBACK:
|
||||
case MQTT_MSG_TYPE_PINGREQ:
|
||||
case MQTT_MSG_TYPE_PINGRESP:
|
||||
case MQTT_MSG_TYPE_DISCONNECT:
|
||||
return (buffer[0] & 0x0f) == 0; /* all flag bits are 0 */
|
||||
case MQTT_MSG_TYPE_PUBREL:
|
||||
case MQTT_MSG_TYPE_SUBSCRIBE:
|
||||
case MQTT_MSG_TYPE_UNSUBSCRIBE:
|
||||
return (buffer[0] & 0x0f) == 0x02; /* only bit 1 is set */
|
||||
case MQTT_MSG_TYPE_PUBLISH:
|
||||
qos = mqtt_get_qos(buffer);
|
||||
dup = mqtt_get_dup(buffer);
|
||||
/*
|
||||
* there is no qos=3 [MQTT-3.3.1-4]
|
||||
* dup flag must be set to 0 for all qos=0 messages [MQTT-3.3.1-2]
|
||||
*/
|
||||
return (qos < 3) && ((qos > 0) || (dup == 0));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t mqtt_msg_buffer_init(mqtt_connection_t *connection, int buffer_size)
|
||||
{
|
||||
memset(&connection->outbound_message, 0, sizeof(mqtt_message_t));
|
||||
connection->buffer = (uint8_t *)calloc(buffer_size, sizeof(uint8_t));
|
||||
if (!connection->buffer) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
connection->buffer_length = buffer_size;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void mqtt_msg_buffer_destroy(mqtt_connection_t *connection)
|
||||
{
|
||||
if (connection) {
|
||||
free(connection->buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
221
managed_components/esp_mqtt/lib/mqtt_outbox.c
Normal file
221
managed_components/esp_mqtt/lib/mqtt_outbox.c
Normal file
@@ -0,0 +1,221 @@
|
||||
#include "mqtt_outbox.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "mqtt_config.h"
|
||||
#include "sys/queue.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifndef CONFIG_MQTT_CUSTOM_OUTBOX
|
||||
static const char *TAG = "outbox";
|
||||
|
||||
typedef struct outbox_item {
|
||||
char *buffer;
|
||||
int len;
|
||||
int msg_id;
|
||||
int msg_type;
|
||||
int msg_qos;
|
||||
outbox_tick_t tick;
|
||||
pending_state_t pending;
|
||||
STAILQ_ENTRY(outbox_item) next;
|
||||
} outbox_item_t;
|
||||
|
||||
STAILQ_HEAD(outbox_list_t, outbox_item);
|
||||
|
||||
struct outbox_t {
|
||||
_Atomic uint64_t size;
|
||||
struct outbox_list_t *list;
|
||||
};
|
||||
|
||||
outbox_handle_t outbox_init(void)
|
||||
{
|
||||
outbox_handle_t outbox = calloc(1, sizeof(struct outbox_t));
|
||||
ESP_MEM_CHECK(TAG, outbox, return NULL);
|
||||
outbox->list = calloc(1, sizeof(struct outbox_list_t));
|
||||
ESP_MEM_CHECK(TAG, outbox->list, {free(outbox); return NULL;});
|
||||
outbox->size = 0;
|
||||
STAILQ_INIT(outbox->list);
|
||||
return outbox;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick)
|
||||
{
|
||||
outbox_item_handle_t item = calloc(1, sizeof(outbox_item_t));
|
||||
ESP_MEM_CHECK(TAG, item, return NULL);
|
||||
item->msg_id = message->msg_id;
|
||||
item->msg_type = message->msg_type;
|
||||
item->msg_qos = message->msg_qos;
|
||||
item->tick = tick;
|
||||
item->len = message->len + message->remaining_len;
|
||||
item->pending = QUEUED;
|
||||
item->buffer = heap_caps_malloc(message->len + message->remaining_len, MQTT_OUTBOX_MEMORY);
|
||||
ESP_MEM_CHECK(TAG, item->buffer, {
|
||||
free(item);
|
||||
return NULL;
|
||||
});
|
||||
memcpy(item->buffer, message->data, message->len);
|
||||
if (message->remaining_data) {
|
||||
memcpy(item->buffer + message->len, message->remaining_data, message->remaining_len);
|
||||
}
|
||||
STAILQ_INSERT_TAIL(outbox->list, item, next);
|
||||
outbox->size += item->len;
|
||||
ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%"PRIu64, message->msg_id, message->msg_type, message->len + message->remaining_len, outbox_get_size(outbox));
|
||||
return item;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox->list, next) {
|
||||
if (item->msg_id == msg_id) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox->list, next) {
|
||||
if (item->pending == pending) {
|
||||
if (tick) {
|
||||
*tick = item->tick;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item_to_delete)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox->list, next) {
|
||||
if (item == item_to_delete) {
|
||||
STAILQ_REMOVE(outbox->list, item, outbox_item, next);
|
||||
outbox->size -= item->len;
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos)
|
||||
{
|
||||
if (item) {
|
||||
*len = item->len;
|
||||
*msg_id = item->msg_id;
|
||||
*msg_type = item->msg_type;
|
||||
*qos = item->msg_qos;
|
||||
return (uint8_t *)item->buffer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox->list, next, tmp) {
|
||||
if (item->msg_id == msg_id && (0xFF & (item->msg_type)) == msg_type) {
|
||||
STAILQ_REMOVE(outbox->list, item, outbox_item, next);
|
||||
outbox->size -= item->len;
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
ESP_LOGD(TAG, "DELETED msgid=%d, msg_type=%d, remain size=%"PRIu64, msg_id, msg_type, outbox_get_size(outbox));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending)
|
||||
{
|
||||
outbox_item_handle_t item = outbox_get(outbox, msg_id);
|
||||
if (item) {
|
||||
item->pending = pending;
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
pending_state_t outbox_item_get_pending(outbox_item_handle_t item)
|
||||
{
|
||||
if (item) {
|
||||
return item->pending;
|
||||
}
|
||||
return QUEUED;
|
||||
}
|
||||
|
||||
esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick)
|
||||
{
|
||||
outbox_item_handle_t item = outbox_get(outbox, msg_id);
|
||||
if (item) {
|
||||
item->tick = tick;
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
int msg_id = -1;
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox->list, next) {
|
||||
if (current_tick - item->tick > timeout) {
|
||||
STAILQ_REMOVE(outbox->list, item, outbox_item, next);
|
||||
free(item->buffer);
|
||||
outbox->size -= item->len;
|
||||
msg_id = item->msg_id;
|
||||
free(item);
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
}
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
int deleted_items = 0;
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox->list, next, tmp) {
|
||||
if (current_tick - item->tick > timeout) {
|
||||
STAILQ_REMOVE(outbox->list, item, outbox_item, next);
|
||||
free(item->buffer);
|
||||
outbox->size -= item->len;
|
||||
free(item);
|
||||
deleted_items ++;
|
||||
}
|
||||
|
||||
}
|
||||
return deleted_items;
|
||||
}
|
||||
|
||||
uint64_t outbox_get_size(outbox_handle_t outbox)
|
||||
{
|
||||
return outbox->size;
|
||||
}
|
||||
|
||||
void outbox_delete_all_items(outbox_handle_t outbox)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox->list, next, tmp) {
|
||||
STAILQ_REMOVE(outbox->list, item, outbox_item, next);
|
||||
outbox->size -= item->len;
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
}
|
||||
}
|
||||
void outbox_destroy(outbox_handle_t outbox)
|
||||
{
|
||||
outbox_delete_all_items(outbox);
|
||||
free(outbox->list);
|
||||
free(outbox);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MQTT_CUSTOM_OUTBOX */
|
||||
48
managed_components/esp_mqtt/lib/platform_esp32_idf.c
Normal file
48
managed_components/esp_mqtt/lib/platform_esp32_idf.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "platform.h"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "esp_log.h"
|
||||
#include "esp_mac.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_random.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const char *TAG = "platform";
|
||||
|
||||
#define MAX_ID_STRING (32)
|
||||
|
||||
#if defined SOC_WIFI_SUPPORTED
|
||||
#define MAC_TYPE ESP_MAC_WIFI_STA
|
||||
#elif defined SOC_EMAC_SUPPORTED
|
||||
#define MAC_TYPE ESP_MAC_ETH
|
||||
#elif defined SOC_IEEE802154_SUPPORTED
|
||||
#define MAC_TYPE ESP_MAC_IEEE802154
|
||||
#endif
|
||||
char *platform_create_id_string(void)
|
||||
{
|
||||
char *id_string = calloc(1, MAX_ID_STRING);
|
||||
ESP_MEM_CHECK(TAG, id_string, return NULL);
|
||||
#ifndef MAC_TYPE
|
||||
ESP_LOGW(TAG, "Soc doesn't provide MAC, client could be disconnected in case of device with same name in the broker.");
|
||||
sprintf(id_string, "esp_mqtt_client_id");
|
||||
#else
|
||||
uint8_t mac[6];
|
||||
esp_read_mac(mac, MAC_TYPE);
|
||||
sprintf(id_string, "ESP32_%02x%02X%02X", mac[3], mac[4], mac[5]);
|
||||
#endif
|
||||
return id_string;
|
||||
}
|
||||
|
||||
int platform_random(int max)
|
||||
{
|
||||
return esp_random() % max;
|
||||
}
|
||||
|
||||
uint64_t platform_tick_get_ms(void)
|
||||
{
|
||||
return esp_timer_get_time()/(int64_t)1000;
|
||||
}
|
||||
|
||||
#endif
|
||||
768
managed_components/esp_mqtt/mqtt5_client.c
Normal file
768
managed_components/esp_mqtt/mqtt5_client.c
Normal file
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "mqtt_client_priv.h"
|
||||
#include "esp_log.h"
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "mqtt5_client";
|
||||
|
||||
static void esp_mqtt5_print_error_code(esp_mqtt5_client_handle_t client, int code);
|
||||
static esp_err_t esp_mqtt5_client_update_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, char *topic, size_t topic_len);
|
||||
static char *esp_mqtt5_client_get_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, size_t *topic_length);
|
||||
static void esp_mqtt5_client_delete_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle);
|
||||
static esp_err_t esp_mqtt5_user_property_copy(mqtt5_user_property_handle_t user_property_new, const mqtt5_user_property_handle_t user_property_old);
|
||||
|
||||
void esp_mqtt5_increment_packet_counter(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
bool msg_dup = mqtt5_get_dup(client->mqtt_state.connection.outbound_message.data);
|
||||
if (msg_dup == false) {
|
||||
client->send_publish_packet_count ++;
|
||||
ESP_LOGD(TAG, "Sent (%d) qos > 0 publish packet without ack", client->send_publish_packet_count);
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_decrement_packet_counter(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->send_publish_packet_count > 0) {
|
||||
client->send_publish_packet_count --;
|
||||
ESP_LOGD(TAG, "Receive (%d) qos > 0 publish packet with ack", client->send_publish_packet_count);
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_parse_pubcomp(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGD(TAG, "MQTT_MSG_TYPE_PUBCOMP return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len));
|
||||
size_t msg_data_len = client->mqtt_state.in_buffer_read_len;
|
||||
client->event.data = mqtt5_get_pubcomp_data(client->mqtt_state.in_buffer, &msg_data_len, &client->event.property->user_property);
|
||||
client->event.data_len = msg_data_len;
|
||||
client->event.total_data_len = msg_data_len;
|
||||
client->event.current_data_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_parse_puback(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGD(TAG, "MQTT_MSG_TYPE_PUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len));
|
||||
size_t msg_data_len = client->mqtt_state.in_buffer_read_len;
|
||||
client->event.data = mqtt5_get_puback_data(client->mqtt_state.in_buffer, &msg_data_len, &client->event.property->user_property);
|
||||
client->event.data_len = msg_data_len;
|
||||
client->event.total_data_len = msg_data_len;
|
||||
client->event.current_data_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_parse_unsuback(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGD(TAG, "MQTT_MSG_TYPE_UNSUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len));
|
||||
size_t msg_data_len = client->mqtt_state.in_buffer_read_len;
|
||||
client->event.data = mqtt5_get_unsuback_data(client->mqtt_state.in_buffer, &msg_data_len, &client->event.property->user_property);
|
||||
client->event.data_len = msg_data_len;
|
||||
client->event.total_data_len = msg_data_len;
|
||||
client->event.current_data_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_parse_suback(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGD(TAG, "MQTT_MSG_TYPE_SUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len));
|
||||
}
|
||||
}
|
||||
|
||||
void esp_mqtt5_parse_disconnect(esp_mqtt5_client_handle_t client, int *disconnect_rsp_code)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
*disconnect_rsp_code = mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len);
|
||||
ESP_LOGD(TAG, "MQTT_MSG_TYPE_DISCONNECT return code is %d", *disconnect_rsp_code);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_parse_connack(esp_mqtt5_client_handle_t client, int *connect_rsp_code)
|
||||
{
|
||||
size_t len = client->mqtt_state.in_buffer_read_len;
|
||||
client->mqtt_state.in_buffer_read_len = 0;
|
||||
uint8_t ack_flag = 0;
|
||||
if (mqtt5_msg_parse_connack_property(client->mqtt_state.in_buffer, len, &client->mqtt_state.
|
||||
connection.information, &client->mqtt5_config->connect_property_info, &client->mqtt5_config->server_resp_property_info, connect_rsp_code, &ack_flag, &client->event.property->user_property) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to parse CONNACK packet");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (*connect_rsp_code == MQTT_CONNECTION_ACCEPTED) {
|
||||
ESP_LOGD(TAG, "Connected");
|
||||
client->event.session_present = ack_flag & 0x01;
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_mqtt5_print_error_code(client, *connect_rsp_code);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_get_publish_data(esp_mqtt5_client_handle_t client, uint8_t *msg_buf, size_t msg_read_len, char **msg_topic, size_t *msg_topic_len, char **msg_data, size_t *msg_data_len)
|
||||
{
|
||||
// get property
|
||||
uint16_t property_len = 0;
|
||||
esp_mqtt5_publish_resp_property_t property = {0};
|
||||
*msg_data = mqtt5_get_publish_property_payload(msg_buf, msg_read_len, msg_topic, msg_topic_len, &property, &property_len, msg_data_len, &client->event.property->user_property);
|
||||
if (*msg_data == NULL) {
|
||||
ESP_LOGE(TAG, "%s: mqtt5_get_publish_property_payload() failed", __func__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (property.topic_alias > client->mqtt5_config->connect_property_info.topic_alias_maximum) {
|
||||
ESP_LOGE(TAG, "%s: Broker response topic alias %d is over the max topic alias %d", __func__, property.topic_alias, client->mqtt5_config->connect_property_info.topic_alias_maximum);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (property.topic_alias) {
|
||||
if (*msg_topic_len == 0) {
|
||||
*msg_topic = esp_mqtt5_client_get_topic_alias(client->mqtt5_config->peer_topic_alias, property.topic_alias, msg_topic_len);
|
||||
if (!*msg_topic) {
|
||||
ESP_LOGE(TAG, "%s: esp_mqtt5_client_get_topic_alias() failed", __func__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
} else {
|
||||
if (esp_mqtt5_client_update_topic_alias(client->mqtt5_config->peer_topic_alias, property.topic_alias, *msg_topic, *msg_topic_len) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: esp_mqtt5_client_update_topic_alias() failed", __func__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client->event.property->payload_format_indicator = property.payload_format_indicator;
|
||||
client->event.property->response_topic = property.response_topic;
|
||||
client->event.property->response_topic_len = property.response_topic_len;
|
||||
client->event.property->correlation_data = property.correlation_data;
|
||||
client->event.property->correlation_data_len = property.correlation_data_len;
|
||||
client->event.property->content_type = property.content_type;
|
||||
client->event.property->content_type_len = property.content_type_len;
|
||||
client->event.property->subscribe_id = property.subscribe_id;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_create_default_config(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
client->event.property = calloc(1, sizeof(esp_mqtt5_event_property_t));
|
||||
ESP_MEM_CHECK(TAG, client->event.property, return ESP_FAIL)
|
||||
client->mqtt5_config = calloc(1, sizeof(mqtt5_config_storage_t));
|
||||
ESP_MEM_CHECK(TAG, client->mqtt5_config, return ESP_FAIL)
|
||||
client->mqtt5_config->server_resp_property_info.max_qos = 2;
|
||||
client->mqtt5_config->server_resp_property_info.retain_available = true;
|
||||
client->mqtt5_config->server_resp_property_info.wildcard_subscribe_available = true;
|
||||
client->mqtt5_config->server_resp_property_info.subscribe_identifiers_available = true;
|
||||
client->mqtt5_config->server_resp_property_info.shared_subscribe_available = true;
|
||||
client->mqtt5_config->server_resp_property_info.receive_maximum = 65535;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void esp_mqtt5_print_error_code(esp_mqtt5_client_handle_t client, int code)
|
||||
{
|
||||
switch (code) {
|
||||
case MQTT5_UNSPECIFIED_ERROR:
|
||||
ESP_LOGW(TAG, "Unspecified error");
|
||||
break;
|
||||
case MQTT5_MALFORMED_PACKET:
|
||||
ESP_LOGW(TAG, "Malformed Packet");
|
||||
break;
|
||||
case MQTT5_PROTOCOL_ERROR:
|
||||
ESP_LOGW(TAG, "Protocol Error");
|
||||
break;
|
||||
case MQTT5_IMPLEMENT_SPECIFIC_ERROR:
|
||||
ESP_LOGW(TAG, "Implementation specific error");
|
||||
break;
|
||||
case MQTT5_UNSUPPORTED_PROTOCOL_VER:
|
||||
ESP_LOGW(TAG, "Unsupported Protocol Version");
|
||||
break;
|
||||
case MQTT5_INVALID_CLIENT_ID:
|
||||
ESP_LOGW(TAG, "Client Identifier not valid");
|
||||
break;
|
||||
case MQTT5_BAD_USERNAME_OR_PWD:
|
||||
ESP_LOGW(TAG, "Bad User Name or Password");
|
||||
break;
|
||||
case MQTT5_NOT_AUTHORIZED:
|
||||
ESP_LOGW(TAG, "Not authorized");
|
||||
break;
|
||||
case MQTT5_SERVER_UNAVAILABLE:
|
||||
ESP_LOGW(TAG, "Server unavailable");
|
||||
break;
|
||||
case MQTT5_SERVER_BUSY:
|
||||
ESP_LOGW(TAG, "Server busy");
|
||||
break;
|
||||
case MQTT5_BANNED:
|
||||
ESP_LOGW(TAG, "Banned");
|
||||
break;
|
||||
case MQTT5_SERVER_SHUTTING_DOWN:
|
||||
ESP_LOGW(TAG, "Server shutting down");
|
||||
break;
|
||||
case MQTT5_BAD_AUTH_METHOD:
|
||||
ESP_LOGW(TAG, "Bad authentication method");
|
||||
break;
|
||||
case MQTT5_KEEP_ALIVE_TIMEOUT:
|
||||
ESP_LOGW(TAG, "Keep Alive timeout");
|
||||
break;
|
||||
case MQTT5_SESSION_TAKEN_OVER:
|
||||
ESP_LOGW(TAG, "Session taken over");
|
||||
break;
|
||||
case MQTT5_TOPIC_FILTER_INVALID:
|
||||
ESP_LOGW(TAG, "Topic Filter invalid");
|
||||
break;
|
||||
case MQTT5_TOPIC_NAME_INVALID:
|
||||
ESP_LOGW(TAG, "Topic Name invalid");
|
||||
break;
|
||||
case MQTT5_PACKET_IDENTIFIER_IN_USE:
|
||||
ESP_LOGW(TAG, "Packet Identifier in use");
|
||||
break;
|
||||
case MQTT5_PACKET_IDENTIFIER_NOT_FOUND:
|
||||
ESP_LOGW(TAG, "Packet Identifier not found");
|
||||
break;
|
||||
case MQTT5_RECEIVE_MAXIMUM_EXCEEDED:
|
||||
ESP_LOGW(TAG, "Receive Maximum exceeded");
|
||||
break;
|
||||
case MQTT5_TOPIC_ALIAS_INVALID:
|
||||
ESP_LOGW(TAG, "Topic Alias invalid");
|
||||
break;
|
||||
case MQTT5_PACKET_TOO_LARGE:
|
||||
ESP_LOGW(TAG, "Packet too large");
|
||||
break;
|
||||
case MQTT5_MESSAGE_RATE_TOO_HIGH:
|
||||
ESP_LOGW(TAG, "Message rate too high");
|
||||
break;
|
||||
case MQTT5_QUOTA_EXCEEDED:
|
||||
ESP_LOGW(TAG, "Quota exceeded");
|
||||
break;
|
||||
case MQTT5_ADMINISTRATIVE_ACTION:
|
||||
ESP_LOGW(TAG, "Administrative action");
|
||||
break;
|
||||
case MQTT5_PAYLOAD_FORMAT_INVALID:
|
||||
ESP_LOGW(TAG, "Payload format invalid");
|
||||
break;
|
||||
case MQTT5_RETAIN_NOT_SUPPORT:
|
||||
ESP_LOGW(TAG, "Retain not supported");
|
||||
break;
|
||||
case MQTT5_QOS_NOT_SUPPORT:
|
||||
ESP_LOGW(TAG, "QoS not supported");
|
||||
break;
|
||||
case MQTT5_USE_ANOTHER_SERVER:
|
||||
ESP_LOGW(TAG, "Use another server");
|
||||
break;
|
||||
case MQTT5_SERVER_MOVED:
|
||||
ESP_LOGW(TAG, "Server moved");
|
||||
break;
|
||||
case MQTT5_SHARED_SUBSCR_NOT_SUPPORTED:
|
||||
ESP_LOGW(TAG, "Shared Subscriptions not supported");
|
||||
break;
|
||||
case MQTT5_CONNECTION_RATE_EXCEEDED:
|
||||
ESP_LOGW(TAG, "Connection rate exceeded");
|
||||
break;
|
||||
case MQTT5_MAXIMUM_CONNECT_TIME:
|
||||
ESP_LOGW(TAG, "Maximum connect time");
|
||||
break;
|
||||
case MQTT5_SUBSCRIBE_IDENTIFIER_NOT_SUPPORT:
|
||||
ESP_LOGW(TAG, "Subscription Identifiers not supported");
|
||||
break;
|
||||
case MQTT5_WILDCARD_SUBSCRIBE_NOT_SUPPORT:
|
||||
ESP_LOGW(TAG, "Wildcard Subscriptions not supported");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Connection refused, Unknow reason");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_subscribe_check(esp_mqtt5_client_handle_t client, int qos)
|
||||
{
|
||||
/* Check Server support QoS level */
|
||||
if (client->mqtt5_config->server_resp_property_info.max_qos < qos) {
|
||||
ESP_LOGE(TAG, "Server only support max QoS level %d", client->mqtt5_config->server_resp_property_info.max_qos);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_publish_check(esp_mqtt5_client_handle_t client, int qos, int retain)
|
||||
{
|
||||
/* Check Server support QoS level */
|
||||
if (client->mqtt5_config->server_resp_property_info.max_qos < qos) {
|
||||
ESP_LOGE(TAG, "Server only support max QoS level %d", client->mqtt5_config->server_resp_property_info.max_qos);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Check Server support RETAIN */
|
||||
if (!client->mqtt5_config->server_resp_property_info.retain_available && retain) {
|
||||
ESP_LOGE(TAG, "Server not support retain");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Flow control to check PUBLISH(No PUBACK or PUBCOMP received) packet sent count(Only record QoS1 and QoS2)*/
|
||||
if (client->send_publish_packet_count > client->mqtt5_config->server_resp_property_info.receive_maximum) {
|
||||
ESP_LOGE(TAG, "Client send more than %d QoS1 and QoS2 PUBLISH packet without no ack", client->mqtt5_config->server_resp_property_info.receive_maximum);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_mqtt5_client_destory(esp_mqtt5_client_handle_t client)
|
||||
{
|
||||
if (client->mqtt_state.connection.information.protocol_ver == MQTT_PROTOCOL_V_5) {
|
||||
if (client->mqtt5_config) {
|
||||
free(client->mqtt5_config->will_property_info.content_type);
|
||||
free(client->mqtt5_config->will_property_info.response_topic);
|
||||
free(client->mqtt5_config->will_property_info.correlation_data);
|
||||
free(client->mqtt5_config->server_resp_property_info.response_info);
|
||||
esp_mqtt5_client_delete_topic_alias(client->mqtt5_config->peer_topic_alias);
|
||||
esp_mqtt5_client_delete_user_property(client->mqtt5_config->connect_property_info.user_property);
|
||||
esp_mqtt5_client_delete_user_property(client->mqtt5_config->will_property_info.user_property);
|
||||
esp_mqtt5_client_delete_user_property(client->mqtt5_config->disconnect_property_info.user_property);
|
||||
free(client->mqtt5_config);
|
||||
}
|
||||
free(client->event.property);
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_mqtt5_client_delete_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle)
|
||||
{
|
||||
if (topic_alias_handle) {
|
||||
mqtt5_topic_alias_item_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, topic_alias_handle, next, tmp) {
|
||||
STAILQ_REMOVE(topic_alias_handle, item, mqtt5_topic_alias, next);
|
||||
free(item->topic);
|
||||
free(item);
|
||||
}
|
||||
free(topic_alias_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t esp_mqtt5_client_update_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, char *topic, size_t topic_len)
|
||||
{
|
||||
mqtt5_topic_alias_item_t item;
|
||||
bool found = false;
|
||||
STAILQ_FOREACH(item, topic_alias_handle, next) {
|
||||
if (item->topic_alias == topic_alias) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if ((item->topic_len != topic_len) || strncmp(topic, item->topic, topic_len)) {
|
||||
free(item->topic);
|
||||
item->topic = calloc(1, topic_len);
|
||||
ESP_MEM_CHECK(TAG, item->topic, return ESP_FAIL);
|
||||
memcpy(item->topic, topic, topic_len);
|
||||
item->topic_len = topic_len;
|
||||
}
|
||||
} else {
|
||||
item = calloc(1, sizeof(mqtt5_topic_alias_t));
|
||||
ESP_MEM_CHECK(TAG, item, return ESP_FAIL);
|
||||
item->topic_alias = topic_alias;
|
||||
item->topic_len = topic_len;
|
||||
item->topic = calloc(1, topic_len);
|
||||
ESP_MEM_CHECK(TAG, item->topic, {
|
||||
free(item);
|
||||
return ESP_FAIL;
|
||||
});
|
||||
memcpy(item->topic, topic, topic_len);
|
||||
STAILQ_INSERT_TAIL(topic_alias_handle, item, next);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static char *esp_mqtt5_client_get_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, size_t *topic_length)
|
||||
{
|
||||
mqtt5_topic_alias_item_t item;
|
||||
STAILQ_FOREACH(item, topic_alias_handle, next) {
|
||||
if (item->topic_alias == topic_alias) {
|
||||
*topic_length = item->topic_len;
|
||||
return item->topic;
|
||||
}
|
||||
}
|
||||
*topic_length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static esp_err_t esp_mqtt5_user_property_copy(mqtt5_user_property_handle_t user_property_new, const mqtt5_user_property_handle_t user_property_old)
|
||||
{
|
||||
if (!user_property_new || !user_property_old) {
|
||||
ESP_LOGE(TAG, "Input is NULL");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
mqtt5_user_property_item_t old_item, new_item;
|
||||
STAILQ_FOREACH(old_item, user_property_old, next) {
|
||||
new_item = calloc(1, sizeof(mqtt5_user_property_t));
|
||||
ESP_MEM_CHECK(TAG, new_item, return ESP_FAIL);
|
||||
new_item->key = strdup(old_item->key);
|
||||
ESP_MEM_CHECK(TAG, new_item->key, {
|
||||
free(new_item);
|
||||
return ESP_FAIL;
|
||||
});
|
||||
new_item->value = strdup(old_item->value);
|
||||
ESP_MEM_CHECK(TAG, new_item->value, {
|
||||
free(new_item->key);
|
||||
free(new_item);
|
||||
return ESP_FAIL;
|
||||
});
|
||||
STAILQ_INSERT_TAIL(user_property_new, new_item, next);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_set_publish_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_publish_property_config_t *property)
|
||||
{
|
||||
if (!client) {
|
||||
ESP_LOGE(TAG, "Client was not initialized");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
MQTT_API_LOCK(client);
|
||||
|
||||
/* Check protocol version */
|
||||
if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGE(TAG, "MQTT protocol version is not v5");
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* Check topic alias less than server maximum topic alias */
|
||||
if (property->topic_alias > client->mqtt5_config->server_resp_property_info.topic_alias_maximum) {
|
||||
ESP_LOGE(TAG, "Topic alias %d is bigger than server support %d", property->topic_alias, client->mqtt5_config->server_resp_property_info.topic_alias_maximum);
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
client->mqtt5_config->publish_property_info = property;
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_subscribe_property_config_t *property)
|
||||
{
|
||||
if (!client) {
|
||||
ESP_LOGE(TAG, "Client was not initialized");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (property->retain_handle > 2) {
|
||||
ESP_LOGE(TAG, "retain_handle only support 0, 1, 2");
|
||||
return -1;
|
||||
}
|
||||
MQTT_API_LOCK(client);
|
||||
|
||||
/* Check protocol version */
|
||||
if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGE(TAG, "MQTT protocol version is not v5");
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (property->is_share_subscribe) {
|
||||
if (property->no_local_flag) {
|
||||
// MQTT-3.8.3-4 not allow that No Local bit to 1 on a Shared Subscription
|
||||
ESP_LOGE(TAG, "Protocol error that no local flag set on shared subscription");
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (!client->mqtt5_config->server_resp_property_info.shared_subscribe_available) {
|
||||
ESP_LOGE(TAG, "MQTT broker not support shared subscribe");
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (!property->share_name || !strlen(property->share_name)) {
|
||||
ESP_LOGE(TAG, "Share name can't be empty for shared subscribe");
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
client->mqtt5_config->subscribe_property_info = property;
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_set_unsubscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_unsubscribe_property_config_t *property)
|
||||
{
|
||||
if (!client) {
|
||||
ESP_LOGE(TAG, "Client was not initialized");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
MQTT_API_LOCK(client);
|
||||
|
||||
/* Check protocol version */
|
||||
if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGE(TAG, "MQTT protocol version is not v5");
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (property->is_share_subscribe) {
|
||||
if (!client->mqtt5_config->server_resp_property_info.shared_subscribe_available) {
|
||||
ESP_LOGE(TAG, "MQTT broker not support shared subscribe");
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (!property->share_name || !strlen(property->share_name)) {
|
||||
ESP_LOGE(TAG, "Share name can't be empty for shared subscribe");
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
client->mqtt5_config->unsubscribe_property_info = property;
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_disconnect_property_config_t *property)
|
||||
{
|
||||
if (!client) {
|
||||
ESP_LOGE(TAG, "Client was not initialized");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
MQTT_API_LOCK(client);
|
||||
|
||||
/* Check protocol version */
|
||||
if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGE(TAG, "MQTT protocol version is not v5");
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (property) {
|
||||
if (property->session_expiry_interval) {
|
||||
client->mqtt5_config->disconnect_property_info.session_expiry_interval = property->session_expiry_interval;
|
||||
}
|
||||
if (property->disconnect_reason) {
|
||||
client->mqtt5_config->disconnect_property_info.disconnect_reason = property->disconnect_reason;
|
||||
}
|
||||
if (property->user_property) {
|
||||
esp_mqtt5_client_delete_user_property(client->mqtt5_config->disconnect_property_info.user_property);
|
||||
client->mqtt5_config->disconnect_property_info.user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t));
|
||||
ESP_MEM_CHECK(TAG, client->mqtt5_config->disconnect_property_info.user_property, {
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_ERR_NO_MEM;
|
||||
});
|
||||
STAILQ_INIT(client->mqtt5_config->disconnect_property_info.user_property);
|
||||
if (esp_mqtt5_user_property_copy(client->mqtt5_config->disconnect_property_info.user_property, property->user_property) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_mqtt5_user_property_copy fail");
|
||||
free(client->mqtt5_config->disconnect_property_info.user_property);
|
||||
client->mqtt5_config->disconnect_property_info.user_property = NULL;
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_connection_property_config_t *connect_property)
|
||||
{
|
||||
if (!client) {
|
||||
ESP_LOGE(TAG, "Client was not initialized");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
MQTT_API_LOCK(client);
|
||||
|
||||
/* Check protocol version */
|
||||
if (client->mqtt_state.connection.information.protocol_ver != MQTT_PROTOCOL_V_5) {
|
||||
ESP_LOGE(TAG, "MQTT protocol version is not v5");
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (connect_property) {
|
||||
if (connect_property->session_expiry_interval) {
|
||||
client->mqtt5_config->connect_property_info.session_expiry_interval = connect_property->session_expiry_interval;
|
||||
}
|
||||
if (connect_property->maximum_packet_size) {
|
||||
if (connect_property->maximum_packet_size > client->mqtt_state.in_buffer_length) {
|
||||
ESP_LOGW(TAG, "Connect maximum_packet_size property is over buffer_size(%d), Please first change it", client->mqtt_state.in_buffer_length);
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_FAIL;
|
||||
} else {
|
||||
client->mqtt5_config->connect_property_info.maximum_packet_size = connect_property->maximum_packet_size;
|
||||
}
|
||||
} else {
|
||||
client->mqtt5_config->connect_property_info.maximum_packet_size = client->mqtt_state.in_buffer_length;
|
||||
}
|
||||
if (connect_property->receive_maximum) {
|
||||
client->mqtt5_config->connect_property_info.receive_maximum = connect_property->receive_maximum;
|
||||
}
|
||||
if (connect_property->topic_alias_maximum) {
|
||||
client->mqtt5_config->connect_property_info.topic_alias_maximum = connect_property->topic_alias_maximum;
|
||||
if (!client->mqtt5_config->peer_topic_alias) {
|
||||
client->mqtt5_config->peer_topic_alias = calloc(1, sizeof(struct mqtt5_topic_alias_list_t));
|
||||
ESP_MEM_CHECK(TAG, client->mqtt5_config->peer_topic_alias, goto _mqtt_set_config_failed);
|
||||
STAILQ_INIT(client->mqtt5_config->peer_topic_alias);
|
||||
}
|
||||
}
|
||||
if (connect_property->request_resp_info) {
|
||||
client->mqtt5_config->connect_property_info.request_resp_info = connect_property->request_resp_info;
|
||||
}
|
||||
if (connect_property->request_problem_info) {
|
||||
client->mqtt5_config->connect_property_info.request_problem_info = connect_property->request_problem_info;
|
||||
}
|
||||
if (connect_property->user_property) {
|
||||
esp_mqtt5_client_delete_user_property(client->mqtt5_config->connect_property_info.user_property);
|
||||
client->mqtt5_config->connect_property_info.user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t));
|
||||
ESP_MEM_CHECK(TAG, client->mqtt5_config->connect_property_info.user_property, goto _mqtt_set_config_failed);
|
||||
STAILQ_INIT(client->mqtt5_config->connect_property_info.user_property);
|
||||
if (esp_mqtt5_user_property_copy(client->mqtt5_config->connect_property_info.user_property, connect_property->user_property) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_mqtt5_user_property_copy fail");
|
||||
goto _mqtt_set_config_failed;
|
||||
}
|
||||
}
|
||||
if (connect_property->payload_format_indicator) {
|
||||
client->mqtt5_config->will_property_info.payload_format_indicator = connect_property->payload_format_indicator;
|
||||
}
|
||||
if (connect_property->will_delay_interval) {
|
||||
client->mqtt5_config->will_property_info.will_delay_interval = connect_property->will_delay_interval;
|
||||
}
|
||||
if (connect_property->message_expiry_interval) {
|
||||
client->mqtt5_config->will_property_info.message_expiry_interval = connect_property->message_expiry_interval;
|
||||
}
|
||||
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(connect_property->content_type, &client->mqtt5_config->will_property_info.content_type), goto _mqtt_set_config_failed);
|
||||
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(connect_property->response_topic, &client->mqtt5_config->will_property_info.response_topic), goto _mqtt_set_config_failed);
|
||||
if (connect_property->correlation_data && connect_property->correlation_data_len) {
|
||||
free(client->mqtt5_config->will_property_info.correlation_data);
|
||||
client->mqtt5_config->will_property_info.correlation_data = malloc(connect_property->correlation_data_len);
|
||||
ESP_MEM_CHECK(TAG, client->mqtt5_config->will_property_info.correlation_data, goto _mqtt_set_config_failed);
|
||||
memcpy(client->mqtt5_config->will_property_info.correlation_data, connect_property->correlation_data, connect_property->correlation_data_len);
|
||||
client->mqtt5_config->will_property_info.correlation_data_len = connect_property->correlation_data_len;
|
||||
}
|
||||
if (connect_property->will_user_property) {
|
||||
esp_mqtt5_client_delete_user_property(client->mqtt5_config->will_property_info.user_property);
|
||||
client->mqtt5_config->will_property_info.user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t));
|
||||
ESP_MEM_CHECK(TAG, client->mqtt5_config->will_property_info.user_property, goto _mqtt_set_config_failed);
|
||||
STAILQ_INIT(client->mqtt5_config->will_property_info.user_property);
|
||||
if (esp_mqtt5_user_property_copy(client->mqtt5_config->will_property_info.user_property, connect_property->will_user_property) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_mqtt5_user_property_copy fail");
|
||||
goto _mqtt_set_config_failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_OK;
|
||||
_mqtt_set_config_failed:
|
||||
esp_mqtt_destroy_config(client);
|
||||
MQTT_API_UNLOCK(client);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_property, esp_mqtt5_user_property_item_t item[], uint8_t item_num)
|
||||
{
|
||||
if (!item_num || !item) {
|
||||
ESP_LOGE(TAG, "Input value is NULL");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (!*user_property) {
|
||||
*user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t));
|
||||
ESP_MEM_CHECK(TAG, *user_property, return ESP_ERR_NO_MEM);
|
||||
STAILQ_INIT(*user_property);
|
||||
}
|
||||
|
||||
for (int i = 0; i < item_num; i ++) {
|
||||
if (item[i].key && item[i].value) {
|
||||
mqtt5_user_property_item_t user_property_item = calloc(1, sizeof(mqtt5_user_property_t));
|
||||
ESP_MEM_CHECK(TAG, user_property_item, goto err);
|
||||
size_t key_len = strlen(item[i].key);
|
||||
size_t value_len = strlen(item[i].value);
|
||||
|
||||
user_property_item->key = calloc(1, key_len + 1);
|
||||
ESP_MEM_CHECK(TAG, user_property_item->key, {
|
||||
free(user_property_item);
|
||||
goto err;
|
||||
});
|
||||
memcpy(user_property_item->key, item[i].key, key_len);
|
||||
user_property_item->key[key_len] = '\0';
|
||||
|
||||
user_property_item->value = calloc(1, value_len + 1);
|
||||
ESP_MEM_CHECK(TAG, user_property_item->value, {
|
||||
free(user_property_item->key);
|
||||
free(user_property_item);
|
||||
goto err;
|
||||
});
|
||||
memcpy(user_property_item->value, item[i].value, value_len);
|
||||
user_property_item->value[value_len] = '\0';
|
||||
|
||||
STAILQ_INSERT_TAIL(*user_property, user_property_item, next);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
err:
|
||||
esp_mqtt5_client_delete_user_property(*user_property);
|
||||
*user_property = NULL;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_err_t esp_mqtt5_client_get_user_property(mqtt5_user_property_handle_t user_property, esp_mqtt5_user_property_item_t *item, uint8_t *item_num)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
if (user_property && item && *item_num) {
|
||||
mqtt5_user_property_item_t user_property_item;
|
||||
uint8_t num = *item_num;
|
||||
STAILQ_FOREACH(user_property_item, user_property, next) {
|
||||
if (i < num) {
|
||||
size_t item_key_len = strlen(user_property_item->key);
|
||||
size_t item_value_len = strlen(user_property_item->value);
|
||||
char *key = calloc(1, item_key_len + 1);
|
||||
ESP_MEM_CHECK(TAG, key, goto err);
|
||||
memcpy(key, user_property_item->key, item_key_len);
|
||||
key[item_key_len] = '\0';
|
||||
char *value = calloc(1, item_value_len + 1);
|
||||
ESP_MEM_CHECK(TAG, value, {
|
||||
free(key);
|
||||
goto err;
|
||||
});
|
||||
memcpy(value, user_property_item->value, item_value_len);
|
||||
value[item_value_len] = '\0';
|
||||
item[i].key = key;
|
||||
item[i].value = value;
|
||||
i ++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*item_num = i;
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Input value is NULL or item_num is 0");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
err:
|
||||
for (j = 0; j < i; j ++) {
|
||||
if (item[j].key) {
|
||||
free((char *)item[j].key);
|
||||
}
|
||||
if (item[j].value) {
|
||||
free((char *)item[j].value);
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
uint8_t esp_mqtt5_client_get_user_property_count(mqtt5_user_property_handle_t user_property)
|
||||
{
|
||||
uint8_t count = 0;
|
||||
if (user_property) {
|
||||
mqtt5_user_property_item_t item;
|
||||
STAILQ_FOREACH(item, user_property, next) {
|
||||
count ++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void esp_mqtt5_client_delete_user_property(mqtt5_user_property_handle_t user_property)
|
||||
{
|
||||
if (user_property) {
|
||||
mqtt5_user_property_item_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, user_property, next, tmp) {
|
||||
STAILQ_REMOVE(user_property, item, mqtt5_user_property, next);
|
||||
free(item->key);
|
||||
free(item->value);
|
||||
free(item);
|
||||
}
|
||||
}
|
||||
free(user_property);
|
||||
}
|
||||
2380
managed_components/esp_mqtt/mqtt_client.c
Normal file
2380
managed_components/esp_mqtt/mqtt_client.c
Normal file
File diff suppressed because it is too large
Load Diff
9
managed_components/esp_mqtt/static-analysis-rules.yml
Normal file
9
managed_components/esp_mqtt/static-analysis-rules.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
limits:
|
||||
"clang-analyzer-core.NullDereference" : 0
|
||||
"clang-analyzer-unix.Malloc" : 0
|
||||
|
||||
ignore:
|
||||
- "llvm-header-guard"
|
||||
- "llvm-include-order"
|
||||
|
||||
skip:
|
||||
@@ -0,0 +1 @@
|
||||
eabf03fe3cb1bdb5325d2725f420c6324c3e150033d893ce3737222929455a55
|
||||
21
managed_components/espressif__usb_host_hid/CHANGELOG.md
Normal file
21
managed_components/espressif__usb_host_hid/CHANGELOG.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## 1.0.3
|
||||
- Fixed a bug with interface mismatch on EP IN transfer complete while several HID devices are present.
|
||||
- Fixed a bug during device freeing, while detaching one of several attached HID devices.
|
||||
|
||||
## 1.0.2
|
||||
|
||||
- Added support for ESP32-P4
|
||||
- Fixed device open procedure for HID devices with multiple non-sequential interfaces.
|
||||
|
||||
## 1.0.1
|
||||
|
||||
- Fixed a bug where configuring the driver with `create_background_task = false` did not properly initialize the driver. This lead to `the hid_host_uninstall()` hang-up.
|
||||
- Fixed a bug where `hid_host_uninstall()` would cause a crash during the call while USB device has not been removed.
|
||||
- Added `hid_host_get_device_info()` to get the basic information of a connected USB HID device.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
- Initial version
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register( SRCS "hid_host.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES usb )
|
||||
202
managed_components/espressif__usb_host_hid/LICENSE
Normal file
202
managed_components/espressif__usb_host_hid/LICENSE
Normal 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.
|
||||
52
managed_components/espressif__usb_host_hid/README.md
Normal file
52
managed_components/espressif__usb_host_hid/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# USB Host HID (Human Interface Device) Driver
|
||||
|
||||
[](https://components.espressif.com/components/espressif/usb_host_hid)
|
||||
|
||||
This directory contains an implementation of a USB HID Driver implemented on top of the [USB Host Library](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html).
|
||||
|
||||
HID driver allows access to HID devices.
|
||||
|
||||
## Usage
|
||||
|
||||
The following steps outline the typical API call pattern of the HID Class Driver:
|
||||
|
||||
1. Install the USB Host Library via 'usb_host_install()'
|
||||
2. Install the HID driver via 'hid_host_install()'
|
||||
3. The HID Host driver device callback provide the following events (via two callbacks):
|
||||
- HID_HOST_DRIVER_EVENT_CONNECTED
|
||||
- HID_HOST_INTERFACE_EVENT_INPUT_REPORT
|
||||
- HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR
|
||||
- HID_HOST_INTERFACE_EVENT_DISCONNECTED
|
||||
|
||||
4. Specific HID device can be opened or closed with:
|
||||
- 'hid_host_device_open()'
|
||||
- 'hid_host_device_close()'
|
||||
5. To enable / disable data receiving in case of event (keyboard key was pressed or mouse device was moved e.t.c) use:
|
||||
- 'hid_host_device_start()'
|
||||
- 'hid_host_device_stop()'
|
||||
6. HID Class specific device requests:
|
||||
- 'hid_host_interface_get_report_descriptor()'
|
||||
- 'hid_class_request_get_report()'
|
||||
- 'hid_class_request_get_idle()'
|
||||
- 'hid_class_request_get_protocol()'
|
||||
- 'hid_class_request_set_report()'
|
||||
- 'hid_class_request_set_idle()'
|
||||
- 'hid_class_request_set_protocol()'
|
||||
7. When HID device event occurs the driver call an interface callback with events:
|
||||
- HID_HOST_INTERFACE_EVENT_INPUT_REPORT
|
||||
- HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR
|
||||
- HID_HOST_INTERFACE_EVENT_DISCONNECTED
|
||||
8. The HID driver can be uninstalled via 'hid_host_uninstall()'
|
||||
|
||||
## Known issues
|
||||
|
||||
- Empty
|
||||
|
||||
## Examples
|
||||
|
||||
- For an example, refer to [hid_host_example](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/hid)
|
||||
|
||||
## Supported Devices
|
||||
|
||||
- HID Driver support any HID compatible device with a USB bIterfaceClass 0x03 (Human Interface Device).
|
||||
- There are two options to handle HID device input data: either in RAW format or via special event handlers (which are available only for HID Devices which support Boot Protocol).
|
||||
1527
managed_components/espressif__usb_host_hid/hid_host.c
Normal file
1527
managed_components/espressif__usb_host_hid/hid_host.c
Normal file
File diff suppressed because it is too large
Load Diff
13
managed_components/espressif__usb_host_hid/idf_component.yml
Normal file
13
managed_components/espressif__usb_host_hid/idf_component.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
dependencies:
|
||||
idf: '>=4.4'
|
||||
description: USB Host HID driver
|
||||
repository: git://github.com/espressif/esp-usb.git
|
||||
repository_info:
|
||||
commit_sha: 39b0de39a90c9e391db26f06d54e4b10f58be231
|
||||
path: host/class/hid/usb_host_hid
|
||||
targets:
|
||||
- esp32s2
|
||||
- esp32s3
|
||||
- esp32p4
|
||||
url: https://github.com/espressif/esp-usb/tree/master/host/class/hid/usb_host_hid
|
||||
version: 1.0.3
|
||||
147
managed_components/espressif__usb_host_hid/include/usb/hid.h
Normal file
147
managed_components/espressif__usb_host_hid/include/usb/hid.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief HID Subclass
|
||||
*
|
||||
* @see 4.2 Subclass, p.8 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_SUBCLASS_NO_SUBCLASS = 0x00,
|
||||
HID_SUBCLASS_BOOT_INTERFACE = 0x01
|
||||
} __attribute__((packed)) hid_subclass_t;
|
||||
|
||||
/**
|
||||
* @brief HID Protocols
|
||||
*
|
||||
* @see 4.3 Protocols, p.9 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_PROTOCOL_NONE = 0x00,
|
||||
HID_PROTOCOL_KEYBOARD = 0x01,
|
||||
HID_PROTOCOL_MOUSE = 0x02,
|
||||
HID_PROTOCOL_MAX
|
||||
} __attribute__((packed)) hid_protocol_t;
|
||||
|
||||
/**
|
||||
* @brief HID Descriptor
|
||||
*
|
||||
* @see 6.2.1 HID Descriptor, p.22 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t bLength; // Numeric expression that is the total size of the HID descriptor
|
||||
uint8_t bDescriptorType; // Constant name specifying type of HID descriptor
|
||||
uint16_t bcdHID; // Numeric expression identifying the HIDClass Specification release
|
||||
uint8_t bCountryCode; // Numeric expression identifying country code of the localized hardware
|
||||
uint8_t bNumDescriptors; // Numeric expression specifying the number of class descriptors (always at least one i.e. Report descriptor.)
|
||||
uint8_t bReportDescriptorType; // Constant name identifying type of class descriptor. See Section 7.1.2: Set_Descriptor Request for a table of class descriptor constants
|
||||
uint16_t wReportDescriptorLength; // Numeric expression that is the total size of the Report descriptor
|
||||
// Optional descriptors may follow further
|
||||
} __attribute__((packed)) hid_descriptor_t;
|
||||
|
||||
/**
|
||||
* @brief HID Country Codes
|
||||
*
|
||||
* @see 6.2.1 HID Descriptor, p.23 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
HID_COUNTRY_CODE_NOT_SUPPORTED = 0x00,
|
||||
HID_COUNTRY_CODE_ARABIC = 0x01,
|
||||
HID_COUNTRY_CODE_BELGIAN = 0x02,
|
||||
HID_COUNTRY_CODE_CANADIAN_BILINGUAL = 0x03,
|
||||
HID_COUNTRY_CODE_CANADIAN_FRENCH = 0x04,
|
||||
HID_COUNTRY_CODE_CZECH = 0x05,
|
||||
HID_COUNTRY_CODE_DANISH = 0x06,
|
||||
HID_COUNTRY_CODE_FINNISH = 0x07,
|
||||
HID_COUNTRY_CODE_FRENCH = 0x08,
|
||||
HID_COUNTRY_CODE_GERMAN = 0x09,
|
||||
HID_COUNTRY_CODE_GREEK = 0x0A,
|
||||
HID_COUNTRY_CODE_HEBREW = 0x0B,
|
||||
HID_COUNTRY_CODE_HUNGARY = 0x0C,
|
||||
HID_COUNTRY_CODE_ISO = 0x0D,
|
||||
HID_COUNTRY_CODE_ITALIAN = 0x0E,
|
||||
HID_COUNTRY_CODE_JAPAN = 0x0F,
|
||||
HID_COUNTRY_CODE_KOREAN = 0x10,
|
||||
HID_COUNTRY_CODE_LATIN_AMERICAN = 0x11,
|
||||
HID_COUNTRY_CODE_NETHERLANDS = 0x12,
|
||||
HID_COUNTRY_CODE_NORWEGIAN = 0x13,
|
||||
HID_COUNTRY_CODE_PERSIAN = 0x14,
|
||||
HID_COUNTRY_CODE_POLAND = 0x15,
|
||||
HID_COUNTRY_CODE_PORTUGUESE = 0x16,
|
||||
HID_COUNTRY_CODE_RUSSIA = 0x17,
|
||||
HID_COUNTRY_CODE_SLOVAKIA = 0x18,
|
||||
HID_COUNTRY_CODE_SPANISH = 0x19,
|
||||
HID_COUNTRY_CODE_SWEDISH = 0x1A,
|
||||
HID_COUNTRY_CODE_SWISS_F = 0x1B,
|
||||
HID_COUNTRY_CODE_SWISS_G = 0x1C,
|
||||
HID_COUNTRY_CODE_SWITZERLAND = 0x1D,
|
||||
HID_COUNTRY_CODE_TAIWAN = 0x1E,
|
||||
HID_COUNTRY_CODE_TURKISH_Q = 0x1F,
|
||||
HID_COUNTRY_CODE_UK = 0x20,
|
||||
HID_COUNTRY_CODE_US = 0x21,
|
||||
HID_COUNTRY_CODE_YUGOSLAVIA = 0x22,
|
||||
HID_COUNTRY_CODE_TURKISH_F = 0x23
|
||||
} __attribute__((packed)) hid_country_code_t;
|
||||
|
||||
/**
|
||||
* @brief HID Class Descriptor Types
|
||||
*
|
||||
* @see 7.1, p.49 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_CLASS_DESCRIPTOR_TYPE_HID = 0x21,
|
||||
HID_CLASS_DESCRIPTOR_TYPE_REPORT = 0x22,
|
||||
HID_CLASS_DESCRIPTOR_TYPE_PHYSICAL = 0x23
|
||||
} __attribute__((packed)) hid_class_descritpor_type_t;
|
||||
|
||||
/**
|
||||
* @brief HID Class-Specific Requests
|
||||
*
|
||||
* @see 7.2, p.50 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_CLASS_SPECIFIC_REQ_GET_REPORT = 0x01,
|
||||
HID_CLASS_SPECIFIC_REQ_GET_IDLE = 0x02,
|
||||
HID_CLASS_SPECIFIC_REQ_GET_PROTOCOL = 0x03,
|
||||
HID_CLASS_SPECIFIC_REQ_SET_REPORT = 0x09,
|
||||
HID_CLASS_SPECIFIC_REQ_SET_IDLE = 0x0A,
|
||||
HID_CLASS_SPECIFIC_REQ_SET_PROTOCOL = 0x0B
|
||||
} __attribute__((packed)) hid_class_specific_req_t;
|
||||
|
||||
/**
|
||||
* @brief HID Report Types
|
||||
*
|
||||
* @see 7.2.1, p.51 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_REPORT_TYPE_INPUT = 0x01,
|
||||
HID_REPORT_TYPE_OUTPUT = 0x02,
|
||||
HID_REPORT_TYPE_FEATURE = 0x03,
|
||||
} __attribute__((packed)) hid_report_type_t;
|
||||
|
||||
/**
|
||||
* @brief HID Report protocol
|
||||
*
|
||||
* @see 7.2.5/7.2.6, p.54 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_REPORT_PROTOCOL_BOOT = 0x00,
|
||||
HID_REPORT_PROTOCOL_REPORT = 0x01,
|
||||
HID_REPORT_PROTOCOL_MAX
|
||||
} __attribute__((packed)) hid_report_protocol_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wchar.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
|
||||
#include "hid.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USB HID HOST string descriptor maximal length
|
||||
*
|
||||
* The maximum possible number of characters in an embedded string is device specific.
|
||||
* For USB devices, the maximum string length is 126 wide characters (not including the terminating NULL character).
|
||||
* This is a length, which is available to upper level application during getting information
|
||||
* of HID Device with 'hid_host_get_device_info' call.
|
||||
*
|
||||
* To decrease memory usage 32 wide characters (64 bytes per every string) is used.
|
||||
*/
|
||||
#define HID_STR_DESC_MAX_LENGTH 32
|
||||
|
||||
typedef struct hid_interface *hid_host_device_handle_t; /**< Device Handle. Handle to a particular HID interface */
|
||||
|
||||
// ------------------------ USB HID Host events --------------------------------
|
||||
/**
|
||||
* @brief USB HID HOST Device event id
|
||||
*/
|
||||
typedef enum {
|
||||
HID_HOST_DRIVER_EVENT_CONNECTED = 0x00, /**< HID Device has been found in connected USB device (at least one) */
|
||||
} hid_host_driver_event_t;
|
||||
|
||||
/**
|
||||
* @brief USB HID HOST Interface event id
|
||||
*/
|
||||
typedef enum {
|
||||
HID_HOST_INTERFACE_EVENT_INPUT_REPORT = 0x00, /**< HID Device input report */
|
||||
HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR, /**< HID Device transfer error */
|
||||
HID_HOST_INTERFACE_EVENT_DISCONNECTED, /**< HID Device has been disconnected */
|
||||
} hid_host_interface_event_t;
|
||||
|
||||
/**
|
||||
* @brief HID device descriptor common data.
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t VID;
|
||||
uint16_t PID;
|
||||
wchar_t iManufacturer[HID_STR_DESC_MAX_LENGTH];
|
||||
wchar_t iProduct[HID_STR_DESC_MAX_LENGTH];
|
||||
wchar_t iSerialNumber[HID_STR_DESC_MAX_LENGTH];
|
||||
} hid_host_dev_info_t;
|
||||
|
||||
/**
|
||||
* @brief USB HID Host device parameters
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t addr; /**< USB Address of connected HID device */
|
||||
uint8_t iface_num; /**< HID Interface Number */
|
||||
uint8_t sub_class; /**< HID Interface SubClass */
|
||||
uint8_t proto; /**< HID Interface Protocol */
|
||||
} hid_host_dev_params_t;
|
||||
|
||||
// ------------------------ USB HID Host callbacks -----------------------------
|
||||
|
||||
/**
|
||||
* @brief USB HID driver event callback.
|
||||
*
|
||||
* @param[in] hid_handle HID device handle (HID Interface)
|
||||
* @param[in] event HID driver event
|
||||
* @param[in] arg User argument from HID driver configuration structure
|
||||
*/
|
||||
typedef void (*hid_host_driver_event_cb_t)(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg);
|
||||
|
||||
/**
|
||||
* @brief USB HID Interface event callback.
|
||||
*
|
||||
* @param[in] hid_device_handle HID device handle (HID Interface)
|
||||
* @param[in] event HID Interface event
|
||||
* @param[in] arg User argument
|
||||
*/
|
||||
typedef void (*hid_host_interface_event_cb_t)(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_interface_event_t event,
|
||||
void *arg);
|
||||
|
||||
// ----------------------------- Public ---------------------------------------
|
||||
/**
|
||||
* @brief HID configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
bool create_background_task; /**< When set to true, background task handling USB events is created.
|
||||
Otherwise user has to periodically call hid_host_handle_events function */
|
||||
size_t task_priority; /**< Task priority of created background task */
|
||||
size_t stack_size; /**< Stack size of created background task */
|
||||
BaseType_t core_id; /**< Select core on which background task will run or tskNO_AFFINITY */
|
||||
hid_host_driver_event_cb_t callback; /**< Callback invoked when HID driver event occurs. Must not be NULL. */
|
||||
void *callback_arg; /**< User provided argument passed to callback */
|
||||
} hid_host_driver_config_t;
|
||||
|
||||
/**
|
||||
* @brief HID device configuration structure (HID Interface)
|
||||
*/
|
||||
typedef struct {
|
||||
hid_host_interface_event_cb_t callback; /**< Callback invoked when HID Interface event occurs */
|
||||
void *callback_arg; /**< User provided argument passed to callback */
|
||||
} hid_host_device_config_t;
|
||||
|
||||
/**
|
||||
* @brief USB HID Host install USB Host HID Class driver
|
||||
*
|
||||
* @param[in] config configuration structure HID to create
|
||||
* @return esp_err_r
|
||||
*/
|
||||
esp_err_t hid_host_install(const hid_host_driver_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief USB HID Host uninstall HID Class driver
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_uninstall(void);
|
||||
|
||||
/**
|
||||
* @brief USB HID Host open a device with specific device parameters
|
||||
*
|
||||
* @param[in] iface_handle Handle of the HID device to open
|
||||
* @param[in] config Configuration structure HID device to open
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_open(hid_host_device_handle_t hid_dev_handle,
|
||||
const hid_host_device_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief USB HID Host close device
|
||||
*
|
||||
* @param[in] hid_dev_handle Handle of the HID device to close
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_close(hid_host_device_handle_t hid_dev_handle);
|
||||
|
||||
/**
|
||||
* @brief HID Host USB event handler
|
||||
*
|
||||
* If HID Host install was made with create_background_task=false configuration,
|
||||
* application needs to handle USB Host events itself.
|
||||
* Do not used if HID host install was made with create_background_task=true configuration
|
||||
*
|
||||
* @param[in] timeout Timeout in ticks. For milliseconds, please use 'pdMS_TO_TICKS()' macros
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_handle_events(uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief HID Device get parameters by handle.
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[out] dev_params Pointer to a dev_params struct to fill
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_get_params(hid_host_device_handle_t hid_dev_handle,
|
||||
hid_host_dev_params_t *dev_params);
|
||||
/**
|
||||
* @brief HID Host get device raw input report data pointer by handle
|
||||
*
|
||||
* This functions should be called after HID Interface device event HID_HOST_INTERFACE_EVENT_INPUT_REPORT
|
||||
* to get the actual raw data of input report.
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] data Pointer to buffer where the input data will be copied
|
||||
* @param[in] data_length_max Max length of data can be copied to data buffer
|
||||
* @param[out] data_length Length of input report
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_get_raw_input_report_data(hid_host_device_handle_t hid_dev_handle,
|
||||
uint8_t *data,
|
||||
size_t data_length_max,
|
||||
size_t *data_length);
|
||||
|
||||
// ------------------------ USB HID Host driver API ----------------------------
|
||||
|
||||
/**
|
||||
* @brief HID Host start awaiting event from a device by handle
|
||||
*
|
||||
* Calls a callback when the HID Interface event has occurred.
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_start(hid_host_device_handle_t hid_dev_handle);
|
||||
|
||||
/**
|
||||
* @brief HID Host stop device
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_stop(hid_host_device_handle_t hid_dev_handle);
|
||||
|
||||
/**
|
||||
* @brief HID Host Get Report Descriptor
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[out] report_desc_len Length of report descriptor
|
||||
*
|
||||
* @return a uint8_t pointer to report descriptor data
|
||||
*/
|
||||
uint8_t *hid_host_get_report_descriptor(hid_host_device_handle_t hid_dev_handle,
|
||||
size_t *report_desc_len);
|
||||
|
||||
|
||||
/**
|
||||
* @brief HID Host Get device information
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
*/
|
||||
esp_err_t hid_host_get_device_info(hid_host_device_handle_t hid_dev_handle,
|
||||
hid_host_dev_info_t *hid_dev_info);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request GET REPORT
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] report_id Report ID
|
||||
* @param[out] report Pointer to buffer for a report data
|
||||
* @param[in/out] report_length Report data length, before the get report contain the maximum value of a report buffer.
|
||||
* After get report there is a value of actual data in report buffer.
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_get_report(hid_host_device_handle_t hid_dev_handle,
|
||||
uint8_t report_type,
|
||||
uint8_t report_id,
|
||||
uint8_t *report,
|
||||
size_t *report_length);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request GET IDLE
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] report_id ReportID
|
||||
* @param[out] idle_rate Idle rate [ms]
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_get_idle(hid_host_device_handle_t hid_dev_handle,
|
||||
uint8_t report_id,
|
||||
uint8_t *idle_rate);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request GET PROTOCOL
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[out] protocol Pointer to HID report protocol (boot or report) of device
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_get_protocol(hid_host_device_handle_t hid_dev_handle,
|
||||
hid_report_protocol_t *protocol);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request SET REPORT
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] report_type Report type
|
||||
* @param[in] report_id Report ID
|
||||
* @param[in] report Pointer to a buffer with report data
|
||||
* @param[in] report_length Report data length
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_set_report(hid_host_device_handle_t hid_dev_handle,
|
||||
uint8_t report_type,
|
||||
uint8_t report_id,
|
||||
uint8_t *report,
|
||||
size_t report_length);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request SET IDLE
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] duration 0 (zero) for the indefinite duration, non-zero, then a fixed duration used.
|
||||
* @param[in] report_id If 0 (zero) the idle rate applies to all input reports generated by the device, otherwise ReportID
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_set_idle(hid_host_device_handle_t hid_dev_handle,
|
||||
uint8_t duration,
|
||||
uint8_t report_id);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request SET PROTOCOL
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] protocol HID report protocol (boot or report)
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_set_protocol(hid_host_device_handle_t hid_dev_handle,
|
||||
hid_report_protocol_t protocol);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------ HID usage keys ---------------------------------------------------------------
|
||||
/**
|
||||
* @brief HID Keys
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
HID_KEY_NO_PRESS = 0x00,
|
||||
HID_KEY_ROLLOVER = 0x01,
|
||||
HID_KEY_POST_FAIL = 0x02,
|
||||
HID_KEY_ERROR_UNDEFINED = 0x03,
|
||||
HID_KEY_A = 0x04,
|
||||
HID_KEY_B = 0x05,
|
||||
HID_KEY_C = 0x06,
|
||||
HID_KEY_D = 0x07,
|
||||
HID_KEY_E = 0x08,
|
||||
HID_KEY_F = 0x09,
|
||||
HID_KEY_G = 0x0A,
|
||||
HID_KEY_H = 0x0B,
|
||||
HID_KEY_I = 0x0C,
|
||||
HID_KEY_J = 0x0D,
|
||||
HID_KEY_K = 0x0E,
|
||||
HID_KEY_L = 0x0F,
|
||||
HID_KEY_M = 0x10,
|
||||
HID_KEY_N = 0x11,
|
||||
HID_KEY_O = 0x12,
|
||||
HID_KEY_P = 0x13,
|
||||
HID_KEY_Q = 0x14,
|
||||
HID_KEY_R = 0x15,
|
||||
HID_KEY_S = 0x16,
|
||||
HID_KEY_T = 0x17,
|
||||
HID_KEY_U = 0x18,
|
||||
HID_KEY_V = 0x19,
|
||||
HID_KEY_W = 0x1A,
|
||||
HID_KEY_X = 0x1B,
|
||||
HID_KEY_Y = 0x1C,
|
||||
HID_KEY_Z = 0x1D,
|
||||
HID_KEY_1 = 0x1E,
|
||||
HID_KEY_2 = 0x1F,
|
||||
HID_KEY_3 = 0x20,
|
||||
HID_KEY_4 = 0x21,
|
||||
HID_KEY_5 = 0x22,
|
||||
HID_KEY_6 = 0x23,
|
||||
HID_KEY_7 = 0x24,
|
||||
HID_KEY_8 = 0x25,
|
||||
HID_KEY_9 = 0x26,
|
||||
HID_KEY_0 = 0x27,
|
||||
HID_KEY_ENTER = 0x28,
|
||||
HID_KEY_ESC = 0x29,
|
||||
HID_KEY_DEL = 0x2A,
|
||||
HID_KEY_TAB = 0x2B,
|
||||
HID_KEY_SPACE = 0x2C,
|
||||
HID_KEY_MINUS = 0x2D,
|
||||
HID_KEY_EQUAL = 0x2E,
|
||||
HID_KEY_OPEN_BRACKET = 0x2F,
|
||||
HID_KEY_CLOSE_BRACKET = 0x30,
|
||||
HID_KEY_BACK_SLASH = 0x31,
|
||||
HID_KEY_SHARP = 0x32,
|
||||
HID_KEY_COLON = 0x33,
|
||||
HID_KEY_QUOTE = 0x34,
|
||||
HID_KEY_TILDE = 0x35,
|
||||
HID_KEY_LESS = 0x36,
|
||||
HID_KEY_GREATER = 0x37,
|
||||
HID_KEY_SLASH = 0x38,
|
||||
HID_KEY_CAPS_LOCK = 0x39,
|
||||
HID_KEY_F1 = 0x3A,
|
||||
HID_KEY_F2 = 0x3B,
|
||||
HID_KEY_F3 = 0x3C,
|
||||
HID_KEY_F4 = 0x3D,
|
||||
HID_KEY_F5 = 0x3E,
|
||||
HID_KEY_F6 = 0x3F,
|
||||
HID_KEY_F7 = 0x40,
|
||||
HID_KEY_F8 = 0x41,
|
||||
HID_KEY_F9 = 0x42,
|
||||
HID_KEY_F10 = 0x43,
|
||||
HID_KEY_F11 = 0x44,
|
||||
HID_KEY_F12 = 0x45,
|
||||
HID_KEY_PRINT_SCREEN = 0x46,
|
||||
HID_KEY_SCROLL_LOCK = 0x47,
|
||||
HID_KEY_PAUSE = 0x48,
|
||||
HID_KEY_INSERT = 0x49,
|
||||
HID_KEY_HOME = 0x4A,
|
||||
HID_KEY_PAGEUP = 0x4B,
|
||||
HID_KEY_DELETE = 0x4C,
|
||||
HID_KEY_END = 0x4D,
|
||||
HID_KEY_PAGEDOWN = 0x4E,
|
||||
HID_KEY_RIGHT = 0x4F,
|
||||
HID_KEY_LEFT = 0x50,
|
||||
HID_KEY_DOWN = 0x51,
|
||||
HID_KEY_UP = 0x52,
|
||||
HID_KEY_NUM_LOCK = 0x53,
|
||||
HID_KEY_KEYPAD_DIV = 0x54,
|
||||
HID_KEY_KEYPAD_MUL = 0x55,
|
||||
HID_KEY_KEYPAD_SUB = 0x56,
|
||||
HID_KEY_KEYPAD_ADD = 0x57,
|
||||
HID_KEY_KEYPAD_ENTER = 0x58,
|
||||
HID_KEY_KEYPAD_1 = 0x59,
|
||||
HID_KEY_KEYPAD_2 = 0x5A,
|
||||
HID_KEY_KEYPAD_3 = 0x5B,
|
||||
HID_KEY_KEYPAD_4 = 0x5C,
|
||||
HID_KEY_KEYPAD_5 = 0x5D,
|
||||
HID_KEY_KEYPAD_6 = 0x5E,
|
||||
HID_KEY_KEYPAD_7 = 0x5F,
|
||||
HID_KEY_KEYPAD_8 = 0x60,
|
||||
HID_KEY_KEYPAD_9 = 0x61,
|
||||
HID_KEY_KEYPAD_0 = 0x62,
|
||||
HID_KEY_KEYPAD_DELETE = 0x63,
|
||||
HID_KEY_KEYPAD_SLASH = 0x64,
|
||||
HID_KEY_APPLICATION = 0x65,
|
||||
HID_KEY_POWER = 0x66,
|
||||
HID_KEY_KEYPAD_EQUAL = 0x67,
|
||||
HID_KEY_F13 = 0x68,
|
||||
HID_KEY_F14 = 0x69,
|
||||
HID_KEY_F15 = 0x6A,
|
||||
HID_KEY_F16 = 0x6B,
|
||||
HID_KEY_F17 = 0x6C,
|
||||
HID_KEY_F18 = 0x6D,
|
||||
HID_KEY_F19 = 0x6E,
|
||||
HID_KEY_F20 = 0x6F,
|
||||
HID_KEY_F21 = 0x70,
|
||||
HID_KEY_F22 = 0x71,
|
||||
HID_KEY_F23 = 0x72,
|
||||
HID_KEY_F24 = 0x73,
|
||||
HID_KEY_EXECUTE = 0x74,
|
||||
HID_KEY_HELP = 0x75,
|
||||
HID_KEY_MENU = 0x76,
|
||||
HID_KEY_SELECT = 0x77,
|
||||
HID_KEY_STOP = 0x78,
|
||||
HID_KEY_AGAIN = 0x79,
|
||||
HID_KEY_UNDO = 0x7A,
|
||||
HID_KEY_CUT = 0x7B,
|
||||
HID_KEY_COPY = 0x7C,
|
||||
HID_KEY_PASTE = 0x7D,
|
||||
HID_KEY_FIND = 0x7E,
|
||||
HID_KEY_MUTE = 0x7F,
|
||||
HID_KEY_VOLUME_UP = 0x80,
|
||||
HID_KEY_VOLUME_DOWN = 0x81,
|
||||
HID_KEY_LOCKING_CAPS_LOCK = 0x82,
|
||||
HID_KEY_LOCKING_NUM_LOCK = 0x83,
|
||||
HID_KEY_LOCKING_SCROLL_LOCK = 0x84,
|
||||
HID_KEY_KEYPAD_COMMA = 0x85,
|
||||
HID_KEY_KEYPAD_EQUAL_SIGN = 0x86,
|
||||
HID_KEY_INTERNATIONAL_1 = 0x87,
|
||||
HID_KEY_INTERNATIONAL_2 = 0x88,
|
||||
HID_KEY_INTERNATIONAL_3 = 0x89,
|
||||
HID_KEY_INTERNATIONAL_4 = 0x8A,
|
||||
HID_KEY_INTERNATIONAL_5 = 0x8B,
|
||||
HID_KEY_INTERNATIONAL_6 = 0x8C,
|
||||
HID_KEY_INTERNATIONAL_7 = 0x8D,
|
||||
HID_KEY_INTERNATIONAL_8 = 0x8E,
|
||||
HID_KEY_INTERNATIONAL_9 = 0x8F,
|
||||
HID_KEY_LANG_1 = 0x90,
|
||||
HID_KEY_LANG_2 = 0x91,
|
||||
HID_KEY_LANG_3 = 0x92,
|
||||
HID_KEY_LANG_4 = 0x93,
|
||||
HID_KEY_LANG_5 = 0x94,
|
||||
HID_KEY_LANG_6 = 0x95,
|
||||
HID_KEY_LANG_7 = 0x96,
|
||||
HID_KEY_LANG_8 = 0x97,
|
||||
HID_KEY_LANG_9 = 0x98,
|
||||
HID_KEY_ALTERNATE_ERASE = 0x99,
|
||||
HID_KEY_SYSREQ = 0x9A,
|
||||
HID_KEY_CANCEL = 0x9B,
|
||||
HID_KEY_CLEAR = 0x9C,
|
||||
HID_KEY_PRIOR = 0x9D,
|
||||
HID_KEY_RETURN = 0x9E,
|
||||
HID_KEY_SEPARATOR = 0x9F,
|
||||
HID_KEY_OUT = 0xA0,
|
||||
HID_KEY_OPER = 0xA1,
|
||||
HID_KEY_CLEAR_AGAIN = 0xA2,
|
||||
HID_KEY_CRSEL = 0xA3,
|
||||
HID_KEY_EXSEL = 0xA4,
|
||||
HID_KEY_KEYPAD_00 = 0xB0,
|
||||
HID_KEY_KEYPAD_000 = 0xB1,
|
||||
HID_KEY_THOUSANDS_SEPARATOR = 0xB2,
|
||||
HID_KEY_DECIMAL_SEPARATOR = 0xB3,
|
||||
HID_KEY_CURRENCY_UNIT = 0xB4,
|
||||
HID_KEY_CURRENCY_SUB_UNIT = 0xB5,
|
||||
HID_KEY_KEYPAD_OPEN_PARENTHESIS = 0xB6,
|
||||
HID_KEY_KEYPAD_CLOSE_PARENTHESIS = 0xB7,
|
||||
HID_KEY_KEYPAD_OPEN_BRACE = 0xB8,
|
||||
HID_KEY_KEYPAD_CLOSE_BRACE = 0xB9,
|
||||
HID_KEY_KEYPAD_TAB = 0xBA,
|
||||
HID_KEY_KEYPAD_BACKSPACE = 0xBB,
|
||||
HID_KEY_KEYPAD_A = 0xBC,
|
||||
HID_KEY_KEYPAD_B = 0xBD,
|
||||
HID_KEY_KEYPAD_C = 0xBE,
|
||||
HID_KEY_KEYPAD_D = 0xBF,
|
||||
HID_KEY_KEYPAD_E = 0xC0,
|
||||
HID_KEY_KEYPAD_F = 0xC1,
|
||||
HID_KEY_KEYPAD_XOR = 0xC2,
|
||||
HID_KEY_KEYPAD_CARET = 0xC3,
|
||||
HID_KEY_KEYPAD_PERCENT = 0xC4,
|
||||
HID_KEY_KEYPAD_LESSER = 0xC5,
|
||||
HID_KEY_KEYPAD_GREATER = 0xC6,
|
||||
HID_KEY_KEYPAD_AND = 0xC7,
|
||||
HID_KEY_KEYPAD_LOGICAL_AND = 0xC8,
|
||||
HID_KEY_KEYPAD_OR = 0xC9,
|
||||
HID_KEY_KEYPAD_LOGICAL_OR = 0xCA,
|
||||
HID_KEY_KEYPAD_COLON = 0xCB,
|
||||
HID_KEY_KEYPAD_SHARP = 0xCC,
|
||||
HID_KEY_KEYPAD_SPACE = 0xCD,
|
||||
HID_KEY_KEYPAD_AT = 0xCE,
|
||||
HID_KEY_KEYPAD_BANG = 0xCF,
|
||||
HID_KEY_KEYPAD_MEMORY_STORE = 0xD0,
|
||||
HID_KEY_KEYPAD_MEMORY_RECALL = 0xD1,
|
||||
HID_KEY_KEYPAD_MEMORY_CLEAD = 0xD2,
|
||||
HID_KEY_KEYPAD_MEMORY_ADD = 0xD3,
|
||||
HID_KEY_KEYPAD_MEMORY_SUBSTRACT = 0xD4,
|
||||
HID_KEY_KEYPAD_MEMORY_MULTIPLY = 0xD5,
|
||||
HID_KEY_KEYPAD_MEMORY_DIVIDE = 0xD6,
|
||||
HID_KEY_KEYPAD_SIGN = 0xD7,
|
||||
HID_KEY_KEYPAD_CLEAR = 0xD8,
|
||||
HID_KEY_KEYPAD_CLEAR_ENTRY = 0xD9,
|
||||
HID_KEY_KEYPAD_BINARY = 0xDA,
|
||||
HID_KEY_KEYPAD_OCTAL = 0xDB,
|
||||
HID_KEY_KEYPAD_DECIMAL = 0xDC,
|
||||
HID_KEY_KEYPAD_HEXADECIMAL = 0xDD,
|
||||
HID_KEY_LEFT_CONTROL = 0xE0,
|
||||
HID_KEY_LEFT_SHIFT = 0xE1,
|
||||
HID_KEY_LEFT_ALT = 0xE2,
|
||||
HID_KEY_LEFT_GUI = 0xE3,
|
||||
HID_KEY_RIGHT_CONTROL = 0xE0,
|
||||
HID_KEY_RIGHT_SHIFT = 0xE1,
|
||||
HID_KEY_RIGHT_ALT = 0xE2,
|
||||
HID_KEY_RIGHT_GUI = 0xE3
|
||||
} __attribute__((packed)) hid_key_t;
|
||||
|
||||
// Modifier bit mask
|
||||
#define HID_LEFT_CONTROL (1 << 0)
|
||||
#define HID_LEFT_SHIFT (1 << 1)
|
||||
#define HID_LEFT_ALT (1 << 2)
|
||||
#define HID_LEFT_GUI (1 << 3)
|
||||
#define HID_RIGHT_CONTROL (1 << 4)
|
||||
#define HID_RIGHT_SHIFT (1 << 5)
|
||||
#define HID_RIGHT_ALT (1 << 6)
|
||||
#define HID_RIGHT_GUI (1 << 7)
|
||||
|
||||
/**
|
||||
* @brief HID Keyboard Key number for Boot Interface
|
||||
*
|
||||
* @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_KEYBOARD_KEY_NUMBER0 = 0,
|
||||
HID_KEYBOARD_KEY_NUMBER1,
|
||||
HID_KEYBOARD_KEY_NUMBER2,
|
||||
HID_KEYBOARD_KEY_NUMBER3,
|
||||
HID_KEYBOARD_KEY_NUMBER4,
|
||||
HID_KEYBOARD_KEY_NUMBER5,
|
||||
HID_KEYBOARD_KEY_MAX,
|
||||
} hid_keyboard_key_number_t;
|
||||
|
||||
/**
|
||||
* @brief HID Keyboard Input Report for Boot Interfaces
|
||||
*
|
||||
* @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint8_t left_ctr: 1;
|
||||
uint8_t left_shift: 1;
|
||||
uint8_t left_alt: 1;
|
||||
uint8_t left_gui: 1;
|
||||
uint8_t rigth_ctr: 1;
|
||||
uint8_t right_shift: 1;
|
||||
uint8_t right_alt: 1;
|
||||
uint8_t right_gui: 1;
|
||||
};
|
||||
uint8_t val;
|
||||
} modifier;
|
||||
uint8_t reserved;
|
||||
uint8_t key[HID_KEYBOARD_KEY_MAX];
|
||||
} __attribute__((packed)) hid_keyboard_input_report_boot_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief HID Mouse Input Report for Boot Interfaces
|
||||
*
|
||||
* @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint8_t button1: 1;
|
||||
uint8_t button2: 1;
|
||||
uint8_t button3: 1;
|
||||
uint8_t reserved: 5;
|
||||
};
|
||||
uint8_t val;
|
||||
} buttons;
|
||||
int8_t x_displacement;
|
||||
int8_t y_displacement;
|
||||
} __attribute__((packed)) hid_mouse_input_report_boot_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
@@ -0,0 +1,15 @@
|
||||
# The following 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)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
../../usb_host_hid
|
||||
)
|
||||
|
||||
# Set the components to include the tests for.
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
list(APPEND EXTRA_COMPONENT_DIRS ../../../../../device/esp_tinyusb)
|
||||
endif()
|
||||
|
||||
project(test_app_usb_host_hid)
|
||||
@@ -0,0 +1,4 @@
|
||||
| Supported Targets | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
# USB: HID Class test application
|
||||
@@ -0,0 +1,25 @@
|
||||
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
|
||||
set (TINYUSB_LIB)
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
set(TINYUSB_LIB "esp_tinyusb")
|
||||
else()
|
||||
set(TINYUSB_LIB "tinyusb")
|
||||
endif()
|
||||
|
||||
# TODO: once IDF_v4.4 is at the EOL support, use WHOLE_ARCHIVE
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity usb usb_host_hid ${TINYUSB_LIB})
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
|
||||
# Due to the backward compatibility to IDFv4.4 (in which WHOLE_ARCHIVE is not implemented) we use following approach:
|
||||
# Any non-static function test_app/main/*.c (apart from test_app_main.c) file is added as an undefined symbol
|
||||
# because otherwise the linker will ignore test_app/main/*.c as it has no other files depending on any
|
||||
# symbols in it.
|
||||
|
||||
# force-link test_hid_basic.c
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u test_hid_setup")
|
||||
# force-link test_hid_err_handling.c
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u test_interface_callback_handler")
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "tinyusb.h"
|
||||
#include "class/hid/hid_device.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "hid_mock_device.h"
|
||||
|
||||
static tusb_iface_count_t tusb_iface_count = 0;
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
/************* TinyUSB descriptors ****************/
|
||||
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_HID * TUD_HID_DESC_LEN)
|
||||
|
||||
/**
|
||||
* @brief HID report descriptor
|
||||
*
|
||||
* In this example we implement Keyboard + Mouse HID device,
|
||||
* so we must define both report descriptors
|
||||
*/
|
||||
const uint8_t hid_report_descriptor[] = {
|
||||
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_ITF_PROTOCOL_KEYBOARD) ),
|
||||
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(HID_ITF_PROTOCOL_MOUSE) )
|
||||
};
|
||||
|
||||
const uint8_t hid_keyboard_report_descriptor[] = {
|
||||
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_ITF_PROTOCOL_KEYBOARD) )
|
||||
};
|
||||
|
||||
const uint8_t hid_mouse_report_descriptor[] = {
|
||||
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(HID_ITF_PROTOCOL_MOUSE) )
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief String descriptor
|
||||
*/
|
||||
const char *hid_string_descriptor[5] = {
|
||||
// array of pointer to string descriptors
|
||||
(char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Device", // 2: Product
|
||||
"123456", // 3: Serials, should use chip ID
|
||||
"Example HID interface", // 4: HID
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Configuration descriptor
|
||||
*
|
||||
* This is a simple configuration descriptor that defines 1 configuration and 1 HID interface
|
||||
*/
|
||||
static const uint8_t hid_configuration_descriptor_one_iface[] = {
|
||||
// Configuration number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, CFG_TUD_HID, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
|
||||
// Interface number, string index, boot protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(0, 4, false, sizeof(hid_report_descriptor), 0x81, 16, 10),
|
||||
TUD_HID_DESCRIPTOR(1, 4, false, sizeof(hid_report_descriptor), 0x82, 16, 10),
|
||||
};
|
||||
|
||||
static const uint8_t hid_configuration_descriptor_two_ifaces[] = {
|
||||
// Configuration number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, CFG_TUD_HID, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
|
||||
// Interface number, string index, boot protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(0, 4, HID_ITF_PROTOCOL_KEYBOARD, sizeof(hid_keyboard_report_descriptor), 0x81, 16, 10),
|
||||
TUD_HID_DESCRIPTOR(2, 4, HID_ITF_PROTOCOL_MOUSE, sizeof(hid_mouse_report_descriptor), 0x82, 16, 10),
|
||||
};
|
||||
|
||||
static const uint8_t *hid_configuration_descriptor_list[TUSB_IFACE_COUNT_MAX] = {
|
||||
hid_configuration_descriptor_one_iface,
|
||||
hid_configuration_descriptor_two_ifaces
|
||||
};
|
||||
#endif // // esp idf >= v5.0.0
|
||||
/********* TinyUSB HID callbacks ***************/
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
// Invoked when received GET HID REPORT DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance)
|
||||
{
|
||||
switch (tusb_iface_count) {
|
||||
case TUSB_IFACE_COUNT_ONE:
|
||||
return hid_report_descriptor;
|
||||
|
||||
case TUSB_IFACE_COUNT_TWO:
|
||||
return (!!instance) ? hid_mouse_report_descriptor : hid_keyboard_report_descriptor;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif // esp idf >= v5.0.0
|
||||
|
||||
#if ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)))
|
||||
#define HID_ITF_PROTOCOL_KEYBOARD HID_PROTOCOL_KEYBOARD
|
||||
#define HID_ITF_PROTOCOL_MOUSE HID_PROTOCOL_MOUSE
|
||||
#endif // 4.4.0 <= esp idf < v5.0.0
|
||||
|
||||
/**
|
||||
* @brief Get Keyboard report
|
||||
*
|
||||
* Fill buffer with test Keyboard report data
|
||||
*
|
||||
* @param[in] buffer Pointer to a buffer for filling
|
||||
* @return uint16_t Length of copied data to buffer
|
||||
*/
|
||||
static inline uint16_t get_keyboard_report(uint8_t *buffer)
|
||||
{
|
||||
hid_keyboard_report_t kb_report = {
|
||||
0, // Keyboard modifier
|
||||
0, // Reserved
|
||||
{ HID_KEY_M, HID_KEY_N, HID_KEY_O, HID_KEY_P, HID_KEY_Q, HID_KEY_R }
|
||||
};
|
||||
memcpy(buffer, &kb_report, sizeof(kb_report));
|
||||
return sizeof(kb_report);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get Mouse report
|
||||
*
|
||||
* Fill buffer with test Mouse report data
|
||||
*
|
||||
* @param[in] buffer Pointer to a buffer for filling
|
||||
* @return uint16_t Length of copied data to buffer
|
||||
*/
|
||||
static inline uint16_t get_mouse_report(uint8_t *buffer)
|
||||
{
|
||||
hid_mouse_report_t mouse_report = {
|
||||
MOUSE_BUTTON_LEFT | MOUSE_BUTTON_RIGHT, // buttons
|
||||
-1, // x
|
||||
127, // y
|
||||
0, // wheel
|
||||
0 // pan
|
||||
};
|
||||
memcpy(buffer, &mouse_report, sizeof(mouse_report));
|
||||
return sizeof(mouse_report);
|
||||
}
|
||||
|
||||
// Invoked when received GET_REPORT control request
|
||||
// Application must fill buffer report's content and return its length.
|
||||
// Return zero will cause the stack to STALL request
|
||||
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
switch (report_id) {
|
||||
case HID_ITF_PROTOCOL_KEYBOARD:
|
||||
return get_keyboard_report(buffer);
|
||||
|
||||
case HID_ITF_PROTOCOL_MOUSE:
|
||||
return get_mouse_report(buffer);
|
||||
|
||||
default:
|
||||
printf("HID mock device, Unhandled ReportID %d\n", report_id);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invoked when received SET_REPORT control request or
|
||||
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
|
||||
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HID Mock device start
|
||||
*
|
||||
* @param[in] iface_count Interface count, when TUSB_IFACE_COUNT_ONE then there is two Interfaces, but equal (Protocol=None).
|
||||
* when TUSB_IFACE_COUNT_TWO then HID device mocked with two independent Interfaces (Protocol=BootKeyboard, Protocol=BootMouse).
|
||||
*/
|
||||
void hid_mock_device(tusb_iface_count_t iface_count)
|
||||
{
|
||||
if (iface_count > TUSB_IFACE_COUNT_MAX) {
|
||||
printf("UHID mock device, wrong iface_count paramteter (%d)\n",
|
||||
iface_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// Global Interfaces count value
|
||||
tusb_iface_count = iface_count;
|
||||
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
.device_descriptor = NULL,
|
||||
.string_descriptor = hid_string_descriptor,
|
||||
.string_descriptor_count = sizeof(hid_string_descriptor) / sizeof(hid_string_descriptor[0]),
|
||||
.configuration_descriptor = hid_configuration_descriptor_list[tusb_iface_count],
|
||||
#endif // esp idf >= v5.0.0
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
printf("HID mock device with %s has been started\n",
|
||||
(TUSB_IFACE_COUNT_ONE == tusb_iface_count)
|
||||
? "1xInterface (Protocol=None)"
|
||||
: "2xInterfaces (Protocol=BootKeyboard, Protocol=BootMouse)");
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
TUSB_IFACE_COUNT_ONE = 0x00,
|
||||
TUSB_IFACE_COUNT_TWO = 0x01,
|
||||
TUSB_IFACE_COUNT_MAX
|
||||
} tusb_iface_count_t;
|
||||
|
||||
void hid_mock_device(tusb_iface_count_t iface_count);
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-530)
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// ____ ___ ___________________ __ __
|
||||
// | | \/ _____/\______ \ _/ |_ ____ _______/ |_
|
||||
// | | /\_____ \ | | _/ \ __\/ __ \ / ___/\ __\.
|
||||
// | | / / \ | | \ | | \ ___/ \___ \ | |
|
||||
// |______/ /_______ / |______ / |__| \___ >____ > |__|
|
||||
// \/ \/ \/ \/
|
||||
printf(" ____ ___ ___________________ __ __ \r\n");
|
||||
printf("| | \\/ _____/\\______ \\ _/ |_ ____ _______/ |_ \r\n");
|
||||
printf("| | /\\_____ \\ | | _/ \\ __\\/ __ \\ / ___/\\ __\\\r\n");
|
||||
printf("| | / / \\ | | \\ | | \\ ___/ \\___ \\ | | \r\n");
|
||||
printf("|______/ /_______ / |______ / |__| \\___ >____ > |__| \r\n");
|
||||
printf(" \\/ \\/ \\/ \\/ \r\n");
|
||||
|
||||
UNITY_BEGIN();
|
||||
unity_run_menu();
|
||||
UNITY_END();
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
@@ -0,0 +1,684 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "unity.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_private/usb_phy.h"
|
||||
#include "usb/usb_host.h"
|
||||
|
||||
#include "usb/hid_host.h"
|
||||
#include "usb/hid_usage_keyboard.h"
|
||||
#include "usb/hid_usage_mouse.h"
|
||||
|
||||
#include "test_hid_basic.h"
|
||||
#include "hid_mock_device.h"
|
||||
|
||||
// USB PHY for device discinnection emulation
|
||||
static usb_phy_handle_t phy_hdl = NULL;
|
||||
|
||||
// Global variable to verify user arg passing through callbacks
|
||||
static uint32_t user_arg_value = 0x8A53E0A4; // Just a constant renadom number
|
||||
|
||||
// Queue and task for possibility to interact with USB device
|
||||
// IMPORTANT: Interaction is not possible within device/interface callback
|
||||
static bool time_to_shutdown = false;
|
||||
static bool time_to_stop_polling = false;
|
||||
QueueHandle_t hid_host_test_event_queue;
|
||||
TaskHandle_t hid_test_task_handle;
|
||||
|
||||
// Multiple tasks testing
|
||||
static hid_host_device_handle_t global_hdl;
|
||||
static int test_num_passed;
|
||||
|
||||
static const char *test_hid_sub_class_names[] = {
|
||||
"NO_SUBCLASS",
|
||||
"BOOT_INTERFACE",
|
||||
};
|
||||
|
||||
static const char *test_hid_proto_names[] = {
|
||||
"NONE",
|
||||
"KEYBOARD",
|
||||
"MOUSE"
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
hid_host_device_handle_t hid_device_handle;
|
||||
hid_host_driver_event_t event;
|
||||
void *arg;
|
||||
} hid_host_test_event_queue_t;
|
||||
|
||||
typedef enum {
|
||||
HID_HOST_TEST_TOUCH_WAY_ASSERT = 0x00,
|
||||
HID_HOST_TEST_TOUCH_WAY_SUDDEN_DISCONNECT = 0x01,
|
||||
} hid_host_test_touch_way_t;
|
||||
|
||||
static void force_conn_state(bool connected, TickType_t delay_ticks)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(phy_hdl);
|
||||
if (delay_ticks > 0) {
|
||||
//Delay of 0 ticks causes a yield. So skip if delay_ticks is 0.
|
||||
vTaskDelay(delay_ticks);
|
||||
}
|
||||
ESP_ERROR_CHECK(usb_phy_action(phy_hdl, (connected) ? USB_PHY_ACTION_HOST_ALLOW_CONN : USB_PHY_ACTION_HOST_FORCE_DISCONN));
|
||||
}
|
||||
|
||||
void hid_host_test_interface_callback(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_interface_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
uint8_t data[64] = { 0 };
|
||||
size_t data_length = 0;
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(hid_device_handle, &dev_params));
|
||||
TEST_ASSERT_EQUAL_PTR_MESSAGE(&user_arg_value, arg, "User argument has lost");
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
|
||||
printf("USB port %d, Interface num %d: ",
|
||||
dev_params.addr,
|
||||
dev_params.iface_num);
|
||||
|
||||
hid_host_device_get_raw_input_report_data(hid_device_handle,
|
||||
data,
|
||||
64,
|
||||
&data_length);
|
||||
|
||||
for (int i = 0; i < data_length; i++) {
|
||||
printf("%02x ", data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
|
||||
printf("USB port %d, iface num %d removed\n",
|
||||
dev_params.addr,
|
||||
dev_params.iface_num);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_close(hid_device_handle) );
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
|
||||
printf("USB Host transfer error\n");
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL_MESSAGE("HID Interface unhandled event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_host_test_callback(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(hid_device_handle, &dev_params));
|
||||
TEST_ASSERT_EQUAL_PTR_MESSAGE(&user_arg_value, arg, "User argument has lost");
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_DRIVER_EVENT_CONNECTED:
|
||||
printf("USB port %d, interface %d, '%s', '%s'\n",
|
||||
dev_params.addr,
|
||||
dev_params.iface_num,
|
||||
test_hid_sub_class_names[dev_params.sub_class],
|
||||
test_hid_proto_names[dev_params.proto]);
|
||||
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = hid_host_test_interface_callback,
|
||||
.callback_arg = (void *) &user_arg_value
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_start(hid_device_handle) );
|
||||
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL_MESSAGE("HID Driver unhandled event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_host_test_concurrent(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(hid_device_handle, &dev_params));
|
||||
TEST_ASSERT_EQUAL_PTR_MESSAGE(&user_arg_value, arg, "User argument has lost");
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_DRIVER_EVENT_CONNECTED:
|
||||
printf("USB port %d, interface %d, '%s', '%s'\n",
|
||||
dev_params.addr,
|
||||
dev_params.iface_num,
|
||||
test_hid_sub_class_names[dev_params.sub_class],
|
||||
test_hid_proto_names[dev_params.proto]);
|
||||
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = hid_host_test_interface_callback,
|
||||
.callback_arg = &user_arg_value
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_start(hid_device_handle) );
|
||||
|
||||
global_hdl = hid_device_handle;
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL_MESSAGE("HID Driver unhandled event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_host_test_device_callback_to_queue(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
const hid_host_test_event_queue_t evt_queue = {
|
||||
.hid_device_handle = hid_device_handle,
|
||||
.event = event,
|
||||
.arg = arg
|
||||
};
|
||||
xQueueSend(hid_host_test_event_queue, &evt_queue, 0);
|
||||
}
|
||||
|
||||
void hid_host_test_requests_callback(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(hid_device_handle, &dev_params));
|
||||
TEST_ASSERT_EQUAL_PTR_MESSAGE(&user_arg_value, arg, "User argument has lost");
|
||||
|
||||
uint8_t *test_buffer = NULL; // for report descriptor
|
||||
unsigned int test_length = 0;
|
||||
uint8_t tmp[10] = { 0 }; // for input report
|
||||
size_t rep_len = 0;
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_DRIVER_EVENT_CONNECTED:
|
||||
printf("USB port %d, interface %d, '%s', '%s'\n",
|
||||
dev_params.addr,
|
||||
dev_params.iface_num,
|
||||
test_hid_sub_class_names[dev_params.sub_class],
|
||||
test_hid_proto_names[dev_params.proto]);
|
||||
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = hid_host_test_interface_callback,
|
||||
.callback_arg = &user_arg_value
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
|
||||
// Class device requests
|
||||
// hid_host_get_report_descriptor
|
||||
test_buffer = hid_host_get_report_descriptor(hid_device_handle, &test_length);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_buffer);
|
||||
printf("HID Report descriptor length: %d\n", test_length);
|
||||
|
||||
// // HID Device info
|
||||
hid_host_dev_info_t hid_dev_info;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_get_device_info(hid_device_handle,
|
||||
&hid_dev_info) );
|
||||
|
||||
printf("\t VID: 0x%04X\n", hid_dev_info.VID);
|
||||
printf("\t PID: 0x%04X\n", hid_dev_info.PID);
|
||||
wprintf(L"\t iProduct: %S \n", hid_dev_info.iProduct);
|
||||
wprintf(L"\t iManufacturer: %S \n", hid_dev_info.iManufacturer);
|
||||
wprintf(L"\t iSerialNumber: %S \n", hid_dev_info.iSerialNumber);
|
||||
|
||||
if (dev_params.proto == HID_PROTOCOL_NONE) {
|
||||
// If Protocol NONE, based on hid1_11.pdf, p.78, all other devices should support
|
||||
rep_len = sizeof(tmp);
|
||||
// For testing with ESP32 we used ReportID = 0x01 (Keyboard ReportID)
|
||||
if (ESP_OK == hid_class_request_get_report(hid_device_handle,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len)) {
|
||||
printf("HID Get Report, type %d, id %d, length: %d:\n",
|
||||
HID_REPORT_TYPE_INPUT, 0, rep_len);
|
||||
for (int i = 0; i < rep_len; i++) {
|
||||
printf("%02X ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
rep_len = sizeof(tmp);
|
||||
// For testing with ESP32 we used ReportID = 0x02 (Mouse ReportID)
|
||||
if (ESP_OK == hid_class_request_get_report(hid_device_handle,
|
||||
HID_REPORT_TYPE_INPUT, 0x02, tmp, &rep_len)) {
|
||||
printf("HID Get Report, type %d, id %d, length: %d:\n",
|
||||
HID_REPORT_TYPE_INPUT, 0, rep_len);
|
||||
for (int i = 0; i < rep_len; i++) {
|
||||
printf("%02X ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
} else {
|
||||
// hid_class_request_get_protocol
|
||||
hid_report_protocol_t proto;
|
||||
if (ESP_OK == hid_class_request_get_protocol(hid_device_handle, &proto)) {
|
||||
printf("HID protocol: %d\n", proto);
|
||||
}
|
||||
|
||||
if (dev_params.proto == HID_PROTOCOL_KEYBOARD) {
|
||||
uint8_t idle_rate;
|
||||
// hid_class_request_get_idle
|
||||
if (ESP_OK == hid_class_request_get_idle(hid_device_handle,
|
||||
0, &idle_rate)) {
|
||||
printf("HID idle rate: %d\n", idle_rate);
|
||||
}
|
||||
// hid_class_request_set_idle
|
||||
if (ESP_OK == hid_class_request_set_idle(hid_device_handle,
|
||||
0, 0)) {
|
||||
printf("HID idle rate set to 0\n");
|
||||
}
|
||||
|
||||
// hid_class_request_get_report
|
||||
rep_len = sizeof(tmp);
|
||||
if (ESP_OK == hid_class_request_get_report(hid_device_handle,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len)) {
|
||||
printf("HID get report type %d, id %d, length: %d\n",
|
||||
HID_REPORT_TYPE_INPUT, 0x00, rep_len);
|
||||
}
|
||||
|
||||
// hid_class_request_set_report
|
||||
uint8_t rep[1] = { 0x00 };
|
||||
if (ESP_OK == hid_class_request_set_report(hid_device_handle,
|
||||
HID_REPORT_TYPE_OUTPUT, 0x01, rep, 1)) {
|
||||
printf("HID set report type %d, id %d\n", HID_REPORT_TYPE_OUTPUT, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
if (dev_params.proto == HID_PROTOCOL_MOUSE) {
|
||||
// hid_class_request_get_report
|
||||
rep_len = sizeof(tmp);
|
||||
if (ESP_OK == hid_class_request_get_report(hid_device_handle,
|
||||
HID_REPORT_TYPE_INPUT, 0x02, tmp, &rep_len)) {
|
||||
printf("HID get report type %d, id %d, length: %d\n",
|
||||
HID_REPORT_TYPE_INPUT, 0x00, rep_len);
|
||||
}
|
||||
}
|
||||
|
||||
// hid_class_request_set_protocol
|
||||
if (ESP_OK == hid_class_request_set_protocol(hid_device_handle,
|
||||
HID_REPORT_PROTOCOL_BOOT)) {
|
||||
printf("HID protocol change to BOOT: %d\n", proto);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_start(hid_device_handle) );
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL_MESSAGE("HID Driver unhandled event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_host_test_task(void *pvParameters)
|
||||
{
|
||||
hid_host_test_event_queue_t evt_queue;
|
||||
// Create queue
|
||||
hid_host_test_event_queue = xQueueCreate(10, sizeof(hid_host_test_event_queue_t));
|
||||
|
||||
// Wait queue
|
||||
while (!time_to_shutdown) {
|
||||
if (xQueueReceive(hid_host_test_event_queue, &evt_queue, pdMS_TO_TICKS(50))) {
|
||||
hid_host_test_requests_callback(evt_queue.hid_device_handle,
|
||||
evt_queue.event,
|
||||
evt_queue.arg);
|
||||
}
|
||||
}
|
||||
|
||||
xQueueReset(hid_host_test_event_queue);
|
||||
vQueueDelete(hid_host_test_event_queue);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void hid_host_test_polling_task(void *pvParameters)
|
||||
{
|
||||
// Wait queue
|
||||
while (!time_to_stop_polling) {
|
||||
hid_host_handle_events(portMAX_DELAY);
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void test_hid_host_device_touch(hid_host_dev_params_t *dev_params,
|
||||
hid_host_test_touch_way_t touch_way)
|
||||
{
|
||||
uint8_t tmp[10] = { 0 }; // for input report
|
||||
size_t rep_len = 0;
|
||||
hid_report_protocol_t proto;
|
||||
|
||||
if (dev_params->proto == HID_PROTOCOL_NONE) {
|
||||
rep_len = sizeof(tmp);
|
||||
// For testing with ESP32 we used ReportID = 0x01 (Keyboard ReportID)
|
||||
if (HID_HOST_TEST_TOUCH_WAY_ASSERT == touch_way) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len));
|
||||
} else {
|
||||
hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Get Protocol
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_class_request_get_protocol(global_hdl, &proto));
|
||||
// Get Report for Keyboard protocol, ReportID = 0x00 (Boot Keyboard ReportID)
|
||||
if (dev_params->proto == HID_PROTOCOL_KEYBOARD) {
|
||||
rep_len = sizeof(tmp);
|
||||
if (HID_HOST_TEST_TOUCH_WAY_ASSERT == touch_way) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len));
|
||||
} else {
|
||||
hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len);
|
||||
}
|
||||
}
|
||||
if (dev_params->proto == HID_PROTOCOL_MOUSE) {
|
||||
rep_len = sizeof(tmp);
|
||||
if (HID_HOST_TEST_TOUCH_WAY_ASSERT == touch_way) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x02, tmp, &rep_len));
|
||||
} else {
|
||||
hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x02, tmp, &rep_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define MULTIPLE_TASKS_TASKS_NUM 10
|
||||
|
||||
void concurrent_task(void *arg)
|
||||
{
|
||||
uint8_t *test_buffer = NULL;
|
||||
unsigned int test_length = 0;
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(global_hdl, &dev_params));
|
||||
|
||||
// Get Report descriptor
|
||||
test_buffer = hid_host_get_report_descriptor(global_hdl, &test_length);
|
||||
TEST_ASSERT_NOT_NULL(test_buffer);
|
||||
|
||||
test_hid_host_device_touch(&dev_params, HID_HOST_TEST_TOUCH_WAY_ASSERT);
|
||||
test_num_passed++;
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void access_task(void *arg)
|
||||
{
|
||||
uint8_t *test_buffer = NULL;
|
||||
unsigned int test_length = 0;
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(global_hdl, &dev_params));
|
||||
|
||||
// Get Report descriptor
|
||||
test_buffer = hid_host_get_report_descriptor(global_hdl, &test_length);
|
||||
TEST_ASSERT_NOT_NULL(test_buffer);
|
||||
|
||||
while (!time_to_shutdown) {
|
||||
test_hid_host_device_touch(&dev_params, HID_HOST_TEST_TOUCH_WAY_ASSERT/* HID_HOST_TEST_TOUCH_WAY_SUDDEN_DISCONNECT */);
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates MULTIPLE_TASKS_TASKS_NUM to get report descriptor and get protocol from HID device.
|
||||
* After test_num_passed - is a global static variable that increases during every successful task test.
|
||||
*/
|
||||
void test_multiple_tasks_access(void)
|
||||
{
|
||||
// Create tasks that will try to access HID dev with global hdl
|
||||
for (int i = 0; i < MULTIPLE_TASKS_TASKS_NUM; i++) {
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(concurrent_task, "HID multi touch", 4096, NULL, i + 3, NULL));
|
||||
}
|
||||
// Wait until all tasks finish
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
|
||||
void test_task_access(void)
|
||||
{
|
||||
// Create task which will be touching the device with control requests, while device is present
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(access_task, "HID touch", 4096, NULL, 3, NULL));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start USB Host and handle common USB host library events while devices/clients are present
|
||||
*
|
||||
* @param[in] arg Main task handle
|
||||
*/
|
||||
static void usb_lib_task(void *arg)
|
||||
{
|
||||
// Initialize the internal USB PHY to connect to the USB OTG peripheral.
|
||||
// We manually install the USB PHY for testing
|
||||
usb_phy_config_t phy_config = {
|
||||
.controller = USB_PHY_CTRL_OTG,
|
||||
.target = USB_PHY_TARGET_INT,
|
||||
.otg_mode = USB_OTG_MODE_HOST,
|
||||
.otg_speed = USB_PHY_SPEED_UNDEFINED, //In Host mode, the speed is determined by the connected device
|
||||
};
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_hdl));
|
||||
|
||||
const usb_host_config_t host_config = {
|
||||
.skip_phy_setup = true,
|
||||
.intr_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
};
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_install(&host_config) );
|
||||
printf("USB Host installed\n");
|
||||
xTaskNotifyGive(arg);
|
||||
|
||||
bool all_clients_gone = false;
|
||||
bool all_dev_free = false;
|
||||
while (!all_clients_gone || !all_dev_free) {
|
||||
uint32_t event_flags;
|
||||
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
|
||||
|
||||
// Release devices once all clients has deregistered
|
||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
|
||||
usb_host_device_free_all();
|
||||
printf("USB Event flags: NO_CLIENTS\n");
|
||||
all_clients_gone = true;
|
||||
}
|
||||
// All devices were removed
|
||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
||||
printf("USB Event flags: ALL_FREE\n");
|
||||
all_dev_free = true;
|
||||
time_to_stop_polling = true;
|
||||
// Notify that device was being disconnected
|
||||
xTaskNotifyGive(arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Change global flag for all tasks still running
|
||||
time_to_shutdown = true;
|
||||
|
||||
// Clean up USB Host
|
||||
vTaskDelay(10); // Short delay to allow clients clean-up
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_uninstall());
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_hdl)); //Tear down USB PHY
|
||||
phy_hdl = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
// ----------------------- Public -------------------------
|
||||
|
||||
/**
|
||||
* @brief Setups HID testing
|
||||
*
|
||||
* - Create USB lib task
|
||||
* - Install HID Host driver
|
||||
*/
|
||||
void test_hid_setup(hid_host_driver_event_cb_t device_callback,
|
||||
hid_test_event_handle_t hid_test_event_handle)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(usb_lib_task,
|
||||
"usb_events",
|
||||
4096,
|
||||
xTaskGetCurrentTaskHandle(),
|
||||
2, NULL, 0));
|
||||
// Wait for notification from usb_lib_task
|
||||
ulTaskNotifyTake(false, 1000);
|
||||
|
||||
// HID host driver config
|
||||
const hid_host_driver_config_t hid_host_driver_config = {
|
||||
.create_background_task = (hid_test_event_handle == HID_TEST_EVENT_HANDLE_IN_DRIVER)
|
||||
? true
|
||||
: false,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = device_callback,
|
||||
.callback_arg = (void *) &user_arg_value
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_install(&hid_host_driver_config) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Teardowns HID testing
|
||||
* - Disconnect connected USB device manually by PHY triggering
|
||||
* - Wait for USB lib task was closed
|
||||
* - Uninstall HID Host driver
|
||||
* - Clear the notification value to 0
|
||||
* - Short delay to allow task to be cleaned up
|
||||
*/
|
||||
void test_hid_teardown(void)
|
||||
{
|
||||
force_conn_state(false, pdMS_TO_TICKS(1000));
|
||||
vTaskDelay(50);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_uninstall() );
|
||||
ulTaskNotifyValueClear(NULL, 1);
|
||||
vTaskDelay(20);
|
||||
}
|
||||
|
||||
// ------------------------- HID Test ------------------------------------------
|
||||
static void test_setup_hid_task(void)
|
||||
{
|
||||
// Task is working until the devices are gone
|
||||
time_to_shutdown = false;
|
||||
// Create process
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(&hid_host_test_task,
|
||||
"hid_task",
|
||||
4 * 1024,
|
||||
NULL,
|
||||
3,
|
||||
&hid_test_task_handle));
|
||||
}
|
||||
|
||||
static void test_setup_hid_polling_task(void)
|
||||
{
|
||||
time_to_stop_polling = false;
|
||||
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(&hid_host_test_polling_task,
|
||||
"hid_task_polling",
|
||||
4 * 1024,
|
||||
NULL, 2, NULL));
|
||||
}
|
||||
|
||||
TEST_CASE("memory_leakage", "[hid_host]")
|
||||
{
|
||||
// Install USB and HID driver with the regular 'hid_host_test_callback'
|
||||
test_hid_setup(hid_host_test_callback, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
// Verify the memory leackage during test environment tearDown()
|
||||
}
|
||||
|
||||
TEST_CASE("multiple_task_access", "[hid_host]")
|
||||
{
|
||||
// Install USB and HID driver with 'hid_host_test_concurrent'
|
||||
test_hid_setup(hid_host_test_concurrent, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Wait for USB device appearing for 250 msec
|
||||
vTaskDelay(250);
|
||||
// Refresh the num passed test value
|
||||
test_num_passed = 0;
|
||||
// Start multiple task access to USB device with control requests
|
||||
test_multiple_tasks_access();
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
// Verify how much tests was done
|
||||
TEST_ASSERT_EQUAL(MULTIPLE_TASKS_TASKS_NUM, test_num_passed);
|
||||
// Verify the memory leackage during test environment tearDown()
|
||||
}
|
||||
|
||||
TEST_CASE("class_specific_requests", "[hid_host]")
|
||||
{
|
||||
// Create external HID events task
|
||||
test_setup_hid_task();
|
||||
// Install USB and HID driver with 'hid_host_test_device_callback_to_queue'
|
||||
test_hid_setup(hid_host_test_device_callback_to_queue, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// All specific control requests will be verified during device connection callback 'hid_host_test_requests_callback'
|
||||
// Wait for test completed for 250 ms
|
||||
vTaskDelay(250);
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
// Verify the memory leackage during test environment tearDown()
|
||||
}
|
||||
|
||||
TEST_CASE("class_specific_requests_with_external_polling", "[hid_host]")
|
||||
{
|
||||
// Create external HID events task
|
||||
test_setup_hid_task();
|
||||
// Install USB and HID driver with 'hid_host_test_device_callback_to_queue'
|
||||
test_hid_setup(hid_host_test_device_callback_to_queue, HID_TEST_EVENT_HANDLE_EXTERNAL);
|
||||
// Create HID Driver events polling task
|
||||
test_setup_hid_polling_task();
|
||||
// All specific control requests will be verified during device connection callback 'hid_host_test_requests_callback'
|
||||
// Wait for test completed for 250 ms
|
||||
vTaskDelay(250);
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
// Verify the memory leackage during test environment tearDown()
|
||||
}
|
||||
|
||||
TEST_CASE("class_specific_requests_with_external_polling_without_polling", "[hid_host]")
|
||||
{
|
||||
// Create external HID events task
|
||||
test_setup_hid_task();
|
||||
// Install USB and HID driver with 'hid_host_test_device_callback_to_queue'
|
||||
test_hid_setup(hid_host_test_device_callback_to_queue, HID_TEST_EVENT_HANDLE_EXTERNAL);
|
||||
// Do not create HID Driver events polling task to eliminate events polling
|
||||
// ...
|
||||
// Wait for 250 ms
|
||||
vTaskDelay(250);
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
// Verify the memory leackage during test environment tearDown()
|
||||
}
|
||||
|
||||
TEST_CASE("sudden_disconnect", "[hid_host]")
|
||||
{
|
||||
// Install USB and HID driver with 'hid_host_test_concurrent'
|
||||
test_hid_setup(hid_host_test_concurrent, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Wait for USB device appearing for 250 msec
|
||||
vTaskDelay(250);
|
||||
// Start task to access USB device with control requests
|
||||
test_task_access();
|
||||
// Tear down test during thr task_access stress the HID device
|
||||
test_hid_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("mock_hid_device", "[hid_device][ignore]")
|
||||
{
|
||||
hid_mock_device(TUSB_IFACE_COUNT_ONE);
|
||||
while (1) {
|
||||
vTaskDelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("mock_hid_device_with_two_ifaces", "[hid_device2][ignore]")
|
||||
{
|
||||
hid_mock_device(TUSB_IFACE_COUNT_TWO);
|
||||
while (1) {
|
||||
vTaskDelay(10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "usb/hid_host.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
HID_TEST_EVENT_HANDLE_IN_DRIVER = 0,
|
||||
HID_TEST_EVENT_HANDLE_EXTERNAL
|
||||
} hid_test_event_handle_t;
|
||||
|
||||
// ------------------------ HID Test -------------------------------------------
|
||||
|
||||
void test_hid_setup(hid_host_driver_event_cb_t device_callback,
|
||||
hid_test_event_handle_t hid_test_event_handle);
|
||||
|
||||
void test_hid_teardown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "unity.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "usb/hid_host.h"
|
||||
|
||||
#include "test_hid_basic.h"
|
||||
|
||||
// ----------------------- Private -------------------------
|
||||
/**
|
||||
* @brief USB HID Host interface callback.
|
||||
*
|
||||
* Handle close event only.
|
||||
*
|
||||
* @param[in] event HID Host device event
|
||||
* @param[in] arg Pointer to arguments, does not used
|
||||
*
|
||||
*/
|
||||
static void test_hid_host_interface_event_close(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_interface_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
switch (event) {
|
||||
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
|
||||
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_close(hid_device_handle) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB HID Host event callback stub.
|
||||
*
|
||||
* Does not handle anything.
|
||||
*
|
||||
* @param[in] event HID Host device event
|
||||
* @param[in] arg Pointer to arguments, does not used
|
||||
*
|
||||
*/
|
||||
static void test_hid_host_event_callback_stub(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
if (event == HID_HOST_DRIVER_EVENT_CONNECTED) {
|
||||
// Device connected
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB HID Host event callback.
|
||||
*
|
||||
* Handle connected event and open a device.
|
||||
*
|
||||
* @param[in] event HID Host device event
|
||||
* @param[in] arg Pointer to arguments, does not used
|
||||
*
|
||||
*/
|
||||
static void test_hid_host_event_callback_open(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
if (event == HID_HOST_DRIVER_EVENT_CONNECTED) {
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = test_hid_host_interface_event_close,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
}
|
||||
}
|
||||
|
||||
// Install HID driver without USB Host and without configuration
|
||||
static void test_install_hid_driver_without_config(void)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(NULL));
|
||||
}
|
||||
|
||||
// Install HID driver without USB Host and with configuration
|
||||
static void test_install_hid_driver_with_wrong_config(void)
|
||||
{
|
||||
const hid_host_driver_config_t hid_host_config_callback_null = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = NULL, /* error expected */
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(&hid_host_config_callback_null));
|
||||
|
||||
const hid_host_driver_config_t hid_host_config_stack_size_null = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 0, /* error expected */
|
||||
.core_id = 0,
|
||||
.callback = test_hid_host_event_callback_stub,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(&hid_host_config_stack_size_null));
|
||||
|
||||
const hid_host_driver_config_t hid_host_config_task_priority_null = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 0,/* error expected */
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = test_hid_host_event_callback_stub,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(&hid_host_config_task_priority_null));
|
||||
|
||||
const hid_host_driver_config_t hid_host_config_correct = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = test_hid_host_event_callback_stub,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
// Invalid state without USB Host installed
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hid_host_install(&hid_host_config_correct));
|
||||
}
|
||||
|
||||
void test_interface_callback_handler(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_interface_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
// Open device without installed driver
|
||||
static void test_claim_interface_without_driver(void)
|
||||
{
|
||||
hid_host_device_handle_t hid_dev_handle = NULL;
|
||||
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = test_interface_callback_handler,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE,
|
||||
hid_host_device_open(hid_dev_handle, &dev_config) );
|
||||
}
|
||||
|
||||
static void test_install_hid_driver_when_already_installed(void)
|
||||
{
|
||||
// Install USB and HID driver with the stub test_hid_host_event_callback_stub
|
||||
test_hid_setup(test_hid_host_event_callback_stub, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Try to install HID driver again
|
||||
const hid_host_driver_config_t hid_host_config = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = test_hid_host_event_callback_stub,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
// Verify error code
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hid_host_install(&hid_host_config));
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
}
|
||||
|
||||
static void test_uninstall_hid_driver_while_device_was_not_opened(void)
|
||||
{
|
||||
// Install USB and HID driver with the stub test_hid_host_event_callback_stub
|
||||
test_hid_setup(test_hid_host_event_callback_stub, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
}
|
||||
|
||||
static void test_uninstall_hid_driver_while_device_is_present(void)
|
||||
{
|
||||
// Install USB and HID driver with the stub test_hid_host_event_callback_stub
|
||||
test_hid_setup(test_hid_host_event_callback_open, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Wait for USB device appearing for 250 msec
|
||||
vTaskDelay(250);
|
||||
// Uninstall HID Driver while device is still connected and verify a result
|
||||
printf("HID Driver uninstall attempt while HID Device is still present ...\n");
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hid_host_uninstall());
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
}
|
||||
|
||||
// ----------------------- Public --------------------------
|
||||
|
||||
/**
|
||||
* @brief HID Error handling test
|
||||
*
|
||||
* There are multiple erroneous scenarios checked in this test.
|
||||
*
|
||||
*/
|
||||
TEST_CASE("error_handling", "[hid_host]")
|
||||
{
|
||||
test_install_hid_driver_without_config();
|
||||
test_install_hid_driver_with_wrong_config();
|
||||
test_claim_interface_without_driver();
|
||||
test_install_hid_driver_when_already_installed();
|
||||
test_uninstall_hid_driver_while_device_was_not_opened();
|
||||
test_uninstall_hid_driver_while_device_is_present();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
from pytest_embedded_idf.dut import IdfDut
|
||||
|
||||
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.usb_host
|
||||
@pytest.mark.parametrize('count', [
|
||||
2,
|
||||
], indirect=True)
|
||||
def test_usb_host_hid(dut: Tuple[IdfDut, IdfDut]) -> None:
|
||||
device = dut[0]
|
||||
host = dut[1]
|
||||
|
||||
# 3.1 Prepare USB device with one Interface for HID tests
|
||||
device.expect_exact('Press ENTER to see the list of tests.')
|
||||
device.write('[hid_device]')
|
||||
device.expect_exact('HID mock device with 1xInterface (Protocol=None) has been started')
|
||||
|
||||
# 3.2 Run HID tests
|
||||
host.run_all_single_board_cases(group='hid_host')
|
||||
|
||||
# 3.3 Prepare USB device with two Interfaces for HID tests
|
||||
device.serial.hard_reset()
|
||||
device.expect_exact('Press ENTER to see the list of tests.')
|
||||
device.write('[hid_device2]')
|
||||
device.expect_exact('HID mock device with 2xInterfaces (Protocol=BootKeyboard, Protocol=BootMouse) has been started')
|
||||
|
||||
# 3.4 Run HID tests
|
||||
host.run_all_single_board_cases(group='hid_host')
|
||||
@@ -0,0 +1,19 @@
|
||||
# Configure TinyUSB, it will be used to mock USB devices
|
||||
CONFIG_TINYUSB=y
|
||||
CONFIG_TINYUSB_MSC_ENABLED=n
|
||||
CONFIG_TINYUSB_CDC_ENABLED=n
|
||||
CONFIG_TINYUSB_CDC_COUNT=0
|
||||
CONFIG_TINYUSB_HID_COUNT=2
|
||||
|
||||
# Disable watchdogs, they'd get triggered during unity interactive menu
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
# Run-time checks of Heap and Stack
|
||||
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
||||
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
|
||||
CONFIG_COMPILER_STACK_CHECK=y
|
||||
|
||||
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
|
||||
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
Reference in New Issue
Block a user