This commit is contained in:
2026-05-22 21:52:50 +03:00
commit be7c60e4dd
1854 changed files with 583428 additions and 0 deletions
@@ -0,0 +1,96 @@
#include "Insights.h"
#include "WiFi.h"
#include "inttypes.h"
#include "esp_err.h"
#include "esp_random.h"
const char insights_auth_key[] = "<ENTER YOUR AUTH KEY>";
#define WIFI_SSID "<ENTER YOUR SSID>"
#define WIFI_PASSPHRASE "<ENTER YOUR PASSWORD>"
#define MAX_CRASHES 5
#define MAX_PTRS 30
#define TAG "sketch"
RTC_NOINIT_ATTR static uint32_t s_reset_count;
static void *s_ptrs[MAX_PTRS];
static void smoke_test() {
int dice;
int count = 0;
bool allocating = false;
while (1) {
dice = esp_random() % 500;
log_i("dice=%d", dice);
if (dice > 0 && dice < 150) {
log_e("[count][%d]", count);
} else if (dice > 150 && dice < 300) {
log_w("[count][%d]", count);
} else if (dice > 300 && dice < 470) {
Insights.event(TAG, "[count][%d]", count);
} else {
/* 30 in 500 probability to crash */
if (s_reset_count > MAX_CRASHES) {
Insights.event(TAG, "[count][%d]", count);
} else {
log_e("[count][%d] [crash_count][%" PRIu32 "] [excvaddr][0x0f] Crashing...", count, s_reset_count);
//ToDo: find better way to crash
abort();
}
}
Insights.metrics.dumpHeap();
if (count % MAX_PTRS == 0) {
allocating = !allocating;
log_i("Allocating:%s\n", allocating ? "true" : "false");
}
if (allocating) {
uint32_t size = 1024 * (esp_random() % 8);
void *p = malloc(size);
if (p) {
memset(p, size, 'A' + (esp_random() % 26));
log_i("Allocated %" PRIu32 " bytes", size);
}
s_ptrs[count % MAX_PTRS] = p;
} else {
free(s_ptrs[count % MAX_PTRS]);
s_ptrs[count % MAX_PTRS] = NULL;
log_i("Freeing some memory...");
}
count++;
delay(1000);
}
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSPHRASE);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
if (!Insights.begin(insights_auth_key)) {
return;
}
Serial.println("=========================================");
Serial.printf("ESP Insights enabled Node ID %s\n", Insights.nodeID());
Serial.println("=========================================");
if (esp_reset_reason() == ESP_RST_POWERON) {
s_reset_count = 1;
} else {
s_reset_count++;
}
}
void loop() {
smoke_test();
delay(100);
}
@@ -0,0 +1,45 @@
# Smoke Test App
## What to expect in this example?
- This example is expected to exercise the various features of the ESP Insights framework
- As a smoke test, this allows you to validate, by a quick perusal of the ESP Insights dashboard, the functioning of all the high-level features
## End-to-End Tests
### Lifecycle of the test (Hard reset resets the cycle)
* Device boots up and logs errors/warnings/events in random order every 10 seconds
* Every error/warning/event log with "diag_smoke" tag is associated with an incremental counter
* There's a 30/500 probability that device will crash, this is done for verification of crash
* Device will crash only five times and hard reset will reset the counter to 1
* On sixth boot onwards device will not crash and logs errors/warnings/events and adds heap metrics
### Facilitate the Auth Key
In this example we will be using the auth key that we downloaded while [setting up ESP Insights account](https://github.com/espressif/esp-insights/tree/main/examples#set-up-esp-insights-account).
Copy Auth Key to the example
```
const char insights_auth_key[] = "<ENTER YOUR AUTH KEY>";
```
### Enter Wi-Fi Credentials
Inside the example sketch, enter your Wi-Fi credentials in `WIFI_SSID` and `WIFI_PASSPHRASE` macros.
### Setup
* Build and flash the sketch and monitor the console
* Device will eventually crash after some time
* Before every crash you will see below log print
```
E (75826) diag_smoke: [count][7] [crash_count][1] [excvaddr][0x0f] Crashing...
// [count][7]: count associated with the log
// [crash_count][1]: This is the first crash since device boot up, this number will increment as the crash count increases
// [excvaddr][0x0f]: Exception vaddr, will see this in crash verification part below
```
* You'll see five crashes([crash_count][5]) and after that device will not crash and will keep on logging and adding metrics
* Onwards this point keep device running for more than 30 minutes
* Now we are all set to visit the [dashboard](https://dashboard.insights.espressif.com)
* Select the node-id printed on the console, look for the below log. It is printed early when device boots up
```
ESP Insights enabled for Node ID ----- wx3vEoGgJPk7Rn5JvRUFs9
```
@@ -0,0 +1,6 @@
requires:
- CONFIG_ESP_INSIGHTS_ENABLED=y
requires_any:
- CONFIG_SOC_WIFI_SUPPORTED=y
- CONFIG_ESP_WIFI_REMOTE_ENABLED=y
@@ -0,0 +1,29 @@
#include "Insights.h"
#include "WiFi.h"
const char insights_auth_key[] = "<ENTER YOUR AUTH KEY>";
#define WIFI_SSID "<ENTER YOUR SSID>"
#define WIFI_PASSPHRASE "<ENTER YOUR PASSWORD>"
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSPHRASE);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
if (!Insights.begin(insights_auth_key)) {
return;
}
Serial.println("=========================================");
Serial.printf("ESP Insights enabled Node ID %s\n", Insights.nodeID());
Serial.println("=========================================");
}
void loop() {
delay(1000);
}
@@ -0,0 +1,43 @@
# Minimal Diagnostics example
- [What to expect in this example](#what-to-expect-in-this-example)
- [Try out the example](#try-out-the-example)
- [Insights Dashboard](#insights-dashboard)
## What to expect in this example?
- This example demonstrates the use of ESP Insights framework in minimal way
- Device will try to connect with the configured Wi-Fi network
- ESP Insights is enabled in this example, so any error/warning logs, crashes will be reported to cloud
- This example collects heap and Wi-Fi metrics every 10 minutes and network variables are collected when they change
## Try out the example
### Facilitate the Auth Key
In this example we will be using the auth key that we downloaded while [setting up ESP Insights account](https://github.com/espressif/esp-insights/tree/main/examples#set-up-esp-insights-account).
Copy Auth Key to the example
```
const char insights_auth_key[] = "<ENTER YOUR AUTH KEY>";
```
### Enter Wi-Fi Credentials
Inside the example sketch, enter your Wi-Fi credentials in `WIFI_SSID` and `WIFI_PASSPHRASE` macros.
### Get the Node ID
- Start the Serial monitor
- Once the device boots, it will connect to the Wi-Fi network, look for logs similar to below and make a note of Node ID.
```
I (4161) esp_insights: =========================================
I (4171) esp_insights: Insights enabled for Node ID 246F2880371C
I (4181) esp_insights: =========================================
```
## Insights Dashboard
Once everything is set up, any diagnostics information reported will show up on the [Insights Dashboard](https://dashboard.insights.espressif.com). Sign in using the your credentials.
### Monitor the device diagnostics
Visit [Nodes](https://dashboard.insights.espressif.com/home/nodes) section on the dashboard and click on the Node ID to monitor device diagnostics information.
> Note: Diagnostics data is reported dynamically or when the buffers are filled to configured threshold. So, it can take some time for the logs to reflect on the dashboard. Moreover, if a large number of logs are generated then data will be sent to cloud but, if it fails(eg reasons: Wi-Fi failure, No internet) then any newer logs will be dropped.
@@ -0,0 +1,6 @@
requires:
- CONFIG_ESP_INSIGHTS_ENABLED=y
requires_any:
- CONFIG_SOC_WIFI_SUPPORTED=y
- CONFIG_ESP_WIFI_REMOTE_ENABLED=y
+8
View File
@@ -0,0 +1,8 @@
name=ESP Insights
version=3.3.7
author=Sanket Wadekar <sanket.wadekar@espressif.com>
maintainer=Sanket Wadekar <sanket.wadekar@espressif.com>
sentence=ESP Insights
paragraph=With this library you can remotely monitor your device error logs, Network variables, WiFi/Heap Metrics, and also custom variables / metrics.
url=https://insights.espressif.com
architectures=esp32
+288
View File
@@ -0,0 +1,288 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "Insights.h"
#ifdef CONFIG_ESP_INSIGHTS_ENABLED
#include "esp_insights.h"
#include "esp_diagnostics.h"
#include "esp_diagnostics_metrics.h"
#include "esp_diagnostics_system_metrics.h"
#include "esp_diagnostics_variables.h"
#include "esp_diagnostics_network_variables.h"
const char *ERROR_INSIGHTS_NOT_INIT = "ESP Insights not initialized";
#define BOOL_FN_OR_ERROR(f, e) \
if (!initialized) { \
log_e("%s", ERROR_INSIGHTS_NOT_INIT); \
return false; \
} \
esp_err_t err = f; \
if (err != ESP_OK) { \
log_e("ESP Insights " e ", err:0x%x", err); \
} \
return err == ESP_OK;
#define BOOL_FN_OR_ERROR_ARG(f, e, a) \
if (!initialized) { \
log_e("%s", ERROR_INSIGHTS_NOT_INIT); \
return false; \
} \
esp_err_t err = f; \
if (err != ESP_OK) { \
log_e("ESP Insights " e ", err:0x%x", a, err); \
} \
return err == ESP_OK;
#define VOID_FN_OR_ERROR(f) \
if (!initialized) { \
log_e("%s", ERROR_INSIGHTS_NOT_INIT); \
return; \
} \
f;
ESPInsightsClass::ESPInsightsClass() : initialized(false) {}
ESPInsightsClass::~ESPInsightsClass() {
end();
}
bool ESPInsightsClass::begin(const char *auth_key, const char *node_id, uint32_t log_type, bool alloc_ext_ram, bool use_default_transport) {
if (!initialized) {
if (log_type == 0xFFFFFFFF) {
log_type = (ESP_DIAG_LOG_TYPE_ERROR | ESP_DIAG_LOG_TYPE_WARNING | ESP_DIAG_LOG_TYPE_EVENT);
}
esp_insights_config_t config = {.log_type = log_type, .node_id = node_id, .auth_key = auth_key, .alloc_ext_ram = alloc_ext_ram};
esp_err_t err = ESP_OK;
if (use_default_transport) {
err = esp_insights_init(&config);
} else {
err = esp_insights_enable(&config);
}
if (err != ESP_OK) {
log_e("Failed to initialize ESP Insights, err:0x%x", err);
}
initialized = err == ESP_OK;
metrics.setInitialized(initialized);
variables.setInitialized(initialized);
} else {
log_i("ESP Insights already initialized");
}
return initialized;
}
void ESPInsightsClass::end() {
if (initialized) {
esp_insights_deinit();
initialized = false;
metrics.setInitialized(initialized);
variables.setInitialized(initialized);
}
}
const char *ESPInsightsClass::nodeID() {
if (!initialized) {
log_e("%s", ERROR_INSIGHTS_NOT_INIT);
return "";
}
return esp_insights_get_node_id();
}
bool ESPInsightsClass::event(const char *tag, const char *format, ...) {
if (!initialized) {
log_e("%s", ERROR_INSIGHTS_NOT_INIT);
return false;
}
char loc_buf[64];
char *temp = loc_buf;
va_list arg;
va_list copy;
va_start(arg, format);
va_copy(copy, arg);
int len = vsnprintf(temp, sizeof(loc_buf), format, copy);
va_end(copy);
if (len < 0) {
va_end(arg);
return false;
};
if (len >= (int)sizeof(loc_buf)) { // comparison of same sign type for the compiler
temp = (char *)malloc(len + 1);
if (temp == NULL) {
va_end(arg);
return false;
}
len = vsnprintf(temp, len + 1, format, arg);
}
va_end(arg);
esp_err_t err = esp_diag_log_event(tag, "%s", temp);
if (temp != loc_buf) {
free(temp);
}
if (err != ESP_OK) {
log_e("Failed to send ESP Insights event, err:0x%x", err);
}
return err == ESP_OK;
}
bool ESPInsightsClass::send() {
BOOL_FN_OR_ERROR(esp_insights_send_data(), "Failed to send");
}
void ESPInsightsClass::dumpTasksStatus() {
VOID_FN_OR_ERROR(esp_diag_task_snapshot_dump());
}
// ESPInsightsMetricsClass
bool ESPInsightsMetricsClass::addBool(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_BOOL), "Failed to add metric '%s'", key);
}
bool ESPInsightsMetricsClass::addInt(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_INT), "Failed to add metric '%s'", key);
}
bool ESPInsightsMetricsClass::addUint(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_UINT), "Failed to add metric '%s'", key);
}
bool ESPInsightsMetricsClass::addFloat(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_FLOAT), "Failed to add metric '%s'", key);
}
bool ESPInsightsMetricsClass::addString(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_STR), "Failed to add metric '%s'", key);
}
bool ESPInsightsMetricsClass::addIPv4(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_IPv4), "Failed to add metric '%s'", key);
}
bool ESPInsightsMetricsClass::addMAC(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_MAC), "Failed to add metric '%s'", key);
}
bool ESPInsightsMetricsClass::setBool(const char *key, bool b) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_add_bool(key, b), "Failed to set metric '%s'", key);
}
bool ESPInsightsMetricsClass::setInt(const char *key, int32_t i) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_add_int(key, i), "Failed to set metric '%s'", key);
}
bool ESPInsightsMetricsClass::setUint(const char *key, uint32_t u) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_add_uint(key, u), "Failed to set metric '%s'", key);
}
bool ESPInsightsMetricsClass::setFloat(const char *key, float f) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_add_float(key, f), "Failed to set metric '%s'", key);
}
bool ESPInsightsMetricsClass::setString(const char *key, const char *str) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_add_str(key, str), "Failed to set metric '%s'", key);
}
bool ESPInsightsMetricsClass::setIPv4(const char *key, uint32_t ip) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_add_ipv4(key, ip), "Failed to set metric '%s'", key);
}
bool ESPInsightsMetricsClass::setMAC(const char *key, uint8_t *mac) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_add_mac(key, mac), "Failed to set metric '%s'", key);
}
bool ESPInsightsMetricsClass::remove(const char *key) {
BOOL_FN_OR_ERROR_ARG(esp_diag_metrics_unregister(key), "Failed to remove metric '%s'", key);
}
bool ESPInsightsMetricsClass::removeAll() {
BOOL_FN_OR_ERROR(esp_diag_metrics_unregister_all(), "Failed to remove metrics");
}
void ESPInsightsMetricsClass::setHeapPeriod(uint32_t seconds) {
VOID_FN_OR_ERROR(esp_diag_heap_metrics_reset_interval(seconds));
}
bool ESPInsightsMetricsClass::dumpHeap() {
BOOL_FN_OR_ERROR(esp_diag_heap_metrics_dump(), "Failed to send heap metrics");
}
void ESPInsightsMetricsClass::setWiFiPeriod(uint32_t seconds) {
VOID_FN_OR_ERROR(esp_diag_wifi_metrics_reset_interval(seconds));
}
bool ESPInsightsMetricsClass::dumpWiFi() {
BOOL_FN_OR_ERROR(esp_diag_wifi_metrics_dump(), "Failed to send wifi metrics");
}
// ESPInsightsVariablesClass
bool ESPInsightsVariablesClass::addBool(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_BOOL), "Failed to add variable '%s'", key);
}
bool ESPInsightsVariablesClass::addInt(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_INT), "Failed to add variable '%s'", key);
}
bool ESPInsightsVariablesClass::addUint(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_UINT), "Failed to add variable '%s'", key);
}
bool ESPInsightsVariablesClass::addFloat(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_FLOAT), "Failed to add variable '%s'", key);
}
bool ESPInsightsVariablesClass::addString(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_STR), "Failed to add variable '%s'", key);
}
bool ESPInsightsVariablesClass::addIPv4(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_IPv4), "Failed to add variable '%s'", key);
}
bool ESPInsightsVariablesClass::addMAC(const char *tag, const char *key, const char *label, const char *path) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_register(tag, key, label, path, ESP_DIAG_DATA_TYPE_MAC), "Failed to add variable '%s'", key);
}
bool ESPInsightsVariablesClass::setBool(const char *key, bool b) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_add_bool(key, b), "Failed to set variable '%s'", key);
}
bool ESPInsightsVariablesClass::setInt(const char *key, int32_t i) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_add_int(key, i), "Failed to set variable '%s'", key);
}
bool ESPInsightsVariablesClass::setUint(const char *key, uint32_t u) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_add_uint(key, u), "Failed to set variable '%s'", key);
}
bool ESPInsightsVariablesClass::setFloat(const char *key, float f) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_add_float(key, f), "Failed to set variable '%s'", key);
}
bool ESPInsightsVariablesClass::setString(const char *key, const char *str) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_add_str(key, str), "Failed to set variable '%s'", key);
}
bool ESPInsightsVariablesClass::setIPv4(const char *key, uint32_t ip) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_add_ipv4(key, ip), "Failed to set variable '%s'", key);
}
bool ESPInsightsVariablesClass::setMAC(const char *key, uint8_t *mac) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_add_mac(key, mac), "Failed to set variable '%s'", key);
}
bool ESPInsightsVariablesClass::remove(const char *key) {
BOOL_FN_OR_ERROR_ARG(esp_diag_variable_unregister(key), "Failed to remove variable '%s'", key);
}
bool ESPInsightsVariablesClass::removeAll() {
BOOL_FN_OR_ERROR(esp_diag_variable_unregister_all(), "Failed to remove variables");
}
ESPInsightsClass Insights;
#endif
+121
View File
@@ -0,0 +1,121 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#ifdef CONFIG_ESP_INSIGHTS_ENABLED
#include "Arduino.h"
#ifdef __cplusplus
class ESPInsightsMetricsClass {
private:
bool initialized;
public:
ESPInsightsMetricsClass() : initialized(false) {}
bool addBool(const char *tag, const char *key, const char *label, const char *path);
bool addInt(const char *tag, const char *key, const char *label, const char *path);
bool addUint(const char *tag, const char *key, const char *label, const char *path);
bool addFloat(const char *tag, const char *key, const char *label, const char *path);
bool addString(const char *tag, const char *key, const char *label, const char *path);
bool addIPv4(const char *tag, const char *key, const char *label, const char *path);
bool addMAC(const char *tag, const char *key, const char *label, const char *path);
bool setBool(const char *key, bool b);
bool setInt(const char *key, int32_t i);
bool setUint(const char *key, uint32_t u);
bool setFloat(const char *key, float f);
bool setString(const char *key, const char *str);
bool setIPv4(const char *key, uint32_t ip);
bool setMAC(const char *key, uint8_t *mac);
bool remove(const char *key);
bool removeAll();
void setHeapPeriod(uint32_t seconds);
void setWiFiPeriod(uint32_t seconds);
bool dumpHeap();
bool dumpWiFi();
//internal use
void setInitialized(bool init) {
initialized = init;
}
};
class ESPInsightsVariablesClass {
private:
bool initialized;
public:
ESPInsightsVariablesClass() : initialized(false) {}
bool addBool(const char *tag, const char *key, const char *label, const char *path);
bool addInt(const char *tag, const char *key, const char *label, const char *path);
bool addUint(const char *tag, const char *key, const char *label, const char *path);
bool addFloat(const char *tag, const char *key, const char *label, const char *path);
bool addString(const char *tag, const char *key, const char *label, const char *path);
bool addIPv4(const char *tag, const char *key, const char *label, const char *path);
bool addMAC(const char *tag, const char *key, const char *label, const char *path);
bool setBool(const char *key, bool b);
bool setInt(const char *key, int32_t i);
bool setUint(const char *key, uint32_t u);
bool setFloat(const char *key, float f);
bool setString(const char *key, const char *str);
bool setIPv4(const char *key, uint32_t ip);
bool setMAC(const char *key, uint8_t *mac);
bool remove(const char *key);
bool removeAll();
//internal use
void setInitialized(bool init) {
initialized = init;
}
};
class ESPInsightsClass {
private:
bool initialized;
public:
ESPInsightsMetricsClass metrics;
ESPInsightsVariablesClass variables;
ESPInsightsClass();
~ESPInsightsClass();
bool begin(const char *auth_key, const char *node_id = NULL, uint32_t log_type = 0xFFFFFFFF, bool alloc_ext_ram = false, bool use_default_transport = true);
void end();
bool send();
const char *nodeID();
void dumpTasksStatus();
bool event(const char *tag, const char *format, ...);
};
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_INSIGHTS)
extern ESPInsightsClass Insights;
#endif
extern "C" {
#endif
#include "esp_err.h"
#include "esp_log.h"
esp_err_t esp_diag_log_event(const char *tag, const char *format, ...) __attribute__((format(printf, 2, 3)));
#define insightsEvent(tag, format, ...) \
{ esp_diag_log_event(tag, "EV (%" PRIu32 ") %s: " format, esp_log_timestamp(), tag, ##__VA_ARGS__); }
#ifdef __cplusplus
}
#endif
#endif