Initial console implementation.

This commit is contained in:
Joe Kearney 2026-01-08 18:53:29 -06:00
parent c8f6bf24fc
commit a25627232c
6 changed files with 371 additions and 7 deletions

View file

@ -0,0 +1,13 @@
idf_component_register(
SRCS
"Console.c"
INCLUDE_DIRS
"."
REQUIRES
"SystemK"
"System_Events"
"console"
"log"
"nvs_flash"
"vfs"
)

View file

@ -0,0 +1,325 @@
#include "esp_console.h"
#include "esp_log.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "linenoise/linenoise.h"
#include "esp_vfs_dev.h"
#include <string.h>
#include <stdio.h>
#define LOG_NVS_NS "logcfg"
#define MAX_TAGS 16
#define MAX_TAGLEN 16
typedef struct
{
char tag[MAX_TAGLEN];
uint8_t level;
} tag_entry_t;
static esp_log_level_t Saved_Global_Level = ESP_LOG_INFO;
static tag_entry_t Tag_Table[MAX_TAGS];
static size_t Tag_Count = 0;
/* ---------------- Utility ---------------- */
static bool parse_level(const char *s, esp_log_level_t *out)
{
if (!strcmp(s, "none"))
*out = ESP_LOG_NONE;
else if (!strcmp(s, "error"))
*out = ESP_LOG_ERROR;
else if (!strcmp(s, "warn"))
*out = ESP_LOG_WARN;
else if (!strcmp(s, "info"))
*out = ESP_LOG_INFO;
else if (!strcmp(s, "debug"))
*out = ESP_LOG_DEBUG;
else if (!strcmp(s, "verbose"))
*out = ESP_LOG_VERBOSE;
else
return false;
return true;
}
static const char *level_str(esp_log_level_t lvl)
{
switch (lvl)
{
case ESP_LOG_NONE:
return "none";
case ESP_LOG_ERROR:
return "error";
case ESP_LOG_WARN:
return "warn";
case ESP_LOG_INFO:
return "info";
case ESP_LOG_DEBUG:
return "debug";
case ESP_LOG_VERBOSE:
return "verbose";
default:
return "?";
}
}
/* ---------------- NVS ---------------- */
static void nvs_save(void)
{
nvs_handle_t h;
esp_err_t err = nvs_open(LOG_NVS_NS, NVS_READWRITE, &h);
if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
ESP_LOGW("Console", "NVS not initialized, cannot save");
return;
}
else if (err != ESP_OK) {
ESP_LOGE("Console", "Failed to open NVS: %s", esp_err_to_name(err));
return;
}
uint8_t lvl = esp_log_get_default_level();
nvs_set_u8(h, "global", lvl);
nvs_set_u8(h, "tagcnt", Tag_Count);
nvs_set_blob(h, "tags", Tag_Table,
Tag_Count * sizeof(tag_entry_t));
nvs_commit(h);
nvs_close(h);
}
static void log_config_load(void)
{
nvs_handle_t h;
esp_err_t err = nvs_open(LOG_NVS_NS, NVS_READONLY, &h);
if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
ESP_LOGW("Console", "NVS not initialized, cannot load");
return;
}
else if (err != ESP_OK) {
ESP_LOGE("Console", "Failed to open NVS: %s", esp_err_to_name(err));
return;
}
uint8_t lvl;
if (nvs_get_u8(h, "global", &lvl) == ESP_OK)
{
esp_log_level_set("*", lvl);
Saved_Global_Level = lvl;
}
uint8_t cnt = 0;
if (nvs_get_u8(h, "tagcnt", &cnt) == ESP_OK && cnt <= MAX_TAGS)
{
size_t sz = cnt * sizeof(tag_entry_t);
if (nvs_get_blob(h, "tags", Tag_Table, &sz) == ESP_OK)
{
Tag_Count = cnt;
for (size_t i = 0; i < Tag_Count; i++)
{
esp_log_level_set(Tag_Table[i].tag,
Tag_Table[i].level);
}
}
}
nvs_close(h);
}
static void nvs_clear_tags(void)
{
nvs_handle_t h;
esp_err_t err = nvs_open(LOG_NVS_NS, NVS_READWRITE, &h);
if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
ESP_LOGW("Console", "NVS not initialized, cannot clear tags");
return;
}
else if (err != ESP_OK) {
ESP_LOGE("Console", "Failed to open NVS: %s", esp_err_to_name(err));
return;
}
nvs_erase_key(h, "tagcnt");
nvs_erase_key(h, "tags");
nvs_commit(h);
nvs_close(h);
}
static void clear_tag_levels(void)
{
esp_log_level_t default_level = esp_log_get_default_level();
for (size_t i = 0; i < Tag_Count; i++)
{
esp_log_level_set(Tag_Table[i].tag, default_level);
}
Tag_Count = 0;
}
/* ---------------- Tag table ---------------- */
static void set_tag_level(const char *tag, esp_log_level_t level)
{
for (size_t i = 0; i < Tag_Count; i++)
{
if (!strcmp(Tag_Table[i].tag, tag))
{
Tag_Table[i].level = level;
return;
}
}
if (Tag_Count < MAX_TAGS)
{
strncpy(Tag_Table[Tag_Count].tag, tag, MAX_TAGLEN - 1);
Tag_Table[Tag_Count].tag[MAX_TAGLEN - 1] = 0;
Tag_Table[Tag_Count].level = level;
Tag_Count++;
}
}
/* ---------------- Console command ---------------- */
static void print_usage(void)
{
printf(
"Usage:\n"
" log show\n"
" log clear\n"
" log off | on\n"
" log <level>\n"
" log tag <tag|*> <level|off>\n"
"Levels: none error warn info debug verbose\n");
}
static int cmd_log(int argc, char **argv)
{
esp_log_level_t level;
if (argc < 2)
{
print_usage();
return 0;
}
if (!strcmp(argv[1], "show"))
{
printf("Global level: %s\n",
level_str(esp_log_get_default_level()));
if (Tag_Count == 0)
{
printf("No tag overrides\n");
}
else
{
printf("Tag overrides:\n");
for (size_t i = 0; i < Tag_Count; i++)
{
printf(" %-10s : %s\n",
Tag_Table[i].tag,
level_str(Tag_Table[i].level));
}
}
return 0;
}
if (!strcmp(argv[1], "clear"))
{
clear_tag_levels();
nvs_clear_tags();
printf("All tag overrides cleared\n");
return 0;
}
if (!strcmp(argv[1], "off"))
{
Saved_Global_Level = esp_log_get_default_level();
esp_log_level_set("*", ESP_LOG_NONE);
nvs_save();
printf("Global logging disabled\n");
return 0;
}
if (!strcmp(argv[1], "on"))
{
esp_log_level_set("*", Saved_Global_Level);
printf("Global logging restored\n");
return 0;
}
if (!strcmp(argv[1], "tag"))
{
if (argc != 4)
{
print_usage();
return 0;
}
const char *tag = argv[2];
const char *lvl = argv[3];
if (!strcmp(lvl, "off"))
{
level = ESP_LOG_NONE;
}
else if (!parse_level(lvl, &level))
{
printf("Invalid level\n");
return 0;
}
esp_log_level_set(tag, level);
set_tag_level(tag, level);
nvs_save();
printf("Tag '%s' set to %s\n", tag, lvl);
return 0;
}
if (!parse_level(argv[1], &level))
{
print_usage();
return 0;
}
esp_log_level_set("*", level);
nvs_save();
printf("Global level set to %s\n", argv[1]);
return 0;
}
/* ---------------- Registration ---------------- */
static void register_log_command(void)
{
const esp_console_cmd_t cmd = {
.command = "log",
.help = "Control logging levels",
.func = &cmd_log,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
void Initialize_Console(void)
{
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
repl_config.prompt = "KTag>";
repl_config.max_cmdline_length = 1024;
log_config_load();
register_log_command();
esp_console_register_help_command();
esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
ESP_ERROR_CHECK(esp_console_start_repl(repl));
}

View file

@ -0,0 +1,23 @@
/*
* This program source code file is part of the KTag project, a DIY laser tag
* game with customizable features and wide interoperability.
*
* 🛡 <https://ktag.clubk.club> 🃞
*
* Copyright © 2026 Joseph P. Kearney and the KTag developers.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* 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 Affero General Public License for more
* details.
*
* There should be a copy of the GNU Affero General Public License in the LICENSE
* file in the root of this repository. If not, see <http://www.gnu.org/licenses/>.
*/
void Initialize_Console(void);

@ -1 +1 @@
Subproject commit f80cb59828aca6f784adcc898aa2603303c5cf2f Subproject commit 3b2718e6f41070de1db08f9d173de94e90cc372a

View file

@ -48,6 +48,7 @@
#include <BLE.h> #include <BLE.h>
#include <IR.h> #include <IR.h>
#include <WiFi.h> #include <WiFi.h>
#include <Console.h>
#include "nvs_flash.h" #include "nvs_flash.h"
#include "HW_NeoPixels.h" #include "HW_NeoPixels.h"
#include "Version.h" #include "Version.h"
@ -82,6 +83,8 @@ void app_main(void)
KLOG_ERROR(TAG, "Error initializing NVS: %s", esp_err_to_name(ret)); KLOG_ERROR(TAG, "Error initializing NVS: %s", esp_err_to_name(ret));
} }
Initialize_Console();
Initialize_SPIFFS(); Initialize_SPIFFS();
Wait_For_System_Event(SYS_SPIFFS_READY, "Timeout initializing SPIFFS!", INITIALIZATION_TIMEOUT_IN_ms); Wait_For_System_Event(SYS_SPIFFS_READY, "Timeout initializing SPIFFS!", INITIALIZATION_TIMEOUT_IN_ms);

