ESPHome 2026.1.5
Loading...
Searching...
No Matches
radon_eye_rd200.cpp
Go to the documentation of this file.
1#include "radon_eye_rd200.h"
3
4#include <cstring>
5
6#ifdef USE_ESP32
7
8namespace esphome {
9namespace radon_eye_rd200 {
10
11static const char *const TAG = "radon_eye_rd200";
12
13static const esp32_ble_tracker::ESPBTUUID SERVICE_UUID_V1 =
14 esp32_ble_tracker::ESPBTUUID::from_raw("00001523-1212-efde-1523-785feabcd123");
15static const esp32_ble_tracker::ESPBTUUID WRITE_CHARACTERISTIC_UUID_V1 =
16 esp32_ble_tracker::ESPBTUUID::from_raw("00001524-1212-efde-1523-785feabcd123");
17static const esp32_ble_tracker::ESPBTUUID READ_CHARACTERISTIC_UUID_V1 =
18 esp32_ble_tracker::ESPBTUUID::from_raw("00001525-1212-efde-1523-785feabcd123");
19static const uint8_t WRITE_COMMAND_V1 = 0x50;
20
21static const esp32_ble_tracker::ESPBTUUID SERVICE_UUID_V2 =
22 esp32_ble_tracker::ESPBTUUID::from_raw("00001523-0000-1000-8000-00805f9b34fb");
23static const esp32_ble_tracker::ESPBTUUID WRITE_CHARACTERISTIC_UUID_V2 =
24 esp32_ble_tracker::ESPBTUUID::from_raw("00001524-0000-1000-8000-00805f9b34fb");
25static const esp32_ble_tracker::ESPBTUUID READ_CHARACTERISTIC_UUID_V2 =
26 esp32_ble_tracker::ESPBTUUID::from_raw("00001525-0000-1000-8000-00805f9b34fb");
27static const uint8_t WRITE_COMMAND_V2 = 0x40;
28
29void RadonEyeRD200::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
30 esp_ble_gattc_cb_param_t *param) {
31 switch (event) {
32 case ESP_GATTC_OPEN_EVT: {
33 if (param->open.status == ESP_GATT_OK) {
34 ESP_LOGI(TAG, "Connected successfully!");
35 }
36 break;
37 }
38
39 case ESP_GATTC_DISCONNECT_EVT: {
40 ESP_LOGW(TAG, "Disconnected!");
41 break;
42 }
43
44 case ESP_GATTC_SEARCH_CMPL_EVT: {
45 if (this->parent()->get_service(SERVICE_UUID_V1) != nullptr) {
46 service_uuid_ = SERVICE_UUID_V1;
47 sensors_write_characteristic_uuid_ = WRITE_CHARACTERISTIC_UUID_V1;
48 sensors_read_characteristic_uuid_ = READ_CHARACTERISTIC_UUID_V1;
49 write_command_ = WRITE_COMMAND_V1;
50 } else if (this->parent()->get_service(SERVICE_UUID_V2) != nullptr) {
51 service_uuid_ = SERVICE_UUID_V2;
52 sensors_write_characteristic_uuid_ = WRITE_CHARACTERISTIC_UUID_V2;
53 sensors_read_characteristic_uuid_ = READ_CHARACTERISTIC_UUID_V2;
54 write_command_ = WRITE_COMMAND_V2;
55 } else {
56 ESP_LOGW(TAG, "No supported device has been found, disconnecting");
57 parent()->set_enabled(false);
58 break;
59 }
60
61 this->read_handle_ = 0;
63 if (chr == nullptr) {
64 char service_buf[esp32_ble::UUID_STR_LEN];
65 char char_buf[esp32_ble::UUID_STR_LEN];
66 ESP_LOGW(TAG, "No sensor read characteristic found at service %s char %s", service_uuid_.to_str(service_buf),
68 break;
69 }
70 this->read_handle_ = chr->handle;
71
73 if (write_chr == nullptr) {
74 char service_buf[esp32_ble::UUID_STR_LEN];
75 char char_buf[esp32_ble::UUID_STR_LEN];
76 ESP_LOGW(TAG, "No sensor write characteristic found at service %s char %s", service_uuid_.to_str(service_buf),
78 break;
79 }
80 this->write_handle_ = write_chr->handle;
81
82 esp_err_t status =
83 esp_ble_gattc_register_for_notify(gattc_if, this->parent()->get_remote_bda(), this->read_handle_);
84 if (status) {
85 ESP_LOGW(TAG, "Error registering for sensor notify, status=%d", status);
86 }
87 break;
88 }
89
90 case ESP_GATTC_WRITE_DESCR_EVT: {
91 if (param->write.status != ESP_GATT_OK) {
92 ESP_LOGE(TAG, "write descr failed, error status = %x", param->write.status);
93 break;
94 }
95 ESP_LOGV(TAG, "Write descr success, writing 0x%02X at write_handle=%d", this->write_command_,
96 this->write_handle_);
97 esp_err_t status =
98 esp_ble_gattc_write_char(gattc_if, this->parent()->get_conn_id(), this->write_handle_, sizeof(write_command_),
99 (uint8_t *) &write_command_, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
100 if (status) {
101 ESP_LOGW(TAG, "Error writing 0x%02x command, status=%d", write_command_, status);
102 }
103 break;
104 }
105
106 case ESP_GATTC_NOTIFY_EVT: {
107 if (param->notify.is_notify) {
108 ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT, receive notify value, %d bytes", param->notify.value_len);
109 } else {
110 ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT, receive indicate value, %d bytes", param->notify.value_len);
111 }
112 read_sensors_(param->notify.value, param->notify.value_len);
113 break;
114 }
115
116 default:
117 break;
118 }
119}
120
121void RadonEyeRD200::read_sensors_(uint8_t *value, uint16_t value_len) {
122 if (value_len < 1) {
123 ESP_LOGW(TAG, "Unexpected empty message");
124 return;
125 }
126
127 uint8_t command = value[0];
128
129 if ((command == WRITE_COMMAND_V1 && value_len < 20) || (command == WRITE_COMMAND_V2 && value_len < 68)) {
130 ESP_LOGW(TAG, "Unexpected command 0x%02X message length %d", command, value_len);
131 return;
132 }
133
134 // Example data V1:
135 // 501085EBB9400000000000000000220025000000
136 // Example data V2:
137 // 4042323230313033525532303338330652443230304e56322e302e3200014a00060a00080000000300010079300000e01108001c00020000003822005c8f423fa4709d3f
138 ESP_LOGV(TAG, "radon sensors raw bytes");
139 ESP_LOG_BUFFER_HEX_LEVEL(TAG, value, value_len, ESP_LOG_VERBOSE);
140
141 // Convert from pCi/L to Bq/m³
142 constexpr float convert_to_bwpm3 = 37.0;
143
144 float radon_now; // in Bq/m³
145 float radon_day; // in Bq/m³
146 float radon_month; // in Bq/m³
147 if (command == WRITE_COMMAND_V1) {
148 // Use memcpy to avoid unaligned memory access
149 float temp;
150 memcpy(&temp, value + 2, sizeof(float));
151 radon_now = temp * convert_to_bwpm3;
152 memcpy(&temp, value + 6, sizeof(float));
153 radon_day = temp * convert_to_bwpm3;
154 memcpy(&temp, value + 10, sizeof(float));
155 radon_month = temp * convert_to_bwpm3;
156 } else if (command == WRITE_COMMAND_V2) {
157 // Use memcpy to avoid unaligned memory access
158 uint16_t temp;
159 memcpy(&temp, value + 33, sizeof(uint16_t));
160 radon_now = temp;
161 memcpy(&temp, value + 35, sizeof(uint16_t));
162 radon_day = temp;
163 memcpy(&temp, value + 37, sizeof(uint16_t));
164 radon_month = temp;
165 } else {
166 ESP_LOGW(TAG, "Unexpected command value: 0x%02X", command);
167 return;
168 }
169
170 if (this->radon_sensor_ != nullptr) {
171 this->radon_sensor_->publish_state(radon_now);
172 }
173
174 if (this->radon_long_term_sensor_ != nullptr) {
175 if (radon_month > 0) {
176 ESP_LOGV(TAG, "Radon Long Term based on month");
177 this->radon_long_term_sensor_->publish_state(radon_month);
178 } else {
179 ESP_LOGV(TAG, "Radon Long Term based on day");
180 this->radon_long_term_sensor_->publish_state(radon_day);
181 }
182 }
183
184 ESP_LOGV(TAG,
185 " Measurements (Bq/m³) now: %0.03f, day: %0.03f, month: %0.03f\n"
186 " Measurements (pCi/L) now: %0.03f, day: %0.03f, month: %0.03f",
187 radon_now, radon_day, radon_month, radon_now / convert_to_bwpm3, radon_day / convert_to_bwpm3,
188 radon_month / convert_to_bwpm3);
189
190 // This instance must not stay connected
191 // so other clients can connect to it (e.g. the
192 // mobile app).
193 parent()->set_enabled(false);
194}
195
198 if (!parent()->enabled) {
199 ESP_LOGW(TAG, "Reconnecting to device");
200 parent()->set_enabled(true);
201 } else {
202 ESP_LOGW(TAG, "Connection in progress");
203 }
204 }
205}
206
208 LOG_SENSOR(" ", "Radon", this->radon_sensor_);
209 LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_);
210}
211
213
214} // namespace radon_eye_rd200
215} // namespace esphome
216
217#endif // USE_ESP32
uint8_t status
Definition bl0942.h:8
This class simplifies creating components that periodically check a state.
Definition component.h:525
void set_enabled(bool enabled)
const char * to_str(std::span< char, UUID_STR_LEN > output) const
Definition ble_uuid.cpp:146
BLECharacteristic * get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr)
void read_sensors_(uint8_t *value, uint16_t value_len)
esp32_ble_tracker::ESPBTUUID sensors_write_characteristic_uuid_
esp32_ble_tracker::ESPBTUUID service_uuid_
esp32_ble_tracker::ESPBTUUID sensors_read_characteristic_uuid_
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:76
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7