ESPHome  2022.6.2
helpers.cpp
Go to the documentation of this file.
1 #include "esphome/core/helpers.h"
2 
3 #include "esphome/core/defines.h"
4 #include "esphome/core/hal.h"
5 
6 #include <cstdio>
7 #include <algorithm>
8 #include <cctype>
9 #include <cmath>
10 #include <cstring>
11 #include <cstdarg>
12 
13 #if defined(USE_ESP8266)
14 #include <osapi.h>
15 #include <user_interface.h>
16 // for xt_rsil()/xt_wsr_ps()
17 #include <Arduino.h>
18 #elif defined(USE_ESP32_FRAMEWORK_ARDUINO)
19 #include <Esp.h>
20 #elif defined(USE_ESP_IDF)
21 #include "esp_system.h"
22 #include <freertos/FreeRTOS.h>
23 #include <freertos/portmacro.h>
24 #endif
25 
26 #ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC
27 #include "esp_efuse.h"
28 #include "esp_efuse_table.h"
29 #endif
30 
31 namespace esphome {
32 
33 // STL backports
34 
35 #if _GLIBCXX_RELEASE < 7
36 std::string to_string(int value) { return str_snprintf("%d", 32, value); } // NOLINT
37 std::string to_string(long value) { return str_snprintf("%ld", 32, value); } // NOLINT
38 std::string to_string(long long value) { return str_snprintf("%lld", 32, value); } // NOLINT
39 std::string to_string(unsigned value) { return str_snprintf("%u", 32, value); } // NOLINT
40 std::string to_string(unsigned long value) { return str_snprintf("%lu", 32, value); } // NOLINT
41 std::string to_string(unsigned long long value) { return str_snprintf("%llu", 32, value); } // NOLINT
42 std::string to_string(float value) { return str_snprintf("%f", 32, value); }
43 std::string to_string(double value) { return str_snprintf("%f", 32, value); }
44 std::string to_string(long double value) { return str_snprintf("%Lf", 32, value); }
45 #endif
46 
47 // Mathematics
48 
49 float lerp(float completion, float start, float end) { return start + (end - start) * completion; }
50 uint8_t crc8(uint8_t *data, uint8_t len) {
51  uint8_t crc = 0;
52 
53  while ((len--) != 0u) {
54  uint8_t inbyte = *data++;
55  for (uint8_t i = 8; i != 0u; i--) {
56  bool mix = (crc ^ inbyte) & 0x01;
57  crc >>= 1;
58  if (mix)
59  crc ^= 0x8C;
60  inbyte >>= 1;
61  }
62  }
63  return crc;
64 }
65 uint32_t fnv1_hash(const std::string &str) {
66  uint32_t hash = 2166136261UL;
67  for (char c : str) {
68  hash *= 16777619UL;
69  hash ^= c;
70  }
71  return hash;
72 }
73 
74 uint32_t random_uint32() {
75 #ifdef USE_ESP32
76  return esp_random();
77 #elif defined(USE_ESP8266)
78  return os_random();
79 #else
80 #error "No random source available for this configuration."
81 #endif
82 }
83 float random_float() { return static_cast<float>(random_uint32()) / static_cast<float>(UINT32_MAX); }
84 bool random_bytes(uint8_t *data, size_t len) {
85 #ifdef USE_ESP32
86  esp_fill_random(data, len);
87  return true;
88 #elif defined(USE_ESP8266)
89  return os_get_random(data, len) == 0;
90 #else
91 #error "No random source available for this configuration."
92 #endif
93 }
94 
95 // Strings
96 
97 bool str_equals_case_insensitive(const std::string &a, const std::string &b) {
98  return strcasecmp(a.c_str(), b.c_str()) == 0;
99 }
100 bool str_startswith(const std::string &str, const std::string &start) { return str.rfind(start, 0) == 0; }
101 bool str_endswith(const std::string &str, const std::string &end) {
102  return str.rfind(end) == (str.size() - end.size());
103 }
104 std::string str_truncate(const std::string &str, size_t length) {
105  return str.length() > length ? str.substr(0, length) : str;
106 }
107 std::string str_until(const char *str, char ch) {
108  char *pos = strchr(str, ch);
109  return pos == nullptr ? std::string(str) : std::string(str, pos - str);
110 }
111 std::string str_until(const std::string &str, char ch) { return str.substr(0, str.find(ch)); }
112 // wrapper around std::transform to run safely on functions from the ctype.h header
113 // see https://en.cppreference.com/w/cpp/string/byte/toupper#Notes
114 template<int (*fn)(int)> std::string str_ctype_transform(const std::string &str) {
115  std::string result;
116  result.resize(str.length());
117  std::transform(str.begin(), str.end(), result.begin(), [](unsigned char ch) { return fn(ch); });
118  return result;
119 }
120 std::string str_lower_case(const std::string &str) { return str_ctype_transform<std::tolower>(str); }
121 std::string str_upper_case(const std::string &str) { return str_ctype_transform<std::toupper>(str); }
122 std::string str_snake_case(const std::string &str) {
123  std::string result;
124  result.resize(str.length());
125  std::transform(str.begin(), str.end(), result.begin(), ::tolower);
126  std::replace(result.begin(), result.end(), ' ', '_');
127  return result;
128 }
129 std::string str_sanitize(const std::string &str) {
130  std::string out;
131  std::copy_if(str.begin(), str.end(), std::back_inserter(out), [](const char &c) {
132  return c == '-' || c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
133  });
134  return out;
135 }
136 std::string str_snprintf(const char *fmt, size_t len, ...) {
137  std::string str;
138  va_list args;
139 
140  str.resize(len);
141  va_start(args, len);
142  size_t out_length = vsnprintf(&str[0], len + 1, fmt, args);
143  va_end(args);
144 
145  if (out_length < len)
146  str.resize(out_length);
147 
148  return str;
149 }
150 std::string str_sprintf(const char *fmt, ...) {
151  std::string str;
152  va_list args;
153 
154  va_start(args, fmt);
155  size_t length = vsnprintf(nullptr, 0, fmt, args);
156  va_end(args);
157 
158  str.resize(length);
159  va_start(args, fmt);
160  vsnprintf(&str[0], length + 1, fmt, args);
161  va_end(args);
162 
163  return str;
164 }
165 
166 // Parsing & formatting
167 
168 size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count) {
169  uint8_t val;
170  size_t chars = std::min(length, 2 * count);
171  for (size_t i = 2 * count - chars; i < 2 * count; i++, str++) {
172  if (*str >= '0' && *str <= '9') {
173  val = *str - '0';
174  } else if (*str >= 'A' && *str <= 'F') {
175  val = 10 + (*str - 'A');
176  } else if (*str >= 'a' && *str <= 'f') {
177  val = 10 + (*str - 'a');
178  } else {
179  return 0;
180  }
181  data[i >> 1] = !(i & 1) ? val << 4 : data[i >> 1] | val;
182  }
183  return chars;
184 }
185 
186 static char format_hex_char(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; }
187 std::string format_hex(const uint8_t *data, size_t length) {
188  std::string ret;
189  ret.resize(length * 2);
190  for (size_t i = 0; i < length; i++) {
191  ret[2 * i] = format_hex_char((data[i] & 0xF0) >> 4);
192  ret[2 * i + 1] = format_hex_char(data[i] & 0x0F);
193  }
194  return ret;
195 }
196 std::string format_hex(const std::vector<uint8_t> &data) { return format_hex(data.data(), data.size()); }
197 
198 static char format_hex_pretty_char(uint8_t v) { return v >= 10 ? 'A' + (v - 10) : '0' + v; }
199 std::string format_hex_pretty(const uint8_t *data, size_t length) {
200  if (length == 0)
201  return "";
202  std::string ret;
203  ret.resize(3 * length - 1);
204  for (size_t i = 0; i < length; i++) {
205  ret[3 * i] = format_hex_pretty_char((data[i] & 0xF0) >> 4);
206  ret[3 * i + 1] = format_hex_pretty_char(data[i] & 0x0F);
207  if (i != length - 1)
208  ret[3 * i + 2] = '.';
209  }
210  if (length > 4)
211  return ret + " (" + to_string(length) + ")";
212  return ret;
213 }
214 std::string format_hex_pretty(const std::vector<uint8_t> &data) { return format_hex_pretty(data.data(), data.size()); }
215 
216 std::string format_hex_pretty(const uint16_t *data, size_t length) {
217  if (length == 0)
218  return "";
219  std::string ret;
220  ret.resize(5 * length - 1);
221  for (size_t i = 0; i < length; i++) {
222  ret[5 * i] = format_hex_pretty_char((data[i] & 0xF000) >> 12);
223  ret[5 * i + 1] = format_hex_pretty_char((data[i] & 0x0F00) >> 8);
224  ret[5 * i + 2] = format_hex_pretty_char((data[i] & 0x00F0) >> 4);
225  ret[5 * i + 3] = format_hex_pretty_char(data[i] & 0x000F);
226  if (i != length - 1)
227  ret[5 * i + 2] = '.';
228  }
229  if (length > 4)
230  return ret + " (" + to_string(length) + ")";
231  return ret;
232 }
233 std::string format_hex_pretty(const std::vector<uint16_t> &data) { return format_hex_pretty(data.data(), data.size()); }
234 
235 ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) {
236  if (on == nullptr && strcasecmp(str, "on") == 0)
237  return PARSE_ON;
238  if (on != nullptr && strcasecmp(str, on) == 0)
239  return PARSE_ON;
240  if (off == nullptr && strcasecmp(str, "off") == 0)
241  return PARSE_OFF;
242  if (off != nullptr && strcasecmp(str, off) == 0)
243  return PARSE_OFF;
244  if (strcasecmp(str, "toggle") == 0)
245  return PARSE_TOGGLE;
246 
247  return PARSE_NONE;
248 }
249 
250 std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) {
251  if (accuracy_decimals < 0) {
252  auto multiplier = powf(10.0f, accuracy_decimals);
253  value = roundf(value * multiplier) / multiplier;
254  accuracy_decimals = 0;
255  }
256  char tmp[32]; // should be enough, but we should maybe improve this at some point.
257  snprintf(tmp, sizeof(tmp), "%.*f", accuracy_decimals, value);
258  return std::string(tmp);
259 }
260 
261 // Colors
262 
263 float gamma_correct(float value, float gamma) {
264  if (value <= 0.0f)
265  return 0.0f;
266  if (gamma <= 0.0f)
267  return value;
268 
269  return powf(value, gamma);
270 }
271 float gamma_uncorrect(float value, float gamma) {
272  if (value <= 0.0f)
273  return 0.0f;
274  if (gamma <= 0.0f)
275  return value;
276 
277  return powf(value, 1 / gamma);
278 }
279 
280 void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value) {
281  float max_color_value = std::max(std::max(red, green), blue);
282  float min_color_value = std::min(std::min(red, green), blue);
283  float delta = max_color_value - min_color_value;
284 
285  if (delta == 0) {
286  hue = 0;
287  } else if (max_color_value == red) {
288  hue = int(fmod(((60 * ((green - blue) / delta)) + 360), 360));
289  } else if (max_color_value == green) {
290  hue = int(fmod(((60 * ((blue - red) / delta)) + 120), 360));
291  } else if (max_color_value == blue) {
292  hue = int(fmod(((60 * ((red - green) / delta)) + 240), 360));
293  }
294 
295  if (max_color_value == 0) {
296  saturation = 0;
297  } else {
298  saturation = delta / max_color_value;
299  }
300 
301  value = max_color_value;
302 }
303 void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue) {
304  float chroma = value * saturation;
305  float hue_prime = fmod(hue / 60.0, 6);
306  float intermediate = chroma * (1 - fabs(fmod(hue_prime, 2) - 1));
307  float delta = value - chroma;
308 
309  if (0 <= hue_prime && hue_prime < 1) {
310  red = chroma;
311  green = intermediate;
312  blue = 0;
313  } else if (1 <= hue_prime && hue_prime < 2) {
314  red = intermediate;
315  green = chroma;
316  blue = 0;
317  } else if (2 <= hue_prime && hue_prime < 3) {
318  red = 0;
319  green = chroma;
320  blue = intermediate;
321  } else if (3 <= hue_prime && hue_prime < 4) {
322  red = 0;
323  green = intermediate;
324  blue = chroma;
325  } else if (4 <= hue_prime && hue_prime < 5) {
326  red = intermediate;
327  green = 0;
328  blue = chroma;
329  } else if (5 <= hue_prime && hue_prime < 6) {
330  red = chroma;
331  green = 0;
332  blue = intermediate;
333  } else {
334  red = 0;
335  green = 0;
336  blue = 0;
337  }
338 
339  red += delta;
340  green += delta;
341  blue += delta;
342 }
343 
344 // System APIs
345 
346 #if defined(USE_ESP8266)
347 IRAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); }
348 IRAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); }
349 #elif defined(USE_ESP32)
350 // only affects the executing core
351 // so should not be used as a mutex lock, only to get accurate timing
352 IRAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); }
353 IRAM_ATTR InterruptLock::~InterruptLock() { portENABLE_INTERRUPTS(); }
354 #endif
355 
356 uint8_t HighFrequencyLoopRequester::num_requests = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
358  if (this->started_)
359  return;
360  num_requests++;
361  this->started_ = true;
362 }
364  if (!this->started_)
365  return;
366  num_requests--;
367  this->started_ = false;
368 }
369 bool HighFrequencyLoopRequester::is_high_frequency() { return num_requests > 0; }
370 
371 void get_mac_address_raw(uint8_t *mac) {
372 #if defined(USE_ESP32)
373 #if defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC)
374  // On some devices, the MAC address that is burnt into EFuse does not
375  // match the CRC that goes along with it. For those devices, this
376  // work-around reads and uses the MAC address as-is from EFuse,
377  // without doing the CRC check.
378  esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, mac, 48);
379 #else
380  esp_efuse_mac_get_default(mac);
381 #endif
382 #elif defined(USE_ESP8266)
383  wifi_get_macaddr(STATION_IF, mac);
384 #endif
385 }
386 std::string get_mac_address() {
387  uint8_t mac[6];
388  get_mac_address_raw(mac);
389  return str_snprintf("%02x%02x%02x%02x%02x%02x", 12, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
390 }
391 std::string get_mac_address_pretty() {
392  uint8_t mac[6];
393  get_mac_address_raw(mac);
394  return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
395 }
396 #ifdef USE_ESP32
397 void set_mac_address(uint8_t *mac) { esp_base_mac_addr_set(mac); }
398 #endif
399 
400 void delay_microseconds_safe(uint32_t us) { // avoids CPU locks that could trigger WDT or affect WiFi/BT stability
401  uint32_t start = micros();
402  const uint32_t lag = 5000; // microseconds, specifies the maximum time for a CPU busy-loop.
403  // it must be larger than the worst-case duration of a delay(1) call (hardware tasks)
404  // 5ms is conservative, it could be reduced when exact BT/WiFi stack delays are known
405  if (us > lag) {
406  delay((us - lag) / 1000UL); // note: in disabled-interrupt contexts delay() won't actually sleep
407  while (micros() - start < us - lag)
408  delay(1); // in those cases, this loop allows to yield for BT/WiFi stack tasks
409  }
410  while (micros() - start < us) // fine delay the remaining usecs
411  ;
412 }
413 
414 } // namespace esphome
void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue)
Convert hue (0-360), saturation (0-1) and value (0-1) to red, green and blue (all 0-1)...
Definition: helpers.cpp:303
std::string str_snake_case(const std::string &str)
Convert the string to snake case (lowercase with underscores).
Definition: helpers.cpp:122
std::string str_truncate(const std::string &str, size_t length)
Truncate a string to a specific length.
Definition: helpers.cpp:104
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals)
Create a string from a value and an accuracy in decimals.
Definition: helpers.cpp:250
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:199
static bool is_high_frequency()
Check whether the loop is running continuously.
Definition: helpers.cpp:369
std::string str_upper_case(const std::string &str)
Convert the string to upper case.
Definition: helpers.cpp:121
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
Definition: helpers.cpp:187
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count)
Parse bytes from a hex-encoded string into a byte array.
Definition: helpers.cpp:168
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition: helpers.cpp:74
std::string str_until(const char *str, char ch)
Extract the part of the string until either the first occurence of the specified character, or the end (requires str to be null-terminated).
Definition: helpers.cpp:107
uint8_t crc8(uint8_t *data, uint8_t len)
Calculate a CRC-8 checksum of data with size len.
Definition: helpers.cpp:50
std::string str_ctype_transform(const std::string &str)
Definition: helpers.cpp:114
float lerp(float completion, float start, float end)
Linearly interpolate between start and end by completion (between 0 and 1).
Definition: helpers.cpp:49
void delay_microseconds_safe(uint32_t us)
Delay for the given amount of microseconds, possibly yielding to other processes during the wait...
Definition: helpers.cpp:400
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:28
bool random_bytes(uint8_t *data, size_t len)
Generate len number of random bytes.
Definition: helpers.cpp:84
ParseOnOffState parse_on_off(const char *str, const char *on, const char *off)
Parse a string that contains either on, off or toggle.
Definition: helpers.cpp:235
ParseOnOffState
Return values for parse_on_off().
Definition: helpers.h:406
float gamma_correct(float value, float gamma)
Applies gamma correction of gamma to value.
Definition: helpers.cpp:263
bool str_startswith(const std::string &str, const std::string &start)
Check whether a string starts with a value.
Definition: helpers.cpp:100
void start()
Start running the loop continuously.
Definition: helpers.cpp:357
void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value)
Convert red, green and blue (all 0-1) values to hue (0-360), saturation (0-1) and value (0-1)...
Definition: helpers.cpp:280
std::string str_lower_case(const std::string &str)
Convert the string to lower case.
Definition: helpers.cpp:120
std::string str_sprintf(const char *fmt,...)
Definition: helpers.cpp:150
std::string get_mac_address()
Get the device MAC address as a string, in lowercase hex notation.
Definition: helpers.cpp:386
bool str_endswith(const std::string &str, const std::string &end)
Check whether a string ends with a value.
Definition: helpers.cpp:101
void stop()
Stop running the loop continuously.
Definition: helpers.cpp:363
void set_mac_address(uint8_t *mac)
Set the MAC address to use from the provided byte array (6 bytes).
Definition: helpers.cpp:397
std::string str_sanitize(const std::string &str)
Sanitizes the input string by removing all characters but alphanumerics, dashes and underscores...
Definition: helpers.cpp:129
std::string to_string(int value)
Definition: helpers.cpp:36
std::string size_t len
Definition: helpers.h:278
uint32_t fnv1_hash(const std::string &str)
Calculate a FNV-1 hash of str.
Definition: helpers.cpp:65
Definition: a4988.cpp:4
uint32_t val
Definition: datatypes.h:85
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
Definition: helpers.cpp:391
std::string str_snprintf(const char *fmt, size_t len,...)
Definition: helpers.cpp:136
float random_float()
Return a random float between 0 and 1.
Definition: helpers.cpp:83
bool str_equals_case_insensitive(const std::string &a, const std::string &b)
Compare strings for equality in case-insensitive manner.
Definition: helpers.cpp:97
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:27
void get_mac_address_raw(uint8_t *mac)
Get the device MAC address as raw bytes, written into the provided byte array (6 bytes).
Definition: helpers.cpp:371
float gamma_uncorrect(float value, float gamma)
Reverts gamma correction of gamma to value.
Definition: helpers.cpp:271