ESPHome 2025.12.1
Loading...
Searching...
No Matches
ble_sensor.cpp
Go to the documentation of this file.
1#include "ble_sensor.h"
4#include "esphome/core/log.h"
6
7#ifdef USE_ESP32
8
9namespace esphome::ble_client {
10
11static const char *const TAG = "ble_sensor";
12
14 // Parent BLEClientNode has a loop() method, but this component uses
15 // polling via update() and BLE callbacks so loop isn't needed
16 this->disable_loop();
17}
18
20 LOG_SENSOR("", "BLE Sensor", this);
21 ESP_LOGCONFIG(TAG,
22 " MAC address : %s\n"
23 " Service UUID : %s\n"
24 " Characteristic UUID: %s\n"
25 " Descriptor UUID : %s\n"
26 " Notifications : %s",
27 this->parent()->address_str(), this->service_uuid_.to_string().c_str(),
28 this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_));
29 LOG_UPDATE_INTERVAL(this);
30}
31
32void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
33 esp_ble_gattc_cb_param_t *param) {
34 switch (event) {
35 case ESP_GATTC_OPEN_EVT: {
36 if (param->open.status == ESP_GATT_OK) {
37 ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str());
38 break;
39 }
40 break;
41 }
42 case ESP_GATTC_CLOSE_EVT: {
43 ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str());
44 this->status_set_warning();
45 this->publish_state(NAN);
46 break;
47 }
48 case ESP_GATTC_SEARCH_CMPL_EVT: {
49 this->handle = 0;
50 auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
51 if (chr == nullptr) {
52 this->status_set_warning();
53 this->publish_state(NAN);
54 ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", this->service_uuid_.to_string().c_str(),
55 this->char_uuid_.to_string().c_str());
56 break;
57 }
58 this->handle = chr->handle;
59 if (this->descr_uuid_.get_uuid().len > 0) {
60 auto *descr = chr->get_descriptor(this->descr_uuid_);
61 if (descr == nullptr) {
62 this->status_set_warning();
63 this->publish_state(NAN);
64 ESP_LOGW(TAG, "No sensor descriptor found at service %s char %s descr %s",
65 this->service_uuid_.to_string().c_str(), this->char_uuid_.to_string().c_str(),
66 this->descr_uuid_.to_string().c_str());
67 break;
68 }
69 this->handle = descr->handle;
70 }
71 if (this->notify_) {
72 auto status = esp_ble_gattc_register_for_notify(this->parent()->get_gattc_if(),
73 this->parent()->get_remote_bda(), chr->handle);
74 if (status) {
75 ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
76 }
77 } else {
78 this->node_state = espbt::ClientState::ESTABLISHED;
79 // For non-notify characteristics, trigger an immediate read after service discovery
80 // to avoid peripherals disconnecting due to inactivity
81 this->update();
82 }
83 break;
84 }
85 case ESP_GATTC_READ_CHAR_EVT: {
86 if (param->read.status != ESP_GATT_OK) {
87 ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
88 break;
89 }
90 if (param->read.handle == this->handle) {
92 this->publish_state(this->parse_data_(param->read.value, param->read.value_len));
93 }
94 break;
95 }
96 case ESP_GATTC_NOTIFY_EVT: {
97 ESP_LOGD(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
98 param->notify.handle, param->notify.value[0]);
99 if (param->notify.handle != this->handle)
100 break;
101 this->publish_state(this->parse_data_(param->notify.value, param->notify.value_len));
102 break;
103 }
104 case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
105 if (param->reg_for_notify.handle == this->handle) {
106 if (param->reg_for_notify.status != ESP_GATT_OK) {
107 ESP_LOGW(TAG, "Error registering for notifications at handle %d, status=%d", param->reg_for_notify.handle,
108 param->reg_for_notify.status);
109 break;
110 }
111 this->node_state = espbt::ClientState::ESTABLISHED;
112 ESP_LOGD(TAG, "Register for notify on %s complete", this->char_uuid_.to_string().c_str());
113 }
114 break;
115 }
116 default:
117 break;
118 }
119}
120
121float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) {
122 if (this->has_data_to_value_) {
123 std::vector<uint8_t> data(value, value + value_len);
124 return this->data_to_value_func_(data);
125 } else {
126 return value[0];
127 }
128}
129
131 if (this->node_state != espbt::ClientState::ESTABLISHED) {
132 ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
133 return;
134 }
135 if (this->handle == 0) {
136 ESP_LOGW(TAG, "[%s] Cannot poll, no service or characteristic found", this->get_name().c_str());
137 return;
138 }
139
140 auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle,
141 ESP_GATT_AUTH_REQ_NONE);
142 if (status) {
143 this->status_set_warning();
144 this->publish_state(NAN);
145 ESP_LOGW(TAG, "[%s] Error sending read request for sensor, status=%d", this->get_name().c_str(), status);
146 }
147}
148
149} // namespace esphome::ble_client
150#endif
uint8_t status
Definition bl0942.h:8
void status_set_warning(const char *message=nullptr)
void disable_loop()
Disable this component's loop.
void status_clear_warning()
const StringRef & get_name() const
espbt::ESPBTUUID service_uuid_
Definition ble_sensor.h:45
float(* data_to_value_func_)(const std::vector< uint8_t > &)
Definition ble_sensor.h:43
float parse_data_(uint8_t *value, uint16_t value_len)
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
espbt::ESPBTUUID descr_uuid_
Definition ble_sensor.h:47
espbt::ESPBTUUID char_uuid_
Definition ble_sensor.h:46
std::string to_string() const
Definition ble_uuid.cpp:146
esp_bt_uuid_t get_uuid() const
Definition ble_uuid.cpp:145
BLECharacteristic * get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr)
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:77