From a92fe02c59863cc471e89ee2c9847caffab3807b Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Tue, 14 Jun 2022 21:26:11 +0200 Subject: [PATCH] Refactored and current playstatus is now received from device --- software/main/include/avrcp.h | 5 ++ software/main/include/helper.h | 14 ++++ software/main/src/avrcp.cpp | 100 +++++++++++++++++++++++++++- software/main/src/can.cpp | 116 ++------------------------------- software/main/src/helper.cpp | 30 +++++++++ software/main/src/main.cpp | 36 ++++------ 6 files changed, 168 insertions(+), 133 deletions(-) diff --git a/software/main/include/avrcp.h b/software/main/include/avrcp.h index 0b10d72..37ead03 100644 --- a/software/main/include/avrcp.h +++ b/software/main/include/avrcp.h @@ -2,4 +2,9 @@ namespace avrcp { void init(); + bool is_playing(); + + void play_pause(); + void forward(); + void backward(); } diff --git a/software/main/include/helper.h b/software/main/include/helper.h index c24311d..2d07709 100644 --- a/software/main/include/helper.h +++ b/software/main/include/helper.h @@ -6,3 +6,17 @@ const char* addr_to_str(esp_bd_addr_t bda); const char* connection_state_to_str(esp_a2d_connection_state_t state); +class MultiPurposeButton { + public: + MultiPurposeButton(void(*short_press)(), void(*long_press)(), uint8_t threshold = 5); + + void tick(bool current); + + private: + void(*short_press)(); + void(*long_press)(); + uint8_t threshold = 5; + bool previous = false; + uint8_t counter = 0; + bool acted = false; +}; diff --git a/software/main/src/avrcp.cpp b/software/main/src/avrcp.cpp index 65e4d04..03bb082 100644 --- a/software/main/src/avrcp.cpp +++ b/software/main/src/avrcp.cpp @@ -6,8 +6,66 @@ #define AVRCP_TAG "APP_AVRCP" +static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap; +static esp_avrc_playback_stat_t playback_status = ESP_AVRC_PLAYBACK_STOPPED; + +static void playback_changed() { + if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap, ESP_AVRC_RN_PLAY_STATUS_CHANGE)) { + esp_avrc_ct_send_register_notification_cmd(1, ESP_AVRC_RN_PLAY_STATUS_CHANGE, 0); + } +} + +static void notify_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter) { + switch (event_id) { + case ESP_AVRC_RN_PLAY_STATUS_CHANGE: + ESP_LOGI(AVRCP_TAG, "Playback status changed: 0x%x", event_parameter->playback); + playback_status = event_parameter->playback; + playback_changed(); + break; + + default: + ESP_LOGI(AVRCP_TAG, "unhandled event: %d", event_id); + break; + } +} + +static void rc_ct_callback(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) { + ESP_LOGD(AVRCP_TAG, "%s event %d", __func__, event); + + switch (event) { + case ESP_AVRC_CT_CONNECTION_STATE_EVT: { + uint8_t *bda = param->conn_stat.remote_bda; + ESP_LOGI(AVRCP_TAG, "AVRC conn_state event: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]", + param->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + if (param->conn_stat.connected) { + /* get remote supported event_ids of peer AVRCP Target */ + esp_avrc_ct_send_get_rn_capabilities_cmd(0); + } else { + /* clear peer notification capability record */ + s_avrc_peer_rn_cap.bits = 0; + } + break; + } + + case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: + notify_handler(param->change_ntf.event_id, ¶m->change_ntf.event_parameter); + break; + + case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: + ESP_LOGI(AVRCP_TAG, "remote rn_cap: count %d, bitmask 0x%x", param->get_rn_caps_rsp.cap_count, param->get_rn_caps_rsp.evt_set.bits); + s_avrc_peer_rn_cap.bits = param->get_rn_caps_rsp.evt_set.bits; + playback_changed(); + break; + + default: + ESP_LOGE(AVRCP_TAG, "%s unhandled event %d", __func__, event); + break; + } +} + static void rc_tg_callback(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t* param) { - ESP_LOGD(AVRCP_TAG, "%s evt %d", __func__, event); + ESP_LOGD(AVRCP_TAG, "%s event %d", __func__, event); switch (event) { case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: @@ -34,7 +92,7 @@ static void rc_tg_callback(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t* break; default: - ESP_LOGE(AVRCP_TAG, "%s unhandled evt %d", __func__, event); + ESP_LOGE(AVRCP_TAG, "%s unhandled event %d", __func__, event); break; } } @@ -42,6 +100,10 @@ static void rc_tg_callback(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t* void avrcp::init() { ESP_LOGI(AVRCP_TAG, "Initializing AVRCP"); + if (esp_avrc_ct_init() == ESP_OK) { + esp_avrc_ct_register_callback(rc_ct_callback); + } + // Initialize AVRCP target if (esp_avrc_tg_init() == ESP_OK) { esp_avrc_tg_register_callback(rc_tg_callback); @@ -54,3 +116,37 @@ void avrcp::init() { ESP_LOGE(AVRCP_TAG, "esp_avrc_tg_init failed"); } } + +bool avrcp::is_playing() { + return playback_status == ESP_AVRC_PLAYBACK_PLAYING; +} + +static void send_cmd(esp_avrc_pt_cmd_t cmd) { + esp_err_t ret = esp_avrc_ct_send_passthrough_cmd(0, cmd, ESP_AVRC_PT_CMD_STATE_PRESSED); + ESP_ERROR_CHECK(ret); + + ret = esp_avrc_ct_send_passthrough_cmd(1, cmd, ESP_AVRC_PT_CMD_STATE_RELEASED); + ESP_ERROR_CHECK(ret); +} + +void avrcp::play_pause() { + if (is_playing()) { + ESP_LOGI(AVRCP_TAG, "Pausing"); + send_cmd(ESP_AVRC_PT_CMD_PAUSE); + } else { + ESP_LOGI(AVRCP_TAG, "Playing"); + send_cmd(ESP_AVRC_PT_CMD_PLAY); + } +} + +void avrcp::forward() { + ESP_LOGI(AVRCP_TAG, "Forward"); + + send_cmd(ESP_AVRC_PT_CMD_FORWARD); +} + +void avrcp::backward() { + ESP_LOGI(AVRCP_TAG, "Backward"); + + send_cmd(ESP_AVRC_PT_CMD_BACKWARD); +} diff --git a/software/main/src/can.cpp b/software/main/src/can.cpp index 2c884f1..b385f81 100644 --- a/software/main/src/can.cpp +++ b/software/main/src/can.cpp @@ -1,7 +1,6 @@ #include #include -#include "esp_avrc_api.h" #include "esp_timer.h" #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" @@ -14,6 +13,8 @@ #include "can.h" #include "can_data.h" +#include "avrcp.h" +#include "helper.h" #define CAN_TAG "APP_CAN" @@ -458,118 +459,15 @@ static void read_message(spi_device_handle_t spi) { read_can_msg(spi, MCP_READ_RX0, &id, &ext, &rtr_bit, &len, buf); } - /* ESP_LOGI(CAN_TAG, "Received: id=%lu, ext=%i, rtr_bit=%i, len=%i", id, ext, rtr_bit, len); */ - - // @TODO Implement the length check in a more elegant manner - /* switch (id) { */ - /* case BUTTONS_ID: */ - /* print_buttons(*(can::Buttons*)buf); */ - /* break; */ - - /* case RADIO_ID: */ - /* print_radio(*(can::Radio*)buf); */ - /* break; */ - - /* default: */ - /* break; */ - /* } */ - // @TODO Only do this if we actually are on AUX2 if (id == BUTTONS_ID) { - static can::Buttons previous; + static MultiPurposeButton button_forward(avrcp::play_pause, avrcp::forward); + static MultiPurposeButton button_backward(nullptr, avrcp::backward); + can::Buttons buttons = *(can::Buttons*)buf; - // Forward - { - static uint8_t counter = 0; - static bool disabled = false; - - if (buttons.forward && !disabled) { - counter++; - - ESP_LOGI(CAN_TAG, "Counter: %i", counter); - - if (counter > 5) { - ESP_LOGI(CAN_TAG, "Forward"); - // Fast forward - esp_err_t ret = esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_FORWARD, ESP_AVRC_PT_CMD_STATE_PRESSED); - ESP_ERROR_CHECK(ret); - - ret = esp_avrc_ct_send_passthrough_cmd(1, ESP_AVRC_PT_CMD_FORWARD, ESP_AVRC_PT_CMD_STATE_RELEASED); - ESP_ERROR_CHECK(ret); - - // Disable actions from happening again until we release the button - disabled = true; - } - } - - if (previous.forward != buttons.forward && !buttons.forward) { - if (counter <= 5) { - // @TODO Get this from the actual device instead of assuming that on startup it is falce - static bool play_status = false; - play_status = !play_status; - - ESP_LOGI(CAN_TAG, "Play button has been pressed"); - - if (play_status) { - ESP_LOGI(CAN_TAG, "Playing"); - // @TODO Abstract this away so that we are not directly interfacing with AVRC things - esp_err_t ret = esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED); - ESP_ERROR_CHECK(ret); - - ret = esp_avrc_ct_send_passthrough_cmd(1, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_RELEASED); - ESP_ERROR_CHECK(ret); - } else { - ESP_LOGI(CAN_TAG, "Pausing"); - esp_err_t ret = esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_PRESSED); - ESP_ERROR_CHECK(ret); - - ret = esp_avrc_ct_send_passthrough_cmd(1, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_RELEASED); - ESP_ERROR_CHECK(ret); - } - } - - counter = 0; - disabled = false; - } - } - - // Backwards - { - static uint8_t counter = 0; - static bool disabled = false; - - if (buttons.backward && !disabled) { - counter++; - - ESP_LOGI(CAN_TAG, "Counter: %i", counter); - - if (counter > 5) { - ESP_LOGI(CAN_TAG, "Backwards"); - // Fast forward - esp_err_t ret = esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_BACKWARD, ESP_AVRC_PT_CMD_STATE_PRESSED); - ESP_ERROR_CHECK(ret); - - ret = esp_avrc_ct_send_passthrough_cmd(1, ESP_AVRC_PT_CMD_BACKWARD, ESP_AVRC_PT_CMD_STATE_RELEASED); - ESP_ERROR_CHECK(ret); - - // Disable actions from happening again until we release the button - disabled = true; - } - } - - if (previous.backward != buttons.backward && !buttons.backward) { - if (counter <= 5) { - // @TODO What do we do when we just press this button? - // Might be nice to somehow trigger the google assistant, altough that requires Handsfree profile, which might interfere with the car itself - } - - counter = 0; - disabled = false; - } - } - - previous = buttons; + button_forward.tick(buttons.forward); + button_backward.tick(buttons.backward); } } diff --git a/software/main/src/helper.cpp b/software/main/src/helper.cpp index 89b9003..3dd36c8 100644 --- a/software/main/src/helper.cpp +++ b/software/main/src/helper.cpp @@ -12,3 +12,33 @@ const char* connection_state_to_str(esp_a2d_connection_state_t state) { const char* states[4] = {"Disconnected", "Connecting", "Connected", "Disconnecting"}; return states[state]; } + +MultiPurposeButton::MultiPurposeButton(void(*short_press)(), void(*long_press)(), uint8_t threshold) : short_press(short_press), long_press(long_press), threshold(threshold) {} + +void MultiPurposeButton::tick(bool current) { + if (current && !acted) { + if (counter >= threshold) { + // Long press + if (long_press) { + ESP_LOGI("MPB", "Long press!"); + long_press(); + } + acted = true; + } + + counter++; + } else if (previous != current && !current) { + // The button just got released + + // Short press + if (counter < threshold) { + if (short_press) { + ESP_LOGI("MPB", "Short press!"); + short_press(); + } + } + + counter = 0; + acted = false; + } +} diff --git a/software/main/src/main.cpp b/software/main/src/main.cpp index 3e5c83d..cd9ab97 100644 --- a/software/main/src/main.cpp +++ b/software/main/src/main.cpp @@ -16,23 +16,23 @@ #define APP_TAG "APP" -void play_task(void*) { +void task(void*) { for (;;) { - esp_err_t ret = esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_PRESSED); + ESP_LOGI("TEST", "Pressing"); + + static uint8_t cmd = ESP_AVRC_PT_CMD_PLAY; + esp_err_t ret = esp_avrc_ct_send_passthrough_cmd(0, cmd, ESP_AVRC_PT_CMD_STATE_PRESSED); + ESP_ERROR_CHECK(ret); + ret = esp_avrc_ct_send_passthrough_cmd(1, cmd, ESP_AVRC_PT_CMD_STATE_RELEASED); ESP_ERROR_CHECK(ret); - ret = esp_avrc_ct_send_passthrough_cmd(1, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_RELEASED); - ESP_ERROR_CHECK(ret); + vTaskDelay(5000 / portTICK_PERIOD_MS); - vTaskDelay(2000 / portTICK_PERIOD_MS); - - ret = esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED); - ESP_ERROR_CHECK(ret); - - ret = esp_avrc_ct_send_passthrough_cmd(1, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_RELEASED); - ESP_ERROR_CHECK(ret); - - vTaskDelay(2000 / portTICK_PERIOD_MS); + if (cmd == ESP_AVRC_PT_CMD_PLAY) { + cmd = ESP_AVRC_PT_CMD_PAUSE; + } else { + cmd = ESP_AVRC_PT_CMD_PLAY; + } } } @@ -49,16 +49,8 @@ extern "C" void app_main() { a2dp::connect_to_last(); - /* xTaskCreate(play_task, "Play task", 2048, nullptr, 0, nullptr); */ + /* xTaskCreate(task, "Task", 2048, nullptr, 0, nullptr); */ can::init(); - - /* uint8_t button_buffer[3] = { 0b01000110, 22, 0x00 }; */ - /* can::Buttons buttons = *(can::Buttons*)button_buffer; */ - /* print_buttons(buttons); */ - - /* uint8_t radio_buffer[4] = { 0b10100000, 0b01000000, 0b00110000, 0x00 }; */ - /* can::Radio radio = *(can::Radio*)radio_buffer; */ - /* print_radio(radio); */ }