From e2782641330b28688762c70be6804cc0e0f40ec3 Mon Sep 17 00:00:00 2001 From: Asklv Date: Sat, 7 Mar 2026 16:15:28 +0800 Subject: [PATCH 1/9] feat: add GPIO policy module and tool header --- main/tools/gpio_policy.c | 107 +++++++++++++++++++++++++++++++++++++++ main/tools/gpio_policy.h | 22 ++++++++ main/tools/tool_gpio.h | 27 ++++++++++ 3 files changed, 156 insertions(+) create mode 100644 main/tools/gpio_policy.c create mode 100644 main/tools/gpio_policy.h create mode 100644 main/tools/tool_gpio.h diff --git a/main/tools/gpio_policy.c b/main/tools/gpio_policy.c new file mode 100644 index 0000000..5c1fb31 --- /dev/null +++ b/main/tools/gpio_policy.c @@ -0,0 +1,107 @@ +#include "tools/gpio_policy.h" + +#include "driver/gpio.h" + +#include +#include +#include + +#ifndef GPIO_IS_VALID_GPIO +#define GPIO_IS_VALID_GPIO(pin) ((pin) >= 0) +#endif + +static bool pin_in_allowlist(int pin, const char *csv) +{ + const char *cursor; + + if (!csv || csv[0] == '\0') { + return false; + } + + cursor = csv; + while (*cursor != '\0') { + char *endptr = NULL; + long value; + + while (*cursor == ' ' || *cursor == '\t' || *cursor == ',') { + cursor++; + } + if (*cursor == '\0') { + break; + } + + value = strtol(cursor, &endptr, 10); + if (endptr == cursor) { + while (*cursor != '\0' && *cursor != ',') { + cursor++; + } + continue; + } + + if ((int)value == pin) { + return true; + } + cursor = endptr; + } + + return false; +} + +static bool pin_is_allowed_impl(int pin, + const char *allowlist_csv, + int min_pin, + int max_pin, + bool block_esp32_flash_pins) +{ + bool in_policy; + + if (pin < 0) { + return false; + } + + /* Block ESP32 flash/PSRAM pins (GPIO 6-11) */ + if (block_esp32_flash_pins && pin >= 6 && pin <= 11) { + return false; + } + + if (allowlist_csv && allowlist_csv[0] != '\0') { + in_policy = pin_in_allowlist(pin, allowlist_csv); + } else { + in_policy = pin >= min_pin && pin <= max_pin; + } + + if (!in_policy) { + return false; + } + + return GPIO_IS_VALID_GPIO((gpio_num_t)pin); +} + +bool gpio_policy_pin_is_allowed(int pin) +{ +#if defined(CONFIG_IDF_TARGET_ESP32) + return pin_is_allowed_impl(pin, MIMI_GPIO_ALLOWED_CSV, + MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN, true); +#else + return pin_is_allowed_impl(pin, MIMI_GPIO_ALLOWED_CSV, + MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN, false); +#endif +} + +bool gpio_policy_pin_forbidden_hint(int pin, char *result, size_t result_len) +{ +#if defined(CONFIG_IDF_TARGET_ESP32) + if (pin >= 6 && pin <= 11) { + snprintf(result, result_len, + "Error: pin %d is reserved for ESP32 flash/PSRAM (GPIO6-11); choose a different pin", + pin); + return true; + } +#else + (void)pin; + (void)result; + (void)result_len; +#endif + + return false; +} diff --git a/main/tools/gpio_policy.h b/main/tools/gpio_policy.h new file mode 100644 index 0000000..1b70674 --- /dev/null +++ b/main/tools/gpio_policy.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +/* GPIO pin range defaults for ESP32-S3 (safe user-accessible pins) */ +#define MIMI_GPIO_MIN_PIN 1 +#define MIMI_GPIO_MAX_PIN 21 +#define MIMI_GPIO_ALLOWED_CSV "" /* empty = use min/max range */ + +/** + * Check if a pin is allowed for user GPIO operations. + * Validates against the allowlist or default range, and blocks + * pins reserved for flash/PSRAM on ESP32. + */ +bool gpio_policy_pin_is_allowed(int pin); + +/** + * Write a human-readable hint if the pin is forbidden for a known reason. + * Returns true if a hint was written (and the caller should return the error). + */ +bool gpio_policy_pin_forbidden_hint(int pin, char *result, size_t result_len); diff --git a/main/tools/tool_gpio.h b/main/tools/tool_gpio.h new file mode 100644 index 0000000..b435b15 --- /dev/null +++ b/main/tools/tool_gpio.h @@ -0,0 +1,27 @@ +#pragma once + +#include "esp_err.h" +#include + +/** + * Initialize GPIO tool — configure allowed pins and directions. + */ +esp_err_t tool_gpio_init(void); + +/** + * Write a GPIO pin HIGH or LOW. + * Input JSON: {"pin": , "state": <0|1>} + */ +esp_err_t tool_gpio_write_execute(const char *input_json, char *output, size_t output_size); + +/** + * Read a single GPIO pin state. + * Input JSON: {"pin": } + */ +esp_err_t tool_gpio_read_execute(const char *input_json, char *output, size_t output_size); + +/** + * Read all allowed GPIO pin states at once. + * Input JSON: {} (no parameters) + */ +esp_err_t tool_gpio_read_all_execute(const char *input_json, char *output, size_t output_size); From 46dc186a804f521d3176a14366046593805419f7 Mon Sep 17 00:00:00 2001 From: Asklv Date: Sat, 7 Mar 2026 16:15:31 +0800 Subject: [PATCH 2/9] feat: implement gpio_write, gpio_read, gpio_read_all handlers --- main/tools/tool_gpio.c | 194 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 main/tools/tool_gpio.c diff --git a/main/tools/tool_gpio.c b/main/tools/tool_gpio.c new file mode 100644 index 0000000..1e43b1b --- /dev/null +++ b/main/tools/tool_gpio.c @@ -0,0 +1,194 @@ +#include "tools/tool_gpio.h" +#include "tools/gpio_policy.h" +#include "mimi_config.h" + +#include "driver/gpio.h" +#include "esp_log.h" +#include "cJSON.h" + +#include +#include + +static const char *TAG = "tool_gpio"; + +esp_err_t tool_gpio_init(void) +{ + ESP_LOGI(TAG, "GPIO tool initialized (pin range %d-%d)", + MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN); + return ESP_OK; +} + +esp_err_t tool_gpio_write_execute(const char *input_json, char *output, size_t output_size) +{ + cJSON *root = cJSON_Parse(input_json); + if (!root) { + snprintf(output, output_size, "Error: invalid JSON input"); + return ESP_ERR_INVALID_ARG; + } + + cJSON *pin_obj = cJSON_GetObjectItem(root, "pin"); + cJSON *state_obj = cJSON_GetObjectItem(root, "state"); + + if (!cJSON_IsNumber(pin_obj)) { + snprintf(output, output_size, "Error: 'pin' required (integer)"); + cJSON_Delete(root); + return ESP_ERR_INVALID_ARG; + } + if (!cJSON_IsNumber(state_obj)) { + snprintf(output, output_size, "Error: 'state' required (0 or 1)"); + cJSON_Delete(root); + return ESP_ERR_INVALID_ARG; + } + + int pin = (int)pin_obj->valuedouble; + int state = (int)state_obj->valuedouble; + + if (!gpio_policy_pin_is_allowed(pin)) { + if (gpio_policy_pin_forbidden_hint(pin, output, output_size)) { + cJSON_Delete(root); + return ESP_ERR_INVALID_ARG; + } + if (MIMI_GPIO_ALLOWED_CSV[0] != '\0') { + snprintf(output, output_size, "Error: pin %d is not in allowed list", pin); + } else { + snprintf(output, output_size, "Error: pin must be %d-%d", + MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN); + } + cJSON_Delete(root); + return ESP_ERR_INVALID_ARG; + } + + if (gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT) != ESP_OK || + gpio_set_level(pin, state ? 1 : 0) != ESP_OK) { + snprintf(output, output_size, "Error: failed to configure/write pin %d", pin); + cJSON_Delete(root); + return ESP_FAIL; + } + + snprintf(output, output_size, "Pin %d set to %s", pin, state ? "HIGH" : "LOW"); + ESP_LOGI(TAG, "gpio_write: pin %d -> %s", pin, state ? "HIGH" : "LOW"); + + cJSON_Delete(root); + return ESP_OK; +} + +esp_err_t tool_gpio_read_execute(const char *input_json, char *output, size_t output_size) +{ + cJSON *root = cJSON_Parse(input_json); + if (!root) { + snprintf(output, output_size, "Error: invalid JSON input"); + return ESP_ERR_INVALID_ARG; + } + + cJSON *pin_obj = cJSON_GetObjectItem(root, "pin"); + if (!cJSON_IsNumber(pin_obj)) { + snprintf(output, output_size, "Error: 'pin' required (integer)"); + cJSON_Delete(root); + return ESP_ERR_INVALID_ARG; + } + + int pin = (int)pin_obj->valuedouble; + + if (!gpio_policy_pin_is_allowed(pin)) { + if (gpio_policy_pin_forbidden_hint(pin, output, output_size)) { + cJSON_Delete(root); + return ESP_ERR_INVALID_ARG; + } + if (MIMI_GPIO_ALLOWED_CSV[0] != '\0') { + snprintf(output, output_size, "Error: pin %d is not in allowed list", pin); + } else { + snprintf(output, output_size, "Error: pin must be %d-%d", + MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN); + } + cJSON_Delete(root); + return ESP_ERR_INVALID_ARG; + } + + /* Enable input path, then read level */ + gpio_set_direction(pin, GPIO_MODE_INPUT); + int level = gpio_get_level(pin); + + snprintf(output, output_size, "Pin %d = %s", pin, level ? "HIGH" : "LOW"); + ESP_LOGI(TAG, "gpio_read: pin %d = %s", pin, level ? "HIGH" : "LOW"); + + cJSON_Delete(root); + return ESP_OK; +} + +esp_err_t tool_gpio_read_all_execute(const char *input_json, char *output, size_t output_size) +{ + (void)input_json; + + char *cursor = output; + size_t remaining = output_size; + int written; + int count = 0; + + written = snprintf(cursor, remaining, "GPIO states: "); + if (written < 0 || (size_t)written >= remaining) { + output[0] = '\0'; + return ESP_FAIL; + } + cursor += (size_t)written; + remaining -= (size_t)written; + + if (MIMI_GPIO_ALLOWED_CSV[0] != '\0') { + /* Iterate over explicit allowlist */ + const char *csv_cursor = MIMI_GPIO_ALLOWED_CSV; + while (*csv_cursor != '\0') { + char *endptr = NULL; + long value; + + while (*csv_cursor == ' ' || *csv_cursor == '\t' || *csv_cursor == ',') { + csv_cursor++; + } + if (*csv_cursor == '\0') break; + + value = strtol(csv_cursor, &endptr, 10); + if (endptr == csv_cursor) { + while (*csv_cursor != '\0' && *csv_cursor != ',') csv_cursor++; + continue; + } + if (!gpio_policy_pin_is_allowed((int)value)) { + csv_cursor = endptr; + continue; + } + + gpio_set_direction((int)value, GPIO_MODE_INPUT); + int level = gpio_get_level((int)value); + + written = snprintf(cursor, remaining, "%s%d=%s", + count == 0 ? "" : ", ", + (int)value, level ? "HIGH" : "LOW"); + if (written < 0 || (size_t)written >= remaining) break; + cursor += (size_t)written; + remaining -= (size_t)written; + count++; + csv_cursor = endptr; + } + } else { + /* Iterate over default range */ + for (int pin = MIMI_GPIO_MIN_PIN; pin <= MIMI_GPIO_MAX_PIN; pin++) { + if (!gpio_policy_pin_is_allowed(pin)) continue; + + gpio_set_direction(pin, GPIO_MODE_INPUT); + int level = gpio_get_level(pin); + + written = snprintf(cursor, remaining, "%s%d=%s", + count == 0 ? "" : ", ", + pin, level ? "HIGH" : "LOW"); + if (written < 0 || (size_t)written >= remaining) break; + cursor += (size_t)written; + remaining -= (size_t)written; + count++; + } + } + + if (count == 0) { + snprintf(output, output_size, "Error: no allowed GPIO pins configured"); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "gpio_read_all: %d pins read", count); + return ESP_OK; +} From d30b3611f3e845e63b540f6ce8c0e646466f7702 Mon Sep 17 00:00:00 2001 From: Asklv Date: Sat, 7 Mar 2026 16:15:34 +0800 Subject: [PATCH 3/9] feat: register GPIO tools in registry and build system --- main/CMakeLists.txt | 2 ++ main/tools/tool_registry.c | 40 +++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 5f3fe1e..09e5d42 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -20,6 +20,8 @@ idf_component_register( "tools/tool_web_search.c" "tools/tool_get_time.c" "tools/tool_files.c" + "tools/tool_gpio.c" + "tools/gpio_policy.c" "skills/skill_loader.c" INCLUDE_DIRS "." diff --git a/main/tools/tool_registry.c b/main/tools/tool_registry.c index 6c82a3e..41128c4 100644 --- a/main/tools/tool_registry.c +++ b/main/tools/tool_registry.c @@ -4,6 +4,7 @@ #include "tools/tool_get_time.h" #include "tools/tool_files.h" #include "tools/tool_cron.h" +#include "tools/tool_gpio.h" #include #include "esp_log.h" @@ -11,7 +12,7 @@ static const char *TAG = "tools"; -#define MAX_TOOLS 12 +#define MAX_TOOLS 16 static mimi_tool_t s_tools[MAX_TOOLS]; static int s_tool_count = 0; @@ -176,6 +177,43 @@ esp_err_t tool_registry_init(void) }; register_tool(&cr); + /* Register GPIO tools */ + tool_gpio_init(); + + mimi_tool_t gw = { + .name = "gpio_write", + .description = "Set a GPIO pin HIGH or LOW. Controls LEDs, relays, and other digital outputs.", + .input_schema_json = + "{\"type\":\"object\"," + "\"properties\":{\"pin\":{\"type\":\"integer\",\"description\":\"GPIO pin number\"}," + "\"state\":{\"type\":\"integer\",\"description\":\"1 for HIGH, 0 for LOW\"}}," + "\"required\":[\"pin\",\"state\"]}", + .execute = tool_gpio_write_execute, + }; + register_tool(&gw); + + mimi_tool_t gr = { + .name = "gpio_read", + .description = "Read a GPIO pin state. Returns HIGH or LOW. Use for checking switches, sensors, and digital inputs.", + .input_schema_json = + "{\"type\":\"object\"," + "\"properties\":{\"pin\":{\"type\":\"integer\",\"description\":\"GPIO pin number\"}}," + "\"required\":[\"pin\"]}", + .execute = tool_gpio_read_execute, + }; + register_tool(&gr); + + mimi_tool_t ga = { + .name = "gpio_read_all", + .description = "Read all allowed GPIO pin states in a single call. Returns each pin's HIGH/LOW state.", + .input_schema_json = + "{\"type\":\"object\"," + "\"properties\":{}," + "\"required\":[]}", + .execute = tool_gpio_read_all_execute, + }; + register_tool(&ga); + build_tools_json(); ESP_LOGI(TAG, "Tool registry initialized"); From 22ac11c9fd531cb399027a48a4930137191b5c46 Mon Sep 17 00:00:00 2001 From: Asklv Date: Sat, 7 Mar 2026 16:15:37 +0800 Subject: [PATCH 4/9] feat: add GPIO tool descriptions to agent system prompt --- main/agent/context_builder.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/main/agent/context_builder.c b/main/agent/context_builder.c index 5f8d503..870b2ac 100644 --- a/main/agent/context_builder.c +++ b/main/agent/context_builder.c @@ -46,8 +46,15 @@ esp_err_t context_build_system_prompt(char *buf, size_t size) "- list_dir: List files, optionally filter by prefix.\n" "- cron_add: Schedule a recurring or one-shot task. The message will trigger an agent turn when the job fires.\n" "- cron_list: List all scheduled cron jobs.\n" - "- cron_remove: Remove a scheduled cron job by ID.\n\n" + "- cron_remove: Remove a scheduled cron job by ID.\n" + "- gpio_write: Set a GPIO pin HIGH or LOW. Use for controlling LEDs, relays, and digital outputs.\n" + "- gpio_read: Read a single GPIO pin state (HIGH or LOW). Use for checking switches, buttons, sensors.\n" + "- gpio_read_all: Read all allowed GPIO pins at once. Good for getting a full status overview.\n\n" "When using cron_add for Telegram delivery, always set channel='telegram' and a valid numeric chat_id.\n\n" + "## GPIO\n" + "You can control hardware GPIO pins on the ESP32-S3. Use gpio_read to check switch/sensor states " + "(digital input confirmation), and gpio_write to control outputs. Pin range is validated by policy — " + "only allowed pins can be accessed. When asked about switch states or digital I/O, use these tools.\n\n" "Use tools when needed. Provide your final answer as text after using tools.\n\n" "## Memory\n" "You have persistent memory stored on local flash:\n" From cf9a1c11ae648a5b6b950d618c62c98c8d55dd16 Mon Sep 17 00:00:00 2001 From: Asklv Date: Sat, 7 Mar 2026 16:15:41 +0800 Subject: [PATCH 5/9] feat: add built-in gpio-control skill for switch confirmation --- spiffs_data/skills/gpio-control.md | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 spiffs_data/skills/gpio-control.md diff --git a/spiffs_data/skills/gpio-control.md b/spiffs_data/skills/gpio-control.md new file mode 100644 index 0000000..9b29336 --- /dev/null +++ b/spiffs_data/skills/gpio-control.md @@ -0,0 +1,37 @@ +# GPIO Control + +Control and monitor GPIO pins on the ESP32-S3 for digital I/O. + +## When to use +When the user asks to: +- Turn on/off LEDs, relays, or other outputs +- Check switch states, button presses, or sensor readings +- Confirm digital I/O status (switch confirmation) +- Get an overview of all GPIO pin states + +## How to use +1. To **read a switch/sensor**: use gpio_read with the pin number + - Returns HIGH (1) or LOW (0) + - HIGH typically means switch is ON / circuit closed + - LOW typically means switch is OFF / circuit open +2. To **set an output**: use gpio_write with pin and state (1=HIGH, 0=LOW) +3. To **scan all pins**: use gpio_read_all for a full status overview +4. For **switch confirmation**: read the pin, report state, optionally toggle and re-read to verify + +## Pin safety +- Only pins within the allowed range can be accessed +- ESP32 flash pins (6-11) are always blocked +- If a pin is rejected, suggest an alternative within the allowed range + +## Example +User: "Check if the switch on pin 4 is on" +→ gpio_read {"pin": 4} +→ "Pin 4 = HIGH" +→ "The switch on pin 4 is currently ON (HIGH)." + +User: "Turn on the relay on pin 5" +→ gpio_write {"pin": 5, "state": 1} +→ "Pin 5 set to HIGH" +→ gpio_read {"pin": 5} +→ "Pin 5 = HIGH" +→ "Relay on pin 5 is now ON. Confirmed HIGH." From 52f3bee043bb24cf13973fc1036528dd26e165b0 Mon Sep 17 00:00:00 2001 From: Asklv Date: Sat, 7 Mar 2026 16:15:44 +0800 Subject: [PATCH 6/9] feat: add GPIO config section to mimi_config.h --- main/mimi_config.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main/mimi_config.h b/main/mimi_config.h index 9be7c08..35a5e1b 100644 --- a/main/mimi_config.h +++ b/main/mimi_config.h @@ -117,6 +117,9 @@ #define MIMI_HEARTBEAT_FILE MIMI_SPIFFS_BASE "/HEARTBEAT.md" #define MIMI_HEARTBEAT_INTERVAL_MS (30 * 60 * 1000) +/* GPIO */ +#define MIMI_GPIO_CONFIG_SECTION 1 /* enable GPIO tools */ + /* Skills */ #define MIMI_SKILLS_PREFIX MIMI_SPIFFS_BASE "/skills/" From 16eb01b49ae2c099def3686ebb4d0d835b685e8e Mon Sep 17 00:00:00 2001 From: Asklv Date: Sun, 8 Mar 2026 12:32:37 +0800 Subject: [PATCH 7/9] fix(build): add esp_driver_gpio dependency for GPIO tool --- main/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 09e5d42..19ce6bd 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -28,5 +28,5 @@ idf_component_register( REQUIRES nvs_flash esp_wifi esp_netif esp_http_client esp_http_server esp_https_ota esp_event json spiffs console vfs app_update esp-tls - esp_timer esp_websocket_client + esp_timer esp_websocket_client esp_driver_gpio ) From 37c0b0d6eeed321b2893937bde66f792eb019e5b Mon Sep 17 00:00:00 2001 From: Asklv Date: Mon, 9 Mar 2026 00:23:03 +0800 Subject: [PATCH 8/9] fix: protect usb console pins --- main/tools/gpio_policy.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/main/tools/gpio_policy.c b/main/tools/gpio_policy.c index 5c1fb31..5c12d30 100644 --- a/main/tools/gpio_policy.c +++ b/main/tools/gpio_policy.c @@ -51,7 +51,8 @@ static bool pin_is_allowed_impl(int pin, const char *allowlist_csv, int min_pin, int max_pin, - bool block_esp32_flash_pins) + bool block_esp32_flash_pins, + bool block_esp32s3_usb_pins) { bool in_policy; @@ -64,6 +65,11 @@ static bool pin_is_allowed_impl(int pin, return false; } + /* USB Serial/JTAG uses GPIO19/20 on ESP32-S3 */ + if (block_esp32s3_usb_pins && (pin == 19 || pin == 20)) { + return false; + } + if (allowlist_csv && allowlist_csv[0] != '\0') { in_policy = pin_in_allowlist(pin, allowlist_csv); } else { @@ -81,10 +87,13 @@ bool gpio_policy_pin_is_allowed(int pin) { #if defined(CONFIG_IDF_TARGET_ESP32) return pin_is_allowed_impl(pin, MIMI_GPIO_ALLOWED_CSV, - MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN, true); + MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN, true, false); +#elif defined(CONFIG_IDF_TARGET_ESP32S3) + return pin_is_allowed_impl(pin, MIMI_GPIO_ALLOWED_CSV, + MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN, false, true); #else return pin_is_allowed_impl(pin, MIMI_GPIO_ALLOWED_CSV, - MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN, false); + MIMI_GPIO_MIN_PIN, MIMI_GPIO_MAX_PIN, false, false); #endif } @@ -97,6 +106,13 @@ bool gpio_policy_pin_forbidden_hint(int pin, char *result, size_t result_len) pin); return true; } +#elif defined(CONFIG_IDF_TARGET_ESP32S3) + if (pin == 19 || pin == 20) { + snprintf(result, result_len, + "Error: pin %d is reserved for ESP32-S3 USB Serial/JTAG (GPIO19/20); choose a different pin", + pin); + return true; + } #else (void)pin; (void)result; From c1f13fa38c52edc52a32dbd9189eae8bcc1823cd Mon Sep 17 00:00:00 2001 From: Asklv Date: Mon, 9 Mar 2026 00:23:03 +0800 Subject: [PATCH 9/9] feat: allow lcd and rgb pins --- main/tools/gpio_policy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/tools/gpio_policy.h b/main/tools/gpio_policy.h index 1b70674..dc7d690 100644 --- a/main/tools/gpio_policy.h +++ b/main/tools/gpio_policy.h @@ -3,10 +3,10 @@ #include #include -/* GPIO pin range defaults for ESP32-S3 (safe user-accessible pins) */ +/* GPIO defaults for ESP32-S3-LCD-1.47B safe user-accessible pins */ #define MIMI_GPIO_MIN_PIN 1 #define MIMI_GPIO_MAX_PIN 21 -#define MIMI_GPIO_ALLOWED_CSV "" /* empty = use min/max range */ +#define MIMI_GPIO_ALLOWED_CSV "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,21,38,46" /** * Check if a pin is allowed for user GPIO operations.