ESPHome  2024.12.0
seeed_mr60fda2.cpp
Go to the documentation of this file.
1 #include "seeed_mr60fda2.h"
2 #include "esphome/core/log.h"
3 
4 #include <cinttypes>
5 #include <utility>
6 
7 namespace esphome {
8 namespace seeed_mr60fda2 {
9 
10 static const char *const TAG = "seeed_mr60fda2";
11 
12 // Prints the component's configuration data. dump_config() prints all of the component's configuration
13 // items in an easy-to-read format, including the configuration key-value pairs.
15  ESP_LOGCONFIG(TAG, "MR60FDA2:");
16 #ifdef USE_BINARY_SENSOR
17  LOG_BINARY_SENSOR(" ", "People Exist Binary Sensor", this->people_exist_binary_sensor_);
18  LOG_BINARY_SENSOR(" ", "Is Fall Binary Sensor", this->fall_detected_binary_sensor_);
19 #endif
20 #ifdef USE_BUTTON
21  LOG_BUTTON(" ", "Get Radar Parameters Button", this->get_radar_parameters_button_);
22  LOG_BUTTON(" ", "Reset Radar Button", this->factory_reset_button_);
23 #endif
24 #ifdef USE_SELECT
25  LOG_SELECT(" ", "Install Height Select", this->install_height_select_);
26  LOG_SELECT(" ", "Height Threshold Select", this->height_threshold_select_);
27  LOG_SELECT(" ", "Sensitivity Select", this->sensitivity_select_);
28 #endif
29 }
30 
31 // Initialisation functions
33  ESP_LOGCONFIG(TAG, "Setting up MR60FDA2...");
34  this->check_uart_settings(115200);
35 
36  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
37  this->current_frame_id_ = 0;
38  this->current_frame_len_ = 0;
39  this->current_data_frame_len_ = 0;
40  this->current_frame_type_ = 0;
41  this->get_radar_parameters();
42 
43  memset(this->current_frame_buf_, 0, FRAME_BUF_MAX_SIZE);
44  memset(this->current_data_buf_, 0, DATA_BUF_MAX_SIZE);
45 
46  ESP_LOGCONFIG(TAG, "Set up MR60FDA2 complete");
47 }
48 
49 // main loop
51  uint8_t byte;
52 
53  // Is there data on the serial port
54  while (this->available()) {
55  this->read_byte(&byte);
56  this->split_frame_(byte); // split data frame
57  }
58 }
59 
70 static uint8_t calculate_checksum(const uint8_t *data, size_t len) {
71  uint8_t checksum = 0;
72  for (size_t i = 0; i < len; i++) {
73  checksum ^= data[i];
74  }
75  checksum = ~checksum;
76  return checksum;
77 }
78 
90 static bool validate_checksum(const uint8_t *data, size_t len, uint8_t expected_checksum) {
91  return calculate_checksum(data, len) == expected_checksum;
92 }
93 
94 static uint8_t find_nearest_index(float value, const float *arr, int size) {
95  int nearest_index = 0;
96  float min_diff = std::abs(value - arr[0]);
97  for (int i = 1; i < size; ++i) {
98  float diff = std::abs(value - arr[i]);
99  if (diff < min_diff) {
100  min_diff = diff;
101  nearest_index = i;
102  }
103  }
104  return nearest_index;
105 }
106 
115 static void float_to_bytes(float value, unsigned char *bytes) {
116  union {
117  float float_value;
118  unsigned char byte_array[4];
119  } u;
120 
121  u.float_value = value;
122  memcpy(bytes, u.byte_array, 4);
123 }
124 
133 static void int_to_bytes(uint32_t value, unsigned char *bytes) {
134  bytes[0] = value & 0xFF;
135  bytes[1] = (value >> 8) & 0xFF;
136  bytes[2] = (value >> 16) & 0xFF;
137  bytes[3] = (value >> 24) & 0xFF;
138 }
139 
140 void MR60FDA2Component::split_frame_(uint8_t buffer) {
141  switch (this->current_frame_locate_) {
142  case LOCATE_FRAME_HEADER: // starting buffer
143  if (buffer == FRAME_HEADER_BUFFER) {
144  this->current_frame_len_ = 1;
145  this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
146  this->current_frame_locate_++;
147  }
148  break;
149  case LOCATE_ID_FRAME1:
150  this->current_frame_id_ = buffer << 8;
151  this->current_frame_len_++;
152  this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
153  this->current_frame_locate_++;
154  break;
155  case LOCATE_ID_FRAME2:
156  this->current_frame_id_ += buffer;
157  this->current_frame_len_++;
158  this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
159  this->current_frame_locate_++;
160  break;
162  this->current_data_frame_len_ = buffer << 8;
163  if (this->current_data_frame_len_ == 0x00) {
164  this->current_frame_len_++;
165  this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
166  this->current_frame_locate_++;
167  } else {
168  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
169  }
170  break;
172  this->current_data_frame_len_ += buffer;
173  if (this->current_data_frame_len_ > DATA_BUF_MAX_SIZE) {
174  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
175  } else {
176  this->current_frame_len_++;
177  this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
178  this->current_frame_locate_++;
179  }
180  break;
181  case LOCATE_TYPE_FRAME1:
182  this->current_frame_type_ = buffer << 8;
183  this->current_frame_len_++;
184  this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
185  this->current_frame_locate_++;
186  break;
187  case LOCATE_TYPE_FRAME2:
188  this->current_frame_type_ += buffer;
189  if ((this->current_frame_type_ == IS_FALL_TYPE_BUFFER) ||
190  (this->current_frame_type_ == PEOPLE_EXIST_TYPE_BUFFER) ||
191  (this->current_frame_type_ == RESULT_INSTALL_HEIGHT) || (this->current_frame_type_ == RESULT_PARAMETERS) ||
192  (this->current_frame_type_ == RESULT_HEIGHT_THRESHOLD) || (this->current_frame_type_ == RESULT_SENSITIVITY)) {
193  this->current_frame_len_++;
194  this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
195  this->current_frame_locate_++;
196  } else {
197  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
198  }
199  break;
201  if (validate_checksum(this->current_frame_buf_, this->current_frame_len_, buffer)) {
202  this->current_frame_len_++;
203  this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
204  this->current_frame_locate_++;
205  } else {
206  ESP_LOGD(TAG, "HEAD_CKSUM_FRAME ERROR: 0x%02x", buffer);
207  ESP_LOGV(TAG, "CURRENT_FRAME: %s %s",
208  format_hex_pretty(this->current_frame_buf_, this->current_frame_len_).c_str(),
209  format_hex_pretty(&buffer, 1).c_str());
210  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
211  }
212  break;
213  case LOCATE_DATA_FRAME:
214  this->current_frame_len_++;
215  this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
216  this->current_data_buf_[this->current_frame_len_ - LEN_TO_DATA_FRAME] = buffer;
217  if (this->current_frame_len_ - LEN_TO_HEAD_CKSUM == this->current_data_frame_len_) {
218  this->current_frame_locate_++;
219  }
220  if (this->current_frame_len_ > FRAME_BUF_MAX_SIZE) {
221  ESP_LOGD(TAG, "PRACTICE_DATA_FRAME_LEN ERROR: %d", this->current_frame_len_ - LEN_TO_HEAD_CKSUM);
222  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
223  }
224  break;
226  if (validate_checksum(this->current_data_buf_, this->current_data_frame_len_, buffer)) {
227  this->current_frame_len_++;
228  this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
229  this->current_frame_locate_++;
230  this->process_frame_();
231  } else {
232  ESP_LOGD(TAG, "DATA_CKSUM_FRAME ERROR: 0x%02x", buffer);
233  ESP_LOGV(TAG, "GET CURRENT_FRAME: %s %s",
234  format_hex_pretty(this->current_frame_buf_, this->current_frame_len_).c_str(),
235  format_hex_pretty(&buffer, 1).c_str());
236 
237  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
238  }
239  break;
240  default:
241  break;
242  }
243 }
244 
245 void MR60FDA2Component::process_frame_() {
246  switch (this->current_frame_type_) {
247  case IS_FALL_TYPE_BUFFER:
248  if (this->fall_detected_binary_sensor_ != nullptr) {
249  this->fall_detected_binary_sensor_->publish_state(this->current_frame_buf_[LEN_TO_HEAD_CKSUM]);
250  }
251  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
252  break;
253 
254  case PEOPLE_EXIST_TYPE_BUFFER:
255  if (this->people_exist_binary_sensor_ != nullptr)
256  this->people_exist_binary_sensor_->publish_state(this->current_frame_buf_[LEN_TO_HEAD_CKSUM]);
257  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
258  break;
259 
260  case RESULT_INSTALL_HEIGHT:
261  if (this->current_data_buf_[0]) {
262  ESP_LOGD(TAG, "Successfully set the mounting height");
263  } else {
264  ESP_LOGD(TAG, "Failed to set the mounting height");
265  }
266  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
267  break;
268 
269  case RESULT_HEIGHT_THRESHOLD:
270  if (this->current_data_buf_[0]) {
271  ESP_LOGD(TAG, "Successfully set the height threshold");
272  } else {
273  ESP_LOGD(TAG, "Failed to set the height threshold");
274  }
275  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
276  break;
277 
278  case RESULT_SENSITIVITY:
279  if (this->current_data_buf_[0]) {
280  ESP_LOGD(TAG, "Successfully set the sensitivity");
281  } else {
282  ESP_LOGD(TAG, "Failed to set the sensitivity");
283  }
284  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
285  break;
286 
287  case RESULT_PARAMETERS: {
288  float install_height_float = 0;
289  float height_threshold_float = 0;
290  uint32_t current_sensitivity = 0;
291  if (this->install_height_select_ != nullptr) {
292  uint32_t current_install_height_int =
293  encode_uint32(current_data_buf_[3], current_data_buf_[2], current_data_buf_[1], current_data_buf_[0]);
294 
295  install_height_float = bit_cast<float>(current_install_height_int);
296  uint32_t select_index = find_nearest_index(install_height_float, INSTALL_HEIGHT, 7);
297  this->install_height_select_->publish_state(this->install_height_select_->at(select_index).value());
298  }
299 
300  if (this->height_threshold_select_ != nullptr) {
301  uint32_t current_height_threshold_int =
302  encode_uint32(current_data_buf_[7], current_data_buf_[6], current_data_buf_[5], current_data_buf_[4]);
303 
304  height_threshold_float = bit_cast<float>(current_height_threshold_int);
305  size_t select_index = find_nearest_index(height_threshold_float, HEIGHT_THRESHOLD, 7);
306  this->height_threshold_select_->publish_state(this->height_threshold_select_->at(select_index).value());
307  }
308 
309  if (this->sensitivity_select_ != nullptr) {
310  current_sensitivity =
311  encode_uint32(current_data_buf_[11], current_data_buf_[10], current_data_buf_[9], current_data_buf_[8]);
312 
313  uint32_t select_index = find_nearest_index(current_sensitivity, SENSITIVITY, 3);
314  this->sensitivity_select_->publish_state(this->sensitivity_select_->at(select_index).value());
315  }
316 
317  ESP_LOGD(TAG, "Mounting height: %.2f, Height threshold: %.2f, Sensitivity: %" PRIu32, install_height_float,
318  height_threshold_float, current_sensitivity);
319  this->current_frame_locate_ = LOCATE_FRAME_HEADER;
320  break;
321  }
322  default:
323  break;
324  }
325 }
326 
327 // Send Heartbeat Packet Command
329  uint8_t send_data[13] = {0x01, 0x00, 0x00, 0x00, 0x04, 0x0E, 0x04, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00};
330  float_to_bytes(INSTALL_HEIGHT[index], &send_data[8]);
331  send_data[12] = calculate_checksum(send_data + 8, 4);
332  this->write_array(send_data, 13);
333  ESP_LOGV(TAG, "SEND INSTALL HEIGHT FRAME: %s", format_hex_pretty(send_data, 13).c_str());
334 }
335 
337  uint8_t send_data[13] = {0x01, 0x00, 0x00, 0x00, 0x04, 0x0E, 0x08, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00};
338  float_to_bytes(INSTALL_HEIGHT[index], &send_data[8]);
339  send_data[12] = calculate_checksum(send_data + 8, 4);
340  this->write_array(send_data, 13);
341  ESP_LOGV(TAG, "SEND HEIGHT THRESHOLD: %s", format_hex_pretty(send_data, 13).c_str());
342 }
343 
345  uint8_t send_data[13] = {0x01, 0x00, 0x00, 0x00, 0x04, 0x0E, 0x0A, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00};
346 
347  int_to_bytes(SENSITIVITY[index], &send_data[8]);
348 
349  send_data[12] = calculate_checksum(send_data + 8, 4);
350  this->write_array(send_data, 13);
351  ESP_LOGV(TAG, "SEND SET SENSITIVITY: %s", format_hex_pretty(send_data, 13).c_str());
352 }
353 
355  uint8_t send_data[8] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x06, 0xF6};
356  this->write_array(send_data, 8);
357  ESP_LOGV(TAG, "SEND GET PARAMETERS: %s", format_hex_pretty(send_data, 8).c_str());
358 }
359 
361  uint8_t send_data[8] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x21, 0x10, 0xCF};
362  this->write_array(send_data, 8);
363  ESP_LOGV(TAG, "SEND RESET: %s", format_hex_pretty(send_data, 8).c_str());
364  this->get_radar_parameters();
365 }
366 
367 } // namespace seeed_mr60fda2
368 } // namespace esphome
std::string format_hex_pretty(const uint8_t *data, size_t length)
Format the byte array data of length len in pretty-printed, human-readable hex.
Definition: helpers.cpp:369
void write_array(const uint8_t *data, size_t len)
Definition: uart.h:21
uint8_t checksum
Definition: bl0906.h:210
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition: helpers.h:187
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits=1, UARTParityOptions parity=UART_CONFIG_PARITY_NONE, uint8_t data_bits=8)
Check that the configuration of the UART bus matches the provided values and otherwise print a warnin...
Definition: uart.cpp:13
bool read_byte(uint8_t *data)
Definition: uart.h:29
std::string size_t len
Definition: helpers.h:293
To bit_cast(const From &src)
Convert data between types, without aliasing issues or undefined behaviour.
Definition: helpers.h:122
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
std::vector< uint8_t > bytes
Definition: sml_parser.h:12