ESPHome  2024.4.0
radon_eye_rd200.cpp
Go to the documentation of this file.
1 #include "radon_eye_rd200.h"
2 
3 #ifdef USE_ESP32
4 
5 namespace esphome {
6 namespace radon_eye_rd200 {
7 
8 static const char *const TAG = "radon_eye_rd200";
9 
10 void RadonEyeRD200::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
11  esp_ble_gattc_cb_param_t *param) {
12  switch (event) {
13  case ESP_GATTC_OPEN_EVT: {
14  if (param->open.status == ESP_GATT_OK) {
15  ESP_LOGI(TAG, "Connected successfully!");
16  }
17  break;
18  }
19 
20  case ESP_GATTC_DISCONNECT_EVT: {
21  ESP_LOGW(TAG, "Disconnected!");
22  break;
23  }
24 
25  case ESP_GATTC_SEARCH_CMPL_EVT: {
26  this->read_handle_ = 0;
28  if (chr == nullptr) {
29  ESP_LOGW(TAG, "No sensor read characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
31  break;
32  }
33  this->read_handle_ = chr->handle;
34 
35  // Write a 0x50 to the write characteristic.
37  if (write_chr == nullptr) {
38  ESP_LOGW(TAG, "No sensor write characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
40  break;
41  }
42  this->write_handle_ = write_chr->handle;
43 
45 
47 
49  break;
50  }
51 
52  case ESP_GATTC_READ_CHAR_EVT: {
53  if (param->read.conn_id != this->parent()->get_conn_id())
54  break;
55  if (param->read.status != ESP_GATT_OK) {
56  ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
57  break;
58  }
59  if (param->read.handle == this->read_handle_) {
60  read_sensors_(param->read.value, param->read.value_len);
61  }
62  break;
63  }
64 
65  default:
66  break;
67  }
68 }
69 
70 void RadonEyeRD200::read_sensors_(uint8_t *value, uint16_t value_len) {
71  if (value_len < 20) {
72  ESP_LOGD(TAG, "Invalid read");
73  return;
74  }
75 
76  // Example data
77  // [13:08:47][D][radon_eye_rd200:107]: result bytes: 5010 85EBB940 00000000 00000000 2200 2500 0000
78  ESP_LOGV(TAG, "result bytes: %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X %02X%02X %02X%02X",
79  value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], value[8], value[9],
80  value[10], value[11], value[12], value[13], value[14], value[15], value[16], value[17], value[18],
81  value[19]);
82 
83  if (value[0] != 0x50) {
84  // This isn't a sensor reading.
85  return;
86  }
87 
88  // Convert from pCi/L to Bq/m³
89  constexpr float convert_to_bwpm3 = 37.0;
90 
91  RadonValue radon_value;
92  radon_value.chars[0] = value[2];
93  radon_value.chars[1] = value[3];
94  radon_value.chars[2] = value[4];
95  radon_value.chars[3] = value[5];
96  float radon_now = radon_value.number * convert_to_bwpm3;
97  if (is_valid_radon_value_(radon_now)) {
98  radon_sensor_->publish_state(radon_now);
99  }
100 
101  radon_value.chars[0] = value[6];
102  radon_value.chars[1] = value[7];
103  radon_value.chars[2] = value[8];
104  radon_value.chars[3] = value[9];
105  float radon_day = radon_value.number * convert_to_bwpm3;
106 
107  radon_value.chars[0] = value[10];
108  radon_value.chars[1] = value[11];
109  radon_value.chars[2] = value[12];
110  radon_value.chars[3] = value[13];
111  float radon_month = radon_value.number * convert_to_bwpm3;
112 
113  if (is_valid_radon_value_(radon_month)) {
114  ESP_LOGV(TAG, "Radon Long Term based on month");
116  } else if (is_valid_radon_value_(radon_day)) {
117  ESP_LOGV(TAG, "Radon Long Term based on day");
119  }
120 
121  ESP_LOGV(TAG, " Measurements (Bq/m³) now: %0.03f, day: %0.03f, month: %0.03f", radon_now, radon_day, radon_month);
122 
123  ESP_LOGV(TAG, " Measurements (pCi/L) now: %0.03f, day: %0.03f, month: %0.03f", radon_now / convert_to_bwpm3,
124  radon_day / convert_to_bwpm3, radon_month / convert_to_bwpm3);
125 
126  // This instance must not stay connected
127  // so other clients can connect to it (e.g. the
128  // mobile app).
129  parent()->set_enabled(false);
130 }
131 
132 bool RadonEyeRD200::is_valid_radon_value_(float radon) { return radon > 0.0 and radon < 37000; }
133 
136  if (!parent()->enabled) {
137  ESP_LOGW(TAG, "Reconnecting to device");
138  parent()->set_enabled(true);
139  parent()->connect();
140  } else {
141  ESP_LOGW(TAG, "Connection in progress");
142  }
143  }
144 }
145 
147  ESP_LOGV(TAG, "writing 0x50 to write service");
148  int request = 0x50;
149  auto status = esp_ble_gattc_write_char_descr(this->parent()->get_gattc_if(), this->parent()->get_conn_id(),
150  this->write_handle_, sizeof(request), (uint8_t *) &request,
151  ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
152  if (status) {
153  ESP_LOGW(TAG, "Error sending write request for sensor, status=%d", status);
154  }
155 }
156 
158  auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(),
159  this->read_handle_, ESP_GATT_AUTH_REQ_NONE);
160  if (status) {
161  ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
162  }
163 }
164 
166  LOG_SENSOR(" ", "Radon", this->radon_sensor_);
167  LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_);
168 }
169 
171  : PollingComponent(10000),
172  service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
173  sensors_write_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(WRITE_CHARACTERISTIC_UUID)),
174  sensors_read_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(READ_CHARACTERISTIC_UUID)) {}
175 
176 } // namespace radon_eye_rd200
177 } // namespace esphome
178 
179 #endif // USE_ESP32
esp32_ble_tracker::ESPBTUUID service_uuid_
This class simplifies creating components that periodically check a state.
Definition: component.h:283
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
esp32_ble_tracker::ESPBTUUID sensors_read_characteristic_uuid_
void set_enabled(bool enabled)
Definition: ble_client.cpp:38
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
esp32_ble_tracker::ESPBTUUID sensors_write_characteristic_uuid_
std::string to_string() const
Definition: ble_uuid.cpp:165
uint8_t status
Definition: bl0942.h:23
BLECharacteristic * get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr)
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
void read_sensors_(uint8_t *value, uint16_t value_len)
espbt::ClientState node_state
Definition: ble_client.h:38