mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-06 03:27:01 +03:00
add 2nd encoder for volume only
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
|
2024-09-28
|
||||||
|
- add dedicated volume encoder
|
||||||
|
- fix memory leak in rotary config creation
|
||||||
|
|
||||||
2024-09-28
|
2024-09-28
|
||||||
- create autoexec NVS entry at the right place (not only whne BT is enabled!
|
- create autoexec NVS entry at the right place (not only whne BT is enabled!
|
||||||
- try to make i2s panic mode work for all esp versions
|
- try to make i2s panic mode work for all esp versions
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ static esp_err_t actrls_process_action (const cJSON * member, actrls_config_t *c
|
|||||||
|
|
||||||
static esp_err_t actrls_init_json(const char *profile_name, bool create);
|
static esp_err_t actrls_init_json(const char *profile_name, bool create);
|
||||||
static void control_rotary_handler(void *client, rotary_event_e event, bool long_press);
|
static void control_rotary_handler(void *client, rotary_event_e event, bool long_press);
|
||||||
|
static void volume_rotary_handler(void *client, rotary_event_e event, bool long_press);
|
||||||
static void rotary_timer( TimerHandle_t xTimer );
|
static void rotary_timer( TimerHandle_t xTimer );
|
||||||
|
|
||||||
static const actrls_config_map_t actrls_config_map[] =
|
static const actrls_config_map_t actrls_config_map[] =
|
||||||
@@ -157,6 +158,24 @@ esp_err_t actrls_init(const char *profile_name) {
|
|||||||
err = create_rotary(NULL, A, B, SW, longpress, control_rotary_handler) ? ESP_OK : ESP_FAIL;
|
err = create_rotary(NULL, A, B, SW, longpress, control_rotary_handler) ? ESP_OK : ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(config);
|
||||||
|
config = config_alloc_get_default(NVS_TYPE_STR, "volume_rotary", NULL, 0);
|
||||||
|
|
||||||
|
// now see if we have a dedicated volume rotary
|
||||||
|
if (config && *config) {
|
||||||
|
int A = -1, B = -1, SW = -1;
|
||||||
|
|
||||||
|
// parse config
|
||||||
|
PARSE_PARAM(config, "A", '=', A);
|
||||||
|
PARSE_PARAM(config, "B", '=', B);
|
||||||
|
PARSE_PARAM(config, "SW", '=', SW);
|
||||||
|
|
||||||
|
// create rotary (no handling of long press)
|
||||||
|
err |= create_volume_rotary(NULL, A, B, SW, volume_rotary_handler) ? ESP_OK : ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(config);
|
||||||
|
|
||||||
// set infrared GPIO if any
|
// set infrared GPIO if any
|
||||||
parse_set_GPIO(set_ir_gpio);
|
parse_set_GPIO(set_ir_gpio);
|
||||||
|
|
||||||
@@ -290,6 +309,29 @@ static void control_rotary_handler(void *client, rotary_event_e event, bool long
|
|||||||
if (action != ACTRLS_NONE) (*current_controls[action])(pressed);
|
if (action != ACTRLS_NONE) (*current_controls[action])(pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void volume_rotary_handler(void *client, rotary_event_e event, bool long_press) {
|
||||||
|
actrls_action_e action = ACTRLS_NONE;
|
||||||
|
bool pressed = true;
|
||||||
|
|
||||||
|
switch(event) {
|
||||||
|
case ROTARY_LEFT:
|
||||||
|
action = ACTRLS_VOLDOWN;
|
||||||
|
break;
|
||||||
|
case ROTARY_RIGHT:
|
||||||
|
action = ACTRLS_VOLUP;
|
||||||
|
break;
|
||||||
|
case ROTARY_PRESSED:
|
||||||
|
action = ACTRLS_TOGGLE;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action != ACTRLS_NONE) (*current_controls[action])(pressed);
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -568,6 +610,13 @@ exit:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
actrls_handler get_ctrl_handler(actrls_action_e action) {
|
||||||
|
return current_controls[action];
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -53,3 +53,9 @@ void actrls_set_default(const actrls_t controls, bool raw_controls, actrls_hook_
|
|||||||
void actrls_set(const actrls_t controls, bool raw_controls, actrls_hook_t *hook, actrls_ir_handler_t *ir_handler);
|
void actrls_set(const actrls_t controls, bool raw_controls, actrls_hook_t *hook, actrls_ir_handler_t *ir_handler);
|
||||||
void actrls_unset(void);
|
void actrls_unset(void);
|
||||||
bool actrls_ir_action(uint16_t addr, uint16_t code);
|
bool actrls_ir_action(uint16_t addr, uint16_t code);
|
||||||
|
|
||||||
|
/* Call this to get the handler for any of the audio actions. It will map to the control specific
|
||||||
|
to the current mode (LMS, AirPlay, Spotify). This is useful if you have a custom way to create
|
||||||
|
buttons (like analogue buttons)
|
||||||
|
*/
|
||||||
|
actrls_handler get_ctrl_handler(actrls_action_e);
|
||||||
|
|||||||
@@ -58,13 +58,13 @@ static struct {
|
|||||||
|
|
||||||
static TimerHandle_t polled_timer;
|
static TimerHandle_t polled_timer;
|
||||||
|
|
||||||
static EXT_RAM_ATTR struct {
|
static EXT_RAM_ATTR struct encoder {
|
||||||
QueueHandle_t queue;
|
QueueHandle_t queue;
|
||||||
void *client;
|
void *client;
|
||||||
rotary_encoder_info_t info;
|
rotary_encoder_info_t info;
|
||||||
int A, B, SW;
|
int A, B, SW;
|
||||||
rotary_handler handler;
|
rotary_handler handler;
|
||||||
} rotary;
|
} rotary, volume;
|
||||||
|
|
||||||
static EXT_RAM_ATTR struct {
|
static EXT_RAM_ATTR struct {
|
||||||
RingbufHandle_t rb;
|
RingbufHandle_t rb;
|
||||||
@@ -227,11 +227,22 @@ static void buttons_task(void* arg) {
|
|||||||
// received a rotary event
|
// received a rotary event
|
||||||
xQueueReceive(rotary.queue, &event, 0);
|
xQueueReceive(rotary.queue, &event, 0);
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Event: position %d, direction %s", event.state.position,
|
ESP_LOGD(TAG, "Rotary event: position %d, direction %s", event.state.position,
|
||||||
event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET");
|
event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET");
|
||||||
|
|
||||||
rotary.handler(rotary.client, event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ?
|
rotary.handler(rotary.client, event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ?
|
||||||
ROTARY_RIGHT : ROTARY_LEFT, false);
|
ROTARY_RIGHT : ROTARY_LEFT, false);
|
||||||
|
} else if (xActivatedMember == volume.queue) {
|
||||||
|
rotary_encoder_event_t event = { 0 };
|
||||||
|
|
||||||
|
// received a volume rotary event
|
||||||
|
xQueueReceive(volume.queue, &event, 0);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Volume event: position %d, direction %s", event.state.position,
|
||||||
|
event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET");
|
||||||
|
|
||||||
|
volume.handler(volume.client, event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ?
|
||||||
|
ROTARY_RIGHT : ROTARY_LEFT, false);
|
||||||
} else {
|
} else {
|
||||||
// this is IR
|
// this is IR
|
||||||
active = infrared_receive(infrared.rb, infrared.handler);
|
active = infrared_receive(infrared.rb, infrared.handler);
|
||||||
@@ -395,7 +406,55 @@ void *button_remap(void *client, int gpio, button_handler handler, int long_pres
|
|||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Rotary encoder handler
|
* Create rotary encoder
|
||||||
|
*/
|
||||||
|
static bool create_rotary_encoder(struct encoder *encoder, void *id, int A, int B, int SW, int long_press, rotary_handler handler, button_handler button) {
|
||||||
|
// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
|
||||||
|
if (A == -1 || B == -1 || A == 36 || A == 39 || B == 36 || B == 39) {
|
||||||
|
ESP_LOGI(TAG, "Cannot create rotary %d %d", A, B);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder->A = A;
|
||||||
|
encoder->B = B;
|
||||||
|
encoder->SW = SW;
|
||||||
|
encoder->client = id;
|
||||||
|
encoder->handler = handler;
|
||||||
|
|
||||||
|
// Initialise the rotary encoder device with the GPIOs for A and B signals
|
||||||
|
rotary_encoder_init(&encoder->info, A, B);
|
||||||
|
|
||||||
|
// Create a queue for events from the rotary encoder driver.
|
||||||
|
encoder->queue = rotary_encoder_create_queue();
|
||||||
|
rotary_encoder_set_queue(&encoder->info, encoder->queue);
|
||||||
|
|
||||||
|
common_task_init();
|
||||||
|
xQueueAddToSet( encoder->queue, common_queue_set );
|
||||||
|
|
||||||
|
// create companion button if rotary has a switch
|
||||||
|
if (SW != -1) button_create(id, SW, BUTTON_LOW, true, 0, button, long_press, -1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Volume button encoder handler
|
||||||
|
*/
|
||||||
|
static void volume_button_handler(void *id, button_event_e event, button_press_e mode, bool long_press) {
|
||||||
|
ESP_LOGI(TAG, "Volume encoder push-button %d", event);
|
||||||
|
volume.handler(id, event == BUTTON_PRESSED ? ROTARY_PRESSED : ROTARY_RELEASED, long_press);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Create volume encoder
|
||||||
|
*/
|
||||||
|
bool create_volume_rotary(void *id, int A, int B, int SW, rotary_handler handler) {
|
||||||
|
ESP_LOGI(TAG, "Created volume encoder A:%d B:%d, SW:%d", A, B, SW);
|
||||||
|
return create_rotary_encoder(&volume, id, A, B, SW, false, handler, volume_button_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Rotary button encoder handler
|
||||||
*/
|
*/
|
||||||
static void rotary_button_handler(void *id, button_event_e event, button_press_e mode, bool long_press) {
|
static void rotary_button_handler(void *id, button_event_e event, button_press_e mode, bool long_press) {
|
||||||
ESP_LOGI(TAG, "Rotary push-button %d", event);
|
ESP_LOGI(TAG, "Rotary push-button %d", event);
|
||||||
@@ -406,34 +465,8 @@ static void rotary_button_handler(void *id, button_event_e event, button_press_e
|
|||||||
* Create rotary encoder
|
* Create rotary encoder
|
||||||
*/
|
*/
|
||||||
bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handler handler) {
|
bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handler handler) {
|
||||||
// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
|
|
||||||
if (A == -1 || B == -1 || A == 36 || A == 39 || B == 36 || B == 39) {
|
|
||||||
ESP_LOGI(TAG, "Cannot create rotary %d %d", A, B);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
rotary.A = A;
|
|
||||||
rotary.B = B;
|
|
||||||
rotary.SW = SW;
|
|
||||||
rotary.client = id;
|
|
||||||
rotary.handler = handler;
|
|
||||||
|
|
||||||
// Initialise the rotary encoder device with the GPIOs for A and B signals
|
|
||||||
rotary_encoder_init(&rotary.info, A, B);
|
|
||||||
|
|
||||||
// Create a queue for events from the rotary encoder driver.
|
|
||||||
rotary.queue = rotary_encoder_create_queue();
|
|
||||||
rotary_encoder_set_queue(&rotary.info, rotary.queue);
|
|
||||||
|
|
||||||
common_task_init();
|
|
||||||
xQueueAddToSet( rotary.queue, common_queue_set );
|
|
||||||
|
|
||||||
// create companion button if rotary has a switch
|
|
||||||
if (SW != -1) button_create(id, SW, BUTTON_LOW, true, 0, rotary_button_handler, long_press, -1);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Created rotary encoder A:%d B:%d, SW:%d", A, B, SW);
|
ESP_LOGI(TAG, "Created rotary encoder A:%d B:%d, SW:%d", A, B, SW);
|
||||||
|
return create_rotary_encoder(&rotary, id, A, B, SW, long_press, handler, rotary_button_handler);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
|
|||||||
@@ -34,5 +34,5 @@ typedef enum { ROTARY_LEFT, ROTARY_RIGHT, ROTARY_PRESSED, ROTARY_RELEASED } rota
|
|||||||
typedef void (*rotary_handler)(void *id, rotary_event_e event, bool long_press);
|
typedef void (*rotary_handler)(void *id, rotary_event_e event, bool long_press);
|
||||||
|
|
||||||
bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handler handler);
|
bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handler handler);
|
||||||
|
bool create_volume_rotary(void *id, int A, int B, int SW, rotary_handler handler);
|
||||||
bool create_infrared(int gpio, infrared_handler handler, infrared_mode_t mode);
|
bool create_infrared(int gpio, infrared_handler handler, infrared_mode_t mode);
|
||||||
|
|||||||
@@ -369,6 +369,12 @@ menu "Squeezelite-ESP32"
|
|||||||
help
|
help
|
||||||
Set GPIO for rotary encoder (quadrature phase). See README on SqueezeESP32 project's GitHub for more details
|
Set GPIO for rotary encoder (quadrature phase). See README on SqueezeESP32 project's GitHub for more details
|
||||||
A=<gpio>,B=<gpio>[,SW=gpio>[[,knobonly[=<ms>]|[,volume][,longpress]]
|
A=<gpio>,B=<gpio>[,SW=gpio>[[,knobonly[=<ms>]|[,volume][,longpress]]
|
||||||
|
config VOLUME_ROTARY_ENCODER
|
||||||
|
string "Volume Rotary Encoder configuration"
|
||||||
|
default ""
|
||||||
|
help
|
||||||
|
Set GPIO for volume rotary encoder (quadrature phase). See README on SqueezeESP32 project's GitHub for more details
|
||||||
|
A=<gpio>,B=<gpio>[,SW=gpio>]
|
||||||
config GPIO_EXP_CONFIG
|
config GPIO_EXP_CONFIG
|
||||||
string "GPIO expander configuration"
|
string "GPIO expander configuration"
|
||||||
help
|
help
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ const DefaultStringVal defaultStringVals[] = {
|
|||||||
{"actrls_config", ""},
|
{"actrls_config", ""},
|
||||||
{"lms_ctrls_raw", "n"},
|
{"lms_ctrls_raw", "n"},
|
||||||
{"rotary_config", CONFIG_ROTARY_ENCODER},
|
{"rotary_config", CONFIG_ROTARY_ENCODER},
|
||||||
|
{"volume_rotary", CONFIG_VOLUME_ROTARY_ENCODER},
|
||||||
{"display_config", CONFIG_DISPLAY_CONFIG},
|
{"display_config", CONFIG_DISPLAY_CONFIG},
|
||||||
{"eth_config", CONFIG_ETH_CONFIG},
|
{"eth_config", CONFIG_ETH_CONFIG},
|
||||||
{"i2c_config", CONFIG_I2C_CONFIG},
|
{"i2c_config", CONFIG_I2C_CONFIG},
|
||||||
|
|||||||
Reference in New Issue
Block a user