/* * Copyright (C) OpenTX * * Based on code named * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "opentx.h" #include #define CLI_COMMAND_MAX_ARGS 8 #define CLI_COMMAND_MAX_LEN 256 OS_TID cliTaskId; TaskStack cliStack; Fifo<256> cliRxFifo; uint8_t cliTracesEnabled = true; // char cliLastLine[CLI_COMMAND_MAX_LEN+1]; typedef int (* CliFunction) (const char ** args); int cliExecLine(char * line); int cliExecCommand(const char ** argv); int cliHelp(const char ** argv); struct CliCommand { const char * name; CliFunction func; const char * args; }; struct MemArea { const char * name; void * start; int size; }; void cliPrompt() { serialPutc('>'); } int toLongLongInt(const char ** argv, int index, long long int * val) { if (*argv[index] == '\0') { return 0; } else { int base = 10; const char * s = argv[index]; if (strlen(s) > 2 && s[0] == '0' && s[1] == 'x') { base = 16; s = &argv[index][2]; } char * endptr = NULL; *val = strtoll(s, &endptr, base); if (*endptr == '\0') return 1; else { serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[index]); return -1; } } } int toInt(const char ** argv, int index, int * val) { long long int lval = 0; int result = toLongLongInt(argv, index, &lval); *val = (int)lval; return result; } int cliBeep(const char ** argv) { int freq = BEEP_DEFAULT_FREQ; int duration = 100; if (toInt(argv, 1, &freq) >= 0 && toInt(argv, 2, &duration) >= 0) { audioQueue.playTone(freq, duration, 20, PLAY_NOW); } return 0; } int cliPlay(const char ** argv) { audioQueue.playFile(argv[1], PLAY_NOW); return 0; } int cliLs(const char ** argv) { FILINFO fno; DIR dir; char *fn; /* This function is assuming non-Unicode cfg. */ #if _USE_LFN TCHAR lfn[_MAX_LFN + 1]; fno.lfname = lfn; fno.lfsize = sizeof(lfn); #endif FRESULT res = f_opendir(&dir, argv[1]); /* Open the directory */ if (res == FR_OK) { for (;;) { res = f_readdir(&dir, &fno); /* Read a directory item */ if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ #if _USE_LFN fn = *fno.lfname ? fno.lfname : fno.fname; #else fn = fno.fname; #endif serialPrint(fn); } } else { serialPrint("%s: Invalid directory \"%s\"", argv[0], argv[1]); } return 0; } int cliTrace(const char ** argv) { if (!strcmp(argv[1], "on")) { cliTracesEnabled = true; } else if (!strcmp(argv[1], "off")) { cliTracesEnabled = false; } else { serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[1]); } return 0; } int cliStackInfo(const char ** argv) { serialPrint("[MAIN] %d available / %d", stackAvailable(), stackSize()); serialPrint("[MENUS] %d available / %d", menusStack.available(), menusStack.size()); serialPrint("[MIXER] %d available / %d", mixerStack.available(), mixerStack.size()); serialPrint("[AUDIO] %d available / %d", audioStack.available(), audioStack.size()); serialPrint("[CLI] %d available / %d", cliStack.available(), cliStack.size()); return 0; } #if defined(PCBFLAMENCO) int cliReadBQ24195(const char ** argv) { int index = 0; if (toInt(argv, 1, &index) > 0) { serialPrint("BQ24195[%d] = 0x%02x", index, i2cReadBQ24195(index)); } else { serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1]); } return 0; } int cliWriteBQ24195(const char ** argv) { int index = 0; int data = 0; if (toInt(argv, 1, &index) > 0 && toInt(argv, 2, &data) > 0) { i2cWriteBQ24195(index, data); } else { serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1], argv[2]); } return 0; } #endif const MemArea memAreas[] = { { "RCC", RCC, sizeof(RCC_TypeDef) }, { "GPIOA", GPIOA, sizeof(GPIO_TypeDef) }, { "GPIOB", GPIOB, sizeof(GPIO_TypeDef) }, { "GPIOC", GPIOC, sizeof(GPIO_TypeDef) }, { "GPIOD", GPIOD, sizeof(GPIO_TypeDef) }, { "GPIOE", GPIOE, sizeof(GPIO_TypeDef) }, { "GPIOF", GPIOF, sizeof(GPIO_TypeDef) }, { "GPIOG", GPIOG, sizeof(GPIO_TypeDef) }, { "USART1", USART1, sizeof(USART_TypeDef) }, { "USART2", USART2, sizeof(USART_TypeDef) }, { "USART3", USART3, sizeof(USART_TypeDef) }, { NULL, NULL, 0 }, }; int cliSet(const char ** argv) { if (!strcmp(argv[1], "rtc")) { struct gtm t; int year, month, day, hour, minute, second; if (toInt(argv, 2, &year) > 0 && toInt(argv, 3, &month) > 0 && toInt(argv, 4, &day) > 0 && toInt(argv, 5, &hour) > 0 && toInt(argv, 6, &minute) > 0 && toInt(argv, 7, &second) > 0) { t.tm_year = year-1900; t.tm_mon = month-1; t.tm_mday = day; t.tm_hour = hour; t.tm_min = minute; t.tm_sec = second; g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated rtcSetTime(&t); } else { serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1], argv[2]); } } else if (!strcmp(argv[1], "volume")) { int level = 0; if (toInt(argv, 2, &level) > 0) { setVolume(level); } else { serialPrint("%s: Invalid argument \"%s\" \"%s\"", argv[0], argv[1], argv[2]); } return 0; } return 0; } int cliDisplay(const char ** argv) { long long int address = 0; for (const MemArea * area = memAreas; area->name != NULL; area++) { if (!strcmp(area->name, argv[1])) { dump((uint8_t *)area->start, area->size); return 0; } } if (!strcmp(argv[1], "keys")) { for (int i=0; i VSysMin"); serialPrint(reg & 0x02 ? "Thermal sensor bad" : "Thermal sensor ok"); serialPrint(reg & 0x04 ? "Power ok" : "Power bad"); serialPrint(reg & 0x08 ? "Connected to charger" : "Not connected to charger"); const char * CHARGE_STATUS[] = { "Not Charging", "Precharge", "Fast Charging", "Charge done" }; serialPrint(CHARGE_STATUS[(reg & 0x30) >> 4]); const char * INPUT_STATUS[] = { "Unknown input", "USB host input", "USB adapter port input", "OTG input" }; serialPrint(INPUT_STATUS[(reg & 0xC0) >> 6]); } { uint8_t reg = i2cReadBQ24195(0x09); if (reg & 0x80) serialPrint("Watchdog timer expiration"); uint8_t chargerFault = (reg & 0x30) >> 4; if (chargerFault == 0x01) serialPrint("Input fault"); else if (chargerFault == 0x02) serialPrint("Thermal shutdown"); else if (chargerFault == 0x03) serialPrint("Charge safety timer expiration"); if (reg & 0x08) serialPrint("Battery over voltage fault"); uint8_t ntcFault = (reg & 0x07); if (ntcFault == 0x05) serialPrint("NTC cold"); else if (ntcFault == 0x06) serialPrint("NTC hot"); } } #endif else if (toLongLongInt(argv, 1, &address) > 0) { int size = 256; if (toInt(argv, 2, &size) >= 0) { dump((uint8_t *)address, size); } } return 0; } int cliDebugVars(const char ** argv) { #if defined(PCBHORUS) && !defined(SIMU) extern unsigned int ioMutexReq, ioMutexRel; extern unsigned int sdReadRetries; serialPrint("ioMutexReq=%d", ioMutexReq); serialPrint("ioMutexRel=%d", ioMutexRel); serialPrint("sdReadRetries=%d", sdReadRetries); #endif return 0; } int cliRepeat(const char ** argv) { int interval = 0; int counter = 0; if (toInt(argv, 1, &interval) > 0 && argv[2]) { interval *= 50; counter = interval; uint8_t c; while (!cliRxFifo.pop(c) || !(c == '\r' || c == '\n' || c == ' ')) { CoTickDelay(10); // 20ms if (++counter >= interval) { cliExecCommand(&argv[2]); counter = 0; } } } else { serialPrint("%s: Invalid arguments", argv[0]); } return 0; } const CliCommand cliCommands[] = { { "beep", cliBeep, "[] []" }, { "ls", cliLs, "" }, { "play", cliPlay, "" }, { "print", cliDisplay, "
[] | " }, { "set", cliSet, " " }, { "stackinfo", cliStackInfo, "" }, { "trace", cliTrace, "on | off" }, #if defined(PCBFLAMENCO) { "read_bq24195", cliReadBQ24195, "" }, { "write_bq24195", cliWriteBQ24195, " " }, #endif { "help", cliHelp, "[]" }, { "debugvars", cliDebugVars, "" }, { "repeat", cliRepeat, " " }, { NULL, NULL, NULL } /* sentinel */ }; int cliHelp(const char ** argv) { for (const CliCommand * command = cliCommands; command->name != NULL; command++) { if (argv[1][0] == '\0' || !strcmp(command->name, argv[0])) { serialPrint("%s %s", command->name, command->args); if (argv[1][0] != '\0') { return 0; } } } if (argv[1][0] != '\0') { serialPrint("Invalid command \"%s\"", argv[0]); } return -1; } int cliExecCommand(const char ** argv) { if (argv[0][0] == '\0') return 0; for (const CliCommand * command = cliCommands; command->name != NULL; command++) { if (!strcmp(command->name, argv[0])) { return command->func(argv); } } serialPrint("Invalid command \"%s\"", argv[0]); return -1; } int cliExecLine(char * line) { int len = strlen(line); const char * argv[CLI_COMMAND_MAX_ARGS]; memset(argv, 0, sizeof(argv)); int argc = 1; argv[0] = line; for (int i=0; i