ESPBMS/BLE.ino
2024-04-23 20:03:35 +01:00

359 lines
10 KiB
C++

BLEScan* pBLEScan = nullptr;
BLEClient* pClient = nullptr;
BLEAdvertisedDevice* pRemoteDevice = nullptr;
BLERemoteService* pRemoteService = nullptr;
BLERemoteCharacteristic* pRemoteCharacteristic_rx = nullptr;
BLERemoteCharacteristic* pRemoteCharacteristic_tx = nullptr;
// 0000ff01-0000-1000-8000-00805f9b34fb
// Notifications from this characteristic is received data from BMS
// NOTIFY, READ
// 0000ff02-0000-1000-8000-00805f9b34fb
// Write this characteristic to send data to BMS
// READ, WRITE, WRITE NO RESPONSE
boolean doScan = false; // becomes true when BLE is initialized and scanning is allowed
boolean doConnect = false; // becomes true when correct ID is found during scanning
boolean ble_client_connected = false; // true when fully connected
unsigned int ble_packets_requested = 0b00; // keeps track of requested packets
unsigned int ble_packets_received = 0b00; // keeps track of received packets
// ======= CALLBACKS =========
void MyEndOfScanCallback(BLEScanResults pBLEScanResult){
bms_status=false; // BMS not found
if(BLE_CALLBACK_DEBUG){
debug("BLE: scan finished");
Serial.println("Scan finished.");
}
}
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks{
// called for each advertising BLE server
void onResult(BLEAdvertisedDevice advertisedDevice){
// found a device
if(BLE_CALLBACK_DEBUG){
debug(
String("BLE: found ") +
String(advertisedDevice.getName().c_str()) +
String(" with address ") +
String(advertisedDevice.getAddress().toString().c_str()) +
String(" and RSSI ") +
String(advertisedDevice.getRSSI())
);
Serial.print("BLE: found ");
Serial.println(advertisedDevice.toString().c_str());
}
// Check if found device is the one we are looking for
if(
strcmp(advertisedDevice.getName().c_str(), BLE_NAME)==0 &&
strcmp(advertisedDevice.getAddress().toString().c_str(), BLE_ADDRESS)==0 &&
advertisedDevice.haveServiceUUID() &&
advertisedDevice.isAdvertisingService(serviceUUID)
){
if(BLE_CALLBACK_DEBUG){
debug("BLE: target device found");
}
pBLEScan->stop();
if(advertisedDevice.getRSSI() >= BLE_MIN_RSSI){
// delete old remote device, create new one
if(pRemoteDevice != nullptr){
delete pRemoteDevice;
}
pRemoteDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
}else{
if(BLE_CALLBACK_DEBUG){
debug("BLE: RSSI of target device below minimum");
}
}
}
}
};
class MyClientCallback : public BLEClientCallbacks{
// called on connect/disconnect
void onConnect(BLEClient* pclient){
if(BLE_CALLBACK_DEBUG){
debug(String("BLE: connecting to ") + String(pclient->getPeerAddress().toString().c_str()));
}
}
void onDisconnect(BLEClient* pclient){
ble_client_connected = false;
doConnect = false;
if(BLE_CALLBACK_DEBUG){
debug(String("BLE: disconnected from ") + String(pclient->getPeerAddress().toString().c_str()));
}
}
};
static void MyNotifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify){
//this is called when BLE server sents data via notification
//hexDump((char*)pData, length);
if(!bleCollectPacket((char *)pData, length)){
debug("ERROR: packet could not be collected.");
}
}
// ======= OTHERS =========
void handleBLE(){
static unsigned long prev_millis_standby = 0;
prev_millis_standby = millis();
while(true){ // loop until we hit a timeout or gathered all packets
if((ble_packets_received == BLE_PACKETSRECEIVED_BEFORE_STANDBY) || (millis()>prev_millis_standby+BLE_TIMEOUT)){
if(ble_packets_received == BLE_PACKETSRECEIVED_BEFORE_STANDBY){
debug("BLE: all packets received");
bms_status=true; // BMS was connected, data up-to-date
}else{
debug("BLE: connection timeout");
bms_status=false; // BMS not (fully) connected
}
break; // we're done with BLE, exit while loop
}
else if (doConnect){
// found the desired BLE server, now connect to it
if (connectToServer()){
ble_client_connected = true;
ble_packets_received=0;
ble_packets_requested=0;
}else{
ble_client_connected = false;
debug("BLE: failed to connect");
}
doConnect = false;
}
if (ble_client_connected){
// if connected to BLE server, request all data
if((ble_packets_requested & 0b01)!=0b01){
// request packet 0b01
debug("BLE: requesting packet 0b01");
delay(50);
if(bmsRequestBasicInfo()){
ble_packets_requested |= 0b01;
}
}else if(((ble_packets_received & 0b01)==0b01) && ((ble_packets_requested & 0b10)!=0b10)){
// request packet 0b10 after 0b01 has been received
debug("BLE: requesting packet 0b10");
delay(50);
if(bmsRequestCellInfo()){
ble_packets_requested |= 0b10;
}
}
}else if ((!doConnect)&&(doScan)){
// we are not connected, so we can scan for devices
debug("BLE: not connected, starting scan");
Serial.print("BLE is not connected, starting scan");
// Disconnect client
if((pClient != nullptr)&&(pClient->isConnected())){
pClient->disconnect();
}
// stop scan (if running) and start a new one
pBLEScan->setActiveScan(true);
pBLEScan->setInterval(1 << 8); // 160 ms
pBLEScan->setWindow(1 << 7); // 80 ms
pBLEScan->start(BLE_SCAN_DURATION, MyEndOfScanCallback, false); // non-blocking, use a callback
doScan=false;
debug("BLE: scan started");
}
}
}
void bleGatherPackets(){
bleStart();
handleBLE();
blePause();
BLEDevice::deinit(false);
}
void bleStart(){
Serial.print("Starting BLE... ");
BLEDevice::init("");
//esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); // release some unused memory
// Retrieve a BLE client
pClient = BLEDevice::createClient();
pClient->setClientCallbacks(new MyClientCallback());
// Retrieve a BLE scanner
pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
bleContinue();
Serial.println("done");
}
void blePause(){
// stop scanning and disconnect from all devices
doScan=false;
// Disconnect client
if((pClient != nullptr)&&(pClient->isConnected())){
pClient->disconnect();
}
delay(50);
pBLEScan->stop();
ble_client_connected=false;
doConnect=false;
ble_packets_received=0;
ble_packets_requested=0;
}
void bleContinue(){
// Prepare for scanning
ble_client_connected=false;
doConnect=false;
ble_packets_received=0;
doScan=true; // start scanning for new devices
}
bool connectToServer(){
if(pRemoteDevice==nullptr){
Serial.println("Invalid remote device, can't connect");
return false;
}
// Disconnect client
if((pClient != nullptr)&&(pClient->isConnected())){
pClient->disconnect();
}
Serial.print("Forming a connection to ");
Serial.println(pRemoteDevice->getAddress().toString().c_str());
delay(100);
// Connect to the remote BLE Server.
pClient->connect(pRemoteDevice);
if(!(pClient->isConnected())){
debug(String("BLE: failed to connect"));
Serial.println("Failed to connect to server");
pClient->disconnect();
return false;
}
Serial.println(" - Connected to server");
// Get remote service
pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr){
debug(String("BLE: failed to find service UUID"));
Serial.print("Failed to find our service UUID: ");
Serial.println(serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our service");
// Get BMS receive characteristic
pRemoteCharacteristic_rx = pRemoteService->getCharacteristic(charUUID_rx);
if (pRemoteCharacteristic_rx == nullptr){
debug(String("BLE: failed to find RX UUID"));
Serial.print("Failed to find rx UUID: ");
Serial.println(charUUID_rx.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found RX characteristic");
// Register callback for remote characteristic (receive channel)
if (pRemoteCharacteristic_rx->canNotify()){
pRemoteCharacteristic_rx->registerForNotify(MyNotifyCallback);
}else{
debug(String("BLE: failed to register notification of remote characteristic"));
Serial.println("Failed to register notification of remote characteristic");
pClient->disconnect();
return false;
}
Serial.println(" - Registered remote characteristic for notification");
// Get BMS transmit characteristic
pRemoteCharacteristic_tx = pRemoteService->getCharacteristic(charUUID_tx);
if (pRemoteCharacteristic_tx == nullptr){
debug(String("BLE: failed to find TX UUID"));
Serial.print("Failed to find tx UUID: ");
Serial.println(charUUID_tx.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found TX characteristic");
// Check whether tx is writeable
if (!(pRemoteCharacteristic_tx->canWriteNoResponse())){
debug(String("BLE: failed TX remote characteristic is not writable"));
Serial.println("Failed TX remote characteristic is not writable");
pClient->disconnect();
return false;
}
Serial.println(" - TX is writable");
delay(BLE_REQUEST_DELAY); // wait, otherwise writeValue doesn't work for some reason
// to do: fix this ugly hack
debug(String("BLE: connected"));
return true;
}
bool sendCommand(uint8_t *data, uint32_t dataLen){
if((pClient!=nullptr)&&(pClient->isConnected())){
pRemoteCharacteristic_tx->writeValue(data, dataLen, false);
return true;
}else{
return false;
}
}