10static const char *
const TAG =
"ufm01";
12static constexpr uint8_t COMMAND_ACK = 0xE5;
13static constexpr uint32_t COMMAND_ACK_TIMEOUT_MS = 200;
15static constexpr float L_PER_M3 = 1000.0f;
16static constexpr float M3_PER_L = 1.0f / L_PER_M3;
18static constexpr std::array<uint8_t, 7> ACTIVE_MODE = {0xFE, 0xFE, 0x11, 0x5C, 0x00, 0x5C, 0x16};
19static constexpr std::array<uint8_t, 7> CLEAR_ACCUMULATED_FLOW = {0xFE, 0xFE, 0x11, 0x5A, 0xFD, 0x57, 0x16};
20static constexpr std::array<uint8_t, 7> RESET_DEVICE = {0xFE, 0xFE, 0x11, 0x5D, 0xCB, 0x28, 0x16};
23static constexpr size_t FRAME_CHECKSUM_INDEX = 30;
24static constexpr size_t FRAME_STOP_INDEX = 31;
25static constexpr uint8_t FRAME_START_BYTE_1 = 0x3C;
26static constexpr uint8_t FRAME_START_BYTE_2 = 0x32;
27static constexpr uint8_t FRAME_STOP_BYTE = 0x16;
28static constexpr uint8_t FRAME_INDEX_INSTANT_FLOW_FLAG = 15;
29static constexpr uint8_t FRAME_INDEX_RESERVED_SECTION = 21;
30static constexpr uint8_t FRAME_INDEX_TEMP_FLAG = 24;
31static constexpr uint8_t FRAME_FLAG_INSTANT_FLOW = 0x0B;
32static constexpr uint8_t FRAME_FLAG_RESERVED_SECTION = 0x0C;
33static constexpr uint8_t FRAME_FLAG_TEMP = 0x0D;
36static constexpr uint8_t FRAME_ACC_FLOW_FLAG_INDEX = 8;
37static constexpr uint8_t ACC_FLOW_M3_FLAG = 0x1A;
38static constexpr uint8_t FRAME_FLOW_SIGN_INDEX = 20;
39static constexpr uint8_t FLOW_NEGATIVE_SIGN = 0x80;
42static constexpr uint8_t FRAME_ST1_INDEX = 28;
43static constexpr uint8_t FRAME_ST2_INDEX = 29;
44static constexpr uint8_t ST1_EMPTY_TUBE_MASK = 0x20;
45static constexpr uint8_t ST2_UFC_ERROR_MASK = 0x20;
46static constexpr uint8_t ST2_FLOW_DIRECTION_WRONG_MASK = 0x08;
47static constexpr uint8_t ST2_FLOW_RATE_OUT_OF_RANGE_MASK = 0x04;
49static float to_float(uint8_t data) {
return (data >> 4) * 10 + (data & 0x0F); }
51static bool check_byte(
const uint8_t data[FRAME_SIZE],
size_t index, uint8_t expected,
const char *name) {
52 if (data[index] == expected)
54 ESP_LOGW(TAG,
"%s (byte %zu) - expected 0x%02X, but was 0x%02X", name, index, expected, data[index]);
58static bool validate_data(uint8_t data[FRAME_SIZE]) {
60 for (
size_t i = 0; i < FRAME_CHECKSUM_INDEX; ++i)
62 return check_byte(data, 0, FRAME_START_BYTE_1,
"start byte 1") &&
63 check_byte(data, 1, FRAME_START_BYTE_2,
"start byte 2") &&
64 check_byte(data, FRAME_INDEX_INSTANT_FLOW_FLAG, FRAME_FLAG_INSTANT_FLOW,
"instant flow flag") &&
65 check_byte(data, FRAME_INDEX_RESERVED_SECTION, FRAME_FLAG_RESERVED_SECTION,
"reserved section flag") &&
66 check_byte(data, FRAME_INDEX_TEMP_FLAG, FRAME_FLAG_TEMP,
"temperature flag") &&
67 check_byte(data, FRAME_CHECKSUM_INDEX, sum,
"checksum") &&
68 check_byte(data, FRAME_STOP_INDEX, FRAME_STOP_BYTE,
"stop byte");
71static float read_accumulated_flow(uint8_t data[FRAME_SIZE]) {
72 return (data[FRAME_ACC_FLOW_FLAG_INDEX] == ACC_FLOW_M3_FLAG ? L_PER_M3 : 1.0f) *
73 (to_float(data[14]) * 10000000.0f + to_float(data[13]) * 100000.0f + to_float(data[12]) * 1000.0f +
74 to_float(data[11]) * 10.0f + to_float(data[10]) * 0.1f + to_float(data[9]) * 0.001f);
77static float read_flow(uint8_t data[FRAME_SIZE]) {
78 return (data[FRAME_FLOW_SIGN_INDEX] == FLOW_NEGATIVE_SIGN ? -1.0f : 1.0f) *
79 (to_float(data[19]) * 10000.0f + to_float(data[18]) * 100.0f + to_float(data[17]) +
80 to_float(data[16]) * 0.01f) *
84static void log_hex(
const uint8_t *data,
size_t len) {
89static float read_temperature(uint8_t data[FRAME_SIZE]) {
91 if (data[27] == 0x00 && (data[26] == 0x00 || data[26] == 0x70) && data[25] == 0x00) {
94 return to_float(data[27]) * 100.0f + to_float(data[26]) + to_float(data[25]) * 0.01f;
97static bool read_ufc_chip_error(
const uint8_t data[FRAME_SIZE]) {
return data[FRAME_ST2_INDEX] & ST2_UFC_ERROR_MASK; }
99static bool read_flow_direction_wrong(
const uint8_t data[FRAME_SIZE]) {
100 return data[FRAME_ST2_INDEX] & ST2_FLOW_DIRECTION_WRONG_MASK;
103static bool read_empty_tube(
const uint8_t data[FRAME_SIZE]) {
return data[FRAME_ST1_INDEX] & ST1_EMPTY_TUBE_MASK; }
105static bool read_flow_rate_out_of_range(
const uint8_t data[FRAME_SIZE]) {
106 return data[FRAME_ST2_INDEX] & ST2_FLOW_RATE_OUT_OF_RANGE_MASK;
109bool UFM01Component::send_command_(
const std::array<uint8_t, 7> &command) {
113 while (
millis() - start < COMMAND_ACK_TIMEOUT_MS) {
117 if (
byte == COMMAND_ACK)
119 ESP_LOGV(TAG,
"Unexpected byte while waiting for command ACK: 0x%02X",
byte);
136 ESP_LOGI(TAG,
"Setting up UFM-01...");
138 ESP_LOGW(TAG,
"Failed to set active mode (no ACK from device)");
143void UFM01Component::dump_config() {
144 ESP_LOGCONFIG(TAG,
"UFM-01:");
146 LOG_SENSOR(
" ",
"Accumulated Flow", this->accumulated_flow_sensor_);
147 LOG_SENSOR(
" ",
"Flow", this->flow_sensor_);
148 LOG_SENSOR(
" ",
"Temperature", this->temperature_sensor_);
150#ifdef USE_BINARY_SENSOR
151 LOG_BINARY_SENSOR(
" ",
"UFC Chip Error", this->ufc_chip_error_binary_sensor_);
152 LOG_BINARY_SENSOR(
" ",
"Flow Direction Wrong", this->flow_direction_wrong_binary_sensor_);
153 LOG_BINARY_SENSOR(
" ",
"Empty Tube", this->empty_tube_binary_sensor_);
154 LOG_BINARY_SENSOR(
" ",
"Flow Rate Out Of Range", this->flow_rate_out_of_range_binary_sensor_);
158 ESP_LOGW(TAG,
"Setup failed: active mode not acknowledged by device");
162void UFM01Component::on_data_(uint8_t data[FRAME_SIZE]) {
163 bool empty_tube = read_empty_tube(data);
164#ifdef USE_BINARY_SENSOR
165 if (this->ufc_chip_error_binary_sensor_ !=
nullptr)
166 this->ufc_chip_error_binary_sensor_->publish_state(read_ufc_chip_error(data));
167 if (this->flow_direction_wrong_binary_sensor_ !=
nullptr)
168 this->flow_direction_wrong_binary_sensor_->publish_state(read_flow_direction_wrong(data));
169 if (this->empty_tube_binary_sensor_ !=
nullptr)
170 this->empty_tube_binary_sensor_->publish_state(empty_tube);
171 if (this->flow_rate_out_of_range_binary_sensor_ !=
nullptr)
172 this->flow_rate_out_of_range_binary_sensor_->publish_state(read_flow_rate_out_of_range(data));
177 if (this->accumulated_flow_sensor_ !=
nullptr)
178 this->accumulated_flow_sensor_->publish_state(read_accumulated_flow(data));
181 if (this->flow_sensor_ !=
nullptr)
182 this->flow_sensor_->publish_state(NAN);
183 if (this->temperature_sensor_ !=
nullptr)
184 this->temperature_sensor_->publish_state(NAN);
186 if (this->flow_sensor_ !=
nullptr)
187 this->flow_sensor_->publish_state(read_flow(data));
188 if (this->temperature_sensor_ !=
nullptr)
189 this->temperature_sensor_->publish_state(read_temperature(data));
194void UFM01Component::loop() {
197 if (!this->
read_byte(&this->data_[this->read_index_])) {
198 ESP_LOGW(TAG,
"unable to read byte");
199 this->read_index_ = 0;
202 if ((this->read_index_ == 0 && this->data_[0] != FRAME_START_BYTE_1) ||
203 (this->read_index_ == 1 && this->data_[1] != FRAME_START_BYTE_2)) {
204 ESP_LOGW(TAG,
"not start of data at %d (is 0x%02X)", this->read_index_, this->data_[this->read_index_]);
205 this->read_index_ = 0;
208 if (++this->read_index_ <
static_cast<int32_t
>(FRAME_SIZE))
212 if (validate_data(this->data_)) {
213 this->on_data_(this->data_);
214 this->read_index_ = 0;
219 log_hex(this->data_,
sizeof(this->data_));
220 ESP_LOGE(TAG,
"unable to read data");
222 i < static_cast<int32_t>(FRAME_STOP_INDEX) && this->read_index_ ==
static_cast<int32_t
>(FRAME_SIZE); ++i) {
223 if ((this->data_[i] == FRAME_START_BYTE_1) && (this->data_[i + 1] == FRAME_START_BYTE_2)) {
224 for (int32_t j = i; j < static_cast<int32_t>(FRAME_SIZE); ++
j)
225 this->data_[j - i] = this->data_[j];
226 this->read_index_ =
static_cast<int32_t
>(FRAME_SIZE) - i;
229 if (this->read_index_ ==
static_cast<int32_t
>(FRAME_SIZE))
230 this->read_index_ = 0;