diff --git a/configs/sdkconfig_comred_new b/configs/sdkconfig_comred_new index 8f0f73a..30ed21a 100644 --- a/configs/sdkconfig_comred_new +++ b/configs/sdkconfig_comred_new @@ -470,12 +470,12 @@ CONFIG_BT_CONTROLLER_ENABLED=y # CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y # CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set -# CONFIG_BT_NIMBLE_LOG_LEVEL_NONE is not set +CONFIG_BT_NIMBLE_LOG_LEVEL_NONE=y # CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR is not set # CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING is not set # CONFIG_BT_NIMBLE_LOG_LEVEL_INFO is not set -CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG=y -CONFIG_BT_NIMBLE_LOG_LEVEL=0 +# CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG is not set +CONFIG_BT_NIMBLE_LOG_LEVEL=4 CONFIG_BT_NIMBLE_MAX_CONNECTIONS=3 CONFIG_BT_NIMBLE_MAX_BONDS=3 CONFIG_BT_NIMBLE_MAX_CCCDS=8 @@ -1814,12 +1814,12 @@ CONFIG_TFT_LOAD_FONT7=y # # ESP-NimBLE-CPP configuration # -# CONFIG_NIMBLE_CPP_LOG_LEVEL_NONE is not set +CONFIG_NIMBLE_CPP_LOG_LEVEL_NONE=y # CONFIG_NIMBLE_CPP_LOG_LEVEL_ERROR is not set # CONFIG_NIMBLE_CPP_LOG_LEVEL_WARNING is not set -CONFIG_NIMBLE_CPP_LOG_LEVEL_INFO=y +# CONFIG_NIMBLE_CPP_LOG_LEVEL_INFO is not set # CONFIG_NIMBLE_CPP_LOG_LEVEL_DEBUG is not set -CONFIG_NIMBLE_CPP_LOG_LEVEL=3 +CONFIG_NIMBLE_CPP_LOG_LEVEL=0 # CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT is not set # CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT is not set # CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT is not set diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 0efe6cd..e0db648 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,11 +1,14 @@ set(BOBBY_HEADERS accessorhelpers.h + antbms.h accessors/globalaccessors.h accessors/settingsaccessors.h accessors/wifiaccessors.h accessors/wifiapconfigaccessors.h accessors/wifistaconfigaccessors.h actions/assertaction.h + actions/bmsclearscanaction.h + actions/bmsscanaction.h actions/dividebyzeroaction.h actions/erasenvsaction.h actions/ledstripanimationactions.h @@ -127,6 +130,7 @@ set(BOBBY_HEADERS screens/batteryinfodisplay.h screens/batterymenu.h screens/blesettingsmenu.h + screens/bmsscanmenu.h screens/boardcomputerhardwaresettingsmenu.h screens/buzzermenu.h screens/calibratevoltagedisplay.h @@ -254,8 +258,11 @@ set(BOBBY_HEADERS ) set(BOBBY_SOURCES + antbms.cpp accessors/wifistaconfigaccessors.cpp actions/assertaction.cpp + actions/bmsclearscanaction.cpp + actions/bmsscanaction.cpp actions/dividebyzeroaction.cpp actions/ledstripanimationactions.cpp actions/ledstripblinkactions.cpp @@ -356,6 +363,7 @@ set(BOBBY_SOURCES screens/batteryinfodisplay.cpp screens/batterymenu.cpp screens/blesettingsmenu.cpp + screens/bmsscanmenu.cpp screens/boardcomputerhardwaresettingsmenu.cpp screens/buzzermenu.cpp screens/calibratevoltagedisplay.cpp diff --git a/main/accessors/settingsaccessors.h b/main/accessors/settingsaccessors.h index 50660a5..2be2a20 100644 --- a/main/accessors/settingsaccessors.h +++ b/main/accessors/settingsaccessors.h @@ -274,3 +274,5 @@ struct FlipScreenAccessor : public NewSettingsAccessor { ConfigWrapper { ConfigWrapper &getConfig() const override { return configs.anhaenger_id; } }; struct UsernameAccessor : public NewSettingsAccessor { ConfigWrapper &getConfig() const override { return configs.otaUsername; } }; + +struct BMSEnabledAccessor : public NewSettingsAccessor { ConfigWrapper &getConfig() const override { return configs.bmsEnabled; } }; diff --git a/main/actions/bmsclearscanaction.cpp b/main/actions/bmsclearscanaction.cpp new file mode 100644 index 0000000..a8aed71 --- /dev/null +++ b/main/actions/bmsclearscanaction.cpp @@ -0,0 +1,9 @@ +#include "bmsclearscanaction.h" + +// local includes +#include "bmsutils.h" + +void BMSClearScanAction::triggered() +{ + bmsutils::antBms.clearScanResults(); +} diff --git a/main/actions/bmsclearscanaction.h b/main/actions/bmsclearscanaction.h new file mode 100644 index 0000000..4da8465 --- /dev/null +++ b/main/actions/bmsclearscanaction.h @@ -0,0 +1,10 @@ +#pragma once + +// 3rdparty lib includes +#include + +class BMSClearScanAction : public virtual espgui::ActionInterface +{ +public: + void triggered() override; +}; diff --git a/main/actions/bmsscanaction.cpp b/main/actions/bmsscanaction.cpp new file mode 100644 index 0000000..8f6d4bb --- /dev/null +++ b/main/actions/bmsscanaction.cpp @@ -0,0 +1,9 @@ +#include "bmsscanaction.h" + +// local includes +#include "bmsutils.h" + +void BMSScanAction::triggered() +{ + bmsutils::antBms.startScan(); +} diff --git a/main/actions/bmsscanaction.h b/main/actions/bmsscanaction.h new file mode 100644 index 0000000..c3fb9c3 --- /dev/null +++ b/main/actions/bmsscanaction.h @@ -0,0 +1,10 @@ +#pragma once + +// 3rdparty lib includes +#include + +class BMSScanAction : public virtual espgui::ActionInterface +{ +public: + void triggered() override; +}; diff --git a/main/antbms.cpp b/main/antbms.cpp new file mode 100644 index 0000000..ee33d70 --- /dev/null +++ b/main/antbms.cpp @@ -0,0 +1,350 @@ +#include "antbms.h" + +// local includes +#include "bmsutils.h" +#include "newsettings.h" + +using namespace std::chrono_literals; + +void ANTBms::init() +{ + // init code + m_initialized = true; + + // scan + startScan(); +} + +void ANTBms::update() +{ + if (!m_initialized) + return; + + handleConnect(); + + if (m_client && (*m_client)->isConnected()) + { + requestData(); + } +} + +void ANTBms::deinit() +{ + // deinit code + m_initialized = false; + + if (m_client) + { + (*m_client)->disconnect(); + m_client.reset(); + } + + if (m_scanResults) + { + m_scanResults.reset(); + } + + if (m_service) + { + m_service.reset(); + } + + if (m_rxCharacteristic) + { + m_rxCharacteristic.reset(); + } + + if (m_txCharacteristic) + { + m_txCharacteristic.reset(); + } + + m_scanStarted = false; + m_initialized = false; +} + +bool ANTBms::isInitialized() const +{ + return m_initialized; +} + +void ANTBms::startScan() +{ + if (!m_initialized) + return; + + ESP_LOGI(TAG, "starting scan"); + + NimBLEScan* pScan = NimBLEDevice::getScan(); + pScan->setActiveScan(true); + pScan->setInterval(100); + pScan->setWindow(99); + pScan->setScanCallbacks(new ScanResultsCallback(this), false); + pScan->start(5000); + + ESP_LOGI(TAG, "scan started"); + + m_scanStarted = true; +} + +void ANTBms::clearScanResults() +{ + m_scanResults.reset(); +} + +bool ANTBms::getScanStatus() const +{ + return m_scanStarted; +} + +const std::optional &ANTBms::getScanResults() +{ + return m_scanResults; +} + +void ANTBms::handleConnect() +{ + if (!m_initialized) + return; + + ESP_LOGD(TAG, "!m_initialized passed"); + + if (m_connected) + return; + + ESP_LOGD(TAG, "m_connected passed"); + + if (!m_scanResults) + return; + + ESP_LOGD(TAG, "!m_scanResults passed"); + + if (m_scanResults && m_scanResults->entries.empty()) + return; + + ESP_LOGD(TAG, "m_scanResults->entries.empty() passed"); + + if (m_client && (*m_client)->isConnected()) + return; + + ESP_LOGD(TAG, "!m_client.has_value() passed"); + + if (configs.bmsAddress.value().empty()) + return; + + ESP_LOGD(TAG, "configs.bmsAddress.value().empty() passed"); + + ESP_LOGI(TAG, "connecting to bms"); + + if (NimBLEDevice::getClientListSize()) + { + m_client = NimBLEDevice::getClientByPeerAddress(configs.bmsAddress.value()); + + if (m_client) + { + if (!(*m_client)->connect(configs.bmsAddress.value())) + { + ESP_LOGE(TAG, "Reconnect failed"); + m_client.reset(); + m_service.reset(); + m_rxCharacteristic.reset(); + m_txCharacteristic.reset(); + + m_connected = false; + return; + } + ESP_LOGI(TAG, "Reconnected to client"); + } + else + { + m_client = NimBLEDevice::getDisconnectedClient(); + } + } + + if (!m_client) + { + if (NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) + { + ESP_LOGE(TAG, "Max clients reached - no more connections available!"); + return; + } + + m_client = NimBLEDevice::createClient(); + + const auto pClient = *m_client; + + pClient->setClientCallbacks(new ClientCallbacks(this), false); + pClient->setConnectTimeout(10); + pClient->setConnectionParams(12, 12, 0, 51); + + if (!pClient->connect(configs.bmsAddress.value())) + { + NimBLEDevice::deleteClient(pClient); + m_client.reset(); + m_service.reset(); + m_rxCharacteristic.reset(); + m_txCharacteristic.reset(); + + m_connected = false; + return; + } + } + + if (!(*m_client)->isConnected()) + { + if (!(*m_client)->connect(configs.bmsAddress.value())) + { + ESP_LOGE(TAG, "Failed to connect"); + m_client.reset(); + m_service.reset(); + m_rxCharacteristic.reset(); + m_txCharacteristic.reset(); + + m_connected = false; + return; + } + } + + ESP_LOGI(TAG, "Connected!"); + m_connected = true; + + m_service = (*m_client)->getService(serviceUUID); + + if (!m_service) + { + ESP_LOGE(TAG, "Failed to find our service UUID: %s", serviceUUID.toString().c_str()); + m_client.reset(); + m_service.reset(); + m_rxCharacteristic.reset(); + m_txCharacteristic.reset(); + + m_connected = false; + return; + } + + if (m_service && (*m_service)) + { + ESP_LOGI(TAG, "Getting characteristic"); + m_rxCharacteristic = (*m_service)->getCharacteristic(charRXUUID); + m_txCharacteristic = (*m_service)->getCharacteristic(charTXUUID); + + if ((m_rxCharacteristic && (*m_rxCharacteristic)) && (m_txCharacteristic && (*m_txCharacteristic))) + { + const auto pChr = *m_rxCharacteristic; + + if (pChr->canNotify()) + { + ESP_LOGI(TAG, "Subscribing to notifications"); + if (!pChr->subscribe(true, bmsutils::_notifyCB)) + { + (*m_client)->disconnect(); + ESP_LOGE(TAG, "Failed to subscribe for notifications"); + m_client.reset(); + m_service.reset(); + m_rxCharacteristic.reset(); + + m_connected = false; + return; + } + ESP_LOGI(TAG, "Subscribed for notifications"); + + m_connected = true; + + // 7ea1010000be1855aa55 + uint8_t data[10] = {0x7e, 0xa1, 0x01, 0x00, 0x00, 0xbe, 0x18, 0x55, 0xaa, 0x55}; + sendCommand(data, sizeof(data)); + return; + } + else + { + ESP_LOGE(TAG, "Characteristic can't notify, disconnecting"); + (*m_client)->disconnect(); + m_client.reset(); + m_service.reset(); + m_rxCharacteristic.reset(); + + m_connected = false; + return; + } + } + else + { + ESP_LOGE(TAG, "Failed to find our characteristic UUID: %s", charRXUUID.toString().c_str()); + m_client.reset(); + m_service.reset(); + m_rxCharacteristic.reset(); + + m_connected = false; + return; + } + } +} + +void ANTBms::notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) +{ + ESP_LOGI(TAG, "Received %s: %s (%.*s)", isNotify ? "notification" : "indication", bmsutils::bytesToHex(pData, length).c_str(), length, pData); +} + +void ANTBms::requestData() +{ + if (!m_initialized) + return; + + if (!m_connected) + return; + + if (espchrono::ago(m_lastRequestTime) > 2000ms || m_newPacketReceived) + { + m_lastRequestTime = espchrono::millis_clock::now(); + + if (m_toggle) + { + bmsGetInfo3(); + m_newPacketReceived = false; + } + else + { + bmsGetInfo4(); + m_newPacketReceived = false; + } + m_toggle = !m_toggle; + } +} + +void ANTBms::sendCommand(uint8_t *pData, size_t length) +{ + if (!m_initialized) + return; + + if (!m_connected) + return; + + if (!m_txCharacteristic) + return; + + if (!(*m_txCharacteristic)) + return; + + if (!(*m_txCharacteristic)->canWrite()) + return; + + ESP_LOGI(TAG, "Sending command: %s", bmsutils::bytesToHex(pData, length).c_str()); + + (*m_txCharacteristic)->writeValue(pData, length, true); +} + +void ANTBms::bmsGetInfo3() +{ + // DD A5 03 00 FF FD 77 + uint8_t data[7] = {0xdd, 0xa5, 0x3, 0x0, 0xff, 0xfd, 0x77}; + + sendCommand(data, sizeof(data)); +} + +void ANTBms::bmsGetInfo4() +{ + // DD A5 04 00 FF FC 77 + uint8_t data[7] = {0xdd, 0xa5, 0x4, 0x0, 0xff, 0xfc, 0x77}; + + sendCommand(data, sizeof(data)); +} diff --git a/main/antbms.h b/main/antbms.h new file mode 100644 index 0000000..e1274e7 --- /dev/null +++ b/main/antbms.h @@ -0,0 +1,126 @@ +#pragma once + +// TODO: jetbrains://idea/navigate/reference?project=bms_base_source_from_JADX&path=com/mayi/bms/bluetooth/BmsBluetoothManager.java + +// system includes +#include + +// esp-idf includes +#include + +// 3rdparty lib includes +#include +#include + +typedef struct { + NimBLEAddress address; + std::string name; +} scanResult_t; + +typedef struct { + std::vector entries; +} scanResults_t; + +const NimBLEUUID serviceUUID{"0000ffe0-0000-1000-8000-00805f9b34fb"}; +const NimBLEUUID charRXUUID {"0000ffe1-0000-1000-8000-00805f9b34fb"}; +//const NimBLEUUID charTXUUID {"0000ffe1-0000-1000-8000-00805f9b34fb"}; // same as RX +const NimBLEUUID charTXUUID {"0000ffe2-0000-1000-8000-00805f9b34fb"}; // different + +class ANTBms +{ +public: + static constexpr const char * const TAG = "ANTBMS"; + + // basic functions + void init(); + void update(); + void deinit(); + + [[nodiscard]] bool isInitialized() const; + + // scans + void startScan(); + + [[nodiscard]] bool getScanStatus() const; + + const std::optional &getScanResults(); + void clearScanResults(); + + void handleConnect(); + + void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); + + void requestData(); + void sendCommand(uint8_t *pData, size_t length); +private: + + class ScanResultsCallback : public NimBLEScanCallbacks + { + public: + explicit ScanResultsCallback(ANTBms* antBms) : m_antBms{antBms} {} + + void onScanEnd(NimBLEScanResults scanResults) override + { + m_antBms->m_scanResults.reset(); + + ESP_LOGI(TAG, "BLE Scan complete"); + + scanResults_t results; + + for (auto &result : scanResults) + { + if (result->isAdvertisingService(serviceUUID)) + { + scanResult_t scanResult; + scanResult.address = result->getAddress(); + scanResult.name = result->getName(); + results.entries.push_back(scanResult); + } + } + + m_antBms->m_scanResults = results; + + m_antBms->m_scanStarted = false; + } + private: + ANTBms* m_antBms; + }; + + class ClientCallbacks : public NimBLEClientCallbacks + { + public: + explicit ClientCallbacks(ANTBms* antBms) : m_antBms{antBms} {} + void onConnect(NimBLEClient* pClient) override + { + m_antBms->m_connected = true; + ESP_LOGD(TAG, "Connected to server"); + } + + void onDisconnect(NimBLEClient* pClient, int reason) override + { + m_antBms->m_connected = false; + ESP_LOGI(TAG, "Disconnected from server (%d)", reason); + } + private: + ANTBms* m_antBms; + }; + + bool m_initialized{false}; + bool m_scanStarted{false}; + bool m_connected{false}; + + bool m_newPacketReceived{false}; + bool m_toggle{false}; + + std::optional m_client; + std::optional m_service; + std::optional m_rxCharacteristic; + std::optional m_txCharacteristic; + + std::optional m_scanResults; + + espchrono::millis_clock::time_point m_lastRequestTime; + + void bmsGetInfo3(); + void bmsGetInfo4(); +}; diff --git a/main/ble_bobby.cpp b/main/ble_bobby.cpp index 51a2f23..0dd5bd5 100644 --- a/main/ble_bobby.cpp +++ b/main/ble_bobby.cpp @@ -55,7 +55,12 @@ void createBle() { ESP_LOGI("BOBBY", "called"); - NimBLEDevice::init(configs.bluetoothName.value()); + if (!NimBLEDevice::getInitialized()) + { + ESP_LOGI("BOBBY", "Initializing BLE"); + NimBLEDevice::init(configs.bluetoothName.value()); + NimBLEDevice::setPower(ESP_PWR_LVL_P9); + } const auto serviceUuid{"0335e46c-f355-4ce6-8076-017de08cee98"}; @@ -94,7 +99,10 @@ void destroyBle() { ESP_LOGI("BOBBY", "called"); - NimBLEDevice::deinit(true); + if (NimBLEDevice::getInitialized()) + { + NimBLEDevice::deinit(true); + } pServer = {}; pService = {}; diff --git a/main/bmsutils.cpp b/main/bmsutils.cpp index 8c04356..f998140 100644 --- a/main/bmsutils.cpp +++ b/main/bmsutils.cpp @@ -1,4 +1,56 @@ #include "bmsutils.h" -namespace bms { -} // namespace bms +// system includes +#include + +// 3rdparty lib includes +#include + +// local includes +#include "newsettings.h" + +namespace bmsutils { + +ANTBms antBms; + +void init() {} + +void update() +{ + const auto initialized = antBms.isInitialized(); + + if (configs.bmsEnabled.value() && !initialized && NimBLEDevice::getInitialized()) + { + ESP_LOGI("bmsutils", "initializing bms"); + antBms.init(); + } + else if (!configs.bmsEnabled.value() && initialized) + { + ESP_LOGI("bmsutils", "deinitializing bms"); + antBms.deinit(); + } + + if (initialized && NimBLEDevice::getInitialized()) + { + antBms.update(); + } +} + +void _notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) +{ + ESP_LOGI("bmsutils", "notifyCB: %s", bmsutils::bytesToHex(pData, length).c_str()); + antBms.notifyCB(pRemoteCharacteristic, pData, length, isNotify); +} + +std::string bytesToHex(uint8_t *pData, size_t length) +{ + std::stringstream ss; + + for (size_t i = 0; i < length; i++) + { + ss << std::hex << std::setfill('0') << std::setw(2) << (int)pData[i]; + } + + return ss.str(); +} +} // namespace bmsutils diff --git a/main/bmsutils.h b/main/bmsutils.h index 478fa10..ae41fed 100644 --- a/main/bmsutils.h +++ b/main/bmsutils.h @@ -1,4 +1,16 @@ #pragma once -namespace bms { -} // namespace bms +// local includes +#include "antbms.h" + +namespace bmsutils { +extern ANTBms antBms; + +void init(); + +void update(); + +void _notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify); + +std::string bytesToHex(uint8_t *pData, size_t length); +} // namespace bmsutils diff --git a/main/newsettings.h b/main/newsettings.h index c6b213b..e43e221 100644 --- a/main/newsettings.h +++ b/main/newsettings.h @@ -487,7 +487,10 @@ public: ConfigWrapperLegacy bleFenceEnabled {false, DoReset, {}, "bleFenceEnabled" }; } bleSettings; - ConfigWrapperLegacy setupDone {false, DoReset, {}, "setupDone" }; + ConfigWrapperLegacy setupDone {false, DoReset, {}, "setupDone" }; + + ConfigWrapperLegacy bmsEnabled {false, DoReset, {}, "bmsEnabled" }; + ConfigWrapperLegacy bmsAddress {std::string{}, DoReset, {}, "bmsAddress" }; #define NEW_SETTINGS(x) \ x(baseMacAddressOverride) \ @@ -809,10 +812,12 @@ public: x(feature.webserver.isEnabled) \ x(feature.webserver_disable_lock.isEnabled) \ x(bleSettings.bleEnabled) \ - x(emulateFeedback) + x(emulateFeedback) \ + x(setupDone) \ + x(bmsEnabled) #define FEATURES(x) \ - x(feature.ble) \ + x(feature.ble) \ x(feature.cloud) \ x(feature.dnsannounce)\ x(feature.esp_now) \ @@ -831,7 +836,7 @@ public: #define HELPER(x) callback(x); NEW_SETTINGS(HELPER) #undef HELPER - callback(bleSettings.bleFenceEnabled); + callback(bmsAddress); } auto getAllConfigParams() @@ -840,7 +845,7 @@ public: #define HELPER(x) std::ref(x), NEW_SETTINGS(HELPER) #undef HELPER - std::ref(bleSettings.bleFenceEnabled) + std::ref(bmsAddress) ); } diff --git a/main/screens/blesettingsmenu.cpp b/main/screens/blesettingsmenu.cpp index 958954c..da64825 100644 --- a/main/screens/blesettingsmenu.cpp +++ b/main/screens/blesettingsmenu.cpp @@ -10,10 +10,10 @@ // local includes #include "accessors/settingsaccessors.h" +#include "bmsscanmenu.h" #include "guihelpers/bobbychangevaluedisplay.h" #include "guihelpers/bobbycheckbox.h" #include "icons/back.h" -#include "screens/settingsmenu.h" #include "texthelpers/bletexthelpers.h" namespace bobby { @@ -21,6 +21,8 @@ namespace bobby { namespace { constexpr char TEXT_BLESETTINGS[] = "BLE settings"; constexpr char TEXT_ENABLED[] = "Enabled"; +constexpr char TEXT_BMS_ENABLED[] = "BMS enabled"; +constexpr char TEXT_BMS_SCANS[] = "BMS scans"; constexpr char TEXT_FENCE_ENABLED[] = "Fence enabled"; constexpr char TEXT_NAME[] = "Name"; constexpr char TEXT_NAME_FORMATTED[] = "Name: &s"; @@ -39,6 +41,10 @@ BleSettingsMenu::BleSettingsMenu() { using namespace espgui; constructMenuItem, BobbyCheckbox, BleEnabledAccessor>>(); + + constructMenuItem, BobbyCheckbox, BMSEnabledAccessor>>(); + constructMenuItem, PushScreenAction>>(); + constructMenuItem, BobbyCheckbox, BleFenceEnabledAccessor>>(); constructMenuItem>(); constructMenuItem>(); diff --git a/main/screens/bmsscanmenu.cpp b/main/screens/bmsscanmenu.cpp new file mode 100644 index 0000000..b2672c2 --- /dev/null +++ b/main/screens/bmsscanmenu.cpp @@ -0,0 +1,140 @@ +#include "bmsscanmenu.h" + +// 3rdparty lib includes +#include +#include +#include +#include +#include + +// local includes +#include "actions/bmsclearscanaction.h" +#include "actions/bmsscanaction.h" +#include "bmsutils.h" +#include "guihelpers/bobbypopupdisplay.h" +#include "icons/back.h" +#include "newsettings.h" + +namespace bobby { + +using namespace espgui; + +namespace { +constexpr char TEXT_STARTSCAN[] = "Start scan"; +constexpr char TEXT_CLEARRESULTS[] = "Clear results"; +constexpr char TEXT_BACK[] = "Back"; + +class BMSScanMenuItem : public MenuItem +{ +public: + BMSScanMenuItem(const scanResult_t &info) : m_info{info} {} + + void setInfo(const scanResult_t &info) { m_info = info; } + + void triggered() override; + std::string text() const override; + +private: + scanResult_t m_info; +}; + +constexpr const size_t extraItemsAtBeginning = 2; // Scan and clear +} // namespace + +BMSScanMenu::BMSScanMenu() +{ + constructMenuItem, BMSScanAction>>(); + constructMenuItem, BMSClearScanAction>>(); + constructMenuItem, PopScreenAction, StaticMenuItemIcon<&bobbyicons::back>>>(); +} + +std::string BMSScanMenu::text() const +{ + const bool scanStatus = bmsutils::antBms.getScanStatus(); + const auto &results = bmsutils::antBms.getScanResults(); + + auto text = fmt::format("Scan {}{}", + [&](){ + if (scanStatus) return "&4"; + else return "&2"; + }(), + [&](){ + if (scanStatus) return "Scanning"; + else return "Finished"; + }()); + + if (!scanStatus && results) + text += fmt::format("&c ({} found)", results->entries.size()); + else if (!scanStatus && !results) + text += fmt::format("&c (0 found)"); + + return text; +} + +void BMSScanMenu::back() +{ + popScreen(); +} + +void BMSScanMenu::start() +{ + Base::start(); + + m_wasScanning = true; +} + +void BMSScanMenu::update() +{ + const auto scanStatus = bmsutils::antBms.getScanStatus(); + + if (scanStatus && !m_wasScanning) + { + m_wasScanning = true; + } + else if (!scanStatus && m_wasScanning) + { + m_wasScanning = false; + + auto backButton = takeLastMenuItem(); + + const auto &result = bmsutils::antBms.getScanResults(); + + for (std::size_t i = 0; i < (result ? result->entries.size() : 0); i++) + { + if (menuItemCount() - extraItemsAtBeginning <= i) + constructMenuItem(result->entries[i]); + else + ((BMSScanMenuItem*)(&getMenuItem(i + extraItemsAtBeginning)))->setInfo(result->entries[i]); + } + + while (menuItemCount() - extraItemsAtBeginning > (result ? result->entries.size() : 0)) + takeLastMenuItem(); + + emplaceMenuItem(std::move(backButton)); + } + + Base::update(); +} + +namespace { + +void BMSScanMenuItem::triggered() +{ + configs.write_config(configs.bmsAddress, m_info.address); + popScreen(); +} + +std::string BMSScanMenuItem::text() const +{ + return fmt::format("{}{}", + [&](){ + if (m_info.name.find("ANT-BLE") != std::string::npos || m_info.address == configs.bmsAddress.value()) + return "&2"; + return ""; + }(), + m_info.name.empty() ? m_info.address.toString() : m_info.name + ); +} + +} // namespace +} // namespace bobby diff --git a/main/screens/bmsscanmenu.h b/main/screens/bmsscanmenu.h new file mode 100644 index 0000000..9a317d0 --- /dev/null +++ b/main/screens/bmsscanmenu.h @@ -0,0 +1,25 @@ +#pragma once + +// local includes +#include "guihelpers/bobbymenudisplay.h" + +namespace bobby { + +class BMSScanMenu : public BobbyMenuDisplay +{ + using Base = BobbyMenuDisplay; +public: + BMSScanMenu(); + + std::string text() const override; + + void start() override; + void update() override; + + void back() override; + +private: + bool m_wasScanning{false}; +}; + +} // namespace bobby diff --git a/main/screens/wifistascanmenu.h b/main/screens/wifistascanmenu.h index 6b2c263..7cec8c1 100644 --- a/main/screens/wifistascanmenu.h +++ b/main/screens/wifistascanmenu.h @@ -19,6 +19,6 @@ public: void update() override; private: - bool m_wasScanning; + bool m_wasScanning{false}; }; } // namespace bobby diff --git a/main/taskmanager.cpp b/main/taskmanager.cpp index 7fef79a..38163a0 100644 --- a/main/taskmanager.cpp +++ b/main/taskmanager.cpp @@ -38,6 +38,7 @@ #include "screens.h" #include "utils.h" #include "feedbackemulator.h" +#include "bmsutils.h" using namespace std::chrono_literals; @@ -68,6 +69,7 @@ BobbySchedulerTask schedulerTasksArr[] { #endif BobbySchedulerTask { "ota", initOta, handleOta, 75ms, false }, BobbySchedulerTask { "ble", initBle, handleBle, 100ms, false }, + BobbySchedulerTask { "bms", bmsutils::init, bmsutils::update, 100ms, false }, BobbySchedulerTask { "webserver", initWebserver, handleWebserver, 100ms, false }, BobbySchedulerTask { "ledstrip", initLedStrip, updateLedStrip, 24ms, false }, BobbySchedulerTask { "espnow", espnow::initESPNow, espnow::handle, 150ms, false },