浏览代码

knob-only navigation

Philippe G 4 年之前
父节点
当前提交
0999d439dd
共有 2 个文件被更改,包括 83 次插入24 次删除
  1. 58 6
      components/services/audio_controls.c
  2. 25 18
      components/squeezelite/controls.c

+ 58 - 6
components/services/audio_controls.c

@@ -11,11 +11,13 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/timers.h"
 #include "esp_system.h"
 #include "esp_log.h"
 #include "cJSON.h"
 #include "buttons.h"
-#include "platform_config.h"
+#include "config.h"
 #include "accessors.h"
 #include "audio_controls.h"
 
@@ -35,6 +37,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 void control_rotary_handler(void *client, rotary_event_e event, bool long_press);
+static void rotary_timer( TimerHandle_t xTimer );
 
 static const actrls_config_map_t actrls_config_map[] =
 		{
@@ -70,6 +73,9 @@ static actrls_ir_handler_t *default_ir_handler, *current_ir_handler;
 static struct {
 	bool long_state;
 	bool volume_lock;
+	TimerHandle_t timer;
+	bool click_pending;
+	int left_count;
 } rotary;
 
 static const struct ir_action_map_s{
@@ -132,8 +138,16 @@ esp_err_t actrls_init(const char *profile_name) {
 		if ((p = strcasestr(config, "A")) != NULL) A = atoi(strchr(p, '=') + 1);
 		if ((p = strcasestr(config, "B")) != NULL) B = atoi(strchr(p, '=') + 1);
 		if ((p = strcasestr(config, "SW")) != NULL) SW = atoi(strchr(p, '=') + 1);
-		if ((p = strcasestr(config, "volume")) != NULL) rotary.volume_lock = true;
-		if ((p = strcasestr(config, "longpress")) != NULL) longpress = 1000;
+		if ((p = strcasestr(config, "knobonly")) != NULL) {
+			p = strchr(p, '=');
+			int double_press = p ? atoi(p + 1) : 350;
+			rotary.timer = xTimerCreate("knobTimer", double_press / portTICK_RATE_MS, pdFALSE, NULL, rotary_timer);
+			longpress = 500;
+			ESP_LOGI(TAG, "single knob navigation %d", double_press);
+		} else {
+			if ((p = strcasestr(config, "volume")) != NULL) rotary.volume_lock = true;
+			if ((p = strcasestr(config, "longpress")) != NULL) longpress = 1000;
+		}	
 				
 		// create rotary (no handling of long press)
 		err = create_rotary(NULL, A, B, SW, longpress, control_rotary_handler) ? ESP_OK : ESP_FAIL;
@@ -222,17 +236,42 @@ static void control_rotary_handler(void *client, rotary_event_e event, bool long
 	
 	switch(event) {
 	case ROTARY_LEFT:
-		if (rotary.long_state) action = ACTRLS_PREV;
+		if (rotary.timer) {
+			if (rotary.left_count) {
+				action = KNOB_LEFT;
+				// need to add a left button the first time
+				if (rotary.left_count == 1) (*current_controls[KNOB_LEFT])(true);
+			}
+			xTimerStart(rotary.timer, 20 / portTICK_RATE_MS);
+			rotary.left_count++;
+		}
+		else if (rotary.long_state) action = ACTRLS_PREV;
 		else if (rotary.volume_lock) action = ACTRLS_VOLDOWN;
 		else action = KNOB_LEFT;
 		break;
 	case ROTARY_RIGHT:
-		if (rotary.long_state) action = ACTRLS_NEXT;
+		if (rotary.timer) {
+			if (rotary.left_count == 1) {
+				action = ACTRLS_PAUSE;
+				rotary.left_count = 0;
+				xTimerStop(rotary.timer, 0);
+			} else action = KNOB_RIGHT;
+		}	
+		else if (rotary.long_state) action = ACTRLS_NEXT;
 		else if (rotary.volume_lock) action = ACTRLS_VOLUP;
 		else action = KNOB_RIGHT;
 		break;
 	case ROTARY_PRESSED:
-		if (long_press)	rotary.long_state = !rotary.long_state;
+		if (rotary.timer) {
+			if (long_press) action = ACTRLS_PLAY;
+			else if (rotary.click_pending) {
+				action = BCTRLS_LEFT;
+				xTimerStop(rotary.timer, 0);
+			} 
+			else xTimerStart(rotary.timer, 20 / portTICK_RATE_MS);
+			rotary.click_pending = !rotary.click_pending;
+		} 
+		else if (long_press) rotary.long_state = !rotary.long_state;
 		else if (rotary.volume_lock) action = ACTRLS_TOGGLE;
 		else action = KNOB_PUSH;
 		break;
@@ -243,6 +282,19 @@ static void control_rotary_handler(void *client, rotary_event_e event, bool long
 	if (action != ACTRLS_NONE) (*current_controls[action])(pressed);
 }
 
+/****************************************************************************************
+ * 
+ */
+static void rotary_timer( TimerHandle_t xTimer ) {
+	if (rotary.click_pending) {
+		(*current_controls[KNOB_PUSH])(true);
+		rotary.click_pending = false;
+	} else if (rotary.left_count) {
+		if (rotary.left_count == 1) (*current_controls[KNOB_LEFT])(true);
+		rotary.left_count = 0;
+	}
+}
+
 /****************************************************************************************
  * 
  */

+ 25 - 18
components/squeezelite/controls.c

@@ -7,7 +7,7 @@
  */
 
 #include "squeezelite.h"
-#include "platform_config.h"
+#include "config.h"
 #include "audio_controls.h"
 
 static log_level loglevel = lINFO;
@@ -49,6 +49,7 @@ struct IR_header {
 static in_addr_t server_ip;
 static u16_t server_hport;
 static u16_t server_cport;
+static int cli_sock = -1;
 static u8_t mac[6];
 static void	(*chained_notify)(in_addr_t, u16_t, u16_t);
 static bool raw_mode;
@@ -243,38 +244,44 @@ const actrls_t LMS_controls = {
  * 
  */
 static void cli_send_cmd(char *cmd) {
-	char packet[64];
-	struct sockaddr_in addr;
-	socklen_t addrlen = sizeof(addr);
-	int len, sock = socket(AF_INET, SOCK_STREAM, 0);
+	char packet[96];
+	int len;
 	
-	addr.sin_family = AF_INET;
-	addr.sin_addr.s_addr = server_ip;
-	addr.sin_port = htons(server_cport);
-
-	if (connect(sock, (struct sockaddr *) &addr, addrlen) < 0) {
-		LOG_ERROR("unable to connect to server %s:%hu with cli", inet_ntoa(server_ip), server_cport);
-		return;
-	}
-
 	len = sprintf(packet, "%02x:%02x:%02x:%02x:%02x:%02x %s\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], cmd);
 	LOG_DEBUG("sending command %s at %s:%hu", packet, inet_ntoa(server_ip), server_cport);
 		
-	if (send(sock, packet, len, 0) < 0) {
+	if (send(cli_sock, packet, len, MSG_DONTWAIT) < 0) {
 		LOG_WARN("cannot send CLI %s", packet);
 	}
-
-	closesocket(sock);
+	
+	// need to empty the RX buffer otherwise we'll lock the TCP/IP stack
+	len = recv(cli_sock, packet, 96, MSG_DONTWAIT);
 }
 
 /****************************************************************************************
  * Notification when server changes
  */
 static void notify(in_addr_t ip, u16_t hport, u16_t cport) {
+	struct sockaddr_in addr;
+	socklen_t addrlen = sizeof(addr);
+	
 	server_ip = ip;
 	server_hport = hport;
 	server_cport = cport;
 	
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = server_ip;
+	addr.sin_port = htons(server_cport);
+	
+	// close existing CLI connection and open new one
+	if (cli_sock >= 0) closesocket(cli_sock);
+	cli_sock = socket(AF_INET, SOCK_STREAM, 0);
+
+	if (connect(cli_sock, (struct sockaddr *) &addr, addrlen) < 0) {
+		LOG_ERROR("unable to connect to server %s:%hu with cli", inet_ntoa(server_ip), server_cport);
+		cli_sock = -1;
+	}
+	
 	LOG_INFO("notified server %s hport %hu cport %hu", inet_ntoa(ip), hport, cport);
 	
 	if (chained_notify) (*chained_notify)(ip, hport, cport);
@@ -303,4 +310,4 @@ void sb_controls_init(void) {
 	
 	chained_notify = server_notify;
 	server_notify = notify;
-}
+}