esp_iot_framework  v0.1.0-alpha
© 2026 AmakeSasha, distributed under a license Apache-2.0
esp_iot_framework_core_ext.h
1 /* SPDX-License-Identifier: Apache-2.0
2  * Project: esp-iot-framework
3  * Library: esp_iot_framework_core
4  * Folder: ./components/esp_iot_framework_core/include
5  * File: esp_iot_framework_core_ext.h
6  *
7  * Copyright 2026 AmakeSasha
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 #ifndef ESP_IOT_FRAMEWORK_CORE_EXT_H
23 #define ESP_IOT_FRAMEWORK_CORE_EXT_H
24 
25 #include <esp_err.h>
26 
27 #ifdef __cplusplus
28  extern "C" {
29 #endif
30 
31 /**
32  * @addtogroup core_ext_group Core Extension
33  * @{
34  *
35  * @details @note This group of modules is available when you include this line
36  * at the beginning of the file.:
37  * @code{c}
38  * #include <esp_iot_framework_core_ext.h>
39  * @endcode
40  *
41  * This API provides direct access to the CORE internal subsystems. It
42  * is designed for building specialized nodes that require low-level system event
43  * orchestration, raw configuration access, and manual NVS storage management.
44  *
45  * @warning Operating at this level bypasses standard safety abstractions.
46  * Improper use can lead to resource contention, deadlocks, or storage
47  * corruption.
48  */
49 
50 
51 
52 /**
53  * @defgroup core_ext_consts Constants
54  * @brief Default values and length limits.
55  * @{
56  *
57  * This module contains default values and limits for Wi-Fi, HTTP Basic Auth,
58  * and other system configuration parameters.
59  */
60 
61 /**
62  * @name Wi-Fi Configuration
63  * @brief Default values and validation limits for @ref wifi_profiles_desc "Wi-Fi profiles".
64  * @{ */
65 /** @brief Default `SSID` value. */
66 #define EIF_WIFI_SSID_DEFAULT "ESP32_SETUP"
67 /** @brief Minimum `SSID` length without null-terminator.<br>Example of the value: `Q` */
68 #define EIF_WIFI_SSID_MIN_LEN 1U
69 /** @brief Maximum `SSID` length including null-terminator.<br>Example of the value: `My_Super_Long_Wifi_Network_Name` */
70 #define EIF_WIFI_SSID_MAX_LEN 32U
71 
72 /** @brief Default `Password` value. */
73 #define EIF_WIFI_PASS_DEFAULT "12345678"
74 /** @brief Minimum `Password` length for WPA2 without null-terminator.<br>Example of the value: `12345678` */
75 #define EIF_WIFI_PASS_MIN_LEN 8U
76 /** @brief Maximum `Password` length including null-terminator.<br>Example of the value: `this_is_a_very_long_password_that_exactly_reaches_63_characters` */
77 #define EIF_WIFI_PASS_MAX_LEN 64U
78 /** @} */
79 
80 /**
81  * @name Basic Auth Configuration
82  * @brief Validation limits for HTTP Basic Auth.
83  * @{ */
84 /** @brief Minimum `Line` length without null-terminator.<br>Example of the value: `Basic YWRtaW46` */
85 #define EIF_BASIC_AUTH_LINE_MIN_LEN 14U
86 /** @brief Maximum `Line` length including null-terminator.<br>Example of the value: `Basic YWRtaW46MTIzNDU2Nzg5MHF3ZXJ0eTBxd2VydHkwcXdlcnR5MDE=` */
87 #define EIF_BASIC_AUTH_LINE_MAX_LEN 59U
88 /** @brief Minimum `Password` length without null-terminator.<br>Example of the value: <code>ㅤ</code>*/
89 #define EIF_BASIC_AUTH_PASS_MIN_LEN 0U
90 /** @brief Maximum `Password` length including null-terminator.<br>Example of the value: `1234567890qwerty0qwerty0qwerty01` */
91 #define EIF_BASIC_AUTH_PASS_MAX_LEN 33U
92 /** @} */
93 
94 /**
95  * @name NVS Storage Keys
96  * @brief The keys used by the NVS module to access the values.
97  * @{ */
98 /** @brief TLS certificate blob. */
99 #define EIF_NVS_KEY_TLS_CERT "eif_tls_cert"
100 /** @brief TLS private key blob. */
101 #define EIF_NVS_KEY_TLS_PRIV_KEY "eif_tls_key"
102 /** @} */
103 
104 /** @} */
105 
106 
107 
108 /**
109  * @defgroup core_ext_tools Tools
110  * @brief Helper utility functions.
111  *
112  * @{
113  *
114  * This module contains inline helper utilities designed for secure and
115  * defensive manipulation of strings, preventing common buffer overflows and
116  * null-pointer dereferences.
117  */
118 
119 /**
120  * @brief Checks if a string is empty.
121  *
122  * Evaluates the provided string to determine if it is either a `NULL`
123  * pointer or points directly to a null-terminator (`\0`).
124  *
125  * @param str Pointer to the string. Can be `NULL`.
126  *
127  * @return
128  * - `true`: The string pointer is `NULL` or the string is empty (`""`).
129  * - `false`: The string is valid and contains at least one character.
130  *
131  * Example of use:
132  * @code{c}
133  * #include <esp_log.h>
134  * #include <esp_iot_framework_core_ext.h>
135  *
136  * #define TAG "STR_UTIL"
137  *
138  * void validate_json_payload(const char * const payload) {
139  * const bool is_empty = eif_strempty(payload);
140  * if (is_empty == true) {
141  * ESP_LOGE(TAG, "Payload is completely empty or NULL.");
142  * } else {
143  * ESP_LOGI(TAG, "Payload validation passed.");
144  * }
145  * }
146  * @endcode
147  */
148 static inline bool eif_strempty(const char * const str) {
149  return ((str == NULL) || (str[0] == '\0'));
150 }
151 
152 /**
153  * @brief Computes the length of string.
154  *
155  * Calculates the length of the string `str`, up to a maximum allowed bound
156  * specified by `max_allowed`.
157  *
158  * @param str Constant pointer to the constant string to measure.
159  * @param max_allowed Maximum number of characters to examine, excluding the
160  * null-terminator.
161  *
162  * @return
163  * * The number of characters in `str` if a null-terminator is found within
164  * the boundary. If `str` is `NULL`, returns `0U`. If no null-terminator
165  * is found within the limit, returns the internal bounded limit.
166  *
167  * Example of use:
168  * @code{c}
169  * #include <esp_log.h>
170  * #include <esp_iot_framework_core_ext.h>
171  *
172  * #define TAG "STR_UTIL"
173  * #define MAX_BUFFER_SIZE 64U
174  *
175  * void check_string_bounds(const char * const user_input) {
176  * const size_t calculated_len = eif_strnlen(user_input, MAX_BUFFER_SIZE);
177  *
178  * if (calculated_len > MAX_BUFFER_SIZE) {
179  * ESP_LOGE(TAG, "Input string exceeds maximum allowed limit of %u.", MAX_BUFFER_SIZE);
180  * } else {
181  * ESP_LOGI(TAG, "String length is safe: %zu bytes.", calculated_len);
182  * }
183  * }
184  * @endcode
185  */
186 static inline size_t eif_strnlen(const char * const str, size_t max_allowed) {
187  size_t search_limit = 0U;
188  if (str != NULL) {
189  if (max_allowed < SIZE_MAX) {
190  search_limit = max_allowed + 1U;
191  } else {
192  search_limit = max_allowed;
193  }
194  }
195 
196  /* Cleanup */
197  /* Required for the new compiler 'GCC 11+' (ESP-IDF v5+) */
198  #if defined(__GNUC__) && (__GNUC__ >= 11)
199  #pragma GCC diagnostic push
200  #pragma GCC diagnostic ignored "-Wstringop-overread"
201  #endif
202  /* @deviation [Rule 21.18] The search limit is set to (max_allowed + 1)
203  * where possible to allow the caller to distinguish between a string
204  * that exactly fits the limit and one that exceeds it. Pre-check
205  * against 'SIZE_MAX' ensures no integer wrap-around occurs. Memory
206  * access is guaranteed to be within safe bounds as strnlen stops at
207  * the first null-terminator or the provided 'search_limit'. */
208  return strnlen(str, search_limit);
209  #if defined(__GNUC__) && (__GNUC__ >= 11)
210  #pragma GCC diagnostic pop
211  #endif
212 }
213 /** @} */
214 
215 
216 
217 /**
218  * @defgroup core_ext_config Configuration
219  * @brief Functions to retrieve current system configuration.
220  *
221  * @{
222  *
223  * This module aggregates all functions used to query current
224  * operational flags, device parameters, and active configuration blocks.
225  *
226  * @note Before calling any function in this group, `eif_core_initialize()`
227  * must be executed exactly once.<br><br>
228  * Examples:
229  * @code{c}
230  * #include <esp_err.h>
231  * #include <esp_iot_framework_core.h>
232  *
233  * void app_main(void) {
234  * ESP_ERROR_CHECK(eif_core_initialize());
235  *
236  * // Further code...
237  * }
238  * @endcode
239  */
240 
241 /**
242  * @brief Wi-Fi profile availability test result.
243  *
244  * Stores the outcome of a network availability check for a specific Wi-Fi profile.
245  * This structure captures whether the target network is reachable, accepts
246  * credentials, and successfully provides a valid IP address.
247  */
248 typedef struct {
249  /**
250  * @brief Network availability status.
251  *
252  * `true` if the network is reachable, credentials are valid, and an IP
253  * address is obtained within the timeout. `false` if the network is
254  * unavailable or the connection fails.
255  */
256  bool connected;
257  /**
258  * @brief Received Signal Strength Indicator (RSSI).
259  *
260  * Measured in dBm during the availability check. Defaults to `-127`
261  * if the network is unreachable or the connection attempt fails.
262  */
263  int8_t rssi;
265 
266 /**
267  * @brief Gets the total number of @ref wifi_profiles_desc "Wi-Fi profiles".
268  *
269  * Returns the number of @ref wifi_profiles_desc "Wi-Fi profiles" stored in
270  * the system. It is set using `eif_set_wifi_profiles_count()`.
271  *
272  * Example of use:
273  * @code{c}
274  * #include <esp_log.h>
275  * #include <esp_iot_framework_core_ext.h>
276  *
277  * #define TAG "WIFI_CFG"
278  *
279  * void check_profiles_bound(void) {
280  * size_t total_profiles = eif_wifi_get_profiles_count();
281  * ESP_LOGI(TAG,
282  * "Framework is currently managing %zu Wi-Fi profile(s).",
283  * total_profiles);
284  * }
285  * @endcode
286  */
288 
289 /**
290  * @brief Gets the index of the currently active @ref wifi_profiles_desc "Wi-Fi profile".
291  *
292  * Returns the zero-based index of the active
293  * @ref wifi_profiles_desc "Wi-Fi profile" currently in use.
294  *
295  * Example of use:
296  * @code{c}
297  * #include <esp_log.h>
298  * #include <esp_iot_framework_core_ext.h>
299  *
300  * #define TAG "WIFI_CFG"
301  *
302  * void log_active_profile_index(void) {
303  * uint8_t active_idx = eif_wifi_get_current_profile_index();
304  * ESP_LOGI(TAG,
305  * "Subsystem is currently operating on profile index: %u",
306  * active_idx);
307  * }
308  * @endcode
309  */
311 
312 /**
313  * @brief Gets the network availability test result for a specific Wi-Fi profile.
314  *
315  * Copies the latest availability check result for the specified profile `index`
316  * into the `out_result` structure.
317  *
318  * @param index Zero-based Wi-Fi profile index to query.
319  * Must be less than `eif_wifi_get_profiles_count()`.
320  * @param out_result Pointer to the structure where the result will be copied.
321  * Cannot be `NULL`.
322  *
323  * @return
324  * - `ESP_OK`: Result was successfully fetched and copied.
325  * - `ESP_ERR_INVALID_ARG`: The `out_result` pointer is `NULL`.
326  * - `ESP_ERR_INVALID_SIZE`: The `index` is out of the possible range.
327  *
328  * Example of use:
329  * @code{c}
330  * #include <esp_log.h>
331  * #include <esp_err.h>
332  * #include <esp_iot_framework_core_ext.h>
333  *
334  * #define TAG "APP_WIFI"
335  *
336  * void check_profile_status(uint8_t profile_idx) {
337  * eif_wifi_test_result result = {0};
338  *
339  * esp_err_t err = eif_wifi_get_test_result(profile_idx, &result);
340  * if (err == ESP_OK) {
341  * ESP_LOGI(TAG, "Profile %u: Connected = %s, RSSI = %d dBm",
342  * profile_idx, result.connected ? "YES" : "NO", result.rssi);
343  * } else if (err == ESP_ERR_INVALID_SIZE) {
344  * ESP_LOGE(TAG, "Profile index %u is out of bounds", profile_idx);
345  * } else {
346  * ESP_LOGE(TAG, "Failed to get test result: %s", esp_err_to_name(err));
347  * }
348  * }
349  * @endcode
350  */
352  uint8_t index, eif_wifi_test_result * const out_result
353 );
354 /** @} */
355 
356 
357 
358 /**
359  * @defgroup core_ext_handlers Event Handlers
360  * @brief System hooks for internal business logic.
361  * @{
362  *
363  * These functions are the primary interface for linking the final product's
364  * business logic with system-wide lifecycle events. They allow the application
365  * to execute specific tasks in direct response to internal state transitions,
366  * hardware updates, and subsystem status changes.
367  *
368  * @note Before calling any function in this group, `eif_core_initialize()`
369  * must be executed exactly once, and all calls must take place prior
370  * to `eif_wifi_initialize()`.<br><br>
371  * Examples:
372  * @code{c}
373  * #include <esp_err.h>
374  * #include <esp_iot_framework_core.h>
375  *
376  * void app_main(void) {
377  * ESP_ERROR_CHECK(eif_core_initialize());
378  *
379  * // Further code...
380  *
381  * ESP_ERROR_CHECK(eif_wifi_initialize());
382  * }
383  * @endcode
384  */
385 
386 /**
387  * @name IP layer events
388  * @{
389  *
390  * These handlers are dispatched automatically by the framework when
391  * the corresponding IP-level event occurs.
392  */
393 
394 /**
395  * @brief Prototype for network IP lifecycle event handlers.
396  */
397 typedef esp_err_t (*eif_handler_ip_t)(void);
398 
399 /**
400  * @brief Register a handler for the `IP_EVENT_STA_GOT_IP`.
401  *
402  * Registers a callback for processing network layer events. It is the primary
403  * trigger point that executes automatically as soon as the station secures a
404  * valid IP lease from the DHCP server.
405  *
406  * @warning This registration is not cumulative. Any previously registered
407  * handler for this event will be overwritten. Only the most recent
408  * assignment is stored and executed by the `CORE`.
409  *
410  * @param handler Pointer to the function to execute. Cannot be `NULL`.
411  *
412  * @return
413  * - `ESP_OK`: Handler registered successfully.
414  * - `ESP_ERR_INVALID_ARG`: The `handler` pointer is `NULL`.
415  *
416  * Example of use:
417  * @code{c}
418  * #include <esp_err.h>
419  * #include <esp_iot_framework_core.h>
420  * #include <esp_iot_framework_core_ext.h>
421  *
422  * esp_err_t ip_got_hanlder(void) {
423  * // Initialize HTTP server or cloud telemetry tasks safely here
424  * return ESP_OK;
425  * }
426  *
427  * void app_main(void) {
428  * ESP_ERROR_CHECK(eif_core_initialize());
429  * ESP_ERROR_CHECK(eif_register_handler_ip_got(ip_got_hanlder));
430  *
431  * // Further code...
432  * }
433  * @endcode
434  */
436 
437 /**
438  * @brief Register a handler for the `IP_EVENT_STA_LOST_IP`.
439  *
440  * @warning Due to the limitations of the SDK, the framework automatically
441  * calls this handler at event `WIFI_EVENT_STA_DISCONNECTED`, since
442  * it is equivalent to `IP_EVENT_STA_LOST_IP`. If `IP_EVENT_STA_LOST_IP`
443  * is separate, the handler will be called twice.
444  *
445  * Registers a callback for processing network layer events. It executes
446  * automatically when the station drops its Wi-Fi connection, the AP kicks the
447  * client, or the DHCP lease expires.
448  *
449  * @warning This registration is not cumulative. Any previously registered
450  * handler for this event will be overwritten. Only the most recent
451  * assignment is stored and executed by the `CORE`.
452  *
453  * @param handler Pointer to the function to execute. Cannot be `NULL`.
454  *
455  * @return
456  * - `ESP_OK`: Handler registered successfully.
457  * - `ESP_ERR_INVALID_ARG`: The `handler` pointer is `NULL`.
458  *
459  * Example of use:
460  * @code{c}
461  * #include <esp_err.h>
462  * #include <esp_iot_framework_core.h>
463  * #include <esp_iot_framework_core_ext.h>
464  *
465  * esp_err_t ip_lost_hanlder(void) {
466  * // Gracefully put cloud communication tasks to sleep
467  * return ESP_OK;
468  * }
469  *
470  * void app_main(void) {
471  * ESP_ERROR_CHECK(eif_core_initialize());
472  * ESP_ERROR_CHECK(eif_register_handler_ip_lost(ip_lost_hanlder));
473  *
474  * // Further code...
475  * }
476  * @endcode
477  */
479 /** @} */
480 
481 /** @} */
482 
483 
484 
485 /**
486  * @defgroup core_ext_nvs Non-Volatile Storage (NVS)
487  * @brief NVS extension layer for data persistence and configuration profiles.
488  * @{
489  *
490  * This module provides high-level functions for data persistence in NVS. It
491  * enforces runtime buffer protection, boundary validation, and key
492  * compatibility checks, completely preventing memory corruption and invalid
493  * configurations within the framework.
494  *
495  * @note Before calling any function in this group, `eif_core_initialize()`
496  * and `eif_nvs_initialize()` must be executed exactly once.<br><br>
497  * Examples:
498  * @code{c}
499  * #include <esp_err.h>
500  * #include <esp_iot_framework_core.h>
501  * #include <esp_iot_framework_core_ext.h>
502  *
503  * void app_main(void) {
504  * ESP_ERROR_CHECK(eif_core_initialize());
505  * ESP_ERROR_CHECK(eif_nvs_initialize());
506  *
507  * // Further code...
508  * }
509  * @endcode
510  */
511 
512 /**
513  * @name String
514  * @brief Functions for reading and writing strings to NVS with length validation.
515  * @{
516  */
517 /**
518  * @brief Validates string length and writes it to NVS.
519  *
520  * Writes the passed string (`value`) to NVS using the passed key (`key`).
521  * Before writing, the function checks whether the string length is in the
522  * range from `min_len` to `max_len`.
523  *
524  * Checking the length depending on `it_can_be_empty` (`len` it is length
525  * `value`):
526  * - If `it_can_be_empty` is **true**: `((len >= min_len) && (len <= max_len)) || (len == 0)`
527  * - If `it_can_be_empty` is **false**: `((len >= min_len) && (len <= max_len))`
528  *
529  * @param key NVS storage key (maximum `15` characters). Cannot be
530  * `NULL`.
531  * @param value The string to save. Cannot be `NULL`.
532  * @param min_len Minimum allowed length (excluding the null-terminator).
533  * @param max_len Maximum allowed length (including the null-terminator).
534  * @param it_can_be_empty Allow saving an empty string, bypassing length limits.
535  *
536  * @return
537  * - `ESP_OK`: String verified, written, and committed successfully.
538  * - `ESP_ERR_INVALID_ARG`: The pointer `key` or `value` is `NULL`.
539  * - `ESP_ERR_INVALID_SIZE`: The length of the `value` is outside the
540  * acceptable range.
541  * - `ESP_ERR_NVS_*`: System errors propagated from `nvs_open()`,
542  * `nvs_set_str()`, or `nvs_commit()`.
543  *
544  * Example of use:
545  * @code{c}
546  * #include <esp_log.h>
547  * #include <esp_err.h>
548  * #include <esp_iot_framework_core_ext.h>
549  *
550  * #define TAG "CONFIG"
551  *
552  * void save_dev_name(char *new_name) {
553  * // Save device name (min 3, max 31 chars, cannot be empty)
554  * esp_err_t err = eif_nvs_value_save("dev_name", new_name, 3, 32, false);
555  *
556  * if (err == ESP_OK) {
557  * ESP_LOGI(TAG, "Name saved!");
558  * } else {
559  * ESP_LOGE(TAG, "Failed to save: %s", esp_err_to_name(err));
560  * }
561  * }
562  * @endcode
563  */
565  const char * const key, const char * const value,
566  size_t min_len, size_t max_len, bool it_can_be_empty
567 );
568 
569 /**
570  * @brief Loads a string from NVS into a pre-allocated buffer.
571  *
572  * Reads the string associated with the `key` and copies it into `value_out`.
573  * The function prevents buffer overflows by forcing the native read operation
574  * to respect the `max_len` limit, ensuring the output is safely stored within
575  * the allocated boundaries.
576  *
577  * @note If `ESP_ERR_NVS_NOT_FOUND` occurs and `max_len > 1U`, the `value_out[0]`
578  * is explicitly set to `\0` to ensure safety.
579  *
580  * @warning `max_len` must match the length of `value_out`, as this requires
581  * `nvs_get_str()`. Otherwise, data may be truncated.
582  *
583  * @param key NVS storage key (maximum `15` characters). Cannot be `NULL`.
584  * @param value_out Pointer to the buffer where the string will be stored.
585  * Cannot be `NULL`.
586  * @param max_len Maximum bytes allocated for `value_out` (including the
587  * null-terminator).
588  *
589  * @return
590  * - `ESP_OK`: Value located and copied successfully.
591  * - `ESP_ERR_INVALID_ARG`: The pointer `key` or `value_out` is `NULL`.
592  * - `ESP_ERR_NVS_NOT_FOUND`: The key does not exist in storage.
593  * - `ESP_ERR_NVS_*`: System errors propagated from `nvs_open()` or
594  * `nvs_get_str()`.
595  *
596  * Example of use:
597  * @code{c}
598  * #include <esp_log.h>
599  * #include <esp_err.h>
600  * #include <esp_iot_framework_core_ext.h>
601  *
602  * #define TAG "CONFIG"
603  *
604  * void load_dev_name(void) {
605  * char buffer[32] = {0};
606  *
607  * // Load saved name. If not found, buffer will be empty ("")
608  * esp_err_t ret = eif_nvs_value_load("dev_name", buffer, sizeof(buffer));
609  *
610  * if (ret == ESP_OK) {
611  * ESP_LOGI(TAG, "Loaded name: %s", buffer);
612  * } else if (ret == ESP_ERR_NVS_NOT_FOUND) {
613  * ESP_LOGW(TAG, "Name not found, using default.");
614  * } else {
615  * ESP_LOGE(TAG, "Error: %s", esp_err_to_name(ret));
616  * }
617  * }
618  * @endcode
619  */
621  const char * const key, char * const value_out, size_t max_len
622 );
623 
624 /**
625  * @brief Dynamically allocates memory and loads a string from NVS.
626  *
627  * Queries storage to determine the data size, allocates a buffer from the heap
628  * via `pvPortMalloc()`, and copies the string into it. If any operation fails,
629  * `*value_out` is guaranteed to be set to `NULL` and `*value_out_len` is set
630  * to `0U`.
631  *
632  * @note This function automatically calls `vPortFree(*value_out)` at the
633  * beginning to clear pre-existing allocations.
634  *
635  * @warning The caller assumes ownership of the allocated buffer. If this
636  * function returns `ESP_OK`, the caller MUST release the memory using
637  * `vPortFree(*value_out)` (or the respective pointer variable) to
638  * avoid leaks.
639  *
640  * @param key NVS storage key (maximum `15` characters). Cannot be
641  * `NULL`.
642  * @param value_out Double pointer to store the allocated buffer address.
643  * Cannot be `NULL`.
644  * @param value_out_len Pointer to store the string length (including the `\0`).
645  * Cannot be `NULL`.
646  *
647  * @return
648  * - `ESP_OK`: Memory allocated and string loaded successfully.
649  * - `ESP_ERR_INVALID_ARG`: The pointer `key`, `value_out`, or
650  * `value_out_len` is `NULL`.
651  * - `ESP_ERR_NVS_NOT_FOUND`: The key does not exist in storage.
652  * - `ESP_ERR_NO_MEM`: Memory could not be allocated due to the lack
653  * of an empty block of the required size.
654  * - `ESP_ERR_NVS_*`: System errors propagated from `nvs_open()` or
655  * `nvs_get_str()`.
656  *
657  * Example of use:
658  * @code{c}
659  * #include <esp_log.h>
660  * #include <esp_err.h>
661  * #include <esp_iot_framework_core_ext.h>
662  *
663  * #define TAG "CONFIG"
664  *
665  * void load_dynamic_token(void) {
666  * char *token_buf = NULL; // Must be initialized to NULL
667  * size_t token_len = 0U;
668  *
669  * esp_err_t err = eif_nvs_value_load_malloc("huge_token", &token_buf, &token_len);
670  *
671  * if (err == ESP_OK) {
672  * ESP_LOGI(TAG, "Token loaded (%u bytes): %s", token_len, token_buf);
673  *
674  * // Mandatory resource cleanup
675  * vPortFree(token_buf);
676  * token_buf = NULL;
677  * } else if (err == ESP_ERR_NVS_NOT_FOUND) {
678  * ESP_LOGW(TAG, "Token not found.");
679  * } else {
680  * ESP_LOGE(TAG, "Failed to load token: %s", esp_err_to_name(err));
681  * }
682  * }
683  * @endcode
684  */
686  const char * key, char ** const value_out, size_t * const value_out_len
687 );
688 /** @} */
689 
690 /**
691  * @name Wi-Fi profile
692  * @brief Functions for reading and writing Wi-Fi network credentials to NVS.
693  * @{
694  */
695 /**
696  * @brief Saves Wi-Fi network credentials for a specific profile index.
697  *
698  * Writes the provided SSID and password to NVS under auto-generated keys.
699  * Before writing, the function verifies that the profile index is within the
700  * valid user range and checks the credential string lengths.
701  *
702  * Valid index range check: `(index >= 1) && (index <= WIFI_PROFILES_MAX_COUNT)`
703  *
704  * If both the ssid and the pass have a length of 0, the length checks are
705  * ignored. This is done to clear the specified slot. The verification is
706  * bypassed according to the following logic:
707  * `(len(ssid) == 0) && (len(pass) == 0)`
708  *
709  * @note Profile index `0` is system-reserved (read-only). If you try to
710  * overwrite it, you will get an error.
711  *
712  * @param index Profile index slot. Must be from `1` to
713  * <code>#EIF_WIFI_PROFILES_MAX_COUNT</code>.
714  * @param ssid Wi-Fi SSID string. Cannot be `NULL`. The length should be from
715  * <code>#EIF_WIFI_SSID_MIN_LEN</code> to
716  * <code>#EIF_WIFI_SSID_MAX_LEN</code>.
717  * @param pass Wi-Fi Password string. Cannot be `NULL`. The length should be
718  * from <code>#EIF_WIFI_PASS_MIN_LEN</code> to
719  * <code>#EIF_WIFI_PASS_MAX_LEN</code>.
720  *
721  * @return
722  * - `ESP_OK`: Profile validated and saved successfully.
723  * - `ESP_ERR_INVALID_ARG`: The pointer `ssid` or `pass` is `NULL`.
724  * - `ESP_ERR_INVALID_SIZE`: The `index` is out of the possible range.
725  * - `ESP_ERR_NVS_*`: System errors propagated from
726  * `eif_nvs_value_save()`.
727  *
728  * Example of use:
729  * @code{c}
730  * #include <esp_log.h>
731  * #include <esp_err.h>
732  * #include <esp_iot_framework_core_ext.h>
733  *
734  * #define TAG "WIFI_CONFIG"
735  *
736  * void save_user_network(void) {
737  * // Save user credentials to slot index 1
738  * esp_err_t err = eif_nvs_wifi_profile_save(1U, "MyHomeWiFi", "SecretPass123");
739  *
740  * if (err == ESP_OK) {
741  * ESP_LOGI(TAG, "Profile 1 stored successfully.");
742  * } else {
743  * ESP_LOGE(TAG, "Failed to store profile: %s", esp_err_to_name(err));
744  * }
745  * }
746  * @endcode
747  */
749  uint8_t index, const char * const ssid, const char * const pass
750 );
751 
752 /**
753  * @brief Loads Wi-Fi network credentials for a specific profile index.
754  *
755  * Reads SSID and password from NVS into pre-allocated buffers.
756  *
757  * Valid index range: `(index >= 0) && (index <= WIFI_PROFILES_MAX_COUNT)`
758  *
759  * @note Querying profile 0 directly returns system-hardcoded credentials
760  * (<code>#EIF_WIFI_SSID_DEFAULT</code> and
761  * <code>#EIF_WIFI_PASS_DEFAULT</code>), completely bypassing
762  * flash memory access. Any other profile index
763  * explicitly queries the underlying NVS partition.
764  *
765  *
766  * @param index Profile index slot. Must be from `0` to
767  * <code>#EIF_WIFI_PROFILES_MAX_COUNT</code>.
768  * @param ssid_out Buffer for SSID. Cannot be `NULL`. The length should be
769  * <code>#EIF_WIFI_SSID_MAX_LEN</code>.
770  * @param pass_out Buffer for password. Cannot be `NULL`. The length should be
771  * <code>#EIF_WIFI_PASS_MAX_LEN</code>.
772  *
773  * @return
774  * - `ESP_OK`: Profile loaded successfully.
775  * - `ESP_ERR_INVALID_ARG`: The pointer `ssid_out` or `pass_out` is `NULL`.
776  * - `ESP_ERR_INVALID_SIZE`: The `index` is out of the possible range.
777  * - `ESP_ERR_NVS_NOT_FOUND`: No data at specified index.
778  * - `ESP_ERR_NVS_*`: System errors propagated from
779  * `eif_nvs_value_load()`.
780  *
781  * Example of use:
782  * @code{c}
783  * #include <esp_log.h>
784  * #include <esp_err.h>
785  * #include <esp_iot_framework_core_ext.h>
786  *
787  * #define TAG "WIFI_CFG"
788  *
789  * void load_profile(uint8_t idx) {
790  * char ssid[EIF_WIFI_SSID_MAX_LEN] = {0};
791  * char pass[EIF_WIFI_PASS_MAX_LEN] = {0};
792  *
793  * if (eif_nvs_wifi_profile_load(idx, ssid, pass) == ESP_OK) {
794  * ESP_LOGI(TAG, "Profile %u: SSID=[%s]", idx, ssid);
795  * }
796  * }
797  * @endcode
798  */
800  uint8_t index, char * const ssid_out, char * const pass_out
801 );
802 /** @} */
803 
804 /**
805  * @name HTTP Basic Authentication
806  * @brief Functions for reading, encoding, and writing Basic Auth credentials to NVS.
807  * @{
808  */
809 /**
810  * @brief Encodes and saves the HTTP Basic Auth credentials to NVS.
811  *
812  * Takes the raw password, combines it with the default username (`admin`),
813  * and encodes the `admin:password` combination into Base64. The final string
814  * is prefixed with <code>Basic&nbsp;</code> (e.g., `Basic YWRtaW46cGFzcw==`) and
815  * saved to NVS.
816  *
817  * If an empty password (length `0`) is provided, the function automatically
818  * generates and saves the default empty-password line (`Basic YWRtaW46`).
819  *
820  * @param pass Raw password string. Cannot be `NULL`. The length should be from
821  * <code>#EIF_BASIC_AUTH_PASS_MIN_LEN</code>
822  * to <code>#EIF_BASIC_AUTH_PASS_MAX_LEN</code>.
823  *
824  * @return
825  * - `ESP_OK`: Password successfully encoded, formatted, and
826  * saved to NVS.
827  * - `ESP_ERR_INVALID_ARG`: The `pass` pointer is `NULL`.
828  * - `ESP_ERR_INVALID_SIZE`: The length of the `pass` is outside the
829  * acceptable range.
830  * - `ESP_ERR_NO_MEM`: Memory could not be allocated due to the lack
831  * of an empty block of the required size.
832  * - `ESP_ERR_NVS_*`: System errors propagated from `eif_nvs_value_save()`.
833  *
834  * @code{c}
835  * #include <esp_log.h>
836  * #include <esp_err.h>
837  * #include <esp_iot_framework_core_ext.h>
838  * #include <esp_iot_framework_core_macros.h>
839  *
840  * #define TAG "AUTH_CFG"
841  *
842  * void update_web_password(const unsigned char *new_pass) {
843  * EIF_TAG_WITH_UNUSED "AUTH";
844  * esp_err_t ret = ESP_OK;
845  *
846  * // Handles validation, Base64 encoding, and NVS storage automatically
847  * EIF_IF_OK_CHECK_ESP_ERR_T(ret, eif_nvs_basic_auth_line_save(new_pass),
848  * "Failed to save Basic Auth credentials");
849  *
850  * if (ret == ESP_OK) {
851  * ESP_LOGI(TAG, "Web authentication updated successfully.");
852  * }
853  * }
854  * @endcode
855  */
856 esp_err_t eif_nvs_basic_auth_line_save(const unsigned char * const pass);
857 
858 /**
859  * @brief Loads the complete HTTP Basic Auth credentials from NVS.
860  *
861  * Reads the stored authorization string from NVS into a pre-allocated buffer.
862  * The output string contains the configuration prefix, username, and password.
863  *
864  * Example of output written to `buf_out`:
865  * @code
866  * Basic YWRtaW46bXlfcGFzcw==
867  * @endcode
868  *
869  * @param buf_out Buffer for Basic Auth string (e.g., `Basic YWRtaW46cGFzcw==`).
870  * Cannot be `NULL`. The length should be
871  * <code>#EIF_BASIC_AUTH_LINE_MAX_LEN</code>.
872  *
873  * @return
874  * - `ESP_OK`: Credential string located and loaded successfully.
875  * - `ESP_ERR_INVALID_ARG`: The `buf_out` pointer is `NULL`.
876  * - `ESP_ERR_NVS_*`: System errors propagated from
877  * `eif_nvs_value_load()`.
878  *
879  * @code{c}
880  * #include <esp_log.h>
881  * #include <esp_err.h>
882  * #include <esp_iot_framework_core_ext.h>
883  * #include <esp_iot_framework_core_macros.h>
884  *
885  * #define TAG "AUTH_RUN"
886  *
887  * void apply_auth_header(void) {
888  * EIF_TAG_WITH_UNUSED "AUTH";
889  * esp_err_t ret = ESP_OK;
890  * char auth_line[EIF_BASIC_AUTH_LINE_MAX_LEN] = {0};
891  *
892  * EIF_IF_OK_CHECK_ESP_ERR_T(ret, eif_nvs_basic_auth_line_load(auth_line),
893  * "Failed to load auth line");
894  *
895  * if (ret == ESP_OK) {
896  * ESP_LOGI(TAG, "Loaded Auth Header: %s", auth_line);
897  * // Pass auth_line to HTTP client configuration here
898  * }
899  * }
900  * @endcode
901  */
902 esp_err_t eif_nvs_basic_auth_line_load(char * const buf_out);
903 /** @} */
904 
905 /** @} */
906 
907 
908 
909 /**
910  * @defgroup core_ext_tasks Task Management
911  * @brief Thread lifecycle control and secure task spawn utilities.
912  * @{
913  *
914  * @details This group establishes a unified, deterministic framework for spawning
915  * and managing FreeRTOS tasks within the ecosystem. It enforces strict input
916  * verification, runtime state validation, and centralized resource allocation
917  * boundaries. By formalizing task initialization paths, this subsystem eliminates
918  * generic firmware vulnerabilities such as race conditions during initialization,
919  * unhandled allocation failures, and orphan tasks causing heap fragmentation.
920  *
921  * @warning In the future, the documentation will be restored to its normal
922  * form.
923  *
924  * @note Before calling any function in this group, `eif_core_initialize()`
925  * must be executed exactly once.<br><br>
926  * Examples:
927  * @code{c}
928  * #include <esp_err.h>
929  * #include <esp_iot_framework_core.h>
930  *
931  * void app_main(void) {
932  * ESP_ERROR_CHECK(eif_core_initialize());
933  *
934  * // Further code...
935  * }
936  * @endcode
937  */
938 
939 /**
940  * @brief Common helper for secure FreeRTOS task spawning.
941  *
942  * This function provides a standardized mechanism to initialize FreeRTOS tasks.
943  * It sequential-validates input arguments, ensures protection against double
944  * spawning via the task handle state, and handles memory exhaustion failures
945  * safely.
946  *
947  * @warning The `p_handle` argument must point to a persistent variable
948  * (typically a module-scoped static) to maintain double-spawn
949  * protection. Once the task finishes execution or triggers a delete,
950  * the underlying task handle variable must be set back to `NULL` to
951  * allow future respawns.
952  *
953  * @param p_handle Pointer to the `TaskHandle_t` variable. If the pointed
954  * handle is already initialized (not `NULL`), the function
955  * returns `ESP_ERR_INVALID_STATE`.
956  * @param f_worker Task function (worker).
957  * @param p_name Task name string for debugging and identification.
958  * @param u32_stack Stack size in bytes.
959  * @param u_prio Task priority.
960  *
961  * @return
962  * - `ESP_OK`: Task created successfully.
963  * - `ESP_ERR_INVALID_ARG`: The pointer `p_handle`, `f_worker` or `p_name`
964  * is `NULL`.
965  * - `ESP_ERR_INVALID_STATE`: Task handle is already in use (task already
966  * exists).
967  * - `ESP_ERR_NO_MEM`: Memory could not be allocated due to the lack
968  * of an empty block of the required size.
969  *
970  * Example of use (but it's better to use #EIF_TASK_LAUNCH macro):
971  * @code{c}
972  * #include <esp_log.h>
973  * #include <freertos/FreeRTOS.h>
974  * #include <freertos/task.h>
975  * #include <esp_iot_framework_core_ext.h>
976  * #include <esp_iot_framework_core_macros.h>
977  *
978  * // Double-call protection
979  * static TaskHandle_t mqtt_hdl = NULL;
980  *
981  * void mqtt_task_worker(void *arg) {
982  * while(1) {
983  * vTaskDelay(pdMS_TO_TICKS(1000));
984  * }
985  * mqtt_hdl = NULL;
986  * vTaskDelete(NULL);
987  * }
988  *
989  * void start_app_tasks(void) {
990  * EIF_TAG_WITH_UNUSED "APP_START";
991  * esp_err_t ret = ESP_OK;
992  *
993  * ret = eif_task_common_spawn(mqtt_hdl, mqtt_task_worker, "mqtt_service", 4096, 5);
994  * if (ret != ESP_OK) {
995  * EIF_LOG_E("Failed to launch MQTT task");
996  * }
997  * }
998  * @endcode
999  *
1000  * @note If double-spawn restrictions are not required (e.g., creating multiple
1001  * instances of the same task), pass a pointer to a temporary standalone
1002  * task handle variable instead of the main module handle.
1003  *
1004  * Example of use (without double-call protection, but it's better to use
1005  * #EIF_TASK_LAUNCH macro):
1006  * @code{c}
1007  * #include <esp_log.h>
1008  * #include <freertos/FreeRTOS.h>
1009  * #include <freertos/task.h>
1010  * #include <esp_iot_framework_core_ext.h>
1011  * #include <esp_iot_framework_core_macros.h>
1012  *
1013  * void task_worker(void *arg) {
1014  * while(1) {
1015  * vTaskDelay(pdMS_TO_TICKS(1000));
1016  * }
1017  * vTaskDelete(NULL);
1018  * }
1019  *
1020  * void start_app_tasks(void) {
1021  * EIF_TAG_WITH_UNUSED "APP_START";
1022  * esp_err_t ret = ESP_OK;
1023  *
1024  * for (int i = 0; i < 5; i++) {
1025  * TaskHandle_t hdl = NULL;
1026  *
1027  * ret = eif_task_common_spawn(hdl, task_worker, "task_worker", 2048, 3);
1028  * if (ret != ESP_OK) {
1029  * EIF_LOG_E("Failed to launch task iteration %d", i);
1030  * }
1031  * }
1032  * }
1033  * @endcode
1034  */
1036  TaskHandle_t * const p_handle, const TaskFunction_t f_worker,
1037  const char * const p_name, const uint32_t u32_stack,
1038  const UBaseType_t u_prio
1039 );
1040 /**
1041  * @brief Spawns an asynchronous task to execute a system reboot.
1042  *
1043  * Spawns an asynchronous task to execute a system reboot.
1044  *
1045  * @return
1046  * - `ESP_OK`: Task created successfully and added to the
1047  * FreeRTOS scheduler.
1048  * - `ESP_ERR_INVALID_ARG`: Internal error. One of the mandatory arguments
1049  * is passed as a `NULL` pointer.
1050  * - `ESP_ERR_INVALID_STATE`: The task handle is already active, meaning
1051  * this task is already running.
1052  * - `ESP_ERR_NO_MEM`: Memory could not be allocated due to the lack
1053  * of an empty block of the required size.
1054  */
1055 esp_err_t eif_task_reboot_launch(void);
1056 /**
1057  * @brief Spawns an asynchronous task to execute a Wi-Fi profile test.
1058  *
1059  * Spawns an asynchronous task to execute a Wi-Fi profile test.
1060  *
1061  * @param profile_index The index of the Wi-Fi profile under test. Must be
1062  * from `0` to number of @ref wifi_profiles_desc "Wi-Fi profiles" stored
1063  * in the system. It is set using `eif_set_wifi_profiles_count()`.
1064  *
1065  * @return
1066  * - `ESP_OK`: Task created successfully and added to the
1067  * FreeRTOS scheduler.
1068  * - `ESP_ERR_INVALID_ARG`: Internal error. One of the mandatory arguments
1069  * is passed as a `NULL` pointer.
1070  * - `ESP_ERR_INVALID_STATE`: The task handle is already active, meaning
1071  * this task is already running.
1072  * - `ESP_ERR_NO_MEM`: Memory could not be allocated due to the lack
1073  * of an empty block of the required size.
1074  */
1075 esp_err_t eif_task_wifi_test_launch(uint8_t profile_index);
1076 #if (defined(CONFIG_EIF_ENABLE_TLS) || defined(DOXYGEN))
1077  /**
1078  * @brief Spawns an asynchronous task to regenerate TLS credentials.
1079  *
1080  * @note Must enable the `CONFIG_EIF_ENABLE_TLS` flag in `Kconfig` to use
1081  *
1082  * Spawns an asynchronous task to regenerate TLS credentials.
1083  *
1084  * @return
1085  * - `ESP_OK`: Task created successfully and added to the
1086  * FreeRTOS scheduler.
1087  * - `ESP_ERR_INVALID_ARG`: Internal error. One of the mandatory
1088  * arguments is passed as a `NULL` pointer.
1089  * - `ESP_ERR_INVALID_STATE`: The task handle is already active, meaning
1090  * this task is already running.
1091  * - `ESP_ERR_NO_MEM`: Memory could not be allocated due to the
1092  * lack of an empty block of the required size.
1093  */
1095 #endif
1096 /**
1097  * @brief Spawns an asynchronous task to execute a firmware rollback.
1098  *
1099  * Spawns an asynchronous task to execute a firmware rollback.
1100  *
1101  * @return
1102  * - `ESP_OK`: Task created successfully and added to the
1103  * FreeRTOS scheduler.
1104  * - `ESP_ERR_INVALID_ARG`: Internal error. One of the mandatory arguments
1105  * is passed as a `NULL` pointer.
1106  * - `ESP_ERR_INVALID_STATE`: The task handle is already active, meaning
1107  * this task is already running.
1108  * - `ESP_ERR_NO_MEM`: Memory could not be allocated due to the lack
1109  * of an empty block of the required size.
1110  */
1112 
1113 /** @} */
1114 
1115 /**
1116  * @defgroup Ofher Other
1117  * @brief Anything that does not belong to a specific section or is not
1118  * part of a group large enough for a separate module.
1119  * @{
1120  */
1121 #ifdef CONFIG_EIF_LOG_ENABLE_REMOTE_DEBUG
1122  /*
1123  * @brief Thread-safe extraction of diagnostic logs from the core ring buffer.
1124  *
1125  * This function retrieves accumulated logs from the internal ring buffer,
1126  * copies them into the provided destination buffer, and releases the
1127  * consumed memory within the ring.
1128  *
1129  * @param dest_buf Pointer to the destination buffer where logs will be copied.
1130  * @param max_size Maximum capacity of the destination buffer in bytes.
1131  *
1132  * @return size_t The number of bytes actually copied into dest_buf.
1133  * Returns 0 if the log buffer is empty.
1134  */
1135  size_t eif_core_log_pop_chunk(char *dest_buf, size_t max_size);
1136 #endif
1137 /** @} */
1138 /** @} */
1139 
1140 #ifdef __cplusplus
1141  }
1142 #endif
1143 #endif
uint8_t eif_wifi_get_current_profile_index(void)
Gets the index of the currently active Wi-Fi profile.
esp_err_t eif_wifi_get_test_result(uint8_t index, eif_wifi_test_result *const out_result)
Gets the network availability test result for a specific Wi-Fi profile.
uint8_t eif_wifi_get_profiles_count(void)
Gets the total number of Wi-Fi profiles.
esp_err_t(* eif_handler_ip_t)(void)
Prototype for network IP lifecycle event handlers.
Definition: esp_iot_framework_core_ext.h:397
esp_err_t eif_register_handler_ip_got(eif_handler_ip_t handler)
Register a handler for the IP_EVENT_STA_GOT_IP.
esp_err_t eif_register_handler_ip_lost(eif_handler_ip_t handler)
Register a handler for the IP_EVENT_STA_LOST_IP.
esp_err_t eif_nvs_wifi_profile_load(uint8_t index, char *const ssid_out, char *const pass_out)
Loads Wi-Fi network credentials for a specific profile index.
esp_err_t eif_nvs_value_load(const char *const key, char *const value_out, size_t max_len)
Loads a string from NVS into a pre-allocated buffer.
esp_err_t eif_nvs_basic_auth_line_save(const unsigned char *const pass)
Encodes and saves the HTTP Basic Auth credentials to NVS.
esp_err_t eif_nvs_value_save(const char *const key, const char *const value, size_t min_len, size_t max_len, bool it_can_be_empty)
Validates string length and writes it to NVS.
esp_err_t eif_nvs_basic_auth_line_load(char *const buf_out)
Loads the complete HTTP Basic Auth credentials from NVS.
esp_err_t eif_nvs_value_load_malloc(const char *key, char **const value_out, size_t *const value_out_len)
Dynamically allocates memory and loads a string from NVS.
esp_err_t eif_nvs_wifi_profile_save(uint8_t index, const char *const ssid, const char *const pass)
Saves Wi-Fi network credentials for a specific profile index.
esp_err_t eif_task_reboot_launch(void)
Spawns an asynchronous task to execute a system reboot.
esp_err_t eif_task_rollback_and_reboot_launch(void)
Spawns an asynchronous task to execute a firmware rollback.
esp_err_t eif_task_tls_recreate_launch(void)
Spawns an asynchronous task to regenerate TLS credentials.
esp_err_t eif_task_wifi_test_launch(uint8_t profile_index)
Spawns an asynchronous task to execute a Wi-Fi profile test.
esp_err_t eif_task_common_spawn(TaskHandle_t *const p_handle, const TaskFunction_t f_worker, const char *const p_name, const uint32_t u32_stack, const UBaseType_t u_prio)
Common helper for secure FreeRTOS task spawning.
static size_t eif_strnlen(const char *const str, size_t max_allowed)
Computes the length of string.
Definition: esp_iot_framework_core_ext.h:186
static bool eif_strempty(const char *const str)
Checks if a string is empty.
Definition: esp_iot_framework_core_ext.h:148
Wi-Fi profile availability test result.
Definition: esp_iot_framework_core_ext.h:248
bool connected
Network availability status.
Definition: esp_iot_framework_core_ext.h:256
int8_t rssi
Received Signal Strength Indicator (RSSI).
Definition: esp_iot_framework_core_ext.h:263