Active BLE connection v3 (#4113)

Co-authored-by: Daniel Cousens <413395+dcousens@users.noreply.github.com>
Co-authored-by: Maurice Makaay <account+github@makaay.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
J. Nick Koston 2022-11-29 08:53:32 -10:00 committed by GitHub
parent ccef7c322f
commit 8414bb9a7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 79 additions and 1 deletions

View File

@ -1150,6 +1150,8 @@ enum BluetoothDeviceRequestType {
BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1; BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1;
BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2; BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2;
BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3; BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3;
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4;
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5;
} }
message BluetoothDeviceRequest { message BluetoothDeviceRequest {

View File

@ -950,7 +950,7 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
resp.webserver_port = USE_WEBSERVER_PORT; resp.webserver_port = USE_WEBSERVER_PORT;
#endif #endif
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 2 : 1; resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 3 : 1;
#endif #endif
return resp; return resp;
} }

View File

@ -6,6 +6,7 @@
namespace esphome { namespace esphome {
namespace api { namespace api {
#ifdef HAS_PROTO_MESSAGE_DUMP
template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) { template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) {
switch (value) { switch (value) {
case enums::ENTITY_CATEGORY_NONE: case enums::ENTITY_CATEGORY_NONE:
@ -351,10 +352,15 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
return "BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR"; return "BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR";
case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR: case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR:
return "BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR"; return "BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR";
case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE:
return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE";
case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE:
return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
} }
#endif
bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) { switch (field_id) {
case 2: { case 2: {

View File

@ -160,6 +160,8 @@ enum BluetoothDeviceRequestType : uint32_t {
BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1, BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1,
BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2, BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2,
BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3, BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3,
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4,
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
}; };
} // namespace enums } // namespace enums

View File

@ -34,6 +34,10 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
this->set_address(0); this->set_address(0);
api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(),
this->proxy_->get_bluetooth_connections_limit()); this->proxy_->get_bluetooth_connections_limit());
} else if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_);
api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(),
this->proxy_->get_bluetooth_connections_limit());
} }
this->seen_mtu_or_services_ = false; this->seen_mtu_or_services_ = false;
break; break;

View File

