diff options
| author | jacqueline <me@jacqueline.id.au> | 2024-09-09 15:15:00 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2024-09-09 15:15:00 +1000 |
| commit | 2b1a01705d62d08cefd6816ba108c5cae48a50ac (patch) | |
| tree | 20ba16a6259ffc335dbcded84fa6bcbe327e9d84 /src/drivers | |
| parent | 9475d10d1000c7e21a7ea311b0c8ee6a72ef46c4 (diff) | |
| parent | acdc9789c90ed6f083d054cd07930e020123457f (diff) | |
| download | tangara-fw-2b1a01705d62d08cefd6816ba108c5cae48a50ac.tar.gz | |
Merge branch 'main' into jqln/tts
Diffstat (limited to 'src/drivers')
| -rw-r--r-- | src/drivers/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/drivers/bluetooth.cpp | 303 | ||||
| -rw-r--r-- | src/drivers/display.cpp | 9 | ||||
| -rw-r--r-- | src/drivers/i2s_dac.cpp | 4 | ||||
| -rw-r--r-- | src/drivers/include/drivers/bluetooth.hpp | 115 | ||||
| -rw-r--r-- | src/drivers/include/drivers/bluetooth_types.hpp | 7 | ||||
| -rw-r--r-- | src/drivers/include/drivers/nvs.hpp | 18 | ||||
| -rw-r--r-- | src/drivers/include/drivers/pcm_buffer.hpp | 8 | ||||
| -rw-r--r-- | src/drivers/include/drivers/samd.hpp | 13 | ||||
| -rw-r--r-- | src/drivers/include/drivers/touchwheel.hpp | 3 | ||||
| -rw-r--r-- | src/drivers/nvs.cpp | 134 | ||||
| -rw-r--r-- | src/drivers/pcm_buffer.cpp | 17 | ||||
| -rw-r--r-- | src/drivers/samd.cpp | 74 | ||||
| -rw-r--r-- | src/drivers/spi.cpp | 2 | ||||
| -rw-r--r-- | src/drivers/test/test_samd.cpp | 4 | ||||
| -rw-r--r-- | src/drivers/touchwheel.cpp | 8 |
16 files changed, 520 insertions, 201 deletions
diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index 33d25894..fea5780f 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -8,5 +8,5 @@ idf_component_register( "samd.cpp" "wm8523.cpp" "nvs.cpp" "haptics.cpp" "spiffs.cpp" "pcm_buffer.cpp" INCLUDE_DIRS "include" REQUIRES "esp_adc" "fatfs" "result" "lvgl" "nvs_flash" "spiffs" "bt" - "tasks" "tinyfsm" "util" "libcppbor") + "tasks" "tinyfsm" "util" "libcppbor" "driver") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/drivers/bluetooth.cpp b/src/drivers/bluetooth.cpp index acb38ce4..3da5dd0c 100644 --- a/src/drivers/bluetooth.cpp +++ b/src/drivers/bluetooth.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include <atomic> +#include <iterator> #include <mutex> #include <ostream> #include <sstream> @@ -116,93 +117,111 @@ IRAM_ATTR auto a2dp_data_cb(uint8_t* buf, int32_t buf_size) -> int32_t { return buf_size; } -Bluetooth::Bluetooth(NvsStorage& storage, tasks::WorkerPool& bg_worker) { +Bluetooth::Bluetooth(NvsStorage& storage, + tasks::WorkerPool& bg_worker, + EventHandler cb) + : nvs_(storage) { sBgWorker = &bg_worker; - bluetooth::BluetoothState::Init(storage); + bluetooth::BluetoothState::Init(storage, cb); } -auto Bluetooth::Enable() -> bool { - auto lock = bluetooth::BluetoothState::lock(); - tinyfsm::FsmList<bluetooth::BluetoothState>::dispatch( - bluetooth::events::Enable{}); +auto Bluetooth::enable(bool en) -> void { + if (en) { + auto lock = bluetooth::BluetoothState::lock(); + tinyfsm::FsmList<bluetooth::BluetoothState>::dispatch( + bluetooth::events::Enable{}); + } else { + // FIXME: the BT tasks unfortunately call back into us while holding an + // internal lock, which then deadlocks with our fsm lock. + // auto lock = bluetooth::BluetoothState::lock(); + tinyfsm::FsmList<bluetooth::BluetoothState>::dispatch( + bluetooth::events::Disable{}); + } +} +auto Bluetooth::enabled() -> bool { + auto lock = bluetooth::BluetoothState::lock(); return !bluetooth::BluetoothState::is_in_state<bluetooth::Disabled>(); } -auto Bluetooth::Disable() -> void { - // FIXME: the BT tasks unfortunately call back into us while holding an - // internal lock, which then deadlocks with our fsm lock. - // auto lock = bluetooth::BluetoothState::lock(); +auto Bluetooth::sources(OutputBuffers* src) -> void { + auto lock = bluetooth::BluetoothState::lock(); + if (src == sStreams) { + return; + } + sStreams = src; tinyfsm::FsmList<bluetooth::BluetoothState>::dispatch( - bluetooth::events::Disable{}); + bluetooth::events::SourcesChanged{}); } -auto Bluetooth::IsEnabled() -> bool { - auto lock = bluetooth::BluetoothState::lock(); - return !bluetooth::BluetoothState::is_in_state<bluetooth::Disabled>(); +auto Bluetooth::softVolume(float f) -> void { + sVolumeFactor = f; } -auto Bluetooth::IsConnected() -> bool { +auto Bluetooth::connectionState() -> ConnectionState { auto lock = bluetooth::BluetoothState::lock(); - return bluetooth::BluetoothState::is_in_state<bluetooth::Connected>(); + if (bluetooth::BluetoothState::is_in_state<bluetooth::Connected>()) { + return ConnectionState::kConnected; + } else if (bluetooth::BluetoothState::is_in_state<bluetooth::Connecting>()) { + return ConnectionState::kConnecting; + } + return ConnectionState::kDisconnected; } -auto Bluetooth::ConnectedDevice() -> std::optional<bluetooth::MacAndName> { +auto Bluetooth::pairedDevice() -> std::optional<bluetooth::MacAndName> { auto lock = bluetooth::BluetoothState::lock(); - if (!bluetooth::BluetoothState::is_in_state<bluetooth::Connected>()) { - return {}; - } - return bluetooth::BluetoothState::preferred_device(); + return bluetooth::BluetoothState::pairedDevice(); } -auto Bluetooth::KnownDevices() -> std::vector<bluetooth::Device> { +auto Bluetooth::pairedDevice(std::optional<bluetooth::MacAndName> dev) -> void { auto lock = bluetooth::BluetoothState::lock(); - std::vector<bluetooth::Device> out = bluetooth::BluetoothState::devices(); - std::sort(out.begin(), out.end(), [](const auto& a, const auto& b) -> bool { - return a.signal_strength < b.signal_strength; - }); - return out; + bluetooth::BluetoothState::pairedDevice(dev); } -auto Bluetooth::SetPreferredDevice(std::optional<bluetooth::MacAndName> dev) - -> void { - auto lock = bluetooth::BluetoothState::lock(); - auto cur = bluetooth::BluetoothState::preferred_device(); - if (dev && cur && dev->mac == cur->mac) { - return; - } - 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<bluetooth::BluetoothState>::dispatch( - bluetooth::events::PreferredDeviceChanged{}); +auto Bluetooth::knownDevices() -> std::vector<bluetooth::MacAndName> { + return nvs_.BluetoothNames(); } -auto Bluetooth::PreferredDevice() -> std::optional<bluetooth::MacAndName> { - auto lock = bluetooth::BluetoothState::lock(); - return bluetooth::BluetoothState::preferred_device(); +auto Bluetooth::forgetKnownDevice(const bluetooth::mac_addr_t& mac) -> void { + nvs_.BluetoothName(mac, {}); } -auto Bluetooth::SetSources(OutputBuffers* src) -> void { +auto Bluetooth::discoveryEnabled(bool en) -> void { auto lock = bluetooth::BluetoothState::lock(); - OutputBuffers* cur = bluetooth::BluetoothState::sources(); - if (src == cur) { - return; - } - bluetooth::BluetoothState::sources(src); - tinyfsm::FsmList<bluetooth::BluetoothState>::dispatch( - bluetooth::events::SourcesChanged{}); + bluetooth::BluetoothState::discovery(en); } -auto Bluetooth::SetVolumeFactor(float f) -> void { - sVolumeFactor = f; +auto Bluetooth::discoveryEnabled() -> bool { + auto lock = bluetooth::BluetoothState::lock(); + return bluetooth::BluetoothState::discovery(); } -auto Bluetooth::SetEventHandler(std::function<void(bluetooth::Event)> cb) - -> void { - auto lock = bluetooth::BluetoothState::lock(); - bluetooth::BluetoothState::event_handler(cb); +auto Bluetooth::discoveredDevices() -> std::vector<bluetooth::MacAndName> { + std::vector<bluetooth::Device> discovered; + { + auto lock = bluetooth::BluetoothState::lock(); + discovered = bluetooth::BluetoothState::discoveredDevices(); + } + + // Show devices with stronger signals first, since they're more likely to be + // physically close (and therefore more likely to be what the user wants). + std::sort(discovered.begin(), discovered.end(), + [](const auto& a, const auto& b) -> bool { + return a.signal_strength < b.signal_strength; + }); + + // Convert to the right format. + std::vector<bluetooth::MacAndName> out; + out.reserve(discovered.size()); + std::transform(discovered.begin(), discovered.end(), std::back_inserter(out), + [&](const bluetooth::Device& dev) { + return bluetooth::MacAndName{ + .mac = dev.address, + .name = {dev.name.data(), dev.name.size()}, + }; + }); + + return out; } static auto DeviceName() -> std::pmr::string { @@ -255,6 +274,10 @@ auto Scanner::StopScanningNow() -> void { } } +auto Scanner::enabled() -> bool { + return enabled_; +} + auto Scanner::HandleGapEvent(const events::internal::Gap& ev) -> void { switch (ev.type) { case ESP_BT_GAP_DISC_RES_EVT: @@ -347,16 +370,18 @@ NvsStorage* BluetoothState::sStorage_; Scanner* BluetoothState::sScanner_; std::mutex BluetoothState::sFsmMutex{}; -std::map<mac_addr_t, Device> BluetoothState::sDevices_{}; -std::optional<MacAndName> BluetoothState::sPreferredDevice_{}; -std::optional<MacAndName> BluetoothState::sConnectingDevice_{}; +std::map<mac_addr_t, Device> BluetoothState::sDiscoveredDevices_{}; +std::optional<MacAndName> BluetoothState::sPairedWith_{}; +std::optional<MacAndName> BluetoothState::sConnectingTo_{}; int BluetoothState::sConnectAttemptsRemaining_{0}; std::function<void(Event)> BluetoothState::sEventHandler_; -auto BluetoothState::Init(NvsStorage& storage) -> void { +auto BluetoothState::Init(NvsStorage& storage, Bluetooth::EventHandler cb) + -> void { sStorage_ = &storage; - sPreferredDevice_ = storage.PreferredBluetoothDevice(); + sEventHandler_ = cb; + sPairedWith_ = storage.PreferredBluetoothDevice(); tinyfsm::FsmList<bluetooth::BluetoothState>::start(); } @@ -364,68 +389,85 @@ auto BluetoothState::lock() -> std::lock_guard<std::mutex> { return std::lock_guard<std::mutex>{sFsmMutex}; } -auto BluetoothState::devices() -> std::vector<Device> { - std::vector<Device> out; - for (const auto& device : sDevices_) { - out.push_back(device.second); - } - return out; +auto BluetoothState::pairedDevice() -> std::optional<MacAndName> { + return sPairedWith_; } -auto BluetoothState::preferred_device() -> std::optional<MacAndName> { - return sPreferredDevice_; -} +auto BluetoothState::pairedDevice(std::optional<MacAndName> dev) -> void { + auto cur = sPairedWith_; + if (dev && cur && dev->mac == cur->mac) { + return; + } + if (dev) { + ESP_LOGI(kTag, "pairing with '%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]); + } + sPairedWith_ = dev; + std::invoke(sEventHandler_, SimpleEvent::kDeviceDiscovered); -auto BluetoothState::preferred_device(std::optional<MacAndName> addr) -> void { - sPreferredDevice_ = addr; + tinyfsm::FsmList<bluetooth::BluetoothState>::dispatch( + bluetooth::events::PairedDeviceChanged{}); } -auto BluetoothState::sources() -> OutputBuffers* { - return sStreams; +auto BluetoothState::discovery() -> bool { + return sScanner_->enabled(); } -auto BluetoothState::sources(OutputBuffers* src) -> void { - sStreams = src; +auto BluetoothState::discovery(bool en) -> void { + if (en) { + if (!sScanner_->enabled()) { + sDiscoveredDevices_.clear(); + } + sScanner_->ScanContinuously(); + } else { + sScanner_->StopScanning(); + } } -auto BluetoothState::event_handler(std::function<void(Event)> cb) -> void { - sEventHandler_ = cb; +auto BluetoothState::discoveredDevices() -> std::vector<Device> { + std::vector<Device> out; + for (const auto& device : sDiscoveredDevices_) { + out.push_back(device.second); + } + return out; } auto BluetoothState::react(const events::DeviceDiscovered& ev) -> void { - bool is_preferred = false; - bool already_known = sDevices_.contains(ev.device.address); - sDevices_[ev.device.address] = ev.device; + bool is_paired = false; + bool already_known = sDiscoveredDevices_.contains(ev.device.address); + sDiscoveredDevices_[ev.device.address] = ev.device; - if (sPreferredDevice_ && ev.device.address == sPreferredDevice_->mac) { - is_preferred = true; + if (sPairedWith_ && ev.device.address == sPairedWith_->mac) { + is_paired = true; } - if (sEventHandler_ && !already_known) { - std::invoke(sEventHandler_, SimpleEvent::kKnownDevicesChanged); + if (!already_known) { + std::invoke(sEventHandler_, SimpleEvent::kDeviceDiscovered); } - if (is_preferred && sPreferredDevice_) { - connect(*sPreferredDevice_); + if (is_paired && sPairedWith_) { + connect(*sPairedWith_); } } auto BluetoothState::connect(const MacAndName& dev) -> bool { - if (sConnectingDevice_ && sConnectingDevice_->mac == dev.mac) { + if (sConnectingTo_ && sConnectingTo_->mac == dev.mac) { sConnectAttemptsRemaining_--; } else { sConnectAttemptsRemaining_ = 3; } if (sConnectAttemptsRemaining_ == 0) { + sConnectingTo_ = {}; return false; } - sConnectingDevice_ = dev; + sConnectingTo_ = dev; 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]); - if (esp_a2d_source_connect(sConnectingDevice_->mac.data()) != ESP_OK) { + if (esp_a2d_source_connect(sConnectingTo_->mac.data()) != ESP_OK) { ESP_LOGI(kTag, "Connecting failed..."); if (sConnectAttemptsRemaining_ > 1) { ESP_LOGI(kTag, "Will retry."); @@ -489,7 +531,7 @@ void Disabled::react(const events::Enable&) { // Set a reasonable name for the device. std::pmr::string name = DeviceName(); - esp_bt_dev_set_device_name(name.c_str()); + esp_bt_gap_set_device_name(name.c_str()); // Initialise GAP. This controls advertising our device, and scanning for // other devices. @@ -553,36 +595,31 @@ void Disabled::react(const events::Enable&) { esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); ESP_LOGI(kTag, "bt enabled"); - if (sPreferredDevice_) { - ESP_LOGI(kTag, "connecting to preferred device '%s'", - sPreferredDevice_->name.c_str()); - connect(*sPreferredDevice_); + if (sPairedWith_) { + ESP_LOGI(kTag, "connecting to paired device '%s'", + sPairedWith_->name.c_str()); + connect(*sPairedWith_); } else { - ESP_LOGI(kTag, "scanning for devices"); transit<Idle>(); } } void Idle::entry() { ESP_LOGI(kTag, "bt is idle"); - sScanner_->ScanContinuously(); + std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged); } void Idle::exit() { - sScanner_->StopScanning(); + std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged); } void Idle::react(const events::Disable& ev) { transit<Disabled>(); } -void Idle::react(const events::PreferredDeviceChanged& ev) { - bool is_discovered = false; - if (sPreferredDevice_ && sDevices_.contains(sPreferredDevice_->mac)) { - is_discovered = true; - } - if (is_discovered) { - connect(*sPreferredDevice_); +void Idle::react(const events::PairedDeviceChanged& ev) { + if (sPairedWith_) { + connect(*sPairedWith_); } } @@ -604,36 +641,32 @@ void Connecting::entry() { sTimeoutTimer = xTimerCreate("bt_timeout", pdMS_TO_TICKS(15000), false, NULL, timeoutCallback); xTimerStart(sTimeoutTimer, portMAX_DELAY); - - sScanner_->StopScanning(); - if (sEventHandler_) { - std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged); - } } void Connecting::exit() { xTimerDelete(sTimeoutTimer, portMAX_DELAY); - - if (sEventHandler_) { - std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged); - } } void Connecting::react(const events::ConnectTimedOut& ev) { ESP_LOGI(kTag, "timed out awaiting connection"); - esp_a2d_source_disconnect(sConnectingDevice_->mac.data()); - if (!connect(*sConnectingDevice_)) { + esp_a2d_source_disconnect(sConnectingTo_->mac.data()); + if (!connect(*sConnectingTo_)) { transit<Idle>(); } } void Connecting::react(const events::Disable& ev) { - // TODO: disconnect gracefully + esp_a2d_source_disconnect(sConnectingTo_->mac.data()); transit<Disabled>(); } -void Connecting::react(const events::PreferredDeviceChanged& ev) { - // TODO. Cancel out and start again. +void Connecting::react(const events::PairedDeviceChanged& ev) { + esp_a2d_source_disconnect(sConnectingTo_->mac.data()); + if (sPairedWith_) { + connect(*sPairedWith_); + } else { + transit<Idle>(); + } } void Connecting::react(events::internal::Gap ev) { @@ -704,29 +737,41 @@ void Connected::entry() { ESP_LOGI(kTag, "entering connected state"); transaction_num_ = 0; - connected_to_ = sConnectingDevice_->mac; - sPreferredDevice_ = sConnectingDevice_; - sConnectingDevice_ = {}; + connected_to_ = sConnectingTo_->mac; + sPairedWith_ = sConnectingTo_; + + sStorage_->BluetoothName(sConnectingTo_->mac, sConnectingTo_->name); + std::invoke(sEventHandler_, SimpleEvent::kKnownDevicesChanged); + + sConnectingTo_ = {}; auto stored_pref = sStorage_->PreferredBluetoothDevice(); - if (!stored_pref || (sPreferredDevice_->name != stored_pref->name || - sPreferredDevice_->mac != stored_pref->mac)) { - sStorage_->PreferredBluetoothDevice(sPreferredDevice_); + if (!stored_pref || (sPairedWith_->name != stored_pref->name || + sPairedWith_->mac != stored_pref->mac)) { + sStorage_->PreferredBluetoothDevice(sPairedWith_); } + + std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged); + + // TODO: if we already have a source, immediately start playing } void Connected::exit() { ESP_LOGI(kTag, "exiting connected state"); esp_a2d_source_disconnect(connected_to_.data()); + + std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged); } void Connected::react(const events::Disable& ev) { transit<Disabled>(); } -void Connected::react(const events::PreferredDeviceChanged& ev) { - sConnectingDevice_ = sPreferredDevice_; - transit<Connecting>(); +void Connected::react(const events::PairedDeviceChanged& ev) { + transit<Idle>(); + if (sPairedWith_) { + connect(*sPairedWith_); + } } void Connected::react(const events::SourcesChanged& ev) { diff --git a/src/drivers/display.cpp b/src/drivers/display.cpp index efc9df93..7321f20b 100644 --- a/src/drivers/display.cpp +++ b/src/drivers/display.cpp @@ -242,6 +242,7 @@ void Display::SendInitialisationSequence(const uint8_t* data) { spi_device_release_bus(handle_); } +IRAM_ATTR void Display::SendCommandWithData(uint8_t command, const uint8_t* data, size_t length) { @@ -249,17 +250,20 @@ void Display::SendCommandWithData(uint8_t command, SendData(data, length); } +IRAM_ATTR void Display::SendCmd(const uint8_t* data, size_t length) { SendTransaction(COMMAND, data, length); } +IRAM_ATTR void Display::SendData(const uint8_t* data, size_t length) { SendTransaction(DATA, data, length); } +IRAM_ATTR void Display::SendTransaction(TransactionType type, - const uint8_t* data, - size_t length) { + const uint8_t* data, + size_t length) { // TODO(jacqueline): What's sending this? if (length == 0) { return; @@ -290,6 +294,7 @@ void Display::SendTransaction(TransactionType type, ESP_ERROR_CHECK(spi_device_transmit(handle_, &sTransaction)); } +IRAM_ATTR void Display::OnLvglFlush(const lv_area_t* area, uint8_t* color_map) { // Swap the pixel byte order first, since we don't want to do this whilst // holding the SPI bus lock. diff --git a/src/drivers/i2s_dac.cpp b/src/drivers/i2s_dac.cpp index 4e2e171a..46bf8e80 100644 --- a/src/drivers/i2s_dac.cpp +++ b/src/drivers/i2s_dac.cpp @@ -46,12 +46,12 @@ extern "C" IRAM_ATTR auto callback(i2s_chan_handle_t handle, if (event == nullptr || user_ctx == nullptr) { return false; } - if (event->data == nullptr || event->size == 0) { + if (event->dma_buf == nullptr || event->size == 0) { return false; } assert(event->size % 4 == 0); - uint8_t* buf = *reinterpret_cast<uint8_t**>(event->data); + uint8_t* buf = reinterpret_cast<uint8_t*>(event->dma_buf); auto* src = reinterpret_cast<OutputBuffers*>(user_ctx); BaseType_t ret1 = src->first.receive( diff --git a/src/drivers/include/drivers/bluetooth.hpp b/src/drivers/include/drivers/bluetooth.hpp index eaecfb2b..99c71e52 100644 --- a/src/drivers/include/drivers/bluetooth.hpp +++ b/src/drivers/include/drivers/bluetooth.hpp @@ -3,6 +3,7 @@ #include <array> #include <atomic> +#include <functional> #include <map> #include <mutex> #include <optional> @@ -25,28 +26,68 @@ namespace drivers { /* - * A handle used to interact with the bluetooth state machine. + * A handle used to interact with the bluetooth state machine. This is the main + * API that the rest of the system should use to interact with Bluetooth. */ class Bluetooth { public: - Bluetooth(NvsStorage& storage, tasks::WorkerPool&); + /* + * Callback invoked when an event is generated by the Bluetooth stack. This + * callback is invoked synchronously from a Bluetooth task context, so + * implementations should immediately hop to a different task to process the + * event. + */ + using EventHandler = std::function<void(bluetooth::Event)>; + + Bluetooth(NvsStorage&, tasks::WorkerPool&, EventHandler); + + /* Enables or disables the entire Bluetooth stack. */ + auto enable(bool en) -> void; + auto enabled() -> bool; + + auto sources(OutputBuffers*) -> void; + auto softVolume(float) -> void; + + enum class ConnectionState { + kConnected, + kConnecting, + kDisconnected, + }; + + auto connectionState() -> ConnectionState; + + /* + * The 'paired' device is a device that will be preferred for connections. + * When Bluetooth is first enabled, we immediately try to connect to the + * paired device. If the paired device is seen during a scan, then we will + * also automatically connect to it. + */ + auto pairedDevice() -> std::optional<bluetooth::MacAndName>; + + /* + * Sets the preferred device. If a device is provided, a connection will be + * attempted immediately, even if the device has not been detected in a + * previous scan. + */ + auto pairedDevice(std::optional<bluetooth::MacAndName> dev) -> void; + + /* A list of devices that have previously been the paired device. */ + auto knownDevices() -> std::vector<bluetooth::MacAndName>; + auto forgetKnownDevice(const bluetooth::mac_addr_t&) -> void; + + /* Enables or disables scanning for nearby Bluetooth devices. */ + auto discoveryEnabled(bool) -> void; + auto discoveryEnabled() -> bool; + + /* + * A list of nearby devices that have been discovered since discovery was + * last enabled. This list may include the paired device, as well as devices + * that are also present in the known devices list. + */ + auto discoveredDevices() -> std::vector<bluetooth::MacAndName>; - auto Enable() -> bool; - auto Disable() -> void; - auto IsEnabled() -> bool; - - auto IsConnected() -> bool; - auto ConnectedDevice() -> std::optional<bluetooth::MacAndName>; - - auto KnownDevices() -> std::vector<bluetooth::Device>; - - auto SetPreferredDevice(std::optional<bluetooth::MacAndName> dev) -> void; - auto PreferredDevice() -> std::optional<bluetooth::MacAndName>; - - auto SetSources(OutputBuffers*) -> void; - auto SetVolumeFactor(float) -> void; - - auto SetEventHandler(std::function<void(bluetooth::Event)> cb) -> void; + private: + NvsStorage& nvs_; }; namespace bluetooth { @@ -56,7 +97,7 @@ struct Enable : public tinyfsm::Event {}; struct Disable : public tinyfsm::Event {}; struct ConnectTimedOut : public tinyfsm::Event {}; -struct PreferredDeviceChanged : public tinyfsm::Event {}; +struct PairedDeviceChanged : public tinyfsm::Event {}; struct SourcesChanged : public tinyfsm::Event {}; struct DeviceDiscovered : public tinyfsm::Event { const Device& device; @@ -94,6 +135,8 @@ class Scanner { auto StopScanning() -> void; auto StopScanningNow() -> void; + auto enabled() -> bool; + auto HandleGapEvent(const events::internal::Gap&) -> void; private: @@ -103,25 +146,22 @@ class Scanner { auto HandleDeviceDiscovery(const esp_bt_gap_cb_param_t& param) -> void; }; +/* + * The main state machine for managing the state of the Bluetooth stack, and + * the current (if any) Bluetooth connection. + */ class BluetoothState : public tinyfsm::Fsm<BluetoothState> { public: - static auto Init(NvsStorage& storage) -> void; + static auto Init(NvsStorage& storage, Bluetooth::EventHandler) -> void; static auto lock() -> std::lock_guard<std::mutex>; - static auto devices() -> std::vector<Device>; - - static auto preferred_device() -> std::optional<bluetooth::MacAndName>; - static auto preferred_device(std::optional<bluetooth::MacAndName>) -> void; + static auto pairedDevice() -> std::optional<bluetooth::MacAndName>; + static auto pairedDevice(std::optional<bluetooth::MacAndName>) -> void; - static auto scanning() -> bool; static auto discovery() -> bool; static auto discovery(bool) -> void; - - static auto sources() -> OutputBuffers*; - static auto sources(OutputBuffers*) -> void; - - static auto event_handler(std::function<void(Event)>) -> void; + static auto discoveredDevices() -> std::vector<Device>; virtual ~BluetoothState(){}; @@ -131,7 +171,7 @@ class BluetoothState : public tinyfsm::Fsm<BluetoothState> { 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::PairedDeviceChanged& ev){}; virtual void react(const events::SourcesChanged& ev){}; virtual void react(const events::DeviceDiscovered&); @@ -146,10 +186,9 @@ class BluetoothState : public tinyfsm::Fsm<BluetoothState> { static Scanner* sScanner_; static std::mutex sFsmMutex; - static std::map<mac_addr_t, Device> sDevices_; - static std::optional<bluetooth::MacAndName> sPreferredDevice_; - - static std::optional<bluetooth::MacAndName> sConnectingDevice_; + static std::map<mac_addr_t, Device> sDiscoveredDevices_; + static std::optional<bluetooth::MacAndName> sPairedWith_; + static std::optional<bluetooth::MacAndName> sConnectingTo_; static int sConnectAttemptsRemaining_; static std::function<void(Event)> sEventHandler_; @@ -176,7 +215,7 @@ class Idle : public BluetoothState { void exit() override; void react(const events::Disable& ev) override; - void react(const events::PreferredDeviceChanged& ev) override; + void react(const events::PairedDeviceChanged& ev) override; void react(events::internal::Gap ev) override; @@ -188,7 +227,7 @@ class Connecting : public BluetoothState { void entry() override; void exit() override; - void react(const events::PreferredDeviceChanged& ev) override; + void react(const events::PairedDeviceChanged& ev) override; void react(const events::ConnectTimedOut& ev) override; void react(const events::Disable& ev) override; @@ -203,7 +242,7 @@ class Connected : public BluetoothState { void entry() override; void exit() override; - void react(const events::PreferredDeviceChanged& ev) override; + void react(const events::PairedDeviceChanged& ev) override; void react(const events::SourcesChanged& ev) override; void react(const events::Disable& ev) override; diff --git a/src/drivers/include/drivers/bluetooth_types.hpp b/src/drivers/include/drivers/bluetooth_types.hpp index d2e55ee5..05caee47 100644 --- a/src/drivers/include/drivers/bluetooth_types.hpp +++ b/src/drivers/include/drivers/bluetooth_types.hpp @@ -27,9 +27,12 @@ struct Device { }; enum class SimpleEvent { - kKnownDevicesChanged, kConnectionStateChanged, - kPreferredDeviceChanged, + kPairedDeviceChanged, + kKnownDevicesChanged, + kDiscoveryChanged, + kDeviceDiscovered, + // Passthrough events kPlayPause, kStop, diff --git a/src/drivers/include/drivers/nvs.hpp b/src/drivers/include/drivers/nvs.hpp index 88dd5ae0..e147c8c7 100644 --- a/src/drivers/include/drivers/nvs.hpp +++ b/src/drivers/include/drivers/nvs.hpp @@ -34,7 +34,7 @@ class Setting { dirty_ = true; } } - auto get() -> std::optional<T>& { return val_; } + auto get() -> std::optional<T> { return val_; } /* Reads the stored value from NVS and parses it into the correct type. */ auto load(nvs_handle_t) -> std::optional<T>; @@ -90,12 +90,19 @@ class NvsStorage { auto LraCalibration() -> std::optional<LraData>; auto LraCalibration(const LraData&) -> void; + auto FastCharge() -> bool; + auto FastCharge(bool) -> void; + auto PreferredBluetoothDevice() -> std::optional<bluetooth::MacAndName>; auto PreferredBluetoothDevice(std::optional<bluetooth::MacAndName>) -> void; auto BluetoothVolume(const bluetooth::mac_addr_t&) -> uint8_t; auto BluetoothVolume(const bluetooth::mac_addr_t&, uint8_t) -> void; + auto BluetoothNames() -> std::vector<bluetooth::MacAndName>; + auto BluetoothName(const bluetooth::mac_addr_t&, std::optional<std::string>) + -> void; + enum class Output : uint8_t { kHeadphones = 0, kBluetooth = 1, @@ -106,6 +113,9 @@ class NvsStorage { auto ScreenBrightness() -> uint_fast8_t; auto ScreenBrightness(uint_fast8_t) -> void; + auto InterfaceTheme() -> std::optional<std::string>; + auto InterfaceTheme(std::string) -> void; + auto ScrollSensitivity() -> uint_fast8_t; auto ScrollSensitivity(uint_fast8_t) -> void; @@ -146,6 +156,7 @@ class NvsStorage { Setting<uint16_t> display_rows_; Setting<uint8_t> haptic_motor_type_; Setting<LraData> lra_calibration_; + Setting<uint8_t> fast_charge_; Setting<uint8_t> brightness_; Setting<uint8_t> sensitivity_; @@ -154,7 +165,12 @@ class NvsStorage { Setting<int8_t> amp_left_bias_; Setting<uint8_t> input_mode_; Setting<uint8_t> output_mode_; + + Setting<std::string> theme_; + Setting<bluetooth::MacAndName> bt_preferred_; + Setting<std::vector<bluetooth::MacAndName>> bt_names_; + Setting<uint8_t> db_auto_index_; util::LruCache<10, bluetooth::mac_addr_t, uint8_t> bt_volumes_; diff --git a/src/drivers/include/drivers/pcm_buffer.hpp b/src/drivers/include/drivers/pcm_buffer.hpp index 968c3398..4e5fa041 100644 --- a/src/drivers/include/drivers/pcm_buffer.hpp +++ b/src/drivers/include/drivers/pcm_buffer.hpp @@ -28,8 +28,12 @@ class PcmBuffer { PcmBuffer(size_t size_in_samples); ~PcmBuffer(); - /* Adds samples to the buffer. */ - auto send(std::span<const int16_t>) -> void; + /* + * Adds samples to the buffer. Returns the number of samples that were added, + * which may be less than the number of samples given if this PcmBuffer is + * close to full. + */ + auto send(std::span<const int16_t>) -> size_t; /* * Fills the given span with samples. If enough samples are available in diff --git a/src/drivers/include/drivers/samd.hpp b/src/drivers/include/drivers/samd.hpp index 897e78d6..ff479225 100644 --- a/src/drivers/include/drivers/samd.hpp +++ b/src/drivers/include/drivers/samd.hpp @@ -10,6 +10,7 @@ #include <optional> #include <string> +#include "drivers/nvs.hpp" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -17,9 +18,7 @@ namespace drivers { class Samd { public: - static auto Create() -> Samd* { return new Samd(); } - - Samd(); + Samd(NvsStorage& nvs); ~Samd(); auto Version() -> std::string; @@ -37,8 +36,14 @@ class Samd { kChargingFast, // The battery is full charged, and we are still plugged in. kFullCharge, + // Charging failed. + kFault, + // The battery status returned isn't a known enum value. + kUnknown, }; + static auto chargeStatusToString(ChargeStatus) -> std::string; + auto GetChargeStatus() -> std::optional<ChargeStatus>; auto UpdateChargeStatus() -> void; @@ -68,6 +73,8 @@ class Samd { Samd& operator=(const Samd&) = delete; private: + NvsStorage& nvs_; + uint8_t version_; std::optional<ChargeStatus> charge_status_; UsbStatus usb_status_; diff --git a/src/drivers/include/drivers/touchwheel.hpp b/src/drivers/include/drivers/touchwheel.hpp index 60902087..9cd925a6 100644 --- a/src/drivers/include/drivers/touchwheel.hpp +++ b/src/drivers/include/drivers/touchwheel.hpp @@ -39,7 +39,8 @@ class TouchWheel { auto Update() -> void; auto GetTouchWheelData() const -> TouchWheelData; - auto PowerDown() -> void; + auto Recalibrate() -> void; + auto LowPowerMode(bool en) -> void; private: TouchWheelData data_; diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp index 5c7d2218..d004201b 100644 --- a/src/drivers/nvs.cpp +++ b/src/drivers/nvs.cpp @@ -26,8 +26,10 @@ static constexpr uint8_t kSchemaVersion = 1; static constexpr char kKeyVersion[] = "ver"; static constexpr char kKeyBluetoothPreferred[] = "bt_dev"; static constexpr char kKeyBluetoothVolumes[] = "bt_vols"; +static constexpr char kKeyBluetoothNames[] = "bt_names"; static constexpr char kKeyOutput[] = "out"; static constexpr char kKeyBrightness[] = "bright"; +static constexpr char kKeyInterfaceTheme[] = "ui_theme"; static constexpr char kKeyAmpMaxVolume[] = "hp_vol_max"; static constexpr char kKeyAmpCurrentVolume[] = "hp_vol"; static constexpr char kKeyAmpLeftBias[] = "hp_bias"; @@ -39,6 +41,7 @@ static constexpr char kKeyDisplayRows[] = "disprows"; static constexpr char kKeyHapticMotorType[] = "hapticmtype"; static constexpr char kKeyLraCalibration[] = "lra_cali"; static constexpr char kKeyDbAutoIndex[] = "dbautoindex"; +static constexpr char kKeyFastCharge[] = "fastchg"; static auto nvs_get_string(nvs_handle_t nvs, const char* key) -> std::optional<std::string> { @@ -130,6 +133,69 @@ auto Setting<bluetooth::MacAndName>::store(nvs_handle_t nvs, } template <> +auto Setting<std::vector<bluetooth::MacAndName>>::load(nvs_handle_t nvs) + -> std::optional<std::vector<bluetooth::MacAndName>> { + auto raw = nvs_get_string(nvs, name_); + if (!raw) { + return {}; + } + auto [parsed, unused, err] = cppbor::parseWithViews( + reinterpret_cast<const uint8_t*>(raw->data()), raw->size()); + if (parsed->type() != cppbor::MAP) { + return {}; + } + std::vector<bluetooth::MacAndName> res; + for (const auto& i : *parsed->asMap()) { + auto mac = i.first->asViewBstr()->view(); + auto name = i.second->asViewTstr()->view(); + bluetooth::MacAndName entry{ + .mac = {}, + .name = {name.begin(), name.end()}, + }; + std::copy(mac.begin(), mac.end(), entry.mac.begin()); + res.push_back(entry); + } + return res; +} + +template <> +auto Setting<std::vector<bluetooth::MacAndName>>::store( + nvs_handle_t nvs, + std::vector<bluetooth::MacAndName> v) -> void { + cppbor::Map cbor{}; + for (const auto& i : v) { + cbor.add(cppbor::Bstr{{i.mac.data(), i.mac.size()}}, cppbor::Tstr{i.name}); + } + auto encoded = cbor.encode(); + nvs_set_blob(nvs, name_, encoded.data(), encoded.size()); +} + +template <> +auto Setting<std::string>::store( + nvs_handle_t nvs, + std::string v) -> void { + cppbor::Tstr cbor{v}; + auto encoded = cbor.encode(); + nvs_set_blob(nvs, name_, encoded.data(), encoded.size()); +} + +template <> +auto Setting<std::string>::load(nvs_handle_t nvs) + -> std::optional<std::string> { + auto raw = nvs_get_string(nvs, name_); + if (!raw) { + return {}; + } + auto [parsed, unused, err] = cppbor::parseWithViews( + reinterpret_cast<const uint8_t*>(raw->data()), raw->size()); + if (parsed->type() != cppbor::TSTR) { + return {}; + } + auto v = parsed->asViewTstr()->view(); + return std::string{v.begin(), v.end()}; +} + +template <> auto Setting<NvsStorage::LraData>::load(nvs_handle_t nvs) -> std::optional<NvsStorage::LraData> { auto raw = nvs_get_string(nvs, name_); @@ -200,6 +266,7 @@ NvsStorage::NvsStorage(nvs_handle_t handle) display_rows_(kKeyDisplayRows), haptic_motor_type_(kKeyHapticMotorType), lra_calibration_(kKeyLraCalibration), + fast_charge_(kKeyFastCharge), brightness_(kKeyBrightness), sensitivity_(kKeyScrollSensitivity), amp_max_vol_(kKeyAmpMaxVolume), @@ -207,7 +274,9 @@ NvsStorage::NvsStorage(nvs_handle_t handle) amp_left_bias_(kKeyAmpLeftBias), input_mode_(kKeyPrimaryInput), output_mode_(kKeyOutput), + theme_{kKeyInterfaceTheme}, bt_preferred_(kKeyBluetoothPreferred), + bt_names_(kKeyBluetoothNames), db_auto_index_(kKeyDbAutoIndex), bt_volumes_(), bt_volumes_dirty_(false) {} @@ -231,7 +300,9 @@ auto NvsStorage::Read() -> void { amp_left_bias_.read(handle_); input_mode_.read(handle_); output_mode_.read(handle_); + theme_.read(handle_); bt_preferred_.read(handle_); + bt_names_.read(handle_); db_auto_index_.read(handle_); readBtVolumes(); } @@ -250,7 +321,9 @@ auto NvsStorage::Write() -> bool { amp_left_bias_.write(handle_); input_mode_.write(handle_); output_mode_.write(handle_); + theme_.write(handle_); bt_preferred_.write(handle_); + bt_names_.write(handle_); db_auto_index_.write(handle_); writeBtVolumes(); return nvs_commit(handle_) == ESP_OK; @@ -341,6 +414,47 @@ auto NvsStorage::BluetoothVolume(const bluetooth::mac_addr_t& mac, uint8_t vol) bt_volumes_.Put(mac, vol); } +auto NvsStorage::BluetoothNames() -> std::vector<bluetooth::MacAndName> { + std::lock_guard<std::mutex> lock{mutex_}; + return bt_names_.get().value_or(std::vector<bluetooth::MacAndName>{}); +} + +auto NvsStorage::BluetoothName(const bluetooth::mac_addr_t& mac, + std::optional<std::string> name) -> void { + std::lock_guard<std::mutex> lock{mutex_}; + auto val = bt_names_.get(); + if (!val) { + val.emplace(); + } + + bool mut = false; + bool found = false; + for (auto it = val->begin(); it != val->end(); it++) { + if (it->mac == mac) { + if (name) { + it->name = *name; + } else { + val->erase(it); + } + found = true; + mut = true; + break; + } + } + + if (!found && name) { + val->push_back(bluetooth::MacAndName{ + .mac = mac, + .name = *name, + }); + mut = true; + } + + if (mut) { + bt_names_.set(*val); + } +} + auto NvsStorage::OutputMode() -> Output { std::lock_guard<std::mutex> lock{mutex_}; switch (output_mode_.get().value_or(0xFF)) { @@ -361,6 +475,16 @@ auto NvsStorage::OutputMode(Output out) -> void { nvs_commit(handle_); } +auto NvsStorage::FastCharge() -> bool { + std::lock_guard<std::mutex> lock{mutex_}; + return fast_charge_.get().value_or(true); +} + +auto NvsStorage::FastCharge(bool en) -> void { + std::lock_guard<std::mutex> lock{mutex_}; + fast_charge_.set(en); +} + auto NvsStorage::ScreenBrightness() -> uint_fast8_t { std::lock_guard<std::mutex> lock{mutex_}; return std::clamp<uint8_t>(brightness_.get().value_or(50), 0, 100); @@ -371,6 +495,16 @@ auto NvsStorage::ScreenBrightness(uint_fast8_t val) -> void { brightness_.set(val); } +auto NvsStorage::InterfaceTheme() -> std::optional<std::string> { + std::lock_guard<std::mutex> lock{mutex_}; + return theme_.get(); +} + +auto NvsStorage::InterfaceTheme(std::string themeFile) -> void { + std::lock_guard<std::mutex> lock{mutex_}; + theme_.set(themeFile); +} + auto NvsStorage::ScrollSensitivity() -> uint_fast8_t { std::lock_guard<std::mutex> lock{mutex_}; return std::clamp<uint8_t>(sensitivity_.get().value_or(128), 0, 255); diff --git a/src/drivers/pcm_buffer.cpp b/src/drivers/pcm_buffer.cpp index 1d2bab1e..1e416301 100644 --- a/src/drivers/pcm_buffer.cpp +++ b/src/drivers/pcm_buffer.cpp @@ -17,6 +17,7 @@ #include "freertos/FreeRTOS.h" #include "esp_heap_caps.h" +#include "freertos/projdefs.h" #include "freertos/ringbuf.h" #include "portmacro.h" @@ -39,9 +40,13 @@ PcmBuffer::~PcmBuffer() { heap_caps_free(buf_); } -auto PcmBuffer::send(std::span<const int16_t> data) -> void { - xRingbufferSend(ringbuf_, data.data(), data.size_bytes(), portMAX_DELAY); +auto PcmBuffer::send(std::span<const int16_t> data) -> size_t { + if (!xRingbufferSend(ringbuf_, data.data(), data.size_bytes(), + pdMS_TO_TICKS(100))) { + return 0; + } sent_ += data.size(); + return data.size(); } IRAM_ATTR auto PcmBuffer::receive(std::span<int16_t> dest, bool mix, bool isr) @@ -67,10 +72,12 @@ IRAM_ATTR auto PcmBuffer::receive(std::span<int16_t> dest, bool mix, bool isr) auto PcmBuffer::clear() -> void { while (!isEmpty()) { - size_t bytes_cleared; + size_t bytes_cleared = 0; void* data = xRingbufferReceive(ringbuf_, &bytes_cleared, 0); - vRingbufferReturnItem(ringbuf_, data); - received_ += bytes_cleared / sizeof(int16_t); + if (data) { + vRingbufferReturnItem(ringbuf_, data); + received_ += bytes_cleared / sizeof(int16_t); + } } } diff --git a/src/drivers/samd.cpp b/src/drivers/samd.cpp index e4aa73ad..c2308760 100644 --- a/src/drivers/samd.cpp +++ b/src/drivers/samd.cpp @@ -5,11 +5,13 @@ */ #include "drivers/samd.hpp" +#include <stdint.h> #include <cstdint> #include <optional> #include <string> +#include "drivers/nvs.hpp" #include "esp_err.h" #include "esp_log.h" #include "hal/gpio_types.h" @@ -32,7 +34,29 @@ namespace drivers { static constexpr gpio_num_t kIntPin = GPIO_NUM_35; -Samd::Samd() { +auto Samd::chargeStatusToString(ChargeStatus status) -> std::string { + switch (status) { + case ChargeStatus::kNoBattery: + return "no_battery"; + case ChargeStatus::kBatteryCritical: + return "critical"; + case ChargeStatus::kDischarging: + return "discharging"; + case ChargeStatus::kChargingRegular: + return "charge_regular"; + case ChargeStatus::kChargingFast: + return "charge_fast"; + case ChargeStatus::kFullCharge: + return "full_charge"; + case ChargeStatus::kFault: + return "fault"; + case ChargeStatus::kUnknown: + default: + return "unknown"; + } +} + +Samd::Samd(NvsStorage& nvs) : nvs_(nvs) { gpio_set_direction(kIntPin, GPIO_MODE_INPUT); // Being able to interface with the SAMD properly is critical. To ensure we @@ -51,7 +75,7 @@ Samd::Samd() { UpdateChargeStatus(); UpdateUsbStatus(); - SetFastChargeEnabled(true); + SetFastChargeEnabled(nvs.FastCharge()); } Samd::~Samd() {} @@ -78,16 +102,38 @@ auto Samd::UpdateChargeStatus() -> void { return; } - // FIXME: Ideally we should be using the three 'charge status' bits to work - // out whether we're actually charging, or if we've got a full charge, - // critically low charge, etc. + // Lower two bits are the usb power status, next three are the BMS status. + // See 'gpio.c' in the SAMD21 firmware for how these bits get packed. + uint8_t charge_state = (raw_res & 0b11100) >> 2; uint8_t usb_state = raw_res & 0b11; - if (usb_state == 0) { - charge_status_ = ChargeStatus::kDischarging; - } else if (usb_state == 1) { - charge_status_ = ChargeStatus::kChargingRegular; - } else { - charge_status_ = ChargeStatus::kChargingFast; + switch (charge_state) { + case 0b000: + charge_status_ = ChargeStatus::kNoBattery; + break; + case 0b001: + // BMS says we're charging; work out how fast we're charging. + if (usb_state >= 0b10 && nvs_.FastCharge()) { + charge_status_ = ChargeStatus::kChargingFast; + } else { + charge_status_ = ChargeStatus::kChargingRegular; + } + break; + case 0b010: + charge_status_ = ChargeStatus::kFullCharge; + break; + case 0b011: + charge_status_ = ChargeStatus::kFault; + break; + case 0b100: + charge_status_ = ChargeStatus::kBatteryCritical; + break; + case 0b101: + charge_status_ = ChargeStatus::kDischarging; + break; + case 0b110: + case 0b111: + charge_status_ = ChargeStatus::kUnknown; + break; } } @@ -127,9 +173,15 @@ auto Samd::ResetToFlashSamd() -> void { } auto Samd::SetFastChargeEnabled(bool en) -> void { + // Always update NVS, so that the setting is right after the SAMD firmware is + // updated. + nvs_.FastCharge(en); + if (version_ < 4) { return; } + ESP_LOGI(kTag, "set fast charge %u", en); + I2CTransaction transaction; transaction.start() .write_addr(kAddress, I2C_MASTER_WRITE) diff --git a/src/drivers/spi.cpp b/src/drivers/spi.cpp index 632fe89f..40487197 100644 --- a/src/drivers/spi.cpp +++ b/src/drivers/spi.cpp @@ -41,7 +41,7 @@ esp_err_t init_spi(void) { // manages its own use of DMA-capable memory. .max_transfer_sz = 4096, .flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_IOMUX_PINS, - .isr_cpu_id = ESP_INTR_CPU_AFFINITY_0, + .isr_cpu_id = ESP_INTR_CPU_AFFINITY_1, .intr_flags = ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, }; diff --git a/src/drivers/test/test_samd.cpp b/src/drivers/test/test_samd.cpp index c466d88e..96248377 100644 --- a/src/drivers/test/test_samd.cpp +++ b/src/drivers/test/test_samd.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ +#include "drivers/nvs.hpp" #include "drivers/samd.hpp" #include <cstdint> @@ -16,7 +17,8 @@ namespace drivers { TEST_CASE("samd21 interface", "[integration]") { I2CFixture i2c; - auto samd = std::make_unique<Samd>(); + std::unique_ptr<drivers::NvsStorage> nvs{drivers::NvsStorage::OpenSync()}; + auto samd = std::make_unique<Samd>(*nvs); REQUIRE(samd); diff --git a/src/drivers/touchwheel.cpp b/src/drivers/touchwheel.cpp index 5d55c6f2..402b839d 100644 --- a/src/drivers/touchwheel.cpp +++ b/src/drivers/touchwheel.cpp @@ -137,8 +137,12 @@ TouchWheelData TouchWheel::GetTouchWheelData() const { return data_; } -auto TouchWheel::PowerDown() -> void { - WriteRegister(LOW_POWER, 0); +auto TouchWheel::Recalibrate() -> void { + WriteRegister(CALIBRATE, 1); +} + +auto TouchWheel::LowPowerMode(bool en) -> void { + WriteRegister(LOW_POWER, en ? 0 : 1); } } // namespace drivers |
