#include "gpio_controller.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include static const char *TAG = TAG_GPIO; // Runtime GPIO state storage static bmc_gpio_state_t gpio_states[BMC_GPIO_COUNT]; static bool gpio_initialized = false; esp_err_t gpio_controller_init(void) { ESP_LOGI(TAG, "Initializing GPIO controller"); // Initialize all GPIOs from default configuration for (int i = 0; i < BMC_GPIO_COUNT; i++) { const bmc_gpio_def_t *def = &bmc_gpio_defaults[i]; // Copy configuration to runtime state gpio_states[i].pin = def->pin; gpio_states[i].name = def->name; gpio_states[i].mode = def->mode; gpio_states[i].inverted = def->inverted; gpio_states[i].is_input_only = def->is_input_only; // Skip configuration for input-only pins when mode is output if (def->is_input_only && def->mode == GPIO_MODE_OUTPUT) { gpio_states[i].mode = GPIO_MODE_INPUT; ESP_LOGW(TAG, "Pin %d (%s) is input-only, forcing input mode", def->pin, def->name); } // Configure GPIO gpio_config_t io_conf = { .pin_bit_mask = (1ULL << def->pin), .mode = gpio_states[i].mode, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; // Enable pull-up for inputs by default if (gpio_states[i].mode == GPIO_MODE_INPUT) { io_conf.pull_up_en = GPIO_PULLUP_ENABLE; } esp_err_t ret = gpio_config(&io_conf); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to configure GPIO %d: %s", def->pin, esp_err_to_name(ret)); return ret; } // Set default value for outputs if (gpio_states[i].mode == GPIO_MODE_OUTPUT) { int value = def->default_value; if (def->inverted) { value = !value; } gpio_set_level(def->pin, value); gpio_states[i].value = value; } else { // Read current input value gpio_states[i].value = gpio_get_level(def->pin); } ESP_LOGI(TAG, "GPIO %d (%s) initialized: mode=%d, value=%d, inverted=%d", def->pin, def->name, gpio_states[i].mode, gpio_states[i].value, gpio_states[i].inverted); } gpio_initialized = true; ESP_LOGI(TAG, "GPIO controller initialized with %d pins", BMC_GPIO_COUNT); return ESP_OK; } int gpio_find_index(gpio_num_t pin) { for (int i = 0; i < BMC_GPIO_COUNT; i++) { if (gpio_states[i].pin == pin) { return i; } } return -1; } const char *gpio_get_name(gpio_num_t pin) { int idx = gpio_find_index(pin); if (idx >= 0) { return gpio_states[idx].name; } return NULL; } esp_err_t gpio_get_all_states(bmc_gpio_state_t *states) { if (!gpio_initialized) { return ESP_ERR_INVALID_STATE; } // Update input values for (int i = 0; i < BMC_GPIO_COUNT; i++) { if (gpio_states[i].mode == GPIO_MODE_INPUT || gpio_states[i].mode == GPIO_MODE_INPUT_OUTPUT) { gpio_states[i].value = gpio_get_level(gpio_states[i].pin); } } memcpy(states, gpio_states, sizeof(gpio_states)); return ESP_OK; } esp_err_t gpio_get_state(gpio_num_t pin, bmc_gpio_state_t *state) { int idx = gpio_find_index(pin); if (idx < 0) { return ESP_ERR_NOT_FOUND; } // Update input value if (gpio_states[idx].mode == GPIO_MODE_INPUT || gpio_states[idx].mode == GPIO_MODE_INPUT_OUTPUT) { gpio_states[idx].value = gpio_get_level(pin); } memcpy(state, &gpio_states[idx], sizeof(bmc_gpio_state_t)); return ESP_OK; } int gpio_read(gpio_num_t pin) { int idx = gpio_find_index(pin); if (idx < 0) { return -1; } int level = gpio_get_level(pin); // Apply inverted logic for display if (gpio_states[idx].inverted) { level = !level; } gpio_states[idx].value = level; return level; } esp_err_t gpio_write(gpio_num_t pin, int value) { int idx = gpio_find_index(pin); if (idx < 0) { ESP_LOGW(TAG, "GPIO %d not found in configuration", pin); return ESP_ERR_NOT_FOUND; } if (gpio_states[idx].is_input_only) { ESP_LOGW(TAG, "GPIO %d (%s) is input-only", pin, gpio_states[idx].name); return ESP_ERR_NOT_SUPPORTED; } // Ensure GPIO is configured as output if (gpio_states[idx].mode != GPIO_MODE_OUTPUT && gpio_states[idx].mode != GPIO_MODE_INPUT_OUTPUT) { ESP_LOGW(TAG, "GPIO %d (%s) is not configured as output", pin, gpio_states[idx].name); return ESP_ERR_INVALID_STATE; } // Apply inverted logic int actual_value = value; if (gpio_states[idx].inverted) { actual_value = !value; } esp_err_t ret = gpio_set_level(pin, actual_value); if (ret == ESP_OK) { gpio_states[idx].value = value; ESP_LOGD(TAG, "GPIO %d (%s) set to %d (actual: %d)", pin, gpio_states[idx].name, value, actual_value); } else { ESP_LOGE(TAG, "Failed to set GPIO %d: %s", pin, esp_err_to_name(ret)); } return ret; } esp_err_t gpio_configure(gpio_num_t pin, gpio_mode_t mode) { int idx = gpio_find_index(pin); if (idx < 0) { return ESP_ERR_NOT_FOUND; } // Check if trying to set input-only pin as output if (gpio_states[idx].is_input_only && mode == GPIO_MODE_OUTPUT) { ESP_LOGW(TAG, "GPIO %d (%s) is input-only, cannot set as output", pin, gpio_states[idx].name); return ESP_ERR_NOT_SUPPORTED; } gpio_config_t io_conf = { .pin_bit_mask = (1ULL << pin), .mode = mode, .pull_up_en = (mode == GPIO_MODE_INPUT) ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; esp_err_t ret = gpio_config(&io_conf); if (ret == ESP_OK) { gpio_states[idx].mode = mode; ESP_LOGI(TAG, "GPIO %d (%s) mode changed to %d", pin, gpio_states[idx].name, mode); } return ret; } esp_err_t gpio_toggle(gpio_num_t pin) { int idx = gpio_find_index(pin); if (idx < 0) { return ESP_ERR_NOT_FOUND; } int current = gpio_states[idx].value; return gpio_write(pin, !current); } // ============================================================================ // Power Control Functions // ============================================================================ esp_err_t gpio_power_on(void) { ESP_LOGI(TAG, "Power ON sequence initiated"); gpio_write(BMC_GPIO_POWER, 1); return ESP_OK; } esp_err_t gpio_power_off(void) { ESP_LOGI(TAG, "Power OFF sequence initiated"); gpio_write(BMC_GPIO_POWER, 0); return ESP_OK; } esp_err_t gpio_reset(void) { ESP_LOGI(TAG, "Reset sequence initiated"); // Pulse the POWER signal gpio_write(BMC_GPIO_POWER, 0); vTaskDelay(pdMS_TO_TICKS(BMC_RESET_PULSE_MS)); gpio_write(BMC_GPIO_POWER, 1); ESP_LOGI(TAG, "Reset completed"); return ESP_OK; } bool gpio_power_status(void) { int level = gpio_read(BMC_GPIO_POWER); return (level == 1); }