From 7ca4c3b631cf57affb4afdb05a81bcb33863375f Mon Sep 17 00:00:00 2001 From: DrZlo13 Date: Wed, 1 Dec 2021 14:15:45 +1000 Subject: [PATCH] Initial CLI implementation --- main/CMakeLists.txt | 5 +- main/cli-commands.c | 23 ++++++ main/cli-commands.h | 20 +++++ main/cli-uart.c | 81 +++++++++++++++++++ main/cli-uart.h | 3 + main/cli.c | 192 ++++++++++++++++++++++++++++++++++++++++++++ main/cli.h | 36 +++++++++ main/main.c | 7 +- main/network-http.c | 4 +- main/network.c | 16 ++-- main/uart.c | 37 --------- main/uart.h | 4 - 12 files changed, 372 insertions(+), 56 deletions(-) create mode 100644 main/cli-commands.c create mode 100644 main/cli-commands.h create mode 100644 main/cli-uart.c create mode 100644 main/cli-uart.h create mode 100644 main/cli.c create mode 100644 main/cli.h delete mode 100644 main/uart.c delete mode 100644 main/uart.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 8f16899..15449fc 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -3,13 +3,16 @@ set(SOURCES "usb-cdc.c" "nvs.c" "led.c" - "uart.c" + "cli-uart.c" "i2c.c" "delay.c" "network.c" "network-http.c" "network-uart.c" "network-gdb.c" + "cli.c" + "cli-uart.c" + "cli-commands.c" ) set(INCLUDES diff --git a/main/cli-commands.c b/main/cli-commands.c new file mode 100644 index 0000000..58f0060 --- /dev/null +++ b/main/cli-commands.c @@ -0,0 +1,23 @@ +#include +#include +#include "cli.h" +#include "cli-commands.h" +#include "helpers.h" + +void cli_help(Cli* cli, mstring_t* args); + +const CliItem cli_items[] = { + { + .name = "help", + .callback = cli_help, + }, +}; + +size_t cli_items_count = COUNT_OF(cli_items); + +void cli_help(Cli* cli, mstring_t* args) { + for(size_t i = 0; i < cli_items_count; i++) { + cli_write_str(cli, cli_items[i].name); + cli_write_eol(cli); + } +} \ No newline at end of file diff --git a/main/cli-commands.h b/main/cli-commands.h new file mode 100644 index 0000000..7cc2933 --- /dev/null +++ b/main/cli-commands.h @@ -0,0 +1,20 @@ +/** + * @file cli-commands.h + * @author Sergey Gavrilov (who.just.the.doctor@gmail.com) + * @version 1.0 + * @date 2021-12-01 + * + * Cli commands list + */ +#pragma once +#include + +typedef void (*CliCallback)(Cli* cli, mstring_t* args); + +struct CliItem { + const char* name; + CliCallback callback; +}; + +extern const CliItem cli_items[]; +extern size_t cli_items_count; \ No newline at end of file diff --git a/main/cli-uart.c b/main/cli-uart.c new file mode 100644 index 0000000..ced5c29 --- /dev/null +++ b/main/cli-uart.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include "cli.h" + +#define CLI_UART_PORT_NUM UART_NUM_1 +#define CLI_UART_TXD_PIN (17) +#define CLI_UART_RXD_PIN (18) +#define CLI_UART_RTS_PIN (UART_PIN_NO_CHANGE) +#define CLI_UART_CTS_PIN (UART_PIN_NO_CHANGE) +#define CLI_UART_BAUD_RATE (115200) +#define CLI_UART_BUF_SIZE (128) + +static Cli* cli_uart; +static QueueHandle_t cli_uart_queue; +static void cli_uart_write(const uint8_t* data, size_t data_size, void* context); + +static void cli_uart_rx_task(void* pvParameters) { + uart_event_t event; + uint8_t* rx_buffer = (uint8_t*)malloc(CLI_UART_BUF_SIZE); + int received = 0; + + for(;;) { + //Waiting for UART event. + if(xQueueReceive(cli_uart_queue, (void*)&event, (portTickType)portMAX_DELAY)) { + bzero(rx_buffer, CLI_UART_BUF_SIZE); + switch(event.type) { + case UART_DATA: + received = + uart_read_bytes(CLI_UART_PORT_NUM, rx_buffer, event.size, portMAX_DELAY); + for(int i = 0; i < received; i++) { + cli_handle_char(cli_uart, rx_buffer[i]); + } + + break; + default: + break; + } + } + } + + free(rx_buffer); + rx_buffer = NULL; + vTaskDelete(NULL); +} + +void cli_uart_init() { + cli_uart = cli_init(); + cli_set_write_cb(cli_uart, cli_uart_write); + + uart_config_t uart_config = { + .baud_rate = CLI_UART_BAUD_RATE, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_APB, + }; + int intr_alloc_flags = 0; + +#if CONFIG_UART_ISR_IN_IRAM + intr_alloc_flags = ESP_INTR_FLAG_IRAM; +#endif + + ESP_ERROR_CHECK(uart_driver_install( + CLI_UART_PORT_NUM, + CLI_UART_BUF_SIZE * 2, + CLI_UART_BUF_SIZE * 2, + 10, + &cli_uart_queue, + intr_alloc_flags)); + ESP_ERROR_CHECK(uart_param_config(CLI_UART_PORT_NUM, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin( + CLI_UART_PORT_NUM, CLI_UART_TXD_PIN, CLI_UART_RXD_PIN, CLI_UART_RTS_PIN, CLI_UART_CTS_PIN)); + + xTaskCreate(cli_uart_rx_task, "cli_uart_rx", 4096, NULL, 5, NULL); +} + +static void cli_uart_write(const uint8_t* data, size_t data_size, void* context) { + uart_write_bytes(CLI_UART_PORT_NUM, data, data_size); +} diff --git a/main/cli-uart.h b/main/cli-uart.h new file mode 100644 index 0000000..6c3b1d4 --- /dev/null +++ b/main/cli-uart.h @@ -0,0 +1,3 @@ +#pragma once + +void cli_uart_init(); \ No newline at end of file diff --git a/main/cli.c b/main/cli.c new file mode 100644 index 0000000..f42dceb --- /dev/null +++ b/main/cli.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include "cli.h" +#include "delay.h" +#include "helpers.h" +#include "cli-commands.h" +#include + +typedef enum { + CliSymbolAsciiSOH = 0x01, + CliSymbolAsciiETX = 0x03, + CliSymbolAsciiEOT = 0x04, + CliSymbolAsciiBell = 0x07, + CliSymbolAsciiBackspace = 0x08, + CliSymbolAsciiTab = 0x09, + CliSymbolAsciiCR = 0x0D, + CliSymbolAsciiEsc = 0x1B, + CliSymbolAsciiUS = 0x1F, + CliSymbolAsciiSpace = 0x20, + CliSymbolAsciiDel = 0x7F, +} CliSymbols; + +struct Cli { + mstring_t* line; + size_t cursor_position; + + void* context; + CliWrite write_cb; + CliFlush flush_cb; +}; + +Cli* cli_init(void) { + Cli* cli = malloc(sizeof(Cli)); + cli->line = mstring_alloc(); + cli->context = NULL; + cli->write_cb = NULL; + cli->flush_cb = NULL; + + return cli; +} + +void cli_reset(Cli* cli) { + mstring_reset(cli->line); + cli->cursor_position = 0; +} + +void cli_set_context(Cli* cli, void* context) { + cli->context = context; +} + +void cli_set_write_cb(Cli* cli, CliWrite write_cb) { + cli->write_cb = write_cb; +} + +void cli_set_flush_cb(Cli* cli, CliFlush flush_cb) { + cli->flush_cb = flush_cb; +} + +void cli_write(Cli* cli, const uint8_t* data, size_t data_size) { + ESP_LOGI("cli", "write"); + if(cli->write_cb != NULL) { + cli->write_cb(data, data_size, cli->context); + } +} + +void cli_flush(Cli* cli) { + ESP_LOGI("cli", "flush"); + if(cli->flush_cb != NULL) { + cli->flush_cb(cli->context); + } +} + +void cli_write_str(Cli* cli, const char* str) { + cli_write(cli, (const uint8_t*)str, strlen(str)); +} + +void cli_write_char(Cli* cli, uint8_t c) { + cli_write(cli, &c, 1); +} + +void cli_write_eol(Cli* cli) { + cli_write_str(cli, "\r\n"); +} + +static void cli_write_motd(Cli* cli) { + cli_write_str(cli, "Hello!\r\nThis is MOTD\r\n"); +} + +static void cli_write_prompt(Cli* cli) { + cli_write_str(cli, "[esp]>"); +} + +static const CliItem* cli_search_item(Cli* cli, const mstring_t* command) { + const CliItem* item = NULL; + + for(size_t i = 0; i < cli_items_count; i++) { + if(mstring_cmp_cstr(command, cli_items[i].name) == 0) { + item = &cli_items[i]; + } + } + + return item; +} + +static bool cli_handle_enter(Cli* cli) { + mstring_t* command = mstring_alloc(); + mstring_t* args = mstring_alloc(); + bool result = false; + const CliItem* item = NULL; + + mstring_strim(cli->line, " \n\r\t"); + + size_t ws = mstring_search_char(cli->line, ' ', 0); + if(ws == STRING_FAILURE) { + mstring_set(command, mstring_get_cstr(cli->line)); + } else { + mstring_set_n(command, cli->line, 0, ws); + mstring_set_n(args, cli->line, ws, mstring_size(cli->line)); + mstring_strim(args, " \n\r\t"); + } + + item = cli_search_item(cli, command); + if(item != NULL) { + item->callback(cli, args); + } else { + cli_write_str(cli, "Not found"); + } + + mstring_free(command); + mstring_free(args); + return result; +} + +static void cli_handle_backspace(Cli* cli) { + if(mstring_size(cli->line) > 0) { + // Other side + cli_write_str(cli, "\e[D\e[1P"); + cli_flush(cli); + // Our side + mstring_set_strn(cli->line, mstring_get_cstr(cli->line), mstring_size(cli->line) - 1); + cli->cursor_position--; + } else { + cli_write_char(cli, CliSymbolAsciiBell); + } +} + +void cli_handle_char(Cli* cli, uint8_t c) { + ESP_LOGI("cli", "%c", c); + + switch(c) { + case CliSymbolAsciiCR: + cli_write_eol(cli); + cli_handle_enter(cli); + cli_reset(cli); + cli_write_eol(cli); + cli_write_prompt(cli); + break; + case CliSymbolAsciiDel: + case CliSymbolAsciiBackspace: + cli_handle_backspace(cli); + break; + case CliSymbolAsciiSOH: + delay(33); + cli_write_motd(cli); + cli_write_eol(cli); + cli_write_prompt(cli); + break; + case CliSymbolAsciiETX: + cli_reset(cli); + cli_write_eol(cli); + cli_write_prompt(cli); + break; + case CliSymbolAsciiEOT: + cli_reset(cli); + break; + case ' ' ... '~': + if(cli->cursor_position == mstring_size(cli->line)) { + mstring_push_back(cli->line, c); + cli_write_char(cli, c); + } else { + mstring_push_back(cli->line, c); + cli_write_str(cli, "\e[4h"); + cli_write_char(cli, c); + cli_write_str(cli, "\e[4l"); + cli_flush(cli); + } + break; + default: + break; + } +} diff --git a/main/cli.h b/main/cli.h new file mode 100644 index 0000000..8f9dadc --- /dev/null +++ b/main/cli.h @@ -0,0 +1,36 @@ +/** + * @file cli.h + * @author Sergey Gavrilov (who.just.the.doctor@gmail.com) + * @version 1.0 + * @date 2021-12-01 + * + * Cli commands + */ + +#pragma once + +typedef struct Cli Cli; +typedef struct CliItem CliItem; + +typedef void (*CliWrite)(const uint8_t* data, size_t data_size, void* context); +typedef void (*CliFlush)(void* context); + +Cli* cli_init(void); + +void cli_set_context(Cli* cli, void* context); + +void cli_set_write_cb(Cli* cli, CliWrite write_cb); + +void cli_set_flush_cb(Cli* cli, CliFlush flush_cb); + +void cli_write(Cli* cli, const uint8_t* data, size_t data_size); + +void cli_flush(Cli* cli); + +void cli_write_str(Cli* cli, const char* str); + +void cli_write_char(Cli* cli, uint8_t c); + +void cli_handle_char(Cli* cli, uint8_t c); + +void cli_write_eol(Cli* cli); \ No newline at end of file diff --git a/main/main.c b/main/main.c index e0ca630..9e9d137 100644 --- a/main/main.c +++ b/main/main.c @@ -8,7 +8,7 @@ #include "nvs.h" #include "gdb_main.h" #include "led.h" -#include "uart.h" +#include "cli-uart.h" #include "i2c.h" #include "network.h" #include "network-http.h" @@ -65,10 +65,9 @@ void app_main(void) { usb_cdc_init(); - // TODO uart and i2c share the same pins, need switching mechanics - // uart_init(); - // uart_print("Uart inited"); + cli_uart_init(); + // TODO uart and i2c share the same pins, need switching mechanics // i2c_init(); // i2c_scan(); diff --git a/main/network-http.c b/main/network-http.c index 21c97c8..2096bb6 100644 --- a/main/network-http.c +++ b/main/network-http.c @@ -361,11 +361,11 @@ static esp_err_t wifi_set_credenitals_handler(httpd_req_t* req) { goto err_fail; } - if(mstring_length(ap_pass) < 8) { + if(mstring_size(ap_pass) < 8) { error_text = JSON_ERROR("too short value in [pass]"); goto err_fail; } - if(mstring_length(ap_ssid) < 1) { + if(mstring_size(ap_ssid) < 1) { error_text = JSON_ERROR("too short value in [ssid]"); goto err_fail; } diff --git a/main/network.c b/main/network.c index 336d74c..4fc7900 100644 --- a/main/network.c +++ b/main/network.c @@ -99,16 +99,16 @@ static void network_start_ap(mstring_t* ap_ssid, mstring_t* ap_pass) { memcpy( &wifi_config.ap.ssid, mstring_get_cstr(ap_ssid), - min(sizeof(wifi_config.ap.ssid), mstring_length(ap_ssid))); + min(sizeof(wifi_config.ap.ssid), mstring_size(ap_ssid))); memcpy( &wifi_config.ap.password, mstring_get_cstr(ap_pass), - min(sizeof(wifi_config.ap.password), mstring_length(ap_pass))); + min(sizeof(wifi_config.ap.password), mstring_size(ap_pass))); - wifi_config.ap.ssid_len = mstring_length(ap_ssid); + wifi_config.ap.ssid_len = mstring_size(ap_ssid); - if(mstring_length(ap_pass) == 0) { + if(mstring_size(ap_pass) == 0) { wifi_config.ap.authmode = WIFI_AUTH_OPEN; } @@ -145,11 +145,11 @@ static bool network_connect_ap(mstring_t* ap_ssid, mstring_t* ap_pass) { memcpy( &wifi_config.sta.ssid, mstring_get_cstr(ap_ssid), - min(sizeof(wifi_config.sta.ssid), mstring_length(ap_ssid))); + min(sizeof(wifi_config.sta.ssid), mstring_size(ap_ssid))); memcpy( &wifi_config.sta.password, mstring_get_cstr(ap_pass), - min(sizeof(wifi_config.sta.password), mstring_length(ap_pass))); + min(sizeof(wifi_config.sta.password), mstring_size(ap_pass))); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); @@ -226,7 +226,7 @@ WIFIMode network_init(void) { nvs_save_string("ap_pass", ap_pass); } - if(mstring_length(ap_pass) < 8) { + if(mstring_size(ap_pass) < 8) { ESP_LOGW(TAG, "too short [ap_pass]"); mstring_set(ap_ssid, ESP_WIFI_SSID); nvs_save_string("ap_ssid", ap_ssid); @@ -236,7 +236,7 @@ WIFIMode network_init(void) { nvs_save_string("ap_mode", ap_mode); } - if(mstring_length(ap_ssid) < 1 || mstring_length(ap_ssid) > 32) { + if(mstring_size(ap_ssid) < 1 || mstring_size(ap_ssid) > 32) { ESP_LOGW(TAG, "too short or too long [ap_ssid]"); mstring_set(ap_ssid, ESP_WIFI_SSID); nvs_save_string("ap_ssid", ap_ssid); diff --git a/main/uart.c b/main/uart.c deleted file mode 100644 index 269c679..0000000 --- a/main/uart.c +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include -#include - -#define UART_PORT_NUM UART_NUM_1 -#define UART_TXD_PIN (17) -#define UART_RXD_PIN (18) -#define UART_RTS_PIN (UART_PIN_NO_CHANGE) -#define UART_CTS_PIN (UART_PIN_NO_CHANGE) -#define UART_BAUD_RATE (115200) -#define UART_BUF_SIZE (1024) - -void uart_init() { - uart_config_t uart_config = { - .baud_rate = UART_BAUD_RATE, - .data_bits = UART_DATA_8_BITS, - .parity = UART_PARITY_DISABLE, - .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, - .source_clk = UART_SCLK_APB, - }; - int intr_alloc_flags = 0; - -#if CONFIG_UART_ISR_IN_IRAM - intr_alloc_flags = ESP_INTR_FLAG_IRAM; -#endif - - ESP_ERROR_CHECK( - uart_driver_install(UART_PORT_NUM, UART_BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags)); - ESP_ERROR_CHECK(uart_param_config(UART_PORT_NUM, &uart_config)); - ESP_ERROR_CHECK( - uart_set_pin(UART_PORT_NUM, UART_TXD_PIN, UART_RXD_PIN, UART_RTS_PIN, UART_CTS_PIN)); -} - -void uart_print(const char* str) { - uart_write_bytes(UART_PORT_NUM, str, strlen(str)); -} diff --git a/main/uart.h b/main/uart.h deleted file mode 100644 index 703f5ff..0000000 --- a/main/uart.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -void uart_init(); -void uart_print(const char* str); \ No newline at end of file