ESPHome  2024.5.0
alpha3.cpp
Go to the documentation of this file.
1 #include "alpha3.h"
2 #include "esphome/core/log.h"
4 #include <lwip/sockets.h> //gives ntohl
5 
6 #ifdef USE_ESP32
7 
8 namespace esphome {
9 namespace alpha3 {
10 
11 static const char *const TAG = "alpha3";
12 
14  ESP_LOGCONFIG(TAG, "ALPHA3");
15  LOG_SENSOR(" ", "Flow", this->flow_sensor_);
16  LOG_SENSOR(" ", "Head", this->head_sensor_);
17  LOG_SENSOR(" ", "Power", this->power_sensor_);
18  LOG_SENSOR(" ", "Current", this->current_sensor_);
19  LOG_SENSOR(" ", "Speed", this->speed_sensor_);
20  LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
21 }
22 
23 void Alpha3::setup() {}
24 
25 void Alpha3::extract_publish_sensor_value_(const uint8_t *response, int16_t length, int16_t response_offset,
26  int16_t value_offset, sensor::Sensor *sensor, float factor) {
27  if (sensor == nullptr)
28  return;
29  // we need to handle cases where a value is split over two packets
30  const int16_t value_length = 4; // 32bit float
31  // offset inside current response packet
32  auto rel_offset = value_offset - response_offset;
33  if (rel_offset <= -value_length)
34  return; // aready passed the value completly
35  if (rel_offset >= length)
36  return; // value not in this packet
37 
38  auto start_offset = std::max(0, rel_offset);
39  auto end_offset = std::min((int16_t) (rel_offset + value_length), length);
40  auto copy_length = end_offset - start_offset;
41  auto buffer_offset = std::max(-rel_offset, 0);
42  std::memcpy(this->buffer_ + buffer_offset, response + start_offset, copy_length);
43 
44  if (rel_offset + value_length <= length) {
45  // we have the whole value
46  void *buffer = this->buffer_; // to prevent warnings when casting the pointer
47  *((int32_t *) buffer) = ntohl(*((int32_t *) buffer)); // values are big endian
48  float fvalue = *((float *) buffer);
49  sensor->publish_state(fvalue * factor);
50  }
51 }
52 
53 bool Alpha3::is_current_response_type_(const uint8_t *response_type) {
54  return !std::memcmp(this->response_type_, response_type, GENI_RESPONSE_TYPE_LENGTH);
55 }
56 
57 void Alpha3::handle_geni_response_(const uint8_t *response, uint16_t length) {
58  if (this->response_offset_ >= this->response_length_) {
59  ESP_LOGD(TAG, "[%s] GENI response begin", this->parent_->address_str().c_str());
60  if (length < GENI_RESPONSE_HEADER_LENGTH) {
61  ESP_LOGW(TAG, "[%s] response to short", this->parent_->address_str().c_str());
62  return;
63  }
64  if (response[0] != 36 || response[2] != 248 || response[3] != 231 || response[4] != 10) {
65  ESP_LOGW(TAG, "[%s] response bytes %d %d %d %d %d don't match GENI HEADER", this->parent_->address_str().c_str(),
66  response[0], response[1], response[2], response[3], response[4]);
67  return;
68  }
69  this->response_length_ = response[1] - GENI_RESPONSE_HEADER_LENGTH + 2; // maybe 2 byte checksum
70  this->response_offset_ = -GENI_RESPONSE_HEADER_LENGTH;
71  std::memcpy(this->response_type_, response + 5, GENI_RESPONSE_TYPE_LENGTH);
72  }
73 
74  auto extract_publish_sensor_value = [response, length, this](int16_t value_offset, sensor::Sensor *sensor,
75  float factor) {
76  this->extract_publish_sensor_value_(response, length, this->response_offset_, value_offset, sensor, factor);
77  };
78 
79  if (this->is_current_response_type_(GENI_RESPONSE_TYPE_FLOW_HEAD)) {
80  ESP_LOGD(TAG, "[%s] FLOW HEAD Response", this->parent_->address_str().c_str());
81  extract_publish_sensor_value(GENI_RESPONSE_FLOW_OFFSET, this->flow_sensor_, 3600.0F);
82  extract_publish_sensor_value(GENI_RESPONSE_HEAD_OFFSET, this->head_sensor_, .0001F);
83  } else if (this->is_current_response_type_(GENI_RESPONSE_TYPE_POWER)) {
84  ESP_LOGD(TAG, "[%s] POWER Response", this->parent_->address_str().c_str());
85  extract_publish_sensor_value(GENI_RESPONSE_POWER_OFFSET, this->power_sensor_, 1.0F);
86  extract_publish_sensor_value(GENI_RESPONSE_CURRENT_OFFSET, this->current_sensor_, 1.0F);
87  extract_publish_sensor_value(GENI_RESPONSE_MOTOR_SPEED_OFFSET, this->speed_sensor_, 1.0F);
88  extract_publish_sensor_value(GENI_RESPONSE_VOLTAGE_AC_OFFSET, this->voltage_sensor_, 1.0F);
89  } else {
90  ESP_LOGW(TAG, "unkown GENI response Type %d %d %d %d %d %d %d %d", this->response_type_[0], this->response_type_[1],
91  this->response_type_[2], this->response_type_[3], this->response_type_[4], this->response_type_[5],
92  this->response_type_[6], this->response_type_[7]);
93  }
94  this->response_offset_ += length;
95 }
96 
97 void Alpha3::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
98  switch (event) {
99  case ESP_GATTC_OPEN_EVT: {
100  if (param->open.status == ESP_GATT_OK) {
101  this->response_offset_ = 0;
102  this->response_length_ = 0;
103  ESP_LOGI(TAG, "[%s] connection open", this->parent_->address_str().c_str());
104  }
105  break;
106  }
107  case ESP_GATTC_CONNECT_EVT: {
108  if (std::memcmp(param->connect.remote_bda, this->parent_->get_remote_bda(), 6) != 0)
109  return;
110  auto ret = esp_ble_set_encryption(param->connect.remote_bda, ESP_BLE_SEC_ENCRYPT);
111  if (ret) {
112  ESP_LOGW(TAG, "esp_ble_set_encryption failed, status=%x", ret);
113  }
114  break;
115  }
116  case ESP_GATTC_DISCONNECT_EVT: {
118  if (this->flow_sensor_ != nullptr)
119  this->flow_sensor_->publish_state(NAN);
120  if (this->head_sensor_ != nullptr)
121  this->head_sensor_->publish_state(NAN);
122  if (this->power_sensor_ != nullptr)
123  this->power_sensor_->publish_state(NAN);
124  if (this->current_sensor_ != nullptr)
125  this->current_sensor_->publish_state(NAN);
126  if (this->speed_sensor_ != nullptr)
127  this->speed_sensor_->publish_state(NAN);
128  if (this->speed_sensor_ != nullptr)
129  this->voltage_sensor_->publish_state(NAN);
130  break;
131  }
132  case ESP_GATTC_SEARCH_CMPL_EVT: {
133  auto *chr = this->parent_->get_characteristic(ALPHA3_GENI_SERVICE_UUID, ALPHA3_GENI_CHARACTERISTIC_UUID);
134  if (chr == nullptr) {
135  ESP_LOGE(TAG, "[%s] No GENI service found at device, not an Alpha3..?", this->parent_->address_str().c_str());
136  break;
137  }
138  auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
139  chr->handle);
140  if (status) {
141  ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
142  }
143  this->geni_handle_ = chr->handle;
144  break;
145  }
146  case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
147  this->node_state = espbt::ClientState::ESTABLISHED;
148  this->update();
149  break;
150  }
151  case ESP_GATTC_NOTIFY_EVT: {
152  if (param->notify.handle == this->geni_handle_) {
153  this->handle_geni_response_(param->notify.value, param->notify.value_len);
154  }
155  break;
156  }
157  default:
158  break;
159  }
160 }
161 
162 void Alpha3::send_request_(uint8_t *request, size_t len) {
163  auto status =
164  esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->geni_handle_, len,
165  request, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
166  if (status)
167  ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
168 }
169 
171  if (this->node_state != espbt::ClientState::ESTABLISHED) {
172  ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
173  return;
174  }
175 
176  if (this->flow_sensor_ != nullptr || this->head_sensor_ != nullptr) {
177  uint8_t geni_request_flow_head[] = {39, 7, 231, 248, 10, 3, 93, 1, 33, 82, 31};
178  this->send_request_(geni_request_flow_head, sizeof(geni_request_flow_head));
179  delay(25); // need to wait between requests
180  }
181  if (this->power_sensor_ != nullptr || this->current_sensor_ != nullptr || this->speed_sensor_ != nullptr ||
182  this->voltage_sensor_ != nullptr) {
183  uint8_t geni_request_power[] = {39, 7, 231, 248, 10, 3, 87, 0, 69, 138, 205};
184  this->send_request_(geni_request_power, sizeof(geni_request_power));
185  delay(25); // need to wait between requests
186  }
187 }
188 } // namespace alpha3
189 } // namespace esphome
190 
191 #endif
sensor::Sensor * power_sensor_
Definition: alpha3.h:55
void handle_geni_response_(const uint8_t *response, uint16_t length)
Definition: alpha3.cpp:57
int16_t response_offset_
Definition: alpha3.h:61
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
Definition: alpha3.cpp:97
bool is_current_response_type_(const uint8_t *response_type)
Definition: alpha3.cpp:53
uint8_t buffer_[4]
Definition: alpha3.h:63
sensor::Sensor * flow_sensor_
Definition: alpha3.h:53
void extract_publish_sensor_value_(const uint8_t *response, int16_t length, int16_t response_offset, int16_t value_offset, sensor::Sensor *sensor, float factor)
Definition: alpha3.cpp:25
void dump_config() override
Definition: alpha3.cpp:13
void update() override
Definition: alpha3.cpp:170
void setup() override
Definition: alpha3.cpp:23
uint16_t geni_handle_
Definition: alpha3.h:59
sensor::Sensor * voltage_sensor_
Definition: alpha3.h:58
sensor::Sensor * speed_sensor_
Definition: alpha3.h:57
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
void send_request_(uint8_t *request, size_t len)
Definition: alpha3.cpp:162
uint8_t status
Definition: bl0942.h:23
std::string size_t len
Definition: helpers.h:292
BLECharacteristic * get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr)
uint16_t length
Definition: tt21100.cpp:12
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
Base-class for all sensors.
Definition: sensor.h:57
int16_t response_length_
Definition: alpha3.h:60
uint8_t response_type_[GENI_RESPONSE_TYPE_LENGTH]
Definition: alpha3.h:62
sensor::Sensor * head_sensor_
Definition: alpha3.h:54
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26
espbt::ClientState node_state
Definition: ble_client.h:38
sensor::Sensor * current_sensor_
Definition: alpha3.h:56