Merge pull request #127 from IRONICBo/feat/gpio-skill-support
This commit is contained in:
@@ -20,6 +20,8 @@ idf_component_register(
|
|||||||
"tools/tool_web_search.c"
|
"tools/tool_web_search.c"
|
||||||
"tools/tool_get_time.c"
|
"tools/tool_get_time.c"
|
||||||
"tools/tool_files.c"
|
"tools/tool_files.c"
|
||||||
|
"tools/tool_gpio.c"
|
||||||
|
"tools/gpio_policy.c"
|
||||||
"skills/skill_loader.c"
|
"skills/skill_loader.c"
|
||||||
"onboard/wifi_onboard.c"
|
"onboard/wifi_onboard.c"
|
||||||
INCLUDE_DIRS
|
INCLUDE_DIRS
|
||||||
@@ -27,5 +29,5 @@ idf_component_register(
|
|||||||
REQUIRES
|
REQUIRES
|
||||||
nvs_flash esp_wifi esp_netif esp_http_client esp_http_server
|
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_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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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"
|
"- 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_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_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"
|
"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"
|
"Use tools when needed. Provide your final answer as text after using tools.\n\n"
|
||||||
"## Memory\n"
|
"## Memory\n"
|
||||||
"You have persistent memory stored on local flash:\n"
|
"You have persistent memory stored on local flash:\n"
|
||||||
|
|||||||
@@ -117,6 +117,9 @@
|
|||||||
#define MIMI_HEARTBEAT_FILE MIMI_SPIFFS_BASE "/HEARTBEAT.md"
|
#define MIMI_HEARTBEAT_FILE MIMI_SPIFFS_BASE "/HEARTBEAT.md"
|
||||||
#define MIMI_HEARTBEAT_INTERVAL_MS (30 * 60 * 1000)
|
#define MIMI_HEARTBEAT_INTERVAL_MS (30 * 60 * 1000)
|
||||||
|
|
||||||
|
/* GPIO */
|
||||||
|
#define MIMI_GPIO_CONFIG_SECTION 1 /* enable GPIO tools */
|
||||||
|
|
||||||
/* Skills */
|
/* Skills */
|
||||||
#define MIMI_SKILLS_PREFIX MIMI_SPIFFS_BASE "/skills/"
|
#define MIMI_SKILLS_PREFIX MIMI_SPIFFS_BASE "/skills/"
|
||||||
|
|
||||||
|
|||||||
123
main/tools/gpio_policy.c
Normal file
123
main/tools/gpio_policy.c
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#include "tools/gpio_policy.h"
|
||||||
|
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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 block_esp32s3_usb_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 {
|
||||||
|
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, 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, 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;
|
||||||
|
}
|
||||||
|
#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;
|
||||||
|
(void)result_len;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
22
main/tools/gpio_policy.h
Normal file
22
main/tools/gpio_policy.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* 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 "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.
|
||||||
|
* 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);
|
||||||
194
main/tools/tool_gpio.c
Normal file
194
main/tools/tool_gpio.c
Normal file
@@ -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 <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
27
main/tools/tool_gpio.h
Normal file
27
main/tools/tool_gpio.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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": <int>, "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": <int>}
|
||||||
|
*/
|
||||||
|
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);
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "tools/tool_get_time.h"
|
#include "tools/tool_get_time.h"
|
||||||
#include "tools/tool_files.h"
|
#include "tools/tool_files.h"
|
||||||
#include "tools/tool_cron.h"
|
#include "tools/tool_cron.h"
|
||||||
|
#include "tools/tool_gpio.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
|
|
||||||
static const char *TAG = "tools";
|
static const char *TAG = "tools";
|
||||||
|
|
||||||
#define MAX_TOOLS 12
|
#define MAX_TOOLS 16
|
||||||
|
|
||||||
static mimi_tool_t s_tools[MAX_TOOLS];
|
static mimi_tool_t s_tools[MAX_TOOLS];
|
||||||
static int s_tool_count = 0;
|
static int s_tool_count = 0;
|
||||||
@@ -176,6 +177,43 @@ esp_err_t tool_registry_init(void)
|
|||||||
};
|
};
|
||||||
register_tool(&cr);
|
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();
|
build_tools_json();
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Tool registry initialized");
|
ESP_LOGI(TAG, "Tool registry initialized");
|
||||||
|
|||||||
37
spiffs_data/skills/gpio-control.md
Normal file
37
spiffs_data/skills/gpio-control.md
Normal file
@@ -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."
|
||||||
Reference in New Issue
Block a user