Current WIP state, protocol sadly not working
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
set(BOBBY_HEADERS
|
||||
accessorhelpers.h
|
||||
antbms.h
|
||||
antbmsmanager.h
|
||||
accessors/globalaccessors.h
|
||||
accessors/settingsaccessors.h
|
||||
accessors/wifiaccessors.h
|
||||
@ -259,6 +260,7 @@ set(BOBBY_HEADERS
|
||||
|
||||
set(BOBBY_SOURCES
|
||||
antbms.cpp
|
||||
antbmsmanager.cpp
|
||||
accessors/wifistaconfigaccessors.cpp
|
||||
actions/assertaction.cpp
|
||||
actions/bmsclearscanaction.cpp
|
||||
|
396
main/antbms.cpp
396
main/antbms.cpp
@ -1,350 +1,108 @@
|
||||
#include "antbms.h"
|
||||
|
||||
// local includes
|
||||
#include "bmsutils.h"
|
||||
#include "newsettings.h"
|
||||
// esp-idf includes
|
||||
#include <esp_log.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
BmsInstruction::BmsInstruction(uint8_t b, uint8_t b2) :
|
||||
functionCode{b},
|
||||
length{b2}
|
||||
{}
|
||||
|
||||
void ANTBms::init()
|
||||
void BmsInstruction::setData(uint8_t *_data, uint8_t _length)
|
||||
{
|
||||
// init code
|
||||
m_initialized = true;
|
||||
|
||||
// scan
|
||||
startScan();
|
||||
std::copy(_data, _data + _length, this->data);
|
||||
}
|
||||
|
||||
void ANTBms::update()
|
||||
int BmsInstruction::getAddress() const
|
||||
{
|
||||
if (!m_initialized)
|
||||
return;
|
||||
return address;
|
||||
}
|
||||
|
||||
handleConnect();
|
||||
void BmsInstruction::setAddress(int _address)
|
||||
{
|
||||
address = _address;
|
||||
}
|
||||
|
||||
if (m_client && (*m_client)->isConnected())
|
||||
uint8_t *BmsInstruction::getInstruction()
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
requestData();
|
||||
return BmsBluetoothInst::buildReadBmsInst(functionCode, address, 0);
|
||||
}
|
||||
return BmsBluetoothInst::buildReadBmsInstWithData(this->functionCode, this->address, this->length, this->data);
|
||||
}
|
||||
|
||||
void ANTBms::deinit()
|
||||
std::string BmsInstruction::toString() const
|
||||
{
|
||||
// deinit code
|
||||
m_initialized = false;
|
||||
return "BmsInstruction{functionCode=" + std::to_string(functionCode) + ", address=" + std::to_string(address) + ", inst = " + ", data = " + "}";
|
||||
}
|
||||
|
||||
if (m_client)
|
||||
int CRC16::calcCrc16(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
// calculate crc16
|
||||
uint16_t crc = 0xFFFF;
|
||||
|
||||
for (int pos = 0; pos < len; pos++)
|
||||
{
|
||||
(*m_client)->disconnect();
|
||||
m_client.reset();
|
||||
}
|
||||
crc ^= (uint16_t) data[pos]; // XOR byte into least sig. byte of crc
|
||||
|
||||
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;
|
||||
for (int i = 8; i != 0; i--)
|
||||
{ // Loop over each bit
|
||||
if ((crc & 0x0001) != 0)
|
||||
{ // If the LSB is set
|
||||
crc >>= 1; // Shift right and XOR 0xA001
|
||||
crc ^= 0xA001;
|
||||
}
|
||||
ESP_LOGI(TAG, "Reconnected to client");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_client = NimBLEDevice::getDisconnectedClient();
|
||||
else // Else LSB is not set
|
||||
crc >>= 1; // Just shift right
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void ANTBms::notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)
|
||||
uint8_t *BmsBluetoothInst::buildReadBmsInst(uint8_t b, int i, uint8_t b2)
|
||||
{
|
||||
ESP_LOGI(TAG, "Received %s: %s (%.*s)", isNotify ? "notification" : "indication", bmsutils::bytesToHex(pData, length).c_str(), length, pData);
|
||||
auto *bArr = new uint8_t[64];
|
||||
bArr[0] = PROTOCOL_FRAME_HEAD;
|
||||
bArr[1] = PROTOCOL_ADD;
|
||||
bArr[2] = b;
|
||||
bArr[3] = (uint8_t) (i & 255);
|
||||
bArr[4] = (uint8_t) ((i >> 8) & 255);
|
||||
bArr[5] = b2;
|
||||
int crc16 = CRC16::calcCrc16(bArr + 1, 5);
|
||||
ESP_LOGI(TAG, "crc: %d", crc16);
|
||||
bArr[6] = (uint8_t) (crc16 >> 8);
|
||||
bArr[7] = (uint8_t) (crc16 & 255);
|
||||
bArr[8] = -86;
|
||||
bArr[9] = 85;
|
||||
return bArr;
|
||||
}
|
||||
|
||||
void ANTBms::requestData()
|
||||
uint8_t *BmsBluetoothInst::buildReadBmsInstWithData(uint8_t b, int i, uint8_t b2, uint8_t *bArr)
|
||||
{
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
if (!m_connected)
|
||||
return;
|
||||
|
||||
if (espchrono::ago(m_lastRequestTime) > 2000ms || m_newPacketReceived)
|
||||
auto *bArr2 = new uint8_t[64 + sizeof(bArr)];
|
||||
bArr2[0] = PROTOCOL_FRAME_HEAD;
|
||||
bArr2[1] = PROTOCOL_ADD;
|
||||
bArr2[2] = b;
|
||||
bArr2[3] = (uint8_t) (i & 255);
|
||||
bArr2[4] = (uint8_t) ((i >> 8) & 255);
|
||||
bArr2[5] = b2;
|
||||
int i2 = 5;
|
||||
for (int _i = 0; _i < sizeof(bArr); _i++)
|
||||
{
|
||||
m_lastRequestTime = espchrono::millis_clock::now();
|
||||
|
||||
if (m_toggle)
|
||||
{
|
||||
bmsGetInfo3();
|
||||
m_newPacketReceived = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bmsGetInfo4();
|
||||
m_newPacketReceived = false;
|
||||
}
|
||||
m_toggle = !m_toggle;
|
||||
i2++;
|
||||
bArr2[i2] = bArr[_i];
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
int i3 = i2 + 1;
|
||||
int crc16 = CRC16::calcCrc16(bArr2 + 1, (uint16_t) (sizeof(bArr) + 5));
|
||||
ESP_LOGI(TAG, "crc: %d", crc16);
|
||||
bArr2[i3] = (uint8_t) (crc16 >> 8);
|
||||
int i4 = i3 + 1;
|
||||
bArr2[i4] = (uint8_t) (crc16 & 255);
|
||||
int i5 = i4 + 1;
|
||||
bArr2[i5] = -86;
|
||||
int i6 = i5 + 1;
|
||||
bArr2[i6] = 85;
|
||||
return bArr2;
|
||||
}
|
||||
|
165
main/antbms.h
165
main/antbms.h
@ -1,126 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: jetbrains://idea/navigate/reference?project=bms_base_source_from_JADX&path=com/mayi/bms/bluetooth/BmsBluetoothManager.java
|
||||
|
||||
// system includes
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
// 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
|
||||
class CRC16
|
||||
{
|
||||
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();
|
||||
static int calcCrc16(const uint8_t *data, uint16_t len);
|
||||
};
|
||||
|
||||
class BmsBluetoothInst
|
||||
{
|
||||
public:
|
||||
static constexpr const char * const TAG = "BMSBluetoothInst";
|
||||
|
||||
static uint8_t *buildReadBmsInst(uint8_t b, int i, uint8_t b2);
|
||||
|
||||
static uint8_t *buildReadBmsInstWithData(uint8_t b, int i, uint8_t b2, uint8_t *bArr);
|
||||
|
||||
static constexpr uint8_t PROTOCOL_ADD = 0xA1;
|
||||
static constexpr uint8_t PROTOCOL_FRAME_HEAD = 0x7E;
|
||||
};
|
||||
|
||||
class BmsInstruction
|
||||
{
|
||||
private:
|
||||
const uint8_t functionCode;
|
||||
const uint8_t length;
|
||||
|
||||
int address{0};
|
||||
|
||||
uint8_t data[32]{};
|
||||
public:
|
||||
static constexpr const char * const TAG = "BMSInstruction";
|
||||
|
||||
BmsInstruction(uint8_t b, uint8_t b2);
|
||||
|
||||
void setData(uint8_t *data, uint8_t length);
|
||||
|
||||
[[nodiscard]] uint8_t getLength() const;
|
||||
|
||||
[[nodiscard]] uint8_t getFunctionCode() const;
|
||||
|
||||
[[nodiscard]] int getAddress() const;
|
||||
|
||||
void setAddress(int address);
|
||||
|
||||
uint8_t* getInstruction();
|
||||
|
||||
[[nodiscard]] std::string toString() const;
|
||||
};
|
||||
|
330
main/antbmsmanager.cpp
Normal file
330
main/antbmsmanager.cpp
Normal file
@ -0,0 +1,330 @@
|
||||
#include "antbmsmanager.h"
|
||||
|
||||
// local includes
|
||||
#include "antbms.h"
|
||||
#include "bmsutils.h"
|
||||
#include "newsettings.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
void ANTBmsManager::init()
|
||||
{
|
||||
// init code
|
||||
m_initialized = true;
|
||||
|
||||
// scan
|
||||
startScan();
|
||||
}
|
||||
|
||||
void ANTBmsManager::update()
|
||||
{
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
handleConnect();
|
||||
|
||||
if (m_client && (*m_client)->isConnected())
|
||||
{
|
||||
requestData();
|
||||
}
|
||||
}
|
||||
|
||||
void ANTBmsManager::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 ANTBmsManager::isInitialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
void ANTBmsManager::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 ANTBmsManager::clearScanResults()
|
||||
{
|
||||
m_scanResults.reset();
|
||||
}
|
||||
|
||||
bool ANTBmsManager::getScanStatus() const
|
||||
{
|
||||
return m_scanStarted;
|
||||
}
|
||||
|
||||
const std::optional<scanResults_t> &ANTBmsManager::getScanResults()
|
||||
{
|
||||
return m_scanResults;
|
||||
}
|
||||
|
||||
void ANTBmsManager::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(); // this sometimes crashes with StoreProhibited
|
||||
|
||||
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;
|
||||
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 ANTBmsManager::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 ANTBmsManager::requestData()
|
||||
{
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
if (!m_connected)
|
||||
return;
|
||||
|
||||
if (espchrono::ago(m_lastRequestTime) > 2000ms || m_newPacketReceived)
|
||||
{
|
||||
m_lastRequestTime = espchrono::millis_clock::now();
|
||||
|
||||
readBmsState();
|
||||
}
|
||||
}
|
||||
|
||||
void ANTBmsManager::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 ANTBmsManager::readBmsState()
|
||||
{
|
||||
BmsInstruction bmsInstruction = BmsInstruction(1, -66);
|
||||
ESP_LOGI(TAG, "read real status, inst: %s", bmsInstruction.toString().c_str());
|
||||
const auto inst = bmsInstruction.getInstruction();
|
||||
sendCommand(inst, sizeof(inst));
|
||||
}
|
||||
|
123
main/antbmsmanager.h
Normal file
123
main/antbmsmanager.h
Normal file
@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
// 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 ANTBmsManager
|
||||
{
|
||||
public:
|
||||
static constexpr const char * const TAG = "ANTBMS-MANAGER";
|
||||
|
||||
// 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);
|
||||
|
||||
void readBmsState();
|
||||
private:
|
||||
|
||||
class ScanResultsCallback : public NimBLEScanCallbacks
|
||||
{
|
||||
public:
|
||||
explicit ScanResultsCallback(ANTBmsManager* 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:
|
||||
ANTBmsManager* m_antBms;
|
||||
};
|
||||
|
||||
class ClientCallbacks : public NimBLEClientCallbacks
|
||||
{
|
||||
public:
|
||||
explicit ClientCallbacks(ANTBmsManager* 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:
|
||||
ANTBmsManager* 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;
|
||||
};
|
@ -11,7 +11,7 @@
|
||||
|
||||
namespace bmsutils {
|
||||
|
||||
ANTBms antBms;
|
||||
ANTBmsManager antBms;
|
||||
|
||||
void init() {}
|
||||
|
||||
@ -24,7 +24,7 @@ void update()
|
||||
ESP_LOGI("bmsutils", "initializing bms");
|
||||
antBms.init();
|
||||
}
|
||||
else if (!configs.bmsEnabled.value() && initialized)
|
||||
else if ((!configs.bmsEnabled.value() || !configs.bleSettings.bleEnabled.value()) && initialized)
|
||||
{
|
||||
ESP_LOGI("bmsutils", "deinitializing bms");
|
||||
antBms.deinit();
|
||||
|
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
// local includes
|
||||
#include "antbms.h"
|
||||
#include "antbmsmanager.h"
|
||||
|
||||
namespace bmsutils {
|
||||
extern ANTBms antBms;
|
||||
extern ANTBmsManager antBms;
|
||||
|
||||
void init();
|
||||
|
||||
|
Reference in New Issue
Block a user