diff --git a/components/Communication/Application.cpp b/components/Communication/Application.cpp new file mode 100644 index 0000000..ff29db3 --- /dev/null +++ b/components/Communication/Application.cpp @@ -0,0 +1,67 @@ +#include "Application.h" +#include +#include +#include +#include + +Application *Application::instance() { + static Application self; + return &self; +} + +void Application::initialize() { + initializeWifi(); +} + +void Application::eventHandler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + auto self = reinterpret_cast(arg); + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + esp_wifi_connect(); + xEventGroupClearBits(self->m_wifiEventGroup, CONNECTED_BIT); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + xEventGroupSetBits(self->m_wifiEventGroup, CONNECTED_BIT); + } +} + +bool Application::wifiConnect(const std::string &ssid, const std::string &password, int timeoutMillisecond) { + wifi_config_t config = {}; + strlcpy((char *)config.sta.ssid, ssid.c_str(), sizeof(config.sta.ssid)); + if (!password.empty()) { + strlcpy((char *)config.sta.password, password.c_str(), sizeof(config.sta.password)); + } + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &config)); + esp_wifi_connect(); + + int bits = + xEventGroupWaitBits(m_wifiEventGroup, CONNECTED_BIT, pdFALSE, pdTRUE, timeoutMillisecond / portTICK_PERIOD_MS); + return (bits & CONNECTED_BIT) != 0; +} + +void Application::initializeWifi() { + esp_log_level_set("wifi", ESP_LOG_WARN); + static bool initialized = false; + if (initialized) { + return; + } + ESP_ERROR_CHECK(esp_netif_init()); + + if (m_wifiEventGroup == nullptr) { + m_wifiEventGroup = 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, &Application::eventHandler, this)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &Application::eventHandler, this)); + 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; +} \ No newline at end of file diff --git a/components/Communication/Application.h b/components/Communication/Application.h new file mode 100644 index 0000000..68f936d --- /dev/null +++ b/components/Communication/Application.h @@ -0,0 +1,59 @@ +#ifndef __APPLICATION_H__ +#define __APPLICATION_H__ + +#include +#include +#include +#include +#include +#include + +class Application { + constexpr static int CONNECTED_BIT = BIT0; + constexpr static auto Namespace = "settings"; + +public: + constexpr static int WifiJoinTimeoutMillisecond = 10000; + static Application *instance(); + bool wifiConnect(const std::string &ssid, const std::string &password, + int timeoutMillisecond = WifiJoinTimeoutMillisecond); + void initialize(); + + template + void setField(const std::string &key, const T &value) { + nvs_handle_t hanlde; + esp_err_t error = nvs_open(Namespace, NVS_READWRITE, &hanlde); + if (error != ESP_OK) { + ESP_LOGI("App", "nvs_open() failed."); + } + + if (std::is_same_v>) { + error = nvs_set_str(hanlde, key.c_str(), value); + } else if constexpr (std::is_same_v) { + error = nvs_set_str(hanlde, key.c_str(), value.c_str()); + } else { + ESP_LOGW("App", "unknown data"); + } + + if (error == ESP_OK) { + error = nvs_commit(hanlde); + } + nvs_close(hanlde); + } + + template + T field(const std::string &key) { + } + +protected: + static void eventHandler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + void initializeWifi(); + + Application() = default; + +private: + EventGroupHandle_t m_wifiEventGroup = nullptr; + + nvs_handle_t m_nvs; +}; +#endif // __APPLICATION_H__ \ No newline at end of file diff --git a/components/Communication/CMakeLists.txt b/components/Communication/CMakeLists.txt index 99651be..1d7cdc0 100644 --- a/components/Communication/CMakeLists.txt +++ b/components/Communication/CMakeLists.txt @@ -1,5 +1,6 @@ idf_component_register(SRCS + Application.h Application.cpp MqttClient.h MqttClient.cpp INCLUDE_DIRS . - REQUIRES mqtt json LedController + REQUIRES esp_wifi nvs_flash mqtt json LedController ) diff --git a/components/command/CMakeLists.txt b/components/command/CMakeLists.txt index 219424a..4d08da0 100644 --- a/components/command/CMakeLists.txt +++ b/components/command/CMakeLists.txt @@ -1,10 +1,10 @@ idf_component_register(SRCS + cmd_nvs.h cmd_nvs.c cmd_system.c cmd_system_common.c - cmd_wifi.h cmd_wifi.c CustomCommand.cpp INCLUDE_DIRS . - REQUIRES console spi_flash driver esp_wifi LedController Communication + REQUIRES console spi_flash nvs_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 c11efa1..849381c 100644 --- a/components/command/CustomCommand.cpp +++ b/components/command/CustomCommand.cpp @@ -1,7 +1,10 @@ #include "CustomCommand.h" +#include "Application.h" #include "LedController.h" #include "MqttClient.h" #include "esp_console.h" +#include +#include #include static int custom_command(int argc, char **argv) { @@ -23,6 +26,36 @@ static int mqtt_command(int argc, char **argv) { return 0; } +static struct { + struct arg_int *timeout; + struct arg_str *ssid; + struct arg_str *password; + struct arg_end *end; +} join_args; + +static int wifi_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] = Application::WifiJoinTimeoutMillisecond; + } + + bool connected = Application::instance()->wifiConnect(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_custom() { const esp_console_cmd_t heap_cmd = { .command = "amass", @@ -33,6 +66,21 @@ void register_custom() { }; ESP_ERROR_CHECK(esp_console_cmd_register(&heap_cmd)); + 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 = &wifi_connect, + .argtable = &join_args, + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&join_cmd)); + const esp_console_cmd_t led_cmd = { .command = "led", .help = "led pwm duty.", diff --git a/components/command/cmd_nvs.c b/components/command/cmd_nvs.c new file mode 100644 index 0000000..5867b80 --- /dev/null +++ b/components/command/cmd_nvs.c @@ -0,0 +1,609 @@ +/* Console example — NVS 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 +#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_err.h" +#include "cmd_nvs.h" +#include "nvs.h" + +typedef struct { + nvs_type_t type; + const char *str; +} type_str_pair_t; + +static const type_str_pair_t type_str_pair[] = { + { NVS_TYPE_I8, "i8" }, + { NVS_TYPE_U8, "u8" }, + { NVS_TYPE_U16, "u16" }, + { NVS_TYPE_I16, "i16" }, + { NVS_TYPE_U32, "u32" }, + { NVS_TYPE_I32, "i32" }, + { NVS_TYPE_U64, "u64" }, + { NVS_TYPE_I64, "i64" }, + { NVS_TYPE_STR, "str" }, + { NVS_TYPE_BLOB, "blob" }, + { NVS_TYPE_ANY, "any" }, +}; + +static const size_t TYPE_STR_PAIR_SIZE = sizeof(type_str_pair) / sizeof(type_str_pair[0]); +static const char *ARG_TYPE_STR = "type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob"; +static char current_namespace[16] = "storage"; +static const char *TAG = "cmd_nvs"; + +static struct { + struct arg_str *key; + struct arg_str *type; + struct arg_str *value; + struct arg_end *end; +} set_args; + +static struct { + struct arg_str *key; + struct arg_str *type; + struct arg_end *end; +} get_args; + +static struct { + struct arg_str *key; + struct arg_end *end; +} erase_args; + +static struct { + struct arg_str *namespace; + struct arg_end *end; +} erase_all_args; + +static struct { + struct arg_str *namespace; + struct arg_end *end; +} namespace_args; + +static struct { + struct arg_str *partition; + struct arg_str *namespace; + struct arg_str *type; + struct arg_end *end; +} list_args; + + +static nvs_type_t str_to_type(const char *type) +{ + for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) { + const type_str_pair_t *p = &type_str_pair[i]; + if (strcmp(type, p->str) == 0) { + return p->type; + } + } + + return NVS_TYPE_ANY; +} + +static const char *type_to_str(nvs_type_t type) +{ + for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) { + const type_str_pair_t *p = &type_str_pair[i]; + if (p->type == type) { + return p->str; + } + } + + return "Unknown"; +} + +static esp_err_t store_blob(nvs_handle_t nvs, const char *key, const char *str_values) +{ + uint8_t value; + size_t str_len = strlen(str_values); + size_t blob_len = str_len / 2; + + if (str_len % 2) { + ESP_LOGE(TAG, "Blob data must contain even number of characters"); + return ESP_ERR_NVS_TYPE_MISMATCH; + } + + char *blob = (char *)malloc(blob_len); + if (blob == NULL) { + return ESP_ERR_NO_MEM; + } + + for (int i = 0, j = 0; i < str_len; i++) { + char ch = str_values[i]; + if (ch >= '0' && ch <= '9') { + value = ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + value = ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'f') { + value = ch - 'a' + 10; + } else { + ESP_LOGE(TAG, "Blob data contain invalid character"); + free(blob); + return ESP_ERR_NVS_TYPE_MISMATCH; + } + + if (i & 1) { + blob[j++] += value; + } else { + blob[j] = value << 4; + } + } + + esp_err_t err = nvs_set_blob(nvs, key, blob, blob_len); + free(blob); + + if (err == ESP_OK) { + err = nvs_commit(nvs); + } + + return err; +} + +static void print_blob(const char *blob, size_t len) +{ + for (int i = 0; i < len; i++) { + printf("%02x", blob[i]); + } + printf("\n"); +} + + +static esp_err_t set_value_in_nvs(const char *key, const char *str_type, const char *str_value) +{ + esp_err_t err; + nvs_handle_t nvs; + bool range_error = false; + + nvs_type_t type = str_to_type(str_type); + + if (type == NVS_TYPE_ANY) { + ESP_LOGE(TAG, "Type '%s' is undefined", str_type); + return ESP_ERR_NVS_TYPE_MISMATCH; + } + + err = nvs_open(current_namespace, NVS_READWRITE, &nvs); + if (err != ESP_OK) { + return err; + } + + if (type == NVS_TYPE_I8) { + int32_t value = strtol(str_value, NULL, 0); + if (value < INT8_MIN || value > INT8_MAX || errno == ERANGE) { + range_error = true; + } else { + err = nvs_set_i8(nvs, key, (int8_t)value); + } + } else if (type == NVS_TYPE_U8) { + uint32_t value = strtoul(str_value, NULL, 0); + if (value > UINT8_MAX || errno == ERANGE) { + range_error = true; + } else { + err = nvs_set_u8(nvs, key, (uint8_t)value); + } + } else if (type == NVS_TYPE_I16) { + int32_t value = strtol(str_value, NULL, 0); + if (value < INT16_MIN || value > INT16_MAX || errno == ERANGE) { + range_error = true; + } else { + err = nvs_set_i16(nvs, key, (int16_t)value); + } + } else if (type == NVS_TYPE_U16) { + uint32_t value = strtoul(str_value, NULL, 0); + if (value > UINT16_MAX || errno == ERANGE) { + range_error = true; + } else { + err = nvs_set_u16(nvs, key, (uint16_t)value); + } + } else if (type == NVS_TYPE_I32) { + int32_t value = strtol(str_value, NULL, 0); + if (errno != ERANGE) { + err = nvs_set_i32(nvs, key, value); + } + } else if (type == NVS_TYPE_U32) { + uint32_t value = strtoul(str_value, NULL, 0); + if (errno != ERANGE) { + err = nvs_set_u32(nvs, key, value); + } + } else if (type == NVS_TYPE_I64) { + int64_t value = strtoll(str_value, NULL, 0); + if (errno != ERANGE) { + err = nvs_set_i64(nvs, key, value); + } + } else if (type == NVS_TYPE_U64) { + uint64_t value = strtoull(str_value, NULL, 0); + if (errno != ERANGE) { + err = nvs_set_u64(nvs, key, value); + } + } else if (type == NVS_TYPE_STR) { + err = nvs_set_str(nvs, key, str_value); + } else if (type == NVS_TYPE_BLOB) { + err = store_blob(nvs, key, str_value); + } + + if (range_error || errno == ERANGE) { + nvs_close(nvs); + return ESP_ERR_NVS_VALUE_TOO_LONG; + } + + if (err == ESP_OK) { + err = nvs_commit(nvs); + if (err == ESP_OK) { + ESP_LOGI(TAG, "Value stored under key '%s'", key); + } + } + + nvs_close(nvs); + return err; +} + +static esp_err_t get_value_from_nvs(const char *key, const char *str_type) +{ + nvs_handle_t nvs; + esp_err_t err; + + nvs_type_t type = str_to_type(str_type); + + if (type == NVS_TYPE_ANY) { + ESP_LOGE(TAG, "Type '%s' is undefined", str_type); + return ESP_ERR_NVS_TYPE_MISMATCH; + } + + err = nvs_open(current_namespace, NVS_READONLY, &nvs); + if (err != ESP_OK) { + return err; + } + + if (type == NVS_TYPE_I8) { + int8_t value; + err = nvs_get_i8(nvs, key, &value); + if (err == ESP_OK) { + printf("%d\n", value); + } + } else if (type == NVS_TYPE_U8) { + uint8_t value; + err = nvs_get_u8(nvs, key, &value); + if (err == ESP_OK) { + printf("%u\n", value); + } + } else if (type == NVS_TYPE_I16) { + int16_t value; + err = nvs_get_i16(nvs, key, &value); + if (err == ESP_OK) { + printf("%u\n", value); + } + } else if (type == NVS_TYPE_U16) { + uint16_t value; + if ((err = nvs_get_u16(nvs, key, &value)) == ESP_OK) { + printf("%u\n", value); + } + } else if (type == NVS_TYPE_I32) { + int32_t value; + if ((err = nvs_get_i32(nvs, key, &value)) == ESP_OK) { + printf("%"PRIi32"\n", value); + } + } else if (type == NVS_TYPE_U32) { + uint32_t value; + if ((err = nvs_get_u32(nvs, key, &value)) == ESP_OK) { + printf("%"PRIu32"\n", value); + } + } else if (type == NVS_TYPE_I64) { + int64_t value; + if ((err = nvs_get_i64(nvs, key, &value)) == ESP_OK) { + printf("%lld\n", value); + } + } else if (type == NVS_TYPE_U64) { + uint64_t value; + if ( (err = nvs_get_u64(nvs, key, &value)) == ESP_OK) { + printf("%llu\n", value); + } + } else if (type == NVS_TYPE_STR) { + size_t len; + if ( (err = nvs_get_str(nvs, key, NULL, &len)) == ESP_OK) { + char *str = (char *)malloc(len); + if ( (err = nvs_get_str(nvs, key, str, &len)) == ESP_OK) { + printf("%s\n", str); + } + free(str); + } + } else if (type == NVS_TYPE_BLOB) { + size_t len; + if ( (err = nvs_get_blob(nvs, key, NULL, &len)) == ESP_OK) { + char *blob = (char *)malloc(len); + if ( (err = nvs_get_blob(nvs, key, blob, &len)) == ESP_OK) { + print_blob(blob, len); + } + free(blob); + } + } + + nvs_close(nvs); + return err; +} + +static esp_err_t erase(const char *key) +{ + nvs_handle_t nvs; + + esp_err_t err = nvs_open(current_namespace, NVS_READWRITE, &nvs); + if (err == ESP_OK) { + err = nvs_erase_key(nvs, key); + if (err == ESP_OK) { + err = nvs_commit(nvs); + if (err == ESP_OK) { + ESP_LOGI(TAG, "Value with key '%s' erased", key); + } + } + nvs_close(nvs); + } + + return err; +} + +static esp_err_t erase_all(const char *name) +{ + nvs_handle_t nvs; + + esp_err_t err = nvs_open(name, NVS_READWRITE, &nvs); + if (err == ESP_OK) { + err = nvs_erase_all(nvs); + if (err == ESP_OK) { + err = nvs_commit(nvs); + } + } + + ESP_LOGI(TAG, "Namespace '%s' was %s erased", name, (err == ESP_OK) ? "" : "not"); + + nvs_close(nvs); + return ESP_OK; +} + +static int list(const char *part, const char *name, const char *str_type) +{ + nvs_type_t type = str_to_type(str_type); + + nvs_iterator_t it = NULL; + esp_err_t result = nvs_entry_find(part, NULL, type, &it); + if (result == ESP_ERR_NVS_NOT_FOUND) { + ESP_LOGE(TAG, "No such entry was found"); + return 1; + } + + if (result != ESP_OK) { + ESP_LOGE(TAG, "NVS error: %s", esp_err_to_name(result)); + return 1; + } + + do { + nvs_entry_info_t info; + nvs_entry_info(it, &info); + result = nvs_entry_next(&it); + + printf("namespace '%s', key '%s', type '%s' \n", + info.namespace_name, info.key, type_to_str(info.type)); + } while (result == ESP_OK); + + if (result != ESP_ERR_NVS_NOT_FOUND) { // the last iteration ran into an internal error + ESP_LOGE(TAG, "NVS error %s at current iteration, stopping.", esp_err_to_name(result)); + return 1; + } + + return 0; +} + +static int set_value(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &set_args); + if (nerrors != 0) { + arg_print_errors(stderr, set_args.end, argv[0]); + return 1; + } + + const char *key = set_args.key->sval[0]; + const char *type = set_args.type->sval[0]; + const char *values = set_args.value->sval[0]; + + esp_err_t err = set_value_in_nvs(key, type, values); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s", esp_err_to_name(err)); + return 1; + } + + return 0; +} + +static int get_value(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &get_args); + if (nerrors != 0) { + arg_print_errors(stderr, get_args.end, argv[0]); + return 1; + } + + const char *key = get_args.key->sval[0]; + const char *type = get_args.type->sval[0]; + + esp_err_t err = get_value_from_nvs(key, type); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s", esp_err_to_name(err)); + return 1; + } + + return 0; +} + +static int erase_value(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &erase_args); + if (nerrors != 0) { + arg_print_errors(stderr, erase_args.end, argv[0]); + return 1; + } + + const char *key = erase_args.key->sval[0]; + + esp_err_t err = erase(key); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s", esp_err_to_name(err)); + return 1; + } + + return 0; +} + +static int erase_namespace(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &erase_all_args); + if (nerrors != 0) { + arg_print_errors(stderr, erase_all_args.end, argv[0]); + return 1; + } + + const char *name = erase_all_args.namespace->sval[0]; + + esp_err_t err = erase_all(name); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s", esp_err_to_name(err)); + return 1; + } + + return 0; +} + +static int set_namespace(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &namespace_args); + if (nerrors != 0) { + arg_print_errors(stderr, namespace_args.end, argv[0]); + return 1; + } + + const char *namespace = namespace_args.namespace->sval[0]; + strlcpy(current_namespace, namespace, sizeof(current_namespace)); + ESP_LOGI(TAG, "Namespace set to '%s'", current_namespace); + return 0; +} + +static int list_entries(int argc, char **argv) +{ + list_args.partition->sval[0] = ""; + list_args.namespace->sval[0] = ""; + list_args.type->sval[0] = ""; + + int nerrors = arg_parse(argc, argv, (void **) &list_args); + if (nerrors != 0) { + arg_print_errors(stderr, list_args.end, argv[0]); + return 1; + } + + const char *part = list_args.partition->sval[0]; + const char *name = list_args.namespace->sval[0]; + const char *type = list_args.type->sval[0]; + + return list(part, name, type); +} + +void register_nvs(void) +{ + set_args.key = arg_str1(NULL, NULL, "", "key of the value to be set"); + set_args.type = arg_str1(NULL, NULL, "", ARG_TYPE_STR); + + set_args.value = arg_str1("v", "value", "", "value to be stored"); + set_args.end = arg_end(2); + + get_args.key = arg_str1(NULL, NULL, "", "key of the value to be read"); + get_args.type = arg_str1(NULL, NULL, "", ARG_TYPE_STR); + get_args.end = arg_end(2); + + erase_args.key = arg_str1(NULL, NULL, "", "key of the value to be erased"); + erase_args.end = arg_end(2); + + erase_all_args.namespace = arg_str1(NULL, NULL, "", "namespace to be erased"); + erase_all_args.end = arg_end(2); + + namespace_args.namespace = arg_str1(NULL, NULL, "", "namespace of the partition to be selected"); + namespace_args.end = arg_end(2); + + list_args.partition = arg_str1(NULL, NULL, "", "partition name"); + list_args.namespace = arg_str0("n", "namespace", "", "namespace name"); + list_args.type = arg_str0("t", "type", "", ARG_TYPE_STR); + list_args.end = arg_end(2); + + const esp_console_cmd_t set_cmd = { + .command = "nvs_set", + .help = "Set key-value pair in selected namespace.\n" + "Examples:\n" + " nvs_set VarName i32 -v 123 \n" + " nvs_set VarName str -v YourString \n" + " nvs_set VarName blob -v 0123456789abcdef \n", + .hint = NULL, + .func = &set_value, + .argtable = &set_args + }; + + const esp_console_cmd_t get_cmd = { + .command = "nvs_get", + .help = "Get key-value pair from selected namespace. \n" + "Example: nvs_get VarName i32", + .hint = NULL, + .func = &get_value, + .argtable = &get_args + }; + + const esp_console_cmd_t erase_cmd = { + .command = "nvs_erase", + .help = "Erase key-value pair from current namespace", + .hint = NULL, + .func = &erase_value, + .argtable = &erase_args + }; + + const esp_console_cmd_t erase_namespace_cmd = { + .command = "nvs_erase_namespace", + .help = "Erases specified namespace", + .hint = NULL, + .func = &erase_namespace, + .argtable = &erase_all_args + }; + + const esp_console_cmd_t namespace_cmd = { + .command = "nvs_namespace", + .help = "Set current namespace", + .hint = NULL, + .func = &set_namespace, + .argtable = &namespace_args + }; + + const esp_console_cmd_t list_entries_cmd = { + .command = "nvs_list", + .help = "List stored key-value pairs stored in NVS." + "Namespace and type can be specified to print only those key-value pairs.\n" + "Following command list variables stored inside 'nvs' partition, under namespace 'storage' with type uint32_t" + "Example: nvs_list nvs -n storage -t u32 \n", + .hint = NULL, + .func = &list_entries, + .argtable = &list_args + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&set_cmd)); + ESP_ERROR_CHECK(esp_console_cmd_register(&get_cmd)); + ESP_ERROR_CHECK(esp_console_cmd_register(&erase_cmd)); + ESP_ERROR_CHECK(esp_console_cmd_register(&namespace_cmd)); + ESP_ERROR_CHECK(esp_console_cmd_register(&list_entries_cmd)); + ESP_ERROR_CHECK(esp_console_cmd_register(&erase_namespace_cmd)); +} diff --git a/components/command/cmd_wifi.h b/components/command/cmd_nvs.h similarity index 67% rename from components/command/cmd_wifi.h rename to components/command/cmd_nvs.h index 2ba4bac..ba4887b 100644 --- a/components/command/cmd_wifi.h +++ b/components/command/cmd_nvs.h @@ -1,8 +1,3 @@ -/* - * 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.) @@ -17,8 +12,8 @@ extern "C" { #endif -// Register WiFi functions -void register_wifi(void); +// Register NVS functions +void register_nvs(void); #ifdef __cplusplus } diff --git a/components/command/cmd_wifi.c b/components/command/cmd_wifi.c deleted file mode 100644 index 59ee518..0000000 --- a/components/command/cmd_wifi.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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/main/main.cpp b/main/main.cpp index 46244b1..9cc2443 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,7 +1,8 @@ +#include "Application.h" #include "CustomCommand.h" #include "LedController.h" +#include "cmd_nvs.h" #include "cmd_system.h" -#include "cmd_wifi.h" #include "driver/uart.h" #include "esp_console.h" #include "esp_log.h" @@ -29,13 +30,16 @@ extern "C" void app_main() { initialize_nvs(); initialize_filesystem(); initialize_console(); + Application::instance()->initialize(); + Application::instance()->setField("author", "amass"); esp_console_register_help_command(); register_system_common(); register_system_sleep(); - register_wifi(); register_custom(); + register_nvs(); LedController::instance()->initialize(); + while (true) { char *line = linenoise(prompt); if (line == NULL) { /* Break on EOF or error */ @@ -73,7 +77,7 @@ static void initialize_console() { esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR); esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF); - uart_config_t uart_config = {0}; + uart_config_t uart_config = {}; uart_config.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE; uart_config.data_bits = UART_DATA_8_BITS; uart_config.parity = UART_PARITY_DISABLE; @@ -84,7 +88,7 @@ static void initialize_console() { esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM); - esp_console_config_t console_config = {0}; + esp_console_config_t console_config = {}; console_config.max_cmdline_args = 8; console_config.max_cmdline_length = 256; console_config.hint_color = atoi(LOG_COLOR_CYAN); diff --git a/partitions.csv b/partitions.csv index d4d8f56..25330ab 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, 1216K, +factory, app, factory, 0x10000, 1280K, storage, data, fat, , 1M,