#pragma once #include #include #include #include "config_key.h" #include "driver/gpio.h" #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/task.h" #include "gui.c" #include "light.c" #include "pca9555.c" #define ENCODER_0_CLK_PORT_IO CONFIG_ENCODER_0_CLK_PORT_IO #define ENCODER_0_DT_PORT_IO CONFIG_ENCODER_0_DT_PORT_IO #define ENCODER_0_SW_PORT_IO CONFIG_ENCODER_0_SW_PORT_IO #define ENCODER_1_CLK_PORT_IO CONFIG_ENCODER_1_CLK_PORT_IO #define ENCODER_1_DT_PORT_IO CONFIG_ENCODER_1_DT_PORT_IO #define ENCODER_1_SW_PORT_IO CONFIG_ENCODER_1_SW_PORT_IO #define ENCODER_INT_GPIO GPIO_NUM_3 #define ESP_INTR_FLAG_DEFAULT 0 #define RISING_EDGE_NOT_ROTATING 0b00000000 #define RISING_EDGE_CLOCKWISE 0b10000000 #define RISING_EDGE_COUNTER_CLOCKWISE 0b11000000 #define RISING_EDGE_ROTATING_MASK 0b11000000 #define FALLING_EDGE_NOT_ROTATING 0b00000000 #define FALLING_EDGE_CLOCKWISE 0b00100000 #define FALLING_EDGE_COUNTER_CLOCKWISE 0b00110000 #define FALLING_EDGE_ROTATING_MASK 0b00110000 static const char *UI_INPUT_TAG = "UiInput"; static QueueHandle_t ui_input_event = NULL; static QueueHandle_t ui_input_raw_event = NULL; typedef struct encoder_state { e_ui_input_raw_key_t key; uint8_t bits; // rising_edge_rotation_direction, // falling_edge_rotation_direction, dt, clk, sw uint8_t value; // dt, clk, sw } encoder_state_t; static encoder_state_t encoder_0_state = {.key = ui_input_raw_key_encoder_0, .bits = 0}; static encoder_state_t encoder_1_state = {.key = ui_input_raw_key_encoder_1, .bits = 0}; uint8_t level_byte; int8_t delta = 0; int64_t ec0_last_time = 0; int64_t ec1_last_time = 0; int64_t ec0_interval = 0; int64_t ec1_interval = 0; static void IRAM_ATTR gpio_isr_handler(void *arg) { xQueueSendFromISR(ui_input_raw_event, NULL, NULL); } static void ui_input_update_embedded_display(void *arg) { s_ui_input_t input; char changing_str[20] = "NC"; for (;;) { if (xQueueReceive(ui_input_event, &input, portMAX_DELAY)) { switch (input.key) { case ui_input_key_display_0_brightness: sprintf(changing_str, "Dis0: % 3d", input.value); break; case ui_input_key_display_1_brightness: sprintf(changing_str, "Dis1: % 3d", input.value); break; case ui_input_key_computer_volume: sprintf(changing_str, "CVol: % 3d", input.value); break; case ui_input_key_display_ambient_lighting_level: break; case ui_input_key_display_ambient_lighting_mode: sprintf(changing_str, "ALMd: % 3d", input.value); break; case ui_input_key_display_0_mode: sprintf(changing_str, "Dis0M: % 2d", input.value); break; case ui_input_key_display_1_mode: sprintf(changing_str, "Dis1M: % 2d", input.value); break; default: strcpy(changing_str, "NC"); break; } ESP_LOGI(UI_INPUT_TAG, "%s", changing_str); } } } static void encoder_value_change(encoder_state_t *state) { if ((state->value >> 1 & 1) == (state->bits >> 1 & 1)) { return; } state->bits = (state->bits & 0b11111000) | (state->value & 0b111); if (state->value >> 1 & 1) { state->bits = (state->bits & (~RISING_EDGE_ROTATING_MASK)) | 0x80 | ~(state->value >> 2 & 1) << 6; if (((state->bits & FALLING_EDGE_ROTATING_MASK) >> 4) == ((state->bits & RISING_EDGE_ROTATING_MASK) >> 6)) { if ((state->bits & RISING_EDGE_ROTATING_MASK) == RISING_EDGE_CLOCKWISE) { delta = -1; } else { delta = 1; } state->bits = (state->bits & (~FALLING_EDGE_ROTATING_MASK)) | FALLING_EDGE_NOT_ROTATING; } else { delta = 0; if ((state->bits & FALLING_EDGE_ROTATING_MASK) != FALLING_EDGE_NOT_ROTATING) { state->bits = (state->bits & (~FALLING_EDGE_ROTATING_MASK)) | FALLING_EDGE_NOT_ROTATING; } } } else { state->bits = (state->bits & (~FALLING_EDGE_ROTATING_MASK)) | 0x20 | (state->value >> 2 & 1) << 4; if (((state->bits & FALLING_EDGE_ROTATING_MASK) >> 4) == ((state->bits & RISING_EDGE_ROTATING_MASK) >> 6)) { if ((state->bits & FALLING_EDGE_ROTATING_MASK) == FALLING_EDGE_CLOCKWISE) { delta = -1; } else { delta = 1; } state->bits = (state->bits & (~RISING_EDGE_ROTATING_MASK)) | RISING_EDGE_NOT_ROTATING; } else { delta = 0; if ((state->bits & RISING_EDGE_ROTATING_MASK) != RISING_EDGE_NOT_ROTATING) { state->bits = (state->bits & (~RISING_EDGE_ROTATING_MASK)) | RISING_EDGE_NOT_ROTATING; } } } if (delta == 0) { return; } s_ui_input_t event = {.value = delta}; if (state->key == ui_input_raw_key_encoder_0) { ec0_interval = esp_timer_get_time() - ec0_last_time; if (ec0_interval < 10000) { // 100ms event.value = event.value * 5; } else if (ec0_interval < 20000) { // 100ms event.value = event.value * 4; } else if (ec0_interval < 50000) { // 100ms event.value = event.value * 3; } else if (ec0_interval < 100000) { // 100ms event.value = event.value * 2; } ec0_last_time = esp_timer_get_time(); if (state->value & 1) { event.key = ui_input_key_computer_volume; } else { event.key = ui_input_key_display_1_brightness; } } else if (state->key == ui_input_raw_key_encoder_1) { if (state->value & 1) { ec1_interval = esp_timer_get_time() - ec1_last_time; if (ec1_interval < 20000) { // 100ms event.value = event.value * 7; } else if (ec1_interval < 30000) { // 100ms event.value = event.value * 5; } else if (ec1_interval < 40000) { // 100ms event.value = event.value * 3; } else if (ec1_interval < 50000) { event.value = event.value * 2; } ec1_last_time = esp_timer_get_time(); event.key = ui_input_key_display_ambient_lighting_level; led_strip_set_brightness(display_ambient_lighting_level + event.value); gui_change_strip_level(display_ambient_lighting_level); } else { event.key = ui_input_key_display_0_brightness; } } xQueueSend(ui_input_event, &event, NULL); ESP_LOGD(UI_INPUT_TAG, "key: %d, delta: %d. delay: %lld, %lld", state->key, event.value, ec0_interval, ec1_interval); } static void ui_input_raw_handler(void *arg) { for (;;) { if (xQueueReceive(ui_input_raw_event, NULL, portMAX_DELAY)) { pca9555_read_one_input(PCA95555_CMD_INPUT_PORT_1, &level_byte); encoder_0_state.value = level_byte & 0x7; encoder_value_change(&encoder_0_state); encoder_1_state.value = level_byte >> 3 & 0x7; encoder_value_change(&encoder_1_state); } } } void ui_input_init(void) { // zero-initialize the config structure. gpio_config_t io_conf = {}; io_conf.mode = GPIO_MODE_INPUT; io_conf.pull_up_en = 1; io_conf.pull_down_en = 0; // interrupt of rising edge io_conf.intr_type = GPIO_INTR_NEGEDGE; io_conf.pin_bit_mask = 1ULL << ENCODER_INT_GPIO; gpio_config(&io_conf); // start encoder task ui_input_event = xQueueCreate(5, sizeof(s_ui_input_t)); ui_input_raw_event = xQueueCreate(10, 0); // hook isr handler for specific gpio pin gpio_isr_handler_add(ENCODER_INT_GPIO, gpio_isr_handler, NULL); // xTaskCreate(ui_input_update_embedded_display, "ui_input_event", 2048, NULL, // 10, NULL); xTaskCreate(ui_input_raw_handler, "ui_input_event", 2048, NULL, 10, NULL); }