ESPHome 2026.10.0-dev
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 char service_buf[esp32_ble::UUID_STR_LEN];
22 char char_buf[esp32_ble::UUID_STR_LEN];
23 char descr_buf[esp32_ble::UUID_STR_LEN];
24 ESP_LOGCONFIG(TAG,
25 " MAC address : %s\n"
26 " Service UUID : %s\n"
27 " Characteristic UUID: %s\n"
28 " Descriptor UUID : %s\n"
29 " Notifications : %s",
30 this->parent()->address_str(), this->service_uuid_.to_str(service_buf),
31 this->char_uuid_.to_str(char_buf), this->descr_uuid_.to_str(descr_buf), YESNO(this->notify_));
32 LOG_UPDATE_INTERVAL(this);
33}
34
35void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
36 esp_ble_gattc_cb_param_t *param) {
37 switch (event) {
38 case ESP_GATTC_OPEN_EVT: {
39 if (param->open.status == ESP_GATT_OK) {
40 ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str());
41 break;
42 }
43 break;
44 }
45 case ESP_GATTC_CLOSE_EVT: {
46 ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str());
47 this->status_set_warning();
48 this->publish_state(NAN);
49 break;
50 }
51 case ESP_GATTC_SEARCH_CMPL_EVT: {
52 this->handle = 0;
53 auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
54 if (chr == nullptr) {
55 this->status_set_warning();
56 this->publish_state(NAN);
57 char service_buf[esp32_ble::UUID_STR_LEN];
58 char char_buf[esp32_ble::UUID_STR_LEN];
59 ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", this->service_uuid_.to_str(service_buf),
60 this->char_uuid_.to_str(char_buf));
61 break;
62 }
63 this->handle = chr->handle;
64 if (this->descr_uuid_.is_set()) {
65 auto *descr = chr->get_descriptor(this->descr_uuid_);
66 if (descr == nullptr) {
67 this->status_set_warning();
68 this->publish_state(NAN);
69 char service_buf[esp32_ble::UUID_STR_LEN];
70 char char_buf[esp32_ble::UUID_STR_LEN];
71 char descr_buf[esp32_ble::UUID_STR_LEN];
72 ESP_LOGW(TAG, "No sensor descriptor found at service %s char %s descr %s",
73 this->service_uuid_.to_str(service_buf), this->char_uuid_.to_str(char_buf),
74 this->descr_uuid_.to_str(descr_buf));
75 break;
76 }
77 this->handle = descr->handle;
78 }
79 if (this->notify_) {
80 auto status = this->parent()->register_for_notify(chr->handle);
81 if (status) {
82 ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
83 }
84 } else {
85 this->node_state = espbt::ClientState::ESTABLISHED;
86 // For non-notify characteristics, trigger an immediate read after service discovery
87 // to avoid peripherals disconnecting due to inactivity
88 this->update();
89 }
90 break;
91 }
92 case ESP_GATTC_READ_CHAR_EVT: {
93 if (param->read.status != ESP_GATT_OK) {
94 ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
95 break;
96 }
97 if (param->read.handle == this->handle) {
99 this->publish_state(this->parse_data_(param->read.value, param->read.value_len));
100 }
101 break;
102 }
103 case ESP_GATTC_NOTIFY_EVT: {
104 if (param->notify.value_len == 0) {
105 ESP_LOGW(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: empty value", this->get_name().c_str());
106 break;
107 }
108 ESP_LOGD(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
109 param->notify.handle, param->notify.value[0]);
110 if (param->notify.handle != this->handle)
111 break;
112 this->publish_state(this->parse_data_(param->notify.value, param->notify.value_len));
113 break;
114 }
115 case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
116 if (param->reg_for_notify.handle == this->handle) {
117 if (param->reg_for_notify.status != ESP_GATT_OK) {
118 ESP_LOGW(TAG, "Error registering for notifications at handle %d, status=%d", param->reg_for_notify.handle,
119 param->reg_for_notify.status);
120 break;
121 }
122 this->node_state = espbt::ClientState::ESTABLISHED;
123 char char_buf[esp32_ble::UUID_STR_LEN];
124 ESP_LOGD(TAG, "Register for notify on %s complete", this->char_uuid_.to_str(char_buf));
125 }
126 break;
127 }
128 default:
129 break;
130 }
131}
132
133float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) {
134 if (this->has_data_to_value_) {
135 std::vector<uint8_t> data(value, value + value_len);
136 return this->data_to_value_func_(data);
137 } else if (value_len > 0) {
138 return value[0];
139 } else {
140 return NAN;
141 }
142}
143
145 if (this->node_state != espbt::ClientState::ESTABLISHED) {
146 ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
147 return;
148 }
149 if (this->handle == 0) {
150 ESP_LOGW(TAG, "[%s] Cannot poll, no service or characteristic found", this->get_name().c_str());
151 return;
152 }
153
154 auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle,
155 ESP_GATT_AUTH_REQ_NONE);
156 if (status) {
157 this->status_set_warning();
158 this->publish_state(NAN);
159 ESP_LOGW(TAG, "[%s] Error sending read request for sensor, status=%d", this->get_name().c_str(), status);
160 }
161}
162
163} // namespace esphome::ble_client
164#endif
uint8_t status
Definition bl0942.h:8
void disable_loop()
Disable this component's loop.
void status_clear_warning()
Definition component.h:289
const StringRef & get_name() const
Definition entity_base.h:71
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
bool is_set() const
True if a UUID has been configured (not default-constructed).
Definition ble_device.h:99
const char * to_str(char *buf) const
Write "0xABCD" / "0xABCDEF01" / the dashed 128-bit form, or "None" for an unset UUID,...
esp_err_t register_for_notify(uint16_t char_handle)
Register for notifications, holding the service release until the registration completes.
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:68