View file

@ -390,7 +390,7 @@ CONFIG_IDF_TOOLCHAIN_GCC=y
CONFIG_IDF_TARGET_ARCH_XTENSA=y CONFIG_IDF_TARGET_ARCH_XTENSA=y
CONFIG_IDF_TARGET_ARCH="xtensa" CONFIG_IDF_TARGET_ARCH="xtensa"
CONFIG_IDF_TARGET="esp32s3" CONFIG_IDF_TARGET="esp32s3"
CONFIG_IDF_INIT_VERSION="5.5.1" CONFIG_IDF_INIT_VERSION="5.5.2"
CONFIG_IDF_TARGET_ESP32S3=y CONFIG_IDF_TARGET_ESP32S3=y
CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009 CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009
@ -998,7 +998,7 @@ CONFIG_BT_ALARM_MAX_NUM=50
# #
# Console Library # Console Library
# #
# CONFIG_CONSOLE_SORTED_HELP is not set CONFIG_CONSOLE_SORTED_HELP=y
# end of Console Library # end of Console Library
# #
@ -1957,14 +1957,14 @@ CONFIG_LOG_DEFAULT_LEVEL_INFO=y
# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set
CONFIG_LOG_DEFAULT_LEVEL=3 CONFIG_LOG_DEFAULT_LEVEL=3
# CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT is not set # CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT is not set
CONFIG_LOG_MAXIMUM_LEVEL_DEBUG=y # CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set
# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE=y
CONFIG_LOG_MAXIMUM_LEVEL=4 CONFIG_LOG_MAXIMUM_LEVEL=5
# #
# Level Settings # Level Settings
# #
# CONFIG_LOG_MASTER_LEVEL is not set CONFIG_LOG_MASTER_LEVEL=y
CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y
# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set # CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set
# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set # CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set