Files
esp32-zigbee/main/switch_driver.c
T
2026-01-15 23:11:17 +00:00

256 lines
9.2 KiB
C

/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* 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 "esp_zigbee_core.h"
#include "switch_driver.h"
#include "device_config.h"
#include "led_indicator.h"
/* Estado atual do relay */
static bool relay_state = false;
/**
* @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 QueueHandle_t 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 (switches 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;
uint32_t press_start_time = 0;
const uint32_t LONG_PRESS_DURATION_MS = 5000; /* 5 segundos para factory reset */
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:
if (value == GPIO_INPUT_LEVEL_ON) {
switch_state = SWITCH_PRESS_DETECTED;
press_start_time = xTaskGetTickCount() * portTICK_PERIOD_MS;
}
break;
case SWITCH_PRESS_DETECTED:
if (value == GPIO_INPUT_LEVEL_ON) {
/* Verifica se botão está pressionado há mais de 5 segundos */
uint32_t current_time = xTaskGetTickCount() * portTICK_PERIOD_MS;
if ((current_time - press_start_time) >= LONG_PRESS_DURATION_MS) {
ESP_LOGI(TAG, "Botão pressionado por 5 segundos - Iniciando factory reset...");
ESP_LOGI(TAG, "Dispositivo entrará em modo de pareamento após reiniciar");
/* Animação arco-íris no LED para feedback visual */
led_indicator_set_state(LED_FACTORY_RESET);
vTaskDelay(pdMS_TO_TICKS(2000)); /* 2 segundos de animação */
/* Factory reset - limpa NVS e reinicia */
esp_zb_factory_reset();
switch_state = SWITCH_IDLE;
}
} else {
switch_state = SWITCH_RELEASE_DETECTED;
}
break;
case SWITCH_RELEASE_DETECTED:
switch_state = SWITCH_IDLE;
/* Verifica se foi pressionamento curto (toggle normal) */
uint32_t press_duration = (xTaskGetTickCount() * portTICK_PERIOD_MS) - press_start_time;
if (press_duration < LONG_PRESS_DURATION_MS) {
/* callback to button_handler para toggle normal */
(*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_PERIOD_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", 4096, 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;
}
void relay_driver_init(void)
{
#ifdef DEVICE_TYPE_RELAY
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << GPIO_OUTPUT_RELAY);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);
/* Inicializa relay desligado */
gpio_set_level(GPIO_OUTPUT_RELAY, 0);
relay_state = false;
ESP_LOGI(TAG, "Relay inicializado no GPIO %d", GPIO_OUTPUT_RELAY);
#endif
}
void relay_set_state(bool state)
{
#ifdef DEVICE_TYPE_RELAY
relay_state = state;
gpio_set_level(GPIO_OUTPUT_RELAY, state ? 1 : 0);
/* Atualiza LED para refletir estado do relay */
led_indicator_set_state(state ? LED_RELAY_ON : LED_RELAY_OFF);
ESP_LOGI(TAG, "Relay %s", state ? "LIGADO" : "DESLIGADO");
#endif
}
void relay_toggle(void)
{
#ifdef DEVICE_TYPE_RELAY
relay_set_state(!relay_state);
#endif
}
bool relay_get_state(void)
{
#ifdef DEVICE_TYPE_RELAY
return relay_state;
#else
return false;
#endif
}