Revamped how volume changes are handled to also support remote volume control
This commit is contained in:
parent
36a27b81f3
commit
f95bcb503a
|
@ -1,3 +1,3 @@
|
||||||
idf_component_register(SRCS "src/main.cpp" "src/helper.cpp" "src/wav.cpp" "src/storage.cpp" "src/i2s.cpp" "src/bluetooth.cpp" "src/avrcp.cpp" "src/a2dp.cpp" "src/twai.cpp"
|
idf_component_register(SRCS "src/main.cpp" "src/helper.cpp" "src/wav.cpp" "src/storage.cpp" "src/i2s.cpp" "src/bluetooth.cpp" "src/avrcp.cpp" "src/a2dp.cpp" "src/twai.cpp" "src/volume.cpp"
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
EMBED_FILES "assets/connect.wav" "assets/disconnect.wav")
|
EMBED_FILES "assets/connect.wav" "assets/disconnect.wav")
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
|
|
||||||
namespace twai {
|
namespace twai {
|
||||||
void init();
|
void init();
|
||||||
|
void change_volume(bool up);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
12
software/main/include/volume.h
Normal file
12
software/main/include/volume.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <sys/lock.h>
|
||||||
|
|
||||||
|
namespace volume_controller {
|
||||||
|
void init();
|
||||||
|
void set_from_radio(int volume);
|
||||||
|
void set_from_remote(int volume);
|
||||||
|
|
||||||
|
uint8_t current();
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
#include "esp_avrc_api.h"
|
#include "esp_avrc_api.h"
|
||||||
|
|
||||||
#include "avrcp.h"
|
#include "avrcp.h"
|
||||||
|
#include "volume.h"
|
||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
|
|
||||||
#define AVRCP_TAG "APP_AVRCP"
|
#define AVRCP_TAG "APP_AVRCP"
|
||||||
|
@ -9,8 +10,6 @@
|
||||||
static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap;
|
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 esp_avrc_playback_stat_t playback_status = ESP_AVRC_PLAYBACK_STOPPED;
|
||||||
|
|
||||||
static uint8_t volume = 127;
|
|
||||||
static _lock_t volume_lock;
|
|
||||||
static bool volume_notify = false;
|
static bool volume_notify = false;
|
||||||
|
|
||||||
static void playback_changed() {
|
static void playback_changed() {
|
||||||
|
@ -33,13 +32,6 @@ static void notify_handler(uint8_t event_id, esp_avrc_rn_param_t *event_paramete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_volume_value(uint8_t v) {
|
|
||||||
ESP_LOGI(AVRCP_TAG, "Setting internal volume value to %i", v);
|
|
||||||
_lock_acquire(&volume_lock);
|
|
||||||
volume = v;
|
|
||||||
_lock_release(&volume_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rc_ct_callback(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) {
|
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);
|
ESP_LOGD(AVRCP_TAG, "%s event %d", __func__, event);
|
||||||
|
|
||||||
|
@ -81,8 +73,7 @@ static void rc_tg_callback(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t*
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT:
|
case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT:
|
||||||
ESP_LOGI(AVRCP_TAG, "AVRC set absolute volume: %d%%", (int)param->set_abs_vol.volume * 100/ 0x7f);
|
ESP_LOGI(AVRCP_TAG, "AVRC set absolute volume: %d%%", (int)param->set_abs_vol.volume * 100/ 0x7f);
|
||||||
// The phone want to change the volume
|
volume_controller::set_from_remote(param->set_abs_vol.volume);
|
||||||
set_volume_value(param->set_abs_vol.volume);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT:
|
case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT:
|
||||||
|
@ -97,7 +88,7 @@ static void rc_tg_callback(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t*
|
||||||
// This will require some sort of CAN bus access
|
// This will require some sort of CAN bus access
|
||||||
volume_notify = true;
|
volume_notify = true;
|
||||||
esp_avrc_rn_param_t rn_param;
|
esp_avrc_rn_param_t rn_param;
|
||||||
rn_param.volume = volume;
|
rn_param.volume = volume_controller::current();
|
||||||
esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_INTERIM, &rn_param);
|
esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_INTERIM, &rn_param);
|
||||||
} else if (param->reg_ntf.event_id == ESP_AVRC_RN_PLAY_POS_CHANGED) {
|
} else if (param->reg_ntf.event_id == ESP_AVRC_RN_PLAY_POS_CHANGED) {
|
||||||
ESP_LOGI(AVRCP_TAG, "We can change the play position?");
|
ESP_LOGI(AVRCP_TAG, "We can change the play position?");
|
||||||
|
@ -112,6 +103,14 @@ static void rc_tg_callback(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::init() {
|
void avrcp::init() {
|
||||||
ESP_LOGI(AVRCP_TAG, "Initializing AVRCP");
|
ESP_LOGI(AVRCP_TAG, "Initializing AVRCP");
|
||||||
|
|
||||||
|
@ -147,14 +146,6 @@ bool avrcp::is_playing() {
|
||||||
return playback_status == ESP_AVRC_PLAYBACK_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() {
|
void avrcp::play() {
|
||||||
ESP_LOGI(AVRCP_TAG, "Playing");
|
ESP_LOGI(AVRCP_TAG, "Playing");
|
||||||
send_cmd(ESP_AVRC_PT_CMD_PLAY);
|
send_cmd(ESP_AVRC_PT_CMD_PLAY);
|
||||||
|
@ -198,18 +189,10 @@ void avrcp::seek_backward() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void avrcp::set_volume(uint8_t v) {
|
void avrcp::set_volume(uint8_t v) {
|
||||||
// Make sure the volume is actually changed
|
|
||||||
if (v == volume) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_volume_value(v);
|
|
||||||
|
|
||||||
// @TODO What we the device supports remote volume but we can not send it yet?
|
|
||||||
if (volume_notify) {
|
if (volume_notify) {
|
||||||
ESP_LOGI(AVRCP_TAG, "Setting remote volume value to %i", v);
|
ESP_LOGI(AVRCP_TAG, "Setting remote volume value to %i", v);
|
||||||
esp_avrc_rn_param_t rn_param;
|
esp_avrc_rn_param_t rn_param;
|
||||||
rn_param.volume = volume;
|
rn_param.volume = v;
|
||||||
esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_CHANGED, &rn_param);
|
esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_CHANGED, &rn_param);
|
||||||
volume_notify = false;
|
volume_notify = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "twai.h"
|
#include "twai.h"
|
||||||
#include "avrcp.h"
|
#include "avrcp.h"
|
||||||
#include "can_data.h"
|
#include "can_data.h"
|
||||||
|
#include "volume.h"
|
||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
|
|
||||||
#define TWAI_TAG "APP_TWAI"
|
#define TWAI_TAG "APP_TWAI"
|
||||||
|
@ -47,6 +48,40 @@ static void send_test() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @TODO Make sure that the other buttons are set to match the current state
|
||||||
|
// That way way the scroll wheel and long pressing will not have unintented effects
|
||||||
|
void twai::change_volume(bool up) {
|
||||||
|
can::Buttons buttons;
|
||||||
|
memset(&buttons, 0, sizeof(buttons));
|
||||||
|
|
||||||
|
if (up) {
|
||||||
|
buttons.volume_up = true;
|
||||||
|
} else {
|
||||||
|
buttons.volume_down = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
twai_message_t message;
|
||||||
|
memset(&message, 0, sizeof(message));
|
||||||
|
|
||||||
|
message.identifier = BUTTONS_ID;
|
||||||
|
message.data_length_code = 3;
|
||||||
|
message.data[0] = ((uint8_t*)&buttons)[0];
|
||||||
|
message.data[1] = ((uint8_t*)&buttons)[1];
|
||||||
|
message.data[2] = ((uint8_t*)&buttons)[2];
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < message.data_length_code; i++) {
|
||||||
|
ESP_LOGI(TWAI_TAG, "%i: 0x%X", i, message.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (twai_transmit(&message, pdMS_TO_TICKS(1000)) == ESP_OK) {
|
||||||
|
ESP_LOGI(TWAI_TAG, "Message queued for transmission");
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TWAI_TAG, "Failed tp queue message for transmission");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void listen(void*) {
|
static void listen(void*) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
twai_message_t message;
|
twai_message_t message;
|
||||||
|
@ -86,7 +121,10 @@ static void listen(void*) {
|
||||||
case VOLUME_ID:
|
case VOLUME_ID:
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
can::Volume volume = can::convert<can::Volume>(message.data, message.data_length_code);
|
can::Volume volume = can::convert<can::Volume>(message.data, message.data_length_code);
|
||||||
avrcp::set_volume(ceil(volume.volume * 4.2f));
|
// Only update the volume if the volume has actually changed
|
||||||
|
if (!volume._1) {
|
||||||
|
volume_controller::set_from_radio(volume.volume);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
88
software/main/src/volume.cpp
Normal file
88
software/main/src/volume.cpp
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "volume.h"
|
||||||
|
#include "avrcp.h"
|
||||||
|
#include "twai.h"
|
||||||
|
|
||||||
|
#define VOLUME_TAG "APP_VOLUME"
|
||||||
|
|
||||||
|
// 0-127
|
||||||
|
static uint8_t volume;
|
||||||
|
// 0-127
|
||||||
|
static uint8_t remote_volume;
|
||||||
|
// 0-30
|
||||||
|
static uint8_t radio_volume;
|
||||||
|
|
||||||
|
static bool synced = false;
|
||||||
|
static _lock_t lock;
|
||||||
|
|
||||||
|
void volume_controller::set_from_radio(int v) {
|
||||||
|
ESP_LOGI(VOLUME_TAG, "Volume on radio updated: %i (0-30)", v);
|
||||||
|
// Update the radio volume
|
||||||
|
_lock_acquire(&lock);
|
||||||
|
radio_volume = v;
|
||||||
|
_lock_release(&lock);
|
||||||
|
|
||||||
|
if (!synced) {
|
||||||
|
ESP_LOGI(VOLUME_TAG, "Not updating internal and remote (SYNCING)");
|
||||||
|
// In this case we are still adjusting the volume of the car to match the remote/internal volume
|
||||||
|
// So we do not want to update these values based on the radio
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the 0 - 30 range of the radio to 0 - 127
|
||||||
|
uint8_t full_range = ceil(v * 4.2f);
|
||||||
|
ESP_LOGI(VOLUME_TAG, "Updating internal and remote to: %i (0-127)", full_range);
|
||||||
|
|
||||||
|
// Update the remote volume
|
||||||
|
avrcp::set_volume(full_range);
|
||||||
|
|
||||||
|
// @TODO Somehow make sure we actually the remote volume is actually set before updating the value
|
||||||
|
_lock_acquire(&lock);
|
||||||
|
volume = full_range;
|
||||||
|
remote_volume = full_range;
|
||||||
|
_lock_release(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void volume_controller::set_from_remote(int v) {
|
||||||
|
ESP_LOGI(VOLUME_TAG, "Volume on remote updated: %i (0-127)", v);
|
||||||
|
|
||||||
|
_lock_acquire(&lock);
|
||||||
|
remote_volume = v;
|
||||||
|
volume = v;
|
||||||
|
|
||||||
|
synced = false;
|
||||||
|
_lock_release(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t volume_controller::current() {
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void correct_volume(void*) {
|
||||||
|
for (;;) {
|
||||||
|
if (!synced) {
|
||||||
|
uint8_t target = floor(volume / 4.2f);
|
||||||
|
|
||||||
|
if (radio_volume == target) {
|
||||||
|
ESP_LOGI(VOLUME_TAG, "SYNCED!");
|
||||||
|
_lock_acquire(&lock);
|
||||||
|
synced = true;
|
||||||
|
_lock_release(&lock);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(VOLUME_TAG, "Adjusting volume: %i", radio_volume < target);
|
||||||
|
twai::change_volume(radio_volume < target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void volume_controller::init() {
|
||||||
|
xTaskCreatePinnedToCore(correct_volume, "Correct volume", 2048, nullptr, 0, nullptr, 1);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user