/* * Copyright (c) 2021 Espressif Systems (Shanghai) CO LTD * All rights reserved. * * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Espressif Systems * integrated circuit in a product or a software update for such product, * must reproduce the above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * 4. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "switch_driver.h" /** * @brief: * This example code shows how to configure light switch with attribute as well as button switch handler. * * @note: Currently only support toggle switch functionality available * * @note: * For other possible switch functions (on/off,level up/down,step up/down). User need to implement and create them by themselves */ static xQueueHandle gpio_evt_queue = NULL; /* button function pair, should be defined in switch example source file */ static switch_func_pair_t *switch_func_pair; /* call back function pointer */ static esp_switch_callback_t func_ptr; /* which button is pressed */ static uint8_t switch_num; static const char *TAG = "ESP_ZB_SWITCH"; static void IRAM_ATTR gpio_isr_handler(void *arg) { xQueueSendFromISR(gpio_evt_queue, (switch_func_pair_t *)arg, NULL); } /** * @brief Enable GPIO (switchs refer to) isr * * @param enabled enable isr if true. */ static void switch_driver_gpios_intr_enabled(bool enabled) { for (int i = 0; i < switch_num; ++i) { if (enabled) { gpio_intr_enable((switch_func_pair + i)->pin); } else { gpio_intr_disable((switch_func_pair + i)->pin); } } } /** * @brief Tasks for checking the button event and debounce the switch state * * @param arg Unused value. */ static void switch_driver_button_detected(void *arg) { gpio_num_t io_num = GPIO_NUM_NC; switch_func_pair_t button_func_pair; static switch_state_t switch_state = SWITCH_IDLE; bool evt_flag = false; for (;;) { /* check if there is any queue received, if yes read out the button_func_pair */ if (xQueueReceive(gpio_evt_queue, &button_func_pair, portMAX_DELAY)) { io_num = button_func_pair.pin; switch_driver_gpios_intr_enabled(false); evt_flag = true; } while (evt_flag) { bool value = gpio_get_level(io_num); switch (switch_state) { case SWITCH_IDLE: switch_state = (value == GPIO_INPUT_LEVEL_ON) ? SWITCH_PRESS_DETECTED : SWITCH_IDLE; break; case SWITCH_PRESS_DETECTED: switch_state = (value == GPIO_INPUT_LEVEL_ON) ? SWITCH_PRESS_DETECTED : SWITCH_RELEASE_DETECTED; break; case SWITCH_RELEASE_DETECTED: switch_state = SWITCH_IDLE; /* callback to button_handler */ (*func_ptr)(button_func_pair); break; default: break; } if (switch_state == SWITCH_IDLE) { switch_driver_gpios_intr_enabled(true); evt_flag = false; break; } vTaskDelay(10 / portTICK_RATE_MS); } } } /** * @brief init GPIO configuration as well as isr * * @param button_func_pair pointer of the button pair. * @param button_num number of button pair. */ static bool switch_driver_gpio_init(switch_func_pair_t *button_func_pair, uint8_t button_num) { gpio_config_t io_conf = {}; switch_func_pair = button_func_pair; switch_num = button_num; uint64_t pin_bit_mask = 0; /* set up button func pair pin mask */ for (int i = 0; i < button_num; ++i) { pin_bit_mask |= (1ULL << (button_func_pair + i)->pin); } /* interrupt of falling edge */ io_conf.intr_type = GPIO_INTR_NEGEDGE; io_conf.pin_bit_mask = pin_bit_mask; io_conf.mode = GPIO_MODE_INPUT; io_conf.pull_up_en = 1; /* configure GPIO with the given settings */ gpio_config(&io_conf); /* create a queue to handle gpio event from isr */ gpio_evt_queue = xQueueCreate(10, sizeof(switch_func_pair_t)); if ( gpio_evt_queue == 0) { ESP_LOGE(TAG, "Queue was not created and must not be used"); return false; } /* start gpio task */ xTaskCreate(switch_driver_button_detected, "button_detected", 2048, NULL, 10, NULL); /* install gpio isr service */ gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); for (int i = 0; i < button_num; ++i) { gpio_isr_handler_add((button_func_pair + i)->pin, gpio_isr_handler, (void *) (button_func_pair + i)); } return true; } bool switch_driver_init(switch_func_pair_t *button_func_pair, uint8_t button_num, esp_switch_callback_t cb) { if (!switch_driver_gpio_init(button_func_pair, button_num)) { return false; } func_ptr = cb; return true; }