Initial bms work; protocol is todo

This commit is contained in:
CommanderRedYT
2022-12-26 06:50:32 +01:00
parent 7a717aca82
commit a9853aff5b
18 changed files with 793 additions and 19 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -274,3 +274,5 @@ struct FlipScreenAccessor : public NewSettingsAccessor<bool> { ConfigWrapper<boo
// Other
struct AnhaengerIdAccessor : public NewSettingsAccessor<uint16_t> { ConfigWrapper<uint16_t> &getConfig() const override { return configs.anhaenger_id; } };
struct UsernameAccessor : public NewSettingsAccessor<std::string> { ConfigWrapper<std::string> &getConfig() const override { return configs.otaUsername; } };
struct BMSEnabledAccessor : public NewSettingsAccessor<bool> { ConfigWrapper<bool> &getConfig() const override { return configs.bmsEnabled; } };

View File

@@ -0,0 +1,9 @@
#include "bmsclearscanaction.h"
// local includes
#include "bmsutils.h"
void BMSClearScanAction::triggered()
{
bmsutils::antBms.clearScanResults();
}

View File

@@ -0,0 +1,10 @@
#pragma once
// 3rdparty lib includes
#include <actioninterface.h>
class BMSClearScanAction : public virtual espgui::ActionInterface
{
public:
void triggered() override;
};

View File

@@ -0,0 +1,9 @@
#include "bmsscanaction.h"
// local includes
#include "bmsutils.h"
void BMSScanAction::triggered()
{
bmsutils::antBms.startScan();
}

View File

@@ -0,0 +1,10 @@
#pragma once
// 3rdparty lib includes
#include <actioninterface.h>
class BMSScanAction : public virtual espgui::ActionInterface
{
public:
void triggered() override;
};

350
main/antbms.cpp Normal file
View File

@@ -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<scanResults_t> &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));
}

126
main/antbms.h Normal file
View File

@@ -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 <optional>
// esp-idf includes
#include <esp_log.h>
// 3rdparty lib includes
#include <NimBLEDevice.h>
#include <espchrono.h>
typedef struct {
NimBLEAddress address;
std::string name;
} scanResult_t;
typedef struct {
std::vector<scanResult_t> 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<scanResults_t> &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<NimBLEClient*> m_client;
std::optional<NimBLERemoteService*> m_service;
std::optional<NimBLERemoteCharacteristic*> m_rxCharacteristic;
std::optional<NimBLERemoteCharacteristic*> m_txCharacteristic;
std::optional<scanResults_t> m_scanResults;
espchrono::millis_clock::time_point m_lastRequestTime;
void bmsGetInfo3();
void bmsGetInfo4();
};

View File

@@ -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 = {};

View File

@@ -1,4 +1,56 @@
#include "bmsutils.h"
namespace bms {
} // namespace bms
// system includes
#include <iomanip>
// 3rdparty lib includes
#include <NimBLEDevice.h>
// 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

View File

@@ -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

View File

@@ -487,7 +487,10 @@ public:
ConfigWrapperLegacy<bool> bleFenceEnabled {false, DoReset, {}, "bleFenceEnabled" };
} bleSettings;
ConfigWrapperLegacy<bool> setupDone {false, DoReset, {}, "setupDone" };
ConfigWrapperLegacy<bool> setupDone {false, DoReset, {}, "setupDone" };
ConfigWrapperLegacy<bool> bmsEnabled {false, DoReset, {}, "bmsEnabled" };
ConfigWrapperLegacy<std::string> 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<ConfigWrapperInterface>(x),
NEW_SETTINGS(HELPER)
#undef HELPER
std::ref<ConfigWrapperInterface>(bleSettings.bleFenceEnabled)
std::ref<ConfigWrapperInterface>(bmsAddress)
);
}

View File

@@ -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<makeComponent<MenuItem, StaticText<TEXT_ENABLED>, BobbyCheckbox, BleEnabledAccessor>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BMS_ENABLED>, BobbyCheckbox, BMSEnabledAccessor>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BMS_SCANS>, PushScreenAction<BMSScanMenu>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_FENCE_ENABLED>, BobbyCheckbox, BleFenceEnabledAccessor>>();
constructMenuItem<makeComponent<MenuItem, BleServerPeerDevicesText, DisabledColor, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, BleCharacSubscribedText, DisabledColor, DummyAction>>();

View File

@@ -0,0 +1,140 @@
#include "bmsscanmenu.h"
// 3rdparty lib includes
#include <actions/popscreenaction.h>
#include <fmt/format.h>
#include <menuitem.h>
#include <richtextrenderer.h>
#include <screenmanager.h>
// 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<makeComponent<MenuItem, StaticText<TEXT_STARTSCAN>, BMSScanAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_CLEARRESULTS>, BMSClearScanAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, 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<BMSScanMenuItem>(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

View File

@@ -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

View File

@@ -19,6 +19,6 @@ public:
void update() override;
private:
bool m_wasScanning;
bool m_wasScanning{false};
};
} // namespace bobby

View File

@@ -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 },