ESPHome  2021.11.3
helpers.cpp
Go to the documentation of this file.
1 #include "esphome/core/helpers.h"
2 #include "esphome/core/defines.h"
3 #include <cstdio>
4 #include <algorithm>
5 #include <cmath>
6 #include <cstring>
7 
8 #if defined(USE_ESP8266)
9 #ifdef USE_WIFI
10 #include <ESP8266WiFi.h>
11 #endif
12 #include <Arduino.h>
13 #include <osapi.h>
14 #elif defined(USE_ESP32_FRAMEWORK_ARDUINO)
15 #include <Esp.h>
16 #elif defined(USE_ESP_IDF)
17 #include "esp_system.h"
18 #include <freertos/FreeRTOS.h>
19 #include <freertos/portmacro.h>
20 #endif
21 #ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC
22 #include "esp_efuse.h"
23 #include "esp_efuse_table.h"
24 #endif
25 
26 #include "esphome/core/log.h"
27 #include "esphome/core/hal.h"
28 
29 namespace esphome {
30 
31 static const char *const TAG = "helpers";
32 
33 void get_mac_address_raw(uint8_t *mac) {
34 #ifdef USE_ESP32
35 #ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC
36  // On some devices, the MAC address that is burnt into EFuse does not
37  // match the CRC that goes along with it. For those devices, this
38  // work-around reads and uses the MAC address as-is from EFuse,
39  // without doing the CRC check.
40  esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, mac, 48);
41 #else
42  esp_efuse_mac_get_default(mac);
43 #endif
44 #endif
45 #if (defined USE_ESP8266 && defined USE_WIFI)
46  WiFi.macAddress(mac);
47 #endif
48 }
49 
50 std::string get_mac_address() {
51  char tmp[20];
52  uint8_t mac[6];
54 #ifdef USE_WIFI
55  sprintf(tmp, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
56 #else
57  return "";
58 #endif
59  return std::string(tmp);
60 }
61 
62 std::string get_mac_address_pretty() {
63  char tmp[20];
64  uint8_t mac[6];
66  sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
67  return std::string(tmp);
68 }
69 
70 #ifdef USE_ESP32
71 void set_mac_address(uint8_t *mac) { esp_base_mac_addr_set(mac); }
72 #endif
73 
74 std::string generate_hostname(const std::string &base) { return base + std::string("-") + get_mac_address(); }
75 
76 uint32_t random_uint32() {
77 #ifdef USE_ESP32
78  return esp_random();
79 #elif defined(USE_ESP8266)
80  return os_random();
81 #endif
82 }
83 
84 double random_double() { return random_uint32() / double(UINT32_MAX); }
85 
86 float random_float() { return float(random_double()); }
87 
88 void fill_random(uint8_t *data, size_t len) {
89 #if defined(USE_ESP_IDF) || defined(USE_ESP32_FRAMEWORK_ARDUINO)
90  esp_fill_random(data, len);
91 #elif defined(USE_ESP8266)
92  int err = os_get_random(data, len);
93  assert(err == 0);
94 #else
95 #error "No random source for this system config"
96 #endif
97 }
98 
99 static uint32_t fast_random_seed = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
100 
101 void fast_random_set_seed(uint32_t seed) { fast_random_seed = seed; }
102 uint32_t fast_random_32() {
103  fast_random_seed = (fast_random_seed * 2654435769ULL) + 40503ULL;
104  return fast_random_seed;
105 }
106 uint16_t fast_random_16() {
107  uint32_t rand32 = fast_random_32();
108  return (rand32 & 0xFFFF) + (rand32 >> 16);
109 }
110 uint8_t fast_random_8() {
111  uint8_t rand32 = fast_random_32();
112  return (rand32 & 0xFF) + ((rand32 >> 8) & 0xFF);
113 }
114 
115 float gamma_correct(float value, float gamma) {
116  if (value <= 0.0f)
117  return 0.0f;
118  if (gamma <= 0.0f)
119  return value;
120 
121  return powf(value, gamma);
122 }
123 float gamma_uncorrect(float value, float gamma) {
124  if (value <= 0.0f)
125  return 0.0f;
126  if (gamma <= 0.0f)
127  return value;
128 
129  return powf(value, 1 / gamma);
130 }
131 
132 std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) {
133  if (accuracy_decimals < 0) {
134  auto multiplier = powf(10.0f, accuracy_decimals);
135  value = roundf(value * multiplier) / multiplier;
136  accuracy_decimals = 0;
137  }
138  char tmp[32]; // should be enough, but we should maybe improve this at some point.
139  snprintf(tmp, sizeof(tmp), "%.*f", accuracy_decimals, value);
140  return std::string(tmp);
141 }
142 std::string uint64_to_string(uint64_t num) {
143  char buffer[17];
144  auto *address16 = reinterpret_cast<uint16_t *>(&num);
145  snprintf(buffer, sizeof(buffer), "%04X%04X%04X%04X", address16[3], address16[2], address16[1], address16[0]);
146  return std::string(buffer);
147 }
148 std::string uint32_to_string(uint32_t num) {
149  char buffer[9];
150  auto *address16 = reinterpret_cast<uint16_t *>(&num);
151  snprintf(buffer, sizeof(buffer), "%04X%04X", address16[1], address16[0]);
152  return std::string(buffer);
153 }
154 
155 ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) {
156  if (on == nullptr && strcasecmp(str, "on") == 0)
157  return PARSE_ON;
158  if (on != nullptr && strcasecmp(str, on) == 0)
159  return PARSE_ON;
160  if (off == nullptr && strcasecmp(str, "off") == 0)
161  return PARSE_OFF;
162  if (off != nullptr && strcasecmp(str, off) == 0)
163  return PARSE_OFF;
164  if (strcasecmp(str, "toggle") == 0)
165  return PARSE_TOGGLE;
166 
167  return PARSE_NONE;
168 }
169 
170 uint8_t crc8(uint8_t *data, uint8_t len) {
171  uint8_t crc = 0;
172 
173  while ((len--) != 0u) {
174  uint8_t inbyte = *data++;
175  for (uint8_t i = 8; i != 0u; i--) {
176  bool mix = (crc ^ inbyte) & 0x01;
177  crc >>= 1;
178  if (mix)
179  crc ^= 0x8C;
180  inbyte >>= 1;
181  }
182  }
183  return crc;
184 }
185 
186 void delay_microseconds_safe(uint32_t us) { // avoids CPU locks that could trigger WDT or affect WiFi/BT stability
187  auto start = micros();
188  const uint32_t lag = 5000; // microseconds, specifies the maximum time for a CPU busy-loop.
189  // it must be larger than the worst-case duration of a delay(1) call (hardware tasks)
190  // 5ms is conservative, it could be reduced when exact BT/WiFi stack delays are known
191  if (us > lag) {
192  delay((us - lag) / 1000UL); // note: in disabled-interrupt contexts delay() won't actually sleep
193  while (micros() - start < us - lag)
194  delay(1); // in those cases, this loop allows to yield for BT/WiFi stack tasks
195  }
196  while (micros() - start < us) // fine delay the remaining usecs
197  ;
198 }
199 
200 uint8_t reverse_bits_8(uint8_t x) {
201  x = ((x & 0xAA) >> 1) | ((x & 0x55) << 1);
202  x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2);
203  x = ((x & 0xF0) >> 4) | ((x & 0x0F) << 4);
204  return x;
205 }
206 
207 uint16_t reverse_bits_16(uint16_t x) {
208  return uint16_t(reverse_bits_8(x & 0xFF) << 8) | uint16_t(reverse_bits_8(x >> 8));
209 }
210 std::string to_string(const std::string &val) { return val; }
211 std::string to_string(int val) {
212  char buf[64];
213  sprintf(buf, "%d", val);
214  return buf;
215 }
216 std::string to_string(long val) { // NOLINT
217  char buf[64];
218  sprintf(buf, "%ld", val);
219  return buf;
220 }
221 std::string to_string(long long val) { // NOLINT
222  char buf[64];
223  sprintf(buf, "%lld", val);
224  return buf;
225 }
226 std::string to_string(unsigned val) { // NOLINT
227  char buf[64];
228  sprintf(buf, "%u", val);
229  return buf;
230 }
231 std::string to_string(unsigned long val) { // NOLINT
232  char buf[64];
233  sprintf(buf, "%lu", val);
234  return buf;
235 }
236 std::string to_string(unsigned long long val) { // NOLINT
237  char buf[64];
238  sprintf(buf, "%llu", val);
239  return buf;
240 }
241 std::string to_string(float val) {
242  char buf[64];
243  sprintf(buf, "%f", val);
244  return buf;
245 }
246 std::string to_string(double val) {
247  char buf[64];
248  sprintf(buf, "%f", val);
249  return buf;
250 }
251 std::string to_string(long double val) {
252  char buf[64];
253  sprintf(buf, "%Lf", val);
254  return buf;
255 }
256 
257 optional<int> parse_hex(const char chr) {
258  int out = chr;
259  if (out >= '0' && out <= '9')
260  return (out - '0');
261  if (out >= 'A' && out <= 'F')
262  return (10 + (out - 'A'));
263  if (out >= 'a' && out <= 'f')
264  return (10 + (out - 'a'));
265  return {};
266 }
267 
268 optional<int> parse_hex(const std::string &str, size_t start, size_t length) {
269  if (str.length() < start) {
270  return {};
271  }
272  size_t end = start + length;
273  if (str.length() < end) {
274  return {};
275  }
276  int out = 0;
277  for (size_t i = start; i < end; i++) {
278  char chr = str[i];
279  auto digit = parse_hex(chr);
280  if (!digit.has_value()) {
281  ESP_LOGW(TAG, "Can't convert '%s' to number, invalid character %c!", str.substr(start, length).c_str(), chr);
282  return {};
283  }
284  out = (out << 4) | *digit;
285  }
286  return out;
287 }
288 
289 uint32_t fnv1_hash(const std::string &str) {
290  uint32_t hash = 2166136261UL;
291  for (char c : str) {
292  hash *= 16777619UL;
293  hash ^= c;
294  }
295  return hash;
296 }
297 bool str_equals_case_insensitive(const std::string &a, const std::string &b) {
298  return strcasecmp(a.c_str(), b.c_str()) == 0;
299 }
300 
301 template<uint32_t> uint32_t reverse_bits(uint32_t x) {
302  return uint32_t(reverse_bits_16(x & 0xFFFF) << 16) | uint32_t(reverse_bits_16(x >> 16));
303 }
304 
305 static int high_freq_num_requests = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
306 
308  if (this->started_)
309  return;
310  high_freq_num_requests++;
311  this->started_ = true;
312 }
314  if (!this->started_)
315  return;
316  high_freq_num_requests--;
317  this->started_ = false;
318 }
319 bool HighFrequencyLoopRequester::is_high_frequency() { return high_freq_num_requests > 0; }
320 
321 template<typename T> T clamp(const T val, const T min, const T max) {
322  if (val < min)
323  return min;
324  if (val > max)
325  return max;
326  return val;
327 }
328 template uint8_t clamp(uint8_t, uint8_t, uint8_t);
329 template float clamp(float, float, float);
330 template int clamp(int, int, int);
331 
332 float lerp(float completion, float start, float end) { return start + (end - start) * completion; }
333 
334 bool str_startswith(const std::string &full, const std::string &start) { return full.rfind(start, 0) == 0; }
335 bool str_endswith(const std::string &full, const std::string &ending) {
336  return full.rfind(ending) == (full.size() - ending.size());
337 }
338 std::string str_sprintf(const char *fmt, ...) {
339  std::string str;
340  va_list args;
341 
342  va_start(args, fmt);
343  size_t length = vsnprintf(nullptr, 0, fmt, args);
344  va_end(args);
345 
346  str.resize(length);
347  va_start(args, fmt);
348  vsnprintf(&str[0], length + 1, fmt, args);
349  va_end(args);
350 
351  return str;
352 }
353 
354 std::string hexencode(const uint8_t *data, uint32_t len) {
355  char buf[20];
356  std::string res;
357  for (size_t i = 0; i < len; i++) {
358  if (i + 1 != len) {
359  sprintf(buf, "%02X.", data[i]);
360  } else {
361  sprintf(buf, "%02X ", data[i]);
362  }
363  res += buf;
364  }
365  sprintf(buf, "(%u)", len);
366  res += buf;
367  return res;
368 }
369 
370 void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value) {
371  float max_color_value = std::max(std::max(red, green), blue);
372  float min_color_value = std::min(std::min(red, green), blue);
373  float delta = max_color_value - min_color_value;
374 
375  if (delta == 0)
376  hue = 0;
377  else if (max_color_value == red)
378  hue = int(fmod(((60 * ((green - blue) / delta)) + 360), 360));
379  else if (max_color_value == green)
380  hue = int(fmod(((60 * ((blue - red) / delta)) + 120), 360));
381  else if (max_color_value == blue)
382  hue = int(fmod(((60 * ((red - green) / delta)) + 240), 360));
383 
384  if (max_color_value == 0)
385  saturation = 0;
386  else
387  saturation = delta / max_color_value;
388 
389  value = max_color_value;
390 }
391 
392 void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue) {
393  float chroma = value * saturation;
394  float hue_prime = fmod(hue / 60.0, 6);
395  float intermediate = chroma * (1 - fabs(fmod(hue_prime, 2) - 1));
396  float delta = value - chroma;
397 
398  if (0 <= hue_prime && hue_prime < 1) {
399  red = chroma;
400  green = intermediate;
401  blue = 0;
402  } else if (1 <= hue_prime && hue_prime < 2) {
403  red = intermediate;
404  green = chroma;
405  blue = 0;
406  } else if (2 <= hue_prime && hue_prime < 3) {
407  red = 0;
408  green = chroma;
409  blue = intermediate;
410  } else if (3 <= hue_prime && hue_prime < 4) {
411  red = 0;
412  green = intermediate;
413  blue = chroma;
414  } else if (4 <= hue_prime && hue_prime < 5) {
415  red = intermediate;
416  green = 0;
417  blue = chroma;
418  } else if (5 <= hue_prime && hue_prime < 6) {
419  red = chroma;
420  green = 0;
421  blue = intermediate;
422  } else {
423  red = 0;
424  green = 0;
425  blue = 0;
426  }
427 
428  red += delta;
429  green += delta;
430  blue += delta;
431 }
432 
433 #ifdef USE_ESP8266
434 IRAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); }
435 IRAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); }
436 #endif
437 #ifdef USE_ESP32
438 IRAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); }
439 IRAM_ATTR InterruptLock::~InterruptLock() { portENABLE_INTERRUPTS(); }
440 #endif
441 
442 // ---------------------------------------------------------------------------------------------------------------------
443 
444 std::string str_truncate(const std::string &str, size_t length) {
445  return str.length() > length ? str.substr(0, length) : str;
446 }
447 std::string str_snake_case(const std::string &str) {
448  std::string result;
449  result.resize(str.length());
450  std::transform(str.begin(), str.end(), result.begin(), ::tolower);
451  std::replace(result.begin(), result.end(), ' ', '_');
452  return result;
453 }
454 std::string str_sanitize(const std::string &str) {
455  std::string out;
456  std::copy_if(str.begin(), str.end(), std::back_inserter(out), [](const char &c) {
457  return c == '-' || c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
458  });
459  return out;
460 }
461 
462 } // namespace esphome
void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue)
Convert hue (0-360) & saturation/value percentage (0-1) to RGB floats (0-1)
Definition: helpers.cpp:392
uint16_t fast_random_16()
Definition: helpers.cpp:106
std::string str_snake_case(const std::string &str)
Convert the string to snake case (lowercase with underscores).
Definition: helpers.cpp:447
std::string str_truncate(const std::string &str, size_t length)
Truncate a string to a specific length.
Definition: helpers.cpp:444
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:132
uint8_t reverse_bits_8(uint8_t x)
Definition: helpers.cpp:200
uint32_t random_uint32()
Return a random 32 bit unsigned integer.
Definition: helpers.cpp:76
std::string generate_hostname(const std::string &base)
Definition: helpers.cpp:74
uint8_t crc8(uint8_t *data, uint8_t len)
Calculate a crc8 of data with the provided data length.
Definition: helpers.cpp:170
void fast_random_set_seed(uint32_t seed)
Definition: helpers.cpp:101
uint8_t fast_random_8()
Definition: helpers.cpp:110
uint32_t reverse_bits(uint32_t x)
Definition: helpers.cpp:301
std::string to_string(const std::string &val)
Definition: helpers.cpp:210
float lerp(float completion, float start, float end)
Linearly interpolate between end start and end by completion.
Definition: helpers.cpp:332
std::string uint32_to_string(uint32_t num)
Convert a uint32_t to a hex string.
Definition: helpers.cpp:148
void delay_microseconds_safe(uint32_t us)
Definition: helpers.cpp:186
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:23
uint16_t reverse_bits_16(uint16_t x)
Definition: helpers.cpp:207
ParseOnOffState
Definition: helpers.h:182
float gamma_correct(float value, float gamma)
Applies gamma correction with the provided gamma to value.
Definition: helpers.cpp:115
bool str_startswith(const std::string &full, const std::string &start)
Definition: helpers.cpp:334
void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value)
Convert RGB floats (0-1) to hue (0-360) & saturation/value percentage (0-1)
Definition: helpers.cpp:370
std::string str_sprintf(const char *fmt,...)
Definition: helpers.cpp:338
std::string get_mac_address()
Get the MAC address as a string, using lower case hex notation.
Definition: helpers.cpp:50
std::string hexencode(const uint8_t *data, uint32_t len)
Definition: helpers.cpp:354
void fill_random(uint8_t *data, size_t len)
Definition: helpers.cpp:88
T clamp(const T val, const T min, const T max)
Clamp the value between min and max.
Definition: helpers.cpp:321
void set_mac_address(uint8_t *mac)
Set the MAC address to use from the provided byte array (6 bytes).
Definition: helpers.cpp:71
uint32_t fast_random_32()
Definition: helpers.cpp:102
optional< int > parse_hex(const char chr)
Definition: helpers.cpp:257
ParseOnOffState parse_on_off(const char *str, const char *on, const char *off)
Definition: helpers.cpp:155
std::string str_sanitize(const std::string &str)
Sanitizes the input string by removing all characters but alphanumerics, dashes and underscores...
Definition: helpers.cpp:454
uint32_t fnv1_hash(const std::string &str)
Definition: helpers.cpp:289
Library based on https://github.com/miguelbalboa/rfid and adapted to ESPHome by . ...
Definition: a4988.cpp:4
double random_double()
Returns a random double between 0 and 1.
Definition: helpers.cpp:84
std::string get_mac_address_pretty()
Get the MAC address as a string, using colon-separated upper case hex notation.
Definition: helpers.cpp:62
bool str_endswith(const std::string &full, const std::string &ending)
Definition: helpers.cpp:335
std::string uint64_to_string(uint64_t num)
Convert a uint64_t to a hex string.
Definition: helpers.cpp:142
float random_float()
Returns a random float between 0 and 1. Essentially just casts random_double() to a float...
Definition: helpers.cpp:86
bool str_equals_case_insensitive(const std::string &a, const std::string &b)
Compare string a to string b (ignoring case) and return whether they are equal.
Definition: helpers.cpp:297
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:22
void get_mac_address_raw(uint8_t *mac)
Read the raw MAC address into the provided byte array (6 bytes).
Definition: helpers.cpp:33
float gamma_uncorrect(float value, float gamma)
Reverts gamma correction with the provided gamma to value.
Definition: helpers.cpp:123