Improve bt scanning responsiveness
This commit is contained in:
+216
-115
@@ -97,6 +97,15 @@ auto Bluetooth::SetPreferredDevice(const bluetooth::mac_addr_t& mac) -> void {
|
||||
bluetooth::events::PreferredDeviceChanged{});
|
||||
}
|
||||
|
||||
auto Bluetooth::SetDeviceDiscovery(bool allowed) -> void {
|
||||
if (allowed == bluetooth::BluetoothState::discovery()) {
|
||||
return;
|
||||
}
|
||||
bluetooth::BluetoothState::discovery(allowed);
|
||||
tinyfsm::FsmList<bluetooth::BluetoothState>::dispatch(
|
||||
bluetooth::events::DiscoveryChanged{});
|
||||
}
|
||||
|
||||
auto Bluetooth::SetSource(StreamBufferHandle_t src) -> void {
|
||||
if (src == bluetooth::BluetoothState::source()) {
|
||||
return;
|
||||
@@ -122,12 +131,144 @@ auto DeviceName() -> std::pmr::string {
|
||||
|
||||
namespace bluetooth {
|
||||
|
||||
static constexpr uint8_t kDiscoveryTimeSeconds = 5;
|
||||
static constexpr uint8_t kDiscoveryMaxResults = 0;
|
||||
|
||||
Scanner::Scanner() : enabled_(false), is_discovering_(false) {}
|
||||
|
||||
auto Scanner::ScanContinuously() -> void {
|
||||
if (enabled_) {
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(kTag, "beginning continuous scan");
|
||||
enabled_ = true;
|
||||
if (enabled_ && !is_discovering_) {
|
||||
ScanOnce();
|
||||
}
|
||||
}
|
||||
|
||||
auto Scanner::ScanOnce() -> void {
|
||||
if (is_discovering_) {
|
||||
return;
|
||||
}
|
||||
is_discovering_ = true;
|
||||
ESP_LOGI(kTag, "scanning...");
|
||||
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY,
|
||||
kDiscoveryTimeSeconds, kDiscoveryMaxResults);
|
||||
}
|
||||
|
||||
auto Scanner::StopScanning() -> void {
|
||||
ESP_LOGI(kTag, "stopping scan");
|
||||
enabled_ = false;
|
||||
}
|
||||
|
||||
auto Scanner::StopScanningNow() -> void {
|
||||
StopScanning();
|
||||
if (is_discovering_) {
|
||||
ESP_LOGI(kTag, "cancelling scan");
|
||||
is_discovering_ = false;
|
||||
esp_bt_gap_cancel_discovery();
|
||||
}
|
||||
}
|
||||
|
||||
auto Scanner::HandleGapEvent(const events::internal::Gap& ev) -> void {
|
||||
switch (ev.type) {
|
||||
case ESP_BT_GAP_DISC_RES_EVT:
|
||||
if (ev.param != nullptr) {
|
||||
// Handle device discovery even if we've been told to stop discovering.
|
||||
HandleDeviceDiscovery(*ev.param);
|
||||
}
|
||||
break;
|
||||
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT:
|
||||
if (ev.param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) {
|
||||
ESP_LOGI(kTag, "discovery finished");
|
||||
if (enabled_) {
|
||||
ESP_LOGI(kTag, "restarting discovery");
|
||||
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY,
|
||||
kDiscoveryTimeSeconds,
|
||||
kDiscoveryMaxResults);
|
||||
} else {
|
||||
is_discovering_ = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_BT_GAP_MODE_CHG_EVT:
|
||||
// todo: mode change. is this important?
|
||||
ESP_LOGI(kTag, "GAP mode changed");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(kTag, "unhandled GAP event: %u", ev.type);
|
||||
}
|
||||
}
|
||||
|
||||
auto Scanner::HandleDeviceDiscovery(const esp_bt_gap_cb_param_t& param)
|
||||
-> void {
|
||||
Device device{};
|
||||
std::copy(std::begin(param.disc_res.bda), std::end(param.disc_res.bda),
|
||||
device.address.begin());
|
||||
|
||||
// Discovery results come back to us as a grab-bag of different key/value
|
||||
// pairs. Parse these into a more structured format first so that they're
|
||||
// easier to work with.
|
||||
uint8_t* eir = nullptr;
|
||||
for (size_t i = 0; i < param.disc_res.num_prop; i++) {
|
||||
esp_bt_gap_dev_prop_t& property = param.disc_res.prop[i];
|
||||
switch (property.type) {
|
||||
case ESP_BT_GAP_DEV_PROP_BDNAME:
|
||||
// Ignored -- we get the device name from the EIR field instead.
|
||||
break;
|
||||
case ESP_BT_GAP_DEV_PROP_COD:
|
||||
device.class_of_device = *reinterpret_cast<uint32_t*>(property.val);
|
||||
break;
|
||||
case ESP_BT_GAP_DEV_PROP_RSSI:
|
||||
device.signal_strength = *reinterpret_cast<int8_t*>(property.val);
|
||||
break;
|
||||
case ESP_BT_GAP_DEV_PROP_EIR:
|
||||
eir = reinterpret_cast<uint8_t*>(property.val);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(kTag, "unknown GAP param %u", property.type);
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore devices with missing or malformed data.
|
||||
if (!esp_bt_gap_is_valid_cod(device.class_of_device) || eir == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: ESP-IDF example code does additional filterering by class of device
|
||||
// at this point. We don't! Per the Bluetooth spec; "No assumptions should be
|
||||
// made about specific functionality or characteristics of any application
|
||||
// based solely on the assignment of the Major or Minor device class."
|
||||
|
||||
// Resolve the name of the device.
|
||||
uint8_t* name;
|
||||
uint8_t length;
|
||||
name = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME,
|
||||
&length);
|
||||
if (!name) {
|
||||
name = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME,
|
||||
&length);
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
device.name = std::pmr::string{reinterpret_cast<char*>(name),
|
||||
static_cast<size_t>(length)};
|
||||
events::DeviceDiscovered ev{.device = device};
|
||||
tinyfsm::FsmList<bluetooth::BluetoothState>::dispatch(ev);
|
||||
}
|
||||
|
||||
NvsStorage* BluetoothState::sStorage_;
|
||||
Scanner* BluetoothState::sScanner_;
|
||||
|
||||
std::mutex BluetoothState::sDevicesMutex_{};
|
||||
std::map<mac_addr_t, Device> BluetoothState::sDevices_{};
|
||||
std::optional<mac_addr_t> BluetoothState::sPreferredDevice_{};
|
||||
mac_addr_t BluetoothState::sCurrentDevice_;
|
||||
mac_addr_t BluetoothState::sCurrentDevice_{0};
|
||||
bool BluetoothState::sIsDiscoveryAllowed_{false};
|
||||
|
||||
std::atomic<StreamBufferHandle_t> BluetoothState::sSource_;
|
||||
std::function<void(Event)> BluetoothState::sEventHandler_;
|
||||
@@ -152,11 +293,21 @@ auto BluetoothState::preferred_device() -> std::optional<mac_addr_t> {
|
||||
return sPreferredDevice_;
|
||||
}
|
||||
|
||||
auto BluetoothState::preferred_device(const mac_addr_t& addr) -> void {
|
||||
auto BluetoothState::preferred_device(std::optional<mac_addr_t> addr) -> void {
|
||||
std::lock_guard lock{sDevicesMutex_};
|
||||
sPreferredDevice_ = addr;
|
||||
}
|
||||
|
||||
auto BluetoothState::discovery() -> bool {
|
||||
std::lock_guard lock{sDevicesMutex_};
|
||||
return sIsDiscoveryAllowed_;
|
||||
}
|
||||
|
||||
auto BluetoothState::discovery(bool en) -> void {
|
||||
std::lock_guard lock{sDevicesMutex_};
|
||||
sIsDiscoveryAllowed_ = en;
|
||||
}
|
||||
|
||||
auto BluetoothState::source() -> StreamBufferHandle_t {
|
||||
std::lock_guard lock{sDevicesMutex_};
|
||||
return sSource_.load();
|
||||
@@ -172,21 +323,67 @@ auto BluetoothState::event_handler(std::function<void(Event)> cb) -> void {
|
||||
sEventHandler_ = cb;
|
||||
}
|
||||
|
||||
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<std::mutex> lock{sDevicesMutex_};
|
||||
sDevices_[ev.device.address] = ev.device;
|
||||
|
||||
if (ev.device.address == sPreferredDevice_) {
|
||||
sCurrentDevice_ = ev.device.address;
|
||||
is_preferred = true;
|
||||
}
|
||||
|
||||
if (sEventHandler_) {
|
||||
std::invoke(sEventHandler_, Event::kKnownDevicesChanged);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_preferred && is_in_state<Idle>()) {
|
||||
ESP_LOGI(kTag, "new device is preferred. connecting.");
|
||||
transit<Connecting>();
|
||||
}
|
||||
}
|
||||
|
||||
auto BluetoothState::react(const events::DiscoveryChanged& ev) -> void {
|
||||
if (sIsDiscoveryAllowed_) {
|
||||
sScanner_->ScanContinuously();
|
||||
} else {
|
||||
sScanner_->StopScanning();
|
||||
}
|
||||
}
|
||||
|
||||
static bool sIsFirstEntry = true;
|
||||
|
||||
void Disabled::entry() {
|
||||
if (sIsFirstEntry) {
|
||||
// We only use BT Classic, to claw back ~60KiB from the BLE firmware.
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_BLE);
|
||||
sScanner_ = new Scanner();
|
||||
sIsFirstEntry = false;
|
||||
return;
|
||||
}
|
||||
|
||||
sScanner_->StopScanningNow();
|
||||
|
||||
esp_bluedroid_disable();
|
||||
esp_bluedroid_deinit();
|
||||
esp_bt_controller_disable();
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -237,103 +434,18 @@ 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<Scanning>();
|
||||
transit<Idle>();
|
||||
}
|
||||
|
||||
static constexpr uint8_t kDiscoveryTimeSeconds = 10;
|
||||
static constexpr uint8_t kDiscoveryMaxResults = 0;
|
||||
|
||||
void Scanning::entry() {
|
||||
ESP_LOGI(kTag, "scanning for devices");
|
||||
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY,
|
||||
kDiscoveryTimeSeconds, kDiscoveryMaxResults);
|
||||
void Idle::entry() {
|
||||
ESP_LOGI(kTag, "bt is idle");
|
||||
}
|
||||
|
||||
void Scanning::exit() {
|
||||
esp_bt_gap_cancel_discovery();
|
||||
}
|
||||
|
||||
void Scanning::react(const events::Disable& ev) {
|
||||
void Idle::react(const events::Disable& ev) {
|
||||
transit<Disabled>();
|
||||
}
|
||||
|
||||
auto Scanning::OnDeviceDiscovered(esp_bt_gap_cb_param_t* param) -> void {
|
||||
Device device{};
|
||||
std::copy(std::begin(param->disc_res.bda), std::end(param->disc_res.bda),
|
||||
device.address.begin());
|
||||
|
||||
// Discovery results come back to us as a grab-bag of different key/value
|
||||
// pairs. Parse these into a more structured format first so that they're
|
||||
// easier to work with.
|
||||
uint8_t* eir = nullptr;
|
||||
for (size_t i = 0; i < param->disc_res.num_prop; i++) {
|
||||
esp_bt_gap_dev_prop_t& property = param->disc_res.prop[i];
|
||||
switch (property.type) {
|
||||
case ESP_BT_GAP_DEV_PROP_BDNAME:
|
||||
// Ignored -- we get the device name from the EIR field instead.
|
||||
break;
|
||||
case ESP_BT_GAP_DEV_PROP_COD:
|
||||
device.class_of_device = *reinterpret_cast<uint32_t*>(property.val);
|
||||
break;
|
||||
case ESP_BT_GAP_DEV_PROP_RSSI:
|
||||
device.signal_strength = *reinterpret_cast<int8_t*>(property.val);
|
||||
break;
|
||||
case ESP_BT_GAP_DEV_PROP_EIR:
|
||||
eir = reinterpret_cast<uint8_t*>(property.val);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(kTag, "unknown GAP param %u", property.type);
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore devices with missing or malformed data.
|
||||
if (!esp_bt_gap_is_valid_cod(device.class_of_device) || eir == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: ESP-IDF example code does additional filterering by class of device
|
||||
// at this point. We don't! Per the Bluetooth spec; "No assumptions should be
|
||||
// made about specific functionality or characteristics of any application
|
||||
// based solely on the assignment of the Major or Minor device class."
|
||||
|
||||
// Resolve the name of the device.
|
||||
uint8_t* name;
|
||||
uint8_t length;
|
||||
name = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME,
|
||||
&length);
|
||||
if (!name) {
|
||||
name = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME,
|
||||
&length);
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
device.name = std::pmr::string{reinterpret_cast<char*>(name),
|
||||
static_cast<size_t>(length)};
|
||||
|
||||
bool is_preferred = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{sDevicesMutex_};
|
||||
sDevices_[device.address] = device;
|
||||
|
||||
if (device.address == sPreferredDevice_) {
|
||||
sCurrentDevice_ = device.address;
|
||||
is_preferred = true;
|
||||
}
|
||||
|
||||
if (sEventHandler_) {
|
||||
std::invoke(sEventHandler_, Event::kKnownDevicesChanged);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_preferred) {
|
||||
transit<Connecting>();
|
||||
}
|
||||
}
|
||||
|
||||
void Scanning::react(const events::PreferredDeviceChanged& ev) {
|
||||
void Idle::react(const events::PreferredDeviceChanged& ev) {
|
||||
bool is_discovered = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{sDevicesMutex_};
|
||||
@@ -342,28 +454,13 @@ void Scanning::react(const events::PreferredDeviceChanged& ev) {
|
||||
}
|
||||
}
|
||||
if (is_discovered) {
|
||||
ESP_LOGI(kTag, "selected known device");
|
||||
transit<Connecting>();
|
||||
}
|
||||
}
|
||||
|
||||
void Scanning::react(const events::internal::Gap& ev) {
|
||||
switch (ev.type) {
|
||||
case ESP_BT_GAP_DISC_RES_EVT:
|
||||
OnDeviceDiscovered(ev.param);
|
||||
break;
|
||||
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT:
|
||||
if (ev.param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) {
|
||||
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY,
|
||||
kDiscoveryTimeSeconds, kDiscoveryMaxResults);
|
||||
}
|
||||
break;
|
||||
case ESP_BT_GAP_MODE_CHG_EVT:
|
||||
// todo: mode change. is this important?
|
||||
ESP_LOGI(kTag, "GAP mode changed");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(kTag, "unhandled GAP event: %u", ev.type);
|
||||
}
|
||||
void Idle::react(const events::internal::Gap& ev) {
|
||||
sScanner_->HandleGapEvent(ev);
|
||||
}
|
||||
|
||||
void Connecting::entry() {
|
||||
@@ -375,6 +472,7 @@ void Connecting::exit() {}
|
||||
|
||||
void Connecting::react(const events::Disable& ev) {
|
||||
// TODO: disconnect gracefully
|
||||
transit<Disabled>();
|
||||
}
|
||||
|
||||
void Connecting::react(const events::PreferredDeviceChanged& ev) {
|
||||
@@ -382,12 +480,13 @@ void Connecting::react(const events::PreferredDeviceChanged& ev) {
|
||||
}
|
||||
|
||||
void Connecting::react(const events::internal::Gap& ev) {
|
||||
sScanner_->HandleGapEvent(ev);
|
||||
switch (ev.type) {
|
||||
case ESP_BT_GAP_AUTH_CMPL_EVT:
|
||||
if (ev.param->auth_cmpl.stat != ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGE(kTag, "auth failed");
|
||||
sPreferredDevice_ = {};
|
||||
transit<Scanning>();
|
||||
transit<Idle>();
|
||||
}
|
||||
break;
|
||||
case ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT:
|
||||
@@ -397,22 +496,22 @@ void Connecting::react(const events::internal::Gap& ev) {
|
||||
case ESP_BT_GAP_PIN_REQ_EVT:
|
||||
ESP_LOGW(kTag, "device needs a pin to connect");
|
||||
sPreferredDevice_ = {};
|
||||
transit<Scanning>();
|
||||
transit<Idle>();
|
||||
break;
|
||||
case ESP_BT_GAP_CFM_REQ_EVT:
|
||||
ESP_LOGW(kTag, "user needs to do cfm. idk man.");
|
||||
sPreferredDevice_ = {};
|
||||
transit<Scanning>();
|
||||
transit<Idle>();
|
||||
break;
|
||||
case ESP_BT_GAP_KEY_NOTIF_EVT:
|
||||
ESP_LOGW(kTag, "the device is telling us a password??");
|
||||
sPreferredDevice_ = {};
|
||||
transit<Scanning>();
|
||||
transit<Idle>();
|
||||
break;
|
||||
case ESP_BT_GAP_KEY_REQ_EVT:
|
||||
ESP_LOGW(kTag, "the device wants a password!");
|
||||
sPreferredDevice_ = {};
|
||||
transit<Scanning>();
|
||||
transit<Idle>();
|
||||
break;
|
||||
case ESP_BT_GAP_MODE_CHG_EVT:
|
||||
ESP_LOGI(kTag, "GAP mode changed");
|
||||
@@ -464,6 +563,7 @@ void Connected::exit() {
|
||||
|
||||
void Connected::react(const events::Disable& ev) {
|
||||
// TODO: disconnect gracefully
|
||||
transit<Disabled>();
|
||||
}
|
||||
|
||||
void Connected::react(const events::PreferredDeviceChanged& ev) {
|
||||
@@ -481,6 +581,7 @@ void Connected::react(const events::SourceChanged& ev) {
|
||||
}
|
||||
|
||||
void Connected::react(const events::internal::Gap& ev) {
|
||||
sScanner_->HandleGapEvent(ev);
|
||||
switch (ev.type) {
|
||||
case ESP_BT_GAP_MODE_CHG_EVT:
|
||||
// todo: is this important?
|
||||
|
||||
@@ -32,6 +32,12 @@ class Bluetooth {
|
||||
auto Disable() -> void;
|
||||
auto IsEnabled() -> bool;
|
||||
|
||||
/*
|
||||
* Sets whether or not the bluetooth stack is allowed to actively scan for
|
||||
* new devices.
|
||||
*/
|
||||
auto SetDeviceDiscovery(bool) -> void;
|
||||
|
||||
auto KnownDevices() -> std::vector<bluetooth::Device>;
|
||||
auto SetPreferredDevice(const bluetooth::mac_addr_t& mac) -> void;
|
||||
|
||||
@@ -47,6 +53,10 @@ struct Disable : public tinyfsm::Event {};
|
||||
|
||||
struct PreferredDeviceChanged : public tinyfsm::Event {};
|
||||
struct SourceChanged : public tinyfsm::Event {};
|
||||
struct DiscoveryChanged : public tinyfsm::Event {};
|
||||
struct DeviceDiscovered : public tinyfsm::Event {
|
||||
const Device& device;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
struct Gap : public tinyfsm::Event {
|
||||
@@ -64,13 +74,37 @@ struct Avrc : public tinyfsm::Event {
|
||||
} // namespace internal
|
||||
} // namespace events
|
||||
|
||||
/*
|
||||
* Utility for managing scanning, independent of the current connection state.
|
||||
*/
|
||||
class Scanner {
|
||||
public:
|
||||
Scanner();
|
||||
|
||||
auto ScanContinuously() -> void;
|
||||
auto ScanOnce() -> void;
|
||||
auto StopScanning() -> void;
|
||||
auto StopScanningNow() -> void;
|
||||
|
||||
auto HandleGapEvent(const events::internal::Gap&) -> void;
|
||||
|
||||
private:
|
||||
bool enabled_;
|
||||
bool is_discovering_;
|
||||
|
||||
auto HandleDeviceDiscovery(const esp_bt_gap_cb_param_t& param) -> void;
|
||||
};
|
||||
|
||||
class BluetoothState : public tinyfsm::Fsm<BluetoothState> {
|
||||
public:
|
||||
static auto Init(NvsStorage& storage) -> void;
|
||||
|
||||
static auto devices() -> std::vector<Device>;
|
||||
static auto preferred_device() -> std::optional<mac_addr_t>;
|
||||
static auto preferred_device(const mac_addr_t&) -> void;
|
||||
static auto preferred_device(std::optional<mac_addr_t>) -> void;
|
||||
|
||||
static auto discovery() -> bool;
|
||||
static auto discovery(bool) -> void;
|
||||
|
||||
static auto source() -> StreamBufferHandle_t;
|
||||
static auto source(StreamBufferHandle_t) -> void;
|
||||
@@ -86,6 +120,9 @@ class BluetoothState : public tinyfsm::Fsm<BluetoothState> {
|
||||
virtual void react(const events::Disable& ev) = 0;
|
||||
virtual void react(const events::PreferredDeviceChanged& ev){};
|
||||
virtual void react(const events::SourceChanged& ev){};
|
||||
virtual void react(const events::DiscoveryChanged&);
|
||||
|
||||
virtual void react(const events::DeviceDiscovered&);
|
||||
|
||||
virtual void react(const events::internal::Gap& ev) = 0;
|
||||
virtual void react(const events::internal::A2dp& ev){};
|
||||
@@ -93,11 +130,13 @@ class BluetoothState : public tinyfsm::Fsm<BluetoothState> {
|
||||
|
||||
protected:
|
||||
static NvsStorage* sStorage_;
|
||||
static Scanner* sScanner_;
|
||||
|
||||
static std::mutex sDevicesMutex_;
|
||||
static std::map<mac_addr_t, Device> sDevices_;
|
||||
static std::optional<mac_addr_t> sPreferredDevice_;
|
||||
static mac_addr_t sCurrentDevice_;
|
||||
static bool sIsDiscoveryAllowed_;
|
||||
|
||||
static std::atomic<StreamBufferHandle_t> sSource_;
|
||||
static std::function<void(Event)> sEventHandler_;
|
||||
@@ -106,19 +145,21 @@ class BluetoothState : public tinyfsm::Fsm<BluetoothState> {
|
||||
class Disabled : public BluetoothState {
|
||||
public:
|
||||
void entry() override;
|
||||
void exit() override;
|
||||
|
||||
void react(const events::Enable& ev) override;
|
||||
void react(const events::Disable& ev) override{};
|
||||
|
||||
void react(const events::internal::Gap& ev) override {}
|
||||
void react(const events::internal::A2dp& ev) override {}
|
||||
void react(const events::DiscoveryChanged& ev) override{};
|
||||
|
||||
using BluetoothState::react;
|
||||
};
|
||||
|
||||
class Scanning : public BluetoothState {
|
||||
class Idle : public BluetoothState {
|
||||
public:
|
||||
void entry() override;
|
||||
void exit() override;
|
||||
|
||||
void react(const events::Disable& ev) override;
|
||||
void react(const events::PreferredDeviceChanged& ev) override;
|
||||
@@ -126,9 +167,6 @@ class Scanning : public BluetoothState {
|
||||
void react(const events::internal::Gap& ev) override;
|
||||
|
||||
using BluetoothState::react;
|
||||
|
||||
private:
|
||||
auto OnDeviceDiscovered(esp_bt_gap_cb_param_t*) -> void;
|
||||
};
|
||||
|
||||
class Connecting : public BluetoothState {
|
||||
|
||||
@@ -33,10 +33,11 @@ class Settings : public MenuScreen {
|
||||
class Bluetooth : public MenuScreen {
|
||||
public:
|
||||
Bluetooth(models::TopBar&, drivers::Bluetooth& bt, drivers::NvsStorage& nvs);
|
||||
~Bluetooth();
|
||||
|
||||
auto ChangeEnabledState(bool enabled) -> void;
|
||||
auto RefreshDevicesList() -> void;
|
||||
auto OnDeviceSelected(size_t index) -> void;
|
||||
auto OnDeviceSelected(ssize_t index) -> void;
|
||||
|
||||
private:
|
||||
auto RemoveAllDevices() -> void;
|
||||
|
||||
@@ -119,6 +119,11 @@ static auto select_device_cb(lv_event_t* ev) {
|
||||
instance->OnDeviceSelected(lv_obj_get_index(ev->target));
|
||||
}
|
||||
|
||||
static auto remove_preferred_cb(lv_event_t* ev) {
|
||||
Bluetooth* instance = reinterpret_cast<Bluetooth*>(ev->user_data);
|
||||
instance->OnDeviceSelected(-1);
|
||||
}
|
||||
|
||||
Bluetooth::Bluetooth(models::TopBar& bar,
|
||||
drivers::Bluetooth& bt,
|
||||
drivers::NvsStorage& nvs)
|
||||
@@ -141,6 +146,11 @@ Bluetooth::Bluetooth(models::TopBar& bar,
|
||||
|
||||
devices_list_ = lv_list_create(content_);
|
||||
RefreshDevicesList();
|
||||
bt_.SetDeviceDiscovery(true);
|
||||
}
|
||||
|
||||
Bluetooth::~Bluetooth() {
|
||||
bt_.SetDeviceDiscovery(false);
|
||||
}
|
||||
|
||||
auto Bluetooth::ChangeEnabledState(bool enabled) -> void {
|
||||
@@ -228,6 +238,11 @@ auto Bluetooth::RemoveAllDevices() -> void {
|
||||
auto Bluetooth::AddPreferredDevice(const drivers::bluetooth::Device& dev)
|
||||
-> void {
|
||||
preferred_device_ = lv_list_add_btn(devices_list_, NULL, dev.name.c_str());
|
||||
lv_obj_t* remove = lv_btn_create(preferred_device_);
|
||||
lv_obj_t* remove_icon = lv_label_create(remove);
|
||||
lv_label_set_text(remove_icon, "x");
|
||||
lv_group_add_obj(group_, remove);
|
||||
|
||||
macs_in_list_.push_back(dev.address);
|
||||
}
|
||||
|
||||
@@ -238,7 +253,16 @@ auto Bluetooth::AddDevice(const drivers::bluetooth::Device& dev) -> void {
|
||||
macs_in_list_.push_back(dev.address);
|
||||
}
|
||||
|
||||
auto Bluetooth::OnDeviceSelected(size_t index) -> void {
|
||||
auto Bluetooth::OnDeviceSelected(ssize_t index) -> void {
|
||||
if (index == -1) {
|
||||
events::System().RunOnTask([=]() {
|
||||
nvs_.PreferredBluetoothDevice({});
|
||||
bt_.SetPreferredDevice({});
|
||||
});
|
||||
RefreshDevicesList();
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell the bluetooth driver that our preference changed.
|
||||
auto it = macs_in_list_.begin();
|
||||
std::advance(it, index);
|
||||
|
||||
Reference in New Issue
Block a user