forked from xiaozhi/xiaozhi-esp32
Support adjust volume with knob in SenseCAP Watcher (#399)
* fix typo, add missing prefix `CONFIG` to `ESP_TASK_WDT_TIMEOUT_S` * add KNOB gpio spec to sensecap config * create new knob component * implement ajust output volume with knob * modify function name to UpperCamelCase * Tidy up comments and logs
This commit is contained in:
52
main/boards/common/knob.cc
Normal file
52
main/boards/common/knob.cc
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#include "knob.h"
|
||||||
|
|
||||||
|
static const char* TAG = "Knob";
|
||||||
|
|
||||||
|
Knob::Knob(gpio_num_t pin_a, gpio_num_t pin_b) {
|
||||||
|
knob_config_t config = {
|
||||||
|
.default_direction = 0,
|
||||||
|
.gpio_encoder_a = static_cast<uint8_t>(pin_a),
|
||||||
|
.gpio_encoder_b = static_cast<uint8_t>(pin_b),
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
knob_handle_ = iot_knob_create(&config);
|
||||||
|
if (knob_handle_ == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create knob instance");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = iot_knob_register_cb(knob_handle_, KNOB_LEFT, knob_callback, this);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to register left callback: %s", esp_err_to_name(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = iot_knob_register_cb(knob_handle_, KNOB_RIGHT, knob_callback, this);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to register right callback: %s", esp_err_to_name(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Knob initialized with pins A:%d B:%d", pin_a, pin_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
Knob::~Knob() {
|
||||||
|
if (knob_handle_ != NULL) {
|
||||||
|
iot_knob_delete(knob_handle_);
|
||||||
|
knob_handle_ = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Knob::OnRotate(std::function<void(bool)> callback) {
|
||||||
|
on_rotate_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Knob::knob_callback(void* arg, void* data) {
|
||||||
|
Knob* knob = static_cast<Knob*>(data);
|
||||||
|
knob_event_t event = iot_knob_get_event(arg);
|
||||||
|
|
||||||
|
if (knob->on_rotate_) {
|
||||||
|
knob->on_rotate_(event == KNOB_RIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
main/boards/common/knob.h
Normal file
25
main/boards/common/knob.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef KNOB_H_
|
||||||
|
#define KNOB_H_
|
||||||
|
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <iot_knob.h>
|
||||||
|
|
||||||
|
class Knob {
|
||||||
|
public:
|
||||||
|
Knob(gpio_num_t pin_a, gpio_num_t pin_b);
|
||||||
|
~Knob();
|
||||||
|
|
||||||
|
void OnRotate(std::function<void(bool)> callback);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void knob_callback(void* arg, void* data);
|
||||||
|
|
||||||
|
knob_handle_t knob_handle_;
|
||||||
|
gpio_num_t pin_a_;
|
||||||
|
gpio_num_t pin_b_;
|
||||||
|
std::function<void(bool)> on_rotate_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KNOB_H_
|
||||||
@@ -114,7 +114,7 @@ private:
|
|||||||
ESP_ERROR_CHECK(spi_bus_initialize(QSPI_LCD_HOST, &bus_config, SPI_DMA_CH_AUTO));
|
ESP_ERROR_CHECK(spi_bus_initialize(QSPI_LCD_HOST, &bus_config, SPI_DMA_CH_AUTO));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Initializespd2010Display() {
|
void InitializeSpd2010Display() {
|
||||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||||
esp_lcd_panel_handle_t panel = nullptr;
|
esp_lcd_panel_handle_t panel = nullptr;
|
||||||
|
|
||||||
@@ -224,7 +224,7 @@ public:
|
|||||||
InitializeI2c();
|
InitializeI2c();
|
||||||
InitializeTca9554();
|
InitializeTca9554();
|
||||||
InitializeSpi();
|
InitializeSpi();
|
||||||
Initializespd2010Display();
|
InitializeSpd2010Display();
|
||||||
InitializeButtons();
|
InitializeButtons();
|
||||||
InitializeIot();
|
InitializeIot();
|
||||||
GetBacklight()->RestoreBrightness();
|
GetBacklight()->RestoreBrightness();
|
||||||
|
|||||||
@@ -54,7 +54,8 @@
|
|||||||
#define BSP_PWR_START_UP (BSP_PWR_SDCARD | BSP_PWR_LCD | BSP_PWR_SYSTEM | BSP_PWR_AI_CHIP | BSP_PWR_CODEC_PA | BSP_PWR_GROVE | BSP_PWR_BAT_ADC)
|
#define BSP_PWR_START_UP (BSP_PWR_SDCARD | BSP_PWR_LCD | BSP_PWR_SYSTEM | BSP_PWR_AI_CHIP | BSP_PWR_CODEC_PA | BSP_PWR_GROVE | BSP_PWR_BAT_ADC)
|
||||||
|
|
||||||
#define BSP_KNOB_BTN (IO_EXPANDER_PIN_NUM_3)
|
#define BSP_KNOB_BTN (IO_EXPANDER_PIN_NUM_3)
|
||||||
|
#define BSP_KNOB_A_PIN GPIO_NUM_41
|
||||||
|
#define BSP_KNOB_B_PIN GPIO_NUM_42
|
||||||
|
|
||||||
/* QSPI */
|
/* QSPI */
|
||||||
#define BSP_SPI3_HOST_PCLK (GPIO_NUM_7)
|
#define BSP_SPI3_HOST_PCLK (GPIO_NUM_7)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "font_awesome_symbols.h"
|
#include "font_awesome_symbols.h"
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
#include "button.h"
|
#include "button.h"
|
||||||
|
#include "knob.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "led/single_led.h"
|
#include "led/single_led.h"
|
||||||
#include "iot/thing_manager.h"
|
#include "iot/thing_manager.h"
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
#include <driver/spi_common.h>
|
#include <driver/spi_common.h>
|
||||||
#include <wifi_station.h>
|
#include <wifi_station.h>
|
||||||
#include <iot_button.h>
|
#include <iot_button.h>
|
||||||
|
#include <iot_knob.h>
|
||||||
#include <esp_io_expander_tca95xx_16bit.h>
|
#include <esp_io_expander_tca95xx_16bit.h>
|
||||||
#include <esp_sleep.h>
|
#include <esp_sleep.h>
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@ class SensecapWatcher : public WifiBoard {
|
|||||||
private:
|
private:
|
||||||
i2c_master_bus_handle_t i2c_bus_;
|
i2c_master_bus_handle_t i2c_bus_;
|
||||||
LcdDisplay* display_;
|
LcdDisplay* display_;
|
||||||
|
std::unique_ptr<Knob> knob_;
|
||||||
esp_io_expander_handle_t io_exp_handle;
|
esp_io_expander_handle_t io_exp_handle;
|
||||||
button_handle_t btns;
|
button_handle_t btns;
|
||||||
PowerSaveTimer* power_save_timer_;
|
PowerSaveTimer* power_save_timer_;
|
||||||
@@ -115,6 +118,41 @@ private:
|
|||||||
assert(ret == ESP_OK);
|
assert(ret == ESP_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OnKnobRotate(bool clockwise) {
|
||||||
|
auto codec = GetAudioCodec();
|
||||||
|
int current_volume = codec->output_volume();
|
||||||
|
int new_volume = current_volume + (clockwise ? 5 : -5);
|
||||||
|
|
||||||
|
// 确保音量在有效范围内
|
||||||
|
if (new_volume > 100) {
|
||||||
|
new_volume = 100;
|
||||||
|
ESP_LOGW(TAG, "Volume reached maximum limit: %d", new_volume);
|
||||||
|
} else if (new_volume < 0) {
|
||||||
|
new_volume = 0;
|
||||||
|
ESP_LOGW(TAG, "Volume reached minimum limit: %d", new_volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
codec->SetOutputVolume(new_volume);
|
||||||
|
ESP_LOGI(TAG, "Volume changed from %d to %d", current_volume, new_volume);
|
||||||
|
|
||||||
|
// 显示通知前检查实际变化
|
||||||
|
if (new_volume != codec->output_volume()) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set volume! Expected:%d Actual:%d",
|
||||||
|
new_volume, codec->output_volume());
|
||||||
|
}
|
||||||
|
GetDisplay()->ShowNotification("音量: " + std::to_string(codec->output_volume()));
|
||||||
|
power_save_timer_->WakeUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeKnob() {
|
||||||
|
knob_ = std::make_unique<Knob>(BSP_KNOB_A_PIN, BSP_KNOB_B_PIN);
|
||||||
|
knob_->OnRotate([this](bool clockwise) {
|
||||||
|
ESP_LOGD(TAG, "Knob rotation detected. Clockwise:%s", clockwise ? "true" : "false");
|
||||||
|
OnKnobRotate(clockwise);
|
||||||
|
});
|
||||||
|
ESP_LOGI(TAG, "Knob initialized with pins A:%d B:%d", BSP_KNOB_A_PIN, BSP_KNOB_B_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
void InitializeButton() {
|
void InitializeButton() {
|
||||||
button_config_t btn_config = {
|
button_config_t btn_config = {
|
||||||
.type = BUTTON_TYPE_CUSTOM,
|
.type = BUTTON_TYPE_CUSTOM,
|
||||||
@@ -250,6 +288,7 @@ public:
|
|||||||
InitializeSpi();
|
InitializeSpi();
|
||||||
InitializeExpander();
|
InitializeExpander();
|
||||||
InitializeButton();
|
InitializeButton();
|
||||||
|
InitializeKnob();
|
||||||
Initializespd2010Display();
|
Initializespd2010Display();
|
||||||
InitializeIot();
|
InitializeIot();
|
||||||
GetBacklight()->RestoreBrightness();
|
GetBacklight()->RestoreBrightness();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ dependencies:
|
|||||||
espressif/esp_codec_dev: "~1.3.2"
|
espressif/esp_codec_dev: "~1.3.2"
|
||||||
espressif/esp-sr: "^2.0.2"
|
espressif/esp-sr: "^2.0.2"
|
||||||
espressif/button: "^3.3.1"
|
espressif/button: "^3.3.1"
|
||||||
|
espressif/knob: "^1.0.0"
|
||||||
lvgl/lvgl: "~9.2.2"
|
lvgl/lvgl: "~9.2.2"
|
||||||
esp_lvgl_port: "~2.4.4"
|
esp_lvgl_port: "~2.4.4"
|
||||||
espressif/esp_io_expander_tca95xx_16bit: "^2.0.0"
|
espressif/esp_io_expander_tca95xx_16bit: "^2.0.0"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ CONFIG_PARTITION_TABLE_CUSTOM=y
|
|||||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||||
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
||||||
|
|
||||||
ESP_TASK_WDT_TIMEOUT_S=10
|
CONFIG_ESP_TASK_WDT_TIMEOUT_S=10
|
||||||
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
|
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
|
||||||
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user