diff --git a/components/console_cmd_mqtt/.cz.yaml b/components/console_cmd_mqtt/.cz.yaml new file mode 100644 index 00000000000..9d23eecba0d --- /dev/null +++ b/components/console_cmd_mqtt/.cz.yaml @@ -0,0 +1,8 @@ +--- +commitizen: + bump_message: 'bump(console): $current_version -> $new_version' + pre_bump_hooks: python ../../ci/changelog.py console_cmd_mqtt + tag_format: console_cmd_mqtt-v$version + version: 1.0.0 + version_files: + - idf_component.yml diff --git a/components/console_cmd_mqtt/CMakeLists.txt b/components/console_cmd_mqtt/CMakeLists.txt new file mode 100644 index 00000000000..e3b7e587f1d --- /dev/null +++ b/components/console_cmd_mqtt/CMakeLists.txt @@ -0,0 +1,7 @@ +idf_component_register(SRCS "console_mqtt.c" + INCLUDE_DIRS "." + PRIV_REQUIRES esp_netif console mqtt) + +if(CONFIG_MQTT_CMD_AUTO_REGISTRATION) + target_link_libraries(${COMPONENT_LIB} PRIVATE "-u console_cmd_mqtt_register") +endif() diff --git a/components/console_cmd_mqtt/Kconfig.projbuild b/components/console_cmd_mqtt/Kconfig.projbuild new file mode 100644 index 00000000000..235bcc4b31e --- /dev/null +++ b/components/console_cmd_mqtt/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "MQTT Configuration" + + config MQTT_CMD_AUTO_REGISTRATION + bool "Enable Console command mqtt Auto-registration" + default y + help + Enabling this allows for the autoregistration of the wifi command. + + config MQTT_BROKER_URL + string "Broker URL or IP address" + default "mqtt://mqtt.eclipseprojects.io" + help + URL or IP address of the broker to connect to + +endmenu diff --git a/components/console_cmd_mqtt/LICENSE b/components/console_cmd_mqtt/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/components/console_cmd_mqtt/LICENSE @@ -0,0 +1,201 @@ + 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. diff --git a/components/console_cmd_mqtt/README.md b/components/console_cmd_mqtt/README.md new file mode 100644 index 00000000000..261fec5e865 --- /dev/null +++ b/components/console_cmd_mqtt/README.md @@ -0,0 +1,75 @@ +# Console command mqtt +The component provides a console where mqtt commands can be executed. + + +## MQTT Configuration: +1. Broker: Use menuconfig **"MQTT Configuration"** to configure the broker url. + + +## API + +### Steps to enable console in an example code: +1. Add this component to your project using ```idf.py add-dependency``` command. +2. In the main file of the example, add the following line: + ```c + #include "console_mqtt.h" + ``` +3. Ensure esp-netif and NVS flash is initialized and default event loop is created in your app_main(): + ```c + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + ``` +4. In your app_main() function, add the following line as the last line: + ```c + ESP_ERROR_CHECK(console_cmd_init()); // Initialize console + + // Register all plugin command added to your project + ESP_ERROR_CHECK(console_cmd_all_register()); + + // To register only mqtt command skip calling console_cmd_all_register() + ESP_ERROR_CHECK(console_cmd_mqtt_register()); + + ESP_ERROR_CHECK(console_cmd_start()); // Start console + ``` + +Note: Auto-registration of a specific plugin command can be disabled from menuconfig. + +### Certificate Integration for Mutual Authentication +To enhance security and enable secure communication over MQTT, we have introduced the option to set certificates within the MQTT console library. With the new `set_mqtt_certs()` function, users can now conveniently supply client and server certificates along with their respective keys. +```c +set_mqtt_certs(client_cert_pem_start, client_cert_pem_end, client_key_pem_start, + client_key_pem_end, server_cert_pem_start, server_cert_pem_end); +``` +This function takes client certificate, client key and server certificate allowing users to specify the necessary certificate and key data. To utilize these certificates, users need to include additional arguments when establishing MQTT connections using the library. Specifically, users should provide the `--cert`, `--key`, and `--cafile` options along with the MQTT connection command. + +### Adding a plugin command or component: +To add a plugin command or any component from IDF component manager into your project, simply include an entry within the `idf_component.yml` file. + +For more details refer [IDF Component Manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html) + + +## Suported command: + +### mqtt: +``` +mqtt [-CD] [-h ] [-u ] [-P ] + mqtt command + -C, --connect Connect to a broker + -h, --host= Specify the host uri to connect to + -u, --username= Provide a username to be used for authenticating with the broker + -P, --password= Provide a password to be used for authenticating with the broker + , --cert Define the PEM encoded certificate for this client, if required by the server + , --key Define the PEM encoded private key for this client, if required by the server + , --cafile Define the PEM encoded CA certificates that are trusted + -D, --disconnect Disconnect from the broker + +mqtt_pub [-t ] [-m ] + mqtt publish command + -t, --topic= Topic to Subscribe/Publish + -m, --message= Message to Publish + +mqtt_sub [-U] [-t ] + mqtt subscribe command + -t, --topic= Topic to Subscribe/Publish + -U, --unsubscribe Unsubscribe from a topic +``` diff --git a/components/console_cmd_mqtt/console_mqtt.c b/components/console_cmd_mqtt/console_mqtt.c new file mode 100644 index 00000000000..c57532859c6 --- /dev/null +++ b/components/console_cmd_mqtt/console_mqtt.c @@ -0,0 +1,445 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "sdkconfig.h" +#include "esp_console.h" +#include "esp_event.h" +#include "esp_log.h" +#include "argtable3/argtable3.h" +#include "console_mqtt.h" +#include "mqtt_client.h" + +static const char *TAG = "console_mqtt"; + +#define CONNECT_HELP_MSG "mqtt -C -h -u -P \n" +#define PUBLISH_HELP_MSG "Usage: mqtt -P -t -d \n" +#define SUBSCRIBE_HELP_MSG "Usage: mqtt -S -t \n" +#define UNSUBSCRIBE_HELP_MSG "Usage: mqtt -U\n" +#define DISCONNECT_HELP_MSG "Usage: mqtt -D\n" + +#if CONFIG_MQTT_CMD_AUTO_REGISTRATION +/** + * Static registration of this plugin is achieved by defining the plugin description + * structure and placing it into .console_cmd_desc section. + * The name of the section and its placement is determined by linker.lf file in 'plugins' component. + */ +static const console_cmd_plugin_desc_t __attribute__((section(".console_cmd_desc"), used)) PLUGIN = { + .name = "console_cmd_mqtt", + .plugin_regd_fn = &console_cmd_mqtt_register +}; +#endif + +static struct { + struct arg_lit *connect; + struct arg_str *uri; + struct arg_str *username; + struct arg_str *password; + struct arg_lit *cert; + struct arg_lit *key; + struct arg_lit *cafile; + struct arg_lit *disconnect; + + struct arg_end *end; +} mqtt_args; + +static struct { + struct arg_str *topic; + struct arg_lit *unsubscribe; + + struct arg_end *end; +} mqtt_sub_args; + +static struct { + struct arg_str *topic; + struct arg_str *message; + + struct arg_end *end; +} mqtt_pub_args; + +static esp_mqtt_client_handle_t client_handle = NULL; + +static const uint8_t *client_cert_pem_start = NULL; +static const uint8_t *client_cert_pem_end = NULL; +static const uint8_t *client_key_pem_start = NULL; +static const uint8_t *client_key_pem_end = NULL; +static const uint8_t *server_cert_pem_start = NULL; +static const uint8_t *server_cert_pem_end = NULL; + +static void log_error_if_nonzero(const char *message, int error_code) +{ + if (error_code != 0) { + ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code); + } +} + +/* + * @brief Event handler registered to receive MQTT events + * + * This function is called by the MQTT client event loop. + * + * @param handler_args user data registered to the event. + * @param base Event base for the handler(always MQTT Base in this example). + * @param event_id The id for the received event. + * @param event_data The data for the event, esp_mqtt_event_handle_t. + */ +static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32, base, event_id); + esp_mqtt_event_handle_t event = event_data; + switch ((esp_mqtt_event_id_t)event_id) { + case MQTT_EVENT_BEFORE_CONNECT: + ESP_LOGI(TAG, "MQTT_EVENT_BEFORE_CONNECT"); + break; + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + ESP_LOGI(TAG, "TOPIC=%.*s\r\n", event->topic_len, event->topic); + ESP_LOGI(TAG, "DATA=%.*s\r\n", event->data_len, event->data); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { + log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err); + log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err); + log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno); + ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno)); + } + break; + default: + ESP_LOGI(TAG, "Other event id:%d", event->event_id); + break; + } +} + + +static esp_mqtt_client_config_t +get_mqtt_config_basic(const char *uri_i) +{ + char *uri; + asprintf(&uri, "%s", uri_i); + + esp_mqtt_client_config_t mqtt_cfg = { + .broker.address.uri = uri, + }; + + return mqtt_cfg; +} + + +static esp_mqtt_client_config_t +get_mqtt_config_username_password(const char *uri_i, const char *username_i, const char *password_i) +{ + char *uri, *username, *password; + + asprintf(&uri, "%s", uri_i); + asprintf(&username, "%s", username_i); + asprintf(&password, "%s", password_i); + + esp_mqtt_client_config_t mqtt_cfg = { + .broker.address.uri = uri, + .credentials.username = username, + .credentials.authentication.password = password + }; + + return mqtt_cfg; +} + + +static void mqtt_config_free(esp_mqtt_client_config_t mqtt_cfg) +{ + if (mqtt_cfg.broker.address.uri) { + free((char *)mqtt_cfg.broker.address.uri); + } + + if (mqtt_cfg.credentials.username) { + free((char *)mqtt_cfg.credentials.username); + } + + if (mqtt_cfg.credentials.authentication.password) { + free((char *)mqtt_cfg.credentials.authentication.password); + } +} + + +static int do_mqtt_cmd(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **)&mqtt_args); + if (nerrors != 0) { + arg_print_errors(stderr, mqtt_args.end, argv[0]); + return 1; + } + + if (mqtt_args.connect->count > 0) { + esp_mqtt_client_config_t mqtt_cfg; + + if (client_handle != NULL) { + ESP_LOGI(TAG, "mqtt client already connected"); + return 0; + } + + if ((mqtt_args.username->count > 0) && (mqtt_args.password->count > 0)) { + if (mqtt_args.uri->count > 0) { + mqtt_cfg = get_mqtt_config_username_password(mqtt_args.uri->sval[0], + mqtt_args.username->sval[0], + mqtt_args.password->sval[0]); + } else { + mqtt_cfg = get_mqtt_config_username_password(CONFIG_MQTT_BROKER_URL, + mqtt_args.username->sval[0], + mqtt_args.password->sval[0]); + } + } else { + if (mqtt_args.uri->count > 0) { + mqtt_cfg = get_mqtt_config_basic(mqtt_args.uri->sval[0]); + } else { + mqtt_cfg = get_mqtt_config_basic(CONFIG_MQTT_BROKER_URL); + } + } + + ESP_LOGI(TAG, "broker: %s", mqtt_cfg.broker.address.uri); + + if (mqtt_args.cafile->count > 0) { + if (server_cert_pem_start && server_cert_pem_end) { + mqtt_cfg.broker.verification.certificate = (const char *)server_cert_pem_start; + } + } + + if (mqtt_args.cert->count > 0) { + if (client_cert_pem_start && client_cert_pem_end) { + mqtt_cfg.credentials.authentication.certificate = (const char *)client_cert_pem_start; + } + + if ((mqtt_args.key->count > 0) && client_key_pem_start && client_key_pem_end) { + mqtt_cfg.credentials.authentication.key = (const char *)client_key_pem_start; + } else { + mqtt_cfg.credentials.authentication.key = NULL; + } + } + + client_handle = esp_mqtt_client_init(&mqtt_cfg); + if (client_handle == NULL) { + ESP_LOGE(TAG, "ERROR: Client init"); + return 1; + } + + /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */ + esp_mqtt_client_register_event(client_handle, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); + esp_mqtt_client_start(client_handle); + + mqtt_config_free(mqtt_cfg); + + } else if (mqtt_args.disconnect->count > 0) { + ESP_LOGD(TAG, "Disconnect command received:"); + + if (client_handle == NULL) { + ESP_LOGE(TAG, "mqtt client not connected"); + return 0; + } + + if (esp_mqtt_client_stop(client_handle) != ESP_OK) { + ESP_LOGE(TAG, "Failed to stop mqtt client task"); + return 1; + } + + client_handle = NULL; + ESP_LOGI(TAG, "mqtt client disconnected"); + } + + return 0; +} + + +esp_err_t set_mqtt_certs(const uint8_t *client_cert_pem_start_i, const uint8_t *client_cert_pem_end_i, + const uint8_t *client_key_pem_start_i, const uint8_t *client_key_pem_end_i, + const uint8_t *server_cert_pem_start_i, const uint8_t *server_cert_pem_end_i) +{ + if (!client_cert_pem_start_i || !client_cert_pem_end_i || + (client_cert_pem_start_i > client_cert_pem_end_i)) { + ESP_LOGE(TAG, "Invalid mqtt Client certs(%d)\n", __LINE__); + return ESP_ERR_INVALID_ARG; + } + + if (client_key_pem_start_i && client_key_pem_end_i && + (client_key_pem_start_i >= client_key_pem_end_i)) { + ESP_LOGE(TAG, "Invalid mqtt cert key(%d)\n", __LINE__); + return ESP_ERR_INVALID_ARG; + } + + if (!server_cert_pem_start_i || !server_cert_pem_end_i || + (server_cert_pem_start_i > server_cert_pem_end_i)) { + ESP_LOGE(TAG, "Invalid mqtt certs(%d)\n", __LINE__); + return ESP_ERR_INVALID_ARG; + } + + client_cert_pem_start = client_cert_pem_start_i; + client_cert_pem_end = client_cert_pem_end_i; + client_key_pem_start = client_key_pem_start_i; + client_key_pem_end = client_key_pem_end_i; + server_cert_pem_start = server_cert_pem_start_i; + server_cert_pem_end = server_cert_pem_end_i; + + return ESP_OK; +} + + +static int do_mqtt_sub_cmd(int argc, char **argv) +{ + int msg_id; + int nerrors = arg_parse(argc, argv, (void **)&mqtt_sub_args); + if (nerrors != 0) { + arg_print_errors(stderr, mqtt_sub_args.end, argv[0]); + return 1; + } + + if (client_handle == NULL) { + ESP_LOGE(TAG, "mqtt client not connected"); + return 0; + } + + if (mqtt_sub_args.unsubscribe->count > 0) { + if (mqtt_sub_args.topic->count <= 0) { + ESP_LOGE(TAG, UNSUBSCRIBE_HELP_MSG); + } + char *topic = (char *)mqtt_sub_args.topic->sval[0]; + + msg_id = esp_mqtt_client_unsubscribe(client_handle, mqtt_sub_args.topic->sval[0]); + ESP_LOGI(TAG, "Unsubscribe successful, msg_id=%d, topic=%s", msg_id, topic); + + } else { + if (mqtt_sub_args.topic->count <= 0) { + ESP_LOGE(TAG, SUBSCRIBE_HELP_MSG); + } + char *topic = (char *)mqtt_sub_args.topic->sval[0]; + + msg_id = esp_mqtt_client_subscribe(client_handle, topic, 0); + ESP_LOGI(TAG, "Subscribe successful, msg_id=%d, topic=%s", msg_id, topic); + + } + + return 0; +} + + +static int do_mqtt_pub_cmd(int argc, char **argv) +{ + int msg_id; + int nerrors = arg_parse(argc, argv, (void **)&mqtt_pub_args); + if (nerrors != 0) { + arg_print_errors(stderr, mqtt_pub_args.end, argv[0]); + return 1; + } + + if (client_handle == NULL) { + ESP_LOGE(TAG, "mqtt client not connected"); + return 0; + } + + if ((mqtt_pub_args.topic->count <= 0) || (mqtt_pub_args.message->count <= 0)) { + ESP_LOGE(TAG, PUBLISH_HELP_MSG); + } + + msg_id = esp_mqtt_client_publish(client_handle, + mqtt_pub_args.topic->sval[0], + mqtt_pub_args.message->sval[0], + 0, 1, 0); + if (msg_id == -1) { + ESP_LOGE(TAG, "mqtt client not connected"); + return 0; + } + ESP_LOGI(TAG, "Publish successful, msg_id=%d, topic=%s, data=%s", + msg_id, mqtt_pub_args.topic->sval[0], mqtt_pub_args.message->sval[0]); + + return 0; +} + +/** + * @brief Registers the mqtt commands. + * + * @return + * - esp_err_t + */ +esp_err_t console_cmd_mqtt_register(void) +{ + esp_err_t ret = ESP_OK; + + /* Register mqtt */ + mqtt_args.connect = arg_lit0("C", "connect", "Connect to a broker"); + mqtt_args.uri = arg_str0("h", "host", "", "Specify the host uri to connect to"); + mqtt_args.username = arg_str0("u", "username", "", "Provide a username to be used for authenticating with the broker"); + mqtt_args.password = arg_str0("P", "password", "", "Provide a password to be used for authenticating with the broker"); + mqtt_args.cert = arg_lit0("", "cert", "Define the PEM encoded certificate for this client, if required by the server"); + mqtt_args.key = arg_lit0("", "key", "Define the PEM encoded private key for this client, if required by the server"); + mqtt_args.cafile = arg_lit0("", "cafile", "Define the PEM encoded CA certificates that are trusted"); + mqtt_args.disconnect = arg_lit0("D", "disconnect", "Disconnect from the broker"); + mqtt_args.end = arg_end(1); + + const esp_console_cmd_t mqtt_cmd = { + .command = "mqtt", + .help = "mqtt command", + .hint = NULL, + .func = &do_mqtt_cmd, + .argtable = &mqtt_args + }; + + ret = esp_console_cmd_register(&mqtt_cmd); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Unable to register mqtt"); + return ret; + } + + /* Register mqtt_pub */ + mqtt_pub_args.topic = arg_str0("t", "topic", "", "Topic to Subscribe/Publish"); + mqtt_pub_args.message = arg_str0("m", "message", "", "Message to Publish"); + mqtt_pub_args.end = arg_end(1); + + const esp_console_cmd_t mqtt_pub_cmd = { + .command = "mqtt_pub", + .help = "mqtt publish command", + .hint = NULL, + .func = &do_mqtt_pub_cmd, + .argtable = &mqtt_pub_args + }; + + ret = esp_console_cmd_register(&mqtt_pub_cmd); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Unable to register mqtt_pub"); + return ret; + } + + /* Register mqtt_sub */ + mqtt_sub_args.topic = arg_str0("t", "topic", "", "Topic to Subscribe/Publish"); + mqtt_sub_args.unsubscribe = arg_lit0("U", "unsubscribe", "Unsubscribe from a topic"); + mqtt_sub_args.end = arg_end(1); + + const esp_console_cmd_t mqtt_sub_cmd = { + .command = "mqtt_sub", + .help = "mqtt subscribe command", + .hint = NULL, + .func = &do_mqtt_sub_cmd, + .argtable = &mqtt_sub_args + }; + + ret = esp_console_cmd_register(&mqtt_sub_cmd); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Unable to register mqtt_sub"); + } + + return ret; +} diff --git a/components/console_cmd_mqtt/console_mqtt.h b/components/console_cmd_mqtt/console_mqtt.h new file mode 100644 index 00000000000..7b0ef96b0e4 --- /dev/null +++ b/components/console_cmd_mqtt/console_mqtt.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "console_simple_init.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Registers the mqtt command. + * + * @return + * - esp_err_t + */ +esp_err_t console_cmd_mqtt_register(void); + + +/** + * @brief Set MQTT client certificates + * + * This function sets the MQTT client certificates for secure communication. + * The function takes the PEM(Privacy Enhanced Mail) encoded certificates and keys as arguments. + * + * @param client_cert_pem_start_i Pointer to the beginning of the client certificate PEM data. + * @param client_cert_pem_end_i Pointer to the end of the client certificate PEM data. + * @param client_key_pem_start_i Pointer to the beginning of the client key PEM data. + * @param client_key_pem_end_i Pointer to the end of the client key PEM data. + * @param server_cert_pem_start_i Pointer to the beginning of the server certificate PEM data. + * @param server_cert_pem_end_i Pointer to the end of the server certificate PEM data. + * + * @return + * ESP_OK on success + * ESP_ERR_INVALID_ARG on invalid arguments + */ +esp_err_t set_mqtt_certs(const uint8_t *client_cert_pem_start_i, const uint8_t *client_cert_pem_end_i, + const uint8_t *client_key_pem_start_i, const uint8_t *client_key_pem_end_i, + const uint8_t *server_cert_pem_start_i, const uint8_t *server_cert_pem_end_i); + +#ifdef __cplusplus +} +#endif diff --git a/components/console_cmd_mqtt/examples/mqtt-basic/CMakeLists.txt b/components/console_cmd_mqtt/examples/mqtt-basic/CMakeLists.txt new file mode 100644 index 00000000000..f0ea68dd08f --- /dev/null +++ b/components/console_cmd_mqtt/examples/mqtt-basic/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five 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) +project(mqtt-basic) diff --git a/components/console_cmd_mqtt/examples/mqtt-basic/README.md b/components/console_cmd_mqtt/examples/mqtt-basic/README.md new file mode 100644 index 00000000000..0bebde23aa1 --- /dev/null +++ b/components/console_cmd_mqtt/examples/mqtt-basic/README.md @@ -0,0 +1,74 @@ +# mqtt command + +## Overview + +This example demonstrates the usage of mqtt command component. +To set up the example with Wi-Fi, Ethernet, or both, go to the project configuration menu (idf.py menuconfig) and choose "Example Connection Configuration." Then pick either Wi-Fi, Ethernet, or both under "Connect using. + +### Command Usage: +``` +esp> help +help [] + Print the summary of all registered commands if no arguments are given, + otherwise print summary of given command. + Name of command + +mqtt [-CD] [-h ] [-u ] [-P ] + mqtt command + -C, --connect Connect to a broker + -h, --host= Specify the host uri to connect to + -u, --username= Provide a username to be used for authenticating with the broker + -P, --password= Provide a password to be used for authenticating with the broker + , --cert Define the PEM encoded certificate for this client, if required by the server + , --key Define the PEM encoded private key for this client, if required by the server + , --cafile Define the PEM encoded CA certificates that are trusted + -D, --disconnect Disconnect from the broker + +mqtt_pub [-t ] [-m ] + mqtt publish command + -t, --topic= Topic to Subscribe/Publish + -m, --message= Message to Publish + +mqtt_sub [-U] [-t ] + mqtt subscribe command + -t, --topic= Topic to Subscribe/Publish + -U, --unsubscribe Unsubscribe from a topic +``` + +#### Connect/Disconnect: +``` +esp> mqtt -h mqtt://192.168.50.185 -C +I (1678559) console_mqtt: broker: mqtt://192.168.50.185 +I (1678559) console_mqtt: MQTT_EVENT_BEFORE_CONNECT +esp> I (1678849) console_mqtt: MQTT_EVENT_CONNECTED +esp> +esp> mqtt -D +I (1691939) console_mqtt: mqtt client disconnected +``` + +#### Subscribe/Unsubscribe: +``` +esp> mqtt_sub -t test0 +I (897289) console_mqtt: Subscribe successful, msg_id=57425, topic=test0 +esp> I (897799) console_mqtt: MQTT_EVENT_SUBSCRIBED, msg_id=57425 +esp> +esp> mqtt_sub -U -t test0 +I (902009) console_mqtt: Unsubscribe successful, msg_id=27663, topic=test0 +esp> I (902509) console_mqtt: MQTT_EVENT_UNSUBSCRIBED, msg_id=27663 +``` + +#### Publish: +``` +esp> mqtt_pub -t test0 -m "Hello, Testing 123" +I (999469) console_mqtt: Publish successful, msg_id=55776, topic=test0, data=Hello, Testing 123 +I (1000009) console_mqtt: MQTT_EVENT_PUBLISHED, msg_id=55776 +esp> +``` + +#### Receiving Data Event: +``` +esp> I (999999) console_mqtt: MQTT_EVENT_DATA +I (999999) console_mqtt: TOPIC=test0 + +I (999999) console_mqtt: DATA=Hello, Testing 123 +``` diff --git a/components/console_cmd_mqtt/examples/mqtt-basic/main/CMakeLists.txt b/components/console_cmd_mqtt/examples/mqtt-basic/main/CMakeLists.txt new file mode 100644 index 00000000000..d61c39878a0 --- /dev/null +++ b/components/console_cmd_mqtt/examples/mqtt-basic/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "mqtt-basic.c" + INCLUDE_DIRS ".") diff --git a/components/console_cmd_mqtt/examples/mqtt-basic/main/idf_component.yml b/components/console_cmd_mqtt/examples/mqtt-basic/main/idf_component.yml new file mode 100644 index 00000000000..c426c8a6aa2 --- /dev/null +++ b/components/console_cmd_mqtt/examples/mqtt-basic/main/idf_component.yml @@ -0,0 +1,8 @@ +dependencies: + idf: + version: ">=5.0" + protocol_examples_common: + path: ${IDF_PATH}/examples/common_components/protocol_examples_common + console_cmd_mqtt: + version: "*" + override_path: '../../../' diff --git a/components/console_cmd_mqtt/examples/mqtt-basic/main/mqtt-basic.c b/components/console_cmd_mqtt/examples/mqtt-basic/main/mqtt-basic.c new file mode 100644 index 00000000000..1fb01def014 --- /dev/null +++ b/components/console_cmd_mqtt/examples/mqtt-basic/main/mqtt-basic.c @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include "esp_netif.h" +#include "nvs_flash.h" +#include "esp_event.h" +#include "console_mqtt.h" +#include "protocol_examples_common.h" + + +void app_main(void) +{ + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_err_t ret = nvs_flash_init(); //Initialize NVS + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * ${IDF_PATH}/examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + + /* Initialize console REPL */ + ESP_ERROR_CHECK(console_cmd_init()); + + /* Register mqtt command */ + ESP_ERROR_CHECK(console_cmd_all_register()); + + // start console REPL + ESP_ERROR_CHECK(console_cmd_start()); + +} diff --git a/components/console_cmd_mqtt/examples/mqtt-basic/pytest_mqtt_basic.py b/components/console_cmd_mqtt/examples/mqtt-basic/pytest_mqtt_basic.py new file mode 100644 index 00000000000..009e1b378a6 --- /dev/null +++ b/components/console_cmd_mqtt/examples/mqtt-basic/pytest_mqtt_basic.py @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + +# -*- coding: utf-8 -*- +import pytest + + +@pytest.mark.esp32 +def test_examples_mqtt_command(dut): + dut.expect('esp>', timeout=30) + dut.write('help mqtt') + dut.expect('mqtt [-CD] [-h ] [-u ] [-P ]', timeout=30) + pass diff --git a/components/console_cmd_mqtt/examples/ssl_mutual_auth/CMakeLists.txt b/components/console_cmd_mqtt/examples/ssl_mutual_auth/CMakeLists.txt new file mode 100644 index 00000000000..2466c7aa300 --- /dev/null +++ b/components/console_cmd_mqtt/examples/ssl_mutual_auth/CMakeLists.txt @@ -0,0 +1,11 @@ +# The following five 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) +project(ssl_mutual_auth) + +# Certs for mqtts://test.mosquitto.org:8884 +target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "main/client.crt" TEXT) +target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "main/client.key" TEXT) +target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "main/mosquitto.org.crt" TEXT) diff --git a/components/console_cmd_mqtt/examples/ssl_mutual_auth/README.md b/components/console_cmd_mqtt/examples/ssl_mutual_auth/README.md new file mode 100644 index 00000000000..b2f77160770 --- /dev/null +++ b/components/console_cmd_mqtt/examples/ssl_mutual_auth/README.md @@ -0,0 +1,125 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | + +# ESP-MQTT SSL Sample application (mutual authentication) + +This example demonstrates the usage of mqtt command component. +It connects to the broker test.mosquitto.org using ssl transport with client certificate and as a demonstration subscribes/unsubscribes and send a message on certain topic. +(Please note that the public broker is maintained by the community so may not be always available, for details please visit http://test.mosquitto.org) + +It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker. + +## How to use example + +### Hardware Required + +This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet. + +### Configure the project + +* Open the project configuration menu (`idf.py menuconfig`) +* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details. + +* Generate your client keys and certificate + +Navigate to the main directory + +``` +cd main +``` + +Generate a client key and a CSR. When you are generating the CSR, do not use the default values. At a minimum, the CSR must include the Country, Organisation and Common Name fields. + +``` +openssl genrsa -out client.key +openssl req -out client.csr -key client.key -new +``` + +Paste the generated CSR in the [Mosquitto test certificate signer](https://test.mosquitto.org/ssl/index.php), click Submit and copy the downloaded `client.crt` in the `main` directory. + +Please note, that the supplied files `client.crt` and `client.key` in the `main` directory are only placeholders for your client certificate and key (i.e. the example "as is" would compile but would not connect to the broker) + +The server certificate `mosquitto.org.crt` can be downloaded in pem format from [mosquitto.org.crt](https://test.mosquitto.org/ssl/mosquitto.org.crt). + +Note: Incase your certificate and keys file name differs, please update the root `CMakeLists.txt` file and main/`ssl_mutual_auth.c` accordingly. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + + +### Command Usage: +``` +esp> help +help [] + Print the summary of all registered commands if no arguments are given, + otherwise print summary of given command. + Name of command + +mqtt [-CD] [-h ] [-u ] [-P ] + mqtt command + -C, --connect Connect to a broker + -h, --host= Specify the host uri to connect to + -u, --username= Provide a username to be used for authenticating with the broker + -P, --password= Provide a password to be used for authenticating with the broker + , --cert Define the PEM encoded certificate for this client, if required by the server + , --key Define the PEM encoded private key for this client, if required by the server + , --cafile Define the PEM encoded CA certificates that are trusted + -D, --disconnect Disconnect from the broker + +mqtt_pub [-t ] [-m ] + mqtt publish command + -t, --topic= Topic to Subscribe/Publish + -m, --message= Message to Publish + +mqtt_sub [-U] [-t ] + mqtt subscribe command + -t, --topic= Topic to Subscribe/Publish + -U, --unsubscribe Unsubscribe from a topic +``` + +#### Connect/Disconnect: +``` +esp> mqtt -h mqtts://test.mosquitto.org:8884 -C --cert --key --cafile +I (668129) console_mqtt: broker: mqtts://test.mosquitto.org:8884 +I (668129) console_mqtt: MQTT_EVENT_BEFORE_CONNECT +esp> I (671679) console_mqtt: MQTT_EVENT_CONNECTED +esp> +esp> mqtt -D +I (1189949) console_mqtt: mqtt client disconnected +``` + +#### Subscribe/Unsubscribe: +``` +esp> mqtt_sub -t test0 +I (897289) console_mqtt: Subscribe successful, msg_id=57425, topic=test0 +esp> I (897799) console_mqtt: MQTT_EVENT_SUBSCRIBED, msg_id=57425 +esp> +esp> mqtt_sub -U -t test0 +I (902009) console_mqtt: Unsubscribe successful, msg_id=27663, topic=test0 +esp> I (902509) console_mqtt: MQTT_EVENT_UNSUBSCRIBED, msg_id=27663 +``` + +#### Publish: +``` +esp> mqtt_pub -t test0 -m "Hello, Testing 123" +I (999469) console_mqtt: Publish successful, msg_id=55776, topic=test0, data=Hello, Testing 123 +I (1000009) console_mqtt: MQTT_EVENT_PUBLISHED, msg_id=55776 +esp> +``` + +#### Receiving data event: +``` +esp> I (999999) console_mqtt: MQTT_EVENT_DATA +I (999999) console_mqtt: TOPIC=test0 + +I (999999) console_mqtt: DATA=Hello, Testing 123 +``` diff --git a/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/CMakeLists.txt b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/CMakeLists.txt new file mode 100644 index 00000000000..3e381022792 --- /dev/null +++ b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "ssl_mutual_auth.c" + INCLUDE_DIRS ".") diff --git a/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/client.crt b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/client.crt new file mode 100644 index 00000000000..fe6f78f84c1 --- /dev/null +++ b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/client.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBkDELMAkGA1UEBhMCR0Ix +FzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTESMBAGA1UE +CgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVpdHRvLm9y +ZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzAeFw0yNDA1MDgwMTM5 +MzVaFw0yNDA4MDYwMTM5MzVaMIGVMQswCQYDVQQGEwJBVTERMA8GA1UECAwIVmlj +dG9yaWExEjAQBgNVBAcMCU1lbGJvdXJuZTESMBAGA1UECgwJRXNwcmVzc2lmMRMw +EQYDVQQLDApOZXR3b3JraW5nMQ4wDAYDVQQDDAVBYmhpazEmMCQGCSqGSIb3DQEJ +ARYXYWJoaWsucm95QGVzcHJlc3NpZi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDTtaF4ilaKKq5Ff409KNblfmJkfqjXBxSWj6fneIbsGssNZopi +6/CRqy0fbnr9fgHXIACdjk5HOmyStdtTHqFOAF+RIOqtjE2kAA2YJmomF9pD28Ph +sDppicxi561j0rvQgWcn1bWaRQe7ZmNXxtoOrXnVlWVwcdUcBiWQ29/2K8r8wNhk +m1O2qdyWsYphIdLutURbH5PkXht6HNoqXIuOTfYcXdz3QDV5Slph105Pox9lNPBg +99RVV8I3DFiDDr1BElqudcSLaecYr8bxiiwjciOcCmX5MX5DccLWzwMSTjknRCEx +wL2T64qnBRYsaKOTp1FwybuMDlvw3J6nbMhpAgMBAAGjGjAYMAkGA1UdEwQCMAAw +CwYDVR0PBAQDAgXgMA0GCSqGSIb3DQEBCwUAA4IBAQBJs3n1mSASzav7qq+UsTza +iWlsY9vio8ikhvtr1tfZyad1lTF9j2g68l5xyJMd34qY+EmSfOOAwKBZUBf36Qya +wDbmOEXmpM89QtZZqG2Nm0GyQTcJ9KMDWE1b0ckt/rkRrWi5mrKbonT250YpPOrt +SCe7Ah4W8kzN58VgmiMjSGKPzYXzJ8D1OmEw93NMGYsHwZ90U3QDVxd3WzHpPSwn +aOOel6eAE4L7FifzoO+6wGHqJz+9LRAqOVH3BFQ5KlIyoNAzJjA6Zz7eFfIRhYKC +1sOzNj04mU783sctgYrya7IzQBihjOESCdZrGMiM14LtpIkjVh3JPMe7CdLHzeYs +-----END CERTIFICATE----- diff --git a/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/client.key b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/client.key new file mode 100644 index 00000000000..9cf1a4c14ff --- /dev/null +++ b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDTtaF4ilaKKq5F +f409KNblfmJkfqjXBxSWj6fneIbsGssNZopi6/CRqy0fbnr9fgHXIACdjk5HOmyS +tdtTHqFOAF+RIOqtjE2kAA2YJmomF9pD28PhsDppicxi561j0rvQgWcn1bWaRQe7 +ZmNXxtoOrXnVlWVwcdUcBiWQ29/2K8r8wNhkm1O2qdyWsYphIdLutURbH5PkXht6 +HNoqXIuOTfYcXdz3QDV5Slph105Pox9lNPBg99RVV8I3DFiDDr1BElqudcSLaecY +r8bxiiwjciOcCmX5MX5DccLWzwMSTjknRCExwL2T64qnBRYsaKOTp1FwybuMDlvw +3J6nbMhpAgMBAAECggEAJxUwPmL3b+FKM71E0yYPaeVnf5rcS3ncpKDXg8U9zydx +FcO4x2M8EYAyv8sy/En/R/n58xwauk6ATaMx3onYiGBMRCv59tjgWmbCG7T3wpmD +qyROaIMSfXo5V4bifPuhvW+uHe33bQfSUheoPkOQ7MW8qJ/ATr5M87P0ymW01ipQ +RBMZlYOJ6h47GJdMZNuUCbCmclS2cVOES6E8grF7gvlJ+6LFZCaGJ6K819e3Mw/h +jhI+VbQUSpnfLA+6D85ShESXNHQUf+WTluVCqqUsTjWUj/WdTRRUIWQ9q4mVWqxB +CrC0p/UAw0PbjB1Anwh/qIjTe+664S9PcM7vN3yJUwKBgQD7getItO2c6+RaOiCi +5xwqwg0SLF+eLnuKg5AND12xDUK43tJYY6j8PeDphVq9sZVgQTBW4lfdn3s0A28H +h6I8YYW3TN2ghJyyrerNyzf2qIHvbyru6Of4jIBGB1G8h45DPzksWJk03nPELVbW +fJWXaAe7VW19Gzo4f1BBimOz7wKBgQDXfbmwzywPT2WpfVLKViWBIRQmT/z/5eWp +4B6/NH44PgOs8Zu0VeQXEQKdsmYXrG4SzpRe7QQ9SNqqntUkudz1yfmO3bM/SpBR +ct29QmHY0GTZ6Epuo1Kl9Q9n7j1So8+oD64fKgM9liC9MokbzflwCxHQ+mZPLgjC +hO7koc6RJwKBgDvKWwDh4ksX2SWKBBA2GQAhObJEUkbsjfoT5L72FDPvDxmb/y18 +I8QzgbCCP1wxR22mkbNWA8VwEH4BAvgrSmpIVN2KrHVokUf5CIT79sXwsVMWfoJl +ZCA2Zpg/TTMglrVt5k2gkmt7JtJQZQCAhZ+E37GtUWYYfvLHcXDjUWiJAoGBALjt +qlez0wnoh0Qmys/dkh3490PRzTsGXkukjH5mXBOEFL9sMMVYGIA7FtWibb7POT9m +jSnRmZvGU/GskRoNbzR3enVCiTs9kBB11RlASJw6avIeSRIdkyXc4rW8XF/5OJHv +suwHr/RATCRRpBx6bR/cQBPpb7dvuBDG6ATJX25tAoGBAPVOfEMFS3uAWy7TCg/v +/KC0s7NqV3MyA0/P4DfbS8soYekd2BE3ISNLcwffHoIVtcIZ5ElM5CVC2opw167J +jvwa2it6/nGQZqIFLtb4xYC291iL4IiTYm0v82jBWDBtw8xqN43vU2hk5EaWhpW0 +8GqLjexkQUVbpe9GS+AKFEbD +-----END PRIVATE KEY----- diff --git a/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/idf_component.yml b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/idf_component.yml new file mode 100644 index 00000000000..c426c8a6aa2 --- /dev/null +++ b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/idf_component.yml @@ -0,0 +1,8 @@ +dependencies: + idf: + version: ">=5.0" + protocol_examples_common: + path: ${IDF_PATH}/examples/common_components/protocol_examples_common + console_cmd_mqtt: + version: "*" + override_path: '../../../' diff --git a/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/mosquitto.org.crt b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/mosquitto.org.crt new file mode 100644 index 00000000000..e76dbd85598 --- /dev/null +++ b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/mosquitto.org.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL +BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG +A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU +BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv +by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE +BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES +MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp +dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg +UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW +Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA +s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH +3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo +E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT +MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV +6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC +6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf ++pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK +sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839 +LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE +m/XriWr/Cq4h/JfB7NTsezVslgkBaoU= +-----END CERTIFICATE----- diff --git a/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/ssl_mutual_auth.c b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/ssl_mutual_auth.c new file mode 100644 index 00000000000..a0574a3be4b --- /dev/null +++ b/components/console_cmd_mqtt/examples/ssl_mutual_auth/main/ssl_mutual_auth.c @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include "nvs_flash.h" +#include "esp_netif.h" +#include "esp_event.h" +#include +#include "console_mqtt.h" +#include "protocol_examples_common.h" + +// Certs for mqtts://test.mosquitto.org:8884 +extern const uint8_t client_cert_pem_start[] asm("_binary_client_crt_start"); +extern const uint8_t client_cert_pem_end[] asm("_binary_client_crt_end"); +extern const uint8_t client_key_pem_start[] asm("_binary_client_key_start"); +extern const uint8_t client_key_pem_end[] asm("_binary_client_key_end"); +extern const uint8_t server_cert_pem_start[] asm("_binary_mosquitto_org_crt_start"); +extern const uint8_t server_cert_pem_end[] asm("_binary_mosquitto_org_crt_end"); + + +void app_main(void) +{ + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_err_t ret = nvs_flash_init(); //Initialize NVS + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * ${IDF_PATH}/examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + + // Initialize console REPL + ESP_ERROR_CHECK(console_cmd_init()); + ESP_ERROR_CHECK(console_cmd_all_register()); + + set_mqtt_certs(client_cert_pem_start, client_cert_pem_end, client_key_pem_start, + client_key_pem_end, server_cert_pem_start, server_cert_pem_end); + + // start console REPL + ESP_ERROR_CHECK(console_cmd_start()); + +} diff --git a/components/console_cmd_mqtt/idf_component.yml b/components/console_cmd_mqtt/idf_component.yml new file mode 100644 index 00000000000..c6beb693174 --- /dev/null +++ b/components/console_cmd_mqtt/idf_component.yml @@ -0,0 +1,10 @@ +version: 1.0.0 +url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_mqtt +description: The component provides a console where the 'mqtt' command can be executed. +dependencies: + idf: + version: '>=5.0' + espressif/console_simple_init: + version: '>=1.1.0' + override_path: '../console_simple_init' + public: true