From 5b6ddf0b028a5f9e647cecc439eafc1f89d4387a Mon Sep 17 00:00:00 2001 From: Philippe G Date: Sat, 9 May 2020 18:07:26 -0700 Subject: [PATCH] add IR and "raw" button mode - release --- components/driver_bt/bt_app_sink.c | 26 ++-- components/raop/raop_sink.c | 26 ++-- components/services/audio_controls.c | 149 +++++++++++++----- components/services/audio_controls.h | 13 +- components/services/buttons.c | 57 +++++-- components/services/buttons.h | 4 + components/services/infrared.c | 177 +++++++++++++++++++++ components/services/infrared.h | 17 ++ components/services/monitor.c | 8 +- components/squeezelite/controls.c | 225 ++++++++++++++++++++++----- components/squeezelite/display.c | 88 +++++------ components/squeezelite/embedded.c | 3 + components/squeezelite/embedded.h | 5 +- components/squeezelite/slimproto.c | 22 ++- main/esp_app_main.c | 5 +- 15 files changed, 662 insertions(+), 163 deletions(-) create mode 100644 components/services/infrared.c create mode 100644 components/services/infrared.h diff --git a/components/driver_bt/bt_app_sink.c b/components/driver_bt/bt_app_sink.c index e0d4d34d..fd03e1a0 100644 --- a/components/driver_bt/bt_app_sink.c +++ b/components/driver_bt/bt_app_sink.c @@ -87,41 +87,49 @@ static EXT_RAM_ATTR struct { bool updated; } s_metadata; -static void bt_volume_up(void) { +static void bt_volume_up(bool pressed) { + if (!pressed) return; // volume UP/DOWN buttons are not supported by iPhone/Android volume_set_by_local_host(s_volume < 127-3 ? s_volume + 3 : 127); (*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume); ESP_LOGI(BT_AV_TAG, "BT volume up %u", s_volume); } -static void bt_volume_down(void) { +static void bt_volume_down(bool pressed) { + if (!pressed) return; // volume UP/DOWN buttons are not supported by iPhone/Android volume_set_by_local_host(s_volume > 3 ? s_volume - 3 : 0); (*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume); } -static void bt_toggle(void) { +static void bt_toggle(bool pressed) { + if (!pressed) return; if (s_audio == AUDIO_PLAYING) esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED); else esp_avrc_ct_send_passthrough_cmd(tl++, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED); } -static void bt_play(void) { +static void bt_play(bool pressed) { + if (!pressed) return; esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED); } -static void bt_pause(void) { +static void bt_pause(bool pressed) { + if (!pressed) return; esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_PRESSED); } -static void bt_stop(void) { +static void bt_stop(bool pressed) { + if (!pressed) return; esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED); } -static void bt_prev(void) { +static void bt_prev(bool pressed) { + if (!pressed) return; esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_BACKWARD, ESP_AVRC_PT_CMD_STATE_PRESSED); } -static void bt_next(void) { +static void bt_next(bool pressed) { + if (!pressed) return; esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_FORWARD, ESP_AVRC_PT_CMD_STATE_PRESSED); } @@ -297,7 +305,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param) // force metadata update update_metadata(true); - actrls_set(controls, NULL); + actrls_set(controls, false, NULL, actrls_ir_action); } else { // if decoder is busy, stop it (would be better to not ACK this command, but don't know how) esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED); diff --git a/components/raop/raop_sink.c b/components/raop/raop_sink.c index 73b711f5..11d20438 100644 --- a/components/raop/raop_sink.c +++ b/components/raop/raop_sink.c @@ -31,42 +31,50 @@ static log_level *loglevel = &raop_loglevel; static struct raop_ctx_s *raop; static raop_cmd_vcb_t cmd_handler_chain; -static void raop_volume_up(void) { +static void raop_volume_up(bool pressed) { + if (!pressed) return; raop_cmd(raop, RAOP_VOLUME_UP, NULL); LOG_INFO("AirPlay volume up"); } -static void raop_volume_down(void) { +static void raop_volume_down(bool pressed) { + if (!pressed) return; raop_cmd(raop, RAOP_VOLUME_DOWN, NULL); LOG_INFO("AirPlay volume down"); } -static void raop_toggle(void) { +static void raop_toggle(bool pressed) { + if (!pressed) return; raop_cmd(raop, RAOP_TOGGLE, NULL); LOG_INFO("AirPlay play/pause"); } -static void raop_pause(void) { +static void raop_pause(bool pressed) { + if (!pressed) return; raop_cmd(raop, RAOP_PAUSE, NULL); LOG_INFO("AirPlay pause"); } -static void raop_play(void) { +static void raop_play(bool pressed) { + if (!pressed) return; raop_cmd(raop, RAOP_PLAY, NULL); LOG_INFO("AirPlay play"); } -static void raop_stop(void) { +static void raop_stop(bool pressed) { + if (!pressed) return; raop_cmd(raop, RAOP_STOP, NULL); LOG_INFO("AirPlay stop"); } -static void raop_prev(void) { +static void raop_prev(bool pressed) { + if (!pressed) return; raop_cmd(raop, RAOP_PREV, NULL); LOG_INFO("AirPlay previous"); } -static void raop_next(void) { +static void raop_next(bool pressed) { + if (!pressed) return; raop_cmd(raop, RAOP_NEXT, NULL); LOG_INFO("AirPlay next"); } @@ -98,7 +106,7 @@ static bool cmd_handler(raop_event_t event, ...) { // now handle events for display switch(event) { case RAOP_SETUP: - actrls_set(controls, NULL); + actrls_set(controls, false, NULL, actrls_ir_action); displayer_control(DISPLAYER_ACTIVATE, "AIRPLAY"); break; case RAOP_PLAY: diff --git a/components/services/audio_controls.c b/components/services/audio_controls.c index 88577dfe..0efd6b19 100644 --- a/components/services/audio_controls.c +++ b/components/services/audio_controls.c @@ -17,6 +17,7 @@ #include "cJSON.h" #include "buttons.h" #include "config.h" +#include "accessors.h" #include "audio_controls.h" typedef esp_err_t (actrls_config_map_handler) (const cJSON * member, actrls_config_t *cur_config,uint32_t offset); @@ -33,6 +34,9 @@ static esp_err_t actrls_process_type (const cJSON * member, actrls_config_t *cur static esp_err_t actrls_process_bool (const cJSON * member, actrls_config_t *cur_config, uint32_t offset); static esp_err_t actrls_process_action (const cJSON * member, actrls_config_t *cur_config, uint32_t offset); +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 const actrls_config_map_t actrls_config_map[] = { {"gpio", offsetof(actrls_config_t,gpio), actrls_process_int}, @@ -61,11 +65,88 @@ static actrls_config_t *json_config; cJSON * control_profiles = NULL; static actrls_t default_controls, current_controls; static actrls_hook_t *default_hook, *current_hook; +static bool default_raw_controls, current_raw_controls; +static actrls_ir_handler_t *default_ir_handler, *current_ir_handler; + static struct { bool long_state; bool volume_lock; } rotary; +static const struct ir_action_map_s{ + uint32_t code; + actrls_action_e action; +} ir_action_map[] = { + {0x7689b04f, BCTRLS_DOWN}, {0x7689906f, BCTRLS_LEFT}, {0x7689d02f, BCTRLS_RIGHT}, {0x7689e01f, BCTRLS_UP}, + {0x768900ff, ACTRLS_VOLDOWN}, {0x7689807f, ACTRLS_VOLUP}, + {0x7689c03f, ACTRLS_PREV}, {0x7689a05f, ACTRLS_NEXT}, + {0x768920df, ACTRLS_PAUSE}, {0x768910ef, ACTRLS_PLAY}, + {0x00, 0x00}, +}; + +/**************************************************************************************** + * This function can be called to map IR codes to default actions + */ +bool actrls_ir_action(uint16_t addr, uint16_t cmd) { + uint32_t code = (addr << 16) | cmd; + struct ir_action_map_s const *map = ir_action_map; + + while (map->code && map->code != code) map++; + + if (map->code && current_controls[map->action]) { + current_controls[map->action](true); + return true; + } else { + return false; + } +} + +/**************************************************************************************** + * + */ +static void ir_handler(uint16_t addr, uint16_t cmd) { + ESP_LOGD(TAG, "recaived IR %04hx:%04hx", addr, cmd); + if (current_ir_handler) current_ir_handler(addr, cmd); +} + +/**************************************************************************************** + * + */ +static void set_ir_gpio(int gpio, char *value) { + if (!strcasecmp(value, "ir") ) { + create_infrared(gpio, ir_handler); + } +} + +/**************************************************************************************** + * + */ +esp_err_t actrls_init(const char *profile_name) { + esp_err_t err = ESP_OK; + char *config = config_alloc_get_default(NVS_TYPE_STR, "rotary_config", NULL, 0); + + if (config && *config) { + char *p; + int A = -1, B = -1, SW = -1, longpress = 0; + + // parse config + 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; + + // create rotary (no handling of long press) + err = create_rotary(NULL, A, B, SW, longpress, control_rotary_handler) ? ESP_OK : ESP_FAIL; + } + + // set infrared GPIO if any + parse_set_GPIO(set_ir_gpio); + + if (!err) return actrls_init_json(profile_name, true); + else return err; +} + /**************************************************************************************** * */ @@ -73,6 +154,13 @@ static void control_handler(void *client, button_event_e event, button_press_e p actrls_config_t *key = (actrls_config_t*) client; actrls_action_detail_t action_detail; + // in raw mode, we just do normal action press *and* release, there is no longpress nor shift + if (current_raw_controls) { + ESP_LOGD(TAG, "calling action %u in raw mode", key->normal[0].action); + if (current_controls[key->normal[0].action]) (*current_controls[key->normal[0].action])(event == BUTTON_PRESSED); + return; + } + switch(press) { case BUTTON_NORMAL: if (long_press) action_detail = key->longpress[event == BUTTON_PRESSED ? 0 : 1]; @@ -87,7 +175,7 @@ static void control_handler(void *client, button_event_e event, button_press_e p break; } - ESP_LOGD(TAG, "control gpio:%u press:%u long:%u event:%u action:%u", key->gpio, press, long_press, event,action_detail.action); + ESP_LOGD(TAG, "control gpio:%u press:%u long:%u event:%u action:%u", key->gpio, press, long_press, event, action_detail.action); // stop here if control hook served the request if (current_hook && (*current_hook)(key->gpio, action_detail.action, event, press, long_press)) return; @@ -114,7 +202,7 @@ static void control_handler(void *client, button_event_e event, button_press_e p } } else if (action_detail.action != ACTRLS_NONE) { ESP_LOGD(TAG, "calling action %u", action_detail.action); - if (current_controls[action_detail.action]) (*current_controls[action_detail.action])(); + if (current_controls[action_detail.action]) (*current_controls[action_detail.action])(event == BUTTON_PRESSED); } } @@ -123,6 +211,15 @@ static void control_handler(void *client, button_event_e event, button_press_e p */ static void control_rotary_handler(void *client, rotary_event_e event, bool long_press) { actrls_action_e action = ACTRLS_NONE; + bool pressed = true; + + // in raw mode, we just pass rotary events + if (current_raw_controls) { + if (event == ROTARY_LEFT) (*current_controls[KNOB_LEFT])(true); + else if (event == ROTARY_RIGHT) (*current_controls[KNOB_RIGHT])(true); + else (*current_controls[KNOB_PUSH])(event == ROTARY_PRESSED); + return; + } switch(event) { case ROTARY_LEFT: @@ -144,17 +241,7 @@ static void control_rotary_handler(void *client, rotary_event_e event, bool long break; } - if (action != ACTRLS_NONE) (*current_controls[action])(); -} - -/**************************************************************************************** - * - */ -esp_err_t actrls_init(int n, const actrls_config_t *config) { - for (int i = 0; i < n; i++) { - button_create((void*) (config + i), config[i].gpio, config[i].type, config[i].pull, config[i].debounce, control_handler, config[i].long_press, config[i].shifter_gpio); - } - return ESP_OK; + if (action != ACTRLS_NONE) (*current_controls[action])(pressed); } /**************************************************************************************** @@ -361,30 +448,13 @@ static void actrls_defaults(actrls_config_t *config) { /**************************************************************************************** * */ -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) { esp_err_t err = ESP_OK; actrls_config_t *cur_config = NULL; actrls_config_t *config_root = NULL; + char *config; const cJSON *button; - char *config = config_alloc_get_default(NVS_TYPE_STR, "rotary_config", NULL, 0); - if (config && *config) { - char *p; - int A = -1, B = -1, SW = -1, longpress = 0; - - // parse config - 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; - - // create rotary (no handling of long press) - err = create_rotary(NULL, A, B, SW, longpress, control_rotary_handler) ? ESP_OK : ESP_FAIL; - } - - if (config) free(config); - if (!profile_name || !*profile_name) return ESP_OK; config = config_alloc_get_default(NVS_TYPE_STR, profile_name, NULL, 0); @@ -416,8 +486,9 @@ esp_err_t actrls_init_json(const char *profile_name, bool create) { esp_err_t loc_err = actrls_process_button(button, cur_config); err = (err == ESP_OK) ? loc_err : err; if (loc_err == ESP_OK) { - if (create) button_create((void*) cur_config, cur_config->gpio,cur_config->type, cur_config->pull,cur_config->debounce, - control_handler, cur_config->long_press, cur_config->shifter_gpio); + if (create) button_create((void*) cur_config, cur_config->gpio,cur_config->type, + cur_config->pull,cur_config->debounce, control_handler, + cur_config->long_press, cur_config->shifter_gpio); } else { ESP_LOGE(TAG,"Error parsing button structure. Button will not be registered."); } @@ -439,18 +510,22 @@ esp_err_t actrls_init_json(const char *profile_name, bool create) { /**************************************************************************************** * */ -void actrls_set_default(const actrls_t controls, actrls_hook_t *hook) { +void actrls_set_default(const actrls_t controls, bool raw_controls, actrls_hook_t *hook, actrls_ir_handler_t *ir_handler) { memcpy(default_controls, controls, sizeof(actrls_t)); memcpy(current_controls, default_controls, sizeof(actrls_t)); default_hook = current_hook = hook; + default_raw_controls = current_raw_controls = raw_controls; + default_ir_handler = current_ir_handler = ir_handler; } /**************************************************************************************** * */ -void actrls_set(const actrls_t controls, actrls_hook_t *hook) { +void actrls_set(const actrls_t controls, bool raw_controls, actrls_hook_t *hook, actrls_ir_handler_t *ir_handler) { memcpy(current_controls, controls, sizeof(actrls_t)); current_hook = hook; + current_raw_controls = raw_controls; + current_ir_handler = ir_handler; } /**************************************************************************************** @@ -459,4 +534,6 @@ void actrls_set(const actrls_t controls, actrls_hook_t *hook) { void actrls_unset(void) { memcpy(current_controls, default_controls, sizeof(actrls_t)); current_hook = default_hook; + current_raw_controls = default_raw_controls; + current_ir_handler = default_ir_handler; } diff --git a/components/services/audio_controls.h b/components/services/audio_controls.h index 7f249105..1fdfd85c 100644 --- a/components/services/audio_controls.h +++ b/components/services/audio_controls.h @@ -18,9 +18,10 @@ typedef enum { ACTRLS_NONE = -1, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, A ACTRLS_REMAP, ACTRLS_MAX } actrls_action_e; -typedef void (*actrls_handler)(void); +typedef void (*actrls_handler)(bool pressed); typedef actrls_handler actrls_t[ACTRLS_MAX - ACTRLS_NONE - 1]; typedef bool actrls_hook_t(int gpio, actrls_action_e action, button_event_e event, button_press_e press, bool long_press); +typedef bool actrls_ir_handler_t(uint16_t addr, uint16_t cmd); // BEWARE any change to struct below must be mapped to actrls_config_map typedef struct { @@ -37,14 +38,16 @@ typedef struct actrl_config_s { actrls_action_detail_t normal[2], longpress[2], shifted[2], longshifted[2]; // [0] keypressed, [1] keyreleased } actrls_config_t; -esp_err_t actrls_init(int n, const actrls_config_t *config); -esp_err_t actrls_init_json(const char *profile_name, bool create); +esp_err_t actrls_init(const char *profile_name); /* Set hook function to non-null to be set your own direct managemet function, which should return true if it managed the control request, false if the normal handling should be done +The add_release boolean forces a release event to be sent if a press action has been +set, whether a release action has been set or not */ -void actrls_set_default(const actrls_t controls, actrls_hook_t *hook); -void actrls_set(const actrls_t controls, actrls_hook_t *hook); +void actrls_set_default(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); +bool actrls_ir_action(uint16_t addr, uint16_t code); diff --git a/components/services/buttons.c b/components/services/buttons.c index 3e097a1b..865a8e6f 100644 --- a/components/services/buttons.c +++ b/components/services/buttons.c @@ -20,6 +20,7 @@ #include "esp_log.h" #include "esp_task.h" #include "driver/gpio.h" +#include "driver/rmt.h" #include "buttons.h" #include "rotary_encoder.h" #include "globdefs.h" @@ -56,11 +57,29 @@ static struct { rotary_handler handler; } rotary; +static struct { + RingbufHandle_t rb; + infrared_handler handler; +} infrared; + static xQueueHandle button_evt_queue; -static QueueSetHandle_t button_queue_set; +static QueueSetHandle_t common_queue_set; static void buttons_task(void* arg); +/**************************************************************************************** + * Start task needed by button,s rotaty and infrared + */ +static void common_task_init(void) { + static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4))); + static EXT_RAM_ATTR StackType_t xStack[BUTTON_STACK_SIZE] __attribute__ ((aligned (4))); + + if (!common_queue_set) { + common_queue_set = xQueueCreateSet(BUTTON_QUEUE_LEN + 1); + xTaskCreateStatic( (TaskFunction_t) buttons_task, "buttons_thread", BUTTON_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer); + } + } + /**************************************************************************************** * GPIO low-level handler */ @@ -107,8 +126,8 @@ static void buttons_task(void* arg) { while (1) { QueueSetMemberHandle_t xActivatedMember; - // wait on button and rotary queues - if ((xActivatedMember = xQueueSelectFromSet( button_queue_set, portMAX_DELAY )) == NULL) continue; + // wait on button, rotary and infrared queues + if ((xActivatedMember = xQueueSelectFromSet( common_queue_set, portMAX_DELAY )) == NULL) continue; if (xActivatedMember == button_evt_queue) { struct button_s button; @@ -150,7 +169,7 @@ static void buttons_task(void* arg) { // button is a copy, so need to go to real context button.self->shifting = false; } - } else { + } else if (xActivatedMember == rotary.queue) { rotary_encoder_event_t event = { 0 }; // received a rotary event @@ -161,6 +180,9 @@ static void buttons_task(void* arg) { rotary.handler(rotary.client, event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? ROTARY_RIGHT : ROTARY_LEFT, false); + } else { + // this is IR + infrared_receive(infrared.rb, infrared.handler); } } } @@ -176,18 +198,14 @@ void dummy_handler(void *id, button_event_e event, button_press_e press) { * Create buttons */ void button_create(void *client, int gpio, int type, bool pull, int debounce, button_handler handler, int long_press, int shifter_gpio) { - static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4))); - static EXT_RAM_ATTR StackType_t xStack[BUTTON_STACK_SIZE] __attribute__ ((aligned (4))); - if (n_buttons >= MAX_BUTTONS) return; ESP_LOGI(TAG, "Creating button using GPIO %u, type %u, pull-up/down %u, long press %u shifter %d", gpio, type, pull, long_press, shifter_gpio); if (!n_buttons) { button_evt_queue = xQueueCreate(BUTTON_QUEUE_LEN, sizeof(struct button_s)); - if (!button_queue_set) button_queue_set = xQueueCreateSet(BUTTON_QUEUE_LEN + 1); - xQueueAddToSet( button_evt_queue, button_queue_set ); - xTaskCreateStatic( (TaskFunction_t) buttons_task, "buttons_thread", BUTTON_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer); + common_task_init(); + xQueueAddToSet( button_evt_queue, common_queue_set ); } // just in case this structure is allocated in a future release @@ -340,8 +358,8 @@ bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handle rotary.queue = rotary_encoder_create_queue(); rotary_encoder_set_queue(&rotary.info, rotary.queue); - if (!button_queue_set) button_queue_set = xQueueCreateSet(BUTTON_QUEUE_LEN + 1); - xQueueAddToSet( rotary.queue, button_queue_set ); + 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); @@ -350,3 +368,18 @@ bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handle return true; } + +/**************************************************************************************** + * Create Infrared + */ +bool create_infrared(int gpio, infrared_handler handler) { + // initialize IR infrastructure + infrared_init(&infrared.rb, gpio); + infrared.handler = handler; + + // join the queue set + common_task_init(); + xRingbufferAddToQueueSetRead(infrared.rb, common_queue_set); + + return (infrared.rb != NULL); +} diff --git a/components/services/buttons.h b/components/services/buttons.h index f8996733..4793fa09 100644 --- a/components/services/buttons.h +++ b/components/services/buttons.h @@ -7,6 +7,8 @@ */ #pragma once + +#include "infrared.h" // button type (pressed = LOW or HIGH, matches GPIO level) #define BUTTON_LOW 0 @@ -32,3 +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); bool create_rotary(void *id, int A, int B, int SW, int long_press, rotary_handler handler); + +bool create_infrared(int gpio, infrared_handler handler); diff --git a/components/services/infrared.c b/components/services/infrared.c new file mode 100644 index 00000000..c637124d --- /dev/null +++ b/components/services/infrared.c @@ -0,0 +1,177 @@ +/* + * infrared receiver (using espressif's example) + * + * (c) Philippe G. 2020, philippe_44@outlook.com + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + * + */ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_err.h" +#include "esp_log.h" +#include "driver/rmt.h" +#include "infrared.h" + +static const char* TAG = "IR"; + +#define RMT_RX_ACTIVE_LEVEL 0 /*!< If we connect with a IR receiver, the data is active low */ + +#define RMT_RX_CHANNEL 0 /*!< RMT channel for receiver */ +#define RMT_CLK_DIV 100 /*!< RMT counter clock divider */ +#define RMT_TICK_10_US (80000000/RMT_CLK_DIV/100000) /*!< RMT counter value for 10 us.(Source clock is APB clock) */ + +#define NEC_HEADER_HIGH_US 9000 /*!< NEC protocol header: positive 9ms */ +#define NEC_HEADER_LOW_US 4500 /*!< NEC protocol header: negative 4.5ms*/ +#define NEC_BIT_ONE_HIGH_US 560 /*!< NEC protocol data bit 1: positive 0.56ms */ +#define NEC_BIT_ONE_LOW_US (2250-NEC_BIT_ONE_HIGH_US) /*!< NEC protocol data bit 1: negative 1.69ms */ +#define NEC_BIT_ZERO_HIGH_US 560 /*!< NEC protocol data bit 0: positive 0.56ms */ +#define NEC_BIT_ZERO_LOW_US (1120-NEC_BIT_ZERO_HIGH_US) /*!< NEC protocol data bit 0: negative 0.56ms */ +#define NEC_BIT_MARGIN 150 /*!< NEC parse margin time */ + +#define NEC_ITEM_DURATION(d) ((d & 0x7fff)*10/RMT_TICK_10_US) /*!< Parse duration time from memory register value */ +#define NEC_DATA_ITEM_NUM 34 /*!< NEC code item number: header + 32bit data + end */ +#define rmt_item32_tIMEOUT_US 9500 /*!< RMT receiver timeout value(us) */ + +/**************************************************************************************** + * + */ +inline bool nec_check_in_range(int duration_ticks, int target_us, int margin_us) { + if(( NEC_ITEM_DURATION(duration_ticks) < (target_us + margin_us)) + && ( NEC_ITEM_DURATION(duration_ticks) > (target_us - margin_us))) { + return true; + } else { + return false; + } +} + +/**************************************************************************************** + * + */ +static bool nec_header_if(rmt_item32_t* item) { + if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL) + && nec_check_in_range(item->duration0, NEC_HEADER_HIGH_US, NEC_BIT_MARGIN) + && nec_check_in_range(item->duration1, NEC_HEADER_LOW_US, NEC_BIT_MARGIN)) { + return true; + } + return false; +} + +/**************************************************************************************** + * + */ +static bool nec_bit_one_if(rmt_item32_t* item) { + if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL) + && nec_check_in_range(item->duration0, NEC_BIT_ONE_HIGH_US, NEC_BIT_MARGIN) + && nec_check_in_range(item->duration1, NEC_BIT_ONE_LOW_US, NEC_BIT_MARGIN)) { + return true; + } + return false; +} + +/**************************************************************************************** + * + */ +static bool nec_bit_zero_if(rmt_item32_t* item) { + if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL) + && nec_check_in_range(item->duration0, NEC_BIT_ZERO_HIGH_US, NEC_BIT_MARGIN) + && nec_check_in_range(item->duration1, NEC_BIT_ZERO_LOW_US, NEC_BIT_MARGIN)) { + return true; + } + return false; +} + +/**************************************************************************************** + * + */ +static int nec_parse_items(rmt_item32_t* item, int item_num, uint16_t* addr, uint16_t* data) { + int w_len = item_num; + if(w_len < NEC_DATA_ITEM_NUM) { + return -1; + } + int i = 0, j = 0; + if(!nec_header_if(item++)) { + return -1; + } + uint16_t addr_t = 0; + for(j = 15; j >= 0; j--) { + if(nec_bit_one_if(item)) { + addr_t |= (1 << j); + } else if(nec_bit_zero_if(item)) { + addr_t |= (0 << j); + } else { + return -1; + } + item++; + i++; + } + uint16_t data_t = 0; + for(j = 15; j >= 0; j--) { + if(nec_bit_one_if(item)) { + data_t |= (1 << j); + } else if(nec_bit_zero_if(item)) { + data_t |= (0 << j); + } else { + return -1; + } + item++; + i++; + } + *addr = addr_t; + *data = data_t; + return i; +} + +/**************************************************************************************** + * + */ +void infrared_receive(RingbufHandle_t rb, infrared_handler handler) { + size_t rx_size = 0; + rmt_item32_t* item = (rmt_item32_t*) xRingbufferReceive(rb, &rx_size, 10 / portTICK_RATE_MS); + + if (item) { + uint16_t addr, cmd; + int offset = 0; + + while (1) { + // parse data value from ringbuffer. + int res = nec_parse_items(item + offset, rx_size / 4 - offset, &addr, &cmd); + if (res > 0) { + offset += res + 1; + handler(addr, cmd); + ESP_LOGD(TAG, "RMT RCV --- addr: 0x%04x cmd: 0x%04x", addr, cmd); + } else break; + } + + // after parsing the data, return spaces to ringbuffer. + vRingbufferReturnItem(rb, (void*) item); + } +} + +/**************************************************************************************** + * + */ +void infrared_init(RingbufHandle_t *rb, int gpio) { + rmt_config_t rmt_rx; + + ESP_LOGI(TAG, "Starting Infrared Receiver on gpio %d", gpio); + + // initialize RMT driver + rmt_rx.channel = RMT_RX_CHANNEL; + rmt_rx.gpio_num = gpio; + rmt_rx.clk_div = RMT_CLK_DIV; + rmt_rx.mem_block_num = 1; + rmt_rx.rmt_mode = RMT_MODE_RX; + rmt_rx.rx_config.filter_en = true; + rmt_rx.rx_config.filter_ticks_thresh = 100; + rmt_rx.rx_config.idle_threshold = rmt_item32_tIMEOUT_US / 10 * (RMT_TICK_10_US); + rmt_config(&rmt_rx); + rmt_driver_install(rmt_rx.channel, 1000, 0); + + // get RMT RX ringbuffer + rmt_get_ringbuf_handle(RMT_RX_CHANNEL, rb); + rmt_rx_start(RMT_RX_CHANNEL, 1); +} \ No newline at end of file diff --git a/components/services/infrared.h b/components/services/infrared.h new file mode 100644 index 00000000..ea0ee202 --- /dev/null +++ b/components/services/infrared.h @@ -0,0 +1,17 @@ +/* + * (c) Philippe G. 2019, philippe_44@outlook.com + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + * + */ + +#pragma once + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/ringbuf.h" + +typedef void (*infrared_handler)(uint16_t addr, uint16_t cmd); +void infrared_receive(RingbufHandle_t rb, infrared_handler handler); +void infrared_init(RingbufHandle_t *rb, int gpio); diff --git a/components/services/monitor.c b/components/services/monitor.c index eb032244..82a51c50 100644 --- a/components/services/monitor.c +++ b/components/services/monitor.c @@ -140,24 +140,28 @@ bool spkfault_svc (void) { /**************************************************************************************** * */ -void set_jack_gpio(int gpio, char *value) { +#ifndef CONFIG_JACK_LOCKED +static void set_jack_gpio(int gpio, char *value) { if (strcasestr(value, "jack")) { char *p; jack.gpio = gpio; if ((p = strchr(value, ':')) != NULL) jack.active = atoi(p + 1); } } +#endif /**************************************************************************************** * */ -void set_spkfault_gpio(int gpio, char *value) { +#ifndef CONFIG_SPKFAULT_LOCKED +static void set_spkfault_gpio(int gpio, char *value) { if (strcasestr(value, "spkfault")) { char *p; spkfault.gpio = gpio; if ((p = strchr(value, ':')) != NULL) spkfault.active = atoi(p + 1); } } +#endif /**************************************************************************************** * diff --git a/components/squeezelite/controls.c b/components/squeezelite/controls.c index db4b54a2..f2a6c0f8 100644 --- a/components/squeezelite/controls.c +++ b/components/squeezelite/controls.c @@ -7,84 +7,225 @@ */ #include "squeezelite.h" +#include "config.h" #include "audio_controls.h" static log_level loglevel = lINFO; +#define DOWN_OFS 0x10000 +#define UP_OFS 0x20000 + +// numbers are simply 0..9 + +// arrow_right.down = 0001000e seems to be missing ... +enum { BUTN_POWER_FRONT = 0X0A, BUTN_ARROW_UP, BUTN_ARROW_DOWN, BUTN_ARROW_LEFT, BUTN_KNOB_PUSH, BUTN_SEARCH, + BUTN_REW, BUTN_FWD, BUTN_PLAY, BUTN_ADD, BUTN_BRIGHTNESS, BUTN_NOW_PLAYING, + BUTN_PAUSE = 0X17, BUTN_BROWSE, BUTN_VOLUP_FRONT, BUTN_VOLDOWN_FRONT, BUTN_SIZE, BUTN_VISUAL, BUTN_VOLUMEMODE, + BUTN_PRESET_1 = 0X23, BUTN_PRESET_2, BUTN_PRESET_3, BUTN_PRESET_4, BUTN_PRESET_5, BUTN_PRESET_6, BUTN_SNOOZE, + BUTN_KNOB_LEFT = 0X5A, BUTN_KNOB_RIGHT }; + +#define BUTN_ARROW_RIGHT BUTN_KNOB_PUSH + +#pragma pack(push, 1) + +struct BUTN_header { + char opcode[4]; + u32_t length; + u32_t jiffies; + u32_t button; +}; + +struct IR_header { + char opcode[4]; + u32_t length; + u32_t jiffies; + u8_t format; // unused + u8_t bits; // unused + u32_t code; +}; + +#pragma pack(pop) + static in_addr_t server_ip; static u16_t server_hport; static u16_t server_cport; static u8_t mac[6]; static void (*chained_notify)(in_addr_t, u16_t, u16_t); +static bool raw_mode; static void cli_send_cmd(char *cmd); -static void lms_volume_up(void) { - cli_send_cmd("button volup"); +/**************************************************************************************** + * Send BUTN + */ +static void sendBUTN(int code, bool pressed) { + struct BUTN_header pkt_header; + + memset(&pkt_header, 0, sizeof(pkt_header)); + memcpy(&pkt_header.opcode, "BUTN", 4); + + pkt_header.length = htonl(sizeof(pkt_header) - 8); + pkt_header.jiffies = htonl(gettime_ms()); + pkt_header.button = htonl(code + (pressed ? DOWN_OFS : UP_OFS)); + + LOG_INFO("sending BUTN code %04x %s", code, pressed ? "down" : "up"); + + LOCK_P; + send_packet((uint8_t *) &pkt_header, sizeof(pkt_header)); + UNLOCK_P; } -static void lms_volume_down(void) { - cli_send_cmd("button voldown"); +/**************************************************************************************** + * Send IR + */ +static void sendIR(u16_t addr, u16_t cmd) { + struct IR_header pkt_header; + + memset(&pkt_header, 0, sizeof(pkt_header)); + memcpy(&pkt_header.opcode, "IR ", 4); + + pkt_header.length = htonl(sizeof(pkt_header) - 8); + pkt_header.jiffies = htonl(gettime_ms()); + pkt_header.format = pkt_header.bits = 0; + pkt_header.code = htonl((addr << 16) | cmd); + + LOG_INFO("sending IR code %04x", (addr << 16) | cmd); + + LOCK_P; + send_packet((uint8_t *) &pkt_header, sizeof(pkt_header)); + UNLOCK_P; } -static void lms_toggle(void) { - cli_send_cmd("pause"); +static void lms_volume_up(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_VOLUP_FRONT, pressed); + } else { + cli_send_cmd("button volup"); + } } -static void lms_pause(void) { - cli_send_cmd("pause 1"); +static void lms_volume_down(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_VOLDOWN_FRONT, pressed); + } else { + cli_send_cmd("button voldown"); + } } -static void lms_play(void) { - cli_send_cmd("button play.single"); +static void lms_toggle(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_PAUSE, pressed); + } else { + cli_send_cmd("pause"); + } } -static void lms_stop(void) { +static void lms_pause(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_PAUSE, pressed); + } else { + cli_send_cmd("pause 1"); + } +} + +static void lms_play(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_PLAY, pressed); + } else { + cli_send_cmd("button play.single"); + } +} + +static void lms_stop(bool pressed) { cli_send_cmd("button stop"); } -static void lms_rew(void) { - cli_send_cmd("button rew.repeat"); +static void lms_rew(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_REW, pressed); + } else { + cli_send_cmd("button rew.repeat"); + } } -static void lms_fwd(void) { - cli_send_cmd("button fwd.repeat"); +static void lms_fwd(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_FWD, pressed); + } else { + cli_send_cmd("button fwd.repeat"); + } } -static void lms_prev(void) { - cli_send_cmd("button rew"); +static void lms_prev(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_REW, pressed); + } else { + cli_send_cmd("button rew"); + } } -static void lms_next(void) { - cli_send_cmd("button fwd"); +static void lms_next(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_FWD, pressed); + } else { + cli_send_cmd("button fwd"); + } } -static void lms_up(void) { - cli_send_cmd("button arrow_up"); +static void lms_up(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_ARROW_UP, pressed); + } else { + cli_send_cmd("button arrow_up"); + } } -static void lms_down(void) { - cli_send_cmd("button arrow_down"); +static void lms_down(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_ARROW_DOWN, pressed); + } else { + cli_send_cmd("button arrow_down"); + } } -static void lms_left(void) { - cli_send_cmd("button arrow_left"); +static void lms_left(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_ARROW_LEFT, pressed); + } else { + cli_send_cmd("button arrow_left"); + } } -static void lms_right(void) { - cli_send_cmd("button arrow_right"); +static void lms_right(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_ARROW_RIGHT, pressed); + } else { + cli_send_cmd("button arrow_right"); + } } -static void lms_knob_left(void) { - cli_send_cmd("button knob_left"); +static void lms_knob_left(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_KNOB_LEFT, pressed); + } else { + cli_send_cmd("button knob_left"); + } } -static void lms_knob_right(void) { - cli_send_cmd("button knob_right"); +static void lms_knob_right(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_KNOB_RIGHT, pressed); + } else { + cli_send_cmd("button knob_right"); + } } -static void lms_knob_push(void) { - cli_send_cmd("button knob_push"); +static void lms_knob_push(bool pressed) { + if (raw_mode) { + sendBUTN(BUTN_KNOB_PUSH, pressed); + } else { + cli_send_cmd("button knob_push"); + } } const actrls_t LMS_controls = { @@ -137,13 +278,27 @@ static void notify(in_addr_t ip, u16_t hport, u16_t cport) { if (chained_notify) (*chained_notify)(ip, hport, cport); } +/**************************************************************************************** + * IR handler + */ +static bool ir_handler(u16_t addr, u16_t cmd) { + sendIR(addr, cmd); + return true; +} + /**************************************************************************************** * Initialize controls - shall be called once from output_init_embedded */ void sb_controls_init(void) { - LOG_INFO("initializing CLI controls"); + char *p = config_alloc_get_default(NVS_TYPE_STR, "lms_ctrls_raw", "n", 0); + raw_mode = p && (*p == '1' || *p == 'Y' || *p == 'y'); + free(p); + + LOG_INFO("initializing audio (buttons/rotary/ir) controls (raw:%u)", raw_mode); + get_mac(mac); - actrls_set_default(LMS_controls, NULL); + actrls_set_default(LMS_controls, raw_mode, NULL, ir_handler); + chained_notify = server_notify; server_notify = notify; } \ No newline at end of file diff --git a/components/squeezelite/display.c b/components/squeezelite/display.c index 86d2d316..75459d6f 100644 --- a/components/squeezelite/display.c +++ b/components/squeezelite/display.c @@ -197,23 +197,20 @@ extern const uint8_t vu_bitmap[] asm("_binary_vu_data_start"); #define ANIM_SCREEN_1 0x04 #define ANIM_SCREEN_2 0x08 -static u8_t ANIC_resp = ANIM_NONE; -static uint16_t SETD_width; - #define SCROLL_STACK_SIZE (3*1024) #define LINELEN 40 static log_level loglevel = lINFO; static bool (*slimp_handler_chain)(u8_t *data, int len); -static void (*slimp_loop_chain)(void); static void (*notify_chain)(in_addr_t ip, u16_t hport, u16_t cport); static bool (*display_bus_chain)(void *from, enum display_bus_cmd_e cmd); #define max(a,b) (((a) > (b)) ? (a) : (b)) static void server(in_addr_t ip, u16_t hport, u16_t cport); -static void send_server(void); +static void sendSETD(u16_t width, u16_t height); +static void sendANIC(u8_t code); static bool handler(u8_t *data, int len); static bool display_bus_handler(void *from, enum display_bus_cmd_e cmd); static void vfdc_handler( u8_t *_data, int bytes_read); @@ -223,7 +220,6 @@ static void grfs_handler(u8_t *data, int len); static void grfg_handler(u8_t *data, int len); static void grfa_handler(u8_t *data, int len); static void visu_handler(u8_t *data, int len); - static void displayer_task(void* arg); /* scrolling undocumented information @@ -293,11 +289,13 @@ bool sb_display_init(void) { return false; } + // inform LMS of our screen dimensions + sendSETD(GDS_GetWidth(display), GDS_GetHeight(display)); + // need to force height to 32 maximum displayer.width = GDS_GetWidth(display); displayer.height = min(GDS_GetHeight(display), SB_HEIGHT); - SETD_width = displayer.width; - + // create visu configuration visu.bar_gap = 1; visu.speed = 100; @@ -319,9 +317,6 @@ bool sb_display_init(void) { slimp_handler_chain = slimp_handler; slimp_handler = handler; - slimp_loop_chain = slimp_loop; - slimp_loop = send_server; - notify_chain = server_notify; server_notify = server; @@ -358,52 +353,44 @@ static bool display_bus_handler(void *from, enum display_bus_cmd_e cmd) { else return true; } - /**************************************************************************************** - * Send message to server (ANIC at that time) + * Send ANImation Complete */ -static void send_server(void) { - /* - This complication is needed as we cannot send direclty to LMS, because - send_packet is not thread safe. So must subscribe to slimproto busy loop - end send from there - */ - if (ANIC_resp != ANIM_NONE) { - struct ANIC_header pkt_header; +static void sendANIC(u8_t code) { + struct ANIC_header pkt_header; - memset(&pkt_header, 0, sizeof(pkt_header)); - memcpy(&pkt_header.opcode, "ANIC", 4); - pkt_header.length = htonl(sizeof(pkt_header) - 8); - pkt_header.mode = ANIC_resp; + memset(&pkt_header, 0, sizeof(pkt_header)); + memcpy(&pkt_header.opcode, "ANIC", 4); + pkt_header.length = htonl(sizeof(pkt_header) - 8); + pkt_header.mode = code; - send_packet((uint8_t *) &pkt_header, sizeof(pkt_header)); + LOCK_P; + send_packet((uint8_t *) &pkt_header, sizeof(pkt_header)); + UNLOCK_P; +} - ANIC_resp = ANIM_NONE; - } - - if (SETD_width) { - struct SETD_header pkt_header; +/**************************************************************************************** + * Send SETD for width + */ +static void sendSETD(u16_t width, u16_t height) { + struct SETD_header pkt_header; - memset(&pkt_header, 0, sizeof(pkt_header)); - memcpy(&pkt_header.opcode, "SETD", 4); + memset(&pkt_header, 0, sizeof(pkt_header)); + memcpy(&pkt_header.opcode, "SETD", 4); - pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2 - pkt_header.length = htonl(sizeof(pkt_header) + 4 - 8); + pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2 + pkt_header.length = htonl(sizeof(pkt_header) + 4 - 8); - u16_t height = GDS_GetHeight(display); - LOG_INFO("sending dimension %ux%u", SETD_width, height); + LOG_INFO("sending dimension %ux%u", width, height); - SETD_width = htons(SETD_width); - height = htons(height); + width = htons(width); + height = htons(height); - send_packet((uint8_t *) &pkt_header, sizeof(pkt_header)); - send_packet((uint8_t *) &SETD_width, 2); - send_packet((uint8_t *) &height, 2); - - SETD_width = 0; - } - - if (slimp_loop_chain) (*slimp_loop_chain)(); + LOCK_P; + send_packet((uint8_t *) &pkt_header, sizeof(pkt_header)); + send_packet((uint8_t *) &width, 2); + send_packet((uint8_t *) &height, 2); + UNLOCK_P; } /**************************************************************************************** @@ -416,11 +403,13 @@ static void server(in_addr_t ip, u16_t hport, u16_t cport) { sprintf(msg, "%s:%hu", inet_ntoa(ip), hport); if (displayer.owned) GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, msg); - SETD_width = displayer.width; displayer.dirty = true; xSemaphoreGive(displayer.mutex); + // inform new LMS server of our capabilities + sendSETD(displayer.width, GDS_GetHeight(display)); + if (notify_chain) (*notify_chain)(ip, hport, cport); } @@ -1141,8 +1130,7 @@ static void displayer_task(void *args) { // see if we need to pause or if we are done if (scroller.mode) { - // can't call directly send_packet from slimproto as it's not re-entrant - ANIC_resp = ANIM_SCROLL_ONCE | ANIM_SCREEN_1; + sendANIC(ANIM_SCROLL_ONCE | ANIM_SCREEN_1); LOG_INFO("scroll-once terminated"); } else { scroller.wake = scroller.pause; diff --git a/components/squeezelite/embedded.c b/components/squeezelite/embedded.c index 92b1b2fe..1ac32b01 100644 --- a/components/squeezelite/embedded.c +++ b/components/squeezelite/embedded.c @@ -15,6 +15,8 @@ #include "esp_timer.h" #include "esp_wifi.h" +mutex_type slimp_mutex; + void get_mac(u8_t mac[]) { esp_read_mac(mac, ESP_MAC_WIFI_STA); } @@ -46,6 +48,7 @@ extern bool sb_display_init(void); u8_t custom_player_id = 12; void embedded_init(void) { + mutex_create(slimp_mutex); sb_controls_init(); if (sb_display_init()) custom_player_id = 100; } diff --git a/components/squeezelite/embedded.h b/components/squeezelite/embedded.h index ec710c8b..85c08003 100644 --- a/components/squeezelite/embedded.h +++ b/components/squeezelite/embedded.h @@ -58,6 +58,9 @@ void embedded_init(void); void register_external(void); void deregister_external(void); void decode_restore(int external); +extern mutex_type slimp_mutex; +#define LOCK_P mutex_lock(slimp_mutex) +#define UNLOCK_P mutex_unlock(slimp_mutex) // must provide or define as 0xffff u16_t get_RSSI(void); @@ -77,5 +80,5 @@ void output_visu_close(void); bool (*slimp_handler)(u8_t *data, int len); void (*slimp_loop)(void); void (*server_notify)(in_addr_t ip, u16_t hport, u16_t cport); - + #endif // EMBEDDED_H diff --git a/components/squeezelite/slimproto.c b/components/squeezelite/slimproto.c index f0b447d6..8522a811 100644 --- a/components/squeezelite/slimproto.c +++ b/components/squeezelite/slimproto.c @@ -67,6 +67,10 @@ event_event wake_e; #define UNLOCK_O mutex_unlock(outputbuf->mutex) #define LOCK_D mutex_lock(decode.mutex) #define UNLOCK_D mutex_unlock(decode.mutex) +#if !EMBEDDED +#define LOCK_P +#define UNLOCK_P +#endif #if IR #define LOCK_I mutex_lock(ir.mutex) #define UNLOCK_I mutex_unlock(ir.mutex) @@ -149,11 +153,12 @@ static void sendHELO(bool reconnect, const char *fixed_cap, const char *var_cap, LOG_INFO("mac: %02x:%02x:%02x:%02x:%02x:%02x", pkt.mac[0], pkt.mac[1], pkt.mac[2], pkt.mac[3], pkt.mac[4], pkt.mac[5]); LOG_INFO("cap: %s%s%s", base_cap, fixed_cap, var_cap); - + LOCK_P; send_packet((u8_t *)&pkt, sizeof(pkt)); send_packet((u8_t *)base_cap, strlen(base_cap)); send_packet((u8_t *)fixed_cap, strlen(fixed_cap)); send_packet((u8_t *)var_cap, strlen(var_cap)); + UNLOCK_P; } static void sendSTAT(const char *event, u32_t server_timestamp) { @@ -205,7 +210,9 @@ static void sendSTAT(const char *event, u32_t server_timestamp) { ms_played - now + status.stream_start, status.device_frames * 1000 / status.current_sample_rate, now - status.updated); } + LOCK_P; send_packet((u8_t *)&pkt, sizeof(pkt)); + UNLOCK_P; } static void sendDSCO(disconnect_code disconnect) { @@ -218,7 +225,9 @@ static void sendDSCO(disconnect_code disconnect) { LOG_DEBUG("DSCO: %d", disconnect); + LOCK_P; send_packet((u8_t *)&pkt, sizeof(pkt)); + UNLOCK_P; } static void sendRESP(const char *header, size_t len) { @@ -229,9 +238,11 @@ static void sendRESP(const char *header, size_t len) { pkt_header.length = htonl(sizeof(pkt_header) + len - 8); LOG_DEBUG("RESP"); - + + LOCK_P; send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); send_packet((u8_t *)header, len); + UNLOCK_P; } static void sendMETA(const char *meta, size_t len) { @@ -243,8 +254,10 @@ static void sendMETA(const char *meta, size_t len) { LOG_DEBUG("META"); + LOCK_P; send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); send_packet((u8_t *)meta, len); + UNLOCK_P; } static void sendSETDName(const char *name) { @@ -258,8 +271,10 @@ static void sendSETDName(const char *name) { LOG_DEBUG("set playername: %s", name); + LOCK_P; send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); send_packet((u8_t *)name, strlen(name) + 1); + UNLOCK_P; } #if IR @@ -274,8 +289,9 @@ void sendIR(u32_t code, u32_t ts) { pkt.ir_code = htonl(code); LOG_DEBUG("IR: ir code: 0x%x ts: %u", code, ts); - + LOCK_P; send_packet((u8_t *)&pkt, sizeof(pkt)); + UNLOCK_P; } #endif diff --git a/main/esp_app_main.c b/main/esp_app_main.c index 083a6a3f..abc89f3a 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -284,6 +284,9 @@ void register_default_nvs(){ ESP_LOGD(TAG,"Registering default Audio control board type %s, value ","actrls_config"); config_set_default(NVS_TYPE_STR, "actrls_config", "", 0); + ESP_LOGD(TAG,"Registering default value for key %s", "lms_ctrls_raw"); + config_set_default(NVS_TYPE_STR, "lms_ctrls_raw", "n", 0); + ESP_LOGD(TAG,"Registering default Audio control board type %s, value %s", "rotary_config", CONFIG_ROTARY_ENCODER); config_set_default(NVS_TYPE_STR, "rotary_config", CONFIG_ROTARY_ENCODER, 0); @@ -392,7 +395,7 @@ void app_main() ESP_LOGD(TAG,"Getting audio control mapping "); char *actrls_config = config_alloc_get_default(NVS_TYPE_STR, "actrls_config", NULL, 0); - if (actrls_init_json(actrls_config, true) == ESP_OK) { + if (actrls_init(actrls_config) == ESP_OK) { ESP_LOGD(TAG,"Initializing audio control buttons type %s", actrls_config); } else { ESP_LOGD(TAG,"No audio control buttons");