feat: restore NVS runtime config with CLI commands (NVS > build-time priority)
Bring back two-layer configuration: build-time defaults from mimi_secrets.h with runtime NVS overrides via serial CLI. NVS values take highest priority so a pre-flashed board can be reconfigured anywhere with just a USB cable. Restored CLI commands: wifi_set, set_tg_token, set_api_key, set_model, set_proxy, clear_proxy, set_search_key. Added new commands: config_show (displays all config with sensitive fields masked), config_reset (clears all NVS overrides), and help (lists all commands). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
20
README.md
20
README.md
@@ -71,7 +71,7 @@ idf.py set-target esp32s3
|
|||||||
|
|
||||||
### Configure
|
### Configure
|
||||||
|
|
||||||
All configuration is done through `mimi_secrets.h` at build time:
|
MimiClaw uses a **two-layer config** system: build-time defaults in `mimi_secrets.h`, with runtime overrides via the serial CLI. CLI values are stored in NVS flash and take priority over build-time values.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp main/mimi_secrets.h.example main/mimi_secrets.h
|
cp main/mimi_secrets.h.example main/mimi_secrets.h
|
||||||
@@ -106,7 +106,23 @@ idf.py -p PORT flash monitor
|
|||||||
|
|
||||||
### CLI Commands
|
### CLI Commands
|
||||||
|
|
||||||
The serial CLI provides debug and maintenance commands:
|
Connect via serial to configure or debug. **Config commands** let you change settings without recompiling — just plug in a USB cable anywhere.
|
||||||
|
|
||||||
|
**Runtime config** (saved to NVS, overrides build-time defaults):
|
||||||
|
|
||||||
|
```
|
||||||
|
mimi> wifi_set MySSID MyPassword # change WiFi network
|
||||||
|
mimi> set_tg_token 123456:ABC... # change Telegram bot token
|
||||||
|
mimi> set_api_key sk-ant-api03-... # change Anthropic API key
|
||||||
|
mimi> set_model claude-sonnet-4-5 # change LLM model
|
||||||
|
mimi> set_proxy 127.0.0.1 7897 # set HTTP proxy
|
||||||
|
mimi> clear_proxy # remove proxy
|
||||||
|
mimi> set_search_key BSA... # set Brave Search API key
|
||||||
|
mimi> config_show # show all config (masked)
|
||||||
|
mimi> config_reset # clear NVS, revert to build-time defaults
|
||||||
|
```
|
||||||
|
|
||||||
|
**Debug & maintenance:**
|
||||||
|
|
||||||
```
|
```
|
||||||
mimi> wifi_status # am I connected?
|
mimi> wifi_status # am I connected?
|
||||||
|
|||||||
27
README_CN.md
27
README_CN.md
@@ -71,7 +71,7 @@ idf.py set-target esp32s3
|
|||||||
|
|
||||||
### 配置
|
### 配置
|
||||||
|
|
||||||
所有配置通过 `mimi_secrets.h` 在编译时写入:
|
MimiClaw 使用**两层配置**:`mimi_secrets.h` 提供编译时默认值,串口 CLI 可在运行时覆盖。CLI 设置的值存在 NVS Flash 中,优先级高于编译时值。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp main/mimi_secrets.h.example main/mimi_secrets.h
|
cp main/mimi_secrets.h.example main/mimi_secrets.h
|
||||||
@@ -110,13 +110,34 @@ idf.py -p PORT flash monitor
|
|||||||
|
|
||||||
**前提**:局域网内有一个支持 HTTP CONNECT 的代理(Clash Verge、V2Ray 等),并开启了「允许局域网连接」。
|
**前提**:局域网内有一个支持 HTTP CONNECT 的代理(Clash Verge、V2Ray 等),并开启了「允许局域网连接」。
|
||||||
|
|
||||||
在 `mimi_secrets.h` 中设置 `MIMI_SECRET_PROXY_HOST` 和 `MIMI_SECRET_PROXY_PORT`。清除代理只需把这两项改回空字符串 `""`,然后重新编译。
|
可以在 `mimi_secrets.h` 中编译时设置,也可以通过串口 CLI 随时修改:
|
||||||
|
|
||||||
|
```
|
||||||
|
mimi> set_proxy 192.168.1.83 7897 # 设置代理
|
||||||
|
mimi> clear_proxy # 清除代理
|
||||||
|
```
|
||||||
|
|
||||||
> **提示**:确保 ESP32-S3 和代理机器在同一局域网。Clash Verge 在「设置 → 允许局域网」中开启。
|
> **提示**:确保 ESP32-S3 和代理机器在同一局域网。Clash Verge 在「设置 → 允许局域网」中开启。
|
||||||
|
|
||||||
### CLI 命令
|
### CLI 命令
|
||||||
|
|
||||||
串口 CLI 提供调试和运维命令:
|
通过串口连接即可配置和调试。**配置命令**让你无需重新编译就能修改设置 — 随时随地插上 USB 线就能改。
|
||||||
|
|
||||||
|
**运行时配置**(存入 NVS,覆盖编译时默认值):
|
||||||
|
|
||||||
|
```
|
||||||
|
mimi> wifi_set MySSID MyPassword # 换 WiFi
|
||||||
|
mimi> set_tg_token 123456:ABC... # 换 Telegram Bot Token
|
||||||
|
mimi> set_api_key sk-ant-api03-... # 换 Anthropic API Key
|
||||||
|
mimi> set_model claude-sonnet-4-5-20250929 # 换模型
|
||||||
|
mimi> set_proxy 192.168.1.83 7897 # 设置代理
|
||||||
|
mimi> clear_proxy # 清除代理
|
||||||
|
mimi> set_search_key BSA... # 设置 Brave Search API Key
|
||||||
|
mimi> config_show # 查看所有配置(脱敏显示)
|
||||||
|
mimi> config_reset # 清除 NVS,恢复编译时默认值
|
||||||
|
```
|
||||||
|
|
||||||
|
**调试与运维:**
|
||||||
|
|
||||||
```
|
```
|
||||||
mimi> wifi_status # 连上了吗?
|
mimi> wifi_status # 连上了吗?
|
||||||
|
|||||||
@@ -104,9 +104,9 @@
|
|||||||
- **MimiClaw**: Not implemented
|
- **MimiClaw**: Not implemented
|
||||||
- **Recommendation**: Requires extra HTTPS request to Whisper API: download Telegram voice -> forward -> get text
|
- **Recommendation**: Requires extra HTTPS request to Whisper API: download Telegram voice -> forward -> get text
|
||||||
|
|
||||||
### [x] ~~Build-time Config File~~
|
### [x] ~~Build-time Config File + Runtime NVS Override~~
|
||||||
- Implemented: `mimi_secrets.h` — sole configuration method (build-time only, no NVS/CLI)
|
- Implemented: `mimi_secrets.h` as build-time defaults, NVS as runtime override via CLI
|
||||||
- Replaces need for YAML config; suitable for MCU workflow
|
- Two-layer config: build-time secrets → NVS fallback, CLI commands to set/show/reset
|
||||||
|
|
||||||
### [ ] WebSocket Gateway Protocol Enhancement
|
### [ ] WebSocket Gateway Protocol Enhancement
|
||||||
- **nanobot**: Gateway port 18790 + richer protocol
|
- **nanobot**: Gateway port 18790 + richer protocol
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
- [x] OTA Update
|
- [x] OTA Update
|
||||||
- [x] WiFi Manager (build-time credentials, exponential backoff)
|
- [x] WiFi Manager (build-time credentials, exponential backoff)
|
||||||
- [x] SPIFFS storage
|
- [x] SPIFFS storage
|
||||||
- [x] Build-time config (`mimi_secrets.h`, sole configuration method)
|
- [x] Build-time config (`mimi_secrets.h`) + runtime NVS override via CLI
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
#include "serial_cli.h"
|
#include "serial_cli.h"
|
||||||
#include "mimi_config.h"
|
#include "mimi_config.h"
|
||||||
#include "wifi/wifi_manager.h"
|
#include "wifi/wifi_manager.h"
|
||||||
|
#include "telegram/telegram_bot.h"
|
||||||
|
#include "llm/llm_proxy.h"
|
||||||
#include "memory/memory_store.h"
|
#include "memory/memory_store.h"
|
||||||
#include "memory/session_mgr.h"
|
#include "memory/session_mgr.h"
|
||||||
|
#include "proxy/http_proxy.h"
|
||||||
|
#include "tools/tool_web_search.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -10,10 +14,32 @@
|
|||||||
#include "esp_console.h"
|
#include "esp_console.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "nvs.h"
|
||||||
#include "argtable3/argtable3.h"
|
#include "argtable3/argtable3.h"
|
||||||
|
|
||||||
static const char *TAG = "cli";
|
static const char *TAG = "cli";
|
||||||
|
|
||||||
|
/* --- wifi_set command --- */
|
||||||
|
static struct {
|
||||||
|
struct arg_str *ssid;
|
||||||
|
struct arg_str *password;
|
||||||
|
struct arg_end *end;
|
||||||
|
} wifi_set_args;
|
||||||
|
|
||||||
|
static int cmd_wifi_set(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int nerrors = arg_parse(argc, argv, (void **)&wifi_set_args);
|
||||||
|
if (nerrors != 0) {
|
||||||
|
arg_print_errors(stderr, wifi_set_args.end, argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
wifi_manager_set_credentials(wifi_set_args.ssid->sval[0],
|
||||||
|
wifi_set_args.password->sval[0]);
|
||||||
|
printf("WiFi credentials saved. Restart to apply.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- wifi_status command --- */
|
/* --- wifi_status command --- */
|
||||||
static int cmd_wifi_status(int argc, char **argv)
|
static int cmd_wifi_status(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -22,6 +48,60 @@ static int cmd_wifi_status(int argc, char **argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- set_tg_token command --- */
|
||||||
|
static struct {
|
||||||
|
struct arg_str *token;
|
||||||
|
struct arg_end *end;
|
||||||
|
} tg_token_args;
|
||||||
|
|
||||||
|
static int cmd_set_tg_token(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int nerrors = arg_parse(argc, argv, (void **)&tg_token_args);
|
||||||
|
if (nerrors != 0) {
|
||||||
|
arg_print_errors(stderr, tg_token_args.end, argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
telegram_set_token(tg_token_args.token->sval[0]);
|
||||||
|
printf("Telegram bot token saved.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- set_api_key command --- */
|
||||||
|
static struct {
|
||||||
|
struct arg_str *key;
|
||||||
|
struct arg_end *end;
|
||||||
|
} api_key_args;
|
||||||
|
|
||||||
|
static int cmd_set_api_key(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int nerrors = arg_parse(argc, argv, (void **)&api_key_args);
|
||||||
|
if (nerrors != 0) {
|
||||||
|
arg_print_errors(stderr, api_key_args.end, argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
llm_set_api_key(api_key_args.key->sval[0]);
|
||||||
|
printf("API key saved.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- set_model command --- */
|
||||||
|
static struct {
|
||||||
|
struct arg_str *model;
|
||||||
|
struct arg_end *end;
|
||||||
|
} model_args;
|
||||||
|
|
||||||
|
static int cmd_set_model(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int nerrors = arg_parse(argc, argv, (void **)&model_args);
|
||||||
|
if (nerrors != 0) {
|
||||||
|
arg_print_errors(stderr, model_args.end, argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
llm_set_model(model_args.model->sval[0]);
|
||||||
|
printf("Model set.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- memory_read command --- */
|
/* --- memory_read command --- */
|
||||||
static int cmd_memory_read(int argc, char **argv)
|
static int cmd_memory_read(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -98,6 +178,116 @@ static int cmd_heap_info(int argc, char **argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- set_proxy command --- */
|
||||||
|
static struct {
|
||||||
|
struct arg_str *host;
|
||||||
|
struct arg_int *port;
|
||||||
|
struct arg_end *end;
|
||||||
|
} proxy_args;
|
||||||
|
|
||||||
|
static int cmd_set_proxy(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int nerrors = arg_parse(argc, argv, (void **)&proxy_args);
|
||||||
|
if (nerrors != 0) {
|
||||||
|
arg_print_errors(stderr, proxy_args.end, argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
http_proxy_set(proxy_args.host->sval[0], (uint16_t)proxy_args.port->ival[0]);
|
||||||
|
printf("Proxy set. Restart to apply.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- clear_proxy command --- */
|
||||||
|
static int cmd_clear_proxy(int argc, char **argv)
|
||||||
|
{
|
||||||
|
http_proxy_clear();
|
||||||
|
printf("Proxy cleared. Restart to apply.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- set_search_key command --- */
|
||||||
|
static struct {
|
||||||
|
struct arg_str *key;
|
||||||
|
struct arg_end *end;
|
||||||
|
} search_key_args;
|
||||||
|
|
||||||
|
static int cmd_set_search_key(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int nerrors = arg_parse(argc, argv, (void **)&search_key_args);
|
||||||
|
if (nerrors != 0) {
|
||||||
|
arg_print_errors(stderr, search_key_args.end, argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
tool_web_search_set_key(search_key_args.key->sval[0]);
|
||||||
|
printf("Search API key saved.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- config_show command --- */
|
||||||
|
static void print_config(const char *label, const char *ns, const char *key,
|
||||||
|
const char *build_val, bool mask)
|
||||||
|
{
|
||||||
|
char nvs_val[128] = {0};
|
||||||
|
const char *source = "not set";
|
||||||
|
const char *display = "(empty)";
|
||||||
|
|
||||||
|
/* NVS takes highest priority */
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
if (nvs_open(ns, NVS_READONLY, &nvs) == ESP_OK) {
|
||||||
|
size_t len = sizeof(nvs_val);
|
||||||
|
if (nvs_get_str(nvs, key, nvs_val, &len) == ESP_OK && nvs_val[0]) {
|
||||||
|
source = "NVS";
|
||||||
|
display = nvs_val;
|
||||||
|
}
|
||||||
|
nvs_close(nvs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fall back to build-time value */
|
||||||
|
if (strcmp(source, "not set") == 0 && build_val[0] != '\0') {
|
||||||
|
source = "build";
|
||||||
|
display = build_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask && strlen(display) > 6 && strcmp(display, "(empty)") != 0) {
|
||||||
|
printf(" %-14s: %.4s**** [%s]\n", label, display, source);
|
||||||
|
} else {
|
||||||
|
printf(" %-14s: %s [%s]\n", label, display, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_config_show(int argc, char **argv)
|
||||||
|
{
|
||||||
|
printf("=== Current Configuration ===\n");
|
||||||
|
print_config("WiFi SSID", MIMI_NVS_WIFI, MIMI_NVS_KEY_SSID, MIMI_SECRET_WIFI_SSID, false);
|
||||||
|
print_config("WiFi Pass", MIMI_NVS_WIFI, MIMI_NVS_KEY_PASS, MIMI_SECRET_WIFI_PASS, true);
|
||||||
|
print_config("TG Token", MIMI_NVS_TG, MIMI_NVS_KEY_TG_TOKEN, MIMI_SECRET_TG_TOKEN, true);
|
||||||
|
print_config("API Key", MIMI_NVS_LLM, MIMI_NVS_KEY_API_KEY, MIMI_SECRET_API_KEY, true);
|
||||||
|
print_config("Model", MIMI_NVS_LLM, MIMI_NVS_KEY_MODEL, MIMI_SECRET_MODEL, false);
|
||||||
|
print_config("Proxy Host", MIMI_NVS_PROXY, MIMI_NVS_KEY_PROXY_HOST, MIMI_SECRET_PROXY_HOST, false);
|
||||||
|
print_config("Proxy Port", MIMI_NVS_PROXY, MIMI_NVS_KEY_PROXY_PORT, MIMI_SECRET_PROXY_PORT, false);
|
||||||
|
print_config("Search Key", MIMI_NVS_SEARCH, MIMI_NVS_KEY_API_KEY, MIMI_SECRET_SEARCH_KEY, true);
|
||||||
|
printf("=============================\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- config_reset command --- */
|
||||||
|
static int cmd_config_reset(int argc, char **argv)
|
||||||
|
{
|
||||||
|
const char *namespaces[] = {
|
||||||
|
MIMI_NVS_WIFI, MIMI_NVS_TG, MIMI_NVS_LLM, MIMI_NVS_PROXY, MIMI_NVS_SEARCH
|
||||||
|
};
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
if (nvs_open(namespaces[i], NVS_READWRITE, &nvs) == ESP_OK) {
|
||||||
|
nvs_erase_all(nvs);
|
||||||
|
nvs_commit(nvs);
|
||||||
|
nvs_close(nvs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("All NVS config cleared. Build-time defaults will be used on restart.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- restart command --- */
|
/* --- restart command --- */
|
||||||
static int cmd_restart(int argc, char **argv)
|
static int cmd_restart(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -120,6 +310,19 @@ esp_err_t serial_cli_init(void)
|
|||||||
ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&hw_config, &repl_config, &repl));
|
ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&hw_config, &repl_config, &repl));
|
||||||
|
|
||||||
/* Register commands */
|
/* Register commands */
|
||||||
|
esp_console_register_help_command();
|
||||||
|
|
||||||
|
/* wifi_set */
|
||||||
|
wifi_set_args.ssid = arg_str1(NULL, NULL, "<ssid>", "WiFi SSID");
|
||||||
|
wifi_set_args.password = arg_str1(NULL, NULL, "<password>", "WiFi password");
|
||||||
|
wifi_set_args.end = arg_end(2);
|
||||||
|
esp_console_cmd_t wifi_set_cmd = {
|
||||||
|
.command = "wifi_set",
|
||||||
|
.help = "Set WiFi SSID and password",
|
||||||
|
.func = &cmd_wifi_set,
|
||||||
|
.argtable = &wifi_set_args,
|
||||||
|
};
|
||||||
|
esp_console_cmd_register(&wifi_set_cmd);
|
||||||
|
|
||||||
/* wifi_status */
|
/* wifi_status */
|
||||||
esp_console_cmd_t wifi_status_cmd = {
|
esp_console_cmd_t wifi_status_cmd = {
|
||||||
@@ -129,6 +332,39 @@ esp_err_t serial_cli_init(void)
|
|||||||
};
|
};
|
||||||
esp_console_cmd_register(&wifi_status_cmd);
|
esp_console_cmd_register(&wifi_status_cmd);
|
||||||
|
|
||||||
|
/* set_tg_token */
|
||||||
|
tg_token_args.token = arg_str1(NULL, NULL, "<token>", "Telegram bot token");
|
||||||
|
tg_token_args.end = arg_end(1);
|
||||||
|
esp_console_cmd_t tg_token_cmd = {
|
||||||
|
.command = "set_tg_token",
|
||||||
|
.help = "Set Telegram bot token",
|
||||||
|
.func = &cmd_set_tg_token,
|
||||||
|
.argtable = &tg_token_args,
|
||||||
|
};
|
||||||
|
esp_console_cmd_register(&tg_token_cmd);
|
||||||
|
|
||||||
|
/* set_api_key */
|
||||||
|
api_key_args.key = arg_str1(NULL, NULL, "<key>", "Anthropic API key");
|
||||||
|
api_key_args.end = arg_end(1);
|
||||||
|
esp_console_cmd_t api_key_cmd = {
|
||||||
|
.command = "set_api_key",
|
||||||
|
.help = "Set Claude API key",
|
||||||
|
.func = &cmd_set_api_key,
|
||||||
|
.argtable = &api_key_args,
|
||||||
|
};
|
||||||
|
esp_console_cmd_register(&api_key_cmd);
|
||||||
|
|
||||||
|
/* set_model */
|
||||||
|
model_args.model = arg_str1(NULL, NULL, "<model>", "Model identifier");
|
||||||
|
model_args.end = arg_end(1);
|
||||||
|
esp_console_cmd_t model_cmd = {
|
||||||
|
.command = "set_model",
|
||||||
|
.help = "Set LLM model (default: " MIMI_LLM_DEFAULT_MODEL ")",
|
||||||
|
.func = &cmd_set_model,
|
||||||
|
.argtable = &model_args,
|
||||||
|
};
|
||||||
|
esp_console_cmd_register(&model_cmd);
|
||||||
|
|
||||||
/* memory_read */
|
/* memory_read */
|
||||||
esp_console_cmd_t mem_read_cmd = {
|
esp_console_cmd_t mem_read_cmd = {
|
||||||
.command = "memory_read",
|
.command = "memory_read",
|
||||||
@@ -175,6 +411,53 @@ esp_err_t serial_cli_init(void)
|
|||||||
};
|
};
|
||||||
esp_console_cmd_register(&heap_cmd);
|
esp_console_cmd_register(&heap_cmd);
|
||||||
|
|
||||||
|
/* set_search_key */
|
||||||
|
search_key_args.key = arg_str1(NULL, NULL, "<key>", "Brave Search API key");
|
||||||
|
search_key_args.end = arg_end(1);
|
||||||
|
esp_console_cmd_t search_key_cmd = {
|
||||||
|
.command = "set_search_key",
|
||||||
|
.help = "Set Brave Search API key for web_search tool",
|
||||||
|
.func = &cmd_set_search_key,
|
||||||
|
.argtable = &search_key_args,
|
||||||
|
};
|
||||||
|
esp_console_cmd_register(&search_key_cmd);
|
||||||
|
|
||||||
|
/* set_proxy */
|
||||||
|
proxy_args.host = arg_str1(NULL, NULL, "<host>", "Proxy host/IP");
|
||||||
|
proxy_args.port = arg_int1(NULL, NULL, "<port>", "Proxy port");
|
||||||
|
proxy_args.end = arg_end(2);
|
||||||
|
esp_console_cmd_t proxy_cmd = {
|
||||||
|
.command = "set_proxy",
|
||||||
|
.help = "Set HTTP proxy (e.g. set_proxy 192.168.1.83 7897)",
|
||||||
|
.func = &cmd_set_proxy,
|
||||||
|
.argtable = &proxy_args,
|
||||||
|
};
|
||||||
|
esp_console_cmd_register(&proxy_cmd);
|
||||||
|
|
||||||
|
/* clear_proxy */
|
||||||
|
esp_console_cmd_t clear_proxy_cmd = {
|
||||||
|
.command = "clear_proxy",
|
||||||
|
.help = "Remove proxy configuration",
|
||||||
|
.func = &cmd_clear_proxy,
|
||||||
|
};
|
||||||
|
esp_console_cmd_register(&clear_proxy_cmd);
|
||||||
|
|
||||||
|
/* config_show */
|
||||||
|
esp_console_cmd_t config_show_cmd = {
|
||||||
|
.command = "config_show",
|
||||||
|
.help = "Show current configuration (build-time + NVS)",
|
||||||
|
.func = &cmd_config_show,
|
||||||
|
};
|
||||||
|
esp_console_cmd_register(&config_show_cmd);
|
||||||
|
|
||||||
|
/* config_reset */
|
||||||
|
esp_console_cmd_t config_reset_cmd = {
|
||||||
|
.command = "config_reset",
|
||||||
|
.help = "Clear all NVS overrides, revert to build-time defaults",
|
||||||
|
.func = &cmd_config_reset,
|
||||||
|
};
|
||||||
|
esp_console_cmd_register(&config_reset_cmd);
|
||||||
|
|
||||||
/* restart */
|
/* restart */
|
||||||
esp_console_cmd_t restart_cmd = {
|
esp_console_cmd_t restart_cmd = {
|
||||||
.command = "restart",
|
.command = "restart",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "esp_http_client.h"
|
#include "esp_http_client.h"
|
||||||
#include "esp_crt_bundle.h"
|
#include "esp_crt_bundle.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
|
#include "nvs.h"
|
||||||
#include "cJSON.h"
|
#include "cJSON.h"
|
||||||
|
|
||||||
static const char *TAG = "llm";
|
static const char *TAG = "llm";
|
||||||
@@ -70,6 +71,7 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
|||||||
|
|
||||||
esp_err_t llm_proxy_init(void)
|
esp_err_t llm_proxy_init(void)
|
||||||
{
|
{
|
||||||
|
/* Start with build-time defaults */
|
||||||
if (MIMI_SECRET_API_KEY[0] != '\0') {
|
if (MIMI_SECRET_API_KEY[0] != '\0') {
|
||||||
strncpy(s_api_key, MIMI_SECRET_API_KEY, sizeof(s_api_key) - 1);
|
strncpy(s_api_key, MIMI_SECRET_API_KEY, sizeof(s_api_key) - 1);
|
||||||
}
|
}
|
||||||
@@ -77,10 +79,26 @@ esp_err_t llm_proxy_init(void)
|
|||||||
strncpy(s_model, MIMI_SECRET_MODEL, sizeof(s_model) - 1);
|
strncpy(s_model, MIMI_SECRET_MODEL, sizeof(s_model) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NVS overrides take highest priority (set via CLI) */
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
if (nvs_open(MIMI_NVS_LLM, NVS_READONLY, &nvs) == ESP_OK) {
|
||||||
|
char tmp[128] = {0};
|
||||||
|
size_t len = sizeof(tmp);
|
||||||
|
if (nvs_get_str(nvs, MIMI_NVS_KEY_API_KEY, tmp, &len) == ESP_OK && tmp[0]) {
|
||||||
|
strncpy(s_api_key, tmp, sizeof(s_api_key) - 1);
|
||||||
|
}
|
||||||
|
len = sizeof(tmp);
|
||||||
|
memset(tmp, 0, sizeof(tmp));
|
||||||
|
if (nvs_get_str(nvs, MIMI_NVS_KEY_MODEL, tmp, &len) == ESP_OK && tmp[0]) {
|
||||||
|
strncpy(s_model, tmp, sizeof(s_model) - 1);
|
||||||
|
}
|
||||||
|
nvs_close(nvs);
|
||||||
|
}
|
||||||
|
|
||||||
if (s_api_key[0]) {
|
if (s_api_key[0]) {
|
||||||
ESP_LOGI(TAG, "LLM proxy initialized (model: %s)", s_model);
|
ESP_LOGI(TAG, "LLM proxy initialized (model: %s)", s_model);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "No API key. Set MIMI_SECRET_API_KEY in mimi_secrets.h");
|
ESP_LOGW(TAG, "No API key. Use CLI: set_api_key <KEY>");
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
@@ -448,3 +466,30 @@ esp_err_t llm_chat_tools(const char *system_prompt,
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── NVS helpers ──────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
esp_err_t llm_set_api_key(const char *api_key)
|
||||||
|
{
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
ESP_ERROR_CHECK(nvs_open(MIMI_NVS_LLM, NVS_READWRITE, &nvs));
|
||||||
|
ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_API_KEY, api_key));
|
||||||
|
ESP_ERROR_CHECK(nvs_commit(nvs));
|
||||||
|
nvs_close(nvs);
|
||||||
|
|
||||||
|
strncpy(s_api_key, api_key, sizeof(s_api_key) - 1);
|
||||||
|
ESP_LOGI(TAG, "API key saved");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t llm_set_model(const char *model)
|
||||||
|
{
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
ESP_ERROR_CHECK(nvs_open(MIMI_NVS_LLM, NVS_READWRITE, &nvs));
|
||||||
|
ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_MODEL, model));
|
||||||
|
ESP_ERROR_CHECK(nvs_commit(nvs));
|
||||||
|
nvs_close(nvs);
|
||||||
|
|
||||||
|
strncpy(s_model, model, sizeof(s_model) - 1);
|
||||||
|
ESP_LOGI(TAG, "Model set to: %s", s_model);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,10 +8,20 @@
|
|||||||
#include "mimi_config.h"
|
#include "mimi_config.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the LLM proxy.
|
* Initialize the LLM proxy. Reads API key and model from build-time secrets, then NVS.
|
||||||
*/
|
*/
|
||||||
esp_err_t llm_proxy_init(void);
|
esp_err_t llm_proxy_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the Anthropic API key to NVS.
|
||||||
|
*/
|
||||||
|
esp_err_t llm_set_api_key(const char *api_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the model identifier to NVS.
|
||||||
|
*/
|
||||||
|
esp_err_t llm_set_model(const char *model);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a chat completion request to Anthropic Messages API (streaming).
|
* Send a chat completion request to Anthropic Messages API (streaming).
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
/* MimiClaw Global Configuration */
|
/* MimiClaw Global Configuration */
|
||||||
|
|
||||||
/* Build-time secrets (sole configuration method) */
|
/* Build-time secrets (highest priority, override NVS) */
|
||||||
#if __has_include("mimi_secrets.h")
|
#if __has_include("mimi_secrets.h")
|
||||||
#include "mimi_secrets.h"
|
#include "mimi_secrets.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -88,3 +88,19 @@
|
|||||||
#define MIMI_CLI_PRIO 3
|
#define MIMI_CLI_PRIO 3
|
||||||
#define MIMI_CLI_CORE 0
|
#define MIMI_CLI_CORE 0
|
||||||
|
|
||||||
|
/* NVS Namespaces */
|
||||||
|
#define MIMI_NVS_WIFI "wifi_config"
|
||||||
|
#define MIMI_NVS_TG "tg_config"
|
||||||
|
#define MIMI_NVS_LLM "llm_config"
|
||||||
|
#define MIMI_NVS_PROXY "proxy_config"
|
||||||
|
#define MIMI_NVS_SEARCH "search_config"
|
||||||
|
|
||||||
|
/* NVS Keys */
|
||||||
|
#define MIMI_NVS_KEY_SSID "ssid"
|
||||||
|
#define MIMI_NVS_KEY_PASS "password"
|
||||||
|
#define MIMI_NVS_KEY_TG_TOKEN "bot_token"
|
||||||
|
#define MIMI_NVS_KEY_API_KEY "api_key"
|
||||||
|
#define MIMI_NVS_KEY_MODEL "model"
|
||||||
|
#define MIMI_NVS_KEY_PROXY_HOST "host"
|
||||||
|
#define MIMI_NVS_KEY_PROXY_PORT "port"
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "nvs.h"
|
||||||
#include "esp_tls.h"
|
#include "esp_tls.h"
|
||||||
#include "esp_crt_bundle.h"
|
#include "esp_crt_bundle.h"
|
||||||
|
|
||||||
@@ -25,11 +26,27 @@ static uint16_t s_proxy_port = 0;
|
|||||||
|
|
||||||
esp_err_t http_proxy_init(void)
|
esp_err_t http_proxy_init(void)
|
||||||
{
|
{
|
||||||
|
/* Start with build-time defaults */
|
||||||
if (MIMI_SECRET_PROXY_HOST[0] != '\0' && MIMI_SECRET_PROXY_PORT[0] != '\0') {
|
if (MIMI_SECRET_PROXY_HOST[0] != '\0' && MIMI_SECRET_PROXY_PORT[0] != '\0') {
|
||||||
strncpy(s_proxy_host, MIMI_SECRET_PROXY_HOST, sizeof(s_proxy_host) - 1);
|
strncpy(s_proxy_host, MIMI_SECRET_PROXY_HOST, sizeof(s_proxy_host) - 1);
|
||||||
s_proxy_port = (uint16_t)atoi(MIMI_SECRET_PROXY_PORT);
|
s_proxy_port = (uint16_t)atoi(MIMI_SECRET_PROXY_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NVS overrides take highest priority (set via CLI) */
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
if (nvs_open(MIMI_NVS_PROXY, NVS_READONLY, &nvs) == ESP_OK) {
|
||||||
|
char tmp[64] = {0};
|
||||||
|
size_t len = sizeof(tmp);
|
||||||
|
if (nvs_get_str(nvs, MIMI_NVS_KEY_PROXY_HOST, tmp, &len) == ESP_OK && tmp[0]) {
|
||||||
|
strncpy(s_proxy_host, tmp, sizeof(s_proxy_host) - 1);
|
||||||
|
uint16_t port = 0;
|
||||||
|
if (nvs_get_u16(nvs, MIMI_NVS_KEY_PROXY_PORT, &port) == ESP_OK && port) {
|
||||||
|
s_proxy_port = port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nvs_close(nvs);
|
||||||
|
}
|
||||||
|
|
||||||
if (s_proxy_host[0] && s_proxy_port) {
|
if (s_proxy_host[0] && s_proxy_port) {
|
||||||
ESP_LOGI(TAG, "Proxy configured: %s:%d", s_proxy_host, s_proxy_port);
|
ESP_LOGI(TAG, "Proxy configured: %s:%d", s_proxy_host, s_proxy_port);
|
||||||
} else {
|
} else {
|
||||||
@@ -38,6 +55,36 @@ esp_err_t http_proxy_init(void)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t http_proxy_set(const char *host, uint16_t port)
|
||||||
|
{
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
ESP_ERROR_CHECK(nvs_open(MIMI_NVS_PROXY, NVS_READWRITE, &nvs));
|
||||||
|
ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_PROXY_HOST, host));
|
||||||
|
ESP_ERROR_CHECK(nvs_set_u16(nvs, MIMI_NVS_KEY_PROXY_PORT, port));
|
||||||
|
ESP_ERROR_CHECK(nvs_commit(nvs));
|
||||||
|
nvs_close(nvs);
|
||||||
|
|
||||||
|
strncpy(s_proxy_host, host, sizeof(s_proxy_host) - 1);
|
||||||
|
s_proxy_port = port;
|
||||||
|
ESP_LOGI(TAG, "Proxy set to %s:%d", s_proxy_host, s_proxy_port);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t http_proxy_clear(void)
|
||||||
|
{
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
ESP_ERROR_CHECK(nvs_open(MIMI_NVS_PROXY, NVS_READWRITE, &nvs));
|
||||||
|
nvs_erase_key(nvs, MIMI_NVS_KEY_PROXY_HOST);
|
||||||
|
nvs_erase_key(nvs, MIMI_NVS_KEY_PROXY_PORT);
|
||||||
|
nvs_commit(nvs);
|
||||||
|
nvs_close(nvs);
|
||||||
|
|
||||||
|
s_proxy_host[0] = '\0';
|
||||||
|
s_proxy_port = 0;
|
||||||
|
ESP_LOGI(TAG, "Proxy cleared");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
bool http_proxy_is_enabled(void)
|
bool http_proxy_is_enabled(void)
|
||||||
{
|
{
|
||||||
return s_proxy_host[0] != '\0' && s_proxy_port != 0;
|
return s_proxy_host[0] != '\0' && s_proxy_port != 0;
|
||||||
|
|||||||
@@ -14,6 +14,16 @@ esp_err_t http_proxy_init(void);
|
|||||||
*/
|
*/
|
||||||
bool http_proxy_is_enabled(void);
|
bool http_proxy_is_enabled(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save proxy host and port to NVS.
|
||||||
|
*/
|
||||||
|
esp_err_t http_proxy_set(const char *host, uint16_t port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove proxy config from NVS.
|
||||||
|
*/
|
||||||
|
esp_err_t http_proxy_clear(void);
|
||||||
|
|
||||||
/* ── Proxied HTTPS connection ─────────────────────────────────── */
|
/* ── Proxied HTTPS connection ─────────────────────────────────── */
|
||||||
|
|
||||||
typedef struct proxy_conn proxy_conn_t;
|
typedef struct proxy_conn proxy_conn_t;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_http_client.h"
|
#include "esp_http_client.h"
|
||||||
#include "esp_crt_bundle.h"
|
#include "esp_crt_bundle.h"
|
||||||
|
#include "nvs.h"
|
||||||
#include "cJSON.h"
|
#include "cJSON.h"
|
||||||
|
|
||||||
static const char *TAG = "telegram";
|
static const char *TAG = "telegram";
|
||||||
@@ -256,10 +257,23 @@ static void telegram_poll_task(void *arg)
|
|||||||
|
|
||||||
esp_err_t telegram_bot_init(void)
|
esp_err_t telegram_bot_init(void)
|
||||||
{
|
{
|
||||||
|
/* NVS overrides take highest priority (set via CLI) */
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
if (nvs_open(MIMI_NVS_TG, NVS_READONLY, &nvs) == ESP_OK) {
|
||||||
|
char tmp[128] = {0};
|
||||||
|
size_t len = sizeof(tmp);
|
||||||
|
if (nvs_get_str(nvs, MIMI_NVS_KEY_TG_TOKEN, tmp, &len) == ESP_OK && tmp[0]) {
|
||||||
|
strncpy(s_bot_token, tmp, sizeof(s_bot_token) - 1);
|
||||||
|
}
|
||||||
|
nvs_close(nvs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* s_bot_token is already initialized from MIMI_SECRET_TG_TOKEN as fallback */
|
||||||
|
|
||||||
if (s_bot_token[0]) {
|
if (s_bot_token[0]) {
|
||||||
ESP_LOGI(TAG, "Telegram bot token loaded (len=%d)", (int)strlen(s_bot_token));
|
ESP_LOGI(TAG, "Telegram bot token loaded (len=%d)", (int)strlen(s_bot_token));
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "No Telegram bot token. Set MIMI_SECRET_TG_TOKEN in mimi_secrets.h");
|
ESP_LOGW(TAG, "No Telegram bot token. Use CLI: set_tg_token <TOKEN>");
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
@@ -357,3 +371,15 @@ esp_err_t telegram_send_message(const char *chat_id, const char *text)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t telegram_set_token(const char *token)
|
||||||
|
{
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
ESP_ERROR_CHECK(nvs_open(MIMI_NVS_TG, NVS_READWRITE, &nvs));
|
||||||
|
ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_TG_TOKEN, token));
|
||||||
|
ESP_ERROR_CHECK(nvs_commit(nvs));
|
||||||
|
nvs_close(nvs);
|
||||||
|
|
||||||
|
strncpy(s_bot_token, token, sizeof(s_bot_token) - 1);
|
||||||
|
ESP_LOGI(TAG, "Telegram bot token saved");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,3 +20,8 @@ esp_err_t telegram_bot_start(void);
|
|||||||
*/
|
*/
|
||||||
esp_err_t telegram_send_message(const char *chat_id, const char *text);
|
esp_err_t telegram_send_message(const char *chat_id, const char *text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the Telegram bot token to NVS.
|
||||||
|
*/
|
||||||
|
esp_err_t telegram_set_token(const char *token);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "esp_http_client.h"
|
#include "esp_http_client.h"
|
||||||
#include "esp_crt_bundle.h"
|
#include "esp_crt_bundle.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
|
#include "nvs.h"
|
||||||
#include "cJSON.h"
|
#include "cJSON.h"
|
||||||
|
|
||||||
static const char *TAG = "web_search";
|
static const char *TAG = "web_search";
|
||||||
@@ -43,14 +44,26 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
|||||||
|
|
||||||
esp_err_t tool_web_search_init(void)
|
esp_err_t tool_web_search_init(void)
|
||||||
{
|
{
|
||||||
|
/* Start with build-time default */
|
||||||
if (MIMI_SECRET_SEARCH_KEY[0] != '\0') {
|
if (MIMI_SECRET_SEARCH_KEY[0] != '\0') {
|
||||||
strncpy(s_search_key, MIMI_SECRET_SEARCH_KEY, sizeof(s_search_key) - 1);
|
strncpy(s_search_key, MIMI_SECRET_SEARCH_KEY, sizeof(s_search_key) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NVS overrides take highest priority (set via CLI) */
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
if (nvs_open(MIMI_NVS_SEARCH, NVS_READONLY, &nvs) == ESP_OK) {
|
||||||
|
char tmp[128] = {0};
|
||||||
|
size_t len = sizeof(tmp);
|
||||||
|
if (nvs_get_str(nvs, MIMI_NVS_KEY_API_KEY, tmp, &len) == ESP_OK && tmp[0]) {
|
||||||
|
strncpy(s_search_key, tmp, sizeof(s_search_key) - 1);
|
||||||
|
}
|
||||||
|
nvs_close(nvs);
|
||||||
|
}
|
||||||
|
|
||||||
if (s_search_key[0]) {
|
if (s_search_key[0]) {
|
||||||
ESP_LOGI(TAG, "Web search initialized (key configured)");
|
ESP_LOGI(TAG, "Web search initialized (key configured)");
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "No search API key. Set MIMI_SECRET_SEARCH_KEY in mimi_secrets.h");
|
ESP_LOGW(TAG, "No search API key. Use CLI: set_search_key <KEY>");
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
@@ -283,3 +296,16 @@ esp_err_t tool_web_search_execute(const char *input_json, char *output, size_t o
|
|||||||
ESP_LOGI(TAG, "Search complete, %d bytes result", (int)strlen(output));
|
ESP_LOGI(TAG, "Search complete, %d bytes result", (int)strlen(output));
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t tool_web_search_set_key(const char *api_key)
|
||||||
|
{
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
ESP_ERROR_CHECK(nvs_open(MIMI_NVS_SEARCH, NVS_READWRITE, &nvs));
|
||||||
|
ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_API_KEY, api_key));
|
||||||
|
ESP_ERROR_CHECK(nvs_commit(nvs));
|
||||||
|
nvs_close(nvs);
|
||||||
|
|
||||||
|
strncpy(s_search_key, api_key, sizeof(s_search_key) - 1);
|
||||||
|
ESP_LOGI(TAG, "Search API key saved");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,3 +18,7 @@ esp_err_t tool_web_search_init(void);
|
|||||||
*/
|
*/
|
||||||
esp_err_t tool_web_search_execute(const char *input_json, char *output, size_t output_size);
|
esp_err_t tool_web_search_execute(const char *input_json, char *output, size_t output_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save Brave Search API key to NVS.
|
||||||
|
*/
|
||||||
|
esp_err_t tool_web_search_set_key(const char *api_key);
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_wifi.h"
|
#include "esp_wifi.h"
|
||||||
#include "esp_netif.h"
|
#include "esp_netif.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "nvs.h"
|
||||||
|
|
||||||
static const char *TAG = "wifi";
|
static const char *TAG = "wifi";
|
||||||
|
|
||||||
@@ -70,14 +72,34 @@ esp_err_t wifi_manager_init(void)
|
|||||||
|
|
||||||
esp_err_t wifi_manager_start(void)
|
esp_err_t wifi_manager_start(void)
|
||||||
{
|
{
|
||||||
if (MIMI_SECRET_WIFI_SSID[0] == '\0') {
|
wifi_config_t wifi_cfg = {0};
|
||||||
ESP_LOGW(TAG, "No WiFi credentials. Set MIMI_SECRET_WIFI_SSID in mimi_secrets.h");
|
bool found = false;
|
||||||
return ESP_ERR_NOT_FOUND;
|
|
||||||
|
/* NVS overrides take highest priority (set via CLI) */
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
if (nvs_open(MIMI_NVS_WIFI, NVS_READONLY, &nvs) == ESP_OK) {
|
||||||
|
size_t len = sizeof(wifi_cfg.sta.ssid);
|
||||||
|
if (nvs_get_str(nvs, MIMI_NVS_KEY_SSID, (char *)wifi_cfg.sta.ssid, &len) == ESP_OK) {
|
||||||
|
len = sizeof(wifi_cfg.sta.password);
|
||||||
|
nvs_get_str(nvs, MIMI_NVS_KEY_PASS, (char *)wifi_cfg.sta.password, &len);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
nvs_close(nvs);
|
||||||
}
|
}
|
||||||
|
|
||||||
wifi_config_t wifi_cfg = {0};
|
/* Fall back to build-time secrets */
|
||||||
strncpy((char *)wifi_cfg.sta.ssid, MIMI_SECRET_WIFI_SSID, sizeof(wifi_cfg.sta.ssid) - 1);
|
if (!found) {
|
||||||
strncpy((char *)wifi_cfg.sta.password, MIMI_SECRET_WIFI_PASS, sizeof(wifi_cfg.sta.password) - 1);
|
if (MIMI_SECRET_WIFI_SSID[0] != '\0') {
|
||||||
|
strncpy((char *)wifi_cfg.sta.ssid, MIMI_SECRET_WIFI_SSID, sizeof(wifi_cfg.sta.ssid) - 1);
|
||||||
|
strncpy((char *)wifi_cfg.sta.password, MIMI_SECRET_WIFI_PASS, sizeof(wifi_cfg.sta.password) - 1);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
ESP_LOGW(TAG, "No WiFi credentials. Use CLI: wifi_set <SSID> <PASS>");
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Connecting to SSID: %s", wifi_cfg.sta.ssid);
|
ESP_LOGI(TAG, "Connecting to SSID: %s", wifi_cfg.sta.ssid);
|
||||||
|
|
||||||
@@ -110,6 +132,18 @@ const char *wifi_manager_get_ip(void)
|
|||||||
return s_ip_str;
|
return s_ip_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t wifi_manager_set_credentials(const char *ssid, const char *password)
|
||||||
|
{
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
ESP_ERROR_CHECK(nvs_open(MIMI_NVS_WIFI, NVS_READWRITE, &nvs));
|
||||||
|
ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_SSID, ssid));
|
||||||
|
ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_PASS, password));
|
||||||
|
ESP_ERROR_CHECK(nvs_commit(nvs));
|
||||||
|
nvs_close(nvs);
|
||||||
|
ESP_LOGI(TAG, "WiFi credentials saved for SSID: %s", ssid);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
EventGroupHandle_t wifi_manager_get_event_group(void)
|
EventGroupHandle_t wifi_manager_get_event_group(void)
|
||||||
{
|
{
|
||||||
return s_wifi_event_group;
|
return s_wifi_event_group;
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ bool wifi_manager_is_connected(void);
|
|||||||
*/
|
*/
|
||||||
const char *wifi_manager_get_ip(void);
|
const char *wifi_manager_get_ip(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save WiFi credentials to NVS.
|
||||||
|
*/
|
||||||
|
esp_err_t wifi_manager_set_credentials(const char *ssid, const char *password);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the event group for WiFi state (WIFI_CONNECTED_BIT / WIFI_FAIL_BIT).
|
* Get the event group for WiFi state (WIFI_CONNECTED_BIT / WIFI_FAIL_BIT).
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user