From 99c56641e9ee531a0553ff19422009dd667a3add Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 6 Feb 2024 12:38:11 +1100 Subject: [PATCH] fix various of bluetooth issues connecting and disconnecting is a bit more consistent now! --- src/app_console/app_console.cpp | 9 ++- src/audio/audio_fsm.cpp | 1 + src/drivers/bluetooth.cpp | 102 +++++++++++++++--------- src/drivers/include/bluetooth.hpp | 15 ++-- src/drivers/include/bluetooth_types.hpp | 5 ++ src/drivers/include/nvs.hpp | 4 +- src/drivers/nvs.cpp | 35 +++++--- src/ui/ui_fsm.cpp | 39 ++++----- 8 files changed, 134 insertions(+), 76 deletions(-) diff --git a/src/app_console/app_console.cpp b/src/app_console/app_console.cpp index 6df63d5c..33f41306 100644 --- a/src/app_console/app_console.cpp +++ b/src/app_console/app_console.cpp @@ -23,6 +23,8 @@ #include "FreeRTOSConfig.h" #include "audio_events.hpp" #include "audio_fsm.hpp" +#include "bluetooth.hpp" +#include "bluetooth_types.hpp" #include "database.hpp" #include "esp_app_desc.h" #include "esp_console.h" @@ -419,8 +421,11 @@ int CmdBtList(int argc, char** argv) { std::cout << "index out of range" << std::endl; return -1; } - AppConsole::sServices->bluetooth().SetPreferredDevice( - devices[index].address); + drivers::bluetooth::MacAndName dev{ + .mac = devices[index].address, + .name = {devices[index].name.data(), devices[index].name.size()}, + }; + AppConsole::sServices->bluetooth().SetPreferredDevice(dev); } else { std::cout << "mac\t\trssi\tname" << std::endl; for (const auto& device : devices) { diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp index b1c5c2b8..b060d3e4 100644 --- a/src/audio/audio_fsm.cpp +++ b/src/audio/audio_fsm.cpp @@ -125,6 +125,7 @@ void AudioState::react(const OutputModeChanged& ev) { break; } sOutput->SetMode(IAudioOutput::Modes::kOnPaused); + sSampleConverter->SetOutput(sOutput); } auto AudioState::playTrack(database::TrackId id) -> void { diff --git a/src/drivers/bluetooth.cpp b/src/drivers/bluetooth.cpp index f58c98a3..4ea56ab0 100644 --- a/src/drivers/bluetooth.cpp +++ b/src/drivers/bluetooth.cpp @@ -23,6 +23,7 @@ #include "esp_wifi.h" #include "esp_wifi_types.h" #include "freertos/portmacro.h" +#include "freertos/projdefs.h" #include "memory_resource.hpp" #include "nvs.hpp" #include "tinyfsm/include/tinyfsm.hpp" @@ -89,8 +90,11 @@ auto Bluetooth::ConnectedDevice() -> std::optional { return {}; } auto looking_for = bluetooth::BluetoothState::preferred_device(); + if (!looking_for) { + return {}; + } for (const auto& dev : bluetooth::BluetoothState::devices()) { - if (dev.address == looking_for) { + if (dev.address == looking_for->mac) { return dev; } } @@ -118,16 +122,21 @@ auto Bluetooth::KnownDevices() -> std::vector { return out; } -auto Bluetooth::SetPreferredDevice(const bluetooth::mac_addr_t& mac) -> void { - if (mac == bluetooth::BluetoothState::preferred_device()) { +auto Bluetooth::SetPreferredDevice(std::optional dev) + -> void { + auto cur = bluetooth::BluetoothState::preferred_device(); + if (dev && cur && dev->mac == cur->mac) { return; } - bluetooth::BluetoothState::preferred_device(mac); + ESP_LOGI(kTag, "preferred is '%s' (%u%u%u%u%u%u)", dev->name.c_str(), + dev->mac[0], dev->mac[1], dev->mac[2], dev->mac[3], dev->mac[4], + dev->mac[5]); + bluetooth::BluetoothState::preferred_device(dev); tinyfsm::FsmList::dispatch( bluetooth::events::PreferredDeviceChanged{}); } -auto Bluetooth::PreferredDevice() -> std::optional { +auto Bluetooth::PreferredDevice() -> std::optional { return bluetooth::BluetoothState::preferred_device(); } @@ -291,8 +300,8 @@ Scanner* BluetoothState::sScanner_; std::mutex BluetoothState::sDevicesMutex_{}; std::map BluetoothState::sDevices_{}; -std::optional BluetoothState::sPreferredDevice_{}; -std::optional BluetoothState::sCurrentDevice_{}; +std::optional BluetoothState::sPreferredDevice_{}; +std::optional BluetoothState::sConnectingDevice_{}; bool BluetoothState::sIsDiscoveryAllowed_{false}; std::atomic BluetoothState::sSource_; @@ -313,12 +322,12 @@ auto BluetoothState::devices() -> std::vector { return out; } -auto BluetoothState::preferred_device() -> std::optional { +auto BluetoothState::preferred_device() -> std::optional { std::lock_guard lock{sDevicesMutex_}; return sPreferredDevice_; } -auto BluetoothState::preferred_device(std::optional addr) -> void { +auto BluetoothState::preferred_device(std::optional addr) -> void { std::lock_guard lock{sDevicesMutex_}; sPreferredDevice_ = addr; } @@ -349,18 +358,18 @@ auto BluetoothState::event_handler(std::function cb) -> void { } auto BluetoothState::react(const events::DeviceDiscovered& ev) -> void { - ESP_LOGI(kTag, "discovered device %s", ev.device.name.c_str()); bool is_preferred = false; { std::lock_guard lock{sDevicesMutex_}; + bool already_known = sDevices_.contains(ev.device.address); sDevices_[ev.device.address] = ev.device; - if (ev.device.address == sPreferredDevice_) { - sCurrentDevice_ = ev.device; + if (sPreferredDevice_ && ev.device.address == sPreferredDevice_->mac) { + sConnectingDevice_ = sPreferredDevice_; is_preferred = true; } - if (sEventHandler_) { + if (sEventHandler_ && !already_known) { std::invoke(sEventHandler_, Event::kKnownDevicesChanged); } } @@ -395,39 +404,39 @@ void Disabled::entry() { esp_bluedroid_disable(); esp_bluedroid_deinit(); esp_bt_controller_disable(); + esp_bt_controller_deinit(); } void Disabled::exit() { if (sIsDiscoveryAllowed_) { ESP_LOGI(kTag, "bt enabled, beginning discovery"); sScanner_->ScanContinuously(); - } else if (sPreferredDevice_) { - ESP_LOGI(kTag, "bt enabled, checking for preferred device"); - sScanner_->ScanOnce(); } else { - ESP_LOGI(kTag, "bt enabled, but not scanning"); + ESP_LOGI(kTag, "bt enabled, scanning once"); + sScanner_->ScanOnce(); } } void Disabled::react(const events::Enable&) { esp_bt_controller_config_t config = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - if (esp_bt_controller_init(&config) != ESP_OK) { - ESP_LOGE(kTag, "initialize controller failed"); + esp_err_t err; + if ((err = esp_bt_controller_init(&config) != ESP_OK)) { + ESP_LOGE(kTag, "initialize controller failed %s", esp_err_to_name(err)); return; } - if (esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) != ESP_OK) { - ESP_LOGE(kTag, "enable controller failed"); + if ((err = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) != ESP_OK)) { + ESP_LOGE(kTag, "enable controller failed %s", esp_err_to_name(err)); return; } - if (esp_bluedroid_init() != ESP_OK) { - ESP_LOGE(kTag, "initialize bluedroid failed"); + if ((err = esp_bluedroid_init() != ESP_OK)) { + ESP_LOGE(kTag, "initialize bluedroid failed %s", esp_err_to_name(err)); return; } - if (esp_bluedroid_enable() != ESP_OK) { - ESP_LOGE(kTag, "enable bluedroid failed"); + if ((err = esp_bluedroid_enable() != ESP_OK)) { + ESP_LOGE(kTag, "enable bluedroid failed %s", esp_err_to_name(err)); return; } @@ -459,7 +468,12 @@ void Disabled::react(const events::Enable&) { // Don't let anyone interact with us before we're ready. esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); - transit(); + if (sPreferredDevice_) { + sConnectingDevice_ = sPreferredDevice_; + transit(); + } else { + transit(); + } } void Idle::entry() { @@ -474,12 +488,12 @@ void Idle::react(const events::PreferredDeviceChanged& ev) { bool is_discovered = false; { std::lock_guard lock{sDevicesMutex_}; - if (sPreferredDevice_ && sDevices_.contains(sPreferredDevice_.value())) { + if (sPreferredDevice_ && sDevices_.contains(sPreferredDevice_->mac)) { is_discovered = true; } } if (is_discovered) { - ESP_LOGI(kTag, "selected known device"); + sConnectingDevice_ = sPreferredDevice_; transit(); } } @@ -489,8 +503,13 @@ void Idle::react(const events::internal::Gap& ev) { } void Connecting::entry() { - ESP_LOGI(kTag, "connecting to device"); - esp_a2d_source_connect(sPreferredDevice_.value().data()); + sScanner_->StopScanning(); + + auto dev = sConnectingDevice_; + ESP_LOGI(kTag, "connecting to '%s' (%u%u%u%u%u%u)", dev->name.c_str(), + dev->mac[0], dev->mac[1], dev->mac[2], dev->mac[3], dev->mac[4], + dev->mac[5]); + esp_a2d_source_connect(sConnectingDevice_->mac.data()); if (sEventHandler_) { std::invoke(sEventHandler_, Event::kConnectionStateChanged); @@ -498,6 +517,7 @@ void Connecting::entry() { } void Connecting::exit() { + ESP_LOGI(kTag, "connecting finished"); if (sEventHandler_) { std::invoke(sEventHandler_, Event::kConnectionStateChanged); } @@ -518,7 +538,7 @@ void Connecting::react(const events::internal::Gap& ev) { case ESP_BT_GAP_AUTH_CMPL_EVT: if (ev.param->auth_cmpl.stat != ESP_BT_STATUS_SUCCESS) { ESP_LOGE(kTag, "auth failed"); - sPreferredDevice_ = {}; + sConnectingDevice_ = {}; transit(); } break; @@ -528,22 +548,22 @@ void Connecting::react(const events::internal::Gap& ev) { break; case ESP_BT_GAP_PIN_REQ_EVT: ESP_LOGW(kTag, "device needs a pin to connect"); - sPreferredDevice_ = {}; + sConnectingDevice_ = {}; transit(); break; case ESP_BT_GAP_CFM_REQ_EVT: ESP_LOGW(kTag, "user needs to do cfm. idk man."); - sPreferredDevice_ = {}; + sConnectingDevice_ = {}; transit(); break; case ESP_BT_GAP_KEY_NOTIF_EVT: ESP_LOGW(kTag, "the device is telling us a password??"); - sPreferredDevice_ = {}; + sConnectingDevice_ = {}; transit(); break; case ESP_BT_GAP_KEY_REQ_EVT: ESP_LOGW(kTag, "the device wants a password!"); - sPreferredDevice_ = {}; + sConnectingDevice_ = {}; transit(); break; case ESP_BT_GAP_MODE_CHG_EVT: @@ -583,8 +603,13 @@ void Connecting::react(const events::internal::A2dp& ev) { void Connected::entry() { ESP_LOGI(kTag, "entering connected state"); + connected_to_ = sConnectingDevice_->mac; + sPreferredDevice_ = sConnectingDevice_; + sConnectingDevice_ = {}; + auto stored_pref = sStorage_->PreferredBluetoothDevice(); - if (stored_pref != sPreferredDevice_) { + if (!stored_pref || (sPreferredDevice_->name != stored_pref->name || + sPreferredDevice_->mac != stored_pref->mac)) { sStorage_->PreferredBluetoothDevice(sPreferredDevice_); } // TODO: if we already have a source, immediately start playing @@ -592,15 +617,16 @@ void Connected::entry() { void Connected::exit() { ESP_LOGI(kTag, "exiting connected state"); + esp_a2d_source_disconnect(connected_to_.data()); } void Connected::react(const events::Disable& ev) { - // TODO: disconnect gracefully transit(); } void Connected::react(const events::PreferredDeviceChanged& ev) { - // TODO: disconnect, move to connecting? or scanning? + sConnectingDevice_ = sPreferredDevice_; + transit(); } void Connected::react(const events::SourceChanged& ev) { diff --git a/src/drivers/include/bluetooth.hpp b/src/drivers/include/bluetooth.hpp index 4aefbc42..291d049d 100644 --- a/src/drivers/include/bluetooth.hpp +++ b/src/drivers/include/bluetooth.hpp @@ -44,8 +44,8 @@ class Bluetooth { auto KnownDevices() -> std::vector; - auto SetPreferredDevice(const bluetooth::mac_addr_t& mac) -> void; - auto PreferredDevice() -> std::optional; + auto SetPreferredDevice(std::optional dev) -> void; + auto PreferredDevice() -> std::optional; auto SetSource(StreamBufferHandle_t) -> void; auto SetEventHandler(std::function cb) -> void; @@ -107,8 +107,8 @@ class BluetoothState : public tinyfsm::Fsm { static auto devices() -> std::vector; - static auto preferred_device() -> std::optional; - static auto preferred_device(std::optional) -> void; + static auto preferred_device() -> std::optional; + static auto preferred_device(std::optional) -> void; static auto scanning() -> bool; static auto discovery() -> bool; @@ -142,8 +142,8 @@ class BluetoothState : public tinyfsm::Fsm { static std::mutex sDevicesMutex_; static std::map sDevices_; - static std::optional sPreferredDevice_; - static std::optional sCurrentDevice_; + static std::optional sPreferredDevice_; + static std::optional sConnectingDevice_; static bool sIsDiscoveryAllowed_; static std::atomic sSource_; @@ -205,6 +205,9 @@ class Connected : public BluetoothState { void react(const events::internal::Avrc& ev) override; using BluetoothState::react; + + private: + mac_addr_t connected_to_; }; } // namespace bluetooth diff --git a/src/drivers/include/bluetooth_types.hpp b/src/drivers/include/bluetooth_types.hpp index 518771e5..87da0ab5 100644 --- a/src/drivers/include/bluetooth_types.hpp +++ b/src/drivers/include/bluetooth_types.hpp @@ -11,6 +11,11 @@ namespace bluetooth { typedef std::array mac_addr_t; +struct MacAndName { + mac_addr_t mac; + std::string name; +}; + struct Device { mac_addr_t address; std::pmr::string name; diff --git a/src/drivers/include/nvs.hpp b/src/drivers/include/nvs.hpp index f592b1c3..1184b72c 100644 --- a/src/drivers/include/nvs.hpp +++ b/src/drivers/include/nvs.hpp @@ -25,8 +25,8 @@ class NvsStorage { auto LockPolarity() -> bool; auto LockPolarity(bool) -> bool; - auto PreferredBluetoothDevice() -> std::optional; - auto PreferredBluetoothDevice(std::optional) -> bool; + auto PreferredBluetoothDevice() -> std::optional; + auto PreferredBluetoothDevice(std::optional) -> bool; enum class Output : uint8_t { kHeadphones = 0, diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp index 8c9aa361..ab623d01 100644 --- a/src/drivers/nvs.cpp +++ b/src/drivers/nvs.cpp @@ -25,7 +25,8 @@ namespace drivers { static constexpr uint8_t kSchemaVersion = 1; static constexpr char kKeyVersion[] = "ver"; -static constexpr char kKeyBluetooth[] = "bt"; +static constexpr char kKeyBluetoothMac[] = "bt_mac"; +static constexpr char kKeyBluetoothName[] = "bt_name"; static constexpr char kKeyOutput[] = "out"; static constexpr char kKeyBrightness[] = "bright"; static constexpr char kKeyAmpMaxVolume[] = "hp_vol_max"; @@ -100,22 +101,36 @@ auto NvsStorage::LockPolarity(bool p) -> bool { } auto NvsStorage::PreferredBluetoothDevice() - -> std::optional { - bluetooth::mac_addr_t out{0}; - size_t size = out.size(); - if (nvs_get_blob(handle_, kKeyBluetooth, out.data(), &size) != ESP_OK) { + -> std::optional { + bluetooth::mac_addr_t mac{0}; + size_t size = mac.size(); + if (nvs_get_blob(handle_, kKeyBluetoothMac, mac.data(), &size) != ESP_OK) { return {}; } + size_t name_len = 0; + if (nvs_get_str(handle_, kKeyBluetoothName, NULL, &name_len) != ESP_OK) { + } + char* raw_name = new char[name_len]; + if (nvs_get_str(handle_, kKeyBluetoothName, raw_name, &name_len) != ESP_OK) { + delete[] raw_name; + return {}; + } + bluetooth::MacAndName out{ + .mac = mac, + .name = {raw_name, name_len}, + }; + delete[] raw_name; return out; } auto NvsStorage::PreferredBluetoothDevice( - std::optional addr) -> bool { - if (!addr) { - nvs_erase_key(handle_, kKeyBluetooth); + std::optional dev) -> bool { + if (!dev) { + nvs_erase_key(handle_, kKeyBluetoothMac); + nvs_erase_key(handle_, kKeyBluetoothName); } else { - nvs_set_blob(handle_, kKeyBluetooth, addr.value().data(), - addr.value().size()); + nvs_set_blob(handle_, kKeyBluetoothMac, dev->mac.data(), dev->mac.size()); + nvs_set_str(handle_, kKeyBluetoothName, dev->name.c_str()); } return nvs_commit(handle_) == ESP_OK; } diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index b3546d55..55c8c84b 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -77,30 +77,33 @@ lua::Property UiState::sBatteryPct{0}; lua::Property UiState::sBatteryMv{0}; lua::Property UiState::sBatteryCharging{false}; -lua::Property UiState::sBluetoothEnabled { - false, [](const lua::LuaValue& val) { - if (!std::holds_alternative(val)) { - return false; - } - if (std::get(val)) { - sServices->bluetooth().Enable(); - sServices->bluetooth().SetDeviceDiscovery(true); - sServices->nvs().OutputMode(drivers::NvsStorage::Output::kBluetooth); - } else { - sServices->bluetooth().Disable(); - sServices->nvs().OutputMode(drivers::NvsStorage::Output::kHeadphones); - } - events::Audio().Dispatch(audio::OutputModeChanged{}); - return true; - } -}; +lua::Property UiState::sBluetoothEnabled{ + false, [](const lua::LuaValue& val) { + if (!std::holds_alternative(val)) { + return false; + } + if (std::get(val)) { + sServices->nvs().OutputMode(drivers::NvsStorage::Output::kBluetooth); + sServices->bluetooth().Enable(); + sServices->bluetooth().SetDeviceDiscovery(true); + } else { + sServices->nvs().OutputMode(drivers::NvsStorage::Output::kHeadphones); + sServices->bluetooth().Disable(); + } + events::Audio().Dispatch(audio::OutputModeChanged{}); + return true; + }}; lua::Property UiState::sBluetoothConnected{false}; lua::Property UiState::sBluetoothPairedDevice{ std::monostate{}, [](const lua::LuaValue& val) { if (std::holds_alternative(val)) { auto dev = std::get(val); - sServices->bluetooth().SetPreferredDevice(dev.address); + sServices->bluetooth().SetPreferredDevice( + drivers::bluetooth::MacAndName{ + .mac = dev.address, + .name = {dev.name.data(), dev.name.size()}, + }); } return false; }};