@ -80,6 +80,10 @@ void BluetoothProxy::loop() {
if (connection->send_service_ == connection->services_.size()) { if (connection->send_service_ == connection->services_.size()) {
connection->send_service_ = -1; connection->send_service_ = -1;
api::global_api_server->send_bluetooth_gatt_services_done(connection->get_address()); api::global_api_server->send_bluetooth_gatt_services_done(connection->get_address());
if (connection->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
connection->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
connection->release_services();
}
} else if (connection->send_service_ >= 0) { } else if (connection->send_service_ >= 0) {
auto &service = connection->services_[connection->send_service_]; auto &service = connection->services_[connection->send_service_];
api::BluetoothGATTGetServicesResponse resp; api::BluetoothGATTGetServicesResponse resp;
@ -171,6 +175,8 @@ BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool rese
void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) { void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) {
switch (msg.request_type) { switch (msg.request_type) {
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE:
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE:
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: { case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: {
auto *connection = this->get_connection_(msg.address, true); auto *connection = this->get_connection_(msg.address, true);
if (connection == nullptr) { if (connection == nullptr) {
@ -186,11 +192,43 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(),
this->get_bluetooth_connections_limit()); this->get_bluetooth_connections_limit());
return; return;
} else if (connection->state() == espbt::ClientState::SEARCHING) {
ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, already searching for device",
connection->get_connection_index(), connection->address_str().c_str());
return;
} else if (connection->state() == espbt::ClientState::DISCOVERED) {
ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, device already discovered",
connection->get_connection_index(), connection->address_str().c_str());
return;
} else if (connection->state() == espbt::ClientState::READY_TO_CONNECT) {
ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, waiting in line to connect",
connection->get_connection_index(), connection->address_str().c_str());
return;
} else if (connection->state() == espbt::ClientState::CONNECTING) {
ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, already connecting", connection->get_connection_index(),
connection->address_str().c_str());
return;
} else if (connection->state() == espbt::ClientState::DISCONNECTING) {
ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, device is disconnecting",
connection->get_connection_index(), connection->address_str().c_str());
return;
} else if (connection->state() != espbt::ClientState::INIT) { } else if (connection->state() != espbt::ClientState::INIT) {
ESP_LOGW(TAG, "[%d] [%s] Connection already in progress", connection->get_connection_index(), ESP_LOGW(TAG, "[%d] [%s] Connection already in progress", connection->get_connection_index(),
connection->address_str().c_str()); connection->address_str().c_str());
return; return;
} }
if (msg.request_type == api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE) {
connection->set_connection_type(espbt::ConnectionType::V3_WITH_CACHE);
ESP_LOGI(TAG, "[%d] [%s] Connecting v3 with cache", connection->get_connection_index(),
connection->address_str().c_str());
} else if (msg.request_type == api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE) {
connection->set_connection_type(espbt::ConnectionType::V3_WITHOUT_CACHE);
ESP_LOGI(TAG, "[%d] [%s] Connecting v3 without cache", connection->get_connection_index(),
connection->address_str().c_str());
} else {
connection->set_connection_type(espbt::ConnectionType::V1);
ESP_LOGI(TAG, "[%d] [%s] Connecting v1", connection->get_connection_index(), connection->address_str().c_str());
}
if (msg.has_address_type) { if (msg.has_address_type) {
connection->remote_bda_[0] = (msg.address >> 40) & 0xFF; connection->remote_bda_[0] = (msg.address >> 40) & 0xFF;
connection->remote_bda_[1] = (msg.address >> 32) & 0xFF; connection->remote_bda_[1] = (msg.address >> 32) & 0xFF;

View File

@ -129,6 +129,11 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
this->set_state(espbt::ClientState::IDLE); this->set_state(espbt::ClientState::IDLE);
break; break;
} }
if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
this->set_state(espbt::ClientState::CONNECTED);
this->state_ = espbt::ClientState::ESTABLISHED;
break;
}
auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->open.conn_id); auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->open.conn_id);
if (ret) { if (ret) {
ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_send_mtu_req failed, status=%x", this->connection_index_, ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_send_mtu_req failed, status=%x", this->connection_index_,
@ -180,6 +185,12 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
break; break;
} }
case ESP_GATTC_REG_FOR_NOTIFY_EVT: { case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
// Client is responsible for flipping the descriptor value
// when using the cache
break;
}
esp_gattc_descr_elem_t desc_result; esp_gattc_descr_elem_t desc_result;
uint16_t count = 1; uint16_t count = 1;
esp_gatt_status_t descr_status = esp_gatt_status_t descr_status =

View File

@ -74,6 +74,8 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
uint8_t get_connection_index() const { return this->connection_index_; } uint8_t get_connection_index() const { return this->connection_index_; }
virtual void set_connection_type(espbt::ConnectionType ct) { this->connection_type_ = ct; }
protected: protected:
int gattc_if_; int gattc_if_;
esp_bd_addr_t remote_bda_; esp_bd_addr_t remote_bda_;
@ -83,6 +85,7 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
std::string address_str_{}; std::string address_str_{};
uint8_t connection_index_; uint8_t connection_index_;
uint16_t mtu_{23}; uint16_t mtu_{23};
espbt::ConnectionType connection_type_{espbt::ConnectionType::V1};
std::vector<BLEService *> services_; std::vector<BLEService *> services_;
}; };

View File

@ -165,6 +165,18 @@ enum class ClientState {
ESTABLISHED, ESTABLISHED,
}; };
enum class ConnectionType {
// The default connection type, we hold all the services in ram
// for the duration of the connection.
V1,
// The client has a cache of the services and mtu so we should not
// fetch them again
V3_WITH_CACHE,
// The client does not need the services and mtu once we send them
// so we should wipe them from memory as soon as we send them
V3_WITHOUT_CACHE
};
class ESPBTClient : public ESPBTDeviceListener { class ESPBTClient : public ESPBTDeviceListener {
public: public:
virtual bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, virtual bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,