From 837c3eacda201e4bc7948839c49d283eb69b28e9 Mon Sep 17 00:00:00 2001 From: Mitchell M Date: Wed, 1 Apr 2026 12:49:59 -0500 Subject: [PATCH] basic expander work --- drivers/expander.cpp | 148 +++++++++++++++++++-------- include/blk_box_drivers/expander.hpp | 12 +-- 2 files changed, 110 insertions(+), 50 deletions(-) diff --git a/drivers/expander.cpp b/drivers/expander.cpp index 2b22a0c..990caca 100644 --- a/drivers/expander.cpp +++ b/drivers/expander.cpp @@ -4,12 +4,16 @@ #include "blk_box_drivers/i2c.h" #include "driver/i2c_master.h" #include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "esp_log.h" #include "esp_err.h" static const char *TAG = "EXPANDER"; -i2c_master_dev_handle_t expander_i2c_dev_handle; +static TaskHandle_t expander_task_handle = NULL; + +static i2c_master_dev_handle_t expander_i2c_dev_handle; const static uint8_t REG_WHOAMI = 0x01; const static uint8_t REG_SW_VERSION = 0x02; @@ -27,15 +31,30 @@ const static uint8_t REG_HALL_SENSITIVITY = 0x31; const static uint8_t REG_CLOSE_SENSITIVITY = 0x32; const static uint8_t REG_SWITCH_TOUCH_EVENT = 0x33; -ExpanderPeripheral expander_peripheral; +ExpanderPeripheral expander_peripheral_singleton; // forward declarations +static void get_events(); static void handle_event(uint8_t event); static void handle_button_switch_event(uint8_t event); static void handle_keypad_event(uint8_t event); static void handle_touch_event(uint8_t event); static void handle_rfid_event(uint8_t event); static void handle_close_hal_event(uint8_t event); +static void expander_task(void *arg); + +// ISR handler +static void IRAM_ATTR expander_isr_handler(void *arg) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if (expander_task_handle != NULL) { + vTaskNotifyGiveFromISR(expander_task_handle, &xHigherPriorityTaskWoken); + } + + if (xHigherPriorityTaskWoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} void init_expander() { ESP_LOGI(TAG, "Initializing expander..."); @@ -50,11 +69,28 @@ void init_expander() { } }; + // TODO: replace all these ESP_ERROR_CHECK with proper error handling that doesn't just crash the program ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_main_bus_handle, &dev_config, &expander_i2c_dev_handle)); ESP_LOGD(TAG, "Expander I2C device added to bus"); - // TODO: setup interrupt on PIN_EXPANDER_INT + // setup interrupt on PIN_EXPANDER_INT + gpio_config_t io_conf = { + .pin_bit_mask = (1ULL << PIN_EXPANDER_INT), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_NEGEDGE + }; + ESP_ERROR_CHECK(gpio_config(&io_conf)); + + // Install ISR service (only call once in your program) + ESP_ERROR_CHECK(gpio_install_isr_service(0)); + + // Attach the ISR to the expander pin + ESP_ERROR_CHECK(gpio_isr_handler_add(PIN_EXPANDER_INT, expander_isr_handler, NULL)); + + // verify the expander connection status by reading the WHOAMI register uint8_t read_buf[2] = {0}; ESP_ERROR_CHECK(i2c_master_transmit_receive(expander_i2c_dev_handle, ®_WHOAMI, 1, read_buf, 1, EXPANDER_TIMEOUT_MS)); @@ -69,24 +105,51 @@ void init_expander() { ESP_ERROR_CHECK(i2c_master_transmit_receive(expander_i2c_dev_handle, ®_SW_VERSION, 1, read_buf, 2, EXPANDER_TIMEOUT_MS)); // init the peripheral struct - expander_peripheral.state_mutex = xSemaphoreCreateMutex(); - expander_peripheral.button_press_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(Button)); - expander_peripheral.button_release_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(Button)); - expander_peripheral.switch_flip_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(SwitchFlip)); - expander_peripheral.switch_touch_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(SwitchTouch)); - expander_peripheral.touch_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(TouchedReleased)); - expander_peripheral.keypad_press_events= xQueueCreate(EXPANDER_KEYPAD_QUEUE_SIZE, sizeof(KeypadKey)); - expander_peripheral.keypad_release_events= xQueueCreate(EXPANDER_KEYPAD_QUEUE_SIZE, sizeof(KeypadKey)); + expander_peripheral_singleton.state_mutex = xSemaphoreCreateMutex(); + expander_peripheral_singleton.button_press_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(Button)); + expander_peripheral_singleton.button_release_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(Button)); + expander_peripheral_singleton.switch_flip_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(SwitchFlip)); + expander_peripheral_singleton.switch_touch_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(SwitchTouch)); + expander_peripheral_singleton.touch_events= xQueueCreate(EXPANDER_EVENT_QUEUE_SIZE, sizeof(TouchedReleased)); + expander_peripheral_singleton.keypad_press_events= xQueueCreate(EXPANDER_KEYPAD_QUEUE_SIZE, sizeof(KeypadKey)); + expander_peripheral_singleton.keypad_release_events= xQueueCreate(EXPANDER_KEYPAD_QUEUE_SIZE, sizeof(KeypadKey)); ESP_LOGI(TAG, "Expander initialized! SW version: v%d.%d", read_buf[0], read_buf[1]); + + // Create the expander background worker task + BaseType_t task_created = xTaskCreate( + expander_task, + "expander_task", + 4096, + NULL, + tskIDLE_PRIORITY + 1, + &expander_task_handle + ); + + if (task_created != pdPASS) { + ESP_LOGE(TAG, "Failed to create expander task"); + } } -void get_events() { +static void expander_task(void *arg) { + (void)arg; + + while (true) { + get_events(); + + // Wait for interrupt notification (signal is sent when INT falls) + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + // loop continues and will process events once INT goes low + printf("WAKE\n"); + } +} + +static void get_events() { uint8_t recv; - do { + while (gpio_get_level(PIN_EXPANDER_INT) == 0) { ESP_ERROR_CHECK(i2c_master_transmit_receive(expander_i2c_dev_handle, ®_EVENT_QUEUE_POP, 1, &recv, 1, EXPANDER_TIMEOUT_MS)); handle_event(recv); - } while (gpio_get_level(PIN_EXPANDER_INT) == 0); + } } static void handle_event(uint8_t event) { @@ -95,7 +158,7 @@ static void handle_event(uint8_t event) { const uint8_t TOUCH = 0b010; const uint8_t RFID = 0b011; - ESP_LOGI(TAG, "Expander event: 0b%08b (0x%02X)", event, event); + ESP_LOGD(TAG, "Expander event: 0b%08b (0x%02X)", event, event); if (event == 0) { ESP_LOGE(TAG, "We read from event queue while it was empty!"); @@ -141,35 +204,35 @@ static void handle_button_switch_event(uint8_t event) { Switch sw = static_cast(number); SwitchFlip sw_flip = SwitchFlip(sw, pressed); - if (xQueueSendToBack(expander_peripheral.switch_flip_events, &sw_flip, 0) != pdTRUE) { + if (xQueueSendToBack(expander_peripheral_singleton.switch_flip_events, &sw_flip, 0) != pdTRUE) { ESP_LOGE(TAG, "Failed to send switch flip event!"); } - xSemaphoreTake(expander_peripheral.state_mutex, portMAX_DELAY); + xSemaphoreTake(expander_peripheral_singleton.state_mutex, portMAX_DELAY); if (pressed) { // set - expander_peripheral.state.switch_state |= 1 << number; + expander_peripheral_singleton.state.switch_state |= 1 << number; } else { // clear - expander_peripheral.state.switch_state &= ~(1 << number); + expander_peripheral_singleton.state.switch_state &= ~(1 << number); } - xSemaphoreGive(expander_peripheral.state_mutex); + xSemaphoreGive(expander_peripheral_singleton.state_mutex); } else { // button Button button = static_cast