#pragma once #include #include #include #include #include #include #include #include #include #include #include "bluetooth_types.hpp" #include "esp_a2dp_api.h" #include "esp_avrc_api.h" #include "esp_gap_bt_api.h" #include "nvs.hpp" #include "tasks.hpp" #include "tinyfsm.hpp" #include "tinyfsm/include/tinyfsm.hpp" namespace drivers { /* * A handle used to interact with the bluetooth state machine. */ class Bluetooth { public: Bluetooth(NvsStorage& storage, tasks::WorkerPool&); auto Enable() -> bool; auto Disable() -> void; auto IsEnabled() -> bool; auto IsConnected() -> bool; auto ConnectedDevice() -> std::optional; auto KnownDevices() -> std::vector; auto SetPreferredDevice(std::optional dev) -> void; auto PreferredDevice() -> std::optional; auto SetSource(StreamBufferHandle_t) -> void; auto SetVolume(uint8_t) -> void; auto SetEventHandler(std::function cb) -> void; }; namespace bluetooth { namespace events { struct Enable : public tinyfsm::Event {}; struct Disable : public tinyfsm::Event {}; struct ConnectTimedOut : public tinyfsm::Event {}; struct PreferredDeviceChanged : public tinyfsm::Event {}; struct SourceChanged : public tinyfsm::Event {}; struct DeviceDiscovered : public tinyfsm::Event { const Device& device; }; struct ChangeVolume : public tinyfsm::Event { const uint8_t volume; }; namespace internal { struct Gap : public tinyfsm::Event { esp_bt_gap_cb_event_t type; esp_bt_gap_cb_param_t* param; }; struct A2dp : public tinyfsm::Event { esp_a2d_cb_event_t type; esp_a2d_cb_param_t* param; }; struct Avrc : public tinyfsm::Event { esp_avrc_ct_cb_event_t type; esp_avrc_ct_cb_param_t param; }; } // 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 { public: static auto Init(NvsStorage& storage) -> void; static auto lock() -> std::lock_guard; static auto devices() -> std::vector; static auto preferred_device() -> std::optional; static auto preferred_device(std::optional) -> void; static auto scanning() -> bool; static auto discovery() -> bool; static auto discovery(bool) -> void; static auto source() -> StreamBufferHandle_t; static auto source(StreamBufferHandle_t) -> void; static auto event_handler(std::function) -> void; virtual ~BluetoothState(){}; virtual void entry() {} virtual void exit() {} virtual void react(const events::Enable& ev){}; virtual void react(const events::Disable& ev) = 0; virtual void react(const events::ConnectTimedOut& ev){}; virtual void react(const events::PreferredDeviceChanged& ev){}; virtual void react(const events::SourceChanged& ev){}; virtual void react(const events::ChangeVolume&) {} virtual void react(const events::DeviceDiscovered&); virtual void react(const events::internal::Gap& ev) = 0; virtual void react(const events::internal::A2dp& ev){}; virtual void react(const events::internal::Avrc& ev){}; protected: static NvsStorage* sStorage_; static Scanner* sScanner_; static std::mutex sFsmMutex; static std::map sDevices_; static std::optional sPreferredDevice_; static std::optional sConnectingDevice_; static int sConnectAttemptsRemaining_; static std::atomic sSource_; static std::function sEventHandler_; auto connect(const bluetooth::MacAndName&) -> bool; }; class Disabled : public BluetoothState { public: void entry() 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 {} using BluetoothState::react; }; 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; void react(const events::internal::Gap& ev) override; using BluetoothState::react; }; class Connecting : public BluetoothState { public: void entry() override; void exit() override; void react(const events::PreferredDeviceChanged& ev) override; void react(const events::ConnectTimedOut& 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; using BluetoothState::react; }; class Connected : public BluetoothState { public: void entry() override; void exit() override; void react(const events::PreferredDeviceChanged& ev) override; void react(const events::SourceChanged& ev) override; void react(const events::ChangeVolume&) 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::internal::Avrc& ev) override; using BluetoothState::react; private: uint8_t transaction_num_; mac_addr_t connected_to_; }; } // namespace bluetooth } // namespace drivers