/** * @file cli_core.c * @brief CLI core implementation: command table + handlers + line execution. * Transport-agnostic: all output goes through the current cli_out_fn * (default: USART1 DMA TX). The UART frontend lives in app_cli.c; * the BLE frame frontend (app_ble_proto.c) switches the output to * framed CLI_RSP responses around cli_exec_line(). */ #include "cli_core.h" #include "app_version.h" #include "bsp_usart.h" #include "app_gpio.h" #include "app_info.h" #include #include #include #include "FreeRTOS.h" #include "task.h" #define CLI_TX_BUF_SIZE 96 #define CLI_MAX_ARGS 8 typedef void (*CliCmdHandler_t)(int argc, char* argv[]); typedef struct { const char* name; const char* help; CliCmdHandler_t handler; } CliCmd_t; static char s_txBuf[CLI_TX_BUF_SIZE]; static cli_out_fn s_out = NULL; /* NULL = default dual output */ /* ------------------------------------------------------------------ */ /* Output */ /* ------------------------------------------------------------------ */ /** * @brief Default output: USART1 DMA TX. BLE-originated commands temporarily * switch the output to framed CLI_RSP (proto_cli_out) via * cli_set_output() around cli_exec_line(). * @note Strings must contain explicit "\r\n" (no auto CR like fputc). */ static void cli_out_uart(const char* str) { bsp_usart_write_dma((const uint8_t*)str, (uint16_t)strlen(str)); } void cli_set_output(cli_out_fn out) { s_out = out; } void cli_write(const char* str) { if (s_out != NULL) { s_out(str); } else { cli_out_uart(str); } } void cli_printf(const char* fmt, ...) { va_list args; va_start(args, fmt); vsnprintf(s_txBuf, CLI_TX_BUF_SIZE, fmt, args); va_end(args); s_txBuf[CLI_TX_BUF_SIZE - 1] = '\0'; cli_write(s_txBuf); } /* ------------------------------------------------------------------ */ /* Built-in commands */ /* ------------------------------------------------------------------ */ static void CmdHelp(int argc, char* argv[]); /** * @brief "version": show firmware and clock info. */ static void CmdVersion(int argc, char* argv[]) { (void)argc; (void)argv; cli_printf("firmware version: %s\r\n", APP_FW_VERSION); cli_write("mcm-ddc-ble: BLE(rdtss) + FreeRTOS + UART(DMA) + CLI\r\n"); cli_printf("SystemCoreClock = %lu Hz\r\n", (unsigned long)SystemCoreClock); } /** * @brief "sysinfo": show FreeRTOS runtime info (uptime, tasks, heap, * per-task state/priority/stack high water mark). */ static void CmdSysInfo(int argc, char* argv[]) { static char s_taskList[384]; /* vTaskList output buffer */ uint32_t ticks = (uint32_t)xTaskGetTickCount(); (void)argc; (void)argv; cli_printf("uptime: %lu ticks (%lu s)\r\n", (unsigned long)ticks, (unsigned long)(ticks / configTICK_RATE_HZ)); cli_printf("tasks: %u\r\n", (unsigned int)uxTaskGetNumberOfTasks()); cli_printf("heap: total %u B, free %u B, min ever free %u B\r\n", (unsigned int)configTOTAL_HEAP_SIZE, (unsigned int)xPortGetFreeHeapSize(), (unsigned int)xPortGetMinimumEverFreeHeapSize()); /* vTaskList lines already end with "\r\n"; stack column is in words */ cli_write("name state prio stack(w) num\r\n"); s_taskList[0] = '\0'; vTaskList(s_taskList); cli_write(s_taskList); } /** * @brief "led <1|2> ": control a board LED. */ static void CmdLed(int argc, char* argv[]) { GPIO_Module* port; uint16_t pin; if (argc < 3) { cli_write("usage: led <1|2> \r\n"); return; } if (argv[1][0] == '1' && argv[1][1] == '\0') { port = LED1_PORT; pin = LED1_PIN; } else if (argv[1][0] == '2' && argv[1][1] == '\0') { port = LED2_PORT; pin = LED2_PIN; } else { cli_write("usage: led <1|2> \r\n"); return; } if (strcmp(argv[2], "on") == 0) { LedOn(port, pin); } else if (strcmp(argv[2], "off") == 0) { LedOff(port, pin); } else if (strcmp(argv[2], "toggle") == 0) { LedBlink(port, pin); } else { cli_write("usage: led <1|2> \r\n"); return; } cli_printf("led %s %s done\r\n", argv[1], argv[2]); } /** * @brief "devinfo": show the same items the BLE INFO_QUERY(all) reports. */ static void CmdDevInfo(int argc, char* argv[]) { int16_t temp = app_info_chip_temp_c10(); uint16_t fan = app_fan_get_rpm(); (void)argc; (void)argv; cli_printf("firmware: %s (0x%08lX)\r\n", APP_FW_VERSION, (unsigned long)APP_FW_VERSION_NUM); if (temp == 0x7FFF) { cli_write("chip temp: ADC read timeout\r\n"); } else { cli_printf("chip temp: %d.%d C\r\n", (int)(temp / 10), (int)(temp % 10)); } cli_printf("vdd: %u mV\r\n", (unsigned int)app_info_vdd_mv()); if (fan == APP_FAN_RPM_INVALID) { cli_write("fan rpm: n/a (no tachometer)\r\n"); } else { cli_printf("fan rpm: %u\r\n", (unsigned int)fan); } cli_printf("uptime: %lu s\r\n", (unsigned long)((uint32_t)xTaskGetTickCount() / configTICK_RATE_HZ)); cli_printf("free heap: %u B\r\n", (unsigned int)xPortGetFreeHeapSize()); } /* Command table; add new commands here */ static const CliCmd_t s_cmds[] = { {"help", "list all commands", CmdHelp}, {"version", "show firmware version", CmdVersion}, {"sysinfo", "show FreeRTOS runtime info (uptime/tasks/heap/task list)", CmdSysInfo}, {"devinfo", "show device info (temp/vdd/fan, same as BLE INFO_QUERY all)", CmdDevInfo}, {"led", "led <1|2> : control board LED", CmdLed}, }; #define CLI_CMD_COUNT (sizeof(s_cmds) / sizeof(s_cmds[0])) /** * @brief "help": list all registered commands. */ static void CmdHelp(int argc, char* argv[]) { uint32_t i; (void)argc; (void)argv; cli_write("commands:\r\n"); for (i = 0; i < CLI_CMD_COUNT; i++) { cli_printf(" %-8s %s\r\n", s_cmds[i].name, s_cmds[i].help); } cli_write("keys: TAB = complete, UP/DOWN = history\r\n"); } uint32_t cli_cmd_count(void) { return CLI_CMD_COUNT; } const char* cli_cmd_name(uint32_t idx) { return (idx < CLI_CMD_COUNT) ? s_cmds[idx].name : NULL; } /* ------------------------------------------------------------------ */ /* Line parsing / dispatch */ /* ------------------------------------------------------------------ */ int cli_exec_line(char* line) { char* argv[CLI_MAX_ARGS]; int argc = 0; char* p = line; uint32_t i; /* Split on spaces/tabs */ while (*p != '\0' && argc < CLI_MAX_ARGS) { while (*p == ' ' || *p == '\t') { p++; } if (*p == '\0') { break; } argv[argc++] = p; while (*p != '\0' && *p != ' ' && *p != '\t') { p++; } if (*p != '\0') { *p++ = '\0'; } } if (argc == 0) { return 0; } for (i = 0; i < CLI_CMD_COUNT; i++) { if (strcmp(argv[0], s_cmds[i].name) == 0) { s_cmds[i].handler(argc, argv); return 0; } } cli_printf("unknown command: %s (try 'help')\r\n", argv[0]); return 1; }