#include "serial_config.h" #include #include #include "nvs.h" #include "nvs_flash.h" void serial_config_defaults(serial_config_t *config) { if (config == NULL) { return; } *config = (serial_config_t) { .version = SERIAL_CONFIG_VERSION, .baud_rate = 115200, .data_bits = SERIAL_CONFIG_DATA_BITS_8, .parity = SERIAL_CONFIG_PARITY_NONE, .stop_bits = SERIAL_CONFIG_STOP_BITS_1, .flow_control = SERIAL_CONFIG_FLOW_CONTROL_NONE, .dtr_behavior = SERIAL_CONFIG_DTR_INACTIVE, .rts_threshold = SERIAL_CONFIG_DEFAULT_RTS_THRESHOLD, }; } esp_err_t serial_config_validate(const serial_config_t *config) { if (config == NULL || config->version != SERIAL_CONFIG_VERSION || config->baud_rate < SERIAL_CONFIG_MIN_BAUD_RATE || config->baud_rate > SERIAL_CONFIG_MAX_BAUD_RATE || config->rts_threshold == 0 || config->rts_threshold > SERIAL_CONFIG_MAX_RTS_THRESHOLD) { return ESP_ERR_INVALID_ARG; } switch (config->data_bits) { case SERIAL_CONFIG_DATA_BITS_7: case SERIAL_CONFIG_DATA_BITS_8: break; default: return ESP_ERR_INVALID_ARG; } switch (config->parity) { case SERIAL_CONFIG_PARITY_NONE: case SERIAL_CONFIG_PARITY_EVEN: case SERIAL_CONFIG_PARITY_ODD: break; default: return ESP_ERR_INVALID_ARG; } switch (config->stop_bits) { case SERIAL_CONFIG_STOP_BITS_1: case SERIAL_CONFIG_STOP_BITS_2: break; default: return ESP_ERR_INVALID_ARG; } switch (config->flow_control) { case SERIAL_CONFIG_FLOW_CONTROL_NONE: case SERIAL_CONFIG_FLOW_CONTROL_RTS_CTS: break; default: return ESP_ERR_INVALID_ARG; } switch (config->dtr_behavior) { case SERIAL_CONFIG_DTR_INACTIVE: case SERIAL_CONFIG_DTR_ACTIVE: case SERIAL_CONFIG_DTR_ON_CONNECT: break; default: return ESP_ERR_INVALID_ARG; } return ESP_OK; } esp_err_t serial_config_to_uart_config(const serial_config_t *config, uart_config_t *uart_config) { if (uart_config == NULL) { return ESP_ERR_INVALID_ARG; } esp_err_t err = serial_config_validate(config); if (err != ESP_OK) { return err; } uart_word_length_t data_bits; switch (config->data_bits) { case SERIAL_CONFIG_DATA_BITS_7: data_bits = UART_DATA_7_BITS; break; case SERIAL_CONFIG_DATA_BITS_8: data_bits = UART_DATA_8_BITS; break; default: return ESP_ERR_INVALID_ARG; } uart_parity_t parity; switch (config->parity) { case SERIAL_CONFIG_PARITY_NONE: parity = UART_PARITY_DISABLE; break; case SERIAL_CONFIG_PARITY_EVEN: parity = UART_PARITY_EVEN; break; case SERIAL_CONFIG_PARITY_ODD: parity = UART_PARITY_ODD; break; default: return ESP_ERR_INVALID_ARG; } uart_stop_bits_t stop_bits; switch (config->stop_bits) { case SERIAL_CONFIG_STOP_BITS_1: stop_bits = UART_STOP_BITS_1; break; case SERIAL_CONFIG_STOP_BITS_2: stop_bits = UART_STOP_BITS_2; break; default: return ESP_ERR_INVALID_ARG; } uart_hw_flowcontrol_t flow_control; switch (config->flow_control) { case SERIAL_CONFIG_FLOW_CONTROL_NONE: flow_control = UART_HW_FLOWCTRL_DISABLE; break; case SERIAL_CONFIG_FLOW_CONTROL_RTS_CTS: flow_control = UART_HW_FLOWCTRL_CTS_RTS; break; default: return ESP_ERR_INVALID_ARG; } *uart_config = (uart_config_t) { .baud_rate = (int)config->baud_rate, .data_bits = data_bits, .parity = parity, .stop_bits = stop_bits, .flow_ctrl = flow_control, .rx_flow_ctrl_thresh = (uint8_t)config->rts_threshold, .source_clk = UART_SCLK_DEFAULT, }; return ESP_OK; } bool serial_config_parse_data_bits(const char *text, serial_config_data_bits_t *value) { if (text == NULL || value == NULL) { return false; } if (strcmp(text, "7") == 0) { *value = SERIAL_CONFIG_DATA_BITS_7; return true; } if (strcmp(text, "8") == 0) { *value = SERIAL_CONFIG_DATA_BITS_8; return true; } return false; } const char *serial_config_data_bits_to_string(serial_config_data_bits_t value) { switch (value) { case SERIAL_CONFIG_DATA_BITS_7: return "7"; case SERIAL_CONFIG_DATA_BITS_8: return "8"; default: return NULL; } } bool serial_config_parse_parity(const char *text, serial_config_parity_t *value) { if (text == NULL || value == NULL) { return false; } if (strcmp(text, "none") == 0) { *value = SERIAL_CONFIG_PARITY_NONE; return true; } if (strcmp(text, "even") == 0) { *value = SERIAL_CONFIG_PARITY_EVEN; return true; } if (strcmp(text, "odd") == 0) { *value = SERIAL_CONFIG_PARITY_ODD; return true; } return false; } const char *serial_config_parity_to_string(serial_config_parity_t value) { switch (value) { case SERIAL_CONFIG_PARITY_NONE: return "none"; case SERIAL_CONFIG_PARITY_EVEN: return "even"; case SERIAL_CONFIG_PARITY_ODD: return "odd"; default: return NULL; } } bool serial_config_parse_stop_bits(const char *text, serial_config_stop_bits_t *value) { if (text == NULL || value == NULL) { return false; } if (strcmp(text, "1") == 0) { *value = SERIAL_CONFIG_STOP_BITS_1; return true; } if (strcmp(text, "2") == 0) { *value = SERIAL_CONFIG_STOP_BITS_2; return true; } return false; } const char *serial_config_stop_bits_to_string(serial_config_stop_bits_t value) { switch (value) { case SERIAL_CONFIG_STOP_BITS_1: return "1"; case SERIAL_CONFIG_STOP_BITS_2: return "2"; default: return NULL; } } bool serial_config_parse_flow_control(const char *text, serial_config_flow_control_t *value) { if (text == NULL || value == NULL) { return false; } if (strcmp(text, "none") == 0) { *value = SERIAL_CONFIG_FLOW_CONTROL_NONE; return true; } if (strcmp(text, "rts-cts") == 0) { *value = SERIAL_CONFIG_FLOW_CONTROL_RTS_CTS; return true; } return false; } const char *serial_config_flow_control_to_string(serial_config_flow_control_t value) { switch (value) { case SERIAL_CONFIG_FLOW_CONTROL_NONE: return "none"; case SERIAL_CONFIG_FLOW_CONTROL_RTS_CTS: return "rts-cts"; default: return NULL; } } bool serial_config_parse_dtr_behavior(const char *text, serial_config_dtr_behavior_t *value) { if (text == NULL || value == NULL) { return false; } if (strcmp(text, "inactive") == 0) { *value = SERIAL_CONFIG_DTR_INACTIVE; return true; } if (strcmp(text, "active") == 0) { *value = SERIAL_CONFIG_DTR_ACTIVE; return true; } if (strcmp(text, "on-connect") == 0) { *value = SERIAL_CONFIG_DTR_ON_CONNECT; return true; } return false; } const char *serial_config_dtr_behavior_to_string(serial_config_dtr_behavior_t value) { switch (value) { case SERIAL_CONFIG_DTR_INACTIVE: return "inactive"; case SERIAL_CONFIG_DTR_ACTIVE: return "active"; case SERIAL_CONFIG_DTR_ON_CONNECT: return "on-connect"; default: return NULL; } } esp_err_t serial_config_storage_init(void) { /* * Never erase the shared default NVS partition automatically. Future Wi-Fi, * certificates, and provisioning data will live there too; destructive * recovery belongs behind an explicit factory-reset operation. */ return nvs_flash_init(); } esp_err_t serial_config_load(serial_config_t *config, bool *used_stored_config) { if (config == NULL || used_stored_config == NULL) { return ESP_ERR_INVALID_ARG; } /* Callers always receive a usable configuration when storage is absent or stale. */ serial_config_defaults(config); *used_stored_config = false; esp_err_t err = serial_config_storage_init(); if (err != ESP_OK) { return err; } nvs_handle_t handle; err = nvs_open(SERIAL_CONFIG_NVS_NAMESPACE, NVS_READONLY, &handle); if (err == ESP_ERR_NVS_NOT_FOUND) { return ESP_OK; } if (err != ESP_OK) { return err; } size_t stored_size = 0; err = nvs_get_blob(handle, SERIAL_CONFIG_NVS_BLOB_KEY, NULL, &stored_size); if (err == ESP_ERR_NVS_NOT_FOUND || err == ESP_ERR_NVS_TYPE_MISMATCH) { nvs_close(handle); return ESP_OK; } if (err != ESP_OK) { nvs_close(handle); return err; } if (stored_size != sizeof(serial_config_t)) { nvs_close(handle); return ESP_OK; } serial_config_t stored_config; err = nvs_get_blob(handle, SERIAL_CONFIG_NVS_BLOB_KEY, &stored_config, &stored_size); nvs_close(handle); if (err == ESP_ERR_NVS_INVALID_LENGTH) { return ESP_OK; } if (err != ESP_OK) { return err; } if (stored_size != sizeof(stored_config) || serial_config_validate(&stored_config) != ESP_OK) { return ESP_OK; } *config = stored_config; *used_stored_config = true; return ESP_OK; } esp_err_t serial_config_save(const serial_config_t *config) { esp_err_t err = serial_config_validate(config); if (err != ESP_OK) { return err; } err = serial_config_storage_init(); if (err != ESP_OK) { return err; } nvs_handle_t handle; err = nvs_open(SERIAL_CONFIG_NVS_NAMESPACE, NVS_READWRITE, &handle); if (err != ESP_OK) { return err; } err = nvs_set_blob(handle, SERIAL_CONFIG_NVS_BLOB_KEY, config, sizeof(*config)); if (err == ESP_OK) { err = nvs_commit(handle); } nvs_close(handle); return err; } esp_err_t serial_config_reset_storage(void) { serial_config_t defaults; serial_config_defaults(&defaults); return serial_config_save(&defaults); }