ESPHome  2024.6.1
ezo.cpp
Go to the documentation of this file.
1 #include "ezo.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/hal.h"
4 
5 namespace esphome {
6 namespace ezo {
7 
8 static const char *const EZO_COMMAND_TYPE_STRINGS[] = {"EZO_READ", "EZO_LED", "EZO_DEVICE_INFORMATION",
9  "EZO_SLOPE", "EZO_CALIBRATION", "EZO_SLEEP",
10  "EZO_I2C", "EZO_T", "EZO_CUSTOM"};
11 
12 static const char *const EZO_CALIBRATION_TYPE_STRINGS[] = {"LOW", "MID", "HIGH"};
13 
15  LOG_SENSOR("", "EZO", this);
16  LOG_I2C_DEVICE(this);
17  if (this->is_failed()) {
18  ESP_LOGE(TAG, "Communication with EZO circuit failed!");
19  }
20  LOG_UPDATE_INTERVAL(this);
21 }
22 
24  // Check if a read is in there already and if not insert on in the second position
25 
26  if (!this->commands_.empty() && this->commands_.front()->command_type != EzoCommandType::EZO_READ &&
27  this->commands_.size() > 1) {
28  bool found = false;
29 
30  for (auto &i : this->commands_) {
31  if (i->command_type == EzoCommandType::EZO_READ) {
32  found = true;
33  break;
34  }
35  }
36 
37  if (!found) {
38  std::unique_ptr<EzoCommand> ezo_command(new EzoCommand);
39  ezo_command->command = "R";
40  ezo_command->command_type = EzoCommandType::EZO_READ;
41  ezo_command->delay_ms = 900;
42 
43  auto it = this->commands_.begin();
44  ++it;
45  this->commands_.insert(it, std::move(ezo_command));
46  }
47 
48  return;
49  }
50 
51  this->get_state();
52 }
53 
55  if (this->commands_.empty()) {
56  return;
57  }
58 
59  EzoCommand *to_run = this->commands_.front().get();
60 
61  if (!to_run->command_sent) {
62  const uint8_t *data = reinterpret_cast<const uint8_t *>(to_run->command.c_str());
63  ESP_LOGVV(TAG, "Sending command \"%s\"", data);
64 
65  this->write(data, to_run->command.length());
66 
67  if (to_run->command_type == EzoCommandType::EZO_SLEEP ||
68  to_run->command_type == EzoCommandType::EZO_I2C) { // Commands with no return data
69  this->commands_.pop_front();
71  this->address_ = this->new_address_;
72  return;
73  }
74 
75  this->start_time_ = millis();
76  to_run->command_sent = true;
77  return;
78  }
79 
80  if (millis() - this->start_time_ < to_run->delay_ms)
81  return;
82 
83  uint8_t buf[32];
84 
85  buf[0] = 0;
86 
87  if (!this->read_bytes_raw(buf, 32)) {
88  ESP_LOGE(TAG, "read error");
89  this->commands_.pop_front();
90  return;
91  }
92 
93  switch (buf[0]) {
94  case 1:
95  break;
96  case 2:
97  ESP_LOGE(TAG, "device returned a syntax error");
98  break;
99  case 254:
100  return; // keep waiting
101  case 255:
102  ESP_LOGE(TAG, "device returned no data");
103  break;
104  default:
105  ESP_LOGE(TAG, "device returned an unknown response: %d", buf[0]);
106  break;
107  }
108 
109  ESP_LOGV(TAG, "Received buffer \"%s\" for command type %s", &buf[1], EZO_COMMAND_TYPE_STRINGS[to_run->command_type]);
110 
111  if (buf[0] == 1) {
112  std::string payload = reinterpret_cast<char *>(&buf[1]);
113  if (!payload.empty()) {
114  switch (to_run->command_type) {
116  // some sensors return multiple comma-separated values, terminate string after first one
117  int start_location = 0;
118  if ((start_location = payload.find(',')) != std::string::npos) {
119  payload.erase(start_location);
120  }
121  auto val = parse_number<float>(payload);
122  if (!val.has_value()) {
123  ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str());
124  } else {
125  this->publish_state(*val);
126  }
127  break;
128  }
130  this->led_callback_.call(payload.back() == '1');
131  break;
132  }
134  int start_location = 0;
135  if ((start_location = payload.find(',')) != std::string::npos) {
136  this->device_infomation_callback_.call(payload.substr(start_location + 1));
137  }
138  break;
139  }
141  int start_location = 0;
142  if ((start_location = payload.find(',')) != std::string::npos) {
143  this->slope_callback_.call(payload.substr(start_location + 1));
144  }
145  break;
146  }
148  int start_location = 0;
149  if ((start_location = payload.find(',')) != std::string::npos) {
150  this->calibration_callback_.call(payload.substr(start_location + 1));
151  }
152  break;
153  }
154  case EzoCommandType::EZO_T: {
155  int start_location = 0;
156  if ((start_location = payload.find(',')) != std::string::npos) {
157  this->t_callback_.call(payload.substr(start_location + 1));
158  }
159  break;
160  }
162  this->custom_callback_.call(payload);
163  break;
164  }
165  default: {
166  break;
167  }
168  }
169  }
170  }
171 
172  this->commands_.pop_front();
173 }
174 
175 void EZOSensor::add_command_(const std::string &command, EzoCommandType command_type, uint16_t delay_ms) {
176  std::unique_ptr<EzoCommand> ezo_command(new EzoCommand);
177  ezo_command->command = command;
178  ezo_command->command_type = command_type;
179  ezo_command->delay_ms = delay_ms;
180  this->commands_.push_back(std::move(ezo_command));
181 };
182 
184  std::string payload = str_sprintf("Cal,%s,%0.2f", EZO_CALIBRATION_TYPE_STRINGS[type], value);
185  this->add_command_(payload, EzoCommandType::EZO_CALIBRATION, 900);
186 }
187 
188 void EZOSensor::set_address(uint8_t address) {
189  if (address > 0 && address < 128) {
190  std::string payload = str_sprintf("I2C,%u", address);
191  this->new_address_ = address;
192  this->add_command_(payload, EzoCommandType::EZO_I2C);
193  } else {
194  ESP_LOGE(TAG, "Invalid I2C address");
195  }
196 }
197 
199 
201 
203 
205 
207 
208 void EZOSensor::set_t(float value) {
209  std::string payload = str_sprintf("T,%0.2f", value);
210  this->add_command_(payload, EzoCommandType::EZO_T);
211 }
212 
213 void EZOSensor::set_tempcomp_value(float temp) { this->set_t(temp); }
214 
216 
219 }
220 
223 }
224 
227 }
228 
230  std::string payload = str_sprintf("Cal,%0.2f", value);
231  this->add_command_(payload, EzoCommandType::EZO_CALIBRATION, 900);
232 }
233 
235 
237 
239  std::string to_send = "L,";
240  to_send += on ? "1" : "0";
241  this->add_command_(to_send, EzoCommandType::EZO_LED);
242 }
243 
244 void EZOSensor::send_custom(const std::string &to_send) { this->add_command_(to_send, EzoCommandType::EZO_CUSTOM); }
245 
246 } // namespace ezo
247 } // namespace esphome
void set_calibration_generic(float value)
Definition: ezo.cpp:229
void loop() override
Definition: ezo.cpp:54
void get_device_information()
Definition: ezo.cpp:198
void set_address(uint8_t address)
Definition: ezo.cpp:188
EzoCommandType command_type
Definition: ezo.h:32
void set_calibration_point_low(float value)
Definition: ezo.cpp:217
std::string command
Definition: ezo.h:29
bool is_failed() const
Definition: component.cpp:143
optional< std::array< uint8_t, N > > read_bytes_raw()
Definition: i2c.h:225
mopeka_std_values val[4]
void send_custom(const std::string &to_send)
Definition: ezo.cpp:244
void set_calibration_point_(EzoCalibrationType type, float value)
Definition: ezo.cpp:183
CallbackManager< void(std::string)> device_infomation_callback_
Definition: ezo.h:100
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
ErrorCode write(const uint8_t *data, size_t len, bool stop=true)
writes an array of bytes to a device using an I2CBus
Definition: i2c.h:186
CallbackManager< void(std::string)> slope_callback_
Definition: ezo.h:102
void set_calibration_point_high(float value)
Definition: ezo.cpp:225
std::string str_sprintf(const char *fmt,...)
Definition: helpers.cpp:312
CallbackManager< void(std::string)> t_callback_
Definition: ezo.h:103
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
uint8_t type
uint32_t start_time_
Definition: ezo.h:107
void set_t(float value)
Definition: ezo.cpp:208
void set_calibration_point_mid(float value)
Definition: ezo.cpp:221
void set_tempcomp_value(float temp)
Definition: ezo.cpp:213
CallbackManager< void(std::string)> custom_callback_
Definition: ezo.h:104
uint8_t address_
store the address of the device on the bus
Definition: i2c.h:269
void add_command_(const std::string &command, EzoCommandType command_type, uint16_t delay_ms=300)
Definition: ezo.cpp:175
CallbackManager< void(std::string)> calibration_callback_
Definition: ezo.h:101
void update() override
Definition: ezo.cpp:23
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
EzoCalibrationType
Definition: ezo.h:25
void get_calibration()
Definition: ezo.cpp:215
void dump_config() override
Definition: ezo.cpp:14
void clear_calibration()
Definition: ezo.cpp:234
void set_led_state(bool on)
Definition: ezo.cpp:238
std::deque< std::unique_ptr< EzoCommand > > commands_
Definition: ezo.h:93
CallbackManager< void(bool)> led_callback_
Definition: ezo.h:105
EzoCommandType
Definition: ezo.h:13