diff --git a/components/Communication/CMakeLists.txt b/components/Communication/CMakeLists.txt new file mode 100644 index 0000000..bf67a25 --- /dev/null +++ b/components/Communication/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register(SRCS + MqttClient.h MqttClient.cpp + INCLUDE_DIRS . + REQUIRES mqtt +) diff --git a/components/Communication/MqttClient.cpp b/components/Communication/MqttClient.cpp new file mode 100644 index 0000000..7df1496 --- /dev/null +++ b/components/Communication/MqttClient.cpp @@ -0,0 +1,88 @@ +#include "MqttClient.h" +#include +#include +#include + +static const char *TAG = "mqtt"; + +static void log_error_if_nonzero(const char *message, int error_code) { + if (error_code != 0) { + ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code); + } +} + +static void 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); + auto event = reinterpret_cast(event_data); + esp_mqtt_client_handle_t client = event->client; + int msg_id; + switch ((esp_mqtt_event_id_t)event_id) { + case MQTT_EVENT_CONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); + ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + + msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); + ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); + ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: + ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_DATA: + ESP_LOGI(TAG, "MQTT_EVENT_DATA"); + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + break; + case MQTT_EVENT_ERROR: + ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + 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; + } +} + +bool MqttClient::initialize(const std::string &username, const std::string &password) { + esp_mqtt_client_config_t config; + memset(&config, 0, sizeof(esp_mqtt_client_config_t)); + config.broker.address.uri = "mqtt://192.168.2.3"; + config.credentials.username = username.c_str(); + config.credentials.authentication.password = password.c_str(); + + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&config); + esp_mqtt_client_register_event(client, static_cast(ESP_EVENT_ANY_ID), mqtt_event_handler, + NULL); + esp_mqtt_client_start(client); + ESP_LOGI(TAG, "connect to %s, username: %s, password: %s", config.broker.address.uri, config.credentials.username, + config.credentials.authentication.password); + return true; +} + +MqttClient *MqttClient::instance() { + static MqttClient self; + return &self; +} diff --git a/components/Communication/MqttClient.h b/components/Communication/MqttClient.h new file mode 100644 index 0000000..5da1b52 --- /dev/null +++ b/components/Communication/MqttClient.h @@ -0,0 +1,14 @@ +#ifndef __MQTTCLIENT_H__ +#define __MQTTCLIENT_H__ + +#include + +class MqttClient { +public: + static MqttClient *instance(); + bool initialize(const std::string &username,const std::string &password); + +protected: + MqttClient() = default; +}; +#endif // __MQTTCLIENT_H__ \ No newline at end of file diff --git a/components/command/CMakeLists.txt b/components/command/CMakeLists.txt index 011b331..219424a 100644 --- a/components/command/CMakeLists.txt +++ b/components/command/CMakeLists.txt @@ -1,9 +1,10 @@ idf_component_register(SRCS cmd_system.c cmd_system_common.c + cmd_wifi.h cmd_wifi.c CustomCommand.cpp INCLUDE_DIRS . - REQUIRES console spi_flash driver LedController + REQUIRES console spi_flash driver esp_wifi LedController Communication ) target_sources(${COMPONENT_LIB} PRIVATE cmd_system_sleep.c) \ No newline at end of file diff --git a/components/command/CustomCommand.cpp b/components/command/CustomCommand.cpp index e6b5aa6..c11efa1 100644 --- a/components/command/CustomCommand.cpp +++ b/components/command/CustomCommand.cpp @@ -1,5 +1,6 @@ #include "CustomCommand.h" #include "LedController.h" +#include "MqttClient.h" #include "esp_console.h" #include @@ -17,12 +18,18 @@ static int led_command(int argc, char **argv) { return 0; } +static int mqtt_command(int argc, char **argv) { + MqttClient::instance()->initialize(argv[1], argv[2]); + return 0; +} + void register_custom() { const esp_console_cmd_t heap_cmd = { .command = "amass", .help = "test command.", .hint = NULL, .func = &custom_command, + .argtable = nullptr, }; ESP_ERROR_CHECK(esp_console_cmd_register(&heap_cmd)); @@ -31,6 +38,16 @@ void register_custom() { .help = "led pwm duty.", .hint = NULL, .func = &led_command, + .argtable = nullptr, }; ESP_ERROR_CHECK(esp_console_cmd_register(&led_cmd)); + + const esp_console_cmd_t mqtt_cmd = { + .command = "mqtt", + .help = "mqtt client.", + .hint = NULL, + .func = &mqtt_command, + .argtable = nullptr, + }; + ESP_ERROR_CHECK(esp_console_cmd_register(&mqtt_cmd)); } diff --git a/components/command/cmd_wifi.c b/components/command/cmd_wifi.c new file mode 100644 index 0000000..59ee518 --- /dev/null +++ b/components/command/cmd_wifi.c @@ -0,0 +1,136 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* Console example — WiFi commands + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include "esp_log.h" +#include "esp_console.h" +#include "argtable3/argtable3.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_wifi.h" +#include "esp_netif.h" +#include "esp_event.h" +#include "cmd_wifi.h" + +#define JOIN_TIMEOUT_MS (10000) + +static EventGroupHandle_t wifi_event_group; +const int CONNECTED_BIT = BIT0; + + +static void event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + } +} + +static void initialise_wifi(void) +{ + esp_log_level_set("wifi", ESP_LOG_WARN); + static bool initialized = false; + if (initialized) { + return; + } + ESP_ERROR_CHECK(esp_netif_init()); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap(); + assert(ap_netif); + esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); + assert(sta_netif); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &event_handler, NULL) ); + ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_NULL) ); + ESP_ERROR_CHECK( esp_wifi_start() ); + initialized = true; +} + +static bool wifi_join(const char *ssid, const char *pass, int timeout_ms) +{ + initialise_wifi(); + wifi_config_t wifi_config = { 0 }; + strlcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); + if (pass) { + strlcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); + } + + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + esp_wifi_connect(); + + int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + pdFALSE, pdTRUE, timeout_ms / portTICK_PERIOD_MS); + return (bits & CONNECTED_BIT) != 0; +} + +/** Arguments used by 'join' function */ +static struct { + struct arg_int *timeout; + struct arg_str *ssid; + struct arg_str *password; + struct arg_end *end; +} join_args; + +static int connect(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &join_args); + if (nerrors != 0) { + arg_print_errors(stderr, join_args.end, argv[0]); + return 1; + } + ESP_LOGI(__func__, "Connecting to '%s'", + join_args.ssid->sval[0]); + + /* set default value*/ + if (join_args.timeout->count == 0) { + join_args.timeout->ival[0] = JOIN_TIMEOUT_MS; + } + + bool connected = wifi_join(join_args.ssid->sval[0], + join_args.password->sval[0], + join_args.timeout->ival[0]); + if (!connected) { + ESP_LOGW(__func__, "Connection timed out"); + return 1; + } + ESP_LOGI(__func__, "Connected"); + return 0; +} + +void register_wifi(void) +{ + join_args.timeout = arg_int0(NULL, "timeout", "", "Connection timeout, ms"); + join_args.ssid = arg_str1(NULL, NULL, "", "SSID of AP"); + join_args.password = arg_str0(NULL, NULL, "", "PSK of AP"); + join_args.end = arg_end(2); + + const esp_console_cmd_t join_cmd = { + .command = "join", + .help = "Join WiFi AP as a station", + .hint = NULL, + .func = &connect, + .argtable = &join_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&join_cmd) ); +} diff --git a/components/command/cmd_wifi.h b/components/command/cmd_wifi.h new file mode 100644 index 0000000..2ba4bac --- /dev/null +++ b/components/command/cmd_wifi.h @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* Console example — declarations of command registration functions. + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +// Register WiFi functions +void register_wifi(void); + +#ifdef __cplusplus +} +#endif diff --git a/main/main.cpp b/main/main.cpp index eaff316..46244b1 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,6 +1,7 @@ #include "CustomCommand.h" #include "LedController.h" #include "cmd_system.h" +#include "cmd_wifi.h" #include "driver/uart.h" #include "esp_console.h" #include "esp_log.h" @@ -31,6 +32,7 @@ extern "C" void app_main() { esp_console_register_help_command(); register_system_common(); register_system_sleep(); + register_wifi(); register_custom(); LedController::instance()->initialize(); diff --git a/partitions.csv b/partitions.csv index cc5ccab..d4d8f56 100644 --- a/partitions.csv +++ b/partitions.csv @@ -2,5 +2,5 @@ # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 1M, +factory, app, factory, 0x10000, 1216K, storage, data, fat, , 1M,