feat: implement gpio_write, gpio_read, gpio_read_all handlers

This commit is contained in:
Asklv
2026-03-07 16:15:31 +08:00
parent e278264133
commit 46dc186a80

194
main/tools/tool_gpio.c Normal file
View 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;
}