Current WIP state, protocol sadly not working
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
set(BOBBY_HEADERS
|
set(BOBBY_HEADERS
|
||||||
accessorhelpers.h
|
accessorhelpers.h
|
||||||
antbms.h
|
antbms.h
|
||||||
|
antbmsmanager.h
|
||||||
accessors/globalaccessors.h
|
accessors/globalaccessors.h
|
||||||
accessors/settingsaccessors.h
|
accessors/settingsaccessors.h
|
||||||
accessors/wifiaccessors.h
|
accessors/wifiaccessors.h
|
||||||
@@ -259,6 +260,7 @@ set(BOBBY_HEADERS
|
|||||||
|
|
||||||
set(BOBBY_SOURCES
|
set(BOBBY_SOURCES
|
||||||
antbms.cpp
|
antbms.cpp
|
||||||
|
antbmsmanager.cpp
|
||||||
accessors/wifistaconfigaccessors.cpp
|
accessors/wifistaconfigaccessors.cpp
|
||||||
actions/assertaction.cpp
|
actions/assertaction.cpp
|
||||||
actions/bmsclearscanaction.cpp
|
actions/bmsclearscanaction.cpp
|
||||||
|
414
main/antbms.cpp
414
main/antbms.cpp
@@ -1,350 +1,108 @@
|
|||||||
#include "antbms.h"
|
#include "antbms.h"
|
||||||
|
|
||||||
// local includes
|
// esp-idf includes
|
||||||
#include "bmsutils.h"
|
#include <esp_log.h>
|
||||||
#include "newsettings.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
|
std::copy(_data, _data + _length, this->data);
|
||||||
m_initialized = true;
|
|
||||||
|
|
||||||
// scan
|
|
||||||
startScan();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ANTBms::update()
|
int BmsInstruction::getAddress() const
|
||||||
{
|
{
|
||||||
if (!m_initialized)
|
return address;
|
||||||
return;
|
}
|
||||||
|
|
||||||
handleConnect();
|
void BmsInstruction::setAddress(int _address)
|
||||||
|
|
||||||
if (m_client && (*m_client)->isConnected())
|
|
||||||
{
|
{
|
||||||
requestData();
|
address = _address;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *BmsInstruction::getInstruction()
|
||||||
|
{
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
return BmsBluetoothInst::buildReadBmsInst(functionCode, address, 0);
|
||||||
|
}
|
||||||
|
return BmsBluetoothInst::buildReadBmsInstWithData(this->functionCode, this->address, this->length, this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BmsInstruction::toString() const
|
||||||
|
{
|
||||||
|
return "BmsInstruction{functionCode=" + std::to_string(functionCode) + ", address=" + std::to_string(address) + ", inst = " + ", data = " + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
int CRC16::calcCrc16(const uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
// calculate crc16
|
||||||
|
uint16_t crc = 0xFFFF;
|
||||||
|
|
||||||
|
for (int pos = 0; pos < len; pos++)
|
||||||
|
{
|
||||||
|
crc ^= (uint16_t) data[pos]; // XOR byte into least sig. byte of crc
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
else // Else LSB is not set
|
||||||
|
crc >>= 1; // Just shift right
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ANTBms::deinit()
|
return crc;
|
||||||
{
|
|
||||||
// deinit code
|
|
||||||
m_initialized = false;
|
|
||||||
|
|
||||||
if (m_client)
|
|
||||||
{
|
|
||||||
(*m_client)->disconnect();
|
|
||||||
m_client.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_scanResults)
|
uint8_t *BmsBluetoothInst::buildReadBmsInst(uint8_t b, int i, uint8_t b2)
|
||||||
{
|
{
|
||||||
m_scanResults.reset();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_service)
|
uint8_t *BmsBluetoothInst::buildReadBmsInstWithData(uint8_t b, int i, uint8_t b2, uint8_t *bArr)
|
||||||
{
|
{
|
||||||
m_service.reset();
|
auto *bArr2 = new uint8_t[64 + sizeof(bArr)];
|
||||||
}
|
bArr2[0] = PROTOCOL_FRAME_HEAD;
|
||||||
|
bArr2[1] = PROTOCOL_ADD;
|
||||||
if (m_rxCharacteristic)
|
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_rxCharacteristic.reset();
|
i2++;
|
||||||
|
bArr2[i2] = bArr[_i];
|
||||||
}
|
}
|
||||||
|
int i3 = i2 + 1;
|
||||||
if (m_txCharacteristic)
|
int crc16 = CRC16::calcCrc16(bArr2 + 1, (uint16_t) (sizeof(bArr) + 5));
|
||||||
{
|
ESP_LOGI(TAG, "crc: %d", crc16);
|
||||||
m_txCharacteristic.reset();
|
bArr2[i3] = (uint8_t) (crc16 >> 8);
|
||||||
}
|
int i4 = i3 + 1;
|
||||||
|
bArr2[i4] = (uint8_t) (crc16 & 255);
|
||||||
m_scanStarted = false;
|
int i5 = i4 + 1;
|
||||||
m_initialized = false;
|
bArr2[i5] = -86;
|
||||||
}
|
int i6 = i5 + 1;
|
||||||
|
bArr2[i6] = 85;
|
||||||
bool ANTBms::isInitialized() const
|
return bArr2;
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
143
main/antbms.h
143
main/antbms.h
@@ -1,126 +1,53 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// TODO: jetbrains://idea/navigate/reference?project=bms_base_source_from_JADX&path=com/mayi/bms/bluetooth/BmsBluetoothManager.java
|
|
||||||
|
|
||||||
// system includes
|
// system includes
|
||||||
#include <optional>
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
// esp-idf includes
|
class CRC16
|
||||||
#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:
|
public:
|
||||||
static constexpr const char * const TAG = "ANTBMS";
|
static int calcCrc16(const uint8_t *data, uint16_t len);
|
||||||
|
};
|
||||||
|
|
||||||
// basic functions
|
class BmsBluetoothInst
|
||||||
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:
|
public:
|
||||||
explicit ScanResultsCallback(ANTBms* antBms) : m_antBms{antBms} {}
|
static constexpr const char * const TAG = "BMSBluetoothInst";
|
||||||
|
|
||||||
void onScanEnd(NimBLEScanResults scanResults) override
|
static uint8_t *buildReadBmsInst(uint8_t b, int i, uint8_t b2);
|
||||||
{
|
|
||||||
m_antBms->m_scanResults.reset();
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "BLE Scan complete");
|
static uint8_t *buildReadBmsInstWithData(uint8_t b, int i, uint8_t b2, uint8_t *bArr);
|
||||||
|
|
||||||
scanResults_t results;
|
static constexpr uint8_t PROTOCOL_ADD = 0xA1;
|
||||||
|
static constexpr uint8_t PROTOCOL_FRAME_HEAD = 0x7E;
|
||||||
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
|
class BmsInstruction
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
const uint8_t functionCode;
|
||||||
|
const uint8_t length;
|
||||||
|
|
||||||
|
int address{0};
|
||||||
|
|
||||||
|
uint8_t data[32]{};
|
||||||
public:
|
public:
|
||||||
explicit ClientCallbacks(ANTBms* antBms) : m_antBms{antBms} {}
|
static constexpr const char * const TAG = "BMSInstruction";
|
||||||
void onConnect(NimBLEClient* pClient) override
|
|
||||||
{
|
|
||||||
m_antBms->m_connected = true;
|
|
||||||
ESP_LOGD(TAG, "Connected to server");
|
|
||||||
}
|
|
||||||
|
|
||||||
void onDisconnect(NimBLEClient* pClient, int reason) override
|
BmsInstruction(uint8_t b, uint8_t b2);
|
||||||
{
|
|
||||||
m_antBms->m_connected = false;
|
void setData(uint8_t *data, uint8_t length);
|
||||||
ESP_LOGI(TAG, "Disconnected from server (%d)", reason);
|
|
||||||
}
|
[[nodiscard]] uint8_t getLength() const;
|
||||||
private:
|
|
||||||
ANTBms* m_antBms;
|
[[nodiscard]] uint8_t getFunctionCode() const;
|
||||||
};
|
|
||||||
|
[[nodiscard]] int getAddress() const;
|
||||||
bool m_initialized{false};
|
|
||||||
bool m_scanStarted{false};
|
void setAddress(int address);
|
||||||
bool m_connected{false};
|
|
||||||
|
uint8_t* getInstruction();
|
||||||
bool m_newPacketReceived{false};
|
|
||||||
bool m_toggle{false};
|
[[nodiscard]] std::string toString() const;
|
||||||
|
|
||||||
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();
|
|
||||||
};
|
};
|
||||||
|
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 {
|
namespace bmsutils {
|
||||||
|
|
||||||
ANTBms antBms;
|
ANTBmsManager antBms;
|
||||||
|
|
||||||
void init() {}
|
void init() {}
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ void update()
|
|||||||
ESP_LOGI("bmsutils", "initializing bms");
|
ESP_LOGI("bmsutils", "initializing bms");
|
||||||
antBms.init();
|
antBms.init();
|
||||||
}
|
}
|
||||||
else if (!configs.bmsEnabled.value() && initialized)
|
else if ((!configs.bmsEnabled.value() || !configs.bleSettings.bleEnabled.value()) && initialized)
|
||||||
{
|
{
|
||||||
ESP_LOGI("bmsutils", "deinitializing bms");
|
ESP_LOGI("bmsutils", "deinitializing bms");
|
||||||
antBms.deinit();
|
antBms.deinit();
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
#include "antbms.h"
|
#include "antbmsmanager.h"
|
||||||
|
|
||||||
namespace bmsutils {
|
namespace bmsutils {
|
||||||
extern ANTBms antBms;
|
extern ANTBmsManager antBms;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user